diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..0e75158e3 --- /dev/null +++ b/.travis.yml @@ -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 diff --git a/contrib/coverity/model.c b/contrib/coverity/model.c new file mode 100644 index 000000000..15535d421 --- /dev/null +++ b/contrib/coverity/model.c @@ -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 ) { +} diff --git a/src/Makefile b/src/Makefile index 2a9cc9e8f..d74565d13 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,6 +9,7 @@ ASFLAGS := LDFLAGS := HOST_CFLAGS := MAKEDEPS := Makefile +CROSS_COMPILE ?= $(CROSS) ############################################################################### # @@ -52,9 +53,7 @@ EINFO := ./util/einfo GENKEYMAP := ./util/genkeymap.pl DOXYGEN := doxygen LCAB := lcab -BINUTILS_DIR := /usr -BFD_DIR := $(BINUTILS_DIR) -ZLIB_DIR := /usr +QEMUIMG := qemu-img ############################################################################### # @@ -63,7 +62,7 @@ ZLIB_DIR := /usr 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 @@ -72,17 +71,23 @@ 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 SRCDIRS += drivers/infiniband +SRCDIRS += drivers/infiniband/mlx_utils_flexboot/src +SRCDIRS += drivers/infiniband/mlx_utils/src/public +SRCDIRS += drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access +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 SRCDIRS += interface/bofm @@ -96,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. # @@ -151,6 +166,9 @@ all : $(ALL) everything : $(Q)$(MAKE) --no-print-directory $(ALL) \ bin/3c509.rom bin/intel.rom bin/intel.mrom \ + bin-x86_64-pcbios/8086100e.mrom bin-x86_64-pcbios/intel.rom \ + bin-x86_64-pcbios/ipxe.usb bin-x86_64-pcbios/ipxe.pxe \ + bin-x86_64-pcbios/undionly.kpxe \ bin-i386-efi/ipxe.efi bin-i386-efi/ipxe.efidrv \ bin-i386-efi/ipxe.efirom \ bin-x86_64-efi/ipxe.efi bin-x86_64-efi/ipxe.efidrv \ diff --git a/src/Makefile.efi b/src/Makefile.efi new file mode 100644 index 000000000..151b33186 --- /dev/null +++ b/src/Makefile.efi @@ -0,0 +1,46 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# The EFI linker script +# +LDSCRIPT = scripts/efi.lds + +# Retain relocation information for elf2efi +# +LDFLAGS += -q -S + +# Media types. +# +NON_AUTO_MEDIA += efi +NON_AUTO_MEDIA += efidrv +NON_AUTO_MEDIA += drv.efi +NON_AUTO_MEDIA += efirom + +# Include SNP driver in the all-drivers build +# +DRIVERS_net += snp + +# Rules for building EFI files +# +$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(ELF2EFI) --subsystem=10 $< $@ + +$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(ELF2EFI) --subsystem=11 $< $@ + +$(BIN)/%.drv.efi : $(BIN)/%.efidrv + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(CP) $< $@ + +$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@ + +$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined + $(QM)$(ECHO) " [CAB] $@" + $(Q)$(LCAB) -n -q $(ALL_drv.efi) $@ + +$(BIN)/%.usb : $(BIN)/%.efi + $(QM)$(ECHO) " [GENEFIDSK] $@" + $(Q)bash util/genefidsk -o $@ -b $(EFI_BOOT_FILE) $< diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 1b6f092bb..00b079263 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -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[-[-][-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) @@ -491,6 +504,11 @@ LDFLAGS += -static # CFLAGS += -include include/compiler.h +# The section type character (e.g. "@" in "@progbits") varies by +# architecture. +# +CFLAGS += -DASM_TCHAR='$(ASM_TCHAR)' -DASM_TCHAR_OPS='$(ASM_TCHAR_OPS)' + # CFLAGS for specific object types # CFLAGS_c += @@ -518,19 +536,25 @@ POST_O := POST_O_DEPS := endif +# Debug level calculations +# +DBGLVL_MAX = -DDBGLVL_MAX=$(firstword $(subst ., ,$(1))) +DBGLVL_DFLT = -DDBGLVL_DFLT=$(lastword $(subst ., ,$(1))) +DBGLVL = $(call DBGLVL_MAX,$(1)) $(call DBGLVL_DFLT,$(1)) + # Rules for specific object types. # COMPILE_c = $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS) RULE_c = $(Q)$(COMPILE_c) -c $< -o $@ $(POST_O) RULE_c_to_ids.o = $(Q)$(ECHO_E) '$(OBJ_IDS_ASM_NL)' | $(ASSEMBLE_S) -o $@ -RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -DDBGLVL_MAX=$* -c $< -o $@ $(POST_O) +RULE_c_to_dbg%.o= $(Q)$(COMPILE_c) $(call DBGLVL,$*) -c $< -o $@ $(POST_O) RULE_c_to_c = $(Q)$(COMPILE_c) -E -c $< > $@ RULE_c_to_s = $(Q)$(COMPILE_c) -S -g0 -c $< -o $@ PREPROCESS_S = $(CPP) $(CFLAGS) $(CFLAGS_S) $(OBJ_CFLAGS) ASSEMBLE_S = $(AS) $(ASFLAGS) RULE_S = $(Q)$(PREPROCESS_S) $< | $(ASSEMBLE_S) -o $@ -RULE_S_to_dbg%.o = $(Q)$(PREPROCESS_S) -DDBGLVL_MAX=$* $< | $(ASSEMBLE_S) -o $@ +RULE_S_to_dbg%.o= $(Q)$(PREPROCESS_S) $(call DBGLVL,$*) $< | $(ASSEMBLE_S) -o $@ RULE_S_to_s = $(Q)$(PREPROCESS_S) $< > $@ GENERIC_TARGETS += ids.o dbg%.o c s @@ -709,6 +733,60 @@ config/named.h : $(CONFIG_LIST) .PRECIOUS : config/named.h +# (Single-element) list of assertion configuration +# +ASSERT_LIST := $(BIN)/.assert.list +ifeq ($(wildcard $(ASSERT_LIST)),) +ASSERT_OLD := +else +ASSERT_OLD := $(shell cat $(ASSERT_LIST)) +endif +ifneq ($(ASSERT_OLD),$(ASSERT)) +$(shell $(ECHO) "$(ASSERT)" > $(ASSERT_LIST)) +endif + +$(ASSERT_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(ASSERT_LIST) + +# Assertion configuration +# +ifneq ($(ASSERT),) +CFLAGS += -DASSERTING=$(ASSERT) +endif + +include/assert.h : $(ASSERT_LIST) + $(Q)$(TOUCH) $@ + +.PRECIOUS : include/assert.h + +# (Single-element) list of profiling configuration +# +PROFILE_LIST := $(BIN)/.profile.list +ifeq ($(wildcard $(PROFILE_LIST)),) +PROFILE_OLD := +else +PROFILE_OLD := $(shell cat $(PROFILE_LIST)) +endif +ifneq ($(PROFILE_OLD),$(PROFILE)) +$(shell $(ECHO) "$(PROFILE)" > $(PROFILE_LIST)) +endif + +$(PROFILE_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(PROFILE_LIST) + +# Profiling configuration +# +ifneq ($(PROFILE),) +CFLAGS += -DPROFILING=$(PROFILE) +endif + +include/ipxe/profile.h : $(PROFILE_LIST) + $(Q)$(TOUCH) $@ + +.PRECIOUS : include/ipxe/profile.h + # These files use .incbin inline assembly to include a binary file. # Unfortunately ccache does not detect this dependency and caches # builds even when the binary file has changed. @@ -734,8 +812,8 @@ $(DBGCOL_LIST) : $(MAKEDEPS) VERYCLEANUP += $(DBGCOL_LIST) DBGCOL_COLOURS := $(subst -, ,$(DBGCOL)) -DBGCOL_MIN := $(word 1,$(DBGCOL_COLOURS)) -DBGCOL_MAX := $(word 2,$(DBGCOL_COLOURS)) +DBGCOL_MIN := $(firstword $(DBGCOL_COLOURS)) +DBGCOL_MAX := $(lastword $(DBGCOL_COLOURS)) debug_DEPS += $(DBGCOL_LIST) @@ -896,7 +974,7 @@ endif # Device ID tables (using IDs from ROM definition file) # define obj_pci_id_asm - .section ".pci_devlist.$(1)", "a", @progbits + .section ".pci_devlist.$(1)", "a", $(ASM_TCHAR)progbits .globl pci_devlist_$(1) pci_devlist_$(1): .short ( 0x$(1) & 0xffff ) @@ -960,13 +1038,13 @@ DRIVERS_ipxe = $(DRIVERS_net) $(DRIVERS_infiniband) \ # TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci") # TGT_ROM_NAME : the ROM name (e.g. "dfe538") # -CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1)) TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@)))) TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS)) -TGT_DRIVERS = $(strip $(if $(DRIVERS_$(TGT_ROM_NAME)), \ - $(DRIVERS_$(TGT_ROM_NAME)), \ - $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \ - $(call CARD_DRIVER,$(TGT_ELEMENT))) )) +TGT_DRIVERS = $(strip $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \ + $(if $(DRIVERS_$(TGT_ELEMENT)), \ + $(DRIVERS_$(TGT_ELEMENT)), \ + $(firstword $(DRIVER_$(TGT_ELEMENT)) \ + $(TGT_ELEMENT))))) TGT_PREFIX_NAME = $(word 2,$(subst ., ,$(notdir $@))) TGT_PREFIX = $(strip $(if $(filter rom,$(TGT_PREFIX_NAME)), \ $(ROM_TYPE_$(TGT_ROM_NAME))rom, \ @@ -1014,10 +1092,12 @@ TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_ENTRY) $(TGT_LD_DRIVERS) \ # the target. # DEBUG_LIST = $(subst $(COMMA), ,$(DEBUG)) -DEBUG_OBJ_LEVEL = $(firstword $(word 2,$(subst :, ,$(1))) 1) -DEBUG_OBJ_BASE = $(word 1,$(subst :, ,$(1))).dbg$(call DEBUG_OBJ_LEVEL,$(1)) -DEBUG_OBJ = $(BIN)/$(call DEBUG_OBJ_BASE,$(1)).o -DEBUG_ORIG_OBJ = $(BIN)/$(word 1,$(subst :, ,$(1))).o +DEBUG_MAX = $(firstword $(word 2,$(subst :, ,$(1))) 1) +DEBUG_DFLT = $(if $(word 3,$(subst :, ,$(1))),.$(word 3,$(subst :, ,$(1)))) +DEBUG_LEVEL = $(call DEBUG_MAX,$(1))$(call DEBUG_DFLT,$(1)) +DEBUG_BASE = $(firstword $(subst :, ,$(1))).dbg$(call DEBUG_LEVEL,$(1)) +DEBUG_OBJ = $(BIN)/$(call DEBUG_BASE,$(1)).o +DEBUG_ORIG_OBJ = $(BIN)/$(firstword $(subst :, ,$(1))).o DEBUG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_OBJ,$(D))) DEBUG_ORIG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_ORIG_OBJ,$(D))) BLIB_OBJS = $(DEBUG_OBJS) $(filter-out $(DEBUG_ORIG_OBJS),$(BOBJS)) @@ -1298,21 +1378,15 @@ CLEANUP += $(ZBIN) # # The EFI image converter # -ELF2EFI_CFLAGS := -I$(BINUTILS_DIR)/include -I$(BFD_DIR)/include \ - -I$(ZLIB_DIR)/include -idirafter include -ELF2EFI_LDFLAGS := -L$(BINUTILS_DIR)/lib -L$(BFD_DIR)/lib -L$(ZLIB_DIR)/lib \ - -lbfd -ldl -lz -Wl,--no-warn-search-mismatch $(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET_IA32 $< \ - $(ELF2EFI_LDFLAGS) -o $@ + $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET32 $< -o $@ CLEANUP += $(ELF2EFI32) $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET_X64 $< \ - $(ELF2EFI_LDFLAGS) -o $@ + $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET64 $< -o $@ CLEANUP += $(ELF2EFI64) $(EFIROM) : util/efirom.c $(MAKEDEPS) diff --git a/src/arch/arm/Makefile b/src/arch/arm/Makefile new file mode 100644 index 000000000..3cee5f3ac --- /dev/null +++ b/src/arch/arm/Makefile @@ -0,0 +1,12 @@ +# Assembler section type character +# +ASM_TCHAR := % +ASM_TCHAR_OPS := %% + +# Include common ARM headers +# +INCDIRS += arch/arm/include + +# ARM-specific directories containing source files +# +SRCDIRS += arch/arm/interface/efi diff --git a/src/arch/arm/Makefile.efi b/src/arch/arm/Makefile.efi new file mode 100644 index 000000000..f04be425b --- /dev/null +++ b/src/arch/arm/Makefile.efi @@ -0,0 +1,6 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Include generic EFI Makefile +# +MAKEDEPS += Makefile.efi +include Makefile.efi diff --git a/src/arch/arm/core/arm_io.c b/src/arch/arm/core/arm_io.c new file mode 100644 index 000000000..1ef571fc1 --- /dev/null +++ b/src/arch/arm/core/arm_io.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include + +/** @file + * + * iPXE I/O API for ARM + * + */ + +/** An ARM I/O qword */ +union arm32_io_qword { + uint64_t qword; + uint32_t dword[2]; +}; + +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + * + * This is not atomic for ARM32. + */ +static uint64_t arm32_readq ( volatile uint64_t *io_addr ) { + volatile union arm32_io_qword *ptr = + container_of ( io_addr, union arm32_io_qword, qword ); + union arm32_io_qword tmp; + + tmp.dword[0] = readl ( &ptr->dword[0] ); + tmp.dword[1] = readl ( &ptr->dword[1] ); + return tmp.qword; +} + +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + * + * This is not atomic for ARM32. + */ +static void arm32_writeq ( uint64_t data, volatile uint64_t *io_addr ) { + volatile union arm32_io_qword *ptr = + container_of ( io_addr, union arm32_io_qword, qword ); + union arm32_io_qword tmp; + + tmp.qword = data; + writel ( tmp.dword[0], &ptr->dword[0] ); + writel ( tmp.dword[1], &ptr->dword[1] ); +} + +PROVIDE_IOAPI_INLINE ( arm, phys_to_bus ); +PROVIDE_IOAPI_INLINE ( arm, bus_to_phys ); +PROVIDE_IOAPI_INLINE ( arm, readb ); +PROVIDE_IOAPI_INLINE ( arm, readw ); +PROVIDE_IOAPI_INLINE ( arm, readl ); +PROVIDE_IOAPI_INLINE ( arm, writeb ); +PROVIDE_IOAPI_INLINE ( arm, writew ); +PROVIDE_IOAPI_INLINE ( arm, writel ); +PROVIDE_IOAPI_INLINE ( arm, iodelay ); +PROVIDE_IOAPI_INLINE ( arm, mb ); +#ifdef __aarch64__ +PROVIDE_IOAPI_INLINE ( arm, readq ); +PROVIDE_IOAPI_INLINE ( arm, writeq ); +#else +PROVIDE_IOAPI ( arm, readq, arm32_readq ); +PROVIDE_IOAPI ( arm, writeq, arm32_writeq ); +#endif diff --git a/src/arch/arm/include/bits/acpi.h b/src/arch/arm/include/bits/acpi.h new file mode 100644 index 000000000..f9f2f00e7 --- /dev/null +++ b/src/arch/arm/include/bits/acpi.h @@ -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 */ diff --git a/src/arch/arm/include/bits/endian.h b/src/arch/arm/include/bits/endian.h new file mode 100644 index 000000000..4506711ad --- /dev/null +++ b/src/arch/arm/include/bits/endian.h @@ -0,0 +1,13 @@ +#ifndef _BITS_ENDIAN_H +#define _BITS_ENDIAN_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* ARM may be either little-endian or big-endian */ +#ifdef __ARM_BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#endif /* _BITS_ENDIAN_H */ diff --git a/src/arch/x86_64/include/bits/entropy.h b/src/arch/arm/include/bits/entropy.h similarity index 74% rename from src/arch/x86_64/include/bits/entropy.h rename to src/arch/arm/include/bits/entropy.h index a9b3bc10e..75fdc90ea 100644 --- a/src/arch/x86_64/include/bits/entropy.h +++ b/src/arch/arm/include/bits/entropy.h @@ -3,7 +3,7 @@ /** @file * - * x86_64-specific entropy API implementations + * ARM-specific entropy API implementations * */ diff --git a/src/arch/arm/include/bits/errfile.h b/src/arch/arm/include/bits/errfile.h new file mode 100644 index 000000000..65f7f719b --- /dev/null +++ b/src/arch/arm/include/bits/errfile.h @@ -0,0 +1,19 @@ +#ifndef _BITS_ERRFILE_H +#define _BITS_ERRFILE_H + +/** @file + * + * ARM-specific error file identifiers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @addtogroup errfile Error file identifiers + * @{ + */ + +/** @} */ + +#endif /* _BITS_ERRFILE_H */ diff --git a/src/arch/arm/include/bits/hyperv.h b/src/arch/arm/include/bits/hyperv.h new file mode 100644 index 000000000..f0e0c8793 --- /dev/null +++ b/src/arch/arm/include/bits/hyperv.h @@ -0,0 +1,12 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_HYPERV_H */ diff --git a/src/arch/arm/include/bits/io.h b/src/arch/arm/include/bits/io.h new file mode 100644 index 000000000..90f6455ec --- /dev/null +++ b/src/arch/arm/include/bits/io.h @@ -0,0 +1,14 @@ +#ifndef _BITS_IO_H +#define _BITS_IO_H + +/** @file + * + * ARM-specific I/O API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_IO_H */ diff --git a/src/arch/arm/include/bits/iomap.h b/src/arch/arm/include/bits/iomap.h new file mode 100644 index 000000000..ae953c450 --- /dev/null +++ b/src/arch/arm/include/bits/iomap.h @@ -0,0 +1,12 @@ +#ifndef _BITS_IOMAP_H +#define _BITS_IOMAP_H + +/** @file + * + * ARM-specific I/O mapping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_IOMAP_H */ diff --git a/src/arch/arm/include/bits/nap.h b/src/arch/arm/include/bits/nap.h new file mode 100644 index 000000000..e30a7146b --- /dev/null +++ b/src/arch/arm/include/bits/nap.h @@ -0,0 +1,14 @@ +#ifndef _BITS_NAP_H +#define _BITS_NAP_H + +/** @file + * + * ARM-specific CPU sleeping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_MAP_H */ diff --git a/src/arch/arm/include/bits/pci_io.h b/src/arch/arm/include/bits/pci_io.h new file mode 100644 index 000000000..fba0eb979 --- /dev/null +++ b/src/arch/arm/include/bits/pci_io.h @@ -0,0 +1,14 @@ +#ifndef _BITS_PCI_IO_H +#define _BITS_PCI_IO_H + +/** @file + * + * ARM PCI I/O API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_PCI_IO_H */ diff --git a/src/arch/x86_64/include/bits/reboot.h b/src/arch/arm/include/bits/reboot.h similarity index 74% rename from src/arch/x86_64/include/bits/reboot.h rename to src/arch/arm/include/bits/reboot.h index f9bcd6a7b..88c50250c 100644 --- a/src/arch/x86_64/include/bits/reboot.h +++ b/src/arch/arm/include/bits/reboot.h @@ -3,7 +3,7 @@ /** @file * - * x86_64-specific reboot API implementations + * ARM-specific reboot API implementations * */ diff --git a/src/arch/x86_64/include/bits/sanboot.h b/src/arch/arm/include/bits/sanboot.h similarity index 74% rename from src/arch/x86_64/include/bits/sanboot.h rename to src/arch/arm/include/bits/sanboot.h index dcab830f6..abd4c79a5 100644 --- a/src/arch/x86_64/include/bits/sanboot.h +++ b/src/arch/arm/include/bits/sanboot.h @@ -3,7 +3,7 @@ /** @file * - * x86_64-specific sanboot API implementations + * ARM-specific sanboot API implementations * */ diff --git a/src/arch/x86_64/include/bits/smbios.h b/src/arch/arm/include/bits/smbios.h similarity index 53% rename from src/arch/x86_64/include/bits/smbios.h rename to src/arch/arm/include/bits/smbios.h index 2f0118d02..d94218116 100644 --- a/src/arch/x86_64/include/bits/smbios.h +++ b/src/arch/arm/include/bits/smbios.h @@ -3,8 +3,10 @@ /** @file * - * i386-specific SMBIOS API implementations + * ARM-specific SMBIOS API implementations * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #endif /* _BITS_SMBIOS_H */ diff --git a/src/arch/x86_64/include/bits/time.h b/src/arch/arm/include/bits/time.h similarity index 74% rename from src/arch/x86_64/include/bits/time.h rename to src/arch/arm/include/bits/time.h index aa74fac8c..724d8b932 100644 --- a/src/arch/x86_64/include/bits/time.h +++ b/src/arch/arm/include/bits/time.h @@ -3,7 +3,7 @@ /** @file * - * x86_64-specific time API implementations + * ARM-specific time API implementations * */ diff --git a/src/arch/x86_64/include/bits/uaccess.h b/src/arch/arm/include/bits/uaccess.h similarity index 52% rename from src/arch/x86_64/include/bits/uaccess.h rename to src/arch/arm/include/bits/uaccess.h index 455829242..87f11509c 100644 --- a/src/arch/x86_64/include/bits/uaccess.h +++ b/src/arch/arm/include/bits/uaccess.h @@ -3,8 +3,10 @@ /** @file * - * x86_64-specific user access API implementations + * ARM-specific user access API implementations * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #endif /* _BITS_UACCESS_H */ diff --git a/src/arch/arm/include/bits/uart.h b/src/arch/arm/include/bits/uart.h new file mode 100644 index 000000000..6f85975f7 --- /dev/null +++ b/src/arch/arm/include/bits/uart.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UART_H +#define _BITS_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UART_H */ diff --git a/src/arch/arm/include/bits/umalloc.h b/src/arch/arm/include/bits/umalloc.h new file mode 100644 index 000000000..27970d7b2 --- /dev/null +++ b/src/arch/arm/include/bits/umalloc.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UMALLOC_H +#define _BITS_UMALLOC_H + +/** @file + * + * ARM-specific user memory allocation API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UMALLOC_H */ diff --git a/src/arch/arm/include/bits/xen.h b/src/arch/arm/include/bits/xen.h new file mode 100644 index 000000000..34f647903 --- /dev/null +++ b/src/arch/arm/include/bits/xen.h @@ -0,0 +1,158 @@ +#ifndef _BITS_XEN_H +#define _BITS_XEN_H + +/** @file + * + * Xen interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Hypercall registers */ +#ifdef __aarch64__ +#define XEN_HC "x16" +#define XEN_REG1 "x0" +#define XEN_REG2 "x1" +#define XEN_REG3 "x2" +#define XEN_REG4 "x3" +#define XEN_REG5 "x4" +#else +#define XEN_HC "r12" +#define XEN_REG1 "r0" +#define XEN_REG2 "r1" +#define XEN_REG3 "r2" +#define XEN_REG4 "r3" +#define XEN_REG5 "r4" +#endif + +/** + * Issue hypercall with one argument + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_1 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + + __asm__ __volatile__ ( "hvc %1" + : "+r" ( reg1 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with two arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_2 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + + __asm__ __volatile__ ( "hvc %2" + : "+r" ( reg1 ), "+r" ( reg2 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with three arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_3 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + + __asm__ __volatile__ ( "hvc %3" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with four arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_4 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + + __asm__ __volatile__ ( "hvc %4" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ), + "+r" ( reg4 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with five arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @v arg5 Fifth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_5 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + register unsigned long reg5 asm ( XEN_REG5 ) = arg5; + + __asm__ __volatile__ ( "hvc %5" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ), + "+r" ( reg4 ), "+r" ( reg5 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +#endif /* _BITS_XEN_H */ diff --git a/src/arch/arm/include/ipxe/arm_io.h b/src/arch/arm/include/ipxe/arm_io.h new file mode 100644 index 000000000..f8765af75 --- /dev/null +++ b/src/arch/arm/include/ipxe/arm_io.h @@ -0,0 +1,105 @@ +#ifndef _IPXE_ARM_IO_H +#define _IPXE_ARM_IO_H + +/** @file + * + * iPXE I/O API for ARM + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOAPI_ARM +#define IOAPI_PREFIX_arm +#else +#define IOAPI_PREFIX_arm __arm_ +#endif + +/* + * Memory space mappings + * + */ + +/** Page shift */ +#define PAGE_SHIFT 12 + +/* + * Physical<->Bus address mappings + * + */ + +static inline __always_inline unsigned long +IOAPI_INLINE ( arm, phys_to_bus ) ( unsigned long phys_addr ) { + return phys_addr; +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) { + return bus_addr; +} + +/* + * MMIO reads and writes up to native word size + * + */ + +#define ARM_READX( _api_func, _type, _insn_suffix, _reg_prefix ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( arm, _api_func ) ( volatile _type *io_addr ) { \ + _type data; \ + __asm__ __volatile__ ( "ldr" _insn_suffix " %" _reg_prefix "0, %1" \ + : "=r" ( data ) : "Qo" ( *io_addr ) ); \ + return data; \ +} +#ifdef __aarch64__ +ARM_READX ( readb, uint8_t, "b", "w" ); +ARM_READX ( readw, uint16_t, "h", "w" ); +ARM_READX ( readl, uint32_t, "", "w" ); +ARM_READX ( readq, uint64_t, "", "" ); +#else +ARM_READX ( readb, uint8_t, "b", "" ); +ARM_READX ( readw, uint16_t, "h", "" ); +ARM_READX ( readl, uint32_t, "", "" ); +#endif + +#define ARM_WRITEX( _api_func, _type, _insn_suffix, _reg_prefix ) \ +static inline __always_inline void \ +IOAPI_INLINE ( arm, _api_func ) ( _type data, volatile _type *io_addr ) { \ + __asm__ __volatile__ ( "str" _insn_suffix " %" _reg_prefix "0, %1" \ + : : "r" ( data ), "Qo" ( *io_addr ) ); \ +} +#ifdef __aarch64__ +ARM_WRITEX ( writeb, uint8_t, "b", "w" ); +ARM_WRITEX ( writew, uint16_t, "h", "w" ); +ARM_WRITEX ( writel, uint32_t, "", "w" ); +ARM_WRITEX ( writeq, uint64_t, "", "" ); +#else +ARM_WRITEX ( writeb, uint8_t, "b", "" ); +ARM_WRITEX ( writew, uint16_t, "h", "" ); +ARM_WRITEX ( writel, uint32_t, "", "" ); +#endif + +/* + * Slow down I/O + * + */ +static inline __always_inline void +IOAPI_INLINE ( arm, iodelay ) ( void ) { + /* Nothing to do */ +} + +/* + * Memory barrier + * + */ +static inline __always_inline void +IOAPI_INLINE ( arm, mb ) ( void ) { + +#ifdef __aarch64__ + __asm__ __volatile__ ( "dmb sy" ); +#else + __asm__ __volatile__ ( "dmb" ); +#endif +} + +#endif /* _IPXE_ARM_IO_H */ diff --git a/src/arch/arm/include/ipxe/efi/efiarm_nap.h b/src/arch/arm/include/ipxe/efi/efiarm_nap.h new file mode 100644 index 000000000..dcbdd3e20 --- /dev/null +++ b/src/arch/arm/include/ipxe/efi/efiarm_nap.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFIARM_NAP_H +#define _IPXE_EFIARM_NAP_H + +/** @file + * + * EFI CPU sleeping + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef NAP_EFIARM +#define NAP_PREFIX_efiarm +#else +#define NAP_PREFIX_efiarm __efiarm_ +#endif + +#endif /* _IPXE_EFIARM_NAP_H */ diff --git a/src/arch/arm/interface/efi/efiarm_nap.c b/src/arch/arm/interface/efi/efiarm_nap.c new file mode 100644 index 000000000..9ed638e9a --- /dev/null +++ b/src/arch/arm/interface/efi/efiarm_nap.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include + +/** @file + * + * iPXE CPU sleeping API for EFI + * + */ + +/** + * Sleep until next interrupt + * + */ +static void efiarm_cpu_nap ( void ) { + /* + * I can't find any EFI API that allows us to put the CPU to + * sleep. The CpuSleep() function is defined in CpuLib.h, but + * isn't part of any exposed protocol so we have no way to + * call it. + * + * The EFI shell doesn't seem to bother sleeping the CPU; it + * just sits there idly burning power. + * + */ + __asm__ __volatile__ ( "wfi" ); +} + +PROVIDE_NAP ( efiarm, cpu_nap, efiarm_cpu_nap ); diff --git a/src/arch/arm32/Makefile b/src/arch/arm32/Makefile new file mode 100644 index 000000000..3a7c09230 --- /dev/null +++ b/src/arch/arm32/Makefile @@ -0,0 +1,23 @@ +# ARM32-specific directories containing source files +# +SRCDIRS += arch/arm32/core +SRCDIRS += arch/arm32/libgcc + +# ARM32-specific flags +# +CFLAGS += -mthumb -mcpu=cortex-a15 -mabi=aapcs -mfloat-abi=soft +CFLAGS += -mword-relocations +ASFLAGS += -mthumb -mcpu=cortex-a15 + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# Include common ARM Makefile +MAKEDEPS += arch/arm/Makefile +include arch/arm/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/arm32/Makefile.$(PLATFORM) +include arch/arm32/Makefile.$(PLATFORM) diff --git a/src/arch/arm32/Makefile.efi b/src/arch/arm32/Makefile.efi new file mode 100644 index 000000000..e139a055a --- /dev/null +++ b/src/arch/arm32/Makefile.efi @@ -0,0 +1,18 @@ +# -*- 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) + +# Specify EFI boot file +# +EFI_BOOT_FILE = bootarm.efi + +# Include generic EFI Makefile +# +MAKEDEPS += arch/arm/Makefile.efi +include arch/arm/Makefile.efi diff --git a/src/arch/arm32/core/arm32_bigint.c b/src/arch/arm32/core/arm32_bigint.c new file mode 100644 index 000000000..839bead18 --- /dev/null +++ b/src/arch/arm32/core/arm32_bigint.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include + +/** @file + * + * Big integer support + */ + +/** + * Multiply big integers + * + * @v multiplicand0 Element 0 of big integer to be multiplied + * @v multiplier0 Element 0 of big integer to be multiplied + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements + */ +void bigint_multiply_raw ( const uint32_t *multiplicand0, + const uint32_t *multiplier0, + uint32_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = + ( ( const void * ) multiplicand0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = + ( ( const void * ) multiplier0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result = + ( ( void * ) result0 ); + unsigned int i; + unsigned int j; + uint32_t multiplicand_element; + uint32_t multiplier_element; + uint32_t *result_elements; + uint32_t discard_low; + uint32_t discard_high; + uint32_t discard_temp; + + /* Zero result */ + memset ( result, 0, sizeof ( *result ) ); + + /* Multiply integers one element at a time */ + for ( i = 0 ; i < size ; i++ ) { + multiplicand_element = multiplicand->element[i]; + for ( j = 0 ; j < size ; j++ ) { + multiplier_element = multiplier->element[j]; + result_elements = &result->element[ i + j ]; + /* Perform a single multiply, and add the + * resulting double-element into the result, + * carrying as necessary. The carry can + * never overflow beyond the end of the + * result, since: + * + * a < 2^{n}, b < 2^{n} => ab < 2^{2n} + */ + __asm__ __volatile__ ( "umull %1, %2, %5, %6\n\t" + "ldr %3, [%0]\n\t" + "adds %3, %1\n\t" + "stmia %0!, {%3}\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, %2\n\t" + "stmia %0!, {%3}\n\t" + "bcc 2f\n\t" + "\n1:\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, #0\n\t" + "stmia %0!, {%3}\n\t" + "bcs 1b\n\t" + "\n2:\n\t" + : "+l" ( result_elements ), + "=l" ( discard_low ), + "=l" ( discard_high ), + "=l" ( discard_temp ), + "+m" ( *result ) + : "l" ( multiplicand_element ), + "l" ( multiplier_element ) + : "cc" ); + } + } +} diff --git a/src/arch/arm32/core/setjmp.S b/src/arch/arm32/core/setjmp.S new file mode 100644 index 000000000..7e7b0fe58 --- /dev/null +++ b/src/arch/arm32/core/setjmp.S @@ -0,0 +1,32 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arm + +/* + * Save stack context for non-local goto + */ + .globl setjmp + .type setjmp, %function +setjmp: + /* Store registers */ + stmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr } + /* Return 0 when returning as setjmp() */ + mov r0, #0 + bx lr + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp + .type longjmp, %function +longjmp: + /* Restore registers */ + ldmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr } + /* Force result to non-zero */ + movs r0, r1 + moveq r0, #1 + /* Return to setjmp() caller */ + bx lr + .size longjmp, . - longjmp diff --git a/src/arch/arm32/include/bits/bigint.h b/src/arch/arm32/include/bits/bigint.h new file mode 100644 index 000000000..103c6c489 --- /dev/null +++ b/src/arch/arm32/include/bits/bigint.h @@ -0,0 +1,316 @@ +#ifndef _BITS_BIGINT_H +#define _BITS_BIGINT_H + +/** @file + * + * Big integer support + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Element of a big integer */ +typedef uint32_t bigint_element_t; + +/** + * Initialise big integer + * + * @v value0 Element 0 of big integer to initialise + * @v size Number of elements + * @v data Raw data + * @v len Length of raw data + */ +static inline __attribute__ (( always_inline )) void +bigint_init_raw ( uint32_t *value0, unsigned int size, + const void *data, size_t len ) { + size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + uint8_t *value_byte = ( ( void * ) value0 ); + const uint8_t *data_byte = ( data + len ); + + /* Copy raw data in reverse order, padding with zeros */ + while ( len-- ) + *(value_byte++) = *(--data_byte); + while ( pad_len-- ) + *(value_byte++) = 0; +} + +/** + * Add big integers + * + * @v addend0 Element 0 of big integer to add + * @v value0 Element 0 of big integer to be added to + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_addend; + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_addend_i; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %2, %0, %8, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldmia %0!, {%3}\n\t" + "ldr %4, [%1]\n\t" + "adcs %4, %3\n\t" + "stmia %1!, {%4}\n\t" + "teq %0, %2\n\t" + "bne 1b\n\t" + : "=l" ( discard_addend ), + "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_addend_i ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( addend0 ), "1" ( value0 ), "l" ( size ) + : "cc" ); +} + +/** + * Subtract big integers + * + * @v subtrahend0 Element 0 of big integer to subtract + * @v value0 Element 0 of big integer to be subtracted from + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_subtrahend; + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_subtrahend_i; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "add %2, %0, %8, lsl #2\n\t" + "cmp %2, %0\n\t" /* set CF */ + "\n1:\n\t" + "ldmia %0!, {%3}\n\t" + "ldr %4, [%1]\n\t" + "sbcs %4, %3\n\t" + "stmia %1!, {%4}\n\t" + "teq %0, %2\n\t" + "bne 1b\n\t" + : "=l" ( discard_subtrahend ), + "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_subtrahend_i ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( subtrahend0 ), "1" ( value0 ), + "l" ( size ) + : "cc" ); +} + +/** + * Rotate big integer left + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_rol_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %2, [%0]\n\t" + "adcs %2, %2\n\t" + "stmia %0!, {%2}\n\t" + "teq %0, %1\n\t" + "bne 1b\n\t" + : "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Rotate big integer right + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_ror_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldmdb %1!, {%2}\n\t" + "rrxs %2, %2\n\t" + "str %2, [%1]\n\t" + "teq %0, %1\n\t" + "bne 1b\n\t" + : "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Test if big integer is equal to zero + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret is_zero Big integer is equal to zero + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_zero_raw ( const uint32_t *value0, unsigned int size ) { + const uint32_t *value = value0; + uint32_t value_i; + + do { + value_i = *(value++); + if ( value_i ) + break; + } while ( --size ); + + return ( value_i == 0 ); +} + +/** + * Compare big integers + * + * @v value0 Element 0 of big integer + * @v reference0 Element 0 of reference big integer + * @v size Number of elements + * @ret geq Big integer is greater than or equal to the reference + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_geq_raw ( const uint32_t *value0, const uint32_t *reference0, + unsigned int size ) { + const uint32_t *value = ( value0 + size ); + const uint32_t *reference = ( reference0 + size ); + uint32_t value_i; + uint32_t reference_i; + + do { + value_i = *(--value); + reference_i = *(--reference); + if ( value_i != reference_i ) + break; + } while ( --size ); + + return ( value_i >= reference_i ); +} + +/** + * Test if bit is set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to test + * @ret is_set Bit is set + */ +static inline __attribute__ (( always_inline )) int +bigint_bit_is_set_raw ( const uint32_t *value0, unsigned int size, + unsigned int bit ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( const void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + return ( value->element[index] & ( 1 << subindex ) ); +} + +/** + * Find highest bit set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret max_bit Highest bit set + 1 (or 0 if no bits set) + */ +static inline __attribute__ (( always_inline )) int +bigint_max_set_bit_raw ( const uint32_t *value0, unsigned int size ) { + const uint32_t *value = ( value0 + size ); + int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) ); + uint32_t value_i; + + do { + value_i = *(--value); + max_bit -= ( 32 - fls ( value_i ) ); + if ( value_i ) + break; + } while ( --size ); + + return max_bit; +} + +/** + * Grow big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, + uint32_t *dest0, unsigned int dest_size ) { + unsigned int pad_size = ( dest_size - source_size ); + + memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) ); + memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) ); +} + +/** + * Shrink big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, + uint32_t *dest0, unsigned int dest_size ) { + + memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) ); +} + +/** + * Finalise big integer + * + * @v value0 Element 0 of big integer to finalise + * @v size Number of elements + * @v out Output buffer + * @v len Length of output buffer + */ +static inline __attribute__ (( always_inline )) void +bigint_done_raw ( const uint32_t *value0, unsigned int size __unused, + void *out, size_t len ) { + const uint8_t *value_byte = ( ( const void * ) value0 ); + uint8_t *out_byte = ( out + len ); + + /* Copy raw data in reverse order */ + while ( len-- ) + *(--out_byte) = *(value_byte++); +} + +extern void bigint_multiply_raw ( const uint32_t *multiplicand0, + const uint32_t *multiplier0, + uint32_t *value0, unsigned int size ); + +#endif /* _BITS_BIGINT_H */ diff --git a/src/arch/arm32/include/bits/bitops.h b/src/arch/arm32/include/bits/bitops.h new file mode 100644 index 000000000..9a5fe14c2 --- /dev/null +++ b/src/arch/arm32/include/bits/bitops.h @@ -0,0 +1,100 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * ARM bit operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 32 ); + unsigned int offset = ( bit % 32 ); + volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index ); + uint32_t mask = ( 1UL << offset ); + uint32_t old; + uint32_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldrex %0, %3\n\t" + "orr %1, %0, %4\n\t" + "strex %2, %1, %3\n\t" + "tst %2, %2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ), + "+Q" ( *dword ) + : "r" ( mask ) + : "cc" ); + + return ( old & mask ); +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 32 ); + unsigned int offset = ( bit % 32 ); + volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index ); + uint32_t mask = ( 1UL << offset ); + uint32_t old; + uint32_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldrex %0, %3\n\t" + "bic %1, %0, %4\n\t" + "strex %2, %1, %3\n\t" + "tst %2, %2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ), + "+Q" ( *dword ) + : "r" ( mask ) + : "cc" ); + + return ( old & mask ); +} + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + + test_and_set_bit ( bit, bits ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + + test_and_clear_bit ( bit, bits ); +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/arch/arm32/include/bits/byteswap.h b/src/arch/arm32/include/bits/byteswap.h new file mode 100644 index 000000000..1fc884bd8 --- /dev/null +++ b/src/arch/arm32/include/bits/byteswap.h @@ -0,0 +1,52 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +/** @file + * + * Byte-order swapping functions + * + */ + +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "rev16 %0, %1" : "=l" ( x ) : "l" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_16s ( uint16_t *x ) { + *x = __bswap_variable_16 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "rev %0, %1" : "=l" ( x ) : "l" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_32s ( uint32_t *x ) { + *x = __bswap_variable_32 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + uint32_t in_high = ( x >> 32 ); + uint32_t in_low = ( x & 0xffffffffUL ); + uint32_t out_high = __bswap_variable_32 ( in_low ); + uint32_t out_low = __bswap_variable_32 ( in_high ); + + return ( ( ( ( uint64_t ) out_high ) << 32 ) | + ( ( uint64_t ) out_low ) ); +} + +static inline __attribute__ (( always_inline )) void +__bswap_64s ( uint64_t *x ) { + *x = __bswap_variable_64 ( *x ); +} + +#endif diff --git a/src/arch/arm32/include/bits/compiler.h b/src/arch/arm32/include/bits/compiler.h new file mode 100644 index 000000000..e420cf922 --- /dev/null +++ b/src/arch/arm32/include/bits/compiler.h @@ -0,0 +1,16 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_ARM_NONE + +#ifndef ASSEMBLY + +#define __asmcall +#define __libgcc + +#endif /* ASSEMBLY */ + +#endif /*_BITS_COMPILER_H */ diff --git a/src/arch/arm32/include/bits/profile.h b/src/arch/arm32/include/bits/profile.h new file mode 100644 index 000000000..2b15d1604 --- /dev/null +++ b/src/arch/arm32/include/bits/profile.h @@ -0,0 +1,30 @@ +#ifndef _BITS_PROFILE_H +#define _BITS_PROFILE_H + +/** @file + * + * Profiling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) uint64_t +profile_timestamp ( void ) { + uint32_t cycles; + + /* Read cycle counter */ + __asm__ __volatile__ ( "mcr p15, 0, %1, c9, c12, 0\n\t" + "mrc p15, 0, %0, c9, c13, 0\n\t" + : "=r" ( cycles ) : "r" ( 1 ) ); + return cycles; +} + +#endif /* _BITS_PROFILE_H */ diff --git a/src/arch/arm32/include/bits/stdint.h b/src/arch/arm32/include/bits/stdint.h new file mode 100644 index 000000000..fe1f9946a --- /dev/null +++ b/src/arch/arm32/include/bits/stdint.h @@ -0,0 +1,23 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +typedef __SIZE_TYPE__ size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/src/arch/arm32/include/bits/string.h b/src/arch/arm32/include/bits/string.h new file mode 100644 index 000000000..5b1c1505d --- /dev/null +++ b/src/arch/arm32/include/bits/string.h @@ -0,0 +1,60 @@ +#ifndef BITS_STRING_H +#define BITS_STRING_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * String functions + * + */ + +/** + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int character, size_t len ) { + + /* Not yet optimised */ + generic_memset ( dest, character, len ); + return dest; +} + +/** + * Copy memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memcpy ( void *dest, const void *src, size_t len ) { + + /* Not yet optimised */ + generic_memcpy ( dest, src, len ); + return dest; +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memmove ( void *dest, const void *src, size_t len ) { + + /* Not yet optimised */ + generic_memmove ( dest, src, len ); + return dest; +} + +#endif /* BITS_STRING_H */ diff --git a/src/arch/arm32/include/bits/strings.h b/src/arch/arm32/include/bits/strings.h new file mode 100644 index 000000000..adbd5f4b4 --- /dev/null +++ b/src/arch/arm32/include/bits/strings.h @@ -0,0 +1,85 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + unsigned long bits = value; + unsigned long lsb; + unsigned int lz; + + /* Extract least significant set bit */ + lsb = ( bits & -bits ); + + /* Count number of leading zeroes before LSB */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) ); + + return ( 32 - lz ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( low ) { + return ( __ffsl ( low ) ); + } else if ( high ) { + return ( 32 + __ffsl ( high ) ); + } else { + return 0; + } +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + unsigned int lz; + + /* Count number of leading zeroes */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) ); + + return ( 32 - lz ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( high ) { + return ( 32 + __flsl ( high ) ); + } else if ( low ) { + return ( __flsl ( low ) ); + } else { + return 0; + } +} + +#endif /* _BITS_STRINGS_H */ diff --git a/src/arch/arm32/include/bits/tcpip.h b/src/arch/arm32/include/bits/tcpip.h new file mode 100644 index 000000000..fc3c5b3ff --- /dev/null +++ b/src/arch/arm32/include/bits/tcpip.h @@ -0,0 +1,19 @@ +#ifndef _BITS_TCPIP_H +#define _BITS_TCPIP_H + +/** @file + * + * Transport-network layer interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline )) uint16_t +tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ) { + + /* Not yet optimised */ + return generic_tcpip_continue_chksum ( partial, data, len ); +} + +#endif /* _BITS_TCPIP_H */ diff --git a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h new file mode 100644 index 000000000..29a235944 --- /dev/null +++ b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM32 + +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ + +#endif diff --git a/src/arch/arm32/include/gdbmach.h b/src/arch/arm32/include/gdbmach.h new file mode 100644 index 000000000..cd152eedd --- /dev/null +++ b/src/arch/arm32/include/gdbmach.h @@ -0,0 +1,45 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include + +typedef unsigned long gdbreg_t; + +/* Register snapshot */ +enum { + /* Not yet implemented */ + GDBMACH_NREGS, +}; + +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) step; +} + +static inline void gdbmach_breakpoint ( void ) { + /* Not yet implemented */ +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); +extern void gdbmach_init ( void ); + +#endif /* GDBMACH_H */ diff --git a/src/arch/arm32/include/limits.h b/src/arch/arm32/include/limits.h new file mode 100644 index 000000000..bb48b75ab --- /dev/null +++ b/src/arch/arm32/include/limits.h @@ -0,0 +1,61 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 4294967295UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/arm32/include/setjmp.h b/src/arch/arm32/include/setjmp.h new file mode 100644 index 000000000..4828b47a2 --- /dev/null +++ b/src/arch/arm32/include/setjmp.h @@ -0,0 +1,38 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A jump buffer */ +typedef struct { + /** Saved r4 */ + uint32_t r4; + /** Saved r5 */ + uint32_t r5; + /** Saved r6 */ + uint32_t r6; + /** Saved r7 */ + uint32_t r7; + /** Saved r8 */ + uint32_t r8; + /** Saved r9 */ + uint32_t r9; + /** Saved r10 */ + uint32_t r10; + /** Saved frame pointer (r11) */ + uint32_t fp; + /** Saved stack pointer (r13) */ + uint32_t sp; + /** Saved link register (r14) */ + uint32_t lr; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/src/arch/arm32/libgcc/lldivmod.S b/src/arch/arm32/libgcc/lldivmod.S new file mode 100644 index 000000000..910be4b78 --- /dev/null +++ b/src/arch/arm32/libgcc/lldivmod.S @@ -0,0 +1,50 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .thumb + +/** + * Unsigned long long division + * + * @v r1:r0 Dividend + * @v r3:r2 Divisor + * @ret r1:r0 Quotient + * @ret r3:r2 Remainder + */ + .section ".text.__aeabi_uldivmod", "ax", %progbits + .globl __aeabi_uldivmod + .type __aeabi_uldivmod, %function +__aeabi_uldivmod: + /* Allocate stack space for remainder and pointer to remainder */ + push {r0, r1, r2, r3, r4, lr} + /* Call __udivmoddi4() */ + add r4, sp, #8 + str r4, [sp] + bl __udivmoddi4 + /* Retrieve remainder and return */ + add sp, sp, #8 + pop {r2, r3, r4, pc} + .size __aeabi_uldivmod, . - __aeabi_uldivmod + +/** + * Signed long long division + * + * @v r1:r0 Dividend + * @v r3:r2 Divisor + * @ret r1:r0 Quotient + * @ret r3:r2 Remainder + */ + .section ".text.__aeabi_ldivmod", "ax", %progbits + .globl __aeabi_ldivmod + .type __aeabi_ldivmod, %function +__aeabi_ldivmod: + /* Allocate stack space for remainder and pointer to remainder */ + push {r0, r1, r2, r3, r4, lr} + /* Call __divmoddi4() */ + add r4, sp, #8 + str r4, [sp] + bl __divmoddi4 + /* Retrieve remainder and return */ + add sp, sp, #8 + pop {r2, r3, r4, pc} + .size __aeabi_ldivmod, . - __aeabi_ldivmod diff --git a/src/arch/arm32/libgcc/llshift.S b/src/arch/arm32/libgcc/llshift.S new file mode 100644 index 000000000..cc16e2615 --- /dev/null +++ b/src/arch/arm32/libgcc/llshift.S @@ -0,0 +1,88 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arm + +/** + * Logical shift left + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_llsl", "ax", %progbits + .globl __aeabi_llsl + .type __aeabi_llsl, %function +__aeabi_llsl: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * high = ( low << ( shift - 32 ) ) + */ + movpl r1, r0, lsl r3 + /* If shift < 32, then + * high = ( ( high << shift ) | ( low >> ( 32 - shift ) ) ) + */ + movmi r1, r1, lsl r2 + rsbmi r3, r2, #32 + orrmi r1, r1, r0, lsr r3 + /* low = ( low << shift ) */ + mov r0, r0, lsl r2 + bx lr + .size __aeabi_llsl, . - __aeabi_llsl + +/** + * Logical shift right + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_llsr", "ax", %progbits + .globl __aeabi_llsr + .type __aeabi_llsr, %function +__aeabi_llsr: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * low = ( high >> ( shift - 32 ) ) + */ + movpl r0, r1, lsr r3 + /* If shift < 32, then + * low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) ) + */ + movmi r0, r0, lsr r2 + rsbmi r3, r2, #32 + orrmi r0, r0, r1, lsl r3 + /* high = ( high >> shift ) */ + mov r1, r1, lsr r2 + bx lr + .size __aeabi_llsr, . - __aeabi_llsr + +/** + * Arithmetic shift right + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_lasr", "ax", %progbits + .globl __aeabi_lasr + .type __aeabi_lasr, %function +__aeabi_lasr: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * low = ( high >> ( shift - 32 ) ) + */ + movpl r0, r1, asr r3 + /* If shift < 32, then + * low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) ) + */ + movmi r0, r0, lsr r2 + rsbmi r3, r2, #32 + orrmi r0, r0, r1, lsl r3 + /* high = ( high >> shift ) */ + mov r1, r1, asr r2 + bx lr + .size __aeabi_lasr, . - __aeabi_lasr diff --git a/src/arch/arm64/Makefile b/src/arch/arm64/Makefile new file mode 100644 index 000000000..9b9dd5ec8 --- /dev/null +++ b/src/arch/arm64/Makefile @@ -0,0 +1,33 @@ +# ARM64-specific directories containing source files +# +SRCDIRS += arch/arm64/core + +# ARM64-specific flags +# +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 + +# Include common ARM Makefile +MAKEDEPS += arch/arm/Makefile +include arch/arm/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/arm64/Makefile.$(PLATFORM) +include arch/arm64/Makefile.$(PLATFORM) diff --git a/src/arch/arm64/Makefile.efi b/src/arch/arm64/Makefile.efi new file mode 100644 index 000000000..998a64d03 --- /dev/null +++ b/src/arch/arm64/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI64) + +# Specify EFI boot file +# +EFI_BOOT_FILE = bootaa64.efi + +# Include generic EFI Makefile +# +MAKEDEPS += arch/arm/Makefile.efi +include arch/arm/Makefile.efi diff --git a/src/arch/arm64/core/arm64_bigint.c b/src/arch/arm64/core/arm64_bigint.c new file mode 100644 index 000000000..bc4ee9a00 --- /dev/null +++ b/src/arch/arm64/core/arm64_bigint.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include + +/** @file + * + * Big integer support + */ + +/** + * Multiply big integers + * + * @v multiplicand0 Element 0 of big integer to be multiplied + * @v multiplier0 Element 0 of big integer to be multiplied + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements + */ +void bigint_multiply_raw ( const uint64_t *multiplicand0, + const uint64_t *multiplier0, + uint64_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = + ( ( const void * ) multiplicand0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = + ( ( const void * ) multiplier0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result = + ( ( void * ) result0 ); + unsigned int i; + unsigned int j; + uint64_t multiplicand_element; + uint64_t multiplier_element; + uint64_t *result_elements; + uint64_t discard_low; + uint64_t discard_high; + uint64_t discard_temp_low; + uint64_t discard_temp_high; + + /* Zero result */ + memset ( result, 0, sizeof ( *result ) ); + + /* Multiply integers one element at a time */ + for ( i = 0 ; i < size ; i++ ) { + multiplicand_element = multiplicand->element[i]; + for ( j = 0 ; j < size ; j++ ) { + multiplier_element = multiplier->element[j]; + result_elements = &result->element[ i + j ]; + /* Perform a single multiply, and add the + * resulting double-element into the result, + * carrying as necessary. The carry can + * never overflow beyond the end of the + * result, since: + * + * a < 2^{n}, b < 2^{n} => ab < 2^{2n} + */ + __asm__ __volatile__ ( "mul %1, %6, %7\n\t" + "umulh %2, %6, %7\n\t" + "ldp %3, %4, [%0]\n\t" + "adds %3, %3, %1\n\t" + "adcs %4, %4, %2\n\t" + "stp %3, %4, [%0], #16\n\t" + "bcc 2f\n\t" + "\n1:\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, %3, xzr\n\t" + "str %3, [%0], #8\n\t" + "bcs 1b\n\t" + "\n2:\n\t" + : "+r" ( result_elements ), + "=&r" ( discard_low ), + "=&r" ( discard_high ), + "=r" ( discard_temp_low ), + "=r" ( discard_temp_high ), + "+m" ( *result ) + : "r" ( multiplicand_element ), + "r" ( multiplier_element ) + : "cc" ); + } + } +} diff --git a/src/arch/arm64/core/arm64_string.c b/src/arch/arm64/core/arm64_string.c new file mode 100644 index 000000000..28a2b73bc --- /dev/null +++ b/src/arch/arm64/core/arm64_string.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 + * + * Optimised string operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Copy memory area + * + * @v dest Destination address + * @v src Source address + * @v len Length + * @ret dest Destination address + */ +void arm64_memcpy ( void *dest, const void *src, size_t len ) { + void *discard_dest; + void *discard_end; + const void *discard_src; + size_t discard_offset; + unsigned long discard_data; + unsigned long discard_low; + unsigned long discard_high; + + /* If length is too short for an "ldp"/"stp" instruction pair, + * then just copy individual bytes. + */ + if ( len < 16 ) { + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "ldrb %w1, [%3, %0]\n\t" + "strb %w1, [%2, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ), + "=&r" ( discard_data ) + : "r" ( dest ), "r" ( src ), "0" ( len ) + : "memory" ); + return; + } + + /* Use "ldp"/"stp" to copy 16 bytes at a time: one initial + * potentially unaligned access, multiple destination-aligned + * accesses, one final potentially unaligned access. + */ + __asm__ __volatile__ ( "ldp %3, %4, [%1], #16\n\t" + "stp %3, %4, [%0], #16\n\t" + "and %3, %0, #15\n\t" + "sub %0, %0, %3\n\t" + "sub %1, %1, %3\n\t" + "bic %2, %5, #15\n\t" + "b 2f\n\t" + "\n1:\n\t" + "ldp %3, %4, [%1], #16\n\t" + "stp %3, %4, [%0], #16\n\t" + "\n2:\n\t" + "cmp %0, %2\n\t" + "bne 1b\n\t" + "ldp %3, %4, [%6, #-16]\n\t" + "stp %3, %4, [%5, #-16]\n\t" + : "=&r" ( discard_dest ), + "=&r" ( discard_src ), + "=&r" ( discard_end ), + "=&r" ( discard_low ), + "=&r" ( discard_high ) + : "r" ( dest + len ), "r" ( src + len ), + "0" ( dest ), "1" ( src ) + : "memory", "cc" ); +} + +/** + * Zero memory region + * + * @v dest Destination region + * @v len Length + */ +void arm64_bzero ( void *dest, size_t len ) { + size_t discard_offset; + void *discard_dest; + void *discard_end; + + /* If length is too short for an "stp" instruction, then just + * zero individual bytes. + */ + if ( len < 16 ) { + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "strb wzr, [%1, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ) + : "r" ( dest ), "0" ( len ) + : "memory" ); + return; + } + + /* Use "stp" to zero 16 bytes at a time: one initial + * potentially unaligned access, multiple aligned accesses, + * one final potentially unaligned access. + */ + __asm__ __volatile__ ( "stp xzr, xzr, [%0], #16\n\t" + "bic %0, %0, #15\n\t" + "bic %1, %2, #15\n\t" + "b 2f\n\t" + "\n1:\n\t" + "stp xzr, xzr, [%0], #16\n\t" + "\n2:\n\t" + "cmp %0, %1\n\t" + "bne 1b\n\t" + "stp xzr, xzr, [%2, #-16]\n\t" + : "=&r" ( discard_dest ), + "=&r" ( discard_end ) + : "r" ( dest + len ), "0" ( dest ) + : "memory", "cc" ); +} + +/** + * Fill memory region + * + * @v dest Destination region + * @v len Length + * @v character Fill character + * + * The unusual parameter order is to allow for more efficient + * tail-calling to arm64_memset() when zeroing a region. + */ +void arm64_memset ( void *dest, size_t len, int character ) { + size_t discard_offset; + + /* Use optimised zeroing code if applicable */ + if ( character == 0 ) { + arm64_bzero ( dest, len ); + return; + } + + /* Fill one byte at a time. Calling memset() with a non-zero + * value is relatively rare and unlikely to be + * performance-critical. + */ + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "strb %w2, [%1, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ) + : "r" ( dest ), "r" ( character ), "0" ( len ) + : "memory" ); +} + +/** + * Copy (possibly overlapping) memory region forwards + * + * @v dest Destination region + * @v src Source region + * @v len Length + */ +void arm64_memmove_forwards ( void *dest, const void *src, size_t len ) { + void *discard_dest; + const void *discard_src; + unsigned long discard_data; + + /* Assume memmove() is not performance-critical, and perform a + * bytewise copy for simplicity. + */ + __asm__ __volatile__ ( "b 2f\n\t" + "\n1:\n\t" + "ldrb %w2, [%1], #1\n\t" + "strb %w2, [%0], #1\n\t" + "\n2:\n\t" + "cmp %0, %3\n\t" + "bne 1b\n\t" + : "=&r" ( discard_dest ), + "=&r" ( discard_src ), + "=&r" ( discard_data ) + : "r" ( dest + len ), "0" ( dest ), "1" ( src ) + : "memory" ); +} + +/** + * Copy (possibly overlapping) memory region backwards + * + * @v dest Destination region + * @v src Source region + * @v len Length + */ +void arm64_memmove_backwards ( void *dest, const void *src, size_t len ) { + size_t discard_offset; + unsigned long discard_data; + + /* Assume memmove() is not performance-critical, and perform a + * bytewise copy for simplicity. + */ + __asm__ __volatile__ ( "cbz %0, 2f\n\t" + "\n1:\n\t" + "sub %0, %0, #1\n\t" + "ldrb %w1, [%3, %0]\n\t" + "strb %w1, [%2, %0]\n\t" + "cbnz %0, 1b\n\t" + "\n2:\n\t" + : "=&r" ( discard_offset ), + "=&r" ( discard_data ) + : "r" ( dest ), "r" ( src ), "0" ( len ) + : "memory" ); +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + */ +void arm64_memmove ( void *dest, const void *src, size_t len ) { + + if ( dest <= src ) { + arm64_memmove_forwards ( dest, src, len ); + } else { + arm64_memmove_backwards ( dest, src, len ); + } +} diff --git a/src/arch/arm64/core/arm64_tcpip.c b/src/arch/arm64/core/arm64_tcpip.c new file mode 100644 index 000000000..0ef04ea42 --- /dev/null +++ b/src/arch/arm64/core/arm64_tcpip.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +/** @file + * + * TCP/IP checksum + * + */ + +#include +#include + +/** Alignment used by main checksumming loop */ +#define TCPIP_CHKSUM_ALIGN 16 + +/** Number of steps in each iteration of the unrolled main checksumming loop */ +#define TCPIP_CHKSUM_UNROLL 4 + +/** + * Calculate continued TCP/IP checkum + * + * @v sum Checksum of already-summed data, in network byte order + * @v data Data buffer + * @v len Length of data buffer + * @ret sum Updated checksum, in network byte order + */ +uint16_t tcpip_continue_chksum ( uint16_t sum, const void *data, + size_t len ) { + intptr_t start; + intptr_t end; + intptr_t mid; + unsigned int pre; + unsigned int post; + unsigned int first; + uint64_t discard_low; + uint64_t discard_high; + + /* Avoid potentially undefined shift operation */ + if ( len == 0 ) + return sum; + + /* Find maximally-aligned midpoint. For short blocks of data, + * this may be aligned to fewer than 16 bytes. + */ + start = ( ( intptr_t ) data ); + end = ( start + len ); + mid = ( end & + ~( ( ~( 1UL << 63 ) ) >> ( 64 - flsl ( start ^ end ) ) ) ); + + /* Calculate pre- and post-alignment lengths */ + pre = ( ( mid - start ) & ( TCPIP_CHKSUM_ALIGN - 1 ) ); + post = ( ( end - mid ) & ( TCPIP_CHKSUM_ALIGN - 1 ) ); + + /* Calculate number of steps in first iteration of unrolled loop */ + first = ( ( ( len - pre - post ) / TCPIP_CHKSUM_ALIGN ) & + ( TCPIP_CHKSUM_UNROLL - 1 ) ); + + /* Calculate checksum */ + __asm__ ( /* Invert sum */ + "eor %w0, %w0, #0xffff\n\t" + /* Clear carry flag */ + "cmn xzr, xzr\n\t" + /* Byteswap and sum pre-alignment byte, if applicable */ + "tbz %w4, #0, 1f\n\t" + "ldrb %w2, [%1], #1\n\t" + "rev16 %w0, %w0\n\t" + "rev16 %w2, %w2\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum pre-alignment halfword, if applicable */ + "tbz %w4, #1, 1f\n\t" + "ldrh %w2, [%1], #2\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum pre-alignment word, if applicable */ + "tbz %w4, #2, 1f\n\t" + "ldr %w2, [%1], #4\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum pre-alignment doubleword, if applicable */ + "tbz %w4, #3, 1f\n\t" + "ldr %2, [%1], #8\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Jump into unrolled (x4) main loop */ + "adr %2, 2f\n\t" + "sub %2, %2, %5, lsl #3\n\t" + "sub %2, %2, %5, lsl #2\n\t" + "br %2\n\t" + "\n1:\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "ldp %2, %3, [%1], #16\n\t" + "adcs %0, %0, %2\n\t" + "adcs %0, %0, %3\n\t" + "\n2:\n\t" + "sub %2, %1, %6\n\t" + "cbnz %2, 1b\n\t" + /* Sum post-alignment doubleword, if applicable */ + "tbz %w7, #3, 1f\n\t" + "ldr %2, [%1], #8\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum post-alignment word, if applicable */ + "tbz %w7, #2, 1f\n\t" + "ldr %w2, [%1], #4\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum post-alignment halfword, if applicable */ + "tbz %w7, #1, 1f\n\t" + "ldrh %w2, [%1], #2\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Sum post-alignment byte, if applicable */ + "tbz %w7, #0, 1f\n\t" + "ldrb %w2, [%1], #1\n\t" + "adcs %0, %0, %2\n\t" + "\n1:\n\t" + /* Fold down to a uint32_t plus carry flag */ + "lsr %2, %0, #32\n\t" + "adcs %w0, %w0, %w2\n\t" + /* Fold down to a uint16_t plus carry in bit 16 */ + "ubfm %2, %0, #0, #15\n\t" + "ubfm %3, %0, #16, #31\n\t" + "adc %w0, %w2, %w3\n\t" + /* Fold down to a uint16_t */ + "tbz %w0, #16, 1f\n\t" + "mov %w2, #0xffff\n\t" + "sub %w0, %w0, %w2\n\t" + "tbz %w0, #16, 1f\n\t" + "sub %w0, %w0, %w2\n\t" + "\n1:\n\t" + /* Byteswap back, if applicable */ + "tbz %w4, #0, 1f\n\t" + "rev16 %w0, %w0\n\t" + "\n1:\n\t" + /* Invert sum */ + "eor %w0, %w0, #0xffff\n\t" + : "+r" ( sum ), "+r" ( data ), "=&r" ( discard_low ), + "=&r" ( discard_high ) + : "r" ( pre ), "r" ( first ), "r" ( end - post ), + "r" ( post ) + : "cc" ); + + return sum; +} diff --git a/src/arch/arm64/core/setjmp.S b/src/arch/arm64/core/setjmp.S new file mode 100644 index 000000000..fa47aa0af --- /dev/null +++ b/src/arch/arm64/core/setjmp.S @@ -0,0 +1,56 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + + /* Must match jmp_buf structure layout */ + .struct 0 +env_x19_x20: .quad 0, 0 +env_x21_x22: .quad 0, 0 +env_x23_x24: .quad 0, 0 +env_x25_x26: .quad 0, 0 +env_x27_x28: .quad 0, 0 +env_x29_x30: .quad 0, 0 +env_sp: .quad 0 + .previous + +/* + * Save stack context for non-local goto + */ + .globl setjmp + .type setjmp, %function +setjmp: + /* Store registers */ + stp x19, x20, [x0, #env_x19_x20] + stp x21, x22, [x0, #env_x21_x22] + stp x23, x24, [x0, #env_x23_x24] + stp x25, x26, [x0, #env_x25_x26] + stp x27, x28, [x0, #env_x27_x28] + stp x29, x30, [x0, #env_x29_x30] + mov x16, sp + str x16, [x0, #env_sp] + /* Return 0 when returning as setjmp() */ + mov x0, #0 + ret + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp + .type longjmp, %function +longjmp: + /* Restore registers */ + ldp x19, x20, [x0, #env_x19_x20] + ldp x21, x22, [x0, #env_x21_x22] + ldp x23, x24, [x0, #env_x23_x24] + ldp x25, x26, [x0, #env_x25_x26] + ldp x27, x28, [x0, #env_x27_x28] + ldp x29, x30, [x0, #env_x29_x30] + ldr x16, [x0, #env_sp] + mov sp, x16 + /* Force result to non-zero */ + cmp w1, #0 + csinc w0, w1, w1, ne + /* Return to setjmp() caller */ + br x30 + .size longjmp, . - longjmp diff --git a/src/arch/arm64/include/bits/bigint.h b/src/arch/arm64/include/bits/bigint.h new file mode 100644 index 000000000..79983b410 --- /dev/null +++ b/src/arch/arm64/include/bits/bigint.h @@ -0,0 +1,317 @@ +#ifndef _BITS_BIGINT_H +#define _BITS_BIGINT_H + +/** @file + * + * Big integer support + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Element of a big integer */ +typedef uint64_t bigint_element_t; + +/** + * Initialise big integer + * + * @v value0 Element 0 of big integer to initialise + * @v size Number of elements + * @v data Raw data + * @v len Length of raw data + */ +static inline __attribute__ (( always_inline )) void +bigint_init_raw ( uint64_t *value0, unsigned int size, + const void *data, size_t len ) { + size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + uint8_t *value_byte = ( ( void * ) value0 ); + const uint8_t *data_byte = ( data + len ); + + /* Copy raw data in reverse order, padding with zeros */ + while ( len-- ) + *(value_byte++) = *(--data_byte); + while ( pad_len-- ) + *(value_byte++) = 0; +} + +/** + * Add big integers + * + * @v addend0 Element 0 of big integer to add + * @v value0 Element 0 of big integer to be added to + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_add_raw ( const uint64_t *addend0, uint64_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_addend; + uint64_t *discard_value; + uint64_t discard_addend_i; + uint64_t discard_value_i; + unsigned int discard_size; + + __asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %3, [%0], #8\n\t" + "ldr %4, [%1]\n\t" + "adcs %4, %4, %3\n\t" + "str %4, [%1], #8\n\t" + "sub %w2, %w2, #1\n\t" + "cbnz %w2, 1b\n\t" + : "=r" ( discard_addend ), + "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_addend_i ), + "=r" ( discard_value_i ), + "+m" ( *value ) + : "0" ( addend0 ), "1" ( value0 ), "2" ( size ) + : "cc" ); +} + +/** + * Subtract big integers + * + * @v subtrahend0 Element 0 of big integer to subtract + * @v value0 Element 0 of big integer to be subtracted from + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_subtrahend; + uint64_t *discard_value; + uint64_t discard_subtrahend_i; + uint64_t discard_value_i; + unsigned int discard_size; + + __asm__ __volatile__ ( "cmp xzr, xzr\n\t" /* set CF */ + "\n1:\n\t" + "ldr %3, [%0], #8\n\t" + "ldr %4, [%1]\n\t" + "sbcs %4, %4, %3\n\t" + "str %4, [%1], #8\n\t" + "sub %w2, %w2, #1\n\t" + "cbnz %w2, 1b\n\t" + : "=r" ( discard_subtrahend ), + "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_subtrahend_i ), + "=r" ( discard_value_i ), + "+m" ( *value ) + : "0" ( subtrahend0 ), "1" ( value0 ), + "2" ( size ) + : "cc" ); +} + +/** + * Rotate big integer left + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_rol_raw ( uint64_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_value; + uint64_t discard_value_i; + unsigned int discard_size; + + __asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %2, [%0]\n\t" + "adcs %2, %2, %2\n\t" + "str %2, [%0], #8\n\t" + "sub %w1, %w1, #1\n\t" + "cbnz %w1, 1b\n\t" + : "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Rotate big integer right + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_ror_raw ( uint64_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint64_t *discard_value; + uint64_t discard_value_i; + uint64_t discard_value_j; + unsigned int discard_size; + + __asm__ __volatile__ ( "mov %3, #0\n\t" + "\n1:\n\t" + "sub %w1, %w1, #1\n\t" + "ldr %2, [%0, %1, lsl #3]\n\t" + "extr %3, %3, %2, #1\n\t" + "str %3, [%0, %1, lsl #3]\n\t" + "mov %3, %2\n\t" + "cbnz %w1, 1b\n\t" + : "=r" ( discard_value ), + "=r" ( discard_size ), + "=r" ( discard_value_i ), + "=r" ( discard_value_j ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) ); +} + +/** + * Test if big integer is equal to zero + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret is_zero Big integer is equal to zero + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_zero_raw ( const uint64_t *value0, unsigned int size ) { + const uint64_t *value = value0; + uint64_t value_i; + + do { + value_i = *(value++); + if ( value_i ) + break; + } while ( --size ); + + return ( value_i == 0 ); +} + +/** + * Compare big integers + * + * @v value0 Element 0 of big integer + * @v reference0 Element 0 of reference big integer + * @v size Number of elements + * @ret geq Big integer is greater than or equal to the reference + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_geq_raw ( const uint64_t *value0, const uint64_t *reference0, + unsigned int size ) { + const uint64_t *value = ( value0 + size ); + const uint64_t *reference = ( reference0 + size ); + uint64_t value_i; + uint64_t reference_i; + + do { + value_i = *(--value); + reference_i = *(--reference); + if ( value_i != reference_i ) + break; + } while ( --size ); + + return ( value_i >= reference_i ); +} + +/** + * Test if bit is set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to test + * @ret is_set Bit is set + */ +static inline __attribute__ (( always_inline )) int +bigint_bit_is_set_raw ( const uint64_t *value0, unsigned int size, + unsigned int bit ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( const void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + return ( !! ( value->element[index] & ( 1UL << subindex ) ) ); +} + +/** + * Find highest bit set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret max_bit Highest bit set + 1 (or 0 if no bits set) + */ +static inline __attribute__ (( always_inline )) int +bigint_max_set_bit_raw ( const uint64_t *value0, unsigned int size ) { + const uint64_t *value = ( value0 + size ); + int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) ); + uint64_t value_i; + + do { + value_i = *(--value); + max_bit -= ( 64 - fls ( value_i ) ); + if ( value_i ) + break; + } while ( --size ); + + return max_bit; +} + +/** + * Grow big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_grow_raw ( const uint64_t *source0, unsigned int source_size, + uint64_t *dest0, unsigned int dest_size ) { + unsigned int pad_size = ( dest_size - source_size ); + + memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) ); + memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) ); +} + +/** + * Shrink big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_shrink_raw ( const uint64_t *source0, unsigned int source_size __unused, + uint64_t *dest0, unsigned int dest_size ) { + + memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) ); +} + +/** + * Finalise big integer + * + * @v value0 Element 0 of big integer to finalise + * @v size Number of elements + * @v out Output buffer + * @v len Length of output buffer + */ +static inline __attribute__ (( always_inline )) void +bigint_done_raw ( const uint64_t *value0, unsigned int size __unused, + void *out, size_t len ) { + const uint8_t *value_byte = ( ( const void * ) value0 ); + uint8_t *out_byte = ( out + len ); + + /* Copy raw data in reverse order */ + while ( len-- ) + *(--out_byte) = *(value_byte++); +} + +extern void bigint_multiply_raw ( const uint64_t *multiplicand0, + const uint64_t *multiplier0, + uint64_t *value0, unsigned int size ); + +#endif /* _BITS_BIGINT_H */ diff --git a/src/arch/arm64/include/bits/bitops.h b/src/arch/arm64/include/bits/bitops.h new file mode 100644 index 000000000..4350f622a --- /dev/null +++ b/src/arch/arm64/include/bits/bitops.h @@ -0,0 +1,100 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * ARM bit operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 64 ); + unsigned int offset = ( bit % 64 ); + volatile uint64_t *qword = ( ( ( volatile uint64_t * ) bits ) + index ); + uint64_t mask = ( 1UL << offset ); + uint64_t old; + uint64_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldxr %0, %3\n\t" + "orr %1, %0, %4\n\t" + "stxr %w2, %1, %3\n\t" + "tst %w2, %w2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&r" ( flag ), + "+Q" ( *qword ) + : "r" ( mask ) + : "cc" ); + + return ( !! ( old & mask ) ); +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 64 ); + unsigned int offset = ( bit % 64 ); + volatile uint64_t *qword = ( ( ( volatile uint64_t * ) bits ) + index ); + uint64_t mask = ( 1UL << offset ); + uint64_t old; + uint64_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldxr %0, %3\n\t" + "bic %1, %0, %4\n\t" + "stxr %w2, %1, %3\n\t" + "tst %w2, %w2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&r" ( flag ), + "+Q" ( *qword ) + : "r" ( mask ) + : "cc" ); + + return ( !! ( old & mask ) ); +} + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + + test_and_set_bit ( bit, bits ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + + test_and_clear_bit ( bit, bits ); +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/arch/arm64/include/bits/byteswap.h b/src/arch/arm64/include/bits/byteswap.h new file mode 100644 index 000000000..169d6c20e --- /dev/null +++ b/src/arch/arm64/include/bits/byteswap.h @@ -0,0 +1,47 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +/** @file + * + * Byte-order swapping functions + * + */ + +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "rev16 %0, %1" : "=r" ( x ) : "r" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_16s ( uint16_t *x ) { + *x = __bswap_variable_16 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "rev32 %0, %1" : "=r" ( x ) : "r" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_32s ( uint32_t *x ) { + *x = __bswap_variable_32 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + __asm__ ( "rev %0, %1" : "=r" ( x ) : "r" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_64s ( uint64_t *x ) { + *x = __bswap_variable_64 ( *x ); +} + +#endif diff --git a/src/arch/arm64/include/bits/compiler.h b/src/arch/arm64/include/bits/compiler.h new file mode 100644 index 000000000..3b129c2fd --- /dev/null +++ b/src/arch/arm64/include/bits/compiler.h @@ -0,0 +1,16 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_AARCH64_NULL + +#ifndef ASSEMBLY + +#define __asmcall +#define __libgcc + +#endif /* ASSEMBLY */ + +#endif /*_BITS_COMPILER_H */ diff --git a/src/arch/arm64/include/bits/profile.h b/src/arch/arm64/include/bits/profile.h new file mode 100644 index 000000000..62ffa3772 --- /dev/null +++ b/src/arch/arm64/include/bits/profile.h @@ -0,0 +1,28 @@ +#ifndef _BITS_PROFILE_H +#define _BITS_PROFILE_H + +/** @file + * + * Profiling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) uint64_t +profile_timestamp ( void ) { + uint64_t cycles; + + /* Read cycle counter */ + __asm__ __volatile__ ( "mrs %0, CNTVCT_EL0\n\t" : "=r" ( cycles ) ); + return cycles; +} + +#endif /* _BITS_PROFILE_H */ diff --git a/src/arch/arm64/include/bits/stdint.h b/src/arch/arm64/include/bits/stdint.h new file mode 100644 index 000000000..9eb72e9c4 --- /dev/null +++ b/src/arch/arm64/include/bits/stdint.h @@ -0,0 +1,21 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +typedef __SIZE_TYPE__ size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/src/arch/arm64/include/bits/string.h b/src/arch/arm64/include/bits/string.h new file mode 100644 index 000000000..c05fbe346 --- /dev/null +++ b/src/arch/arm64/include/bits/string.h @@ -0,0 +1,106 @@ +#ifndef BITS_STRING_H +#define BITS_STRING_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * String functions + * + */ + +extern void arm64_bzero ( void *dest, size_t len ); +extern void arm64_memset ( void *dest, size_t len, int character ); +extern void arm64_memcpy ( void *dest, const void *src, size_t len ); +extern void arm64_memmove_forwards ( void *dest, const void *src, size_t len ); +extern void arm64_memmove_backwards ( void *dest, const void *src, size_t len ); +extern void arm64_memmove ( void *dest, const void *src, size_t len ); + +/** + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int character, size_t len ) { + + /* Allow gcc to generate inline "stX xzr" instructions for + * small, constant lengths. + */ + if ( __builtin_constant_p ( character ) && ( character == 0 ) && + __builtin_constant_p ( len ) && ( len <= 64 ) ) { + __builtin_memset ( dest, 0, len ); + return dest; + } + + /* For zeroing larger or non-constant lengths, use the + * optimised variable-length zeroing code. + */ + if ( __builtin_constant_p ( character ) && ( character == 0 ) ) { + arm64_bzero ( dest, len ); + return dest; + } + + /* Not necessarily zeroing: use basic variable-length code */ + arm64_memset ( dest, len, character ); + return dest; +} + +/** + * Copy memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memcpy ( void *dest, const void *src, size_t len ) { + + /* Allow gcc to generate inline "ldX"/"stX" instructions for + * small, constant lengths. + */ + if ( __builtin_constant_p ( len ) && ( len <= 64 ) ) { + __builtin_memcpy ( dest, src, len ); + return dest; + } + + /* Otherwise, use variable-length code */ + arm64_memcpy ( dest, src, len ); + return dest; +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memmove ( void *dest, const void *src, size_t len ) { + ssize_t offset = ( dest - src ); + + /* If required direction of copy is known at build time, then + * use the appropriate forwards/backwards copy directly. + */ + if ( __builtin_constant_p ( offset ) ) { + if ( offset <= 0 ) { + arm64_memmove_forwards ( dest, src, len ); + return dest; + } else { + arm64_memmove_backwards ( dest, src, len ); + return dest; + } + } + + /* Otherwise, use ambidirectional copy */ + arm64_memmove ( dest, src, len ); + return dest; +} + +#endif /* BITS_STRING_H */ diff --git a/src/arch/arm64/include/bits/strings.h b/src/arch/arm64/include/bits/strings.h new file mode 100644 index 000000000..d5340f484 --- /dev/null +++ b/src/arch/arm64/include/bits/strings.h @@ -0,0 +1,69 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long long bits = value; + unsigned long long lsb; + unsigned int lz; + + /* Extract least significant set bit */ + lsb = ( bits & -bits ); + + /* Count number of leading zeroes before LSB */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) ); + + return ( 64 - lz ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + + return __ffsll ( value ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned int lz; + + /* Count number of leading zeroes */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) ); + + return ( 64 - lz ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + + return __flsll ( value ); +} + +#endif /* _BITS_STRINGS_H */ diff --git a/src/arch/arm64/include/bits/tcpip.h b/src/arch/arm64/include/bits/tcpip.h new file mode 100644 index 000000000..68686534e --- /dev/null +++ b/src/arch/arm64/include/bits/tcpip.h @@ -0,0 +1,15 @@ +#ifndef _BITS_TCPIP_H +#define _BITS_TCPIP_H + +/** @file + * + * Transport-network layer interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern uint16_t tcpip_continue_chksum ( uint16_t sum, const void *data, + size_t len ); + +#endif /* _BITS_TCPIP_H */ diff --git a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h new file mode 100644 index 000000000..bb26aae4d --- /dev/null +++ b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM64 + +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ + +#endif diff --git a/src/arch/arm64/include/gdbmach.h b/src/arch/arm64/include/gdbmach.h new file mode 100644 index 000000000..cd152eedd --- /dev/null +++ b/src/arch/arm64/include/gdbmach.h @@ -0,0 +1,45 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include + +typedef unsigned long gdbreg_t; + +/* Register snapshot */ +enum { + /* Not yet implemented */ + GDBMACH_NREGS, +}; + +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) step; +} + +static inline void gdbmach_breakpoint ( void ) { + /* Not yet implemented */ +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); +extern void gdbmach_init ( void ); + +#endif /* GDBMACH_H */ diff --git a/src/arch/arm64/include/limits.h b/src/arch/arm64/include/limits.h new file mode 100644 index 000000000..8cf87b471 --- /dev/null +++ b/src/arch/arm64/include/limits.h @@ -0,0 +1,59 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 9223372036854775807L +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 18446744073709551615UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/arm64/include/setjmp.h b/src/arch/arm64/include/setjmp.h new file mode 100644 index 000000000..85a7a9cad --- /dev/null +++ b/src/arch/arm64/include/setjmp.h @@ -0,0 +1,44 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A jump buffer */ +typedef struct { + /** Saved x19 */ + uint64_t x19; + /** Saved x20 */ + uint64_t x20; + /** Saved x21 */ + uint64_t x21; + /** Saved x22 */ + uint64_t x22; + /** Saved x23 */ + uint64_t x23; + /** Saved x24 */ + uint64_t x24; + /** Saved x25 */ + uint64_t x25; + /** Saved x26 */ + uint64_t x26; + /** Saved x27 */ + uint64_t x27; + /** Saved x28 */ + uint64_t x28; + /** Saved frame pointer (x29) */ + uint64_t x29; + /** Saved link register (x30) */ + uint64_t x30; + /** Saved stack pointer (x31) */ + uint64_t sp; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 7f706a5a4..b7c2792d9 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -74,49 +74,21 @@ 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 -# Define version string for lkrnprefix.S -# -CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" - -# Locations of isolinux files -# -SYSLINUX_DIR_LIST := \ - /usr/lib/syslinux \ - /usr/lib/syslinux/bios \ - /usr/lib/syslinux/modules/bios \ - /usr/share/syslinux \ - /usr/share/syslinux/bios \ - /usr/share/syslinux/modules/bios \ - /usr/local/share/syslinux \ - /usr/local/share/syslinux/bios \ - /usr/local/share/syslinux/modules/bios \ - /usr/lib/ISOLINUX -ISOLINUX_BIN_LIST := \ - $(ISOLINUX_BIN) \ - $(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST)) -LDLINUX_C32_LIST := \ - $(LDLINUX_C32) \ - $(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST)) -ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) -LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST))) - # i386-specific directories containing source files # -SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix -SRCDIRS += arch/i386/firmware/pcbios -SRCDIRS += arch/i386/image -SRCDIRS += arch/i386/interface/pcbios -SRCDIRS += arch/i386/interface/pxe -SRCDIRS += arch/i386/interface/pxeparent -SRCDIRS += arch/i386/interface/syslinux -SRCDIRS += arch/i386/interface/vmware -SRCDIRS += arch/i386/hci/commands +SRCDIRS += arch/i386/core +SRCDIRS += arch/i386/tests # Include common x86 Makefile # diff --git a/src/arch/i386/Makefile.efi b/src/arch/i386/Makefile.efi index aa809eb5d..37ede65ac 100644 --- a/src/arch/i386/Makefile.efi +++ b/src/arch/i386/Makefile.efi @@ -8,6 +8,10 @@ ELF2EFI = $(ELF2EFI32) # CFLAGS += -malign-double +# Specify EFI boot file +# +EFI_BOOT_FILE = bootia32.efi + # Include generic EFI Makefile # MAKEDEPS += arch/x86/Makefile.efi diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios index 02952f81b..dfb8db0a0 100644 --- a/src/arch/i386/Makefile.pcbios +++ b/src/arch/i386/Makefile.pcbios @@ -1,101 +1,6 @@ # -*- makefile -*- : Force emacs to use Makefile mode -# The i386 linker script +# Include generic BIOS Makefile # -LDSCRIPT = arch/i386/scripts/i386.lds - -# Stop ld from complaining about our customised linker script -# -LDFLAGS += -N --no-check-sections - -# pcbios specific drivers -SRCDIRS += arch/i386/drivers -SRCDIRS += arch/i386/drivers/net - -# Media types. -# -MEDIA += rom -MEDIA += mrom -MEDIA += pcirom -MEDIA += isarom -MEDIA += pxe -MEDIA += kpxe -MEDIA += kkpxe -MEDIA += kkkpxe -MEDIA += lkrn -MEDIA += dsk -MEDIA += nbi -MEDIA += hd -MEDIA += raw -MEDIA += exe - -# Padding rules -# -PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff -PAD_mrom = $(PAD_rom) -PAD_pcirom = $(PAD_rom) -PAD_isarom = $(PAD_rom) -PAD_dsk = $(PERL) $(PADIMG) --blksize=512 -PAD_hd = $(PERL) $(PADIMG) --blksize=32768 -PAD_exe = $(PERL) $(PADIMG) --blksize=512 - -# Finalisation rules -# -FINALISE_rom = $(PERL) $(FIXROM) -FINALISE_mrom = $(FINALISE_rom) -FINALISE_pcirom = $(FINALISE_rom) -FINALISE_isarom = $(FINALISE_rom) - -# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc. -# -LIST_NAME_rom := ROMS -LIST_NAME_mrom := ROMS -LIST_NAME_pcirom := ROMS -LIST_NAME_isarom := ROMS - -# rule to make a non-emulation ISO boot image -NON_AUTO_MEDIA += iso -%iso: %lkrn util/geniso - $(QM)$(ECHO) " [GENISO] $@" - $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) LDLINUX_C32=$(LDLINUX_C32) \ - VERSION="$(VERSION)" bash util/geniso -o $@ $< - -# rule to make a floppy emulation ISO boot image -NON_AUTO_MEDIA += liso -%liso: %lkrn util/geniso - $(QM)$(ECHO) " [GENISO] $@" - $(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $< - -# rule to make a syslinux floppy image (mountable, bootable) -NON_AUTO_MEDIA += sdsk -%sdsk: %lkrn util/gensdsk - $(QM)$(ECHO) " [GENSDSK] $@" - $(Q)bash util/gensdsk $@ $< - -# rule to write disk images to /dev/fd0 -NON_AUTO_MEDIA += fd0 -%fd0 : %dsk - $(QM)$(ECHO) " [DD] $@" - $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0 - $(Q)sync - -# Special target for building Master Boot Record binary -$(BIN)/mbr.bin : $(BIN)/mbr.o - $(QM)$(ECHO) " [OBJCOPY] $@" - $(Q)$(OBJCOPY) -O binary $< $@ - -# rule to make a USB disk image -$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o - $(QM)$(ECHO) " [OBJCOPY] $@" - $(Q)$(OBJCOPY) -O binary $< $@ - -NON_AUTO_MEDIA += usb -%usb: $(BIN)/usbdisk.bin %hd - $(QM)$(ECHO) " [FINISH] $@" - $(Q)cat $^ > $@ - -# Padded floppy image (e.g. for iLO) -NON_AUTO_MEDIA += pdsk -%pdsk : %dsk - $(Q)cp $< $@ - $(Q)$(PADIMG) --blksize=1474560 $@ +MAKEDEPS += arch/x86/Makefile.pcbios +include arch/x86/Makefile.pcbios diff --git a/src/arch/i386/core/gdbidt.S b/src/arch/i386/core/gdbidt.S index a1e309d7c..666ecce3c 100644 --- a/src/arch/i386/core/gdbidt.S +++ b/src/arch/i386/core/gdbidt.S @@ -15,41 +15,29 @@ /* POSIX signal numbers for reporting traps to GDB */ #define SIGILL 4 #define SIGTRAP 5 -#define SIGBUS 7 #define SIGFPE 8 -#define SIGSEGV 11 #define SIGSTKFLT 16 - .globl gdbmach_nocode_sigfpe -gdbmach_nocode_sigfpe: + .globl gdbmach_sigfpe +gdbmach_sigfpe: pushl $SIGFPE jmp gdbmach_interrupt - .globl gdbmach_nocode_sigtrap -gdbmach_nocode_sigtrap: + .globl gdbmach_sigtrap +gdbmach_sigtrap: pushl $SIGTRAP jmp gdbmach_interrupt - .globl gdbmach_nocode_sigstkflt -gdbmach_nocode_sigstkflt: + .globl gdbmach_sigstkflt +gdbmach_sigstkflt: pushl $SIGSTKFLT jmp gdbmach_interrupt - .globl gdbmach_nocode_sigill -gdbmach_nocode_sigill: + .globl gdbmach_sigill +gdbmach_sigill: pushl $SIGILL jmp gdbmach_interrupt - .globl gdbmach_withcode_sigbus -gdbmach_withcode_sigbus: - movl $SIGBUS, (%esp) - jmp gdbmach_interrupt - - .globl gdbmach_withcode_sigsegv -gdbmach_withcode_sigsegv: - movl $SIGSEGV, (%esp) - jmp gdbmach_interrupt - /* When invoked, the stack contains: eflags, cs, eip, signo. */ #define IH_OFFSET_GDB_REGS ( 0 ) #define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS ) diff --git a/src/arch/i386/core/gdbmach.c b/src/arch/i386/core/gdbmach.c deleted file mode 100644 index d92a4ac08..000000000 --- a/src/arch/i386/core/gdbmach.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2008 Stefan Hajnoczi . - * - * 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 ); - -#include -#include -#include -#include -#include -#include -#include - -/** @file - * - * GDB architecture-specific bits for i386 - * - */ - -enum { - DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */ - DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */ -}; - -/** Hardware breakpoint, fields stored in x86 bit pattern form */ -struct hwbp { - int type; /* type (1=write watchpoint, 3=access watchpoint) */ - unsigned long addr; /* linear address */ - size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */ - int enabled; -}; - -static struct hwbp hwbps [ 4 ]; -static gdbreg_t dr7 = DR7_CLEAR; - -static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { - struct hwbp *available = NULL; - unsigned int i; - for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { - if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { - return &hwbps [ i ]; - } - if ( !hwbps [ i ].enabled ) { - available = &hwbps [ i ]; - } - } - return available; -} - -static void gdbmach_commit_hwbp ( struct hwbp *bp ) { - unsigned int regnum = bp - hwbps; - - /* Set breakpoint address */ - assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) ); - switch ( regnum ) { - case 0: - __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); - break; - case 1: - __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); - break; - case 2: - __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); - break; - case 3: - __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); - break; - } - - /* Set type */ - dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); - dr7 |= bp->type << ( 16 + 4 * regnum ); - - /* Set length */ - dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); - dr7 |= bp->len << ( 18 + 4 * regnum ); - - /* Set/clear local enable bit */ - dr7 &= ~( 0x3 << 2 * regnum ); - dr7 |= bp->enabled << 2 * regnum; -} - -int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { - struct hwbp *bp; - - /* Check and convert breakpoint type to x86 type */ - switch ( type ) { - case GDBMACH_WATCH: - type = 0x1; - break; - case GDBMACH_AWATCH: - type = 0x3; - break; - default: - return 0; /* unsupported breakpoint type */ - } - - /* Only lengths 1, 2, and 4 are supported */ - if ( len != 2 && len != 4 ) { - len = 1; - } - len--; /* convert to x86 breakpoint length bit pattern */ - - /* Calculate linear address by adding segment base */ - addr += virt_offset; - - /* Set up the breakpoint */ - bp = gdbmach_find_hwbp ( type, addr, len ); - if ( !bp ) { - return 0; /* ran out of hardware breakpoints */ - } - bp->type = type; - bp->addr = addr; - bp->len = len; - bp->enabled = enable; - gdbmach_commit_hwbp ( bp ); - return 1; -} - -static void gdbmach_disable_hwbps ( void ) { - /* Store and clear hardware breakpoints */ - __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); -} - -static void gdbmach_enable_hwbps ( void ) { - /* Clear breakpoint status register */ - __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) ); - - /* Restore hardware breakpoints */ - __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); -} - -__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { - gdbmach_disable_hwbps(); - gdbstub_handler ( signo, regs ); - gdbmach_enable_hwbps(); -} - -static void * gdbmach_interrupt_vectors[] = { - gdbmach_nocode_sigfpe, /* Divide by zero */ - gdbmach_nocode_sigtrap, /* Debug trap */ - NULL, /* Non-maskable interrupt */ - gdbmach_nocode_sigtrap, /* Breakpoint */ - gdbmach_nocode_sigstkflt, /* Overflow */ - gdbmach_nocode_sigstkflt, /* Bound range exceeded */ - gdbmach_nocode_sigill, /* Invalid opcode */ - NULL, /* Device not available */ - gdbmach_withcode_sigbus, /* Double fault */ - NULL, /* Coprocessor segment overrun */ - gdbmach_withcode_sigsegv, /* Invalid TSS */ - gdbmach_withcode_sigsegv, /* Segment not present */ - gdbmach_withcode_sigsegv, /* Stack segment fault */ - gdbmach_withcode_sigsegv, /* General protection fault */ - gdbmach_withcode_sigsegv, /* Page fault */ -}; - -void gdbmach_init ( void ) { - unsigned int i; - - for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) / - sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) { - set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] ); - } -} diff --git a/src/arch/i386/core/rdtsc_timer.c b/src/arch/i386/core/rdtsc_timer.c deleted file mode 100644 index e720a239c..000000000 --- a/src/arch/i386/core/rdtsc_timer.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown . - * - * 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 - * - * RDTSC timer - * - */ - -#include -#include -#include - -/** - * Number of TSC ticks per microsecond - * - * This is calibrated on the first use of the timer. - */ -static unsigned long rdtsc_ticks_per_usec; - -/** - * Delay for a fixed number of microseconds - * - * @v usecs Number of microseconds for which to delay - */ -static void rdtsc_udelay ( unsigned long usecs ) { - unsigned long start; - unsigned long elapsed; - - /* Sanity guard, since we may divide by this */ - if ( ! usecs ) - usecs = 1; - - start = currticks(); - if ( rdtsc_ticks_per_usec ) { - /* Already calibrated; busy-wait until done */ - do { - elapsed = ( currticks() - start ); - } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); - } else { - /* Not yet calibrated; use 8254 PIT and calibrate - * based on result. - */ - pit8254_udelay ( usecs ); - elapsed = ( currticks() - start ); - rdtsc_ticks_per_usec = ( elapsed / usecs ); - DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " - "(%ld MHz)\n", elapsed, usecs, - ( rdtsc_ticks_per_usec << TSC_SHIFT ) ); - } -} - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -static unsigned long rdtsc_ticks_per_sec ( void ) { - - /* Calibrate timer, if not already done */ - if ( ! rdtsc_ticks_per_usec ) - udelay ( 1 ); - - /* Sanity check */ - assert ( rdtsc_ticks_per_usec != 0 ); - - return ( rdtsc_ticks_per_usec * 1000 * 1000 ); -} - -PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay ); -PROVIDE_TIMER_INLINE ( rdtsc, currticks ); -PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec ); diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/i386/core/virtaddr.S deleted file mode 100644 index 425591570..000000000 --- a/src/arch/i386/core/virtaddr.S +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Functions to support the virtual addressing method of relocation - * that Etherboot uses. - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) - -#include "librm.h" - - .arch i386 - .text - .code32 - -/**************************************************************************** - * _virt_to_phys (virtual addressing) - * - * Switch from virtual to flat physical addresses. %esp is adjusted - * to a physical value. Segment registers are set to flat physical - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _virt_to_phys -_virt_to_phys: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Change return address to a physical address */ - movl virt_offset, %ebp - addl %ebp, 12(%esp) - - /* Switch to physical code segment */ - cli - pushl $PHYSICAL_CS - leal 1f(%ebp), %eax - pushl %eax - lret -1: - /* Reload other segment registers and adjust %esp */ - movl $PHYSICAL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - movl %eax, %ss - addl %ebp, %esp - - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret - -/**************************************************************************** - * _phys_to_virt (flat physical addressing) - * - * Switch from flat physical to virtual addresses. %esp is adjusted - * to a virtual value. Segment registers are set to virtual - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _phys_to_virt -_phys_to_virt: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Switch to virtual code segment */ - cli - ljmp $VIRTUAL_CS, $1f -1: - /* Reload data segment registers */ - movl $VIRTUAL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - - /* Reload stack segment and adjust %esp */ - movl virt_offset, %ebp - movl %eax, %ss - subl %ebp, %esp - - /* Change the return address to a virtual address */ - subl %ebp, 12(%esp) - - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret - -/**************************************************************************** - * _intr_to_virt (virtual code segment, virtual or physical stack segment) - * - * Switch from virtual code segment with either a virtual or physical - * stack segment to using virtual addressing. %esp is adjusted if - * necessary to a virtual value. Segment registers are set to virtual - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _intr_to_virt -_intr_to_virt: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Check whether stack segment is physical or virtual */ - movl %ss, %eax - cmpw $VIRTUAL_DS, %ax - movl $VIRTUAL_DS, %eax - - /* Reload data segment registers */ - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - - /* Reload stack segment and adjust %esp if necessary */ - je 1f - movl virt_offset, %ebp - movl %eax, %ss - subl %ebp, %esp -1: - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret diff --git a/src/arch/i386/include/bits/compiler.h b/src/arch/i386/include/bits/compiler.h index 87201135f..7c4a09396 100644 --- a/src/arch/i386/include/bits/compiler.h +++ b/src/arch/i386/include/bits/compiler.h @@ -9,7 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ -#define __asmcall __attribute__ (( cdecl, regparm(0) )) +#define __asmcall __attribute__ (( used, cdecl, regparm(0) )) /** * Declare a function with libgcc implicit linkage diff --git a/src/arch/i386/include/bits/hyperv.h b/src/arch/i386/include/bits/hyperv.h index 3565c8a83..0ba58afb7 100644 --- a/src/arch/i386/include/bits/hyperv.h +++ b/src/arch/i386/include/bits/hyperv.h @@ -46,27 +46,4 @@ hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, return result; } -/** - * Set bit atomically - * - * @v bits Bit field - * @v bit Bit to set - */ -static inline __attribute__ (( always_inline )) void -hv_set_bit ( void *bits, unsigned int bit ) { - struct { - uint32_t dword[ ( bit / 32 ) + 1 ]; - } *dwords = bits; - - /* Set bit using "lock bts". Inform compiler that any memory - * from the start of the bit field up to and including the - * dword containing this bit may be modified. (This is - * overkill but shouldn't matter in practice since we're - * unlikely to subsequently read other bits from the same bit - * field.) - */ - __asm__ __volatile__ ( "lock bts %1, %0" - : "+m" ( *dwords ) : "Ir" ( bit ) ); -} - #endif /* _BITS_HYPERV_H */ diff --git a/src/arch/i386/include/bits/timer.h b/src/arch/i386/include/bits/timer.h deleted file mode 100644 index f7d86d78c..000000000 --- a/src/arch/i386/include/bits/timer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _BITS_TIMER_H -#define _BITS_TIMER_H - -/** @file - * - * i386-specific timer API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include - -#endif /* _BITS_TIMER_H */ diff --git a/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/src/arch/i386/include/efi/ipxe/dhcp_arch.h index c17c1ea5e..cf3dbe499 100644 --- a/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -33,14 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '6', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_IA32 -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_IA32 ) - -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ #endif diff --git a/src/arch/i386/include/gdbmach.h b/src/arch/i386/include/gdbmach.h index 416ae341a..52cce7833 100644 --- a/src/arch/i386/include/gdbmach.h +++ b/src/arch/i386/include/gdbmach.h @@ -47,12 +47,10 @@ enum { }; /* Interrupt vectors */ -extern void gdbmach_nocode_sigfpe ( void ); -extern void gdbmach_nocode_sigtrap ( void ); -extern void gdbmach_nocode_sigstkflt ( void ); -extern void gdbmach_nocode_sigill ( void ); -extern void gdbmach_withcode_sigbus ( void ); -extern void gdbmach_withcode_sigsegv ( void ); +extern void gdbmach_sigfpe ( void ); +extern void gdbmach_sigtrap ( void ); +extern void gdbmach_sigstkflt ( void ); +extern void gdbmach_sigill ( void ); static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { regs [ GDBMACH_EIP ] = pc; diff --git a/src/arch/i386/include/ipxe/bios_timer.h b/src/arch/i386/include/ipxe/bios_timer.h deleted file mode 100644 index 6b88a623c..000000000 --- a/src/arch/i386/include/ipxe/bios_timer.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _IPXE_BIOS_TIMER_H -#define _IPXE_BIOS_TIMER_H - -/** @file - * - * BIOS timer - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_PCBIOS -#define TIMER_PREFIX_pcbios -#else -#define TIMER_PREFIX_pcbios __pcbios_ -#endif - -#include - -/** - * Delay for a fixed number of microseconds - * - * @v usecs Number of microseconds for which to delay - */ -static inline __always_inline void -TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) { - /* BIOS timer is not high-resolution enough for udelay(), so - * we use the 8254 Programmable Interval Timer. - */ - pit8254_udelay ( usecs ); -} - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -static inline __always_inline unsigned long -TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) { - /* BIOS timer ticks over at 18.2 ticks per second */ - return 18; -} - -#endif /* _IPXE_BIOS_TIMER_H */ diff --git a/src/arch/i386/include/ipxe/rdtsc_timer.h b/src/arch/i386/include/ipxe/rdtsc_timer.h deleted file mode 100644 index 598f4bb08..000000000 --- a/src/arch/i386/include/ipxe/rdtsc_timer.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _IPXE_RDTSC_TIMER_H -#define _IPXE_RDTSC_TIMER_H - -/** @file - * - * RDTSC timer - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_RDTSC -#define TIMER_PREFIX_rdtsc -#else -#define TIMER_PREFIX_rdtsc __rdtsc_ -#endif - -/** - * RDTSC values can easily overflow an unsigned long. We discard the - * low-order bits in order to obtain sensibly-scaled values. - */ -#define TSC_SHIFT 8 - -/** - * Get current system time in ticks - * - * @ret ticks Current time, in ticks - */ -static inline __always_inline unsigned long -TIMER_INLINE ( rdtsc, currticks ) ( void ) { - unsigned long ticks; - - __asm__ __volatile__ ( "rdtsc\n\t" - "shrdl %1, %%edx, %%eax\n\t" - : "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" ); - return ticks; -} - -#endif /* _IPXE_RDTSC_TIMER_H */ diff --git a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h index e07e4c192..e22f50b37 100644 --- a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -33,14 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86 ) - -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ #endif diff --git a/src/arch/i386/include/pxeparent.h b/src/arch/i386/include/pxeparent.h deleted file mode 100644 index b31e24a76..000000000 --- a/src/arch/i386/include/pxeparent.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef PXEPARENT_H -#define PXEPARENT_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include - -extern int pxeparent_call ( SEGOFF16_t entry, unsigned int function, - void *params, size_t params_len ); - -#endif diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/setjmp.h index fe1a9ef4d..98566696a 100644 --- a/src/arch/i386/include/setjmp.h +++ b/src/arch/i386/include/setjmp.h @@ -4,7 +4,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#include /** A jump buffer */ typedef struct { @@ -22,29 +21,10 @@ typedef struct { uint32_t ebp; } jmp_buf[1]; -/** A real-mode-extended jump buffer */ -typedef struct { - /** Jump buffer */ - jmp_buf env; - /** Real-mode stack pointer */ - segoff_t rm_stack; -} rmjmp_buf[1]; - extern int __asmcall __attribute__ (( returns_twice )) setjmp ( jmp_buf env ); extern void __asmcall __attribute__ (( noreturn )) longjmp ( jmp_buf env, int val ); -#define rmsetjmp( _env ) ( { \ - (_env)->rm_stack.segment = rm_ss; \ - (_env)->rm_stack.offset = rm_sp; \ - setjmp ( (_env)->env ); } ) \ - -#define rmlongjmp( _env, _val ) do { \ - rm_ss = (_env)->rm_stack.segment; \ - rm_sp = (_env)->rm_stack.offset; \ - longjmp ( (_env)->env, (_val) ); \ - } while ( 0 ) - #endif /* _SETJMP_H */ diff --git a/src/arch/i386/interface/pxeparent/pxeparent.c b/src/arch/i386/interface/pxeparent/pxeparent.c deleted file mode 100644 index 0b6be9a03..000000000 --- a/src/arch/i386/interface/pxeparent/pxeparent.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * 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 -#include -#include -#include -#include -#include - -/** @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; - unsigned long started; - unsigned long stopped; - 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 ); - profile_start_at ( &profiler->p2r, profile_started ( &profiler->total)); - 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, profile_stopped ( &profiler->total )); - - /* 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; -} - diff --git a/src/tests/gdbstub_test.S b/src/arch/i386/tests/gdbstub_test.S similarity index 100% rename from src/tests/gdbstub_test.S rename to src/arch/i386/tests/gdbstub_test.S diff --git a/src/tests/gdbstub_test.gdb b/src/arch/i386/tests/gdbstub_test.gdb similarity index 100% rename from src/tests/gdbstub_test.gdb rename to src/arch/i386/tests/gdbstub_test.gdb diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S deleted file mode 100644 index 863e22415..000000000 --- a/src/arch/i386/transitions/librm.S +++ /dev/null @@ -1,671 +0,0 @@ -/* - * librm: a library for interfacing to real-mode code - * - * Michael Brown - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) - -/* Drag in local definitions */ -#include "librm.h" - -/* For switches to/from protected mode */ -#define CR0_PE 1 - -/* Size of various C data structures */ -#define SIZEOF_I386_SEG_REGS 12 -#define SIZEOF_I386_REGS 32 -#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS ) -#define SIZEOF_I386_FLAGS 4 -#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS ) - - .arch i386 - -/**************************************************************************** - * Global descriptor table - * - * Call init_librm to set up the GDT before attempting to use any - * protected-mode code. - * - * NOTE: This must be located before prot_to_real, otherwise gas - * throws a "can't handle non absolute segment in `ljmp'" error due to - * not knowing the value of REAL_CS when the ljmp is encountered. - * - * Note also that putting ".word gdt_end - gdt - 1" directly into - * gdt_limit, rather than going via gdt_length, will also produce the - * "non absolute segment" error. This is most probably a bug in gas. - **************************************************************************** - */ - .section ".data16", "aw", @progbits - .align 16 -gdt: -gdtr: /* The first GDT entry is unused, the GDTR can fit here. */ -gdt_limit: .word gdt_length - 1 -gdt_base: .long 0 - .word 0 /* padding */ - - .org gdt + VIRTUAL_CS, 0 -virtual_cs: /* 32 bit protected mode code segment, virtual addresses */ - .word 0xffff, 0 - .byte 0, 0x9f, 0xcf, 0 - - .org gdt + VIRTUAL_DS, 0 -virtual_ds: /* 32 bit protected mode data segment, virtual addresses */ - .word 0xffff, 0 - .byte 0, 0x93, 0xcf, 0 - - .org gdt + PHYSICAL_CS, 0 -physical_cs: /* 32 bit protected mode code segment, physical addresses */ - .word 0xffff, 0 - .byte 0, 0x9f, 0xcf, 0 - - .org gdt + PHYSICAL_DS, 0 -physical_ds: /* 32 bit protected mode data segment, physical addresses */ - .word 0xffff, 0 - .byte 0, 0x93, 0xcf, 0 - - .org gdt + REAL_CS, 0 -real_cs: /* 16 bit real mode code segment */ - .word 0xffff, 0 - .byte 0, 0x9b, 0x00, 0 - - .org gdt + REAL_DS -real_ds: /* 16 bit real mode data segment */ - .word 0xffff, ( REAL_DS << 4 ) - .byte 0, 0x93, 0x00, 0 - -gdt_end: - .equ gdt_length, gdt_end - gdt - -/**************************************************************************** - * init_librm (real-mode far call, 16-bit real-mode far return address) - * - * Initialise the GDT ready for transitions to protected mode. - * - * Parameters: - * %cs : .text16 segment - * %ds : .data16 segment - * %edi : Physical base of protected-mode code (virt_offset) - **************************************************************************** - */ - .section ".text16", "ax", @progbits - .code16 - .globl init_librm -init_librm: - /* Preserve registers */ - pushl %eax - pushl %ebx - - /* Store virt_offset and set up virtual_cs and virtual_ds segments */ - movl %edi, %eax - movw $virtual_cs, %bx - call set_seg_base - movw $virtual_ds, %bx - call set_seg_base - movl %edi, rm_virt_offset - - /* Negate virt_offset */ - negl %edi - - /* Store rm_cs and text16, set up real_cs segment */ - xorl %eax, %eax - movw %cs, %ax - movw %ax, %cs:rm_cs - shll $4, %eax - movw $real_cs, %bx - call set_seg_base - addr32 leal (%eax, %edi), %ebx - movl %ebx, rm_text16 - - /* Store rm_ds and data16 */ - xorl %eax, %eax - movw %ds, %ax - movw %ax, %cs:rm_ds - shll $4, %eax - addr32 leal (%eax, %edi), %ebx - movl %ebx, rm_data16 - - /* Set GDT base */ - movl %eax, gdt_base - addl $gdt, gdt_base - - /* Initialise IDT */ - pushl $init_idt - pushw %cs - call prot_call - popl %eax /* discard */ - - /* Restore registers */ - negl %edi - popl %ebx - popl %eax - lret - - .section ".text16", "ax", @progbits - .code16 -set_seg_base: -1: movw %ax, 2(%bx) - rorl $16, %eax - movb %al, 4(%bx) - movb %ah, 7(%bx) - roll $16, %eax - ret - -/**************************************************************************** - * real_to_prot (real-mode near call, 32-bit virtual return address) - * - * Switch from 16-bit real-mode to 32-bit protected mode with virtual - * addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and - * the protected-mode %esp is restored from the saved pm_esp. - * Interrupts are disabled. All other registers may be destroyed. - * - * The return address for this function should be a 32-bit virtual - * address. - * - * Parameters: - * %ecx : number of bytes to move from RM stack to PM stack - * - **************************************************************************** - */ - .section ".text16", "ax", @progbits - .code16 -real_to_prot: - /* Enable A20 line */ - call enable_a20 - /* A failure at this point is fatal, and there's nothing we - * can do about it other than lock the machine to make the - * problem immediately visible. - */ -1: jc 1b - - /* Make sure we have our data segment available */ - movw %cs:rm_ds, %ax - movw %ax, %ds - - /* Add virt_offset, text16 and data16 to stack to be - * copied, and also copy the return address. - */ - pushl rm_virt_offset - pushl rm_text16 - pushl rm_data16 - addw $16, %cx /* %ecx must be less than 64kB anyway */ - - /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */ - xorl %ebp, %ebp - movw %ss, %bp - movzwl %sp, %edx - movl %ebp, %eax - shll $4, %eax - addr32 leal (%eax,%edx), %esi - subl rm_virt_offset, %esi - - /* Load protected-mode global descriptor table */ - data32 lgdt gdtr - - /* Zero segment registers. This wastes around 12 cycles on - * real hardware, but saves a substantial number of emulated - * instructions under KVM. - */ - xorw %ax, %ax - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - movw %ax, %ss - - /* Switch to protected mode */ - cli - movl %cr0, %eax - orb $CR0_PE, %al - movl %eax, %cr0 - data32 ljmp $VIRTUAL_CS, $r2p_pmode - .section ".text", "ax", @progbits - .code32 -r2p_pmode: - /* Set up protected-mode data segments and stack pointer */ - movw $VIRTUAL_DS, %ax - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - movw %ax, %ss - movl pm_esp, %esp - - /* Load protected-mode interrupt descriptor table */ - lidt idtr - - /* Record real-mode %ss:sp (after removal of data) */ - movw %bp, rm_ss - addl %ecx, %edx - movw %dx, rm_sp - - /* Move data from RM stack to PM stack */ - subl %ecx, %esp - movl %esp, %edi - rep movsb - - /* Publish virt_offset, text16 and data16 for PM code to use */ - popl data16 - popl text16 - popl virt_offset - - /* Return to virtual address */ - ret - -/**************************************************************************** - * prot_to_real (protected-mode near call, 32-bit real-mode return address) - * - * Switch from 32-bit protected mode with virtual addresses to 16-bit - * real mode. The protected-mode %esp is stored in pm_esp and the - * real-mode %ss:sp is restored from the saved rm_ss and rm_sp. The - * high word of the real-mode %esp is set to zero. All real-mode data - * segment registers are loaded from the saved rm_ds. Interrupts are - * *not* enabled, since we want to be able to use prot_to_real in an - * ISR. All other registers may be destroyed. - * - * The return address for this function should be a 32-bit (sic) - * real-mode offset within .code16. - * - * Parameters: - * %ecx : number of bytes to move from PM stack to RM stack - * %esi : real-mode global and interrupt descriptor table registers - * - **************************************************************************** - */ - .section ".text", "ax", @progbits - .code32 -prot_to_real: - /* Copy real-mode global descriptor table register to RM code segment */ - movl text16, %edi - leal rm_gdtr(%edi), %edi - movsw - movsl - - /* Load real-mode interrupt descriptor table register */ - lidt (%esi) - - /* Add return address to data to be moved to RM stack */ - addl $4, %ecx - - /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */ - movzwl rm_ss, %ebp - movzwl rm_sp, %edx - subl %ecx, %edx - movl %ebp, %eax - shll $4, %eax - leal (%eax,%edx), %edi - subl virt_offset, %edi - - /* Move data from PM stack to RM stack */ - movl %esp, %esi - rep movsb - - /* Record protected-mode %esp (after removal of data) */ - movl %esi, pm_esp - - /* Load real-mode segment limits */ - movw $REAL_DS, %ax - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - movw %ax, %ss - ljmp $REAL_CS, $p2r_rmode - .section ".text16", "ax", @progbits - .code16 -p2r_rmode: - /* Load real-mode GDT */ - data32 lgdt %cs:rm_gdtr - /* Switch to real mode */ - movl %cr0, %eax - andb $0!CR0_PE, %al - movl %eax, %cr0 -p2r_ljmp_rm_cs: - ljmp $0, $1f -1: - /* Set up real-mode data segments and stack pointer */ - movw %cs:rm_ds, %ax - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - movw %bp, %ss - movl %edx, %esp - - /* Return to real-mode address */ - data32 ret - - - /* Real-mode code and data segments. Assigned by the call to - * init_librm. rm_cs doubles as the segment part of the jump - * instruction used by prot_to_real. Both are located in - * .text16 rather than .data16: rm_cs since it forms part of - * the jump instruction within the code segment, and rm_ds - * since real-mode code needs to be able to locate the data - * segment with no other reference available. - */ - .globl rm_cs - .equ rm_cs, ( p2r_ljmp_rm_cs + 3 ) - - .section ".text16.data", "aw", @progbits - .globl rm_ds -rm_ds: .word 0 - - /* Real-mode global and interrupt descriptor table registers */ - .section ".text16.data", "aw", @progbits -rm_gdtr: - .word 0 /* Limit */ - .long 0 /* Base */ - -/**************************************************************************** - * prot_call (real-mode far call, 16-bit real-mode far return address) - * - * Call a specific C function in the protected-mode code. The - * prototype of the C function must be - * void function ( struct i386_all_regs *ix86 ); - * ix86 will point to a struct containing the real-mode registers - * at entry to prot_call. - * - * All registers will be preserved across prot_call(), unless the C - * function explicitly overwrites values in ix86. Interrupt status - * and GDT will also be preserved. Gate A20 will be enabled. - * - * Note that prot_call() does not rely on the real-mode stack - * remaining intact in order to return, since everything relevant is - * copied to the protected-mode stack for the duration of the call. - * In particular, this means that a real-mode prefix can make a call - * to main() which will return correctly even if the prefix's stack - * gets vapourised during the Etherboot run. (The prefix cannot rely - * on anything else on the stack being preserved, so should move any - * critical data to registers before calling main()). - * - * Parameters: - * function : virtual address of protected-mode function to call - * - * Example usage: - * pushl $pxe_api_call - * call prot_call - * addw $4, %sp - * to call in to the C function - * void pxe_api_call ( struct i386_all_regs *ix86 ); - **************************************************************************** - */ - -#define PC_OFFSET_GDT ( 0 ) -#define PC_OFFSET_IDT ( PC_OFFSET_GDT + 6 ) -#define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 6 ) -#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS ) -#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 ) -#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 ) - - .section ".text16", "ax", @progbits - .code16 - .globl prot_call -prot_call: - /* Preserve registers, flags and GDT on external RM stack */ - pushfl - pushal - pushw %gs - pushw %fs - pushw %es - pushw %ds - pushw %ss - pushw %cs - subw $PC_OFFSET_IX86, %sp - movw %sp, %bp - sidt PC_OFFSET_IDT(%bp) - sgdt PC_OFFSET_GDT(%bp) - - /* For sanity's sake, clear the direction flag as soon as possible */ - cld - - /* Switch to protected mode and move register dump to PM stack */ - movl $PC_OFFSET_END, %ecx - pushl $pc_pmode - jmp real_to_prot - .section ".text", "ax", @progbits - .code32 -pc_pmode: - /* Call function */ - leal PC_OFFSET_IX86(%esp), %eax - pushl %eax - call *(PC_OFFSET_FUNCTION+4)(%esp) - popl %eax /* discard */ - - /* Switch to real mode and move register dump back to RM stack */ - movl $PC_OFFSET_END, %ecx - movl %esp, %esi - pushl $pc_rmode - jmp prot_to_real - .section ".text16", "ax", @progbits - .code16 -pc_rmode: - /* Restore registers and flags and return */ - addw $( PC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp - popw %ds - popw %es - popw %fs - popw %gs - popal - /* popal skips %esp. We therefore want to do "movl -20(%sp), - * %esp", but -20(%sp) is not a valid 80386 expression. - * Fortunately, prot_to_real() zeroes the high word of %esp, so - * we can just use -20(%esp) instead. - */ - addr32 movl -20(%esp), %esp - popfl - lret - -/**************************************************************************** - * real_call (protected-mode near call, 32-bit virtual return address) - * - * Call a real-mode function from protected-mode code. - * - * The non-segment register values will be passed directly to the - * real-mode code. The segment registers will be set as per - * prot_to_real. The non-segment register values set by the real-mode - * function will be passed back to the protected-mode caller. A - * result of this is that this routine cannot be called directly from - * C code, since it clobbers registers that the C ABI expects the - * callee to preserve. - * - * librm.h defines a convenient macro REAL_CODE() for using real_call. - * See librm.h and realmode.h for details and examples. - * - * Parameters: - * (32-bit) near pointer to real-mode function to call - * - * Returns: none - **************************************************************************** - */ - -#define RC_OFFSET_PRESERVE_REGS ( 0 ) -#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS ) -#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 ) -#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 ) - - .section ".text", "ax", @progbits - .code32 - .globl real_call -real_call: - /* Create register dump and function pointer copy on PM stack */ - pushal - pushl RC_OFFSET_FUNCTION(%esp) - - /* Switch to real mode and move register dump to RM stack */ - movl $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx - pushl $rc_rmode - movl $rm_default_gdtr_idtr, %esi - jmp prot_to_real - .section ".text16", "ax", @progbits - .code16 -rc_rmode: - /* Call real-mode function */ - popl rc_function - popal - call *rc_function - pushal - - /* For sanity's sake, clear the direction flag as soon as possible */ - cld - - /* Switch to protected mode and move register dump back to PM stack */ - movl $RC_OFFSET_RETADDR, %ecx - pushl $rc_pmode - jmp real_to_prot - .section ".text", "ax", @progbits - .code32 -rc_pmode: - /* Restore registers and return */ - popal - ret - - - /* Function vector, used because "call xx(%sp)" is not a valid - * 16-bit expression. - */ - .section ".data16", "aw", @progbits -rc_function: .word 0, 0 - - /* Default real-mode global and interrupt descriptor table registers */ - .section ".data", "aw", @progbits -rm_default_gdtr_idtr: - .word 0 /* Global descriptor table limit */ - .long 0 /* Global descriptor table base */ - .word 0x03ff /* Interrupt descriptor table limit */ - .long 0 /* Interrupt descriptor table base */ - -/**************************************************************************** - * flatten_real_mode (real-mode near call) - * - * Switch to flat real mode - * - **************************************************************************** - */ - .section ".text16", "ax", @progbits - .code16 - .globl flatten_real_mode -flatten_real_mode: - /* Modify GDT to use flat real mode */ - movb $0x8f, real_cs + 6 - movb $0x8f, real_ds + 6 - /* Call dummy protected-mode function */ - pushl $flatten_dummy - pushw %cs - call prot_call - addw $4, %sp - /* Restore GDT */ - movb $0x00, real_cs + 6 - movb $0x00, real_ds + 6 - /* Return */ - ret - - .section ".text", "ax", @progbits - .code32 -flatten_dummy: - ret - -/**************************************************************************** - * Interrupt wrapper - * - * Used by the protected-mode interrupt vectors to call the - * interrupt() function. - * - * May be entered with either physical or virtual stack segment. - **************************************************************************** - */ - .globl interrupt_wrapper -interrupt_wrapper: - /* Preserve segment registers and original %esp */ - pushl %ds - pushl %es - pushl %fs - pushl %gs - pushl %ss - pushl %esp - - /* Switch to virtual addressing */ - call _intr_to_virt - - /* Expand IRQ number to whole %eax register */ - movzbl %al, %eax - - /* Call interrupt handler */ - call interrupt - - /* Restore original stack and segment registers */ - lss (%esp), %esp - popl %ss - popl %gs - popl %fs - popl %es - popl %ds - - /* Restore registers and return */ - popal - iret - -/**************************************************************************** - * Stored real-mode and protected-mode stack pointers - * - * The real-mode stack pointer is stored here whenever real_to_prot - * is called and restored whenever prot_to_real is called. The - * converse happens for the protected-mode stack pointer. - * - * Despite initial appearances this scheme is, in fact re-entrant, - * because program flow dictates that we always return via the point - * we left by. For example: - * PXE API call entry - * 1 real => prot - * ... - * Print a text string - * ... - * 2 prot => real - * INT 10 - * 3 real => prot - * ... - * ... - * 4 prot => real - * PXE API call exit - * - * At point 1, the RM mode stack value, say RPXE, is stored in - * rm_ss,sp. We want this value to still be present in rm_ss,sp when - * we reach point 4. - * - * At point 2, the RM stack value is restored from RPXE. At point 3, - * the RM stack value is again stored in rm_ss,sp. This *does* - * overwrite the RPXE that we have stored there, but it's the same - * value, since the code between points 2 and 3 has managed to return - * to us. - **************************************************************************** - */ - .section ".data", "aw", @progbits - .globl rm_sp -rm_sp: .word 0 - .globl rm_ss -rm_ss: .word 0 -pm_esp: .long _estack - -/**************************************************************************** - * Virtual address offsets - * - * These are used by the protected-mode code to map between virtual - * and physical addresses, and to access variables in the .text16 or - * .data16 segments. - **************************************************************************** - */ - /* Internal copies, created by init_librm (which runs in real mode) */ - .section ".data16", "aw", @progbits -rm_virt_offset: .long 0 -rm_text16: .long 0 -rm_data16: .long 0 - - /* Externally-visible copies, created by real_to_prot */ - .section ".data", "aw", @progbits - .globl virt_offset -virt_offset: .long 0 - .globl text16 -text16: .long 0 - .globl data16 -data16: .long 0 diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c deleted file mode 100644 index becb02677..000000000 --- a/src/arch/i386/transitions/librm_mgmt.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * librm: a library for interfacing to real-mode code - * - * Michael Brown - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include -#include -#include - -/* - * This file provides functions for managing librm. - * - */ - -/** The interrupt wrapper */ -extern char interrupt_wrapper[]; - -/** The interrupt vectors */ -static struct interrupt_vector intr_vec[NUM_INT]; - -/** The interrupt descriptor table */ -struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) )); - -/** The interrupt descriptor table register */ -struct idtr idtr = { - .limit = ( sizeof ( idt ) - 1 ), -}; - -/** Timer interrupt profiler */ -static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" }; - -/** Other interrupt profiler */ -static struct profiler other_irq_profiler __profiler = { .name = "irq.other" }; - -/** - * Allocate space on the real-mode stack and copy data there from a - * user buffer - * - * @v data User buffer - * @v size Size of stack data - * @ret sp New value of real-mode stack pointer - */ -uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) { - userptr_t rm_stack; - rm_sp -= size; - rm_stack = real_to_user ( rm_ss, rm_sp ); - memcpy_user ( rm_stack, 0, data, 0, size ); - return rm_sp; -}; - -/** - * Deallocate space on the real-mode stack, optionally copying back - * data to a user buffer. - * - * @v data User buffer - * @v size Size of stack data - */ -void remove_user_from_rm_stack ( userptr_t data, size_t size ) { - if ( data ) { - userptr_t rm_stack = real_to_user ( rm_ss, rm_sp ); - memcpy_user ( rm_stack, 0, data, 0, size ); - } - rm_sp += size; -}; - -/** - * Set interrupt vector - * - * @v intr Interrupt number - * @v vector Interrupt vector, or NULL to disable - */ -void set_interrupt_vector ( unsigned int intr, void *vector ) { - struct interrupt_descriptor *idte; - - idte = &idt[intr]; - idte->segment = VIRTUAL_CS; - idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); - idte->low = ( ( ( uint32_t ) vector ) & 0xffff ); - idte->high = ( ( ( uint32_t ) vector ) >> 16 ); -} - -/** - * Initialise interrupt descriptor table - * - */ -void init_idt ( void ) { - struct interrupt_vector *vec; - unsigned int intr; - - /* Initialise the interrupt descriptor table and interrupt vectors */ - for ( intr = 0 ; intr < NUM_INT ; intr++ ) { - vec = &intr_vec[intr]; - vec->pushal = PUSHAL_INSN; - vec->movb = MOVB_INSN; - vec->intr = intr; - vec->jmp = JMP_INSN; - vec->offset = ( ( uint32_t ) interrupt_wrapper - - ( uint32_t ) vec->next ); - set_interrupt_vector ( intr, vec ); - } - DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n", - intr_vec, sizeof ( intr_vec[0] ), - virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); - - /* Initialise the interrupt descriptor table register */ - idtr.base = virt_to_phys ( idt ); -} - -/** - * Determine interrupt profiler (for debugging) - * - * @v intr Interrupt number - * @ret profiler Profiler - */ -static struct profiler * interrupt_profiler ( int intr ) { - - switch ( intr ) { - case IRQ_INT ( 0 ) : - return &timer_irq_profiler; - default: - return &other_irq_profiler; - } -} - -/** - * Interrupt handler - * - * @v intr Interrupt number - */ -void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int intr ) { - struct profiler *profiler = interrupt_profiler ( intr ); - uint32_t discard_eax; - - /* Reissue interrupt in real mode */ - profile_start ( profiler ); - __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t" - "\n1:\n\t" - "int $0x00\n\t" ) - : "=a" ( discard_eax ) : "0" ( intr ) ); - profile_stop ( profiler ); - profile_exclude ( profiler ); -} - -PROVIDE_UACCESS_INLINE ( librm, phys_to_user ); -PROVIDE_UACCESS_INLINE ( librm, user_to_phys ); -PROVIDE_UACCESS_INLINE ( librm, virt_to_user ); -PROVIDE_UACCESS_INLINE ( librm, user_to_virt ); -PROVIDE_UACCESS_INLINE ( librm, userptr_add ); -PROVIDE_UACCESS_INLINE ( librm, memcpy_user ); -PROVIDE_UACCESS_INLINE ( librm, memmove_user ); -PROVIDE_UACCESS_INLINE ( librm, memset_user ); -PROVIDE_UACCESS_INLINE ( librm, strlen_user ); -PROVIDE_UACCESS_INLINE ( librm, memchr_user ); diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 98c49b98d..011260cac 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -1,3 +1,8 @@ +# Assembler section type character +# +ASM_TCHAR := @ +ASM_TCHAR_OPS := @ + # Include common x86 headers # INCDIRS += arch/x86/include @@ -5,11 +10,17 @@ INCDIRS += arch/x86/include # x86-specific directories containing source files # SRCDIRS += arch/x86/core +SRCDIRS += arch/x86/image +SRCDIRS += arch/x86/interface/pcbios +SRCDIRS += arch/x86/interface/pxe SRCDIRS += arch/x86/interface/efi +SRCDIRS += arch/x86/interface/vmware +SRCDIRS += arch/x86/interface/syslinux SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen SRCDIRS += arch/x86/drivers/hyperv +SRCDIRS += arch/x86/transitions # breaks building some of the linux-related objects CFLAGS += -Ulinux @@ -17,6 +28,10 @@ CFLAGS += -Ulinux # disable valgrind CFLAGS += -DNVALGRIND +# Define version string for lkrnprefix.S +# +CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" + # Include Hyper-V driver in the all-drivers build # DRIVERS_hyperv += hyperv diff --git a/src/arch/x86/Makefile.efi b/src/arch/x86/Makefile.efi index f73bc7d5d..f04be425b 100644 --- a/src/arch/x86/Makefile.efi +++ b/src/arch/x86/Makefile.efi @@ -1,42 +1,6 @@ # -*- makefile -*- : Force emacs to use Makefile mode -# The EFI linker script +# Include generic EFI Makefile # -LDSCRIPT = arch/x86/scripts/efi.lds - -# Retain relocation information for elf2efi -# -LDFLAGS += -q -S - -# Media types. -# -NON_AUTO_MEDIA += efi -NON_AUTO_MEDIA += efidrv -NON_AUTO_MEDIA += drv.efi -NON_AUTO_MEDIA += efirom - -# Include SNP driver in the all-drivers build -# -DRIVERS_net += snp - -# Rules for building EFI files -# -$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI) - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(ELF2EFI) --subsystem=10 $< $@ - -$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI) - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(ELF2EFI) --subsystem=11 $< $@ - -$(BIN)/%.drv.efi : $(BIN)/%.efidrv - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(CP) $< $@ - -$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM) - $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@ - -$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined - $(QM)$(ECHO) " [CAB] $@" - $(Q)$(LCAB) -n -q $(ALL_drv.efi) $@ +MAKEDEPS += Makefile.efi +include Makefile.efi diff --git a/src/arch/x86/Makefile.pcbios b/src/arch/x86/Makefile.pcbios new file mode 100644 index 000000000..c44eefc1f --- /dev/null +++ b/src/arch/x86/Makefile.pcbios @@ -0,0 +1,132 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# BIOS-specific directories containing source files +# +SRCDIRS += arch/x86/drivers/net + +# The i386 linker script +# +LDSCRIPT = arch/x86/scripts/pcbios.lds + +# Stop ld from complaining about our customised linker script +# +LDFLAGS += -N --no-check-sections + +# Prefix always starts at address zero +# +LDFLAGS += --section-start=.prefix=0 + +# Media types. +# +MEDIA += rom +MEDIA += mrom +MEDIA += pcirom +MEDIA += isarom +MEDIA += pxe +MEDIA += kpxe +MEDIA += kkpxe +MEDIA += kkkpxe +MEDIA += lkrn +MEDIA += dsk +MEDIA += nbi +MEDIA += hd +MEDIA += raw +MEDIA += exe + +# Padding rules +# +PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff +PAD_mrom = $(PAD_rom) +PAD_pcirom = $(PAD_rom) +PAD_isarom = $(PAD_rom) +PAD_dsk = $(PERL) $(PADIMG) --blksize=512 +PAD_hd = $(PERL) $(PADIMG) --blksize=32768 +PAD_exe = $(PERL) $(PADIMG) --blksize=512 + +# Finalisation rules +# +FINALISE_rom = $(PERL) $(FIXROM) +FINALISE_mrom = $(FINALISE_rom) +FINALISE_pcirom = $(FINALISE_rom) +FINALISE_isarom = $(FINALISE_rom) + +# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc. +# +LIST_NAME_rom := ROMS +LIST_NAME_mrom := ROMS +LIST_NAME_pcirom := ROMS +LIST_NAME_isarom := ROMS + +# Locations of isolinux files +# +SYSLINUX_DIR_LIST := \ + /usr/lib/syslinux \ + /usr/lib/syslinux/bios \ + /usr/lib/syslinux/modules/bios \ + /usr/share/syslinux \ + /usr/share/syslinux/bios \ + /usr/share/syslinux/modules/bios \ + /usr/local/share/syslinux \ + /usr/local/share/syslinux/bios \ + /usr/local/share/syslinux/modules/bios \ + /usr/lib/ISOLINUX +ISOLINUX_BIN_LIST := \ + $(ISOLINUX_BIN) \ + $(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST)) +LDLINUX_C32_LIST := \ + $(LDLINUX_C32) \ + $(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST)) +ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) +LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST))) + +# rule to make a non-emulation ISO boot image +NON_AUTO_MEDIA += iso +%iso: %lkrn util/geniso + $(QM)$(ECHO) " [GENISO] $@" + $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) LDLINUX_C32=$(LDLINUX_C32) \ + VERSION="$(VERSION)" bash util/geniso -o $@ $< + +# rule to make a floppy emulation ISO boot image +NON_AUTO_MEDIA += liso +%liso: %lkrn util/geniso + $(QM)$(ECHO) " [GENISO] $@" + $(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $< + +# rule to make a syslinux floppy image (mountable, bootable) +NON_AUTO_MEDIA += sdsk +%sdsk: %lkrn util/gensdsk + $(QM)$(ECHO) " [GENSDSK] $@" + $(Q)bash util/gensdsk $@ $< + +# rule to write disk images to /dev/fd0 +NON_AUTO_MEDIA += fd0 +%fd0 : %dsk + $(QM)$(ECHO) " [DD] $@" + $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0 + $(Q)sync + +# Special target for building Master Boot Record binary +$(BIN)/mbr.tmp : $(BIN)/mbr.o + $(QM)$(ECHO) " [LD] $@" + $(Q)$(LD) $(LDFLAGS) -o $@ -e mbr $< + +# rule to make a USB disk image +$(BIN)/usbdisk.tmp : $(BIN)/usbdisk.o + $(QM)$(ECHO) " [LD] $@" + $(Q)$(LD) $(LDFLAGS) -o $@ -e mbr $< + +NON_AUTO_MEDIA += usb +%usb: $(BIN)/usbdisk.bin %hd + $(QM)$(ECHO) " [FINISH] $@" + $(Q)cat $^ > $@ + +NON_AUTO_MEDIA += vhd +%vhd: %usb + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(QEMUIMG) convert -f raw -O vpc $< $@ + +# Padded floppy image (e.g. for iLO) +NON_AUTO_MEDIA += pdsk +%pdsk : %dsk + $(Q)cp $< $@ + $(Q)$(PADIMG) --blksize=1474560 $@ diff --git a/src/arch/i386/core/basemem_packet.c b/src/arch/x86/core/basemem_packet.c similarity index 100% rename from src/arch/i386/core/basemem_packet.c rename to src/arch/x86/core/basemem_packet.c diff --git a/src/arch/i386/core/cachedhcp.c b/src/arch/x86/core/cachedhcp.c similarity index 93% rename from src/arch/i386/core/cachedhcp.c rename to src/arch/x86/core/cachedhcp.c index a5c624035..ff35b9256 100644 --- a/src/arch/i386/core/cachedhcp.c +++ b/src/arch/x86/core/cachedhcp.c @@ -58,6 +58,7 @@ static void cachedhcp_init ( void ) { struct dhcp_packet *dhcppkt; struct dhcp_packet *tmp; struct dhcphdr *dhcphdr; + size_t max_len; size_t len; /* Do nothing if no cached DHCPACK is present */ @@ -69,23 +70,25 @@ static void cachedhcp_init ( void ) { /* No reliable way to determine length before parsing packet; * start by assuming maximum length permitted by PXE. */ - len = sizeof ( BOOTPLAYER_t ); + max_len = sizeof ( BOOTPLAYER_t ); /* Allocate and populate DHCP packet */ - dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len ); + dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len ); if ( ! dhcppkt ) { DBGC ( colour, "CACHEDHCP could not allocate copy\n" ); return; } dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) ); copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0, - len ); - dhcppkt_init ( dhcppkt, dhcphdr, len ); + max_len ); + dhcppkt_init ( dhcppkt, dhcphdr, max_len ); - /* Resize packet to required length. If reallocation fails, - * just continue to use the original packet. + /* Shrink packet to required length. If reallocation fails, + * just continue to use the original packet and waste the + * unused space. */ len = dhcppkt_len ( dhcppkt ); + assert ( len <= max_len ); tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) ); if ( tmp ) dhcppkt = tmp; diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c index bc5a6c68c..1a7c93e83 100644 --- a/src/arch/x86/core/cpuid.c +++ b/src/arch/x86/core/cpuid.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include /** @file @@ -32,15 +33,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** Colour for debug messages */ +#define colour 0x861d + /** * Check whether or not CPUID instruction is supported * - * @ret is_supported CPUID instruction is supported + * @ret rc Return status code */ -int cpuid_is_supported ( void ) { +static int cpuid_instruction_supported ( void ) { unsigned long original; unsigned long inverted; + /* Check for instruction existence via flag modifiability */ __asm__ ( "pushf\n\t" "pushf\n\t" "pop %0\n\t" @@ -53,7 +58,54 @@ int cpuid_is_supported ( void ) { "popf\n\t" : "=&r" ( original ), "=&r" ( inverted ) : "ir" ( CPUID_FLAG ) ); - return ( ( original ^ inverted ) & CPUID_FLAG ); + if ( ! ( ( original ^ inverted ) & CPUID_FLAG ) ) { + DBGC ( colour, "CPUID instruction is not supported\n" ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Check whether or not CPUID function is supported + * + * @v function CPUID function + * @ret rc Return status code + */ +int cpuid_supported ( uint32_t function ) { + uint32_t max_function; + uint32_t discard_b; + uint32_t discard_c; + uint32_t discard_d; + int rc; + + /* Check that CPUID instruction is available */ + if ( ( rc = cpuid_instruction_supported() ) != 0 ) + return rc; + + /* Find highest supported function number within this family */ + cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b, + &discard_c, &discard_d ); + + /* Fail if maximum function number is meaningless (e.g. if we + * are attempting to call an extended function on a CPU which + * does not support them). + */ + if ( ( max_function & CPUID_AMD_CHECK_MASK ) != + ( function & CPUID_AMD_CHECK_MASK ) ) { + DBGC ( colour, "CPUID invalid maximum function %#08x\n", + max_function ); + return -EINVAL; + } + + /* Fail if this function is not supported */ + if ( function > max_function ) { + DBGC ( colour, "CPUID function %#08x not supported\n", + function ); + return -ENOTTY; + } + + return 0; } /** @@ -62,23 +114,18 @@ int cpuid_is_supported ( void ) { * @v features x86 CPU features to fill in */ static void x86_intel_features ( struct x86_features *features ) { - uint32_t max_level; uint32_t discard_a; uint32_t discard_b; - uint32_t discard_c; - uint32_t discard_d; + int rc; /* Check that features are available via CPUID */ - cpuid ( CPUID_VENDOR_ID, &max_level, &discard_b, &discard_c, - &discard_d ); - if ( max_level < CPUID_FEATURES ) { - DBGC ( features, "CPUID has no Intel-defined features (max " - "level %08x)\n", max_level ); + if ( ( rc = cpuid_supported ( CPUID_FEATURES ) ) != 0 ) { + DBGC ( features, "CPUID has no Intel-defined features\n" ); return; } /* 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 ); @@ -91,27 +138,18 @@ static void x86_intel_features ( struct x86_features *features ) { * @v features x86 CPU features to fill in */ static void x86_amd_features ( struct x86_features *features ) { - uint32_t max_level; uint32_t discard_a; uint32_t discard_b; - uint32_t discard_c; - uint32_t discard_d; + int rc; /* Check that features are available via CPUID */ - cpuid ( CPUID_AMD_MAX_FN, &max_level, &discard_b, &discard_c, - &discard_d ); - if ( ( max_level & CPUID_AMD_CHECK_MASK ) != CPUID_AMD_CHECK ) { - DBGC ( features, "CPUID has no extended functions\n" ); - return; - } - if ( max_level < CPUID_AMD_FEATURES ) { - DBGC ( features, "CPUID has no AMD-defined features (max " - "level %08x)\n", max_level ); + if ( ( rc = cpuid_supported ( CPUID_AMD_FEATURES ) ) != 0 ) { + DBGC ( features, "CPUID has no AMD-defined features\n" ); return; } /* 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 ); @@ -127,12 +165,6 @@ void x86_features ( struct x86_features *features ) { /* Clear all features */ memset ( features, 0, sizeof ( *features ) ); - /* Check that CPUID instruction is available */ - if ( ! cpuid_is_supported() ) { - DBGC ( features, "CPUID instruction is not supported\n" ); - return; - } - /* Get Intel-defined features */ x86_intel_features ( features ); diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c index 08bd3918a..0b67ee91d 100644 --- a/src/arch/x86/core/cpuid_settings.c +++ b/src/arch/x86/core/cpuid_settings.c @@ -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/.", 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/.0x40..". 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,62 +153,46 @@ static int cpuid_settings_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { uint32_t function; - uint32_t max_function; + uint32_t subfunction; uint32_t num_functions; uint32_t registers; uint32_t num_registers; uint32_t buf[4]; uint32_t output; - uint32_t discard_b; - uint32_t discard_c; - uint32_t discard_d; size_t frag_len; size_t result_len = 0; - - /* Fail unless CPUID is supported */ - if ( ! cpuid_is_supported() ) { - DBGC ( settings, "CPUID not supported\n" ); - return -ENOTSUP; - } - - /* Find highest supported function number within this set */ - function = CPUID_FUNCTION ( setting->tag ); - cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b, - &discard_c, &discard_d ); - - /* Fail if maximum function number is meaningless (e.g. if we - * are attempting to call an extended function on a CPU which - * does not support them). - */ - if ( ( max_function & CPUID_AMD_CHECK_MASK ) != - ( function & CPUID_AMD_CHECK_MASK ) ) { - DBGC ( settings, "CPUID invalid maximum function\n" ); - return -ENOTSUP; - } + int rc; /* Call each function in turn */ - num_functions = CPUID_NUM_FUNCTIONS ( setting->tag ); + function = CPUID_FUNCTION ( 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 */ - if ( function > max_function ) { - DBGC ( settings, "CPUID function %#08x not supported\n", - function ); - return -ENOTSUP; + if ( ( rc = cpuid_supported ( function ) ) != 0 ) { + DBGC ( settings, "CPUID function %#08x not supported: " + "%s\n", function, strerror ( rc ) ); + return rc; } /* 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 ) @@ -260,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, @@ -271,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, diff --git a/src/arch/i386/core/dumpregs.c b/src/arch/x86/core/dumpregs.c similarity index 78% rename from src/arch/i386/core/dumpregs.c rename to src/arch/x86/core/dumpregs.c index 82dc21847..a5108ea14 100644 --- a/src/arch/i386/core/dumpregs.c +++ b/src/arch/x86/core/dumpregs.c @@ -6,11 +6,8 @@ void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { __asm__ __volatile__ ( TEXT16_CODE ( ".globl dump_regs\n\t" "\ndump_regs:\n\t" - "pushl $_dump_regs\n\t" - "pushw %%cs\n\t" - "call prot_call\n\t" - "addr32 leal 4(%%esp), %%esp\n\t" - "ret\n\t" ) : : ); + VIRT_CALL ( _dump_regs ) + "ret\n\t" ) : ); printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" diff --git a/src/arch/x86/core/gdbmach.c b/src/arch/x86/core/gdbmach.c new file mode 100644 index 000000000..af6abfedd --- /dev/null +++ b/src/arch/x86/core/gdbmach.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * GDB architecture-specific bits for x86 + * + */ + +/** Number of hardware breakpoints */ +#define NUM_HWBP 4 + +/** Debug register 7: Global breakpoint enable */ +#define DR7_G( bp ) ( 2 << ( 2 * (bp) ) ) + +/** Debug register 7: Global exact breakpoint enable */ +#define DR7_GE ( 1 << 9 ) + +/** Debug register 7: Break on data writes */ +#define DR7_RWLEN_WRITE 0x11110000 + +/** Debug register 7: Break on data access */ +#define DR7_RWLEN_ACCESS 0x33330000 + +/** Debug register 7: One-byte length */ +#define DR7_RWLEN_1 0x00000000 + +/** Debug register 7: Two-byte length */ +#define DR7_RWLEN_2 0x44440000 + +/** Debug register 7: Four-byte length */ +#define DR7_RWLEN_4 0xcccc0000 + +/** Debug register 7: Eight-byte length */ +#define DR7_RWLEN_8 0x88880000 + +/** Debug register 7: Breakpoint R/W and length mask */ +#define DR7_RWLEN_MASK( bp ) ( 0xf0000 << ( 4 * (bp) ) ) + +/** Hardware breakpoint addresses (debug registers 0-3) */ +static unsigned long dr[NUM_HWBP]; + +/** Active value of debug register 7 */ +static unsigned long dr7 = DR7_GE; + +/** + * Update debug registers + * + */ +static void gdbmach_update ( void ) { + + /* Set debug registers */ + __asm__ __volatile__ ( "mov %0, %%dr0" : : "r" ( dr[0] ) ); + __asm__ __volatile__ ( "mov %0, %%dr1" : : "r" ( dr[1] ) ); + __asm__ __volatile__ ( "mov %0, %%dr2" : : "r" ( dr[2] ) ); + __asm__ __volatile__ ( "mov %0, %%dr3" : : "r" ( dr[3] ) ); + __asm__ __volatile__ ( "mov %0, %%dr7" : : "r" ( dr7 ) ); +} + +/** + * Find reusable or available hardware breakpoint + * + * @v addr Linear address + * @v rwlen Control bits + * @ret bp Hardware breakpoint, or negative error + */ +static int gdbmach_find ( unsigned long addr, unsigned int rwlen ) { + unsigned int i; + int bp = -ENOENT; + + /* Look for a reusable or available breakpoint */ + for ( i = 0 ; i < NUM_HWBP ; i++ ) { + + /* If breakpoint is not enabled, then it is available */ + if ( ! ( dr7 & DR7_G ( i ) ) ) { + bp = i; + continue; + } + + /* If breakpoint is enabled and has the same address + * and control bits, then reuse it. + */ + if ( ( dr[i] == addr ) && + ( ( ( dr7 ^ rwlen ) & DR7_RWLEN_MASK ( i ) ) == 0 ) ) { + bp = i; + break; + } + } + + return bp; +} + +/** + * Set hardware breakpoint + * + * @v type GDB breakpoint type + * @v addr Virtual address + * @v len Length + * @v enable Enable (not disable) breakpoint + * @ret rc Return status code + */ +int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ) { + unsigned int rwlen; + unsigned long mask; + int bp; + + /* Parse breakpoint type */ + switch ( type ) { + case GDBMACH_WATCH: + rwlen = DR7_RWLEN_WRITE; + break; + case GDBMACH_AWATCH: + rwlen = DR7_RWLEN_ACCESS; + break; + default: + return -ENOTSUP; + } + + /* Parse breakpoint length */ + switch ( len ) { + case 1: + rwlen |= DR7_RWLEN_1; + break; + case 2: + rwlen |= DR7_RWLEN_2; + break; + case 4: + rwlen |= DR7_RWLEN_4; + break; + case 8: + rwlen |= DR7_RWLEN_8; + break; + default: + return -ENOTSUP; + } + + /* Convert to linear address */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + addr = virt_to_phys ( ( void * ) addr ); + + /* Find reusable or available hardware breakpoint */ + bp = gdbmach_find ( addr, rwlen ); + if ( bp < 0 ) + return ( enable ? -ENOBUFS : 0 ); + + /* Configure this breakpoint */ + DBGC ( &dr[0], "GDB bp %d at %p+%zx type %d (%sabled)\n", + bp, ( ( void * ) addr ), len, type, ( enable ? "en" : "dis" ) ); + dr[bp] = addr; + mask = DR7_RWLEN_MASK ( bp ); + dr7 = ( ( dr7 & ~mask ) | ( rwlen & mask ) ); + mask = DR7_G ( bp ); + dr7 &= ~mask; + if ( enable ) + dr7 |= mask; + + /* Update debug registers */ + gdbmach_update(); + + return 0; +} + +/** + * Handle exception + * + * @v signo GDB signal number + * @v regs Register dump + */ +__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { + unsigned long dr7_disabled = DR7_GE; + unsigned long dr6_clear = 0; + + /* Temporarily disable breakpoints */ + __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7_disabled ) ); + + /* Handle exception */ + DBGC ( &dr[0], "GDB signal %d\n", signo ); + DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) ); + gdbstub_handler ( signo, regs ); + DBGC ( &dr[0], "GDB signal %d returning\n", signo ); + DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) ); + + /* Clear breakpoint status register */ + __asm__ __volatile__ ( "mov %0, %%dr6\n" : : "r" ( dr6_clear ) ); + + /* Re-enable breakpoints */ + __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7 ) ); +} + +/** + * CPU exception vectors + * + * Note that we cannot intercept anything from INT8 (double fault) + * upwards, since these overlap by default with IRQ0-7. + */ +static void * gdbmach_vectors[] = { + gdbmach_sigfpe, /* Divide by zero */ + gdbmach_sigtrap, /* Debug trap */ + NULL, /* Non-maskable interrupt */ + gdbmach_sigtrap, /* Breakpoint */ + gdbmach_sigstkflt, /* Overflow */ + gdbmach_sigstkflt, /* Bound range exceeded */ + gdbmach_sigill, /* Invalid opcode */ +}; + +/** + * Initialise GDB + */ +void gdbmach_init ( void ) { + unsigned int i; + + /* Hook CPU exception vectors */ + for ( i = 0 ; i < ( sizeof ( gdbmach_vectors ) / + sizeof ( gdbmach_vectors[0] ) ) ; i++ ) { + if ( gdbmach_vectors[i] ) + set_interrupt_vector ( i, gdbmach_vectors[i] ); + } +} diff --git a/src/arch/x86/core/linux/linux_api.c b/src/arch/x86/core/linux/linux_api.c index 0bed9fd57..17b1f3fd4 100644 --- a/src/arch/x86/core/linux/linux_api.c +++ b/src/arch/x86/core/linux/linux_api.c @@ -108,3 +108,42 @@ void * linux_mremap ( void *old_address, __kernel_size_t old_size, int linux_munmap ( void *addr, __kernel_size_t length ) { return linux_syscall ( __NR_munmap, addr, length ); } + +int linux_socket ( int domain, int type_, int protocol ) { +#ifdef __NR_socket + return linux_syscall ( __NR_socket, domain, type_, protocol ); +#else +#ifndef SOCKOP_socket +# define SOCKOP_socket 1 +#endif + unsigned long sc_args[] = { domain, type_, protocol }; + return linux_syscall ( __NR_socketcall, SOCKOP_socket, sc_args ); +#endif +} + +int linux_bind ( int fd, const struct sockaddr *addr, socklen_t addrlen ) { +#ifdef __NR_bind + return linux_syscall ( __NR_bind, fd, addr, addrlen ); +#else +#ifndef SOCKOP_bind +# define SOCKOP_bind 2 +#endif + unsigned long sc_args[] = { fd, (unsigned long)addr, addrlen }; + return linux_syscall ( __NR_socketcall, SOCKOP_bind, sc_args ); +#endif +} + +ssize_t linux_sendto ( int fd, const void *buf, size_t len, int flags, + const struct sockaddr *daddr, socklen_t addrlen ) { +#ifdef __NR_sendto + return linux_syscall ( __NR_sendto, fd, buf, len, flags, + daddr, addrlen ); +#else +#ifndef SOCKOP_sendto +# define SOCKOP_sendto 11 +#endif + unsigned long sc_args[] = { fd, (unsigned long)buf, len, + flags, (unsigned long)daddr, addrlen }; + return linux_syscall ( __NR_socketcall, SOCKOP_sendto, sc_args ); +#endif +} diff --git a/src/arch/i386/core/patch_cf.S b/src/arch/x86/core/patch_cf.S similarity index 100% rename from src/arch/i386/core/patch_cf.S rename to src/arch/x86/core/patch_cf.S diff --git a/src/arch/i386/core/pci_autoboot.c b/src/arch/x86/core/pci_autoboot.c similarity index 100% rename from src/arch/i386/core/pci_autoboot.c rename to src/arch/x86/core/pci_autoboot.c diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 9b8e6b1d9..0d09be84b 100644 --- a/src/arch/x86/core/pcidirect.c +++ b/src/arch/x86/core/pcidirect.c @@ -36,10 +36,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Prepare for Type 1 PCI configuration space access * * @v pci PCI device - * @v where Location within PCI configuration space + * @v where Location within PCI configuration space */ void pcidirect_prepare ( struct pci_device *pci, int where ) { - outl ( ( 0x80000000 | ( pci->busdevfn << 8 ) | ( where & ~3 ) ), + uint16_t busdevfn = ( pci->busdevfn & 0xffff ); + + outl ( ( 0x80000000 | ( busdevfn << 8 ) | ( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS ); } diff --git a/src/arch/x86/core/rdtsc_timer.c b/src/arch/x86/core/rdtsc_timer.c new file mode 100644 index 000000000..bee5f1ca4 --- /dev/null +++ b/src/arch/x86/core/rdtsc_timer.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2008 Michael Brown . + * + * 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 + * + * RDTSC timer + * + */ + +#include +#include +#include +#include +#include + +/** Number of microseconds to use for TSC calibration */ +#define TSC_CALIBRATE_US 1024 + +/** TSC increment per microsecond */ +static unsigned long tsc_per_us; + +/** Minimum resolution for scaled TSC timer */ +#define TSC_SCALED_HZ 32 + +/** TSC scale (expressed as a bit shift) + * + * We use this to avoid the need for 64-bit divsion on 32-bit systems. + */ +static unsigned int tsc_scale; + +/** Number of timer ticks per scaled TSC increment */ +static unsigned long ticks_per_scaled_tsc; + +/** Colour for debug messages */ +#define colour &tsc_per_us + +/** + * Get raw TSC value + * + * @ret tsc Raw TSC value + */ +static inline __always_inline unsigned long rdtsc_raw ( void ) { + unsigned long raw; + + __asm__ __volatile__ ( "rdtsc\n\t" : "=a" ( raw ) : : "edx" ); + return raw; +} + +/** + * Get TSC value, shifted to avoid rollover within a realistic timescale + * + * @ret tsc Scaled TSC value + */ +static inline __always_inline unsigned long rdtsc_scaled ( void ) { + unsigned long scaled; + + __asm__ __volatile__ ( "rdtsc\n\t" + "shrdl %b1, %%edx, %%eax\n\t" + : "=a" ( scaled ) : "c" ( tsc_scale ) : "edx" ); + return scaled; +} + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long rdtsc_currticks ( void ) { + unsigned long scaled; + + scaled = rdtsc_scaled(); + return ( scaled * ticks_per_scaled_tsc ); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void rdtsc_udelay ( unsigned long usecs ) { + unsigned long start; + unsigned long elapsed; + unsigned long threshold; + + start = rdtsc_raw(); + threshold = ( usecs * tsc_per_us ); + do { + elapsed = ( rdtsc_raw() - start ); + } while ( elapsed < threshold ); +} + +/** + * Probe RDTSC timer + * + * @ret rc Return status code + */ +static int rdtsc_probe ( void ) { + unsigned long before; + unsigned long after; + unsigned long elapsed; + uint32_t apm; + uint32_t discard_a; + uint32_t discard_b; + uint32_t discard_c; + int rc; + + /* Check that TSC is invariant */ + if ( ( rc = cpuid_supported ( CPUID_APM ) ) != 0 ) { + DBGC ( colour, "RDTSC cannot determine APM features: %s\n", + strerror ( rc ) ); + return rc; + } + 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 ); + return -ENOTTY; + } + + /* Calibrate udelay() timer via 8254 PIT */ + before = rdtsc_raw(); + pit8254_udelay ( TSC_CALIBRATE_US ); + after = rdtsc_raw(); + elapsed = ( after - before ); + tsc_per_us = ( elapsed / TSC_CALIBRATE_US ); + if ( ! tsc_per_us ) { + DBGC ( colour, "RDTSC has zero TSC per microsecond\n" ); + return -EIO; + } + + /* Calibrate currticks() scaling factor */ + tsc_scale = 31; + ticks_per_scaled_tsc = ( ( 1UL << tsc_scale ) / + ( tsc_per_us * ( 1000000 / TICKS_PER_SEC ) ) ); + while ( ticks_per_scaled_tsc > ( TICKS_PER_SEC / TSC_SCALED_HZ ) ) { + tsc_scale--; + ticks_per_scaled_tsc >>= 1; + } + DBGC ( colour, "RDTSC has %ld tsc per us, %ld ticks per 2^%d tsc\n", + tsc_per_us, ticks_per_scaled_tsc, tsc_scale ); + if ( ! ticks_per_scaled_tsc ) { + DBGC ( colour, "RDTSC has zero ticks per TSC\n" ); + return -EIO; + } + + return 0; +} + +/** RDTSC timer */ +struct timer rdtsc_timer __timer ( TIMER_PREFERRED ) = { + .name = "rdtsc", + .probe = rdtsc_probe, + .currticks = rdtsc_currticks, + .udelay = rdtsc_udelay, +}; diff --git a/src/arch/i386/core/relocate.c b/src/arch/x86/core/relocate.c similarity index 76% rename from src/arch/i386/core/relocate.c rename to src/arch/x86/core/relocate.c index 54ad387e4..765d46560 100644 --- a/src/arch/i386/core/relocate.c +++ b/src/arch/x86/core/relocate.c @@ -10,14 +10,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* - * The linker passes in the symbol _max_align, which is the alignment - * that we must preserve, in bytes. - * - */ -extern char _max_align[]; -#define max_align ( ( unsigned int ) _max_align ) - /* Linker symbols */ extern char _textdata[]; extern char _etextdata[]; @@ -30,6 +22,12 @@ extern char _etextdata[]; */ #define MAX_ADDR (0xfff00000UL) +/* Preserve alignment to a 4kB page + * + * Required for x86_64, and doesn't hurt for i386. + */ +#define ALIGN 4096 + /** * Relocate iPXE * @@ -44,8 +42,8 @@ extern char _etextdata[]; */ __asmcall void relocate ( struct i386_all_regs *ix86 ) { struct memory_map memmap; - unsigned long start, end, size, padded_size, max; - unsigned long new_start, new_end; + uint32_t start, end, size, padded_size, max; + uint32_t new_start, new_end; unsigned i; /* Get memory map and current location */ @@ -53,17 +51,17 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { start = virt_to_phys ( _textdata ); end = virt_to_phys ( _etextdata ); size = ( end - start ); - padded_size = ( size + max_align - 1 ); + padded_size = ( size + ALIGN - 1 ); - DBG ( "Relocate: currently at [%lx,%lx)\n" - "...need %lx bytes for %d-byte alignment\n", - start, end, padded_size, max_align ); + DBG ( "Relocate: currently at [%x,%x)\n" + "...need %x bytes for %d-byte alignment\n", + start, end, padded_size, ALIGN ); /* Determine maximum usable address */ max = MAX_ADDR; if ( ix86->regs.ebp < max ) { max = ix86->regs.ebp; - DBG ( "Limiting relocation to [0,%lx)\n", max ); + DBG ( "Limiting relocation to [0,%x)\n", max ); } /* Walk through the memory map and find the highest address @@ -72,7 +70,7 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { new_end = end; for ( i = 0 ; i < memmap.count ; i++ ) { struct memory_region *region = &memmap.regions[i]; - unsigned long r_start, r_end; + uint32_t r_start, r_end; DBG ( "Considering [%llx,%llx)\n", region->start, region->end); @@ -81,17 +79,17 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { * with using just 32-bit arithmetic after this stage. */ if ( region->start > max ) { - DBG ( "...starts after max=%lx\n", max ); + DBG ( "...starts after max=%x\n", max ); continue; } r_start = region->start; if ( region->end > max ) { - DBG ( "...end truncated to max=%lx\n", max ); + DBG ( "...end truncated to max=%x\n", max ); r_end = max; } else { r_end = region->end; } - DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end ); + DBG ( "...usable portion is [%x,%x)\n", r_start, r_end ); /* If we have rounded down r_end below r_ start, skip * this block. @@ -103,7 +101,7 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { /* Check that there is enough space to fit in iPXE */ if ( ( r_end - r_start ) < size ) { - DBG ( "...too small (need %lx bytes)\n", size ); + DBG ( "...too small (need %x bytes)\n", size ); continue; } @@ -125,10 +123,10 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) { * required alignemnt. */ new_start = new_end - padded_size; - new_start += ( start - new_start ) & ( max_align - 1 ); + new_start += ( ( start - new_start ) & ( ALIGN - 1 ) ); new_end = new_start + size; - DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n", + DBG ( "Relocating from [%x,%x) to [%x,%x)\n", start, end, new_start, new_end ); /* Let prefix know what to copy */ diff --git a/src/arch/i386/core/runtime.c b/src/arch/x86/core/runtime.c similarity index 100% rename from src/arch/i386/core/runtime.c rename to src/arch/x86/core/runtime.c diff --git a/src/arch/i386/core/stack.S b/src/arch/x86/core/stack.S similarity index 76% rename from src/arch/i386/core/stack.S rename to src/arch/x86/core/stack.S index 98f1cd9b9..995c397ca 100644 --- a/src/arch/i386/core/stack.S +++ b/src/arch/x86/core/stack.S @@ -2,6 +2,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 +#ifdef __x86_64__ +#define STACK_SIZE 8192 +#else +#define STACK_SIZE 4096 +#endif + /**************************************************************************** * Internal stack **************************************************************************** @@ -10,6 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .align 8 .globl _stack _stack: - .space 4096 + .space STACK_SIZE .globl _estack _estack: diff --git a/src/arch/i386/core/stack16.S b/src/arch/x86/core/stack16.S similarity index 100% rename from src/arch/i386/core/stack16.S rename to src/arch/x86/core/stack16.S diff --git a/src/arch/i386/core/video_subr.c b/src/arch/x86/core/video_subr.c similarity index 97% rename from src/arch/i386/core/video_subr.c rename to src/arch/x86/core/video_subr.c index 3f701bd96..f5cc4cdd4 100644 --- a/src/arch/i386/core/video_subr.c +++ b/src/arch/x86/core/video_subr.c @@ -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] = ' '; } diff --git a/src/arch/x86/core/x86_io.c b/src/arch/x86/core/x86_io.c index 3081fa8b9..6c6b6e1e7 100644 --- a/src/arch/x86/core/x86_io.c +++ b/src/arch/x86/core/x86_io.c @@ -74,9 +74,6 @@ static __unused void i386_writeq ( uint64_t data, volatile uint64_t *io_addr ) { PROVIDE_IOAPI_INLINE ( x86, phys_to_bus ); PROVIDE_IOAPI_INLINE ( x86, bus_to_phys ); -PROVIDE_IOAPI_INLINE ( x86, ioremap ); -PROVIDE_IOAPI_INLINE ( x86, iounmap ); -PROVIDE_IOAPI_INLINE ( x86, io_to_bus ); PROVIDE_IOAPI_INLINE ( x86, readb ); PROVIDE_IOAPI_INLINE ( x86, readw ); PROVIDE_IOAPI_INLINE ( x86, readl ); diff --git a/src/arch/x86/core/x86_tcpip.c b/src/arch/x86/core/x86_tcpip.c index 88042f5f7..ed323d5d0 100644 --- a/src/arch/x86/core/x86_tcpip.c +++ b/src/arch/x86/core/x86_tcpip.c @@ -42,8 +42,8 @@ extern char x86_tcpip_loop_end[]; * @v len Length of data buffer * @ret cksum Updated checksum, in network byte order */ -uint16_t x86_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ) { +uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data, + size_t len ) { unsigned long sum = ( ( ~partial ) & 0xffff ); unsigned long initial_word_count; unsigned long loop_count; diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index f73829bd5..1903d1db2 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -39,6 +39,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include #include #include @@ -51,6 +53,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define HV_MESSAGE_MAX_WAIT_MS 1000 +/** Hyper-V timer frequency (fixed 10Mhz) */ +#define HV_TIMER_HZ 10000000 + +/** Hyper-V timer scale factor (used to avoid 64-bit division) */ +#define HV_TIMER_SHIFT 18 + /** * Convert a Hyper-V status code to an iPXE status code * @@ -145,36 +153,48 @@ static void hv_free_message ( struct hv_hypervisor *hv ) { /** * Check whether or not we are running in Hyper-V * - * @v hv Hyper-V hypervisor * @ret rc Return status code */ -static int hv_check_hv ( struct hv_hypervisor *hv ) { +static int hv_check_hv ( void ) { struct x86_features features; uint32_t interface_id; uint32_t discard_ebx; uint32_t discard_ecx; uint32_t discard_edx; - uint32_t available; - uint32_t permissions; /* Check for presence of a hypervisor (not necessarily Hyper-V) */ x86_features ( &features ); if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) { - DBGC ( hv, "HV %p not running in a hypervisor\n", hv ); + DBGC ( HV_INTERFACE_ID, "HV not running in a hypervisor\n" ); return -ENODEV; } /* 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, "HV %p not running in Hyper-V (interface ID " - "%#08x)\n", hv, interface_id ); + DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface " + "ID %#08x)\n", interface_id ); return -ENODEV; } + return 0; +} + +/** + * Check required features + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_check_features ( struct hv_hypervisor *hv ) { + uint32_t available; + uint32_t permissions; + uint32_t discard_ecx; + 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", @@ -201,12 +221,34 @@ static int hv_check_hv ( 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; @@ -226,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 ); @@ -249,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; } /** @@ -278,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 ); @@ -304,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 ); @@ -337,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 * @@ -373,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 ); @@ -509,6 +564,10 @@ static int hv_probe ( struct root_device *rootdev ) { struct hv_hypervisor *hv; int rc; + /* Check we are running in Hyper-V */ + if ( ( rc = hv_check_hv() ) != 0 ) + goto err_check_hv; + /* Allocate and initialise structure */ hv = zalloc ( sizeof ( *hv ) ); if ( ! hv ) { @@ -516,9 +575,13 @@ static int hv_probe ( struct root_device *rootdev ) { goto err_alloc; } - /* Check we are running in Hyper-V */ - if ( ( rc = hv_check_hv ( hv ) ) != 0 ) - goto err_check_hv; + /* Check features */ + 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, @@ -530,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 ) @@ -547,17 +608,17 @@ 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_hv: + err_check_uefi: + err_check_features: free ( hv ); err_alloc: + err_check_hv: return rc; } @@ -576,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 */ @@ -590,6 +652,167 @@ 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 + * + * @ret rc Return status code + */ +static int hv_timer_probe ( void ) { + uint32_t available; + uint32_t discard_ebx; + uint32_t discard_ecx; + uint32_t discard_edx; + int rc; + + /* Check we are running in Hyper-V */ + if ( ( rc = hv_check_hv() ) != 0 ) + return rc; + + /* Check for available reference counter */ + 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" ); + return -ENODEV; + } + + return 0; +} + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long hv_currticks ( void ) { + + /* Calculate time using a combination of bit shifts and + * multiplication (to avoid a 64-bit division). + */ + return ( ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) >> HV_TIMER_SHIFT ) * + ( TICKS_PER_SEC / ( HV_TIMER_HZ >> HV_TIMER_SHIFT ) ) ); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void hv_udelay ( unsigned long usecs ) { + uint32_t start; + uint32_t elapsed; + uint32_t threshold; + + /* Spin until specified number of 10MHz ticks have elapsed */ + start = rdmsr ( HV_X64_MSR_TIME_REF_COUNT ); + threshold = ( usecs * ( HV_TIMER_HZ / 1000000 ) ); + do { + elapsed = ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) - start ); + } while ( elapsed < threshold ); +} + +/** Hyper-V timer */ +struct timer hv_timer __timer ( TIMER_PREFERRED ) = { + .name = "Hyper-V", + .probe = hv_timer_probe, + .currticks = hv_currticks, + .udelay = hv_udelay, +}; + /* Drag in objects via hv_root_device */ REQUIRING_SYMBOL ( hv_root_device ); diff --git a/src/arch/x86/drivers/hyperv/hyperv.h b/src/arch/x86/drivers/hyperv/hyperv.h index 0d09beb44..08031fc6d 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.h +++ b/src/arch/x86/drivers/hyperv/hyperv.h @@ -21,6 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Get hypervisor features */ #define HV_CPUID_FEATURES 0x40000003UL +/** Time reference counter MSR is available */ +#define HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR 0x00000002UL + /** SynIC MSRs are available */ #define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL @@ -39,6 +42,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Hypercall page MSR */ #define HV_X64_MSR_HYPERCALL 0x40000001UL +/** Time reference MSR */ +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020UL + /** SynIC control MSR */ #define HV_X64_MSR_SCONTROL 0x40000080UL diff --git a/src/arch/i386/drivers/net/undi.c b/src/arch/x86/drivers/net/undi.c similarity index 88% rename from src/arch/i386/drivers/net/undi.c rename to src/arch/x86/drivers/net/undi.c index 9820cf629..87c93c3b4 100644 --- a/src/arch/i386/drivers/net/undi.c +++ b/src/arch/x86/drivers/net/undi.c @@ -94,23 +94,14 @@ static int undipci_probe ( struct pci_device *pci ) { } } - /* Add to device hierarchy */ - snprintf ( undi->dev.name, sizeof ( undi->dev.name ), - "UNDI-%s", pci->dev.name ); - memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) ); - undi->dev.parent = &pci->dev; - INIT_LIST_HEAD ( &undi->dev.children ); - list_add ( &undi->dev.siblings, &pci->dev.children ); - /* Create network device */ - if ( ( rc = undinet_probe ( undi ) ) != 0 ) + if ( ( rc = undinet_probe ( undi, &pci->dev ) ) != 0 ) goto err_undinet_probe; return 0; err_undinet_probe: undi_unload ( undi ); - list_del ( &undi->dev.siblings ); err_find_rom: err_load_pci: free ( undi ); @@ -128,7 +119,6 @@ static void undipci_remove ( struct pci_device *pci ) { undinet_remove ( undi ); undi_unload ( undi ); - list_del ( &undi->dev.siblings ); free ( undi ); pci_set_drvdata ( pci, NULL ); } diff --git a/src/arch/i386/drivers/net/undiisr.S b/src/arch/x86/drivers/net/undiisr.S similarity index 96% rename from src/arch/i386/drivers/net/undiisr.S rename to src/arch/x86/drivers/net/undiisr.S index b27effe1d..2428d1f5d 100644 --- a/src/arch/i386/drivers/net/undiisr.S +++ b/src/arch/x86/drivers/net/undiisr.S @@ -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 diff --git a/src/arch/i386/drivers/net/undiload.c b/src/arch/x86/drivers/net/undiload.c similarity index 92% rename from src/arch/i386/drivers/net/undiload.c rename to src/arch/x86/drivers/net/undiload.c index 7160ee384..492dae4ba 100644 --- a/src/arch/i386/drivers/net/undiload.c +++ b/src/arch/x86/drivers/net/undiload.c @@ -72,7 +72,8 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { /* Only one UNDI instance may be loaded at any given time */ if ( undi_loader_entry.segment ) { DBG ( "UNDI %p cannot load multiple instances\n", undi ); - return -EBUSY; + rc = -EBUSY; + goto err_multiple; } /* Set up START_UNDI parameters */ @@ -90,10 +91,15 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { undi_loader.UNDI_CS = fbms_seg; fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 ); undi_loader.UNDI_DS = fbms_seg; + undi->fbms = ( fbms_seg >> 6 ); + set_fbms ( undi->fbms ); + DBGC ( undi, "UNDI %p allocated [%d,%d) kB of base memory\n", + undi, undi->fbms, undi->restore_fbms ); /* Debug info */ - DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ", - undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS ); + DBGC ( undi, "UNDI %p loading ROM %p to CS %04x:%04zx DS %04x:%04zx " + "for ", undi, undirom, undi_loader.UNDI_CS, undirom->code_size, + undi_loader.UNDI_DS, undirom->data_size ); if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { unsigned int bus = ( undi->pci_busdevfn >> 8 ); unsigned int devfn = ( undi->pci_busdevfn & 0xff ); @@ -116,15 +122,11 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { : "=a" ( exit ) : "a" ( __from_data16 ( &undi_loader ) ) : "ebx", "ecx", "edx", "esi", "edi" ); - if ( exit != PXENV_EXIT_SUCCESS ) { - /* Clear entry point */ - memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) ); - rc = -EUNDILOAD ( undi_loader.Status ); DBGC ( undi, "UNDI %p loader failed: %s\n", undi, strerror ( rc ) ); - return rc; + goto err_loader; } /* Populate PXE device structure */ @@ -138,13 +140,13 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset, undi->entry.segment, undi->entry.offset ); - /* Update free base memory counter */ - undi->fbms = ( fbms_seg >> 6 ); - set_fbms ( undi->fbms ); - DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n", - undi, undi->fbms, undi->restore_fbms ); - return 0; + + err_loader: + set_fbms ( undi->restore_fbms ); + memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) ); + err_multiple: + return rc; } /** diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c similarity index 66% rename from src/arch/i386/drivers/net/undinet.c rename to src/arch/x86/drivers/net/undinet.c index 6450665ff..9b7d6d849 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -33,10 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include -#include /** @file * @@ -56,6 +56,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 +86,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 @@ -143,8 +388,7 @@ static void undinet_hook_isr ( unsigned int irq ) { assert ( undiisr_irq == 0 ); undiisr_irq = irq; - hook_bios_interrupt ( IRQ_INT ( irq ), - ( ( unsigned int ) undiisr ), + hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ), &undiisr_next_handler ); } @@ -157,8 +401,7 @@ static void undinet_unhook_isr ( unsigned int irq ) { assert ( irq <= IRQ_MAX ); - unhook_bios_interrupt ( IRQ_INT ( irq ), - ( ( unsigned int ) undiisr ), + unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ), &undiisr_next_handler ); undiisr_irq = 0; } @@ -218,9 +461,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 @@ -283,16 +523,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; } @@ -371,14 +607,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 */ @@ -482,8 +715,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 @@ -492,8 +725,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 ); @@ -518,9 +751,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: @@ -535,8 +767,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 ) { @@ -576,6 +808,10 @@ struct undinet_irq_broken { uint16_t pci_vendor; /** PCI device ID */ uint16_t pci_device; + /** PCI subsystem vendor ID */ + uint16_t pci_subsys_vendor; + /** PCI subsystem ID */ + uint16_t pci_subsys; }; /** @@ -591,26 +827,44 @@ struct undinet_irq_broken { */ static const struct undinet_irq_broken undinet_irq_broken_list[] = { /* HP XX70x laptops */ - { .pci_vendor = 0x8086, .pci_device = 0x1502 }, - { .pci_vendor = 0x8086, .pci_device = 0x1503 }, + { 0x8086, 0x1502, PCI_ANY_ID, PCI_ANY_ID }, + { 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID }, + /* HP 745 G3 laptop */ + { 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID }, }; /** * Check for devices with broken support for generating interrupts * - * @v undi UNDI device + * @v desc Device description * @ret irq_is_broken Interrupt support is broken; no interrupts are generated */ -static int undinet_irq_is_broken ( struct undi_device *undi ) { +static int undinet_irq_is_broken ( struct device_description *desc ) { const struct undinet_irq_broken *broken; + struct pci_device pci; + uint16_t subsys_vendor; + uint16_t subsys; unsigned int i; + /* Ignore non-PCI devices */ + if ( desc->bus_type != BUS_TYPE_PCI ) + return 0; + + /* Read subsystem IDs */ + pci_init ( &pci, desc->location ); + pci_read_config_word ( &pci, PCI_SUBSYSTEM_VENDOR_ID, &subsys_vendor ); + pci_read_config_word ( &pci, PCI_SUBSYSTEM_ID, &subsys ); + + /* Check for a match against the broken device list */ for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) / sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) { broken = &undinet_irq_broken_list[i]; - if ( ( undi->dev.desc.bus_type == BUS_TYPE_PCI ) && - ( undi->dev.desc.vendor == broken->pci_vendor ) && - ( undi->dev.desc.device == broken->pci_device ) ) { + if ( ( broken->pci_vendor == desc->vendor ) && + ( broken->pci_device == desc->device ) && + ( ( broken->pci_subsys_vendor == subsys_vendor ) || + ( broken->pci_subsys_vendor == PCI_ANY_ID ) ) && + ( ( broken->pci_subsys == subsys ) || + ( broken->pci_subsys == PCI_ANY_ID ) ) ) { return 1; } } @@ -621,9 +875,10 @@ static int undinet_irq_is_broken ( struct undi_device *undi ) { * Probe UNDI device * * @v undi UNDI device + * @v dev Underlying generic device * @ret rc Return status code */ -int undinet_probe ( struct undi_device *undi ) { +int undinet_probe ( struct undi_device *undi, struct device *dev ) { struct net_device *netdev; struct undi_nic *undinic; struct s_PXENV_START_UNDI start_undi; @@ -644,9 +899,9 @@ int undinet_probe ( struct undi_device *undi ) { netdev_init ( netdev, &undinet_operations ); undinic = netdev->priv; undi_set_drvdata ( undi, netdev ); - netdev->dev = &undi->dev; + 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 */ @@ -657,9 +912,9 @@ int undinet_probe ( struct undi_device *undi ) { 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; @@ -667,9 +922,9 @@ int undinet_probe ( struct undi_device *undi ) { /* 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 @@ -679,10 +934,10 @@ int undinet_probe ( struct undi_device *undi ) { */ 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; @@ -697,26 +952,24 @@ int undinet_probe ( struct undi_device *undi ) { /* 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 ); undinic->irq = undi_info.IntNumber; if ( undinic->irq > IRQ_MAX ) { - DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n", + DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n", undinic, undinic->irq ); - rc = -EINVAL; - goto err_bad_irq; + undinic->irq = 0; } DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n", undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq ); /* 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, @@ -733,7 +986,7 @@ int undinet_probe ( struct undi_device *undi ) { undinic ); undinic->hacks |= UNDI_HACK_EB54; } - if ( undinet_irq_is_broken ( undi ) ) { + if ( undinet_irq_is_broken ( &dev->desc ) ) { DBGC ( undinic, "UNDINIC %p forcing polling mode due to " "broken interrupts\n", undinic ); undinic->irq_supported = 0; @@ -751,22 +1004,21 @@ int undinet_probe ( struct undi_device *undi ) { err_register: err_undi_get_iface_info: - err_bad_irq: err_undi_get_information: 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 ); @@ -797,22 +1049,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 ); diff --git a/src/arch/i386/drivers/net/undionly.c b/src/arch/x86/drivers/net/undionly.c similarity index 81% rename from src/arch/i386/drivers/net/undionly.c rename to src/arch/x86/drivers/net/undionly.c index 70dbe4bfd..9c9ca1274 100644 --- a/src/arch/i386/drivers/net/undionly.c +++ b/src/arch/x86/drivers/net/undionly.c @@ -50,6 +50,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * addition to the UNDI driver, build e.g. "bin/undi.dsk". */ +/** UNDI root bus device */ +static struct device undibus_dev; + /** * Probe UNDI root bus * @@ -60,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ static int undibus_probe ( struct root_device *rootdev ) { struct undi_device *undi = &preloaded_undi; + struct device *dev = &undibus_dev; int rc; /* Check for a valie preloaded UNDI device */ @@ -69,34 +73,32 @@ static int undibus_probe ( struct root_device *rootdev ) { } /* Add to device hierarchy */ - undi->dev.driver_name = "undionly"; + dev->driver_name = "undionly"; if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { - undi->dev.desc.bus_type = BUS_TYPE_PCI; - undi->dev.desc.location = undi->pci_busdevfn; - undi->dev.desc.vendor = undi->pci_vendor; - undi->dev.desc.device = undi->pci_device; - snprintf ( undi->dev.name, sizeof ( undi->dev.name ), - "UNDI-PCI%02x:%02x.%x", - PCI_BUS ( undi->pci_busdevfn ), + dev->desc.bus_type = BUS_TYPE_PCI; + dev->desc.location = undi->pci_busdevfn; + dev->desc.vendor = undi->pci_vendor; + dev->desc.device = undi->pci_device; + snprintf ( dev->name, sizeof ( dev->name ), + "0000:%02x:%02x.%x", PCI_BUS ( undi->pci_busdevfn ), PCI_SLOT ( undi->pci_busdevfn ), PCI_FUNC ( undi->pci_busdevfn ) ); } else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) { - undi->dev.desc.bus_type = BUS_TYPE_ISAPNP; - snprintf ( undi->dev.name, sizeof ( undi->dev.name ), - "UNDI-ISAPNP" ); + dev->desc.bus_type = BUS_TYPE_ISAPNP; + snprintf ( dev->name, sizeof ( dev->name ), "ISAPNP" ); } - undi->dev.parent = &rootdev->dev; - list_add ( &undi->dev.siblings, &rootdev->dev.children); - INIT_LIST_HEAD ( &undi->dev.children ); + dev->parent = &rootdev->dev; + list_add ( &dev->siblings, &rootdev->dev.children); + INIT_LIST_HEAD ( &dev->children ); /* Create network device */ - if ( ( rc = undinet_probe ( undi ) ) != 0 ) + if ( ( rc = undinet_probe ( undi, dev ) ) != 0 ) goto err; return 0; err: - list_del ( &undi->dev.siblings ); + list_del ( &dev->siblings ); return rc; } @@ -107,9 +109,10 @@ static int undibus_probe ( struct root_device *rootdev ) { */ static void undibus_remove ( struct root_device *rootdev __unused ) { struct undi_device *undi = &preloaded_undi; + struct device *dev = &undibus_dev; undinet_remove ( undi ); - list_del ( &undi->dev.siblings ); + list_del ( &dev->siblings ); } /** UNDI bus root device driver */ diff --git a/src/arch/i386/drivers/net/undipreload.c b/src/arch/x86/drivers/net/undipreload.c similarity index 100% rename from src/arch/i386/drivers/net/undipreload.c rename to src/arch/x86/drivers/net/undipreload.c diff --git a/src/arch/i386/drivers/net/undirom.c b/src/arch/x86/drivers/net/undirom.c similarity index 99% rename from src/arch/i386/drivers/net/undirom.c rename to src/arch/x86/drivers/net/undirom.c index b54c6170f..257b12411 100644 --- a/src/arch/i386/drivers/net/undirom.c +++ b/src/arch/x86/drivers/net/undirom.c @@ -168,7 +168,7 @@ static int undirom_probe ( unsigned int rom_segment ) { /* Add to UNDI ROM list and return */ DBGC ( undirom, "UNDIROM %p registered\n", undirom ); - list_add ( &undirom->list, &undiroms ); + list_add_tail ( &undirom->list, &undiroms ); return 0; err: diff --git a/src/arch/x86/drivers/xen/hvm.c b/src/arch/x86/drivers/xen/hvm.c index 7ac32d54c..57196f555 100644 --- a/src/arch/x86/drivers/xen/hvm.c +++ b/src/arch/x86/drivers/xen/hvm.c @@ -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 */ diff --git a/src/arch/i386/hci/commands/pxe_cmd.c b/src/arch/x86/hci/commands/pxe_cmd.c similarity index 100% rename from src/arch/i386/hci/commands/pxe_cmd.c rename to src/arch/x86/hci/commands/pxe_cmd.c diff --git a/src/arch/i386/image/bootsector.c b/src/arch/x86/image/bootsector.c similarity index 93% rename from src/arch/i386/image/bootsector.c rename to src/arch/x86/image/bootsector.c index dba87613c..67dad04f8 100644 --- a/src/arch/i386/image/bootsector.c +++ b/src/arch/x86/image/bootsector.c @@ -71,9 +71,9 @@ int call_bootsector ( unsigned int segment, unsigned int offset, DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset ); /* Hook INTs 18 and 19 to capture failure paths */ - hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + hook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail, &int18_vector ); - hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + hook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail, &int19_vector ); /* Boot the loaded sector @@ -132,9 +132,9 @@ int call_bootsector ( unsigned int segment, unsigned int offset, DBG ( "Booted disk returned via INT 18 or 19\n" ); /* Unhook INTs 18 and 19 */ - unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + unhook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail, &int18_vector ); - unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + unhook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail, &int19_vector ); return -ECANCELED; diff --git a/src/arch/i386/image/bzimage.c b/src/arch/x86/image/bzimage.c similarity index 98% rename from src/arch/i386/image/bzimage.c rename to src/arch/x86/image/bzimage.c index a64206cd3..51498bf95 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -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; @@ -522,10 +524,12 @@ static void bzimage_load_initrds ( struct image *image, /* Find highest usable address */ top = userptr_add ( highest->data, bzimage_align ( highest->len ) ); - if ( user_to_phys ( top, 0 ) > bzimg->mem_limit ) - top = phys_to_user ( bzimg->mem_limit ); + if ( user_to_phys ( top, -1 ) > bzimg->mem_limit ) { + top = phys_to_user ( ( bzimg->mem_limit + 1 ) & + ~( INITRD_ALIGN - 1 ) ); + } DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n", - image, user_to_phys ( top, 0 ) ); + image, user_to_phys ( top, -1 ) ); /* Load initrds in order */ for_each_image ( initrd ) { @@ -631,9 +635,9 @@ static int bzimage_exec ( struct image *image ) { "pushw %w2\n\t" "pushw $0\n\t" "lret\n\t" ) - : : "r" ( bzimg.rm_kernel_seg ), - "r" ( bzimg.rm_heap ), - "r" ( bzimg.rm_kernel_seg + 0x20 ) ); + : : "R" ( bzimg.rm_kernel_seg ), + "R" ( bzimg.rm_heap ), + "R" ( bzimg.rm_kernel_seg + 0x20 ) ); /* There is no way for the image to return, since we provide * no return address. diff --git a/src/arch/i386/image/com32.c b/src/arch/x86/image/com32.c similarity index 77% rename from src/arch/i386/image/com32.c rename to src/arch/x86/image/com32.c index ff64fd1a1..016652877 100644 --- a/src/arch/i386/image/com32.c +++ b/src/arch/x86/image/com32.c @@ -76,8 +76,6 @@ static int com32_exec_loop ( struct image *image ) { assert ( avail_mem_top != 0 ); - com32_external_esp = phys_to_virt ( avail_mem_top ); - /* Hook COMBOOT API interrupts */ hook_comboot_interrupts(); @@ -88,34 +86,44 @@ static int com32_exec_loop ( struct image *image ) { */ unregister_image ( image ); - __asm__ __volatile__ ( - "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ - "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ - "call _virt_to_phys\n\t" /* Switch to flat physical address space */ - "sti\n\t" /* Enable interrupts */ - "pushl %0\n\t" /* Pointer to CDECL helper function */ - "pushl %1\n\t" /* Pointer to FAR call helper function */ - "pushl %2\n\t" /* Size of low memory bounce buffer */ - "pushl %3\n\t" /* Pointer to low memory bounce buffer */ - "pushl %4\n\t" /* Pointer to INT call helper function */ - "pushl %5\n\t" /* Pointer to the command line arguments */ - "pushl $6\n\t" /* Number of additional arguments */ - "call *%6\n\t" /* Execute image */ - "cli\n\t" /* Disable interrupts */ - "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ - "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ - : - : - /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), - /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), - /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), - /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), - /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), - /* %5 */ "r" ( virt_to_phys ( image->cmdline ? - image->cmdline : "" ) ), - /* %6 */ "r" ( COM32_START_PHYS ) - : - "memory" ); + __asm__ __volatile__ ( PHYS_CODE ( + /* Preserve registers */ + "pushal\n\t" + /* Preserve stack pointer */ + "subl $4, %k0\n\t" + "movl %%esp, (%k0)\n\t" + /* Switch to COM32 stack */ + "movl %k0, %%esp\n\t" + /* Enable interrupts */ + "sti\n\t" + /* Construct stack frame */ + "pushl %k1\n\t" + "pushl %k2\n\t" + "pushl %k3\n\t" + "pushl %k4\n\t" + "pushl %k5\n\t" + "pushl %k6\n\t" + "pushl $6\n\t" + /* Call COM32 entry point */ + "movl %k7, %k0\n\t" + "call *%k0\n\t" + /* Disable interrupts */ + "cli\n\t" + /* Restore stack pointer */ + "movl 24(%%esp), %%esp\n\t" + /* Restore registers */ + "popal\n\t" ) + : + : "r" ( avail_mem_top ), + "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), + "r" ( virt_to_phys ( com32_farcall_wrapper ) ), + "r" ( get_fbms() * 1024 - ( COM32_BOUNCE_SEG << 4 ) ), + "i" ( COM32_BOUNCE_SEG << 4 ), + "r" ( virt_to_phys ( com32_intcall_wrapper ) ), + "r" ( virt_to_phys ( image->cmdline ? + image->cmdline : "" ) ), + "i" ( COM32_START_PHYS ) + : "memory" ); DBGC ( image, "COM32 %p: returned\n", image ); break; @@ -147,7 +155,7 @@ static int com32_exec_loop ( struct image *image ) { /** * Check image name extension - * + * * @v image COM32 image * @ret rc Return status code */ @@ -155,7 +163,7 @@ static int com32_identify ( struct image *image ) { const char *ext; static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; uint8_t buf[5]; - + if ( image->len >= 5 ) { /* Check for magic number * mov eax,21cd4cffh diff --git a/src/arch/i386/image/comboot.c b/src/arch/x86/image/comboot.c similarity index 99% rename from src/arch/i386/image/comboot.c rename to src/arch/x86/image/comboot.c index 20b5ae1e7..9a847f0ff 100644 --- a/src/arch/i386/image/comboot.c +++ b/src/arch/x86/image/comboot.c @@ -64,7 +64,7 @@ struct comboot_psp { /** * Copy command line to PSP - * + * * @v image COMBOOT image */ static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) { @@ -97,7 +97,7 @@ static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) /** * Initialize PSP - * + * * @v image COMBOOT image * @v seg_userptr segment to initialize */ @@ -213,7 +213,7 @@ static int comboot_exec_loop ( struct image *image ) { /** * Check image name extension - * + * * @v image COMBOOT image * @ret rc Return status code */ @@ -254,7 +254,7 @@ static int comboot_prepare_segment ( struct image *image ) seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); /* Allow etra 0x100 bytes before image for PSP */ - filesz = image->len + 0x100; + filesz = image->len + 0x100; /* Ensure the entire 64k segment is free */ memsz = 0xFFFF; @@ -289,7 +289,7 @@ static int comboot_probe ( struct image *image ) { /* Check if this is a COMBOOT image */ if ( ( rc = comboot_identify ( image ) ) != 0 ) { - + return rc; } @@ -304,7 +304,7 @@ static int comboot_probe ( struct image *image ) { */ static int comboot_exec ( struct image *image ) { int rc; - + /* Sanity check for filesize */ if( image->len >= 0xFF00 ) { DBGC( image, "COMBOOT %p: image too large\n", diff --git a/src/arch/i386/image/elfboot.c b/src/arch/x86/image/elfboot.c similarity index 100% rename from src/arch/i386/image/elfboot.c rename to src/arch/x86/image/elfboot.c diff --git a/src/arch/i386/image/initrd.c b/src/arch/x86/image/initrd.c similarity index 100% rename from src/arch/i386/image/initrd.c rename to src/arch/x86/image/initrd.c diff --git a/src/arch/i386/image/multiboot.c b/src/arch/x86/image/multiboot.c similarity index 100% rename from src/arch/i386/image/multiboot.c rename to src/arch/x86/image/multiboot.c diff --git a/src/arch/i386/image/nbi.c b/src/arch/x86/image/nbi.c similarity index 99% rename from src/arch/i386/image/nbi.c rename to src/arch/x86/image/nbi.c index 99046144d..b691bee20 100644 --- a/src/arch/i386/image/nbi.c +++ b/src/arch/x86/image/nbi.c @@ -241,7 +241,7 @@ static int nbi_process_segments ( struct image *image, */ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) { int discard_D, discard_S, discard_b; - int rc; + int32_t rc; DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image, imgheader->execaddr.segoff.segment, @@ -283,7 +283,7 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) { 0 }; int discard_D, discard_S, discard_b; - int rc; + int32_t rc; DBGC ( image, "NBI %p executing 32-bit image at %lx\n", image, imgheader->execaddr.linear ); diff --git a/src/arch/i386/image/pxe_image.c b/src/arch/x86/image/pxe_image.c similarity index 96% rename from src/arch/i386/image/pxe_image.c rename to src/arch/x86/image/pxe_image.c index 297a618b8..b6bcb18b4 100644 --- a/src/arch/i386/image/pxe_image.c +++ b/src/arch/x86/image/pxe_image.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -87,6 +88,10 @@ static int pxe_exec ( struct image *image ) { /* Reset console since PXE NBP will probably use it */ console_reset(); + /* Disable IRQ, if applicable */ + if ( netdev_irq_supported ( netdev ) && netdev->dev->desc.irq ) + disable_irq ( netdev->dev->desc.irq ); + /* Start PXE NBP */ rc = pxe_start_nbp(); diff --git a/src/arch/i386/image/sdi.c b/src/arch/x86/image/sdi.c similarity index 100% rename from src/arch/i386/image/sdi.c rename to src/arch/x86/image/sdi.c diff --git a/src/arch/i386/include/basemem.h b/src/arch/x86/include/basemem.h similarity index 100% rename from src/arch/i386/include/basemem.h rename to src/arch/x86/include/basemem.h diff --git a/src/arch/i386/include/basemem_packet.h b/src/arch/x86/include/basemem_packet.h similarity index 100% rename from src/arch/i386/include/basemem_packet.h rename to src/arch/x86/include/basemem_packet.h diff --git a/src/arch/i386/include/bios.h b/src/arch/x86/include/bios.h similarity index 79% rename from src/arch/i386/include/bios.h rename to src/arch/x86/include/bios.h index 988bbc62b..14e7acbc7 100644 --- a/src/arch/i386/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -4,8 +4,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 +#define BDA_EBDA 0x000e #define BDA_EQUIPMENT_WORD 0x0010 #define BDA_FBMS 0x0013 +#define BDA_TICKS 0x006c +#define BDA_MIDNIGHT 0x0070 #define BDA_REBOOT 0x0072 #define BDA_REBOOT_WARM 0x1234 #define BDA_NUM_DRIVES 0x0075 diff --git a/src/arch/i386/include/bios_disks.h b/src/arch/x86/include/bios_disks.h similarity index 100% rename from src/arch/i386/include/bios_disks.h rename to src/arch/x86/include/bios_disks.h diff --git a/src/arch/i386/include/biosint.h b/src/arch/x86/include/biosint.h similarity index 95% rename from src/arch/i386/include/biosint.h rename to src/arch/x86/include/biosint.h index 67d6a3811..f47116f70 100644 --- a/src/arch/i386/include/biosint.h +++ b/src/arch/x86/include/biosint.h @@ -29,5 +29,6 @@ extern void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler, extern int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler, struct segoff *chain_vector ); +extern void check_bios_interrupts ( void ); #endif /* BIOSINT_H */ diff --git a/src/arch/x86/include/bits/acpi.h b/src/arch/x86/include/bits/acpi.h new file mode 100644 index 000000000..a6ff90804 --- /dev/null +++ b/src/arch/x86/include/bits/acpi.h @@ -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 + +#endif /* _BITS_ACPI_H */ diff --git a/src/arch/x86/include/bits/bitops.h b/src/arch/x86/include/bits/bitops.h new file mode 100644 index 000000000..17dcf1024 --- /dev/null +++ b/src/arch/x86/include/bits/bitops.h @@ -0,0 +1,94 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * x86 bit operations + * + * We perform atomic bit set and bit clear operations using "lock bts" + * and "lock btr". We use the output constraint to inform the + * compiler that any memory from the start of the bit field up to and + * including the byte containing the bit may be modified. (This is + * overkill but shouldn't matter in practice since we're unlikely to + * subsequently read other bits from the same bit field.) + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + + __asm__ __volatile__ ( "lock bts %1, %0" + : "+m" ( *bytes ) : "Ir" ( bit ) ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + + __asm__ __volatile__ ( "lock btr %1, %0" + : "+m" ( *bytes ) : "Ir" ( bit ) ); +} + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + int old; + + __asm__ __volatile__ ( "lock bts %2, %0\n\t" + "sbb %1, %1\n\t" + : "+m" ( *bytes ), "=r" ( old ) + : "Ir" ( bit ) ); + return old; +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + volatile struct { + uint8_t byte[ ( bit / 8 ) + 1 ]; + } *bytes = bits; + int old; + + __asm__ __volatile__ ( "lock btr %2, %0\n\t" + "sbb %1, %1\n\t" + : "+m" ( *bytes ), "=r" ( old ) + : "Ir" ( bit ) ); + return old; +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/arch/i386/include/bits/entropy.h b/src/arch/x86/include/bits/entropy.h similarity index 79% rename from src/arch/i386/include/bits/entropy.h rename to src/arch/x86/include/bits/entropy.h index bfeb5e3b5..5ac7fcd2e 100644 --- a/src/arch/i386/include/bits/entropy.h +++ b/src/arch/x86/include/bits/entropy.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific entropy API implementations + * x86-specific entropy API implementations * */ diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 0d1617d20..b0ae1abc2 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -22,6 +22,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_apm ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 ) #define ERRFILE_vesafb ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 ) #define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) +#define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 ) +#define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 ) +#define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 ) +#define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 ) +#define ERRFILE_rdtsc_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 ) +#define ERRFILE_acpi_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00130000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) @@ -52,7 +58,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) -#define ERRFILE_efi_entropy ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00020000 ) /** @} */ diff --git a/src/arch/x86/include/bits/iomap.h b/src/arch/x86/include/bits/iomap.h new file mode 100644 index 000000000..d6fff257e --- /dev/null +++ b/src/arch/x86/include/bits/iomap.h @@ -0,0 +1,14 @@ +#ifndef _BITS_IOMAP_H +#define _BITS_IOMAP_H + +/** @file + * + * x86-specific I/O mapping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_IOMAP_H */ diff --git a/src/arch/i386/include/bits/nap.h b/src/arch/x86/include/bits/nap.h similarity index 79% rename from src/arch/i386/include/bits/nap.h rename to src/arch/x86/include/bits/nap.h index e8bcfd13b..7103b94c0 100644 --- a/src/arch/i386/include/bits/nap.h +++ b/src/arch/x86/include/bits/nap.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific CPU sleeping API implementations + * x86-specific CPU sleeping API implementations * */ diff --git a/src/arch/i386/include/bits/reboot.h b/src/arch/x86/include/bits/reboot.h similarity index 79% rename from src/arch/i386/include/bits/reboot.h rename to src/arch/x86/include/bits/reboot.h index 803dacfe4..e702dd3d0 100644 --- a/src/arch/i386/include/bits/reboot.h +++ b/src/arch/x86/include/bits/reboot.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific reboot API implementations + * x86-specific reboot API implementations * */ diff --git a/src/arch/i386/include/bits/sanboot.h b/src/arch/x86/include/bits/sanboot.h similarity index 79% rename from src/arch/i386/include/bits/sanboot.h rename to src/arch/x86/include/bits/sanboot.h index f02d2e649..1b9924e64 100644 --- a/src/arch/i386/include/bits/sanboot.h +++ b/src/arch/x86/include/bits/sanboot.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific sanboot API implementations + * x86-specific sanboot API implementations * */ diff --git a/src/arch/i386/include/bits/smbios.h b/src/arch/x86/include/bits/smbios.h similarity index 79% rename from src/arch/i386/include/bits/smbios.h rename to src/arch/x86/include/bits/smbios.h index 2ab31e74b..9977c87ac 100644 --- a/src/arch/i386/include/bits/smbios.h +++ b/src/arch/x86/include/bits/smbios.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific SMBIOS API implementations + * x86-specific SMBIOS API implementations * */ diff --git a/src/arch/x86/include/bits/tcpip.h b/src/arch/x86/include/bits/tcpip.h index 5c2baffcf..0ac55b1a0 100644 --- a/src/arch/x86/include/bits/tcpip.h +++ b/src/arch/x86/include/bits/tcpip.h @@ -9,9 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -extern uint16_t x86_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ); - -#define tcpip_continue_chksum x86_tcpip_continue_chksum +extern uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data, + size_t len ); #endif /* _BITS_TCPIP_H */ diff --git a/src/arch/i386/include/bits/time.h b/src/arch/x86/include/bits/time.h similarity index 79% rename from src/arch/i386/include/bits/time.h rename to src/arch/x86/include/bits/time.h index 6a5d63d32..556d96f64 100644 --- a/src/arch/i386/include/bits/time.h +++ b/src/arch/x86/include/bits/time.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific time API implementations + * x86-specific time API implementations * */ diff --git a/src/arch/i386/include/bits/uaccess.h b/src/arch/x86/include/bits/uaccess.h similarity index 76% rename from src/arch/i386/include/bits/uaccess.h rename to src/arch/x86/include/bits/uaccess.h index aac09ba95..e9e7e5af5 100644 --- a/src/arch/i386/include/bits/uaccess.h +++ b/src/arch/x86/include/bits/uaccess.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific user access API implementations + * x86-specific user access API implementations * */ diff --git a/src/arch/i386/include/bits/umalloc.h b/src/arch/x86/include/bits/umalloc.h similarity index 74% rename from src/arch/i386/include/bits/umalloc.h rename to src/arch/x86/include/bits/umalloc.h index 113f16fd1..5d1f554d8 100644 --- a/src/arch/i386/include/bits/umalloc.h +++ b/src/arch/x86/include/bits/umalloc.h @@ -3,7 +3,7 @@ /** @file * - * i386-specific user memory allocation API implementations + * x86-specific user memory allocation API implementations * */ diff --git a/src/arch/x86/include/bits/xen.h b/src/arch/x86/include/bits/xen.h index fc065ea38..3433cea1f 100644 --- a/src/arch/x86/include/bits/xen.h +++ b/src/arch/x86/include/bits/xen.h @@ -161,23 +161,4 @@ xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall, return retval; } -/** - * Test and clear pending event - * - * @v xen Xen hypervisor - * @v port Event channel port - * @ret pending Event was pending - */ -static inline __attribute__ (( always_inline )) uint8_t -xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) { - uint8_t pending; - - __asm__ __volatile__ ( "lock btr %2, %0\n\t" - "setc %1\n\t" - : "+m" ( xen->shared->evtchn_pending ), - "=a" ( pending ) - : "Ir" ( port ) ); - return pending; -} - #endif /* _BITS_XEN_H */ diff --git a/src/arch/i386/include/bochs.h b/src/arch/x86/include/bochs.h similarity index 100% rename from src/arch/i386/include/bochs.h rename to src/arch/x86/include/bochs.h diff --git a/src/arch/i386/include/bootsector.h b/src/arch/x86/include/bootsector.h similarity index 100% rename from src/arch/i386/include/bootsector.h rename to src/arch/x86/include/bootsector.h diff --git a/src/arch/i386/include/bzimage.h b/src/arch/x86/include/bzimage.h similarity index 100% rename from src/arch/i386/include/bzimage.h rename to src/arch/x86/include/bzimage.h diff --git a/src/arch/i386/include/comboot.h b/src/arch/x86/include/comboot.h similarity index 95% rename from src/arch/i386/include/comboot.h rename to src/arch/x86/include/comboot.h index 2d2f04fe1..69c6ef024 100644 --- a/src/arch/i386/include/comboot.h +++ b/src/arch/x86/include/comboot.h @@ -10,7 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include -#include +#include #include /** Segment used for COMBOOT PSP and image */ @@ -29,7 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define COMBOOT_FEATURE_LOCAL_BOOT (1 << 0) #define COMBOOT_FEATURE_IDLE_LOOP (1 << 1) -/** Maximum number of shuffle descriptors for +/** Maximum number of shuffle descriptors for * shuffle and boot functions * (INT 22h AX=0012h, 001Ah, 001Bh) */ @@ -102,7 +102,7 @@ typedef struct { extern void hook_comboot_interrupts ( ); extern void unhook_comboot_interrupts ( ); -/* These are not the correct prototypes, but it doens't matter, +/* These are not the correct prototypes, but it doens't matter, * as we only ever get the address of these functions; * they are only called from COM32 code running in PHYS_CODE */ @@ -116,8 +116,6 @@ extern int comboot_resolv ( const char *name, struct in_addr *address ); /* setjmp/longjmp context buffer used to return after loading an image */ extern rmjmp_buf comboot_return; -extern void *com32_external_esp; - #define COMBOOT_EXIT 1 #define COMBOOT_EXIT_RUN_KERNEL 2 #define COMBOOT_EXIT_COMMAND 3 diff --git a/src/arch/i386/include/fakee820.h b/src/arch/x86/include/fakee820.h similarity index 100% rename from src/arch/i386/include/fakee820.h rename to src/arch/x86/include/fakee820.h diff --git a/src/arch/i386/include/initrd.h b/src/arch/x86/include/initrd.h similarity index 100% rename from src/arch/i386/include/initrd.h rename to src/arch/x86/include/initrd.h diff --git a/src/arch/i386/include/int13.h b/src/arch/x86/include/int13.h similarity index 100% rename from src/arch/i386/include/int13.h rename to src/arch/x86/include/int13.h diff --git a/src/arch/x86/include/ipxe/acpipwr.h b/src/arch/x86/include/ipxe/acpipwr.h new file mode 100644 index 000000000..93da09429 --- /dev/null +++ b/src/arch/x86/include/ipxe/acpipwr.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_ACPIPWR_H +#define _IPXE_ACPIPWR_H + +/** @file + * + * ACPI power off + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int acpi_poweroff ( void ); + +#endif /* _IPXE_ACPIPWR_H */ diff --git a/src/arch/x86/include/ipxe/apm.h b/src/arch/x86/include/ipxe/apm.h new file mode 100644 index 000000000..21d913ac4 --- /dev/null +++ b/src/arch/x86/include/ipxe/apm.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_APM_H +#define _IPXE_APM_H + +/** @file + * + * Advanced Power Management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int apm_poweroff ( void ); + +#endif /* _IPXE_APM_H */ diff --git a/src/arch/i386/include/ipxe/bios_nap.h b/src/arch/x86/include/ipxe/bios_nap.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_nap.h rename to src/arch/x86/include/ipxe/bios_nap.h diff --git a/src/arch/i386/include/ipxe/bios_reboot.h b/src/arch/x86/include/ipxe/bios_reboot.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_reboot.h rename to src/arch/x86/include/ipxe/bios_reboot.h diff --git a/src/arch/i386/include/ipxe/bios_sanboot.h b/src/arch/x86/include/ipxe/bios_sanboot.h similarity index 55% rename from src/arch/i386/include/ipxe/bios_sanboot.h rename to src/arch/x86/include/ipxe/bios_sanboot.h index 1a86b7d57..85d698039 100644 --- a/src/arch/i386/include/ipxe/bios_sanboot.h +++ b/src/arch/x86/include/ipxe/bios_sanboot.h @@ -15,15 +15,4 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SANBOOT_PREFIX_pcbios __pcbios_ #endif -/** - * Get default SAN drive number - * - * @ret drive Default drive number - */ -static inline __always_inline unsigned int -SANBOOT_INLINE ( pcbios, san_default_drive ) ( void ) { - /* Default to booting from first hard disk */ - return 0x80; -} - #endif /* _IPXE_BIOS_SANBOOT_H */ diff --git a/src/arch/i386/include/ipxe/bios_smbios.h b/src/arch/x86/include/ipxe/bios_smbios.h similarity index 100% rename from src/arch/i386/include/ipxe/bios_smbios.h rename to src/arch/x86/include/ipxe/bios_smbios.h diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index da85d0b88..0ae572da4 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -57,25 +57,32 @@ struct x86_features { /** Get CPU model */ #define CPUID_MODEL 0x80000002UL +/** Get APM information */ +#define CPUID_APM 0x80000007UL + +/** Invariant TSC */ +#define CPUID_APM_EDX_TSC_INVARIANT 0x00000100UL + /** * Issue CPUID instruction * - * @v operation CPUID operation + * @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 operation, 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" ( operation ) ); + : "0" ( function ), "2" ( subfunction ) ); } -extern int cpuid_is_supported ( void ); +extern int cpuid_supported ( uint32_t function ); extern void x86_features ( struct x86_features *features ); #endif /* _IPXE_CPUID_H */ diff --git a/src/arch/i386/include/ipxe/errno/pcbios.h b/src/arch/x86/include/ipxe/errno/pcbios.h similarity index 100% rename from src/arch/i386/include/ipxe/errno/pcbios.h rename to src/arch/x86/include/ipxe/errno/pcbios.h diff --git a/src/arch/i386/include/ipxe/guestrpc.h b/src/arch/x86/include/ipxe/guestrpc.h similarity index 100% rename from src/arch/i386/include/ipxe/guestrpc.h rename to src/arch/x86/include/ipxe/guestrpc.h diff --git a/src/arch/x86/include/ipxe/iomap_pages.h b/src/arch/x86/include/ipxe/iomap_pages.h new file mode 100644 index 000000000..18e0a3002 --- /dev/null +++ b/src/arch/x86/include/ipxe/iomap_pages.h @@ -0,0 +1,24 @@ +#ifndef _IPXE_IOMAP_PAGES_H +#define _IPXE_IOMAP_PAGES_H + +/** @file + * + * I/O mapping API using page tables + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOMAP_PAGES +#define IOMAP_PREFIX_pages +#else +#define IOMAP_PREFIX_pages __pages_ +#endif + +static inline __always_inline unsigned long +IOMAP_INLINE ( pages, io_to_bus ) ( volatile const void *io_addr ) { + /* Not easy to do; just return the CPU address for debugging purposes */ + return ( ( intptr_t ) io_addr ); +} + +#endif /* _IPXE_IOMAP_PAGES_H */ diff --git a/src/arch/i386/include/ipxe/memtop_umalloc.h b/src/arch/x86/include/ipxe/memtop_umalloc.h similarity index 100% rename from src/arch/i386/include/ipxe/memtop_umalloc.h rename to src/arch/x86/include/ipxe/memtop_umalloc.h diff --git a/src/arch/x86/include/ipxe/rsdp.h b/src/arch/x86/include/ipxe/rsdp.h new file mode 100644 index 000000000..7e32c0011 --- /dev/null +++ b/src/arch/x86/include/ipxe/rsdp.h @@ -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 */ diff --git a/src/arch/i386/include/ipxe/rtc_entropy.h b/src/arch/x86/include/ipxe/rtc_entropy.h similarity index 94% rename from src/arch/i386/include/ipxe/rtc_entropy.h rename to src/arch/x86/include/ipxe/rtc_entropy.h index e214745d0..581abcd3e 100644 --- a/src/arch/i386/include/ipxe/rtc_entropy.h +++ b/src/arch/x86/include/ipxe/rtc_entropy.h @@ -22,7 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @ret min_entropy min-entropy of each sample */ -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) { /* The min-entropy has been measured on several platforms @@ -38,7 +38,7 @@ ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) { * safety margin to allow for some potential non-independence * of samples. */ - return 1.3; + return MIN_ENTROPY ( 1.3 ); } extern uint8_t rtc_sample ( void ); diff --git a/src/arch/i386/include/ipxe/rtc_time.h b/src/arch/x86/include/ipxe/rtc_time.h similarity index 100% rename from src/arch/i386/include/ipxe/rtc_time.h rename to src/arch/x86/include/ipxe/rtc_time.h diff --git a/src/arch/i386/include/ipxe/vesafb.h b/src/arch/x86/include/ipxe/vesafb.h similarity index 100% rename from src/arch/i386/include/ipxe/vesafb.h rename to src/arch/x86/include/ipxe/vesafb.h diff --git a/src/arch/i386/include/ipxe/vmware.h b/src/arch/x86/include/ipxe/vmware.h similarity index 100% rename from src/arch/i386/include/ipxe/vmware.h rename to src/arch/x86/include/ipxe/vmware.h diff --git a/src/arch/x86/include/ipxe/x86_io.h b/src/arch/x86/include/ipxe/x86_io.h index 5214e9fbb..a6ebe1f4c 100644 --- a/src/arch/x86/include/ipxe/x86_io.h +++ b/src/arch/x86/include/ipxe/x86_io.h @@ -32,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PAGE_SHIFT 12 /* - * Physical<->Bus and Bus<->I/O address mappings + * Physical<->Bus address mappings * */ @@ -46,21 +46,6 @@ IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) { return bus_addr; } -static inline __always_inline void * -IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { - return ( bus_addr ? phys_to_virt ( bus_addr ) : NULL ); -} - -static inline __always_inline void -IOAPI_INLINE ( x86, iounmap ) ( volatile const void *io_addr __unused ) { - /* Nothing to do */ -} - -static inline __always_inline unsigned long -IOAPI_INLINE ( x86, io_to_bus ) ( volatile const void *io_addr ) { - return virt_to_phys ( io_addr ); -} - /* * MMIO reads and writes up to native word size * diff --git a/src/arch/i386/include/kir.h b/src/arch/x86/include/kir.h similarity index 100% rename from src/arch/i386/include/kir.h rename to src/arch/x86/include/kir.h diff --git a/src/arch/i386/include/libkir.h b/src/arch/x86/include/libkir.h similarity index 100% rename from src/arch/i386/include/libkir.h rename to src/arch/x86/include/libkir.h diff --git a/src/arch/i386/include/librm.h b/src/arch/x86/include/librm.h similarity index 52% rename from src/arch/i386/include/librm.h rename to src/arch/x86/include/librm.h index a8a578a39..5196d390f 100644 --- a/src/arch/i386/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -7,19 +7,62 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * Don't change these unless you really know what you're doing. */ - #define VIRTUAL_CS 0x08 #define VIRTUAL_DS 0x10 #define PHYSICAL_CS 0x18 #define PHYSICAL_DS 0x20 #define REAL_CS 0x28 #define REAL_DS 0x30 -#if 0 -#define LONG_CS 0x38 -#define LONG_DS 0x40 +#define P2R_DS 0x38 +#define LONG_CS 0x40 + +/* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS + * + * In a 64-bit build, we set the bases of VIRTUAL_CS and VIRTUAL_DS + * such that truncating a .textdata symbol value to 32 bits gives a + * valid 32-bit virtual address. + * + * The C code is compiled with -mcmodel=kernel and so we must place + * all .textdata symbols within the negative 2GB of the 64-bit address + * space. Consequently, all .textdata symbols will have the MSB set + * after truncation to 32 bits. This means that a straightforward + * R_X86_64_32 relocation record for the symbol will fail, since the + * truncated symbol value will not correctly zero-extend to the + * original 64-bit value. + * + * Using an R_X86_64_32S relocation record would work, but there is no + * (sensible) way to generate these relocation records within 32-bit + * or 16-bit code. + * + * The simplest solution is to generate an R_X86_64_32 relocation + * record with an addend of (-0xffffffff00000000). Since all + * .textdata symbols are within the negative 2GB of the 64-bit address + * space, this addend acts to effectively truncate the symbol to 32 + * bits, thereby matching the semantics of the R_X86_64_32 relocation + * records generated for 32-bit and 16-bit code. + * + * In a 32-bit build, this problem does not exist, and we can just use + * the .textdata symbol values directly. + */ +#ifdef __x86_64__ +#define VIRTUAL(address) ( (address) - 0xffffffff00000000 ) +#else +#define VIRTUAL(address) (address) #endif -#ifndef ASSEMBLY +#ifdef ASSEMBLY + +/** + * Call C function from real-mode code + * + * @v function C function + */ +.macro virtcall function + pushl $VIRTUAL(\function) + call virt_call +.endm + +#else /* ASSEMBLY */ #ifdef UACCESS_LIBRM #define UACCESS_PREFIX_librm @@ -27,8 +70,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_PREFIX_librm __librm_ #endif +/** + * Call C function from real-mode code + * + * @v function C function + */ +#define VIRT_CALL( function ) \ + "pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \ + "call virt_call\n\t" + /* Variables in librm.S */ -extern unsigned long virt_offset; +extern const unsigned long virt_offset; /** * Convert physical address to user pointer @@ -38,6 +90,15 @@ extern unsigned long virt_offset; */ static inline __always_inline userptr_t UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) { + + /* In a 64-bit build, any valid physical address is directly + * usable as a virtual address, since the low 4GB is + * identity-mapped. + */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) + return phys_addr; + + /* In a 32-bit build, subtract virt_offset */ return ( phys_addr - virt_offset ); } @@ -50,7 +111,20 @@ UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) { */ static inline __always_inline unsigned long UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) { - return ( userptr + offset + virt_offset ); + unsigned long addr = ( userptr + offset ); + + /* In a 64-bit build, any virtual address in the low 4GB is + * directly usable as a physical address, since the low 4GB is + * identity-mapped. + */ + if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) && + ( addr <= 0xffffffffUL ) ) + return addr; + + /* In a 32-bit build or in a 64-bit build with a virtual + * address above 4GB: add virt_offset + */ + return ( addr + virt_offset ); } static inline __always_inline userptr_t @@ -119,8 +193,8 @@ UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset, * */ -extern char *data16; -extern char *text16; +extern char * const data16; +extern char * const text16; #define __data16( variable ) \ __attribute__ (( section ( ".data16" ) )) \ @@ -165,27 +239,37 @@ extern char *text16; /* Variables in librm.S, present in the normal data segment */ extern uint16_t rm_sp; extern uint16_t rm_ss; -extern uint16_t __text16 ( rm_cs ); +extern const uint16_t __text16 ( rm_cs ); #define rm_cs __use_text16 ( rm_cs ) -extern uint16_t __text16 ( rm_ds ); +extern const uint16_t __text16 ( rm_ds ); #define rm_ds __use_text16 ( rm_ds ) extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); +/* CODE_DEFAULT: restore default .code32/.code64 directive */ +#ifdef __x86_64__ +#define CODE_DEFAULT ".code64" +#else +#define CODE_DEFAULT ".code32" +#endif + +/* LINE_SYMBOL: declare a symbol for the current source code line */ +#define LINE_SYMBOL _S2 ( OBJECT ) "__line_" _S2 ( __LINE__ ) "__%=:" + /* TEXT16_CODE: declare a fragment of code that resides in .text16 */ #define TEXT16_CODE( asm_code_str ) \ ".section \".text16\", \"ax\", @progbits\n\t" \ + "\n" LINE_SYMBOL "\n\t" \ ".code16\n\t" \ asm_code_str "\n\t" \ - ".code32\n\t" \ + CODE_DEFAULT "\n\t" \ ".previous\n\t" /* REAL_CODE: declare a fragment of code that executes in real mode */ #define REAL_CODE( asm_code_str ) \ - "pushl $1f\n\t" \ + "push $1f\n\t" \ "call real_call\n\t" \ - "addl $4, %%esp\n\t" \ TEXT16_CODE ( "\n1:\n\t" \ asm_code_str \ "\n\t" \ @@ -193,23 +277,39 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); /* PHYS_CODE: declare a fragment of code that executes in flat physical mode */ #define PHYS_CODE( asm_code_str ) \ - "call _virt_to_phys\n\t" \ + "push $1f\n\t" \ + "call phys_call\n\t" \ + ".section \".text.phys\", \"ax\", @progbits\n\t"\ + "\n" LINE_SYMBOL "\n\t" \ + ".code32\n\t" \ + "\n1:\n\t" \ asm_code_str \ - "call _phys_to_virt\n\t" + "\n\t" \ + "ret\n\t" \ + CODE_DEFAULT "\n\t" \ + ".previous\n\t" /** Number of interrupts */ #define NUM_INT 256 -/** An interrupt descriptor table register */ -struct idtr { +/** A 32-bit interrupt descriptor table register */ +struct idtr32 { /** Limit */ uint16_t limit; /** Base */ uint32_t base; } __attribute__ (( packed )); -/** An interrupt descriptor table entry */ -struct interrupt_descriptor { +/** A 64-bit interrupt descriptor table register */ +struct idtr64 { + /** Limit */ + uint16_t limit; + /** Base */ + uint64_t base; +} __attribute__ (( packed )); + +/** A 32-bit interrupt descriptor table entry */ +struct interrupt32_descriptor { /** Low 16 bits of address */ uint16_t low; /** Code segment */ @@ -222,23 +322,44 @@ struct interrupt_descriptor { uint16_t high; } __attribute__ (( packed )); +/** A 64-bit interrupt descriptor table entry */ +struct interrupt64_descriptor { + /** Low 16 bits of address */ + uint16_t low; + /** Code segment */ + uint16_t segment; + /** Unused */ + uint8_t unused; + /** Type and attributes */ + uint8_t attr; + /** Middle 16 bits of address */ + uint16_t mid; + /** High 32 bits of address */ + uint32_t high; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + /** Interrupt descriptor is present */ #define IDTE_PRESENT 0x80 /** Interrupt descriptor 32-bit interrupt gate type */ #define IDTE_TYPE_IRQ32 0x0e +/** Interrupt descriptor 64-bit interrupt gate type */ +#define IDTE_TYPE_IRQ64 0x0e + /** An interrupt vector * * Each interrupt vector comprises an eight-byte fragment of code: * - * 60 pushal + * 50 pushl %eax (or pushq %rax in long mode) * b0 xx movb $INT, %al * e9 xx xx xx xx jmp interrupt_wrapper */ struct interrupt_vector { - /** "pushal" instruction */ - uint8_t pushal; + /** "push" instruction */ + uint8_t push; /** "movb" instruction */ uint8_t movb; /** Interrupt number */ @@ -251,8 +372,8 @@ struct interrupt_vector { uint8_t next[0]; } __attribute__ (( packed )); -/** "pushal" instruction */ -#define PUSHAL_INSN 0x60 +/** "push %eax" instruction */ +#define PUSH_INSN 0x50 /** "movb" instruction */ #define MOVB_INSN 0xb0 @@ -260,8 +381,97 @@ struct interrupt_vector { /** "jmp" instruction */ #define JMP_INSN 0xe9 +/** 32-bit interrupt wrapper stack frame */ +struct interrupt_frame32 { + uint32_t esp; + uint32_t ss; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t ebp; + uint32_t edi; + uint32_t esi; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +} __attribute__ (( packed )); + +/** 64-bit interrupt wrapper stack frame */ +struct interrupt_frame64 { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rbp; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} __attribute__ (( packed )); + extern void set_interrupt_vector ( unsigned int intr, void *vector ); +/** A page table */ +struct page_table { + /** Page address and flags */ + uint64_t page[512]; +}; + +/** Page flags */ +enum page_flags { + /** Page is present */ + PAGE_P = 0x01, + /** Page is writable */ + PAGE_RW = 0x02, + /** Page is accessible by user code */ + PAGE_US = 0x04, + /** Page-level write-through */ + PAGE_PWT = 0x08, + /** Page-level cache disable */ + PAGE_PCD = 0x10, + /** Page is a large page */ + PAGE_PS = 0x80, + /** Page is the last page in an allocation + * + * This bit is ignored by the hardware. We use it to track + * the size of allocations made by ioremap(). + */ + PAGE_LAST = 0x800, +}; + +/** The I/O space page table */ +extern struct page_table io_pages; + +/** I/O page size + * + * We choose to use 2MB pages for I/O space, to minimise the number of + * page table entries required. + */ +#define IO_PAGE_SIZE 0x200000UL + +/** I/O page base address + * + * We choose to place I/O space immediately above the identity-mapped + * 32-bit address space. + */ +#define IO_BASE ( ( void * ) 0x100000000ULL ) + #endif /* ASSEMBLY */ #endif /* LIBRM_H */ diff --git a/src/arch/i386/include/memsizes.h b/src/arch/x86/include/memsizes.h similarity index 100% rename from src/arch/i386/include/memsizes.h rename to src/arch/x86/include/memsizes.h diff --git a/src/arch/i386/include/multiboot.h b/src/arch/x86/include/multiboot.h similarity index 100% rename from src/arch/i386/include/multiboot.h rename to src/arch/x86/include/multiboot.h diff --git a/src/arch/x86/include/pic8259.h b/src/arch/x86/include/pic8259.h index f02e62909..dbec5fd2c 100644 --- a/src/arch/x86/include/pic8259.h +++ b/src/arch/x86/include/pic8259.h @@ -37,8 +37,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Register command values */ #define OCW3_ID 0x08 -#define OCW3_READ_IRR 0x03 -#define OCW3_READ_ISR 0x02 +#define OCW3_READ_IRR 0x02 +#define OCW3_READ_ISR 0x03 #define ICR_EOI_NON_SPECIFIC 0x20 #define ICR_EOI_NOP 0x40 #define ICR_EOI_SPECIFIC 0x60 diff --git a/src/arch/i386/include/pnpbios.h b/src/arch/x86/include/pnpbios.h similarity index 100% rename from src/arch/i386/include/pnpbios.h rename to src/arch/x86/include/pnpbios.h diff --git a/src/arch/i386/include/pxe.h b/src/arch/x86/include/pxe.h similarity index 100% rename from src/arch/i386/include/pxe.h rename to src/arch/x86/include/pxe.h diff --git a/src/arch/i386/include/pxe_api.h b/src/arch/x86/include/pxe_api.h similarity index 100% rename from src/arch/i386/include/pxe_api.h rename to src/arch/x86/include/pxe_api.h diff --git a/src/arch/i386/include/pxe_call.h b/src/arch/x86/include/pxe_call.h similarity index 97% rename from src/arch/i386/include/pxe_call.h rename to src/arch/x86/include/pxe_call.h index cbd548318..2ad0a9505 100644 --- a/src/arch/i386/include/pxe_call.h +++ b/src/arch/x86/include/pxe_call.h @@ -10,7 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -#include +#include struct net_device; diff --git a/src/arch/i386/include/pxe_error.h b/src/arch/x86/include/pxe_error.h similarity index 100% rename from src/arch/i386/include/pxe_error.h rename to src/arch/x86/include/pxe_error.h diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/x86/include/pxe_types.h similarity index 100% rename from src/arch/i386/include/pxe_types.h rename to src/arch/x86/include/pxe_types.h diff --git a/src/arch/i386/include/realmode.h b/src/arch/x86/include/realmode.h similarity index 100% rename from src/arch/i386/include/realmode.h rename to src/arch/x86/include/realmode.h diff --git a/src/arch/i386/include/registers.h b/src/arch/x86/include/registers.h similarity index 98% rename from src/arch/i386/include/registers.h rename to src/arch/x86/include/registers.h index d9aa3c376..dd3b59fd5 100644 --- a/src/arch/i386/include/registers.h +++ b/src/arch/x86/include/registers.h @@ -167,7 +167,7 @@ struct i386_seg_regs { * * @endcode * - * prot_call() and kir_call() create this data structure on the stack + * virt_call() and kir_call() create this data structure on the stack * and pass in a pointer to this structure. * */ diff --git a/src/arch/x86/include/rmsetjmp.h b/src/arch/x86/include/rmsetjmp.h new file mode 100644 index 000000000..3470be477 --- /dev/null +++ b/src/arch/x86/include/rmsetjmp.h @@ -0,0 +1,28 @@ +#ifndef _RMSETJMP_H +#define _RMSETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** A real-mode-extended jump buffer */ +typedef struct { + /** Jump buffer */ + jmp_buf env; + /** Real-mode stack pointer */ + segoff_t rm_stack; +} rmjmp_buf[1]; + +#define rmsetjmp( _env ) ( { \ + (_env)->rm_stack.segment = rm_ss; \ + (_env)->rm_stack.offset = rm_sp; \ + setjmp ( (_env)->env ); } ) \ + +#define rmlongjmp( _env, _val ) do { \ + rm_ss = (_env)->rm_stack.segment; \ + rm_sp = (_env)->rm_stack.offset; \ + longjmp ( (_env)->env, (_val) ); \ + } while ( 0 ) + +#endif /* _RMSETJMP_H */ diff --git a/src/arch/i386/include/rtc.h b/src/arch/x86/include/rtc.h similarity index 100% rename from src/arch/i386/include/rtc.h rename to src/arch/x86/include/rtc.h diff --git a/src/arch/i386/include/sdi.h b/src/arch/x86/include/sdi.h similarity index 100% rename from src/arch/i386/include/sdi.h rename to src/arch/x86/include/sdi.h diff --git a/src/arch/i386/include/undi.h b/src/arch/x86/include/undi.h similarity index 98% rename from src/arch/i386/include/undi.h rename to src/arch/x86/include/undi.h index 7a5624f93..adf0c01e0 100644 --- a/src/arch/i386/include/undi.h +++ b/src/arch/x86/include/undi.h @@ -53,8 +53,6 @@ struct undi_device { */ UINT16_t flags; - /** Generic device */ - struct device dev; /** Driver-private data * * Use undi_set_drvdata() and undi_get_drvdata() to access this diff --git a/src/arch/i386/include/undiload.h b/src/arch/x86/include/undiload.h similarity index 100% rename from src/arch/i386/include/undiload.h rename to src/arch/x86/include/undiload.h diff --git a/src/arch/i386/include/undinet.h b/src/arch/x86/include/undinet.h similarity index 72% rename from src/arch/i386/include/undinet.h rename to src/arch/x86/include/undinet.h index 2798c4466..04fdd6000 100644 --- a/src/arch/i386/include/undinet.h +++ b/src/arch/x86/include/undinet.h @@ -10,8 +10,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct undi_device; +struct device; -extern int undinet_probe ( struct undi_device *undi ); +extern int undinet_probe ( struct undi_device *undi, struct device *dev ); extern void undinet_remove ( struct undi_device *undi ); #endif /* _UNDINET_H */ diff --git a/src/arch/i386/include/undipreload.h b/src/arch/x86/include/undipreload.h similarity index 100% rename from src/arch/i386/include/undipreload.h rename to src/arch/x86/include/undipreload.h diff --git a/src/arch/i386/include/undirom.h b/src/arch/x86/include/undirom.h similarity index 100% rename from src/arch/i386/include/undirom.h rename to src/arch/x86/include/undirom.h diff --git a/src/arch/i386/include/vga.h b/src/arch/x86/include/vga.h similarity index 100% rename from src/arch/i386/include/vga.h rename to src/arch/x86/include/vga.h diff --git a/src/arch/x86/interface/pcbios/acpi_timer.c b/src/arch/x86/interface/pcbios/acpi_timer.c new file mode 100644 index 000000000..82e85a034 --- /dev/null +++ b/src/arch/x86/interface/pcbios/acpi_timer.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * ACPI power management timer + * + */ + +/** ACPI timer frequency (fixed 3.579545MHz) */ +#define ACPI_TIMER_HZ 3579545 + +/** ACPI timer mask + * + * Timers may be implemented as either 24-bit or 32-bit counters. We + * simplify the code by pessimistically assuming that the timer has + * only 24 bits. + */ +#define ACPI_TIMER_MASK 0x00ffffffUL + +/** Power management timer register address */ +static unsigned int pm_tmr; + +struct timer acpi_timer __timer ( TIMER_PREFERRED ); + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long acpi_currticks ( void ) { + static unsigned long offset; + static uint32_t prev; + uint32_t now; + + /* Read timer and account for wraparound */ + now = ( inl ( pm_tmr ) & ACPI_TIMER_MASK ); + if ( now < prev ) { + offset += ( ( ACPI_TIMER_MASK + 1 ) / + ( ACPI_TIMER_HZ / TICKS_PER_SEC ) ); + } + prev = now; + + /* Convert to timer ticks */ + return ( offset + ( now / ( ACPI_TIMER_HZ / TICKS_PER_SEC ) ) ); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void acpi_udelay ( unsigned long usecs ) { + uint32_t start; + uint32_t elapsed; + uint32_t threshold; + + /* Delay until a suitable number of ticks have elapsed. We do + * not need to allow for multiple wraparound, since the + * wraparound period for a 24-bit timer at 3.579545MHz is + * around 4700000us. + */ + start = inl ( pm_tmr ); + threshold = ( ( usecs * ACPI_TIMER_HZ ) / 1000000 ); + do { + elapsed = ( ( inl ( pm_tmr ) - start ) & ACPI_TIMER_MASK ); + } while ( elapsed < threshold ); +} + +/** + * Probe ACPI power management timer + * + * @ret rc Return status code + */ +static int acpi_timer_probe ( void ) { + struct acpi_fadt fadtab; + userptr_t fadt; + unsigned int pm_tmr_blk; + + /* Locate FADT */ + fadt = acpi_find ( FADT_SIGNATURE, 0 ); + if ( ! fadt ) { + DBGC ( &acpi_timer, "ACPI could not find FADT\n" ); + return -ENOENT; + } + + /* Read FADT */ + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + pm_tmr_blk = le32_to_cpu ( fadtab.pm_tmr_blk ); + if ( ! pm_tmr_blk ) { + DBGC ( &acpi_timer, "ACPI has no timer\n" ); + return -ENOENT; + } + + /* Record power management timer register address */ + pm_tmr = ( pm_tmr_blk + ACPI_PM_TMR ); + + return 0; +} + +/** ACPI timer */ +struct timer acpi_timer __timer ( TIMER_PREFERRED ) = { + .name = "acpi", + .probe = acpi_timer_probe, + .currticks = acpi_currticks, + .udelay = acpi_udelay, +}; diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c new file mode 100644 index 000000000..dc164c7d5 --- /dev/null +++ b/src/arch/x86/interface/pcbios/acpipwr.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * ACPI power off + * + */ + +/** Colour for debug messages */ +#define colour FADT_SIGNATURE + +/** _S5_ signature */ +#define S5_SIGNATURE ACPI_SIGNATURE ( '_', 'S', '5', '_' ) + +/** + * Power off the computer using ACPI + * + * @ret rc Return status code + */ +int acpi_poweroff ( void ) { + struct acpi_fadt fadtab; + userptr_t fadt; + unsigned int pm1a_cnt_blk; + unsigned int pm1b_cnt_blk; + unsigned int pm1a_cnt; + unsigned int pm1b_cnt; + unsigned int slp_typa; + unsigned int slp_typb; + int s5; + int rc; + + /* Locate FADT */ + fadt = acpi_find ( FADT_SIGNATURE, 0 ); + if ( ! fadt ) { + DBGC ( colour, "ACPI could not find FADT\n" ); + return -ENOENT; + } + + /* Read FADT */ + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + pm1a_cnt_blk = le32_to_cpu ( fadtab.pm1a_cnt_blk ); + pm1b_cnt_blk = le32_to_cpu ( fadtab.pm1b_cnt_blk ); + pm1a_cnt = ( pm1a_cnt_blk + ACPI_PM1_CNT ); + pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT ); + + /* Extract \_S5 from DSDT or any SSDT */ + s5 = acpi_sx ( S5_SIGNATURE ); + if ( s5 < 0 ) { + rc = s5; + DBGC ( colour, "ACPI could not extract \\_S5: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Power off system */ + if ( pm1a_cnt_blk ) { + slp_typa = ( ( s5 >> 0 ) & 0xff ); + DBGC ( colour, "ACPI PM1a sleep type %#x => %04x\n", + slp_typa, pm1a_cnt ); + outw ( ( ACPI_PM1_CNT_SLP_TYP ( slp_typa ) | + ACPI_PM1_CNT_SLP_EN ), pm1a_cnt ); + } + if ( pm1b_cnt_blk ) { + slp_typb = ( ( s5 >> 8 ) & 0xff ); + DBGC ( colour, "ACPI PM1b sleep type %#x => %04x\n", + slp_typb, pm1b_cnt ); + outw ( ( ACPI_PM1_CNT_SLP_TYP ( slp_typb ) | + ACPI_PM1_CNT_SLP_EN ), pm1b_cnt ); + } + + /* On some systems, execution will continue briefly. Delay to + * avoid potentially confusing log messages. + */ + mdelay ( 1000 ); + + DBGC ( colour, "ACPI power off failed\n" ); + return -EPROTO; +} diff --git a/src/arch/i386/interface/pcbios/apm.c b/src/arch/x86/interface/pcbios/apm.c similarity index 96% rename from src/arch/i386/interface/pcbios/apm.c rename to src/arch/x86/interface/pcbios/apm.c index 50b19cb81..680dbb16a 100644 --- a/src/arch/i386/interface/pcbios/apm.c +++ b/src/arch/x86/interface/pcbios/apm.c @@ -32,14 +32,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -#include +#include /** * Power off the computer using APM * * @ret rc Return status code */ -static int apm_poweroff ( void ) { +int apm_poweroff ( void ) { uint16_t apm_version; uint16_t apm_signature; uint16_t apm_flags; @@ -108,5 +108,3 @@ static int apm_poweroff ( void ) { /* Should never happen */ return -ECANCELED; } - -PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff ); diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/x86/interface/pcbios/basemem.c similarity index 100% rename from src/arch/i386/firmware/pcbios/basemem.c rename to src/arch/x86/interface/pcbios/basemem.c diff --git a/src/arch/i386/firmware/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c similarity index 63% rename from src/arch/i386/firmware/pcbios/bios_console.c rename to src/arch/x86/interface/pcbios/bios_console.c index 63413cdc1..08fa6d036 100644 --- a/src/arch/i386/firmware/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -26,9 +26,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include +#include #include +#include #include #define ATTR_BOLD 0x08 @@ -66,6 +69,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Current character attribute */ static unsigned int bios_attr = ATTR_DEFAULT; +/** Keypress injection lock */ +static uint8_t __text16 ( bios_inject_lock ); +#define bios_inject_lock __use_text16 ( bios_inject_lock ) + +/** Vector for chaining to other INT 16 handlers */ +static struct segoff __text16 ( int16_vector ); +#define int16_vector __use_text16 ( int16_vector ) + +/** Assembly wrapper */ +extern void int16_wrapper ( void ); + /** * Handle ANSI CUP (cursor position) * @@ -84,9 +98,7 @@ static void bios_handle_cup ( struct ansiesc_context *ctx __unused, if ( cy < 0 ) cy = 0; - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0200 ), "b" ( 1 ), "d" ( ( cy << 8 ) | cx ) ); } @@ -104,9 +116,7 @@ static void bios_handle_ed ( struct ansiesc_context *ctx __unused, /* We assume that we always clear the whole screen */ assert ( params[0] == ANSIESC_ED_ALL ); - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ), "c" ( 0 ), "d" ( ( ( console_height - 1 ) << 8 ) | @@ -174,9 +184,7 @@ static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused, /* Get character height */ get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT ); - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0100 ), "c" ( ( ( height - 2 ) << 8 ) | ( height - 1 ) ) ); @@ -193,9 +201,7 @@ static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused, unsigned int count __unused, int params[] __unused ) { - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "int $0x10\n\t" - "cli\n\t" ) + __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" ) : : "a" ( 0x0100 ), "c" ( 0x2000 ) ); } @@ -229,7 +235,6 @@ static void bios_putchar ( int character ) { /* Print character with attribute */ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ - "sti\n\t" /* Skip non-printable characters */ "cmpb $0x20, %%al\n\t" "jb 1f\n\t" @@ -250,7 +255,6 @@ static void bios_putchar ( int character ) { "xorw %%bx, %%bx\n\t" "movb $0x0e, %%ah\n\t" "int $0x10\n\t" - "cli\n\t" "popl %%ebp\n\t" /* gcc bug */ ) : "=a" ( discard_a ), "=b" ( discard_b ), "=c" ( discard_c ) @@ -265,30 +269,35 @@ static void bios_putchar ( int character ) { * not in the middle of such a sequence, this will point to a NUL * (note: not "will be NULL"). */ -static const char *ansi_input = ""; +static const char *bios_ansi_input = ""; -/** A mapping from a BIOS scan code to an ANSI escape sequence */ -#define BIOS_KEY( key, ansi ) key ansi "\0" +/** A BIOS key */ +struct bios_key { + /** Scancode */ + uint8_t scancode; + /** Key code */ + uint16_t key; +} __attribute__ (( packed )); -/** Mapping from BIOS scan codes to ANSI escape sequences */ -static const char ansi_sequences[] = { - BIOS_KEY ( "\x53", "[3~" ) /* Delete */ - BIOS_KEY ( "\x48", "[A" ) /* Up arrow */ - BIOS_KEY ( "\x50", "[B" ) /* Down arrow */ - BIOS_KEY ( "\x4b", "[D" ) /* Left arrow */ - BIOS_KEY ( "\x4d", "[C" ) /* Right arrow */ - BIOS_KEY ( "\x47", "[H" ) /* Home */ - BIOS_KEY ( "\x4f", "[F" ) /* End */ - BIOS_KEY ( "\x49", "[5~" ) /* Page up */ - BIOS_KEY ( "\x51", "[6~" ) /* Page down */ - BIOS_KEY ( "\x3f", "[15~" ) /* F5 */ - BIOS_KEY ( "\x40", "[17~" ) /* F6 */ - BIOS_KEY ( "\x41", "[18~" ) /* F7 */ - BIOS_KEY ( "\x42", "[19~" ) /* F8 (required for PXE) */ - BIOS_KEY ( "\x43", "[20~" ) /* F9 */ - BIOS_KEY ( "\x44", "[21~" ) /* F10 */ - BIOS_KEY ( "\x85", "[23~" ) /* F11 */ - BIOS_KEY ( "\x86", "[24~" ) /* F12 */ +/** Mapping from BIOS scan codes to iPXE key codes */ +static const struct bios_key bios_keys[] = { + { 0x53, KEY_DC }, + { 0x48, KEY_UP }, + { 0x50, KEY_DOWN }, + { 0x4b, KEY_LEFT }, + { 0x4d, KEY_RIGHT }, + { 0x47, KEY_HOME }, + { 0x4f, KEY_END }, + { 0x49, KEY_PPAGE }, + { 0x51, KEY_NPAGE }, + { 0x3f, KEY_F5 }, + { 0x40, KEY_F6 }, + { 0x41, KEY_F7 }, + { 0x42, KEY_F8 }, + { 0x43, KEY_F9 }, + { 0x44, KEY_F10 }, + { 0x85, KEY_F11 }, + { 0x86, KEY_F12 }, }; /** @@ -297,14 +306,35 @@ static const char ansi_sequences[] = { * @v scancode BIOS scancode * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL */ -static const char * scancode_to_ansi_seq ( unsigned int scancode ) { - const char *seq = ansi_sequences; +static const char * bios_ansi_seq ( unsigned int scancode ) { + static char buf[ 5 /* "[" + two digits + terminator + NUL */ ]; + unsigned int key; + unsigned int terminator; + unsigned int n; + unsigned int i; + char *tmp = buf; - while ( *seq ) { - if ( *(seq++) == ( ( char ) scancode ) ) - return seq; - seq += ( strlen ( seq ) + 1 ); + /* Construct ANSI escape sequence for scancode, if known */ + for ( i = 0 ; i < ( sizeof ( bios_keys ) / + sizeof ( bios_keys[0] ) ) ; i++ ) { + + /* Look for matching scancode */ + if ( bios_keys[i].scancode != scancode ) + continue; + + /* Construct escape sequence */ + key = bios_keys[i].key; + n = KEY_ANSI_N ( key ); + terminator = KEY_ANSI_TERMINATOR ( key ); + *(tmp++) = '['; + if ( n ) + tmp += sprintf ( tmp, "%d", n ); + *(tmp++) = terminator; + *(tmp++) = '\0'; + assert ( tmp <= &buf[ sizeof ( buf ) ] ); + return buf; } + DBG ( "Unrecognised BIOS scancode %02x\n", scancode ); return NULL; } @@ -336,16 +366,23 @@ static int bios_getchar ( void ) { const char *ansi_seq; /* If we are mid-sequence, pass out the next byte */ - if ( ( character = *ansi_input ) ) { - ansi_input++; + if ( ( character = *bios_ansi_input ) ) { + bios_ansi_input++; return character; } + /* Do nothing if injection is in progress */ + if ( bios_inject_lock ) + return 0; + /* Read character from real BIOS console */ + bios_inject_lock++; __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" "int $0x16\n\t" "cli\n\t" ) - : "=a" ( keypress ) : "a" ( 0x1000 ) ); + : "=a" ( keypress ) + : "a" ( 0x1000 ), "m" ( bios_inject_lock ) ); + bios_inject_lock--; character = ( keypress & 0xff ); /* If it's a normal character, just map and return it */ @@ -353,9 +390,9 @@ static int bios_getchar ( void ) { return bios_keymap ( character ); /* Otherwise, check for a special key that we know about */ - if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) { + if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) { /* Start of escape sequence: return ESC (0x1b) */ - ansi_input = ansi_seq; + bios_ansi_input = ansi_seq; return 0x1b; } @@ -373,23 +410,143 @@ static int bios_iskey ( void ) { unsigned int flags; /* If we are mid-sequence, we are always ready */ - if ( *ansi_input ) + if ( *bios_ansi_input ) return 1; + /* Do nothing if injection is in progress */ + if ( bios_inject_lock ) + return 0; + /* Otherwise check the real BIOS console */ + bios_inject_lock++; __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" "int $0x16\n\t" "pushfw\n\t" "popw %w0\n\t" "cli\n\t" ) - : "=r" ( flags ), "=a" ( discard_a ) - : "a" ( 0x1100 ) ); + : "=R" ( flags ), "=a" ( discard_a ) + : "a" ( 0x1100 ), "m" ( bios_inject_lock ) ); + bios_inject_lock--; return ( ! ( flags & ZF ) ); } +/** BIOS console */ struct console_driver bios_console __console_driver = { .putchar = bios_putchar, .getchar = bios_getchar, .iskey = bios_iskey, .usage = CONSOLE_PCBIOS, }; + +/** + * Inject keypresses + * + * @v ix86 Registers as passed to INT 16 + */ +static __asmcall void bios_inject ( struct i386_all_regs *ix86 ) { + unsigned int discard_a; + unsigned int scancode; + unsigned int i; + uint16_t keypress; + int key; + + /* If this is a blocking call, then loop until the + * non-blocking variant of the call indicates that a keypress + * is available. Do this without acquiring the injection + * lock, so that injection may take place. + */ + if ( ( ix86->regs.ah & ~0x10 ) == 0x00 ) { + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "\n1:\n\t" + "pushw %%ax\n\t" + "int $0x16\n\t" + "popw %%ax\n\t" + "jc 2f\n\t" + "jz 1b\n\t" + "\n2:\n\t" + "cli\n\t" ) + : "=a" ( discard_a ) + : "a" ( ix86->regs.eax | 0x0100 ), + "m" ( bios_inject_lock ) ); + } + + /* Acquire injection lock */ + bios_inject_lock++; + + /* Check for keypresses */ + if ( iskey() ) { + + /* Get key */ + key = getkey ( 0 ); + + /* Reverse internal CR->LF mapping */ + if ( key == '\n' ) + key = '\r'; + + /* Convert to keypress */ + keypress = ( ( key << 8 ) | key ); + + /* Handle special keys */ + if ( key >= KEY_MIN ) { + for ( i = 0 ; i < ( sizeof ( bios_keys ) / + sizeof ( bios_keys[0] ) ) ; i++ ) { + if ( bios_keys[i].key == key ) { + scancode = bios_keys[i].scancode; + keypress = ( scancode << 8 ); + break; + } + } + } + + /* Inject keypress */ + DBGC ( &bios_console, "BIOS injecting keypress %04x\n", + keypress ); + __asm__ __volatile__ ( REAL_CODE ( "int $0x16\n\t" ) + : "=a" ( discard_a ) + : "a" ( 0x0500 ), "c" ( keypress ), + "m" ( bios_inject_lock ) ); + } + + /* Release injection lock */ + bios_inject_lock--; +} + +/** + * Start up keypress injection + * + */ +static void bios_inject_startup ( void ) { + + /* Assembly wrapper to call bios_inject() */ + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint16_wrapper:\n\t" + "pushfw\n\t" + "cmpb $0, %%cs:bios_inject_lock\n\t" + "jnz 1f\n\t" + VIRT_CALL ( bios_inject ) + "\n1:\n\t" + "popfw\n\t" + "ljmp *%%cs:int16_vector\n\t" ) : ); + + /* Hook INT 16 */ + hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ), + &int16_vector ); +} + +/** + * Shut down keypress injection + * + * @v booting System is shutting down for OS boot + */ +static void bios_inject_shutdown ( int booting __unused ) { + + /* Unhook INT 16 */ + unhook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ), + &int16_vector ); +} + +/** Keypress injection startup function */ +struct startup_fn bios_inject_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .startup = bios_inject_startup, + .shutdown = bios_inject_shutdown, +}; diff --git a/src/arch/i386/interface/pcbios/bios_nap.c b/src/arch/x86/interface/pcbios/bios_nap.c similarity index 100% rename from src/arch/i386/interface/pcbios/bios_nap.c rename to src/arch/x86/interface/pcbios/bios_nap.c diff --git a/src/arch/i386/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c similarity index 77% rename from src/arch/i386/interface/pcbios/bios_reboot.c rename to src/arch/x86/interface/pcbios/bios_reboot.c index 10a1ecb89..071173f19 100644 --- a/src/arch/i386/interface/pcbios/bios_reboot.c +++ b/src/arch/x86/interface/pcbios/bios_reboot.c @@ -32,6 +32,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** * Reboot system @@ -46,7 +48,27 @@ static void bios_reboot ( int warm ) { put_real ( flag, BDA_SEG, BDA_REBOOT ); /* Jump to system reset vector */ - __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : : ); + __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : ); +} + +/** + * Power off system + * + * @ret rc Return status code + */ +static int bios_poweroff ( void ) { + int rc; + + /* Try APM */ + if ( ( rc = apm_poweroff() ) != 0 ) + DBG ( "APM power off failed: %s\n", strerror ( rc ) ); + + /* Try ACPI */ + if ( ( rc = acpi_poweroff() ) != 0 ) + DBG ( "ACPI power off failed: %s\n", strerror ( rc ) ); + + return rc; } PROVIDE_REBOOT ( pcbios, reboot, bios_reboot ); +PROVIDE_REBOOT ( pcbios, poweroff, bios_poweroff ); diff --git a/src/arch/i386/interface/pcbios/bios_smbios.c b/src/arch/x86/interface/pcbios/bios_smbios.c similarity index 100% rename from src/arch/i386/interface/pcbios/bios_smbios.c rename to src/arch/x86/interface/pcbios/bios_smbios.c diff --git a/src/arch/i386/interface/pcbios/bios_timer.c b/src/arch/x86/interface/pcbios/bios_timer.c similarity index 66% rename from src/arch/i386/interface/pcbios/bios_timer.c rename to src/arch/x86/interface/pcbios/bios_timer.c index 3299c9aae..49e1d2265 100644 --- a/src/arch/i386/interface/pcbios/bios_timer.c +++ b/src/arch/x86/interface/pcbios/bios_timer.c @@ -32,6 +32,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include + +/** Number of ticks per day + * + * This seems to be the normative value, as used by e.g. SeaBIOS to + * decide when to set the midnight rollover flag. + */ +#define BIOS_TICKS_PER_DAY 0x1800b0 + +/** Number of ticks per BIOS tick */ +#define TICKS_PER_BIOS_TICK \ + ( ( TICKS_PER_SEC * 60 * 60 * 24 ) / BIOS_TICKS_PER_DAY ) /** * Get current system time in ticks @@ -43,7 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * of calling timeofday BIOS interrupt. */ static unsigned long bios_currticks ( void ) { - static int days = 0; + static uint32_t offset; uint32_t ticks; uint8_t midnight; @@ -53,18 +65,25 @@ static unsigned long bios_currticks ( void ) { "nop\n\t" "cli\n\t" ); - get_real ( ticks, BDA_SEG, 0x006c ); - get_real ( midnight, BDA_SEG, 0x0070 ); + /* Read current BIOS time of day */ + get_real ( ticks, BDA_SEG, BDA_TICKS ); + get_real ( midnight, BDA_SEG, BDA_MIDNIGHT ); + /* Handle midnight rollover */ if ( midnight ) { midnight = 0; - put_real ( midnight, BDA_SEG, 0x0070 ); - days += 0x1800b0; + put_real ( midnight, BDA_SEG, BDA_MIDNIGHT ); + offset += BIOS_TICKS_PER_DAY; } + ticks += offset; - return ( days + ticks ); + /* Convert to timer ticks */ + return ( ticks * TICKS_PER_BIOS_TICK ); } -PROVIDE_TIMER_INLINE ( pcbios, udelay ); -PROVIDE_TIMER ( pcbios, currticks, bios_currticks ); -PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec ); +/** BIOS timer */ +struct timer bios_timer __timer ( TIMER_NORMAL ) = { + .name = "bios", + .currticks = bios_currticks, + .udelay = pit8254_udelay, +}; diff --git a/src/arch/i386/interface/pcbios/biosint.c b/src/arch/x86/interface/pcbios/biosint.c similarity index 78% rename from src/arch/i386/interface/pcbios/biosint.c rename to src/arch/x86/interface/pcbios/biosint.c index 3b8e80438..667e9ed81 100644 --- a/src/arch/i386/interface/pcbios/biosint.c +++ b/src/arch/x86/interface/pcbios/biosint.c @@ -90,3 +90,30 @@ int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler, hooked_bios_interrupts--; return 0; } + +/** + * Dump changes to interrupt vector table (for debugging) + * + */ +void check_bios_interrupts ( void ) { + static struct segoff vectors[256]; + static uint8_t initialised; + struct segoff vector; + unsigned int i; + + /* Print any changed interrupt vectors */ + for ( i = 0; i < ( sizeof ( vectors ) / sizeof ( vectors[0] ) ); i++ ) { + copy_from_real ( &vector, 0, ( i * sizeof ( vector ) ), + sizeof ( vector ) ); + if ( memcmp ( &vector, &vectors[i], sizeof ( vector ) ) == 0 ) + continue; + if ( initialised ) { + dbg_printf ( "INT %02x changed %04x:%04x => " + "%04x:%04x\n", i, vectors[i].segment, + vectors[i].offset, vector.segment, + vector.offset ); + } + memcpy ( &vectors[i], &vector, sizeof ( vectors[i] ) ); + } + initialised = 1; +} diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/x86/interface/pcbios/e820mangler.S similarity index 100% rename from src/arch/i386/firmware/pcbios/e820mangler.S rename to src/arch/x86/interface/pcbios/e820mangler.S diff --git a/src/arch/i386/firmware/pcbios/fakee820.c b/src/arch/x86/interface/pcbios/fakee820.c similarity index 96% rename from src/arch/i386/firmware/pcbios/fakee820.c rename to src/arch/x86/interface/pcbios/fakee820.c index 15f4d772f..8b083c4f0 100644 --- a/src/arch/i386/firmware/pcbios/fakee820.c +++ b/src/arch/x86/interface/pcbios/fakee820.c @@ -88,11 +88,11 @@ void fake_e820 ( void ) { "ljmp *%%cs:real_int15_vector\n\t" ) : : "i" ( sizeof ( e820map ) ) ); - hook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + hook_bios_interrupt ( 0x15, ( intptr_t ) int15_fakee820, &real_int15_vector ); } void unfake_e820 ( void ) { - unhook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + unhook_bios_interrupt ( 0x15, ( intptr_t ) int15_fakee820, &real_int15_vector ); } diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/x86/interface/pcbios/hidemem.c similarity index 96% rename from src/arch/i386/firmware/pcbios/hidemem.c rename to src/arch/x86/interface/pcbios/hidemem.c index 253c601ff..a3728123c 100644 --- a/src/arch/i386/firmware/pcbios/hidemem.c +++ b/src/arch/x86/interface/pcbios/hidemem.c @@ -76,9 +76,9 @@ extern struct segoff __text16 ( int15_vector ); extern char _textdata[]; extern char _etextdata[]; extern char _text16_memsz[]; -#define _text16_memsz ( ( unsigned int ) _text16_memsz ) +#define _text16_memsz ( ( size_t ) _text16_memsz ) extern char _data16_memsz[]; -#define _data16_memsz ( ( unsigned int ) _data16_memsz ) +#define _data16_memsz ( ( size_t ) _data16_memsz ) /** * Hide region of memory from system memory map @@ -179,8 +179,7 @@ static void hide_etherboot ( void ) { } /* Hook INT 15 */ - hook_bios_interrupt ( 0x15, ( unsigned int ) int15, - &int15_vector ); + hook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector ); /* Dump memory map after mangling */ DBG ( "Hidden iPXE from system memory map\n" ); @@ -210,7 +209,7 @@ static void unhide_etherboot ( int flags __unused ) { } /* Try to unhook INT 15 */ - if ( ( rc = unhook_bios_interrupt ( 0x15, ( unsigned int ) int15, + if ( ( rc = unhook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector ) ) != 0 ) { DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) ); /* Leave it hooked; there's nothing else we can do, diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c similarity index 55% rename from src/arch/i386/interface/pcbios/int13.c rename to src/arch/x86/interface/pcbios/int13.c index f0450da90..ca789a0d1 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -29,20 +29,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include -#include -#include -#include -#include -#include -#include #include #include #include #include -#include #include #include #include @@ -59,31 +51,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** - * Overall timeout for INT 13 commands (independent of underlying device - * - * Underlying devices should ideally never become totally stuck. - * However, if they do, then the INT 13 mechanism provides 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 INT13_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) - -/** An INT 13 emulated drive */ -struct int13_drive { - /** Reference count */ - struct refcnt refcnt; - /** List of all registered drives */ - struct list_head list; - - /** Block device URI */ - struct uri *uri; - /** Underlying block device interface */ - struct interface block; - - /** BIOS in-use drive number (0x00-0xff) */ - unsigned int drive; +/** INT 13 SAN device private data */ +struct int13_data { /** BIOS natural drive number (0x00-0xff) * * This is the drive number that would have been assigned by @@ -96,16 +65,6 @@ struct int13_drive { */ unsigned int natural_drive; - /** Block device capacity */ - struct block_device_capacity capacity; - /** INT 13 emulated blocksize shift - * - * To allow for emulation of CD-ROM access, this represents - * the left-shift required to translate from INT 13 blocks to - * underlying blocks. - */ - unsigned int blksize_shift; - /** Number of cylinders * * The cylinder number field in an INT 13 call is ten bits @@ -131,13 +90,8 @@ struct int13_drive { */ unsigned int sectors_per_track; - /** Drive is a CD-ROM */ - int is_cdrom; /** Address of El Torito boot catalog (if any) */ unsigned int boot_catalog; - - /** Underlying device status, if in error */ - int block_rc; /** Status of last operation */ int last_status; }; @@ -158,9 +112,6 @@ static struct int13_fdd_parameters __data16 ( int13_fdd_params ) = { }; #define int13_fdd_params __use_data16 ( int13_fdd_params ) -/** List of registered emulated drives */ -static LIST_HEAD ( int13s ); - /** * Equipment word * @@ -189,293 +140,37 @@ static uint8_t __text16 ( num_drives ); #define num_drives __use_text16 ( num_drives ) /** - * Calculate INT 13 drive sector size + * Calculate SAN device capacity (limited to 32 bits) * - * @v int13 Emulated drive - * @ret blksize Sector size - */ -static inline size_t int13_blksize ( struct int13_drive *int13 ) { - return ( int13->capacity.blksize << int13->blksize_shift ); -} - -/** - * Calculate INT 13 drive capacity - * - * @v int13 Emulated drive + * @v sandev SAN device * @ret blocks Number of blocks */ -static inline uint64_t int13_capacity ( struct int13_drive *int13 ) { - return ( int13->capacity.blocks >> int13->blksize_shift ); -} - -/** - * Calculate INT 13 drive capacity (limited to 32 bits) - * - * @v int13 Emulated drive - * @ret blocks Number of blocks - */ -static inline uint32_t int13_capacity32 ( struct int13_drive *int13 ) { - uint64_t capacity = int13_capacity ( int13 ); +static inline uint32_t int13_capacity32 ( struct san_device *sandev ) { + uint64_t capacity = sandev_capacity ( sandev ); return ( ( capacity <= 0xffffffffUL ) ? capacity : 0xffffffff ); } /** - * Test if INT 13 drive is a floppy disk drive + * Test if SAN device is a floppy disk drive * - * @v int13 Emulated drive - * @ret is_fdd Emulated drive is a floppy disk + * @v sandev SAN device + * @ret is_fdd SAN device is a floppy disk drive */ -static inline int int13_is_fdd ( struct int13_drive *int13 ) { - return ( ! ( int13->drive & 0x80 ) ); -} - -/** An INT 13 command */ -struct int13_command { - /** Status */ - int rc; - /** INT 13 drive */ - struct int13_drive *int13; - /** Underlying block device interface */ - struct interface block; - /** Command timeout timer */ - struct retry_timer timer; -}; - -/** - * Record INT 13 drive capacity - * - * @v command INT 13 command - * @v capacity Block device capacity - */ -static void int13_command_capacity ( struct int13_command *command, - struct block_device_capacity *capacity ) { - memcpy ( &command->int13->capacity, capacity, - sizeof ( command->int13->capacity ) ); +static inline int int13_is_fdd ( struct san_device *sandev ) { + return ( ! ( sandev->drive & 0x80 ) ); } /** - * Close INT 13 command + * Parse El Torito parameters * - * @v command INT 13 command - * @v rc Reason for close - */ -static void int13_command_close ( struct int13_command *command, int rc ) { - intf_restart ( &command->block, rc ); - stop_timer ( &command->timer ); - command->rc = rc; -} - -/** - * Handle INT 13 command timer expiry - * - * @v timer Timer - */ -static void int13_command_expired ( struct retry_timer *timer, - int over __unused ) { - struct int13_command *command = - container_of ( timer, struct int13_command, timer ); - - int13_command_close ( command, -ETIMEDOUT ); -} - -/** INT 13 command interface operations */ -static struct interface_operation int13_command_op[] = { - INTF_OP ( intf_close, struct int13_command *, int13_command_close ), - INTF_OP ( block_capacity, struct int13_command *, - int13_command_capacity ), -}; - -/** INT 13 command interface descriptor */ -static struct interface_descriptor int13_command_desc = - INTF_DESC ( struct int13_command, block, int13_command_op ); - -/** - * Open (or reopen) INT 13 emulated drive underlying block device - * - * @v int13 Emulated drive - * @ret rc Return status code - */ -static int int13_reopen_block ( struct int13_drive *int13 ) { - int rc; - - /* Close any existing block device */ - intf_restart ( &int13->block, -ECONNRESET ); - - /* Open block device */ - if ( ( rc = xfer_open_uri ( &int13->block, int13->uri ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not reopen block " - "device: %s\n", int13->drive, strerror ( rc ) ); - int13->block_rc = rc; - return rc; - } - - /* Clear block device error status */ - int13->block_rc = 0; - - return 0; -} - -/** - * Prepare to issue INT 13 command - * - * @v command INT 13 command - * @v int13 Emulated drive - * @ret rc Return status code - */ -static int int13_command_start ( struct int13_command *command, - struct int13_drive *int13 ) { - int rc; - - /* Sanity check */ - assert ( command->int13 == NULL ); - assert ( ! timer_running ( &command->timer ) ); - - /* Reopen block device if necessary */ - if ( ( int13->block_rc != 0 ) && - ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) ) - return rc; - - /* Initialise command */ - command->rc = -EINPROGRESS; - command->int13 = int13; - start_timer_fixed ( &command->timer, INT13_COMMAND_TIMEOUT ); - - /* Wait for block control interface to become ready */ - while ( ( command->rc == -EINPROGRESS ) && - ( xfer_window ( &int13->block ) == 0 ) ) { - step(); - } - - return ( ( command->rc == -EINPROGRESS ) ? - int13->block_rc : command->rc ); -} - -/** - * Wait for INT 13 command to complete - * - * @v command INT 13 command - * @ret rc Return status code - */ -static int int13_command_wait ( struct int13_command *command ) { - - /* Sanity check */ - assert ( timer_running ( &command->timer ) ); - - /* Wait for command to complete */ - while ( command->rc == -EINPROGRESS ) - step(); - - assert ( ! timer_running ( &command->timer ) ); - return command->rc; -} - -/** - * Terminate INT 13 command - * - * @v command INT 13 command - */ -static void int13_command_stop ( struct int13_command *command ) { - stop_timer ( &command->timer ); - command->int13 = NULL; -} - -/** The single active INT 13 command */ -static struct int13_command int13_command = { - .block = INTF_INIT ( int13_command_desc ), - .timer = TIMER_INIT ( int13_command_expired ), -}; - -/** - * Read from or write to INT 13 drive - * - * @v int13 Emulated drive - * @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 int13_rw ( struct int13_drive *int13, 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 ) ) { - struct int13_command *command = &int13_command; - unsigned int frag_count; - size_t frag_len; - int rc; - - /* Translate to underlying blocksize */ - lba <<= int13->blksize_shift; - count <<= int13->blksize_shift; - - while ( count ) { - - /* Determine fragment length */ - frag_count = count; - if ( frag_count > int13->capacity.max_count ) - frag_count = int13->capacity.max_count; - frag_len = ( int13->capacity.blksize * frag_count ); - - /* Issue command */ - if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) || - ( ( rc = block_rw ( &int13->block, &command->block, lba, - frag_count, buffer, - frag_len ) ) != 0 ) || - ( ( rc = int13_command_wait ( command ) ) != 0 ) ) { - int13_command_stop ( command ); - return rc; - } - int13_command_stop ( command ); - - /* Move to next fragment */ - lba += frag_count; - count -= frag_count; - buffer = userptr_add ( buffer, frag_len ); - } - - return 0; -} - -/** - * Read INT 13 drive capacity - * - * @v int13 Emulated drive - * @ret rc Return status code - */ -static int int13_read_capacity ( struct int13_drive *int13 ) { - struct int13_command *command = &int13_command; - int rc; - - /* Issue command */ - if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) || - ( ( rc = block_read_capacity ( &int13->block, - &command->block ) ) != 0 ) || - ( ( rc = int13_command_wait ( command ) ) != 0 ) ) { - int13_command_stop ( command ); - return rc; - } - - int13_command_stop ( command ); - return 0; -} - -/** - * Parse ISO9660 parameters - * - * @v int13 Emulated drive + * @v sandev SAN device * @v scratch Scratch area for single-sector reads * @ret rc Return status code * - * Reads and parses ISO9660 parameters, if present. + * Reads and parses El Torito parameters, if present. */ -static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) { - static const struct iso9660_primary_descriptor_fixed primary_check = { - .type = ISO9660_TYPE_PRIMARY, - .id = ISO9660_ID, - }; - struct iso9660_primary_descriptor *primary = scratch; +static int int13_parse_eltorito ( struct san_device *sandev, void *scratch ) { + struct int13_data *int13 = sandev->priv; static const struct eltorito_descriptor_fixed boot_check = { .type = ISO9660_TYPE_BOOT, .id = ISO9660_ID, @@ -483,69 +178,34 @@ static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) { .system_id = "EL TORITO SPECIFICATION", }; struct eltorito_descriptor *boot = scratch; - unsigned int blksize; - unsigned int blksize_shift; int rc; - /* Calculate required blocksize shift */ - blksize = int13_blksize ( int13 ); - blksize_shift = 0; - while ( blksize < ISO9660_BLKSIZE ) { - blksize <<= 1; - blksize_shift++; - } - if ( blksize > ISO9660_BLKSIZE ) { - /* Do nothing if the blksize is invalid for CD-ROM access */ - return 0; - } - - /* Read primary volume descriptor */ - if ( ( rc = int13_rw ( int13, - ( ISO9660_PRIMARY_LBA << blksize_shift ), 1, - virt_to_user ( primary ), block_read ) ) != 0 ){ - DBGC ( int13, "INT13 drive %02x could not read ISO9660 " - "primary volume descriptor: %s\n", - int13->drive, strerror ( rc ) ); - return rc; - } - - /* Do nothing unless this is an ISO image */ - if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) != 0 ) - return 0; - DBGC ( int13, "INT13 drive %02x contains an ISO9660 filesystem; " - "treating as CD-ROM\n", int13->drive ); - int13->is_cdrom = 1; - /* Read boot record volume descriptor */ - if ( ( rc = int13_rw ( int13, - ( ELTORITO_LBA << blksize_shift ), 1, - virt_to_user ( boot ), block_read ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not read El Torito boot " + if ( ( rc = sandev_read ( sandev, ELTORITO_LBA, 1, + virt_to_user ( boot ) ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not read El Torito boot " "record volume descriptor: %s\n", - int13->drive, strerror ( rc ) ); + sandev->drive, strerror ( rc ) ); return rc; } /* Check for an El Torito boot catalog */ if ( memcmp ( boot, &boot_check, sizeof ( boot_check ) ) == 0 ) { int13->boot_catalog = boot->sector; - DBGC ( int13, "INT13 drive %02x has an El Torito boot catalog " - "at LBA %08x\n", int13->drive, int13->boot_catalog ); + DBGC ( sandev, "INT13 drive %02x has an El Torito boot catalog " + "at LBA %08x\n", sandev->drive, int13->boot_catalog ); } else { - DBGC ( int13, "INT13 drive %02x has no El Torito boot " - "catalog\n", int13->drive ); + DBGC ( sandev, "INT13 drive %02x has no El Torito boot " + "catalog\n", sandev->drive ); } - /* Configure drive for no-emulation CD-ROM access */ - int13->blksize_shift += blksize_shift; - return 0; } /** * Guess INT 13 hard disk drive geometry * - * @v int13 Emulated drive + * @v sandev SAN device * @v scratch Scratch area for single-sector reads * @ret heads Guessed number of heads * @ret sectors Guessed number of sectors per track @@ -553,46 +213,83 @@ static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) { * * Guesses the drive geometry by inspecting the partition table. */ -static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch, +static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch, unsigned int *heads, unsigned int *sectors ) { struct master_boot_record *mbr = scratch; struct partition_table_entry *partition; unsigned int i; + unsigned int start_cylinder; + unsigned int start_head; + unsigned int start_sector; + unsigned int end_head; + unsigned int end_sector; int rc; - /* Default guess is xx/255/63 */ - *heads = 255; - *sectors = 63; - /* Read partition table */ - if ( ( rc = int13_rw ( int13, 0, 1, virt_to_user ( mbr ), - block_read ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not read " + if ( ( rc = sandev_read ( sandev, 0, 1, virt_to_user ( mbr ) ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not read " "partition table to guess geometry: %s\n", - int13->drive, strerror ( rc ) ); + sandev->drive, strerror ( rc ) ); return rc; } - DBGC2 ( int13, "INT13 drive %02x has MBR:\n", int13->drive ); - DBGC2_HDA ( int13, 0, mbr, sizeof ( *mbr ) ); - DBGC ( int13, "INT13 drive %02x has signature %08x\n", - int13->drive, mbr->signature ); + DBGC2 ( sandev, "INT13 drive %02x has MBR:\n", sandev->drive ); + DBGC2_HDA ( sandev, 0, mbr, sizeof ( *mbr ) ); + DBGC ( sandev, "INT13 drive %02x has signature %08x\n", + sandev->drive, mbr->signature ); /* Scan through partition table and modify guesses for * heads and sectors_per_track if we find any used * partitions. */ + *heads = 0; + *sectors = 0; for ( i = 0 ; i < 4 ; i++ ) { + + /* Skip empty partitions */ partition = &mbr->partitions[i]; if ( ! partition->type ) continue; - *heads = ( PART_HEAD ( partition->chs_end ) + 1 ); - *sectors = PART_SECTOR ( partition->chs_end ); - DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based " - "on partition %d\n", - int13->drive, *heads, *sectors, ( i + 1 ) ); + + /* If partition starts on cylinder 0 then we can + * unambiguously determine the number of sectors. + */ + start_cylinder = PART_CYLINDER ( partition->chs_start ); + start_head = PART_HEAD ( partition->chs_start ); + start_sector = PART_SECTOR ( partition->chs_start ); + if ( ( start_cylinder == 0 ) && ( start_head != 0 ) ) { + *sectors = ( ( partition->start + 1 - start_sector ) / + start_head ); + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "xx/xx/%d based on partition %d\n", + sandev->drive, *sectors, ( i + 1 ) ); + } + + /* If partition ends on a higher head or sector number + * than our current guess, then increase the guess. + */ + end_head = PART_HEAD ( partition->chs_end ); + end_sector = PART_SECTOR ( partition->chs_end ); + if ( ( end_head + 1 ) > *heads ) { + *heads = ( end_head + 1 ); + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "xx/%d/xx based on partition %d\n", + sandev->drive, *heads, ( i + 1 ) ); + } + if ( end_sector > *sectors ) { + *sectors = end_sector; + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "xx/xx/%d based on partition %d\n", + sandev->drive, *sectors, ( i + 1 ) ); + } } + /* Default guess is xx/255/63 */ + if ( ! *heads ) + *heads = 255; + if ( ! *sectors ) + *sectors = 63; + return 0; } @@ -623,17 +320,17 @@ static const struct int13_fdd_geometry int13_fdd_geometries[] = { /** * Guess INT 13 floppy disk drive geometry * - * @v int13 Emulated drive + * @v sandev SAN device * @ret heads Guessed number of heads * @ret sectors Guessed number of sectors per track * @ret rc Return status code * * Guesses the drive geometry by inspecting the disk size. */ -static int int13_guess_geometry_fdd ( struct int13_drive *int13, +static int int13_guess_geometry_fdd ( struct san_device *sandev, unsigned int *heads, unsigned int *sectors ) { - unsigned int blocks = int13_capacity ( int13 ); + unsigned int blocks = sandev_capacity ( sandev ); const struct int13_fdd_geometry *geometry; unsigned int cylinders; unsigned int i; @@ -646,8 +343,8 @@ static int int13_guess_geometry_fdd ( struct int13_drive *int13, *heads = INT13_FDD_HEADS ( geometry ); *sectors = INT13_FDD_SECTORS ( geometry ); if ( ( cylinders * (*heads) * (*sectors) ) == blocks ) { - DBGC ( int13, "INT13 drive %02x guessing C/H/S " - "%d/%d/%d based on size %dK\n", int13->drive, + DBGC ( sandev, "INT13 drive %02x guessing C/H/S " + "%d/%d/%d based on size %dK\n", sandev->drive, cylinders, *heads, *sectors, ( blocks / 2 ) ); return 0; } @@ -658,36 +355,33 @@ static int int13_guess_geometry_fdd ( struct int13_drive *int13, */ *heads = 2; *sectors = 18; - DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size " - "%dK\n", int13->drive, *heads, *sectors, ( blocks / 2 ) ); + DBGC ( sandev, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size " + "%dK\n", sandev->drive, *heads, *sectors, ( blocks / 2 ) ); return 0; } /** * Guess INT 13 drive geometry * - * @v int13 Emulated drive + * @v sandev SAN device * @v scratch Scratch area for single-sector reads * @ret rc Return status code */ -static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) { +static int int13_guess_geometry ( struct san_device *sandev, void *scratch ) { + struct int13_data *int13 = sandev->priv; unsigned int guessed_heads; unsigned int guessed_sectors; unsigned int blocks; unsigned int blocks_per_cyl; int rc; - /* Don't even try when the blksize is invalid for C/H/S access */ - if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) - return 0; - /* Guess geometry according to drive type */ - if ( int13_is_fdd ( int13 ) ) { - if ( ( rc = int13_guess_geometry_fdd ( int13, &guessed_heads, + if ( int13_is_fdd ( sandev ) ) { + if ( ( rc = int13_guess_geometry_fdd ( sandev, &guessed_heads, &guessed_sectors )) != 0) return rc; } else { - if ( ( rc = int13_guess_geometry_hdd ( int13, scratch, + if ( ( rc = int13_guess_geometry_hdd ( sandev, scratch, &guessed_heads, &guessed_sectors )) != 0) return rc; @@ -700,7 +394,7 @@ static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) { int13->sectors_per_track = guessed_sectors; if ( ! int13->cylinders ) { /* Avoid attempting a 64-bit divide on a 32-bit system */ - blocks = int13_capacity32 ( int13 ); + blocks = int13_capacity32 ( sandev ); blocks_per_cyl = ( int13->heads * int13->sectors_per_track ); assert ( blocks_per_cyl != 0 ); int13->cylinders = ( blocks / blocks_per_cyl ); @@ -715,7 +409,8 @@ static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) { * Update BIOS drive count */ static void int13_sync_num_drives ( void ) { - struct int13_drive *int13; + struct san_device *sandev; + struct int13_data *int13; uint8_t *counter; uint8_t max_drive; uint8_t required; @@ -726,18 +421,19 @@ static void int13_sync_num_drives ( void ) { num_fdds = ( ( equipment_word & 0x0001 ) ? ( ( ( equipment_word >> 6 ) & 0x3 ) + 1 ) : 0 ); - /* Ensure count is large enough to cover all of our emulated drives */ - list_for_each_entry ( int13, &int13s, list ) { - counter = ( int13_is_fdd ( int13 ) ? &num_fdds : &num_drives ); - max_drive = int13->drive; + /* Ensure count is large enough to cover all of our SAN devices */ + for_each_sandev ( sandev ) { + int13 = sandev->priv; + counter = ( int13_is_fdd ( sandev ) ? &num_fdds : &num_drives ); + max_drive = sandev->drive; if ( max_drive < int13->natural_drive ) max_drive = int13->natural_drive; required = ( ( max_drive & 0x7f ) + 1 ); if ( *counter < required ) { *counter = required; - DBGC ( int13, "INT13 drive %02x added to drive count: " + DBGC ( sandev, "INT13 drive %02x added to drive count: " "%d HDDs, %d FDDs\n", - int13->drive, num_drives, num_fdds ); + sandev->drive, num_drives, num_fdds ); } } @@ -769,21 +465,17 @@ static void int13_check_num_drives ( void ) { /** * INT 13, 00 - Reset disk system * - * @v int13 Emulated drive + * @v sandev SAN device * @ret status Status code */ -static int int13_reset ( struct int13_drive *int13, +static int int13_reset ( struct san_device *sandev, struct i386_all_regs *ix86 __unused ) { int rc; - DBGC2 ( int13, "Reset drive\n" ); + DBGC2 ( sandev, "Reset drive\n" ); - /* Reopen underlying block device */ - if ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) - return -INT13_STATUS_RESET_FAILED; - - /* Check that block device is functional */ - if ( ( rc = int13_read_capacity ( int13 ) ) != 0 ) + /* Reset SAN device */ + if ( ( rc = sandev_reset ( sandev ) ) != 0 ) return -INT13_STATUS_RESET_FAILED; return 0; @@ -792,37 +484,38 @@ static int int13_reset ( struct int13_drive *int13, /** * INT 13, 01 - Get status of last operation * - * @v int13 Emulated drive + * @v sandev SAN device * @ret status Status code */ -static int int13_get_last_status ( struct int13_drive *int13, +static int int13_get_last_status ( struct san_device *sandev, struct i386_all_regs *ix86 __unused ) { - DBGC2 ( int13, "Get status of last operation\n" ); + struct int13_data *int13 = sandev->priv; + + DBGC2 ( sandev, "Get status of last operation\n" ); return int13->last_status; } /** * Read / write sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v al Number of sectors to read or write (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number * @v cl (bits 5:0) Sector number * @v dh Head number * @v es:bx Data buffer - * @v block_rw Block read/write method + * @v sandev_rw SAN device read/write method * @ret status Status code * @ret al Number of sectors read or written */ -static int int13_rw_sectors ( struct int13_drive *int13, +static int int13_rw_sectors ( struct san_device *sandev, struct i386_all_regs *ix86, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, - unsigned int count, - userptr_t buffer, - size_t len ) ) { + int ( * sandev_rw ) ( struct san_device *sandev, + uint64_t lba, + unsigned int count, + userptr_t buffer ) ) { + struct int13_data *int13 = sandev->priv; unsigned int cylinder, head, sector; unsigned long lba; unsigned int count; @@ -830,10 +523,10 @@ static int int13_rw_sectors ( struct int13_drive *int13, int rc; /* Validate blocksize */ - if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) { - DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) " + if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) { + DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) " "for non-extended read/write\n", - int13->drive, int13_blksize ( int13 ) ); + sandev->drive, sandev_blksize ( sandev ) ); return -INT13_STATUS_INVALID; } @@ -844,7 +537,7 @@ static int int13_rw_sectors ( struct int13_drive *int13, if ( ( cylinder >= int13->cylinders ) || ( head >= int13->heads ) || ( sector < 1 ) || ( sector > int13->sectors_per_track ) ) { - DBGC ( int13, "C/H/S %d/%d/%d out of range for geometry " + DBGC ( sandev, "C/H/S %d/%d/%d out of range for geometry " "%d/%d/%d\n", cylinder, head, sector, int13->cylinders, int13->heads, int13->sectors_per_track ); return -INT13_STATUS_INVALID; @@ -854,14 +547,14 @@ static int int13_rw_sectors ( struct int13_drive *int13, count = ix86->regs.al; buffer = real_to_user ( ix86->segs.es, ix86->regs.bx ); - DBGC2 ( int13, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n", + DBGC2 ( sandev, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n", cylinder, head, sector, lba, ix86->segs.es, ix86->regs.bx, count ); /* Read from / write to block device */ - if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x I/O failed: %s\n", - int13->drive, strerror ( rc ) ); + if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ){ + DBGC ( sandev, "INT13 drive %02x I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; } @@ -871,7 +564,7 @@ static int int13_rw_sectors ( struct int13_drive *int13, /** * INT 13, 02 - Read sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v al Number of sectors to read (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -881,16 +574,17 @@ static int int13_rw_sectors ( struct int13_drive *int13, * @ret status Status code * @ret al Number of sectors read */ -static int int13_read_sectors ( struct int13_drive *int13, +static int int13_read_sectors ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Read: " ); - return int13_rw_sectors ( int13, ix86, block_read ); + + DBGC2 ( sandev, "Read: " ); + return int13_rw_sectors ( sandev, ix86, sandev_read ); } /** * INT 13, 03 - Write sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v al Number of sectors to write (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -900,16 +594,17 @@ static int int13_read_sectors ( struct int13_drive *int13, * @ret status Status code * @ret al Number of sectors written */ -static int int13_write_sectors ( struct int13_drive *int13, +static int int13_write_sectors ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Write: " ); - return int13_rw_sectors ( int13, ix86, block_write ); + + DBGC2 ( sandev, "Write: " ); + return int13_rw_sectors ( sandev, ix86, sandev_write ); } /** * INT 13, 08 - Get drive parameters * - * @v int13 Emulated drive + * @v sandev SAN device * @ret status Status code * @ret ch Low bits of maximum cylinder number * @ret cl (bits 7:6) High bits of maximum cylinder number @@ -917,19 +612,20 @@ static int int13_write_sectors ( struct int13_drive *int13, * @ret dh Maximum head number * @ret dl Number of drives */ -static int int13_get_parameters ( struct int13_drive *int13, +static int int13_get_parameters ( struct san_device *sandev, struct i386_all_regs *ix86 ) { + struct int13_data *int13 = sandev->priv; unsigned int max_cylinder = int13->cylinders - 1; unsigned int max_head = int13->heads - 1; unsigned int max_sector = int13->sectors_per_track; /* sic */ - DBGC2 ( int13, "Get drive parameters\n" ); + DBGC2 ( sandev, "Get drive parameters\n" ); /* Validate blocksize */ - if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) { - DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) " + if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) { + DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) " "for non-extended parameters\n", - int13->drive, int13_blksize ( int13 ) ); + sandev->drive, sandev_blksize ( sandev ) ); return -INT13_STATUS_INVALID; } @@ -937,10 +633,10 @@ static int int13_get_parameters ( struct int13_drive *int13, ix86->regs.ch = ( max_cylinder & 0xff ); ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector ); ix86->regs.dh = max_head; - ix86->regs.dl = ( int13_is_fdd ( int13 ) ? num_fdds : num_drives ); + ix86->regs.dl = ( int13_is_fdd ( sandev ) ? num_fdds : num_drives ); /* Floppy-specific parameters */ - if ( int13_is_fdd ( int13 ) ) { + if ( int13_is_fdd ( sandev ) ) { ix86->regs.bl = INT13_FDD_TYPE_1M44; ix86->segs.es = rm_ds; ix86->regs.di = __from_data16 ( &int13_fdd_params ); @@ -952,21 +648,21 @@ static int int13_get_parameters ( struct int13_drive *int13, /** * INT 13, 15 - Get disk type * - * @v int13 Emulated drive + * @v sandev SAN device * @ret ah Type code * @ret cx:dx Sector count * @ret status Status code / disk type */ -static int int13_get_disk_type ( struct int13_drive *int13, +static int int13_get_disk_type ( struct san_device *sandev, struct i386_all_regs *ix86 ) { uint32_t blocks; - DBGC2 ( int13, "Get disk type\n" ); + DBGC2 ( sandev, "Get disk type\n" ); - if ( int13_is_fdd ( int13 ) ) { + if ( int13_is_fdd ( sandev ) ) { return INT13_DISK_TYPE_FDD; } else { - blocks = int13_capacity32 ( int13 ); + blocks = int13_capacity32 ( sandev ); ix86->regs.cx = ( blocks >> 16 ); ix86->regs.dx = ( blocks & 0xffff ); return INT13_DISK_TYPE_HDD; @@ -976,16 +672,17 @@ static int int13_get_disk_type ( struct int13_drive *int13, /** * INT 13, 41 - Extensions installation check * - * @v int13 Emulated drive + * @v sandev SAN device * @v bx 0x55aa * @ret bx 0xaa55 * @ret cx Extensions API support bitmap * @ret status Status code / API version */ -static int int13_extension_check ( struct int13_drive *int13 __unused, +static int int13_extension_check ( struct san_device *sandev __unused, struct i386_all_regs *ix86 ) { + if ( ix86->regs.bx == 0x55aa ) { - DBGC2 ( int13, "INT13 extensions installation check\n" ); + DBGC2 ( sandev, "INT13 extensions installation check\n" ); ix86->regs.bx = 0xaa55; ix86->regs.cx = ( INT13_EXTENSION_LINEAR | INT13_EXTENSION_EDD | @@ -999,19 +696,17 @@ static int int13_extension_check ( struct int13_drive *int13 __unused, /** * Extended read / write * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet - * @v block_rw Block read/write method + * @v sandev_rw SAN device read/write method * @ret status Status code */ -static int int13_extended_rw ( struct int13_drive *int13, +static int int13_extended_rw ( struct san_device *sandev, struct i386_all_regs *ix86, - int ( * block_rw ) ( struct interface *control, - struct interface *data, - uint64_t lba, - unsigned int count, - userptr_t buffer, - size_t len ) ) { + int ( * sandev_rw ) ( struct san_device *sandev, + uint64_t lba, + unsigned int count, + userptr_t buffer ) ) { struct int13_disk_address addr; uint8_t bufsize; uint64_t lba; @@ -1023,14 +718,14 @@ static int int13_extended_rw ( struct int13_drive *int13, * ELTORITO.SYS seems to assume that we are really a CD-ROM if * we support extended reads for a floppy drive. */ - if ( int13_is_fdd ( int13 ) ) + if ( int13_is_fdd ( sandev ) ) return -INT13_STATUS_INVALID; /* Get buffer size */ get_real ( bufsize, ix86->segs.ds, ( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) ); if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) { - DBGC2 ( int13, "\n", bufsize ); + DBGC2 ( sandev, "\n", bufsize ); return -INT13_STATUS_INVALID; } @@ -1038,17 +733,17 @@ static int int13_extended_rw ( struct int13_drive *int13, memset ( &addr, 0, sizeof ( addr ) ); copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize ); lba = addr.lba; - DBGC2 ( int13, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) ); + DBGC2 ( sandev, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) ); if ( ( addr.count == 0xff ) || ( ( addr.buffer.segment == 0xffff ) && ( addr.buffer.offset == 0xffff ) ) ) { buffer = phys_to_user ( addr.buffer_phys ); - DBGC2 ( int13, "%08llx", + DBGC2 ( sandev, "%08llx", ( ( unsigned long long ) addr.buffer_phys ) ); } else { buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset ); - DBGC2 ( int13, "%04x:%04x", addr.buffer.segment, + DBGC2 ( sandev, "%04x:%04x", addr.buffer.segment, addr.buffer.offset ); } if ( addr.count <= 0x7f ) { @@ -1056,15 +751,15 @@ static int int13_extended_rw ( struct int13_drive *int13, } else if ( addr.count == 0xff ) { count = addr.long_count; } else { - DBGC2 ( int13, " \n", addr.count ); + DBGC2 ( sandev, " \n", addr.count ); return -INT13_STATUS_INVALID; } - DBGC2 ( int13, " (count %ld)\n", count ); + DBGC2 ( sandev, " (count %ld)\n", count ); /* Read from / write to block device */ - if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x extended I/O failed: %s\n", - int13->drive, strerror ( rc ) ); + if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x extended I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); /* Record that no blocks were transferred successfully */ addr.count = 0; put_real ( addr.count, ix86->segs.ds, @@ -1079,37 +774,39 @@ static int int13_extended_rw ( struct int13_drive *int13, /** * INT 13, 42 - Extended read * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_read ( struct int13_drive *int13, +static int int13_extended_read ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Extended read: " ); - return int13_extended_rw ( int13, ix86, block_read ); + + DBGC2 ( sandev, "Extended read: " ); + return int13_extended_rw ( sandev, ix86, sandev_read ); } /** * INT 13, 43 - Extended write * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_write ( struct int13_drive *int13, +static int int13_extended_write ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - DBGC2 ( int13, "Extended write: " ); - return int13_extended_rw ( int13, ix86, block_write ); + + DBGC2 ( sandev, "Extended write: " ); + return int13_extended_rw ( sandev, ix86, sandev_write ); } /** * INT 13, 44 - Verify sectors * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_verify ( struct int13_drive *int13, +static int int13_extended_verify ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_disk_address addr; uint64_t lba; @@ -1121,7 +818,7 @@ static int int13_extended_verify ( struct int13_drive *int13, sizeof ( addr )); lba = addr.lba; count = addr.count; - DBGC2 ( int13, "Verify: LBA %08llx (count %ld)\n", + DBGC2 ( sandev, "Verify: LBA %08llx (count %ld)\n", ( ( unsigned long long ) lba ), count ); } @@ -1132,11 +829,11 @@ static int int13_extended_verify ( struct int13_drive *int13, /** * INT 13, 44 - Extended seek * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_seek ( struct int13_drive *int13, +static int int13_extended_seek ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_disk_address addr; uint64_t lba; @@ -1148,7 +845,7 @@ static int int13_extended_seek ( struct int13_drive *int13, sizeof ( addr )); lba = addr.lba; count = addr.count; - DBGC2 ( int13, "Seek: LBA %08llx (count %ld)\n", + DBGC2 ( sandev, "Seek: LBA %08llx (count %ld)\n", ( ( unsigned long long ) lba ), count ); } @@ -1159,12 +856,13 @@ static int int13_extended_seek ( struct int13_drive *int13, /** * Build device path information * - * @v int13 Emulated drive + * @v sandev SAN device * @v dpi Device path information * @ret rc Return status code */ -static int int13_device_path_info ( struct int13_drive *int13, +static int int13_device_path_info ( struct san_device *sandev, struct edd_device_path_information *dpi ) { + struct san_path *sanpath; struct device *device; struct device_description *desc; unsigned int i; @@ -1172,15 +870,17 @@ static int int13_device_path_info ( struct int13_drive *int13, int rc; /* Reopen block device if necessary */ - if ( ( int13->block_rc != 0 ) && - ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) ) + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) return rc; + sanpath = sandev->active; + assert ( sanpath != NULL ); /* Get underlying hardware device */ - device = identify_device ( &int13->block ); + device = identify_device ( &sanpath->block ); if ( ! device ) { - DBGC ( int13, "INT13 drive %02x cannot identify hardware " - "device\n", int13->drive ); + DBGC ( sandev, "INT13 drive %02x cannot identify hardware " + "device\n", sandev->drive ); return -ENODEV; } @@ -1195,16 +895,16 @@ static int int13_device_path_info ( struct int13_drive *int13, dpi->interface_path.pci.channel = 0xff; /* unused */ break; default: - DBGC ( int13, "INT13 drive %02x unrecognised bus type %d\n", - int13->drive, desc->bus_type ); + DBGC ( sandev, "INT13 drive %02x unrecognised bus type %d\n", + sandev->drive, desc->bus_type ); return -ENOTSUP; } /* Get EDD block device description */ - if ( ( rc = edd_describe ( &int13->block, &dpi->interface_type, + if ( ( rc = edd_describe ( &sanpath->block, &dpi->interface_type, &dpi->device_path ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x cannot identify block device: " - "%s\n", int13->drive, strerror ( rc ) ); + DBGC ( sandev, "INT13 drive %02x cannot identify block device: " + "%s\n", sandev->drive, strerror ( rc ) ); return rc; } @@ -1221,12 +921,13 @@ static int int13_device_path_info ( struct int13_drive *int13, /** * INT 13, 48 - Get extended parameters * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Drive parameter table * @ret status Status code */ -static int int13_get_extended_parameters ( struct int13_drive *int13, +static int int13_get_extended_parameters ( struct san_device *sandev, struct i386_all_regs *ix86 ) { + struct int13_data *int13 = sandev->priv; struct int13_disk_parameters params; struct segoff address; size_t len = sizeof ( params ); @@ -1237,26 +938,26 @@ static int int13_get_extended_parameters ( struct int13_drive *int13, get_real ( bufsize, ix86->segs.ds, ( ix86->regs.si + offsetof ( typeof ( params ), bufsize ))); - DBGC2 ( int13, "Get extended drive parameters to %04x:%04x+%02x\n", + DBGC2 ( sandev, "Get extended drive parameters to %04x:%04x+%02x\n", ix86->segs.ds, ix86->regs.si, bufsize ); /* Build drive parameters */ memset ( ¶ms, 0, sizeof ( params ) ); params.flags = INT13_FL_DMA_TRANSPARENT; if ( ( int13->cylinders < 1024 ) && - ( int13_capacity ( int13 ) <= INT13_MAX_CHS_SECTORS ) ) { + ( sandev_capacity ( sandev ) <= INT13_MAX_CHS_SECTORS ) ) { params.flags |= INT13_FL_CHS_VALID; } params.cylinders = int13->cylinders; params.heads = int13->heads; params.sectors_per_track = int13->sectors_per_track; - params.sectors = int13_capacity ( int13 ); - params.sector_size = int13_blksize ( int13 ); + params.sectors = sandev_capacity ( sandev ); + params.sector_size = sandev_blksize ( sandev ); memset ( ¶ms.dpte, 0xff, sizeof ( params.dpte ) ); - if ( ( rc = int13_device_path_info ( int13, ¶ms.dpi ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not provide device " + if ( ( rc = int13_device_path_info ( sandev, ¶ms.dpi ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not provide device " "path information: %s\n", - int13->drive, strerror ( rc ) ); + sandev->drive, strerror ( rc ) ); len = offsetof ( typeof ( params ), dpi ); } @@ -1272,11 +973,11 @@ static int int13_get_extended_parameters ( struct int13_drive *int13, params.bufsize = offsetof ( typeof ( params ), dpi ); } - DBGC ( int13, "INT 13 drive %02x described using extended " - "parameters:\n", int13->drive ); + DBGC ( sandev, "INT 13 drive %02x described using extended " + "parameters:\n", sandev->drive ); address.segment = ix86->segs.ds; address.offset = ix86->regs.si; - DBGC_HDA ( int13, address, ¶ms, len ); + DBGC_HDA ( sandev, address, ¶ms, len ); /* Return drive parameters */ if ( len > bufsize ) @@ -1289,29 +990,29 @@ static int int13_get_extended_parameters ( struct int13_drive *int13, /** * INT 13, 4b - Get status or terminate CD-ROM emulation * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Specification packet * @ret status Status code */ -static int int13_cdrom_status_terminate ( struct int13_drive *int13, +static int int13_cdrom_status_terminate ( struct san_device *sandev, struct i386_all_regs *ix86 ) { struct int13_cdrom_specification specification; - DBGC2 ( int13, "Get CD-ROM emulation status to %04x:%04x%s\n", + DBGC2 ( sandev, "Get CD-ROM emulation status to %04x:%04x%s\n", ix86->segs.ds, ix86->regs.si, ( ix86->regs.al ? "" : " and terminate" ) ); /* Fail if we are not a CD-ROM */ - if ( ! int13->is_cdrom ) { - DBGC ( int13, "INT13 drive %02x is not a CD-ROM\n", - int13->drive ); + if ( ! sandev->is_cdrom ) { + DBGC ( sandev, "INT13 drive %02x is not a CD-ROM\n", + sandev->drive ); return -INT13_STATUS_INVALID; } /* Build specification packet */ memset ( &specification, 0, sizeof ( specification ) ); specification.size = sizeof ( specification ); - specification.drive = int13->drive; + specification.drive = sandev->drive; /* Return specification packet */ copy_to_real ( ix86->segs.ds, ix86->regs.si, &specification, @@ -1324,33 +1025,35 @@ static int int13_cdrom_status_terminate ( struct int13_drive *int13, /** * INT 13, 4d - Read CD-ROM boot catalog * - * @v int13 Emulated drive + * @v sandev SAN device * @v ds:si Command packet * @ret status Status code */ -static int int13_cdrom_read_boot_catalog ( struct int13_drive *int13, +static int int13_cdrom_read_boot_catalog ( struct san_device *sandev, struct i386_all_regs *ix86 ) { + struct int13_data *int13 = sandev->priv; struct int13_cdrom_boot_catalog_command command; + unsigned int start; int rc; /* Read parameters from command packet */ copy_from_real ( &command, ix86->segs.ds, ix86->regs.si, sizeof ( command ) ); - DBGC2 ( int13, "Read CD-ROM boot catalog to %08x\n", command.buffer ); + DBGC2 ( sandev, "Read CD-ROM boot catalog to %08x\n", command.buffer ); /* Fail if we have no boot catalog */ if ( ! int13->boot_catalog ) { - DBGC ( int13, "INT13 drive %02x has no boot catalog\n", - int13->drive ); + DBGC ( sandev, "INT13 drive %02x has no boot catalog\n", + sandev->drive ); return -INT13_STATUS_INVALID; } + start = ( int13->boot_catalog + command.start ); /* Read from boot catalog */ - if ( ( rc = int13_rw ( int13, ( int13->boot_catalog + command.start ), - command.count, phys_to_user ( command.buffer ), - block_read ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not read boot catalog: " - "%s\n", int13->drive, strerror ( rc ) ); + if ( ( rc = sandev_read ( sandev, start, command.count, + phys_to_user ( command.buffer ) ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not read boot catalog: " + "%s\n", sandev->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; } @@ -1364,79 +1067,81 @@ static int int13_cdrom_read_boot_catalog ( struct int13_drive *int13, static __asmcall void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; - struct int13_drive *int13; + struct san_device *sandev; + struct int13_data *int13; int status; /* Check BIOS hasn't killed off our drive */ int13_check_num_drives(); - list_for_each_entry ( int13, &int13s, list ) { + for_each_sandev ( sandev ) { - if ( bios_drive != int13->drive ) { + int13 = sandev->priv; + if ( bios_drive != sandev->drive ) { /* Remap any accesses to this drive's natural number */ if ( bios_drive == int13->natural_drive ) { - DBGC2 ( int13, "INT13,%02x (%02x) remapped to " + DBGC2 ( sandev, "INT13,%02x (%02x) remapped to " "(%02x)\n", ix86->regs.ah, - bios_drive, int13->drive ); - ix86->regs.dl = int13->drive; + bios_drive, sandev->drive ); + ix86->regs.dl = sandev->drive; return; } else if ( ( ( bios_drive & 0x7f ) == 0x7f ) && ( command == INT13_CDROM_STATUS_TERMINATE ) - && int13->is_cdrom ) { + && sandev->is_cdrom ) { /* Catch non-drive-specific CD-ROM calls */ } else { continue; } } - DBGC2 ( int13, "INT13,%02x (%02x): ", + DBGC2 ( sandev, "INT13,%02x (%02x): ", ix86->regs.ah, bios_drive ); switch ( command ) { case INT13_RESET: - status = int13_reset ( int13, ix86 ); + status = int13_reset ( sandev, ix86 ); break; case INT13_GET_LAST_STATUS: - status = int13_get_last_status ( int13, ix86 ); + status = int13_get_last_status ( sandev, ix86 ); break; case INT13_READ_SECTORS: - status = int13_read_sectors ( int13, ix86 ); + status = int13_read_sectors ( sandev, ix86 ); break; case INT13_WRITE_SECTORS: - status = int13_write_sectors ( int13, ix86 ); + status = int13_write_sectors ( sandev, ix86 ); break; case INT13_GET_PARAMETERS: - status = int13_get_parameters ( int13, ix86 ); + status = int13_get_parameters ( sandev, ix86 ); break; case INT13_GET_DISK_TYPE: - status = int13_get_disk_type ( int13, ix86 ); + status = int13_get_disk_type ( sandev, ix86 ); break; case INT13_EXTENSION_CHECK: - status = int13_extension_check ( int13, ix86 ); + status = int13_extension_check ( sandev, ix86 ); break; case INT13_EXTENDED_READ: - status = int13_extended_read ( int13, ix86 ); + status = int13_extended_read ( sandev, ix86 ); break; case INT13_EXTENDED_WRITE: - status = int13_extended_write ( int13, ix86 ); + status = int13_extended_write ( sandev, ix86 ); break; case INT13_EXTENDED_VERIFY: - status = int13_extended_verify ( int13, ix86 ); + status = int13_extended_verify ( sandev, ix86 ); break; case INT13_EXTENDED_SEEK: - status = int13_extended_seek ( int13, ix86 ); + status = int13_extended_seek ( sandev, ix86 ); break; case INT13_GET_EXTENDED_PARAMETERS: - status = int13_get_extended_parameters ( int13, ix86 ); + status = int13_get_extended_parameters ( sandev, ix86 ); break; case INT13_CDROM_STATUS_TERMINATE: - status = int13_cdrom_status_terminate ( int13, ix86 ); + status = int13_cdrom_status_terminate ( sandev, ix86 ); break; case INT13_CDROM_READ_BOOT_CATALOG: - status = int13_cdrom_read_boot_catalog ( int13, ix86 ); + status = int13_cdrom_read_boot_catalog ( sandev, ix86 ); break; default: - DBGC2 ( int13, "*** Unrecognised INT13 ***\n" ); + DBGC2 ( sandev, "*** Unrecognised INT13 ***\n" ); status = -INT13_STATUS_INVALID; break; } @@ -1447,8 +1152,8 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) { /* Negative status indicates an error */ if ( status < 0 ) { status = -status; - DBGC ( int13, "INT13,%02x (%02x) failed with status " - "%02x\n", ix86->regs.ah, int13->drive, status ); + DBGC ( sandev, "INT13,%02x (%02x) failed with status " + "%02x\n", ix86->regs.ah, sandev->drive, status ); } else { ix86->flags &= ~CF; } @@ -1480,9 +1185,7 @@ static void int13_hook_vector ( void ) { /* Clear OF, set CF, call int13() */ "orb $0, %%al\n\t" "stc\n\t" - "pushl %0\n\t" - "pushw %%cs\n\t" - "call prot_call\n\t" + VIRT_CALL ( int13 ) /* Chain if OF not set */ "jo 1f\n\t" "pushfw\n\t" @@ -1513,228 +1216,143 @@ static void int13_hook_vector ( void ) { "\n3:\n\t" "movw %%bp, %%sp\n\t" "popw %%bp\n\t" - "iret\n\t" ) - : : "i" ( int13 ) ); + "iret\n\t" ) : : ); - hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, - &int13_vector ); + hook_bios_interrupt ( 0x13, ( intptr_t ) int13_wrapper, &int13_vector ); } /** * Unhook INT 13 handler */ static void int13_unhook_vector ( void ) { - unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, + unhook_bios_interrupt ( 0x13, ( intptr_t ) int13_wrapper, &int13_vector ); } /** - * Check INT13 emulated drive flow control window + * Hook INT 13 SAN device * - * @v int13 Emulated drive - */ -static size_t int13_block_window ( struct int13_drive *int13 __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; -} - -/** - * Handle INT 13 emulated drive underlying block device closing - * - * @v int13 Emulated drive - * @v rc Reason for close - */ -static void int13_block_close ( struct int13_drive *int13, int rc ) { - - /* Any closing is an error from our point of view */ - if ( rc == 0 ) - rc = -ENOTCONN; - - DBGC ( int13, "INT13 drive %02x went away: %s\n", - int13->drive, strerror ( rc ) ); - - /* Record block device error code */ - int13->block_rc = rc; - - /* Shut down interfaces */ - intf_restart ( &int13->block, rc ); -} - -/** INT 13 drive interface operations */ -static struct interface_operation int13_block_op[] = { - INTF_OP ( xfer_window, struct int13_drive *, int13_block_window ), - INTF_OP ( intf_close, struct int13_drive *, int13_block_close ), -}; - -/** INT 13 drive interface descriptor */ -static struct interface_descriptor int13_block_desc = - INTF_DESC ( struct int13_drive, block, int13_block_op ); - -/** - * Free INT 13 emulated drive - * - * @v refcnt Reference count - */ -static void int13_free ( struct refcnt *refcnt ) { - struct int13_drive *int13 = - container_of ( refcnt, struct int13_drive, refcnt ); - - uri_put ( int13->uri ); - free ( int13 ); -} - -/** - * Hook INT 13 emulated drive - * - * @v uri URI * @v drive Drive number - * @ret rc Return status code + * @v uris List of URIs + * @v count Number of URIs + * @v flags Flags + * @ret drive Drive number, or negative error * * Registers the drive with the INT 13 emulation subsystem, and hooks * the INT 13 interrupt vector (if not already hooked). */ -static int int13_hook ( struct uri *uri, unsigned int drive ) { - struct int13_drive *int13; +static int int13_hook ( unsigned int drive, struct uri **uris, + unsigned int count, unsigned int flags ) { + struct san_device *sandev; + struct int13_data *int13; unsigned int natural_drive; void *scratch; + int need_hook = ( ! have_sandevs() ); int rc; /* Calculate natural drive number */ int13_sync_num_drives(); natural_drive = ( ( drive & 0x80 ) ? ( num_drives | 0x80 ) : num_fdds ); - /* Check that drive number is not in use */ - list_for_each_entry ( int13, &int13s, list ) { - if ( int13->drive == drive ) { - rc = -EADDRINUSE; - goto err_in_use; - } - } + /* Use natural drive number if directed to do so */ + if ( ( drive & 0x7f ) == 0x7f ) + drive = natural_drive; - /* Allocate and initialise structure */ - int13 = zalloc ( sizeof ( *int13 ) ); - if ( ! int13 ) { + /* Allocate SAN device */ + sandev = alloc_sandev ( uris, count, sizeof ( *int13 ) ); + if ( ! sandev ) { rc = -ENOMEM; - goto err_zalloc; + goto err_alloc; } - ref_init ( &int13->refcnt, int13_free ); - intf_init ( &int13->block, &int13_block_desc, &int13->refcnt ); - int13->uri = uri_get ( uri ); - int13->drive = drive; + int13 = sandev->priv; int13->natural_drive = natural_drive; - /* Open block device interface */ - if ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) - goto err_reopen_block; - - /* Read device capacity */ - if ( ( rc = int13_read_capacity ( int13 ) ) != 0 ) - goto err_read_capacity; + /* Register SAN device */ + if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) { + DBGC ( sandev, "INT13 drive %02x could not register: %s\n", + drive, strerror ( rc ) ); + goto err_register; + } /* Allocate scratch area */ - scratch = malloc ( int13_blksize ( int13 ) ); + scratch = malloc ( sandev_blksize ( sandev ) ); if ( ! scratch ) goto err_alloc_scratch; /* Parse parameters, if present */ - if ( ( rc = int13_parse_iso9660 ( int13, scratch ) ) != 0 ) - goto err_parse_iso9660; + if ( sandev->is_cdrom && + ( ( rc = int13_parse_eltorito ( sandev, scratch ) ) != 0 ) ) + goto err_parse_eltorito; - /* Give drive a default geometry */ - if ( ( rc = int13_guess_geometry ( int13, scratch ) ) != 0 ) + /* Give drive a default geometry, if applicable */ + if ( ( sandev_blksize ( sandev ) == INT13_BLKSIZE ) && + ( ( rc = int13_guess_geometry ( sandev, scratch ) ) != 0 ) ) goto err_guess_geometry; - DBGC ( int13, "INT13 drive %02x (naturally %02x) registered with C/H/S " - "geometry %d/%d/%d\n", int13->drive, int13->natural_drive, - int13->cylinders, int13->heads, int13->sectors_per_track ); + DBGC ( sandev, "INT13 drive %02x (naturally %02x) registered with " + "C/H/S geometry %d/%d/%d\n", + sandev->drive, int13->natural_drive, int13->cylinders, + int13->heads, int13->sectors_per_track ); /* Hook INT 13 vector if not already hooked */ - if ( list_empty ( &int13s ) ) { + if ( need_hook ) { int13_hook_vector(); devices_get(); } - /* Add to list of emulated drives */ - list_add ( &int13->list, &int13s ); - /* Update BIOS drive count */ int13_sync_num_drives(); free ( scratch ); - return 0; + return drive; err_guess_geometry: - err_parse_iso9660: + err_parse_eltorito: free ( scratch ); err_alloc_scratch: - err_read_capacity: - err_reopen_block: - intf_shutdown ( &int13->block, rc ); - ref_put ( &int13->refcnt ); - err_zalloc: - err_in_use: + unregister_sandev ( sandev ); + err_register: + sandev_put ( sandev ); + err_alloc: return rc; } /** - * Find INT 13 emulated drive by drive number - * - * @v drive Drive number - * @ret int13 Emulated drive, or NULL - */ -static struct int13_drive * int13_find ( unsigned int drive ) { - struct int13_drive *int13; - - list_for_each_entry ( int13, &int13s, list ) { - if ( int13->drive == drive ) - return int13; - } - return NULL; -} - -/** - * Unhook INT 13 emulated drive + * Unhook INT 13 SAN device * * @v drive Drive number * * Unregisters the drive from the INT 13 emulation subsystem. If this - * is the last emulated drive, the INT 13 vector is unhooked (if + * is the last SAN device, the INT 13 vector is unhooked (if * possible). */ static void int13_unhook ( unsigned int drive ) { - struct int13_drive *int13; + struct san_device *sandev; /* Find drive */ - int13 = int13_find ( drive ); - if ( ! int13 ) { - DBG ( "INT13 cannot find emulated drive %02x\n", drive ); + sandev = sandev_find ( drive ); + if ( ! sandev ) { + DBG ( "INT13 cannot find drive %02x\n", drive ); return; } - /* Shut down interfaces */ - intf_shutdown ( &int13->block, 0 ); - - /* Remove from list of emulated drives */ - list_del ( &int13->list ); + /* Unregister SAN device */ + unregister_sandev ( sandev ); /* Should adjust BIOS drive count, but it's difficult * to do so reliably. */ - DBGC ( int13, "INT13 drive %02x unregistered\n", int13->drive ); + DBGC ( sandev, "INT13 drive %02x unregistered\n", sandev->drive ); /* Unhook INT 13 vector if no more drives */ - if ( list_empty ( &int13s ) ) { + if ( ! have_sandevs() ) { devices_put(); int13_unhook_vector(); } - /* Drop list's reference to drive */ - ref_put ( &int13->refcnt ); + /* Drop reference to drive */ + sandev_put ( sandev ); } /** @@ -1745,7 +1363,7 @@ static void int13_unhook ( unsigned int drive ) { * @ret rc Return status code */ static int int13_load_mbr ( unsigned int drive, struct segoff *address ) { - uint8_t status; + uint16_t status; int discard_b, discard_c, discard_d; uint16_t magic; @@ -1769,7 +1387,7 @@ static int int13_load_mbr ( unsigned int drive, struct segoff *address ) { : "a" ( 0x0201 ), "b" ( *address ), "c" ( 1 ), "d" ( drive ) ); if ( status ) { - DBG ( "INT13 drive %02x could not read MBR (status %02x)\n", + DBG ( "INT13 drive %02x could not read MBR (status %04x)\n", drive, status ); return -EIO; } @@ -1812,7 +1430,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { struct eltorito_validation_entry valid; struct eltorito_boot_entry boot; } __attribute__ (( packed )) catalog; - uint8_t status; + uint16_t status; /* Use INT 13, 4d to read the boot catalog */ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" @@ -1827,7 +1445,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { "S" ( __from_data16 ( &eltorito_cmd ) ) ); if ( status ) { DBG ( "INT13 drive %02x could not read El Torito boot catalog " - "(status %02x)\n", drive, status ); + "(status %04x)\n", drive, status ); return -EIO; } copy_from_user ( &catalog, phys_to_user ( eltorito_cmd.buffer ), 0, @@ -1874,7 +1492,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { "S" ( __from_data16 ( &eltorito_address ) ) ); if ( status ) { DBG ( "INT13 drive %02x could not read El Torito boot image " - "(status %02x)\n", drive, status ); + "(status %04x)\n", drive, status ); return -EIO; } @@ -1885,6 +1503,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { * Attempt to boot from an INT 13 drive * * @v drive Drive number + * @v filename Filename (or NULL to use default) * @ret rc Return status code * * This boots from the specified INT 13 drive by loading the Master @@ -1894,7 +1513,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) { * * Note that this function can never return success, by definition. */ -static int int13_boot ( unsigned int drive ) { +static int int13_boot ( unsigned int drive, const char *filename __unused ) { struct memory_map memmap; struct segoff address; int rc; @@ -1922,71 +1541,86 @@ static int int13_boot ( unsigned int drive ) { return -ECANCELED; /* -EIMPOSSIBLE */ } -/** A boot firmware table generated by iPXE */ -union xbft_table { - /** ACPI header */ - struct acpi_description_header acpi; - /** Padding */ - char pad[768]; -}; +/** Maximum size of boot firmware table(s) */ +#define XBFTAB_SIZE 768 -/** The boot firmware table generated by iPXE */ -static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) )); +/** Alignment of boot firmware table entries */ +#define XBFTAB_ALIGN 16 + +/** The boot firmware table(s) generated by iPXE */ +static uint8_t __bss16_array ( xbftab, [XBFTAB_SIZE] ) + __attribute__ (( aligned ( XBFTAB_ALIGN ) )); #define xbftab __use_data16 ( xbftab ) +/** Total used length of boot firmware tables */ +static size_t xbftab_used; + /** - * Describe INT 13 emulated drive for SAN-booted operating system + * Install ACPI table * - * @v drive Drive number + * @v acpi ACPI description header * @ret rc Return status code */ -static int int13_describe ( unsigned int drive ) { - struct int13_drive *int13; +static int int13_install ( struct acpi_header *acpi ) { struct segoff xbft_address; - int rc; + struct acpi_header *installed; + size_t len; - /* Find drive */ - int13 = int13_find ( drive ); - if ( ! int13 ) { - DBG ( "INT13 cannot find emulated drive %02x\n", drive ); - return -ENODEV; + /* Check length */ + len = acpi->length; + if ( len > ( sizeof ( xbftab ) - xbftab_used ) ) { + DBGC ( acpi, "INT13 out of space for %s table\n", + acpi_name ( acpi->signature ) ); + return -ENOSPC; } - /* Reopen block device if necessary */ - if ( ( int13->block_rc != 0 ) && - ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) ) - return rc; - - /* Clear table */ - memset ( &xbftab, 0, sizeof ( xbftab ) ); + /* Install table */ + installed = ( ( ( void * ) xbftab ) + xbftab_used ); + memcpy ( installed, acpi, len ); + xbft_address.segment = rm_ds; + xbft_address.offset = __from_data16 ( installed ); /* Fill in common parameters */ - strncpy ( xbftab.acpi.oem_id, "FENSYS", - sizeof ( xbftab.acpi.oem_id ) ); - strncpy ( xbftab.acpi.oem_table_id, "iPXE", - sizeof ( xbftab.acpi.oem_table_id ) ); + strncpy ( installed->oem_id, "FENSYS", + sizeof ( installed->oem_id ) ); + strncpy ( installed->oem_table_id, "iPXE", + sizeof ( installed->oem_table_id ) ); - /* Fill in remaining parameters */ - if ( ( rc = acpi_describe ( &int13->block, &xbftab.acpi, - sizeof ( xbftab ) ) ) != 0 ) { - DBGC ( int13, "INT13 drive %02x could not create ACPI " - "description: %s\n", int13->drive, strerror ( rc ) ); + /* Fix checksum */ + acpi_fix_checksum ( installed ); + + /* Update used length */ + xbftab_used = ( ( xbftab_used + len + XBFTAB_ALIGN - 1 ) & + ~( XBFTAB_ALIGN - 1 ) ); + + DBGC ( acpi, "INT13 installed %s:\n", + acpi_name ( installed->signature ) ); + DBGC_HDA ( acpi, xbft_address, installed, len ); + return 0; +} + +/** + * Describe SAN devices for SAN-booted operating system + * + * @ret rc Return status code + */ +static int int13_describe ( void ) { + int rc; + + /* Clear tables */ + memset ( &xbftab, 0, sizeof ( xbftab ) ); + xbftab_used = 0; + + /* Install ACPI tables */ + if ( ( rc = acpi_install ( int13_install ) ) != 0 ) { + DBG ( "INT13 could not install ACPI tables: %s\n", + strerror ( rc ) ); return rc; } - /* Fix up ACPI checksum */ - acpi_fix_checksum ( &xbftab.acpi ); - xbft_address.segment = rm_ds; - xbft_address.offset = __from_data16 ( &xbftab ); - DBGC ( int13, "INT13 drive %02x described using boot firmware " - "table:\n", int13->drive ); - DBGC_HDA ( int13, xbft_address, &xbftab, - le32_to_cpu ( xbftab.acpi.length ) ); - return 0; } -PROVIDE_SANBOOT_INLINE ( pcbios, san_default_drive ); PROVIDE_SANBOOT ( pcbios, san_hook, int13_hook ); PROVIDE_SANBOOT ( pcbios, san_unhook, int13_unhook ); PROVIDE_SANBOOT ( pcbios, san_boot, int13_boot ); diff --git a/src/arch/i386/interface/pcbios/int13con.c b/src/arch/x86/interface/pcbios/int13con.c similarity index 92% rename from src/arch/i386/interface/pcbios/int13con.c rename to src/arch/x86/interface/pcbios/int13con.c index 2414c6909..8106cd153 100644 --- a/src/arch/i386/interface/pcbios/int13con.c +++ b/src/arch/x86/interface/pcbios/int13con.c @@ -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; diff --git a/src/arch/i386/firmware/pcbios/memmap.c b/src/arch/x86/interface/pcbios/memmap.c similarity index 98% rename from src/arch/i386/firmware/pcbios/memmap.c rename to src/arch/x86/interface/pcbios/memmap.c index bcacecd6a..daae382b8 100644 --- a/src/arch/i386/firmware/pcbios/memmap.c +++ b/src/arch/x86/interface/pcbios/memmap.c @@ -92,7 +92,7 @@ static unsigned int extmemsize_e801 ( void ) { "int $0x15\n\t" "pushfw\n\t" "popw %w0\n\t" ) - : "=r" ( flags ), + : "=R" ( flags ), "=a" ( extmem_1m_to_16m_k ), "=b" ( extmem_16m_plus_64k ), "=c" ( confmem_1m_to_16m_k ), @@ -174,7 +174,7 @@ static int meme820 ( struct memory_map *memmap ) { struct memory_region *prev_region = NULL; uint32_t next = 0; uint32_t smap; - size_t size; + uint32_t size; unsigned int flags; unsigned int discard_D; @@ -216,7 +216,7 @@ static int meme820 ( struct memory_map *memmap ) { } if ( size < E820_MIN_SIZE ) { - DBG ( "INT 15,e820 returned only %zd bytes\n", size ); + DBG ( "INT 15,e820 returned only %d bytes\n", size ); return -EINVAL; } diff --git a/src/arch/i386/interface/pcbios/memtop_umalloc.c b/src/arch/x86/interface/pcbios/memtop_umalloc.c similarity index 79% rename from src/arch/i386/interface/pcbios/memtop_umalloc.c rename to src/arch/x86/interface/pcbios/memtop_umalloc.c index 957f8e324..f1ab73e29 100644 --- a/src/arch/i386/interface/pcbios/memtop_umalloc.c +++ b/src/arch/x86/interface/pcbios/memtop_umalloc.c @@ -38,6 +38,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** Maximum usable address for external allocated memory */ +#define EM_MAX_ADDRESS 0xffffffffUL + /** Alignment of external allocated memory */ #define EM_ALIGN ( 4 * 1024 ) @@ -61,6 +64,56 @@ static userptr_t bottom = UNULL; /** Remaining space on heap */ static size_t heap_size; +/** + * Find largest usable memory region + * + * @ret start Start of region + * @ret len Length of region + */ +size_t largest_memblock ( userptr_t *start ) { + struct memory_map memmap; + struct memory_region *region; + physaddr_t max = EM_MAX_ADDRESS; + physaddr_t region_start; + physaddr_t region_end; + size_t region_len; + unsigned int i; + size_t len = 0; + + /* Avoid returning uninitialised data on error */ + *start = UNULL; + + /* Scan through all memory regions */ + get_memmap ( &memmap ); + for ( i = 0 ; i < memmap.count ; i++ ) { + region = &memmap.regions[i]; + DBG ( "Considering [%llx,%llx)\n", region->start, region->end ); + + /* Truncate block to maximum physical address */ + if ( region->start > max ) { + DBG ( "...starts after maximum address %lx\n", max ); + continue; + } + region_start = region->start; + if ( region->end > max ) { + DBG ( "...end truncated to maximum address %lx\n", max); + region_end = 0; /* =max, given the wraparound */ + } else { + region_end = region->end; + } + region_len = ( region_end - region_start ); + + /* Use largest block */ + if ( region_len > len ) { + DBG ( "...new best block found\n" ); + *start = phys_to_user ( region_start ); + len = region_len; + } + } + + return len; +} + /** * Initialise external heap * diff --git a/src/arch/i386/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c similarity index 97% rename from src/arch/i386/interface/pcbios/pcibios.c rename to src/arch/x86/interface/pcbios/pcibios.c index 34efa0b39..07ac0c18d 100644 --- a/src/arch/i386/interface/pcbios/pcibios.c +++ b/src/arch/x86/interface/pcbios/pcibios.c @@ -70,7 +70,7 @@ static int pcibios_num_bus ( void ) { */ int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ int discard_b, discard_D; - int status; + uint16_t status; __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" "int $0x1a\n\t" @@ -85,7 +85,7 @@ int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ "b" ( pci->busdevfn ) : "edx" ); - return ( ( status >> 8 ) & 0xff ); + return ( status >> 8 ); } /** @@ -98,7 +98,7 @@ int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ */ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ int discard_b, discard_c, discard_D; - int status; + uint16_t status; __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" "int $0x1a\n\t" @@ -111,7 +111,7 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ "b" ( pci->busdevfn ), "c" ( value ) : "edx" ); - return ( ( status >> 8 ) & 0xff ); + return ( status >> 8 ); } PROVIDE_PCIAPI ( pcbios, pci_num_bus, pcibios_num_bus ); diff --git a/src/arch/i386/firmware/pcbios/pnpbios.c b/src/arch/x86/interface/pcbios/pnpbios.c similarity index 100% rename from src/arch/i386/firmware/pcbios/pnpbios.c rename to src/arch/x86/interface/pcbios/pnpbios.c diff --git a/src/arch/x86/interface/pcbios/rsdp.c b/src/arch/x86/interface/pcbios/rsdp.c new file mode 100644 index 000000000..8da0b5588 --- /dev/null +++ b/src/arch/x86/interface/pcbios/rsdp.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include + +/** 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 ); diff --git a/src/arch/i386/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c similarity index 71% rename from src/arch/i386/interface/pcbios/rtc_entropy.c rename to src/arch/x86/interface/pcbios/rtc_entropy.c index 9aab03c03..e9e6baa59 100644 --- a/src/arch/i386/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -31,14 +31,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include +#include #include #include #include #include -/** RTC "interrupt triggered" flag */ -static uint8_t __text16 ( rtc_flag ); -#define rtc_flag __use_text16 ( rtc_flag ) +/** Maximum time to wait for an RTC interrupt, in milliseconds */ +#define RTC_MAX_WAIT_MS 100 /** RTC interrupt handler */ extern void rtc_isr ( void ); @@ -46,6 +47,10 @@ extern void rtc_isr ( void ); /** Previous RTC interrupt handler */ static struct segoff rtc_old_handler; +/** Flag set by RTC interrupt handler */ +extern volatile uint8_t __text16 ( rtc_flag ); +#define rtc_flag __use_text16 ( rtc_flag ) + /** * Hook RTC interrupt handler * @@ -58,27 +63,27 @@ static void rtc_hook_isr ( void ) { /* Preserve registers */ "pushw %%ax\n\t" /* Set "interrupt triggered" flag */ - "cs movb $0x01, %c0\n\t" + "movb $0x01, %%cs:rtc_flag\n\t" /* Read RTC status register C to * acknowledge interrupt */ - "movb %3, %%al\n\t" - "outb %%al, %1\n\t" - "inb %2\n\t" + "movb %2, %%al\n\t" + "outb %%al, %0\n\t" + "inb %1\n\t" /* Send EOI */ "movb $0x20, %%al\n\t" "outb %%al, $0xa0\n\t" "outb %%al, $0x20\n\t" /* Restore registers and return */ "popw %%ax\n\t" - "iret\n\t" ) + "iret\n\t" + "\nrtc_flag:\n\t" + ".byte 0\n\t" ) : - : "p" ( __from_text16 ( &rtc_flag ) ), - "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ), + : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ), "i" ( RTC_STATUS_C ) ); - hook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr, - &rtc_old_handler ); + hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler ); } /** @@ -88,7 +93,7 @@ static void rtc_hook_isr ( void ) { static void rtc_unhook_isr ( void ) { int rc; - rc = unhook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr, + rc = unhook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler ); assert ( rc == 0 ); /* Should always be able to unhook */ } @@ -100,6 +105,10 @@ static void rtc_unhook_isr ( void ) { static void rtc_enable_int ( void ) { uint8_t status_b; + /* Clear any stale pending interrupts via status register C */ + outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS ); + inb ( CMOS_DATA ); + /* Set Periodic Interrupt Enable bit in status register B */ outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS ); status_b = inb ( CMOS_DATA ); @@ -129,18 +138,60 @@ static void rtc_disable_int ( void ) { inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */ } +/** + * Check that entropy gathering is functional + * + * @ret rc Return status code + */ +static int rtc_entropy_check ( void ) { + unsigned int i; + + /* Check that RTC interrupts are working */ + rtc_flag = 0; + for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) { + + /* Allow interrupts to occur */ + __asm__ __volatile__ ( "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" ); + + /* Check for RTC interrupt flag */ + if ( rtc_flag ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" ); + return -ETIMEDOUT; +} + /** * Enable entropy gathering * * @ret rc Return status code */ static int rtc_entropy_enable ( void ) { + int rc; + /* Hook ISR and enable RTC interrupts */ rtc_hook_isr(); enable_irq ( RTC_IRQ ); rtc_enable_int(); + /* Check that RTC interrupts are working */ + if ( ( rc = rtc_entropy_check() ) != 0 ) + goto err_check; + return 0; + + err_check: + rtc_disable_int(); + disable_irq ( RTC_IRQ ); + rtc_unhook_isr(); + return rc; } /** @@ -149,6 +200,7 @@ static int rtc_entropy_enable ( void ) { */ static void rtc_entropy_disable ( void ) { + /* Disable RTC interrupts and unhook ISR */ rtc_disable_int(); disable_irq ( RTC_IRQ ); rtc_unhook_isr(); @@ -168,9 +220,9 @@ uint8_t rtc_sample ( void ) { REAL_CODE ( /* Enable interrupts */ "sti\n\t" /* Wait for RTC interrupt */ - "cs movb %b2, %c4\n\t" + "movb %b2, %%cs:rtc_flag\n\t" "\n1:\n\t" - "cs xchgb %b2, %c4\n\t" /* Serialize */ + "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */ "testb %b2, %b2\n\t" "jz 1b\n\t" /* Read "before" TSC */ @@ -179,9 +231,9 @@ uint8_t rtc_sample ( void ) { "pushl %0\n\t" /* Wait for another RTC interrupt */ "xorb %b2, %b2\n\t" - "cs movb %b2, %c4\n\t" + "movb %b2, %%cs:rtc_flag\n\t" "\n1:\n\t" - "cs xchgb %b2, %c4\n\t" /* Serialize */ + "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */ "testb %b2, %b2\n\t" "jz 1b\n\t" /* Read "after" TSC */ @@ -191,8 +243,8 @@ uint8_t rtc_sample ( void ) { /* Disable interrupts */ "cli\n\t" ) - : "=a" ( after ), "=d" ( before ), "=q" ( temp ) - : "2" ( 0 ), "p" ( __from_text16 ( &rtc_flag ) ) ); + : "=a" ( after ), "=d" ( before ), "=Q" ( temp ) + : "2" ( 0 ) ); return ( after - before ); } diff --git a/src/arch/i386/interface/pcbios/rtc_time.c b/src/arch/x86/interface/pcbios/rtc_time.c similarity index 100% rename from src/arch/i386/interface/pcbios/rtc_time.c rename to src/arch/x86/interface/pcbios/rtc_time.c diff --git a/src/arch/i386/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c similarity index 100% rename from src/arch/i386/interface/pcbios/vesafb.c rename to src/arch/x86/interface/pcbios/vesafb.c diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/x86/interface/pxe/pxe_call.c similarity index 93% rename from src/arch/i386/interface/pxe/pxe_call.c rename to src/arch/x86/interface/pxe/pxe_call.c index 0b67880c7..671182991 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/x86/interface/pxe/pxe_call.c @@ -27,7 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include +#include #include #include #include @@ -54,6 +54,14 @@ extern void pxe_int_1a ( void ); /** INT 1A hooked flag */ static int int_1a_hooked = 0; +/** Real-mode code segment size */ +extern char _text16_memsz[]; +#define _text16_memsz ( ( size_t ) _text16_memsz ) + +/** Real-mode data segment size */ +extern char _data16_memsz[]; +#define _data16_memsz ( ( size_t ) _data16_memsz ) + /** PXENV_UNDI_TRANSMIT API call profiler */ static struct profiler pxe_api_tx_profiler __profiler = { .name = "pxeapi.tx" }; @@ -272,7 +280,7 @@ void pxe_activate ( struct net_device *netdev ) { /* Ensure INT 1A is hooked */ if ( ! int_1a_hooked ) { - hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, + hook_bios_interrupt ( 0x1a, ( intptr_t ) pxe_int_1a, &pxe_int_1a_vector ); devices_get(); int_1a_hooked = 1; @@ -305,10 +313,10 @@ int pxe_deactivate ( void ) { /* Ensure INT 1A is unhooked, if possible */ if ( int_1a_hooked ) { if ( ( rc = unhook_bios_interrupt ( 0x1a, - (unsigned int) pxe_int_1a, + ( intptr_t ) pxe_int_1a, &pxe_int_1a_vector ))!= 0){ - DBG ( "Could not unhook INT 1A: %s\n", - strerror ( rc ) ); + DBGC ( &pxe_netdev, "PXE could not unhook INT 1A: %s\n", + strerror ( rc ) ); return rc; } devices_put(); @@ -331,10 +339,14 @@ int pxe_start_nbp ( void ) { int discard_b, discard_c, discard_d, discard_D; uint16_t status; + DBGC ( &pxe_netdev, "PXE NBP starting with netdev %s, code %04x:%04zx, " + "data %04x:%04zx\n", ( pxe_netdev ? pxe_netdev->name : ""), + rm_cs, _text16_memsz, rm_ds, _data16_memsz ); + /* Allow restarting NBP via PXENV_RESTART_TFTP */ jmp = rmsetjmp ( pxe_restart_nbp ); if ( jmp ) - DBG ( "Restarting NBP (%x)\n", jmp ); + DBGC ( &pxe_netdev, "PXE NBP restarting (%x)\n", jmp ); /* Far call to PXE NBP */ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ diff --git a/src/arch/i386/interface/pxe/pxe_entry.S b/src/arch/x86/interface/pxe/pxe_entry.S similarity index 98% rename from src/arch/i386/interface/pxe/pxe_entry.S rename to src/arch/x86/interface/pxe/pxe_entry.S index 07852cd50..663aa842e 100644 --- a/src/arch/i386/interface/pxe/pxe_entry.S +++ b/src/arch/x86/interface/pxe/pxe_entry.S @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .arch i386 /**************************************************************************** @@ -120,10 +122,7 @@ pxenv_null_entry: .section ".text16", "ax", @progbits .code16 pxenv_entry: - pushl $pxe_api_call - pushw %cs - call prot_call - addl $4, %esp + virtcall pxe_api_call lret .size pxenv_entry, . - pxenv_entry diff --git a/src/arch/i386/interface/pxe/pxe_exit_hook.c b/src/arch/x86/interface/pxe/pxe_exit_hook.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_exit_hook.c rename to src/arch/x86/interface/pxe/pxe_exit_hook.c diff --git a/src/arch/i386/interface/pxe/pxe_file.c b/src/arch/x86/interface/pxe/pxe_file.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_file.c rename to src/arch/x86/interface/pxe/pxe_file.c diff --git a/src/arch/i386/interface/pxe/pxe_loader.c b/src/arch/x86/interface/pxe/pxe_loader.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_loader.c rename to src/arch/x86/interface/pxe/pxe_loader.c diff --git a/src/arch/i386/interface/pxe/pxe_preboot.c b/src/arch/x86/interface/pxe/pxe_preboot.c similarity index 99% rename from src/arch/i386/interface/pxe/pxe_preboot.c rename to src/arch/x86/interface/pxe/pxe_preboot.c index cc9c052ed..09e721b34 100644 --- a/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/src/arch/x86/interface/pxe/pxe_preboot.c @@ -33,7 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include #include @@ -44,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include "pxe.h" #include "pxe_call.h" diff --git a/src/arch/i386/interface/pxe/pxe_tftp.c b/src/arch/x86/interface/pxe/pxe_tftp.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_tftp.c rename to src/arch/x86/interface/pxe/pxe_tftp.c diff --git a/src/arch/i386/interface/pxe/pxe_udp.c b/src/arch/x86/interface/pxe/pxe_udp.c similarity index 97% rename from src/arch/i386/interface/pxe/pxe_udp.c rename to src/arch/x86/interface/pxe/pxe_udp.c index 071cb59db..5a04f0865 100644 --- a/src/arch/i386/interface/pxe/pxe_udp.c +++ b/src/arch/x86/interface/pxe/pxe_udp.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -180,6 +181,15 @@ static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip; DBG ( " %s\n", inet_ntoa ( pxe_udp.local.sin_addr ) ); + /* Open network device, if necessary */ + if ( pxe_netdev && ( ! netdev_is_open ( pxe_netdev ) ) && + ( ( rc = netdev_open ( pxe_netdev ) ) != 0 ) ) { + DBG ( "PXENV_UDP_OPEN could not (implicitly) open %s: %s\n", + pxe_netdev->name, strerror ( rc ) ); + pxenv_udp_open->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + /* Open promiscuous UDP connection */ intf_restart ( &pxe_udp.xfer, 0 ); if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) { diff --git a/src/arch/i386/interface/pxe/pxe_undi.c b/src/arch/x86/interface/pxe/pxe_undi.c similarity index 100% rename from src/arch/i386/interface/pxe/pxe_undi.c rename to src/arch/x86/interface/pxe/pxe_undi.c diff --git a/src/arch/i386/interface/syslinux/com32_call.c b/src/arch/x86/interface/syslinux/com32_call.c similarity index 91% rename from src/arch/i386/interface/syslinux/com32_call.c rename to src/arch/x86/interface/syslinux/com32_call.c index 75dcc238f..19fdbaff9 100644 --- a/src/arch/i386/interface/syslinux/com32_call.c +++ b/src/arch/x86/interface/syslinux/com32_call.c @@ -46,6 +46,9 @@ uint16_t __bss16 ( com32_saved_sp ); */ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + DBGC ( &com32_regs, "COM32 INT%x in %#08lx out %#08lx\n", + interrupt, inregs_phys, outregs_phys ); + memcpy_user ( virt_to_user( &com32_regs ), 0, phys_to_user ( inregs_phys ), 0, sizeof(com32sys_t) ); @@ -76,7 +79,7 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad /* patch INT instruction */ "pushw %%ax\n\t" "movb %%ss:(com32_int_vector), %%al\n\t" - "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" + "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" /* perform a jump to avoid problems with cache * consistency in self-modifying code on some CPUs (486) */ @@ -106,7 +109,7 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad if ( outregs_phys ) { memcpy_user ( phys_to_user ( outregs_phys ), 0, - virt_to_user( &com32_regs ), 0, + virt_to_user( &com32_regs ), 0, sizeof(com32sys_t) ); } } @@ -116,6 +119,9 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad */ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + DBGC ( &com32_regs, "COM32 farcall %04x:%04x in %#08lx out %#08lx\n", + ( proc >> 16 ), ( proc & 0xffff ), inregs_phys, outregs_phys ); + memcpy_user ( virt_to_user( &com32_regs ), 0, phys_to_user ( inregs_phys ), 0, sizeof(com32sys_t) ); @@ -165,7 +171,7 @@ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t if ( outregs_phys ) { memcpy_user ( phys_to_user ( outregs_phys ), 0, - virt_to_user( &com32_regs ), 0, + virt_to_user( &com32_regs ), 0, sizeof(com32sys_t) ); } } @@ -176,13 +182,16 @@ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz ) { int32_t eax; + DBGC ( &com32_regs, "COM32 cfarcall %04x:%04x params %#08lx+%#zx\n", + ( proc >> 16 ), ( proc & 0xffff ), stack, stacksz ); + copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz ); com32_farcall_proc = proc; __asm__ __volatile__ ( REAL_CODE ( "lcall *%%ss:(com32_farcall_proc)\n\t" ) : "=a" (eax) - : + : : "ecx", "edx" ); remove_user_from_rm_stack ( 0, stacksz ); diff --git a/src/arch/i386/interface/syslinux/com32_wrapper.S b/src/arch/x86/interface/syslinux/com32_wrapper.S similarity index 51% rename from src/arch/i386/interface/syslinux/com32_wrapper.S rename to src/arch/x86/interface/syslinux/com32_wrapper.S index c9d1452b4..d59a3392c 100644 --- a/src/arch/i386/interface/syslinux/com32_wrapper.S +++ b/src/arch/x86/interface/syslinux/com32_wrapper.S @@ -19,79 +19,82 @@ FILE_LICENCE ( GPL2_OR_LATER ) - .text - .arch i386 - .code32 +#include "librm.h" + .text + + .code32 .globl com32_farcall_wrapper com32_farcall_wrapper: + movl $VIRTUAL(com32_farcall), %eax + jmp com32_wrapper - movl $com32_farcall, %eax - jmp com32_wrapper - - + .code32 .globl com32_cfarcall_wrapper com32_cfarcall_wrapper: + movl $VIRTUAL(com32_cfarcall), %eax + jmp com32_wrapper - movl $com32_cfarcall, %eax - jmp com32_wrapper - - + .code32 .globl com32_intcall_wrapper com32_intcall_wrapper: + movl $VIRTUAL(com32_intcall), %eax + /* fall through */ - movl $com32_intcall, %eax - /*jmp com32_wrapper*/ /* fall through */ - + .code32 com32_wrapper: + + /* Disable interrupts */ cli /* Switch to internal virtual address space */ - call _phys_to_virt + call _phys_to_virt - mov %eax, (com32_helper_function) +#ifdef __x86_64__ - /* Save external COM32 stack pointer */ - movl %esp, (com32_external_esp) + .code64 - /* Copy arguments to caller-save registers */ - movl 12(%esp), %eax - movl 8(%esp), %ecx - movl 4(%esp), %edx + /* Preserve registers which are callee-save for COM32 (i386 API) */ + pushq %rdi + pushq %rsi + pushq %rbp - /* Switch to internal stack */ - movl (com32_internal_esp), %esp + /* Extract parameters from stack */ + movl 28(%rsp), %edi + movl 32(%rsp), %esi + movl 36(%rsp), %edx - /* Copy arguments to internal stack */ - pushl %eax - pushl %ecx - pushl %edx + /* Align stack pointer */ + movq %rsp, %rbp + andq $~0x07, %rsp - call *(com32_helper_function) + /* Call helper function */ + movslq %eax, %rax + call *%rax - /* Clean up stack */ - addl $12, %esp + /* Restore stack pointer */ + movq %rbp, %rsp - /* Save internal stack pointer and restore external stack pointer */ - movl %esp, (com32_internal_esp) - movl (com32_external_esp), %esp + /* Restore registers */ + popq %rbp + popq %rsi + popq %rdi + +#else /* _x86_64 */ + + /* Call helper function */ + pushl 12(%esp) + pushl 12(%esp) + pushl 12(%esp) + call *%eax + addl $12, %esp + +#endif /* _x86_64 */ /* Switch to external flat physical address space */ - call _virt_to_phys + call _virt_to_phys + .code32 + /* Reenable interrupts and return */ sti ret - - - .data - -/* Internal iPXE virtual address space %esp */ -.globl com32_internal_esp -.lcomm com32_internal_esp, 4 - -/* External flat physical address space %esp */ -.globl com32_external_esp -.lcomm com32_external_esp, 4 - -/* Function pointer of helper to call */ -.lcomm com32_helper_function, 4 diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/x86/interface/syslinux/comboot_call.c similarity index 93% rename from src/arch/i386/interface/syslinux/comboot_call.c rename to src/arch/x86/interface/syslinux/comboot_call.c index 69d94c407..e70f200e3 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/x86/interface/syslinux/comboot_call.c @@ -32,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include +#include #include #include #include @@ -489,7 +489,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { struct in_addr addr; copy_from_user ( hostname, hostname_u, 0, len + 1 ); - + /* TODO: * "If the hostname does not contain a dot (.), the * local domain name is automatically appended." @@ -519,7 +519,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { /* Jump to real-mode entry point */ __asm__ __volatile__ ( - REAL_CODE ( + REAL_CODE ( "pushw %0\n\t" "popw %%ds\n\t" "pushl %1\n\t" @@ -660,42 +660,30 @@ void hook_comboot_interrupts ( ) { __asm__ __volatile__ ( TEXT16_CODE ( "\nint20_wrapper:\n\t" - "pushl %0\n\t" - "pushw %%cs\n\t" - "call prot_call\n\t" - "addw $4, %%sp\n\t" + VIRT_CALL ( int20 ) + "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) - : : "i" ( int20 ) ); + "iret\n\t" ) : ); - hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, - &int20_vector ); + hook_bios_interrupt ( 0x20, ( intptr_t ) int20_wrapper, &int20_vector ); __asm__ __volatile__ ( TEXT16_CODE ( "\nint21_wrapper:\n\t" - "pushl %0\n\t" - "pushw %%cs\n\t" - "call prot_call\n\t" - "addw $4, %%sp\n\t" + VIRT_CALL ( int21 ) + "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) - : : "i" ( int21 ) ); + "iret\n\t" ) : ); - hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, - &int21_vector ); + hook_bios_interrupt ( 0x21, ( intptr_t ) int21_wrapper, &int21_vector ); __asm__ __volatile__ ( TEXT16_CODE ( "\nint22_wrapper:\n\t" - "pushl %0\n\t" - "pushw %%cs\n\t" - "call prot_call\n\t" - "addw $4, %%sp\n\t" + VIRT_CALL ( int22 ) + "clc\n\t" "call patch_cf\n\t" - "iret\n\t" ) - : : "i" ( int22) ); + "iret\n\t" ) : ); - hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, - &int22_vector ); + hook_bios_interrupt ( 0x22, ( intptr_t ) int22_wrapper, &int22_vector ); } /** @@ -703,13 +691,13 @@ void hook_comboot_interrupts ( ) { */ void unhook_comboot_interrupts ( ) { - unhook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, + unhook_bios_interrupt ( 0x20, ( intptr_t ) int20_wrapper, &int20_vector ); - unhook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, + unhook_bios_interrupt ( 0x21, ( intptr_t ) int21_wrapper, &int21_vector ); - unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, + unhook_bios_interrupt ( 0x22, ( intptr_t ) int22_wrapper, &int22_vector ); } diff --git a/src/arch/i386/interface/syslinux/comboot_resolv.c b/src/arch/x86/interface/syslinux/comboot_resolv.c similarity index 100% rename from src/arch/i386/interface/syslinux/comboot_resolv.c rename to src/arch/x86/interface/syslinux/comboot_resolv.c diff --git a/src/arch/i386/interface/vmware/guestinfo.c b/src/arch/x86/interface/vmware/guestinfo.c similarity index 100% rename from src/arch/i386/interface/vmware/guestinfo.c rename to src/arch/x86/interface/vmware/guestinfo.c diff --git a/src/arch/i386/interface/vmware/guestrpc.c b/src/arch/x86/interface/vmware/guestrpc.c similarity index 100% rename from src/arch/i386/interface/vmware/guestrpc.c rename to src/arch/x86/interface/vmware/guestrpc.c diff --git a/src/arch/i386/interface/vmware/vmconsole.c b/src/arch/x86/interface/vmware/vmconsole.c similarity index 100% rename from src/arch/i386/interface/vmware/vmconsole.c rename to src/arch/x86/interface/vmware/vmconsole.c diff --git a/src/arch/i386/interface/vmware/vmware.c b/src/arch/x86/interface/vmware/vmware.c similarity index 100% rename from src/arch/i386/interface/vmware/vmware.c rename to src/arch/x86/interface/vmware/vmware.c diff --git a/src/arch/i386/prefix/bootpart.S b/src/arch/x86/prefix/bootpart.S similarity index 100% rename from src/arch/i386/prefix/bootpart.S rename to src/arch/x86/prefix/bootpart.S diff --git a/src/arch/i386/prefix/dskprefix.S b/src/arch/x86/prefix/dskprefix.S similarity index 99% rename from src/arch/i386/prefix/dskprefix.S rename to src/arch/x86/prefix/dskprefix.S index 7aa017ccd..0503f113d 100644 --- a/src/arch/i386/prefix/dskprefix.S +++ b/src/arch/x86/prefix/dskprefix.S @@ -18,6 +18,8 @@ FILE_LICENCE ( GPL2_ONLY ) +#include + .equ BOOTSEG, 0x07C0 /* original address of boot-sector */ .equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ @@ -370,10 +372,8 @@ start_runtime: lret .section ".text16", "awx", @progbits 1: - pushl $main - pushw %cs - call prot_call - popl %ecx /* discard */ + /* Run iPXE */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/i386/prefix/exeprefix.S b/src/arch/x86/prefix/exeprefix.S similarity index 98% rename from src/arch/i386/prefix/exeprefix.S rename to src/arch/x86/prefix/exeprefix.S index 5c648d51d..c351456e2 100644 --- a/src/arch/i386/prefix/exeprefix.S +++ b/src/arch/x86/prefix/exeprefix.S @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + /* Initial temporary stack size */ #define EXE_STACK_SIZE 0x400 @@ -148,10 +150,7 @@ _exe_start: movl %esi, cmdline_phys /* Run iPXE */ - pushl $main - pushw %cs - call prot_call - popl %ecx /* discard */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/i386/prefix/hdprefix.S b/src/arch/x86/prefix/hdprefix.S similarity index 87% rename from src/arch/i386/prefix/hdprefix.S rename to src/arch/x86/prefix/hdprefix.S index 1d012d80b..28c8a532d 100644 --- a/src/arch/i386/prefix/hdprefix.S +++ b/src/arch/x86/prefix/hdprefix.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .text .arch i386 .section ".prefix", "awx", @progbits @@ -25,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 */ @@ -51,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 @@ -99,10 +105,8 @@ start_image: lret .section ".text16", "awx", @progbits 1: - pushl $main - pushw %cs - call prot_call - popl %ecx /* discard */ + /* Run iPXE */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/i386/prefix/isaromprefix.S b/src/arch/x86/prefix/isaromprefix.S similarity index 100% rename from src/arch/i386/prefix/isaromprefix.S rename to src/arch/x86/prefix/isaromprefix.S diff --git a/src/arch/i386/prefix/kkkpxeprefix.S b/src/arch/x86/prefix/kkkpxeprefix.S similarity index 100% rename from src/arch/i386/prefix/kkkpxeprefix.S rename to src/arch/x86/prefix/kkkpxeprefix.S diff --git a/src/arch/i386/prefix/kkpxeprefix.S b/src/arch/x86/prefix/kkpxeprefix.S similarity index 100% rename from src/arch/i386/prefix/kkpxeprefix.S rename to src/arch/x86/prefix/kkpxeprefix.S diff --git a/src/arch/i386/prefix/kpxeprefix.S b/src/arch/x86/prefix/kpxeprefix.S similarity index 100% rename from src/arch/i386/prefix/kpxeprefix.S rename to src/arch/x86/prefix/kpxeprefix.S diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S similarity index 84% rename from src/arch/i386/prefix/libprefix.S rename to src/arch/x86/prefix/libprefix.S index 7d5c1ed53..ffb211058 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .arch i386 /* Image compression enabled */ @@ -34,10 +36,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Allow for DBG()-style messages within libprefix */ #ifdef NDEBUG - .macro progress message + .macro progress message, regs:vararg .endm #else - .macro progress message + .macro dumpreg reg, others:vararg + pushl %eax + movl \reg, %eax + pushw %di + xorw %di, %di + call print_space + call print_hex_dword + popw %di + popl %eax + .ifnb \others + dumpreg \others + .endif + .endm + + .macro progress message, regs:vararg pushfl pushw %ds pushw %si @@ -49,6 +65,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) call print_message popw %di popw %si + .ifnb \regs + dumpreg \regs + .endif + pushw %di + pushw %ax + xorw %di, %di + movb $( '\n' ), %al + call print_character + popw %ax + popw %di popw %ds popfl .section ".prefix.data", "aw", @progbits @@ -69,7 +95,7 @@ progress_\@: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_character", "awx", @progbits .code16 .globl print_character print_character: @@ -107,7 +133,7 @@ print_character: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_space", "awx", @progbits .code16 .globl print_space print_space: @@ -132,7 +158,7 @@ print_space: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_message", "awx", @progbits .code16 .globl print_message print_message: @@ -162,7 +188,7 @@ print_message: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_hex", "awx", @progbits .code16 .globl print_hex_dword print_hex_dword: @@ -210,7 +236,7 @@ print_hex_nibble: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_pci_busdevfn", "awx", @progbits .code16 .globl print_pci_busdevfn print_pci_busdevfn: @@ -247,7 +273,7 @@ print_pci_busdevfn: * %ds:di : next character in output buffer (if applicable) ***************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.print_kill_line", "awx", @progbits .code16 .globl print_kill_line print_kill_line: @@ -285,7 +311,7 @@ print_kill_line: * None **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.copy_bytes", "awx", @progbits .code16 copy_bytes: pushl %ecx @@ -308,7 +334,7 @@ copy_bytes: * None **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.zero_bytes", "awx", @progbits .code16 zero_bytes: pushl %ecx @@ -339,11 +365,12 @@ zero_bytes: * Returns: * %esi : next source physical address * %edi : next destination physical address + * CF : as returned by memcpy()-like function * Corrupts: * None **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.process_bytes", "awx", @progbits .code16 process_bytes: @@ -354,6 +381,7 @@ process_bytes: pushl %ebp /* Construct GDT on stack (since .prefix may not be writable) */ + .equ GDT_LEN, 0x20 .equ PM_DS, 0x18 /* Flat data segment */ pushl $0x00cf9300 pushl $0x0000ffff @@ -367,7 +395,7 @@ process_bytes: pushw $0xffff pushl $0 /* Base and length */ pushw %ss - pushw $0x1f + pushw $( GDT_LEN - 1 ) movzwl %sp, %ebp shll $4, 0x02(%bp) addl %ebp, 0x02(%bp) @@ -405,7 +433,9 @@ process_bytes: /* Return to (flat) real mode */ movl %cr0, %eax + pushfw andb $0!CR0_PE, %al + popfw movl %eax, %cr0 lret 2: /* lret will ljmp to here */ @@ -431,7 +461,7 @@ process_bytes: /* Restore GDT */ data32 lgdt -8(%bp) - addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp + leaw GDT_LEN(%bp), %sp /* Restore registers and return */ popl %ebp @@ -459,6 +489,7 @@ process_bytes: call *%bx /* Convert %ds:esi and %es:edi back to physical addresses */ + pushfw xorl %eax, %eax movw %ds, %ax shll $4, %eax @@ -467,6 +498,7 @@ process_bytes: movw %es, %ax shll $4, %eax addl %eax, %edi + popfw /* Restore registers and return */ popw %es @@ -491,11 +523,12 @@ process_bytes: * Returns: * %esi : next source physical address (will be a multiple of 16) * %edi : next destination physical address (will be a multiple of 16) + * CF set on failure * Corrupts: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.install_block", "awx", @progbits .code16 install_block: /* Preserve registers */ @@ -509,6 +542,7 @@ install_block: movw $copy_bytes, %bx #endif call process_bytes + jc 99f /* Zero .bss portion */ negl %ecx @@ -520,9 +554,9 @@ install_block: addl $0xf, %esi andl $~0xf, %esi addl $0xf, %edi - andl $~0xf, %edi + andl $~0xf, %edi /* Will also clear CF */ - /* Restore registers and return */ +99: /* Restore registers and return */ popw %bx popl %ecx ret @@ -544,7 +578,7 @@ install_block: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.alloc_basemem", "awx", @progbits .code16 .globl alloc_basemem alloc_basemem: @@ -558,14 +592,11 @@ alloc_basemem: shlw $6, %ax /* Calculate .data16 segment address */ - subw $_data16_memsz_pgh, %ax + subw $_data16_memsz_ppgh, %ax pushw %ax - /* Calculate .text16 segment address. Round down to ensure - * low bits are zero, to speed up mode transitions under KVM. - */ - subw $_text16_memsz_pgh, %ax - andb $~0x03, %al + /* Calculate .text16 segment address */ + subw $_text16_memsz_ppgh, %ax pushw %ax /* Update FBMS */ @@ -594,7 +625,7 @@ alloc_basemem: * none **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.free_basemem", "ax", @progbits .code16 .globl free_basemem free_basemem: @@ -616,8 +647,8 @@ free_basemem: /* OK to free memory */ movw %cs, %ax - addw $_text16_memsz_pgh, %ax - addw $_data16_memsz_pgh, %ax + addw $_text16_memsz_ppgh, %ax + addw $_data16_memsz_ppgh, %ax shrw $6, %ax movw %ax, %fs:0x13 xorw %ax, %ax @@ -628,7 +659,7 @@ free_basemem: ret .size free_basemem, . - free_basemem - .section ".text16.data", "aw", @progbits + .section ".text16.data.hooked_bios_interrupts", "aw", @progbits .globl hooked_bios_interrupts hooked_bios_interrupts: .word 0 @@ -648,11 +679,11 @@ hooked_bios_interrupts: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.install", "awx", @progbits .code16 .globl install install: - progress "install:\n" + progress "\ninstall:" /* Preserve registers */ pushl %esi pushl %edi @@ -691,11 +722,11 @@ install: * none **************************************************************************** */ - .section ".prefix.lib", "awx", @progbits + .section ".prefix.install_prealloc", "awx", @progbits .code16 .globl install_prealloc install_prealloc: - progress "install_prealloc:\n" + progress "\ninstall_prealloc:", %eax, %ebx, %esi, %edi, %ebp /* Save registers on external stack */ pushal pushw %ds @@ -719,7 +750,6 @@ install_prealloc: pushl %edi /* Install .text16.early and calculate %ecx as offset to next block */ - progress " .text16.early\n" pushl %esi xorl %esi, %esi movw %cs, %si @@ -730,7 +760,9 @@ install_prealloc: shll $4, %edi movl $_text16_early_filesz, %ecx movl $_text16_early_memsz, %edx + progress " .text16.early ", %esi, %edi, %ecx, %edx call install_block /* .text16.early */ + jc install_block_death popl %ecx /* Calculate offset to next block */ subl %esi, %ecx negl %ecx @@ -742,53 +774,26 @@ install_prealloc: * already have 4GB segment limits as a result of calling * install_block.) */ - progress " access_highmem\n" + progress " access_highmem" pushw %cs pushw $1f pushw %ax pushw $access_highmem lret 1: /* Die if we could not access high memory */ - jnc 3f - movw $a20_death_message, %si - xorw %di, %di - call print_message -2: jmp 2b - .section ".prefix.data", "aw", @progbits -a20_death_message: - .asciz "\nHigh memory inaccessible - cannot continue\n" - .size a20_death_message, . - a20_death_message - .previous -3: + jc access_highmem_death + #endif /* Open payload (which may not yet be in memory) */ - progress " open_payload\n" + progress " open_payload ", %esi, %ecx pushw %cs pushw $1f pushw %ax pushw $open_payload lret 1: /* Die if we could not access the payload */ - jnc 3f - xorw %di, %di - movl %esi, %eax - call print_hex_dword - call print_space - movl %ecx, %eax - call print_hex_dword - movw $payload_death_message, %si - call print_message -2: /* Halt system */ - cli - hlt - jmp 2b - .section ".prefix.data", "aw", @progbits -payload_death_message: - .asciz "\nPayload inaccessible - cannot continue\n" - .size payload_death_message, . - payload_death_message - .previous -3: + jc open_payload_death /* Calculate physical address of payload (i.e. first source) */ testl %esi, %esi @@ -798,16 +803,18 @@ payload_death_message: 1: addl %ecx, %esi /* Install .text16.late and .data16 */ - progress " .text16.late\n" movl $_text16_late_filesz, %ecx movl $_text16_late_memsz, %edx + progress " .text16.late ", %esi, %edi, %ecx, %edx call install_block /* .text16.late */ - progress " .data16\n" + jc install_block_death movzwl %bx, %edi shll $4, %edi movl $_data16_filesz, %ecx movl $_data16_filesz, %edx /* do not zero our temporary stack */ + progress " .data16 ", %esi, %edi, %ecx, %edx call install_block /* .data16 */ + jc install_block_death /* Set up %ds for access to .data16 */ movw %bx, %ds @@ -830,6 +837,7 @@ payload_death_message: movw %ax, %di addl $0x400, %edi subl $_textdata_memsz_kb, %edi + andw $~0x03, %di shll $10, %edi /* Sanity check: if we have ended up below 1MB, use 1MB */ cmpl $0x100000, %edi @@ -841,11 +849,12 @@ payload_death_message: * prior to reading the E820 memory map and relocating * properly. */ - progress " .textdata\n" pushl %edi movl $_textdata_filesz, %ecx movl $_textdata_memsz, %edx + progress " .textdata ", %esi, %edi, %ecx, %edx call install_block + jc install_block_death popl %edi #endif /* KEEP_IT_REAL */ @@ -865,10 +874,19 @@ payload_death_message: #ifndef KEEP_IT_REAL /* Initialise librm at current location */ - progress " init_librm\n" + progress " init_librm ", %eax, %ebx, %edi movw %ax, (init_librm_vector+2) lcall *init_librm_vector + /* Prepare for return to .prefix segment */ + pushw %cs + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16.install_prealloc", "ax", @progbits +1: /* Inhibit INT 15,e820 and INT 15,e801 if applicable */ testl %ebp, %ebp jnz 1f @@ -879,14 +897,15 @@ payload_death_message: * relocate() will return with %esi, %edi and %ecx set up * ready for the copy to the new location. */ - progress " relocate\n" - movw %ax, (prot_call_vector+2) - pushl $relocate - lcall *prot_call_vector - popl %edx /* discard */ + virtcall relocate + /* Jump back to .prefix segment */ + pushw $1f + lret + .section ".prefix.install_prealloc", "awx", @progbits +1: /* Copy code to new location */ - progress " copy\n" + progress " copy ", %esi, %edi, %ecx pushl %edi pushw %bx movw $copy_bytes, %bx @@ -895,7 +914,7 @@ payload_death_message: popl %edi /* Initialise librm at new location */ - progress " init_librm\n" + progress " init_librm ", %eax, %ebx, %edi lcall *init_librm_vector #else /* KEEP_IT_REAL */ @@ -907,7 +926,7 @@ payload_death_message: #endif /* KEEP_IT_REAL */ /* Close access to payload */ - progress " close_payload\n" + progress " close_payload" movw %ax, (close_payload_vector+2) lcall *close_payload_vector @@ -921,7 +940,7 @@ payload_death_message: /* Vectors for far calls to .text16 functions. Must be in * .data16, since .prefix may not be writable. */ - .section ".data16", "aw", @progbits + .section ".data16.install_prealloc", "aw", @progbits #ifdef KEEP_IT_REAL init_libkir_vector: .word init_libkir @@ -932,10 +951,6 @@ init_librm_vector: .word init_librm .word 0 .size init_librm_vector, . - init_librm_vector -prot_call_vector: - .word prot_call - .word 0 - .size prot_call_vector, . - prot_call_vector #endif close_payload_vector: .word close_payload @@ -943,7 +958,7 @@ close_payload_vector: .size close_payload_vector, . - close_payload_vector /* Dummy routines to open and close payload */ - .section ".text16.early.data", "aw", @progbits + .section ".text16.early.data.open_payload", "aw", @progbits .weak open_payload .weak close_payload open_payload: @@ -953,6 +968,52 @@ close_payload: .size open_payload, . - open_payload .size close_payload, . - close_payload + /* Report installation failure */ + .section ".prefix.install_death", "ax", @progbits +install_death: + pushw %cs + popw %ds + xorw %di, %di + call print_hex_dword + call print_space + movl %esi, %eax + call print_hex_dword + call print_space + movl %ecx, %eax + call print_hex_dword + movw $install_death_message, %si + call print_message +2: /* Halt system */ + cli + hlt + jmp 2b + .size install_death, . - install_death + .section ".prefix.data.install_death_message", "aw", @progbits +install_death_message: + .asciz "\nInstallation failed - cannot continue\n" + .size install_death_message, . - install_death_message + + /* Report failure to access high memory */ + .section ".prefix.install_block_death", "ax", @progbits +install_block_death: + movl $0x1b101b10, %eax + jmp install_death + .size install_block_death, . - install_block_death + + /* Report failure to access high memory */ + .section ".prefix.access_highmem_death", "ax", @progbits +access_highmem_death: + movl $0x0a200a20, %eax + jmp install_death + .size access_highmem_death, . - access_highmem_death + + /* Report failure to open payload */ + .section ".prefix.open_payload_death", "ax", @progbits +open_payload_death: + xorl %eax, %eax + jmp install_death + .size open_payload_death, . - open_payload_death + /**************************************************************************** * uninstall * @@ -966,7 +1027,7 @@ close_payload: * none **************************************************************************** */ - .section ".text16", "ax", @progbits + .section ".text16.uninstall", "ax", @progbits .code16 .globl uninstall uninstall: diff --git a/src/arch/i386/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S similarity index 98% rename from src/arch/i386/prefix/lkrnprefix.S rename to src/arch/x86/prefix/lkrnprefix.S index 64135e14b..922181f0e 100644 --- a/src/arch/i386/prefix/lkrnprefix.S +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + #define BZI_LOAD_HIGH_ADDR 0x100000 .text @@ -197,10 +199,7 @@ no_cmd_line: movl %ecx, initrd_len /* Run iPXE */ - pushl $main - pushw %cs - call prot_call - popl %ecx /* discard */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/i386/prefix/mbr.S b/src/arch/x86/prefix/mbr.S similarity index 95% rename from src/arch/i386/prefix/mbr.S rename to src/arch/x86/prefix/mbr.S index a1e237de8..032c0e775 100644 --- a/src/arch/i386/prefix/mbr.S +++ b/src/arch/x86/prefix/mbr.S @@ -6,6 +6,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .code16 .org 0 + .globl mbr mbr: movw $exec_sector, %bp jmp find_active_partition diff --git a/src/arch/i386/prefix/mromprefix.S b/src/arch/x86/prefix/mromprefix.S similarity index 96% rename from src/arch/i386/prefix/mromprefix.S rename to src/arch/x86/prefix/mromprefix.S index b636b92af..73a869d90 100644 --- a/src/arch/i386/prefix/mromprefix.S +++ b/src/arch/x86/prefix/mromprefix.S @@ -456,6 +456,24 @@ pci_set_mem_access: ret .size pci_set_mem_access, . - pci_set_mem_access +/* Update image source address for UNDI loader + * + * Parameters: + * %esi : Image source address + * Returns: + * %esi : Image source address + */ + .section ".prefix", "ax", @progbits + .globl undiloader_source +undiloader_source: + /* Always use expansion ROM BAR directly when installing via + * the UNDI loader entry point, since the PMM-allocated block + * may collide with whatever is calling the UNDI loader entry + * point. + */ + xorl %esi, %esi + ret + /* Payload prefix * * We include a dummy ROM header to cover the "hidden" portion of the @@ -474,6 +492,7 @@ mromheader: .word 0 .size mromheader, . - mromheader + .align 4 mpciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ diff --git a/src/arch/i386/prefix/nbiprefix.S b/src/arch/x86/prefix/nbiprefix.S similarity index 96% rename from src/arch/i386/prefix/nbiprefix.S rename to src/arch/x86/prefix/nbiprefix.S index 16c79566c..de38e4af6 100644 --- a/src/arch/i386/prefix/nbiprefix.S +++ b/src/arch/x86/prefix/nbiprefix.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .text .arch i386 .code16 @@ -66,10 +68,8 @@ _nbi_start: lret .section ".text16", "awx", @progbits 1: - pushl $main - pushw %cs - call prot_call - popl %ecx /* discard */ + /* Run iPXE */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/i386/prefix/nullprefix.S b/src/arch/x86/prefix/nullprefix.S similarity index 100% rename from src/arch/i386/prefix/nullprefix.S rename to src/arch/x86/prefix/nullprefix.S diff --git a/src/arch/i386/prefix/pciromprefix.S b/src/arch/x86/prefix/pciromprefix.S similarity index 100% rename from src/arch/i386/prefix/pciromprefix.S rename to src/arch/x86/prefix/pciromprefix.S diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S similarity index 99% rename from src/arch/i386/prefix/pxeprefix.S rename to src/arch/x86/prefix/pxeprefix.S index 465ce4345..52ea18039 100644 --- a/src/arch/i386/prefix/pxeprefix.S +++ b/src/arch/x86/prefix/pxeprefix.S @@ -16,6 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .org 0 .code16 +#include #include #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) @@ -820,10 +821,7 @@ run_ipxe: movl %ecx, cached_dhcpack_phys /* Run main program */ - pushl $main - pushw %cs - call prot_call - popl %ecx /* discard */ + virtcall main /* Uninstall iPXE */ call uninstall diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S similarity index 95% rename from src/arch/i386/prefix/romprefix.S rename to src/arch/x86/prefix/romprefix.S index 18dda2b37..978b07b57 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -8,6 +8,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include #include #include @@ -87,6 +88,7 @@ checksum: .previous .ifeqs BUSTYPE, "PCIR" + .align 4 pciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ @@ -123,7 +125,7 @@ pci_devlist_end: .long pciheader_image_length .long 512 .long 0 - .ascii ZINFO_TYPE_ADxW + .ascii "ADHW" .long pciheader_runtime_length .long 512 .long 0 @@ -182,6 +184,7 @@ prodstr_pci_id: .globl undiheader .weak undiloader + .align 4 undiheader: .ascii "UNDI" /* Signature */ .byte undiheader_len /* Length of structure */ @@ -196,6 +199,7 @@ undiheader: .equ undiheader_len, . - undiheader .size undiheader, . - undiheader + .align 4 ipxeheader: .ascii "iPXE" /* Signature */ .byte ipxeheader_len /* Length of structure */ @@ -402,19 +406,22 @@ pmm_scan: /* Shrink ROM */ movb shrunk_rom_size, %al movb %al, romheader_size -1: /* Allocate decompression PMM block. Round up the size to the - * nearest 128kB and use the size within the PMM handle; this - * allows the same decompression area to be shared between - * multiple iPXE ROMs even with differing build IDs +1: /* Allocate decompression PMM block. Allow 4kB for page + * alignment and round up the size to the nearest 128kB, then + * use the size within the PMM handle; this allows the same + * decompression area to be shared between multiple iPXE ROMs + * even with differing build IDs */ movl $_textdata_memsz_pgh, %ecx - addl $0x00001fff, %ecx - andl $0xffffe000, %ecx + addl $( 0x00000100 /* 4kB */ + 0x00001fff /* 128kB - 1 */ ), %ecx + andl $( 0xffffe000 /* ~( 128kB - 1 ) */ ), %ecx movl %ecx, %ebx shrw $12, %bx orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx movw $get_pmm_decompress_to, %bp call get_pmm + addl $( 0x00000fff /* 4kB - 1 */ ), %esi + andl $( 0xfffff000 /* ~( 4kB - 1 ) */ ), %esi movl %esi, decompress_to /* Restore registers */ popal @@ -435,13 +442,25 @@ no_pmm: * memory. Will be a no-op for lower PCI versions. */ .ifeqs BUSTYPE, "PCIR" + /* Get runtime segment address and length */ + movw %gs, %ax + movw %ax, %es + movzbw romheader_size, %cx + /* Print runtime segment address */ xorw %di, %di call print_space - movw %gs, %ax call print_hex_word - movzbw romheader_size, %cx + /* Fail if we have insufficient space in final location */ + movw %cs, %si + cmpw %si, %ax + je 1f + cmpw pciheader_runtime_length, %cx + jbe 1f + movb $( '!' ), %al + call print_character + xorw %cx, %cx +1: /* Copy to final location */ shlw $9, %cx - movw %ax, %es xorw %si, %si xorw %di, %di cs rep movsb @@ -791,11 +810,8 @@ exec: /* Set %ds = %cs */ #endif /* AUTOBOOT_ROM_FILTER */ .endif - /* Call main() */ - pushl $main - pushw %cs - call prot_call - popl %eax /* discard */ + /* Run iPXE */ + virtcall main /* Set up flat real mode for return to BIOS */ call flatten_real_mode diff --git a/src/arch/i386/prefix/undiloader.S b/src/arch/x86/prefix/undiloader.S similarity index 64% rename from src/arch/i386/prefix/undiloader.S rename to src/arch/x86/prefix/undiloader.S index 5cace44b7..1d77110e7 100644 --- a/src/arch/i386/prefix/undiloader.S +++ b/src/arch/x86/prefix/undiloader.S @@ -1,5 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include + .text .code16 .arch i386 @@ -18,32 +20,37 @@ undiloader: pushw %ds pushw %es pushw %bx + /* ROM segment address to %ds */ pushw %cs popw %ds + /* UNDI loader parameter structure address into %es:%di */ movw %sp, %bx movw %ss:22(%bx), %di movw %ss:24(%bx), %es + /* Install to specified real-mode addresses */ pushw %di movw %es:12(%di), %bx movw %es:14(%di), %ax movl image_source, %esi - movl decompress_to, %edi + call undiloader_source + xorl %edi, %edi orl $0xffffffff, %ebp /* Allow arbitrary relocation */ call install_prealloc popw %di - /* Call UNDI loader C code */ - pushl $pxe_loader_call - pushw %cs - pushw $1f + + /* Jump to .text16 segment */ pushw %ax - pushw $prot_call + pushw $1f lret -1: popw %bx /* discard */ - popw %bx /* discard */ - /* Restore registers and return */ + .section ".text16", "ax", @progbits +1: + /* Call UNDI loader C code */ + virtcall pxe_loader_call + +1: /* Restore registers and return */ popw %bx popw %es popw %ds @@ -51,4 +58,16 @@ undiloader: popl %edi popl %esi lret - .size undiloader, . - undiloader + +/* Update image source address for UNDI loader + * + * Parameters: + * %esi : Image source address + * Returns: + * %esi : Image source address + */ + .section ".prefix", "ax", @progbits + .globl undiloader_source + .weak undiloader_source +undiloader_source: + ret diff --git a/src/arch/i386/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S similarity index 95% rename from src/arch/i386/prefix/unlzma.S rename to src/arch/x86/prefix/unlzma.S index 8d4b3c1a8..ce18c756f 100644 --- a/src/arch/i386/prefix/unlzma.S +++ b/src/arch/x86/prefix/unlzma.S @@ -58,6 +58,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); .code32 #endif /* CODE16 */ +#define CRCPOLY 0xedb88320 +#define CRCSEED 0xffffffff + /**************************************************************************** * Debugging **************************************************************************** @@ -862,6 +865,44 @@ bcj_filter: ret .size bcj_filter, . - bcj_filter +/**************************************************************************** + * Verify CRC32 + * + * Parameters: + * %ds:%esi : Start of compressed input data + * %edx : Length of compressed input data (including CRC) + * Returns: + * CF clear if CRC32 is zero + * All other registers are preserved + * Corrupts: + * %eax + * %ebx + * %ecx + * %edx + * %esi + **************************************************************************** + */ +verify_crc32: + /* Calculate CRC */ + addl %esi, %edx + movl $CRCSEED, %ebx +1: ADDR32 lodsb + xorb %al, %bl + movw $8, %cx +2: rcrl %ebx + jnc 3f + xorl $CRCPOLY, %ebx +3: ADDR16 loop 2b + cmpl %esi, %edx + jne 1b + /* Set CF if result is nonzero */ + testl %ebx, %ebx + jz 1f + stc +1: /* Return */ + ret + .size verify_crc32, . - verify_crc32 + /**************************************************************************** * decompress (real-mode or 16/32-bit protected-mode near call) * @@ -873,6 +914,7 @@ bcj_filter: * Returns: * %ds:%esi - End of compressed input data * %es:%edi - End of decompressed output data + * CF set if CRC32 was incorrect * All other registers are preserved * * NOTE: It would be possible to build a smaller version of the @@ -888,6 +930,13 @@ decompress: pushl %ecx pushl %edx pushl %ebp + /* Verify CRC32 */ + ADDR32 lodsl + movl %eax, %edx + pushl %esi + call verify_crc32 + popl %esi + jc 99f /* Allocate parameter block */ subl $sizeof__lzma_dec, %esp movl %esp, %ebp @@ -928,8 +977,11 @@ decompress: movl out_start(%ebp), %esi call bcj_filter popl %esi - /* Restore registers and return */ + /* Skip CRC */ + ADDR32 lodsl + /* Free parameter block (and clear CF) */ addl $sizeof__lzma_dec, %esp +99: /* Restore registers and return */ popl %ebp popl %edx popl %ecx diff --git a/src/arch/i386/prefix/unlzma16.S b/src/arch/x86/prefix/unlzma16.S similarity index 100% rename from src/arch/i386/prefix/unlzma16.S rename to src/arch/x86/prefix/unlzma16.S diff --git a/src/arch/i386/prefix/usbdisk.S b/src/arch/x86/prefix/usbdisk.S similarity index 100% rename from src/arch/i386/prefix/usbdisk.S rename to src/arch/x86/prefix/usbdisk.S diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/x86/scripts/pcbios.lds similarity index 86% rename from src/arch/i386/scripts/i386.lds rename to src/arch/x86/scripts/pcbios.lds index 38c89e14b..c9a91c02b 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/x86/scripts/pcbios.lds @@ -26,6 +26,20 @@ SECTIONS { PROVIDE ( _max_align = 16 ); + /* + * Values used in page table calculations + * + * On older versions of ld (without the SANE_EXPR feature), + * numeric literals within a section description tend to be + * interpreted as section-relative symbols. + * + */ + _page_size = 4096; + _page_size_1 = ( _page_size - 1 ); + _pte_size = 8; + _pte_count = ( _page_size / _pte_size ); + _pte_count_1 = ( _pte_count - 1 ); + /* * Allow decompressor to require a minimum amount of temporary stack * space. @@ -127,6 +141,18 @@ SECTIONS { *(COMMON) *(.stack) *(.stack.*) + _pages = .; + *(.pages) + *(.pages.*) + _use_page_tables = ABSOLUTE ( . ) - ABSOLUTE ( _pages ); + _textdata_paged_len = + ABSOLUTE ( ABSOLUTE ( . ) - ABSOLUTE ( _textdata ) ); + _textdata_ptes = + ABSOLUTE ( ( _textdata_paged_len + _page_size_1 ) / _page_size ); + _textdata_pdes = + ABSOLUTE ( ( _textdata_ptes + _pte_count_1 ) / _pte_count ); + . += ( _use_page_tables ? ( _textdata_pdes * _page_size ) : 0 ); + _epages = .; _etextdata = .; } _textdata_filesz = ABSOLUTE ( _mtextdata ) - ABSOLUTE ( _textdata ); @@ -247,8 +273,8 @@ SECTIONS { * Values calculated to save code from doing it * */ - _text16_memsz_pgh = ( ( _text16_memsz + 15 ) / 16 ); - _data16_memsz_pgh = ( ( _data16_memsz + 15 ) / 16 ); + _text16_memsz_ppgh = ( ( ( _text16_memsz + 63 ) / 64 ) * 4 ); + _data16_memsz_ppgh = ( ( ( _data16_memsz + 63 ) / 64 ) * 4 ); _textdata_memsz_pgh = ( ( _textdata_memsz + 15 ) / 16 ); _textdata_memsz_kb = ( ( _textdata_memsz + 1023 ) / 1024 ); } diff --git a/src/tests/comboot/shuffle-simple.asm b/src/arch/x86/tests/comboot/shuffle-simple.asm similarity index 99% rename from src/tests/comboot/shuffle-simple.asm rename to src/arch/x86/tests/comboot/shuffle-simple.asm index efc7d9b46..fa574bd72 100644 --- a/src/tests/comboot/shuffle-simple.asm +++ b/src/arch/x86/tests/comboot/shuffle-simple.asm @@ -2,7 +2,7 @@ org 100h jmp start - + shuffle_start: push 0xB800 pop es @@ -37,4 +37,3 @@ source: dd 0 dd shuffle_len num_shuffle_descriptors equ 1 - diff --git a/src/tests/comboot/version.asm b/src/arch/x86/tests/comboot/version.asm similarity index 100% rename from src/tests/comboot/version.asm rename to src/arch/x86/tests/comboot/version.asm diff --git a/src/arch/i386/transitions/liba20.S b/src/arch/x86/transitions/liba20.S similarity index 100% rename from src/arch/i386/transitions/liba20.S rename to src/arch/x86/transitions/liba20.S diff --git a/src/arch/i386/transitions/libkir.S b/src/arch/x86/transitions/libkir.S similarity index 100% rename from src/arch/i386/transitions/libkir.S rename to src/arch/x86/transitions/libkir.S diff --git a/src/arch/i386/transitions/libpm.S b/src/arch/x86/transitions/libpm.S similarity index 100% rename from src/arch/i386/transitions/libpm.S rename to src/arch/x86/transitions/libpm.S diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S new file mode 100644 index 000000000..9d3eff954 --- /dev/null +++ b/src/arch/x86/transitions/librm.S @@ -0,0 +1,1621 @@ +/* + * librm: a library for interfacing to real-mode code + * + * Michael Brown + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +/* Drag in general configuration */ +#include + +/* Drag in local definitions */ +#include "librm.h" + +/* CR0: protection enabled */ +#define CR0_PE ( 1 << 0 ) + +/* CR0: paging */ +#define CR0_PG ( 1 << 31 ) + +/* CR4: physical address extensions */ +#define CR4_PAE ( 1 << 5 ) + +/* Extended feature enable MSR (EFER) */ +#define MSR_EFER 0xc0000080 + +/* EFER: long mode enable */ +#define EFER_LME ( 1 << 8 ) + +/* Page: present */ +#define PG_P 0x01 + +/* Page: read/write */ +#define PG_RW 0x02 + +/* Page: user/supervisor */ +#define PG_US 0x04 + +/* Page: page size */ +#define PG_PS 0x80 + +/* Size of various paging-related data structures */ +#define SIZEOF_PTE_LOG2 3 +#define SIZEOF_PTE ( 1 << SIZEOF_PTE_LOG2 ) +#define SIZEOF_PT_LOG2 12 +#define SIZEOF_PT ( 1 << SIZEOF_PT_LOG2 ) +#define SIZEOF_4KB_PAGE_LOG2 12 +#define SIZEOF_4KB_PAGE ( 1 << SIZEOF_4KB_PAGE_LOG2 ) +#define SIZEOF_2MB_PAGE_LOG2 21 +#define SIZEOF_2MB_PAGE ( 1 << SIZEOF_2MB_PAGE_LOG2 ) +#define SIZEOF_LOW_4GB_LOG2 32 +#define SIZEOF_LOW_4GB ( 1 << SIZEOF_LOW_4GB_LOG2 ) + +/* Size of various C data structures */ +#define SIZEOF_I386_SEG_REGS 12 +#define SIZEOF_I386_REGS 32 +#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS ) +#define SIZEOF_I386_FLAGS 4 +#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS ) +#define SIZEOF_X86_64_REGS 128 + +/* Size of an address */ +#ifdef __x86_64__ +#define SIZEOF_ADDR 8 +#else +#define SIZEOF_ADDR 4 +#endif + +/* Default code size */ +#ifdef __x86_64__ +#define CODE_DEFAULT code64 +#else +#define CODE_DEFAULT code32 +#endif + +/* Selectively assemble code for 32-bit/64-bit builds */ +#ifdef __x86_64__ +#define if32 if 0 +#define if64 if 1 +#else +#define if32 if 1 +#define if64 if 0 +#endif + +/**************************************************************************** + * Global descriptor table + * + * Call init_librm to set up the GDT before attempting to use any + * protected-mode code. + * + * NOTE: This must be located before prot_to_real, otherwise gas + * throws a "can't handle non absolute segment in `ljmp'" error due to + * not knowing the value of REAL_CS when the ljmp is encountered. + * + * Note also that putting ".word gdt_end - gdt - 1" directly into + * gdt_limit, rather than going via gdt_length, will also produce the + * "non absolute segment" error. This is most probably a bug in gas. + **************************************************************************** + */ + .section ".data16.gdt", "aw", @progbits + .align 16 +gdt: +gdtr: /* The first GDT entry is unused, the GDTR can fit here. */ +gdt_limit: .word gdt_length - 1 +gdt_base: .long 0 + .word 0 /* padding */ + + .org gdt + VIRTUAL_CS, 0 +virtual_cs: /* 32 bit protected mode code segment, virtual addresses */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + + .org gdt + VIRTUAL_DS, 0 +virtual_ds: /* 32 bit protected mode data segment, virtual addresses */ + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 + + .org gdt + PHYSICAL_CS, 0 +physical_cs: /* 32 bit protected mode code segment, physical addresses */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + + .org gdt + PHYSICAL_DS, 0 +physical_ds: /* 32 bit protected mode data segment, physical addresses */ + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 + + .org gdt + REAL_CS, 0 +real_cs: /* 16 bit real mode code segment */ + .word 0xffff, 0 + .byte 0, 0x9b, 0x00, 0 + + .org gdt + REAL_DS, 0 +real_ds: /* 16 bit real mode data segment */ + .word 0xffff, 0 + .byte 0, 0x93, 0x00, 0 + + .org gdt + P2R_DS, 0 +p2r_ds: /* 16 bit real mode data segment for prot_to_real transition */ + .word 0xffff, ( P2R_DS << 4 ) + .byte 0, 0x93, 0x00, 0 + + .org gdt + LONG_CS, 0 +long_cs: /* 64 bit long mode code segment */ + .word 0, 0 + .byte 0, 0x9a, 0x20, 0 + +gdt_end: + .equ gdt_length, gdt_end - gdt + +/**************************************************************************** + * Stored real-mode and protected-mode stack pointers + * + * The real-mode stack pointer is stored here whenever real_to_prot + * is called and restored whenever prot_to_real is called. The + * converse happens for the protected-mode stack pointer. + * + * Despite initial appearances this scheme is, in fact re-entrant, + * because program flow dictates that we always return via the point + * we left by. For example: + * PXE API call entry + * 1 real => prot + * ... + * Print a text string + * ... + * 2 prot => real + * INT 10 + * 3 real => prot + * ... + * ... + * 4 prot => real + * PXE API call exit + * + * At point 1, the RM mode stack value, say RPXE, is stored in + * rm_ss,sp. We want this value to still be present in rm_ss,sp when + * we reach point 4. + * + * At point 2, the RM stack value is restored from RPXE. At point 3, + * the RM stack value is again stored in rm_ss,sp. This *does* + * overwrite the RPXE that we have stored there, but it's the same + * value, since the code between points 2 and 3 has managed to return + * to us. + **************************************************************************** + */ + .section ".bss.rm_ss_sp", "aw", @nobits + .globl rm_sp +rm_sp: .word 0 + .globl rm_ss +rm_ss: .word 0 + + .section ".data.pm_esp", "aw", @progbits +pm_esp: .long VIRTUAL(_estack) + +/**************************************************************************** + * Temporary static data buffer + * + * This is used to reduce the amount of real-mode stack space consumed + * during mode transitions, since we are sometimes called with very + * little real-mode stack space available. + **************************************************************************** + */ + /* Temporary static buffer usage by virt_call */ + .struct 0 +VC_TMP_GDT: .space 6 +VC_TMP_IDT: .space 6 +VC_TMP_PAD: .space 4 /* for alignment */ +.if64 +VC_TMP_CR3: .space 4 +VC_TMP_CR4: .space 4 +VC_TMP_EMER: .space 8 +.endif +#ifdef TIVOLI_VMM_WORKAROUND +VC_TMP_FXSAVE: .space 512 +#endif +VC_TMP_END: + .previous + + /* Temporary static buffer usage by real_call */ + .struct 0 +RC_TMP_FUNCTION: .space 4 +RC_TMP_END: + .previous + + /* Shared temporary static buffer */ + .section ".bss16.rm_tmpbuf", "aw", @nobits + .align 16 +rm_tmpbuf: + .space VC_TMP_END + .size rm_tmpbuf, . - rm_tmpbuf + +/**************************************************************************** + * Virtual address offsets + * + * These are used by the protected-mode code to map between virtual + * and physical addresses, and to access variables in the .text16 or + * .data16 segments. + **************************************************************************** + */ + .struct 0 +VA_VIRT_OFFSET: .space SIZEOF_ADDR +VA_TEXT16: .space SIZEOF_ADDR +VA_DATA16: .space SIZEOF_ADDR +VA_SIZE: + .previous + + /* Internal copies, used only by librm itself */ + .section ".bss16.rm_virt_addrs", "aw", @nobits +rm_virt_addrs: .space VA_SIZE + .equ rm_virt_offset, ( rm_virt_addrs + VA_VIRT_OFFSET ) + .equ rm_text16, ( rm_virt_addrs + VA_TEXT16 ) + .equ rm_data16, ( rm_virt_addrs + VA_DATA16 ) + + /* Externally visible variables, used by C code */ + .section ".bss.virt_addrs", "aw", @nobits +virt_addrs: .space VA_SIZE + .globl virt_offset + .equ virt_offset, ( virt_addrs + VA_VIRT_OFFSET ) + .globl text16 + .equ text16, ( virt_addrs + VA_TEXT16 ) + .globl data16 + .equ data16, ( virt_addrs + VA_DATA16 ) + +/**************************************************************************** + * init_librm (real-mode far call, 16-bit real-mode far return address) + * + * Initialise the GDT ready for transitions to protected mode. + * + * Parameters: + * %cs : .text16 segment + * %ds : .data16 segment + * %edi : Physical base of protected-mode code + **************************************************************************** + */ + .section ".text16.init_librm", "ax", @progbits + .code16 + .globl init_librm +init_librm: + /* Preserve registers */ + pushl %eax + pushl %ebx + pushl %edi + + /* Store rm_virt_offset and set up virtual_cs and virtual_ds segments */ + subl $VIRTUAL(_textdata), %edi + movl %edi, rm_virt_offset +.if64 ; setae (rm_virt_offset+4) ; .endif + movl %edi, %eax + movw $virtual_cs, %bx + call set_seg_base + movw $virtual_ds, %bx + call set_seg_base + + /* Store rm_cs and rm_text16, set up real_cs segment */ + xorl %eax, %eax + movw %cs, %ax + movw %ax, %cs:rm_cs + shll $4, %eax + movw $real_cs, %bx + call set_seg_base +.if32 ; subl %edi, %eax ; .endif + movl %eax, rm_text16 + + /* Store rm_ds and rm_data16, set up real_ds segment and GDT base */ + xorl %eax, %eax + movw %ds, %ax + movw %ax, %cs:rm_ds + shll $4, %eax + movw $real_ds, %bx + call set_seg_base + movl %eax, gdt_base + addl $gdt, gdt_base +.if32 ; subl %edi, %eax ; .endif + movl %eax, rm_data16 + + /* Configure virt_call for protected mode, if applicable */ +.if64 ; movl $VIRTUAL(vc_pmode), %cs:vc_jmp_offset ; .endif + + /* Switch to protected mode */ + virtcall init_librm_pmode + .section ".text.init_librm", "ax", @progbits + .code32 +init_librm_pmode: + + /* Store virt_offset, text16, and data16 */ + pushw %ds + movw $REAL_DS, %ax + movw %ax, %ds + movl $rm_virt_addrs, %esi + movl $VIRTUAL(virt_addrs), %edi + movl $( VA_SIZE / 4 ), %ecx + rep movsl + popw %ds + +.if64 ; /* Initialise long mode, if applicable */ + movl VIRTUAL(virt_offset), %edi + leal VIRTUAL(p2l_ljmp_target)(%edi), %eax + movl %eax, VIRTUAL(p2l_ljmp_offset) + call init_pages +.endif + /* Return to real mode */ + ret + .section ".text16.init_librm", "ax", @progbits + .code16 +init_librm_rmode: + + /* Configure virt_call for long mode, if applicable */ +.if64 ; movl $VIRTUAL(vc_lmode), %cs:vc_jmp_offset ; .endif + + /* Initialise IDT */ + virtcall init_idt + + /* Restore registers */ + popl %edi + popl %ebx + popl %eax + lret + + .section ".text16.set_seg_base", "ax", @progbits + .code16 +set_seg_base: +1: movw %ax, 2(%bx) + rorl $16, %eax + movb %al, 4(%bx) + movb %ah, 7(%bx) + roll $16, %eax + ret + +/**************************************************************************** + * real_to_prot (real-mode near call, 32-bit virtual return address) + * + * Switch from 16-bit real-mode to 32-bit protected mode with virtual + * addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and + * the protected-mode %esp is restored from the saved pm_esp. + * Interrupts are disabled. All other registers may be destroyed. + * + * The return address for this function should be a 32-bit virtual + * address. + * + * Parameters: + * %ecx : number of bytes to move from RM stack to PM stack + * %edx : number of bytes to copy from RM temporary buffer to PM stack + * + **************************************************************************** + */ + .section ".text16.real_to_prot", "ax", @progbits + .code16 +real_to_prot: + /* Enable A20 line */ + call enable_a20 + /* A failure at this point is fatal, and there's nothing we + * can do about it other than lock the machine to make the + * problem immediately visible. + */ +1: jc 1b + + /* Make sure we have our data segment available */ + movw %cs:rm_ds, %ds + + /* Add protected-mode return address to length of data to be copied */ + addw $4, %cx /* %ecx must be less than 64kB anyway */ + + /* Real-mode %ss:%sp => %ebp and virtual address => %esi */ + xorl %eax, %eax + movw %ss, %ax + shll $4, %eax + movzwl %sp, %ebp + addr32 leal (%eax,%ebp), %esi + subl rm_virt_offset, %esi + shll $12, %eax + orl %eax, %ebp + + /* Real-mode data segment virtual address => %ebx */ + movl rm_data16, %ebx +.if64 ; subl rm_virt_offset, %ebx ; .endif + + /* Load protected-mode global descriptor table */ + data32 lgdt gdtr + + /* Zero segment registers. This wastes around 12 cycles on + * real hardware, but saves a substantial number of emulated + * instructions under KVM. + */ + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + + /* Switch to protected mode (with paging disabled if applicable) */ + cli + movl %cr0, %eax +.if64 ; andl $~CR0_PG, %eax ; .endif + orb $CR0_PE, %al + movl %eax, %cr0 + data32 ljmp $VIRTUAL_CS, $VIRTUAL(r2p_pmode) + .section ".text.real_to_prot", "ax", @progbits + .code32 +r2p_pmode: + /* Set up protected-mode data segments and stack pointer */ + movw $VIRTUAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl VIRTUAL(pm_esp), %esp + + /* Load protected-mode interrupt descriptor table */ + lidt VIRTUAL(idtr32) + + /* Record real-mode %ss:sp (after removal of data) */ + addl %ecx, %ebp + movl %ebp, VIRTUAL(rm_sp) + + /* Move data from RM stack to PM stack */ + subl %edx, %esp + subl %ecx, %esp + movl %esp, %edi + rep movsb + + /* Copy data from RM temporary buffer to PM stack */ + leal rm_tmpbuf(%ebx), %esi + movl %edx, %ecx + rep movsb + + /* Return to virtual address */ + ret + +/**************************************************************************** + * prot_to_real (protected-mode near call, 32-bit real-mode return address) + * + * Switch from 32-bit protected mode with virtual addresses to 16-bit + * real mode. The protected-mode %esp is stored in pm_esp and the + * real-mode %ss:sp is restored from the saved rm_ss and rm_sp. The + * high word of the real-mode %esp is set to zero. All real-mode data + * segment registers are loaded from the saved rm_ds. Interrupts are + * *not* enabled, since we want to be able to use prot_to_real in an + * ISR. All other registers may be destroyed. + * + * The return address for this function should be a 32-bit (sic) + * real-mode offset within .code16. + * + * Parameters: + * %ecx : number of bytes to move from PM stack to RM stack + * %edx : number of bytes to move from PM stack to RM temporary buffer + * %esi : real-mode global and interrupt descriptor table registers + * + **************************************************************************** + */ + .section ".text.prot_to_real", "ax", @progbits + .code32 +prot_to_real: + /* Copy real-mode global descriptor table register to RM code segment */ + movl VIRTUAL(text16), %edi +.if64 ; subl VIRTUAL(virt_offset), %edi ; .endif + leal rm_gdtr(%edi), %edi + movsw + movsl + + /* Load real-mode interrupt descriptor table register */ + lidt (%esi) + + /* Add return address to data to be moved to RM stack */ + addl $4, %ecx + + /* Real-mode %ss:sp => %ebp and virtual address => %edi */ + movl VIRTUAL(rm_sp), %ebp + subl %ecx, %ebp + movzwl VIRTUAL(rm_ss), %eax + shll $4, %eax + movzwl %bp, %edi + addl %eax, %edi + subl VIRTUAL(virt_offset), %edi + + /* Move data from PM stack to RM stack */ + movl %esp, %esi + rep movsb + + /* Move data from PM stack to RM temporary buffer */ + movl VIRTUAL(data16), %edi +.if64 ; subl VIRTUAL(virt_offset), %edi ; .endif + addl $rm_tmpbuf, %edi + movl %edx, %ecx + rep movsb + + /* Record protected-mode %esp (after removal of data) */ + movl %esi, VIRTUAL(pm_esp) + + /* Load real-mode segment limits */ + movw $P2R_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + ljmp $REAL_CS, $p2r_rmode + .section ".text16.prot_to_real", "ax", @progbits + .code16 +p2r_rmode: + /* Load real-mode GDT */ + data32 lgdt %cs:rm_gdtr + /* Switch to real mode */ + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 +p2r_ljmp_rm_cs: + ljmp $0, $1f +1: + /* Set up real-mode data segments and stack pointer */ + movw %cs:rm_ds, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movl %ebp, %eax + shrl $16, %eax + movw %ax, %ss + movzwl %bp, %esp + + /* Return to real-mode address */ + data32 ret + + + /* Real-mode code and data segments. Assigned by the call to + * init_librm. rm_cs doubles as the segment part of the jump + * instruction used by prot_to_real. Both are located in + * .text16 rather than .data16: rm_cs since it forms part of + * the jump instruction within the code segment, and rm_ds + * since real-mode code needs to be able to locate the data + * segment with no other reference available. + */ + .globl rm_cs + .equ rm_cs, ( p2r_ljmp_rm_cs + 3 ) + + .section ".text16.data.rm_ds", "aw", @progbits + .globl rm_ds +rm_ds: .word 0 + + /* Real-mode global and interrupt descriptor table registers */ + .section ".text16.data.rm_gdtr", "aw", @progbits +rm_gdtr: + .word 0 /* Limit */ + .long 0 /* Base */ + +/**************************************************************************** + * phys_to_prot (protected-mode near call, 32-bit physical return address) + * + * Switch from 32-bit protected mode with physical addresses to 32-bit + * protected mode with virtual addresses. %esp is adjusted to a + * virtual address. All other registers are preserved. + * + * The return address for this function should be a 32-bit physical + * (sic) address. + * + **************************************************************************** + */ + .section ".text.phys_to_prot", "ax", @progbits + .code32 + .globl phys_to_prot +phys_to_prot: + /* Preserve registers */ + pushl %eax + pushl %ebp + + /* Switch to virtual code segment */ + cli + ljmp $VIRTUAL_CS, $VIRTUAL(1f) +1: + /* Switch to virtual data segment and adjust %esp */ + movw $VIRTUAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl VIRTUAL(virt_offset), %ebp + subl %ebp, %esp + + /* Adjust return address to a virtual address */ + subl %ebp, 8(%esp) + + /* Restore registers and return */ + popl %ebp + popl %eax + ret + +.if32 /* Expose as _phys_to_virt for use by COMBOOT, if applicable */ + .globl _phys_to_virt + .equ _phys_to_virt, phys_to_prot +.endif + +/**************************************************************************** + * prot_to_phys (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with virtual addresses to 32-bit + * protected mode with physical addresses. %esp is adjusted to a + * physical address. All other registers are preserved. + * + * The return address for this function should be a 32-bit virtual + * (sic) address. + * + **************************************************************************** + */ + .section ".text.prot_to_phys", "ax", @progbits + .code32 +prot_to_phys: + /* Preserve registers */ + pushl %eax + pushl %ebp + + /* Adjust return address to a physical address */ + movl VIRTUAL(virt_offset), %ebp + addl %ebp, 8(%esp) + + /* Switch to physical code segment */ + cli + pushl $PHYSICAL_CS + leal VIRTUAL(1f)(%ebp), %eax + pushl %eax + lret +1: + /* Switch to physical data segment and adjust %esp */ + movw $PHYSICAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + addl %ebp, %esp + + /* Restore registers and return */ + popl %ebp + popl %eax + ret + +.if32 /* Expose as _virt_to_phys for use by COMBOOT, if applicable */ + .globl _virt_to_phys + .equ _virt_to_phys, prot_to_phys +.endif + +/**************************************************************************** + * intr_to_prot (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with a virtual code segment and + * either a physical or virtual stack segment to 32-bit protected mode + * with normal virtual addresses. %esp is adjusted if necessary to a + * virtual address. All other registers are preserved. + * + * The return address for this function should be a 32-bit virtual + * address. + * + **************************************************************************** + */ + .section ".text.intr_to_prot", "ax", @progbits + .code32 + .globl intr_to_prot +intr_to_prot: + /* Preserve registers */ + pushl %eax + + /* Check whether stack segment is physical or virtual */ + movw %ss, %ax + cmpw $VIRTUAL_DS, %ax + movw $VIRTUAL_DS, %ax + + /* Reload data segment registers */ + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Reload stack segment and adjust %esp if necessary */ + je 1f + movw %ax, %ss + subl VIRTUAL(virt_offset), %esp +1: + /* Restore registers and return */ + popl %eax + ret + + /* Expose as _intr_to_virt for use by GDB */ + .globl _intr_to_virt + .equ _intr_to_virt, intr_to_prot + +/**************************************************************************** + * prot_to_long (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with virtual addresses to 64-bit + * long mode. The protected-mode %esp is adjusted to a physical + * address. All other registers are preserved. + * + * The return address for this function should be a 32-bit (sic) + * virtual address. + * + **************************************************************************** + */ + .if64 + + .section ".text.prot_to_long", "ax", @progbits + .code32 +prot_to_long: + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edx + + /* Set up PML4 */ + movl VIRTUAL(pml4), %eax + movl %eax, %cr3 + + /* Enable PAE */ + movl %cr4, %eax + orb $CR4_PAE, %al + movl %eax, %cr4 + + /* Enable long mode */ + movl $MSR_EFER, %ecx + rdmsr + orw $EFER_LME, %ax + wrmsr + + /* Enable paging */ + movl %cr0, %eax + orl $CR0_PG, %eax + movl %eax, %cr0 + + /* Restore registers */ + popl %edx + popl %ecx + popl %eax + + /* Construct 64-bit return address */ + pushl (%esp) + movl $0xffffffff, 4(%esp) +p2l_ljmp: + /* Switch to long mode (using a physical %rip) */ + ljmp $LONG_CS, $0 + .code64 +p2l_lmode: + /* Adjust and zero-extend %esp to a physical address */ + addl virt_offset, %esp + + /* Use long-mode IDT */ + lidt idtr64 + + /* Return to virtual address */ + ret + + /* Long mode jump offset and target. Required since an ljmp + * in protected mode will zero-extend the offset, and so + * cannot reach an address within the negative 2GB as used by + * -mcmodel=kernel. Assigned by the call to init_librm. + */ + .equ p2l_ljmp_offset, ( p2l_ljmp + 1 ) + .equ p2l_ljmp_target, p2l_lmode + + .endif + +/**************************************************************************** + * long_to_prot (long-mode near call, 64-bit virtual return address) + * + * Switch from 64-bit long mode to 32-bit protected mode with virtual + * addresses. The long-mode %rsp is adjusted to a virtual address. + * All other registers are preserved. + * + * The return address for this function should be a 64-bit (sic) + * virtual address. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_to_prot", "ax", @progbits + .code64 +long_to_prot: + /* Switch to protected mode */ + ljmp *l2p_vector + .code32 +l2p_pmode: + /* Adjust %esp to a virtual address */ + subl VIRTUAL(virt_offset), %esp + + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edx + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable PAE (in case external non-PAE-aware code enables paging) */ + movl %cr4, %eax + andb $~CR4_PAE, %al + movl %eax, %cr4 + + /* Disable long mode */ + movl $MSR_EFER, %ecx + rdmsr + andw $~EFER_LME, %ax + wrmsr + + /* Restore registers */ + popl %edx + popl %ecx + popl %eax + + /* Use protected-mode IDT */ + lidt VIRTUAL(idtr32) + + /* Return */ + ret $4 + + /* Long mode jump vector. Required since there is no "ljmp + * immediate" instruction in long mode. + */ + .section ".data.l2p_vector", "aw", @progbits +l2p_vector: + .long VIRTUAL(l2p_pmode), VIRTUAL_CS + + .endif + +/**************************************************************************** + * long_save_regs (long-mode near call, 64-bit virtual return address) + * + * Preserve registers that are accessible only in long mode. This + * includes %r8-%r15 and the upper halves of %rax, %rbx, %rcx, %rdx, + * %rsi, %rdi, and %rbp. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_preserve_regs", "ax", @progbits + .code64 +long_preserve_regs: + /* Preserve registers */ + pushq %rax + pushq %rcx + pushq %rdx + pushq %rbx + pushq %rsp + pushq %rbp + pushq %rsi + pushq %rdi + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + /* Return */ + jmp *SIZEOF_X86_64_REGS(%rsp) + + .endif + +/**************************************************************************** + * long_restore_regs (long-mode near call, 64-bit virtual return address) + * + * Restore registers that are accessible only in long mode. This + * includes %r8-%r15 and the upper halves of %rax, %rbx, %rcx, %rdx, + * %rsi, %rdi, and %rbp. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_restore_regs", "ax", @progbits + .code64 +long_restore_regs: + /* Move return address above register dump */ + popq SIZEOF_X86_64_REGS(%rsp) + + /* Restore registers */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + movl %edi, (%rsp) + popq %rdi + movl %esi, (%rsp) + popq %rsi + movl %ebp, (%rsp) + popq %rbp + leaq 8(%rsp), %rsp /* discard */ + movl %ebx, (%rsp) + popq %rbx + movl %edx, (%rsp) + popq %rdx + movl %ecx, (%rsp) + popq %rcx + movl %eax, (%rsp) + popq %rax + + /* Return */ + ret + + .endif + +/**************************************************************************** + * virt_call (real-mode near call, 16-bit real-mode near return address) + * + * Call a specific C function in 32-bit protected mode or 64-bit long + * mode (as applicable). The prototype of the C function must be + * void function ( struct i386_all_regs *ix86 ); + * ix86 will point to a struct containing the real-mode registers + * at entry to virt_call(). + * + * All registers will be preserved across virt_call(), unless the C + * function explicitly overwrites values in ix86. Interrupt status + * and GDT will also be preserved. Gate A20 will be enabled. + * + * Note that virt_call() does not rely on the real-mode stack + * remaining intact in order to return, since everything relevant is + * copied to the protected-mode stack for the duration of the call. + * In particular, this means that a real-mode prefix can make a call + * to main() which will return correctly even if the prefix's stack + * gets vapourised during the Etherboot run. (The prefix cannot rely + * on anything else on the stack being preserved, so should move any + * critical data to registers before calling main()). + * + * Parameters: + * function : 32-bit virtual address of function to call + * + * Example usage: + * pushl $pxe_api_call + * call virt_call + * to call in to the C function + * void pxe_api_call ( struct i386_all_regs *ix86 ); + **************************************************************************** + */ + .struct 0 +VC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS +VC_OFFSET_PADDING: .space 2 /* for alignment */ +VC_OFFSET_RETADDR: .space 2 +VC_OFFSET_PARAMS: +VC_OFFSET_FUNCTION: .space 4 +VC_OFFSET_END: + .previous + + .section ".text16.virt_call", "ax", @progbits + .code16 + .globl virt_call +virt_call: + /* Preserve registers and flags on external RM stack */ + pushw %ss /* padding */ + pushfl + pushal + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + + /* Claim ownership of temporary static buffer */ + cli + movw %cs:rm_ds, %ds + +#ifdef TIVOLI_VMM_WORKAROUND + /* Preserve FPU, MMX and SSE state in temporary static buffer */ + fxsave ( rm_tmpbuf + VC_TMP_FXSAVE ) +#endif + /* Preserve GDT and IDT in temporary static buffer */ + sidt ( rm_tmpbuf + VC_TMP_IDT ) + sgdt ( rm_tmpbuf + VC_TMP_GDT ) + +.if64 ; /* Preserve control registers, if applicable */ + movl $MSR_EFER, %ecx + rdmsr + movl %eax, ( rm_tmpbuf + VC_TMP_EMER + 0 ) + movl %edx, ( rm_tmpbuf + VC_TMP_EMER + 4 ) + movl %cr4, %eax + movl %eax, ( rm_tmpbuf + VC_TMP_CR4 ) + movl %cr3, %eax + movl %eax, ( rm_tmpbuf + VC_TMP_CR3 ) +.endif + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to protected mode and move register dump to PM stack */ + movl $VC_OFFSET_END, %ecx + movl $VC_TMP_END, %edx + pushl $VIRTUAL(vc_pmode) +vc_jmp: jmp real_to_prot + .section ".text.virt_call", "ax", @progbits + .code32 +vc_pmode: + /* Call function (in protected mode) */ + pushl %esp + call *(VC_OFFSET_FUNCTION+4)(%esp) + popl %eax /* discard */ + +.if64 ; /* Switch to long mode */ + jmp 1f +vc_lmode: + call prot_to_long + .code64 + + /* Call function (in long mode) */ + movq %rsp, %rdi + movslq VC_OFFSET_FUNCTION(%rsp), %rax + callq *%rax + + /* Switch to protected mode */ + call long_to_prot +1: .code32 +.endif + /* Switch to real mode and move register dump back to RM stack */ + movl $VC_OFFSET_END, %ecx + movl $VC_TMP_END, %edx + leal VC_TMP_GDT(%esp, %ecx), %esi + pushl $vc_rmode + jmp prot_to_real + .section ".text16.virt_call", "ax", @progbits + .code16 +vc_rmode: +.if64 ; /* Restore control registers, if applicable */ + movw %sp, %bp + movl ( rm_tmpbuf + VC_TMP_CR3 ), %eax + movl %eax, %cr3 + movl ( rm_tmpbuf + VC_TMP_CR4 ), %eax + movl %eax, %cr4 + movl ( rm_tmpbuf + VC_TMP_EMER + 0 ), %eax + movl ( rm_tmpbuf + VC_TMP_EMER + 4 ), %edx + movl $MSR_EFER, %ecx + wrmsr +.endif + +#ifdef TIVOLI_VMM_WORKAROUND + /* Restore FPU, MMX and SSE state from temporary static buffer */ + fxrstor ( rm_tmpbuf + VC_TMP_FXSAVE ) +#endif + /* Restore registers and flags and return */ + popl %eax /* skip %cs and %ss */ + popw %ds + popw %es + popw %fs + popw %gs + popal + /* popal skips %esp. We therefore want to do "movl -20(%sp), + * %esp", but -20(%sp) is not a valid 80386 expression. + * Fortunately, prot_to_real() zeroes the high word of %esp, so + * we can just use -20(%esp) instead. + */ + addr32 movl -20(%esp), %esp + popfl + popw %ss /* padding */ + + /* Return and discard function parameters */ + ret $( VC_OFFSET_END - VC_OFFSET_PARAMS ) + + + /* Protected-mode jump target */ + .equ vc_jmp_offset, ( vc_jmp - 4 ) + +/**************************************************************************** + * real_call (protected-mode near call, 32-bit virtual return address) + * real_call (long-mode near call, 64-bit virtual return address) + * + * Call a real-mode function from protected-mode or long-mode code. + * + * The non-segment register values will be passed directly to the + * real-mode code. The segment registers will be set as per + * prot_to_real. The non-segment register values set by the real-mode + * function will be passed back to the protected-mode or long-mode + * caller. A result of this is that this routine cannot be called + * directly from C code, since it clobbers registers that the C ABI + * expects the callee to preserve. + * + * librm.h defines a convenient macro REAL_CODE() for using real_call. + * See librm.h and realmode.h for details and examples. + * + * Parameters: + * function : offset within .text16 of real-mode function to call + * + * Returns: none + **************************************************************************** + */ + .struct 0 +RC_OFFSET_REGS: .space SIZEOF_I386_REGS +RC_OFFSET_REGS_END: +RC_OFFSET_FUNCTION_COPY:.space 4 +.if64 +RC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS +RC_OFFSET_LREG_RETADDR: .space SIZEOF_ADDR +.endif +RC_OFFSET_RETADDR: .space SIZEOF_ADDR +RC_OFFSET_PARAMS: +RC_OFFSET_FUNCTION: .space SIZEOF_ADDR +RC_OFFSET_END: + .previous + + .section ".text.real_call", "ax", @progbits + .CODE_DEFAULT + .globl real_call +real_call: +.if64 ; /* Preserve registers and switch to protected mode, if applicable */ + call long_preserve_regs + call long_to_prot + .code32 +.endif + /* Create register dump and function pointer copy on PM stack */ + pushl ( RC_OFFSET_FUNCTION - RC_OFFSET_FUNCTION_COPY - 4 )(%esp) + pushal + + /* Switch to real mode and move register dump to RM stack */ + movl $RC_OFFSET_REGS_END, %ecx + movl $RC_TMP_END, %edx + pushl $rc_rmode + movl $VIRTUAL(rm_default_gdtr_idtr), %esi + jmp prot_to_real + .section ".text16.real_call", "ax", @progbits + .code16 +rc_rmode: + /* Call real-mode function */ + popal + call *( rm_tmpbuf + RC_TMP_FUNCTION ) + pushal + + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to protected mode and move register dump back to PM stack */ + movl $RC_OFFSET_REGS_END, %ecx + xorl %edx, %edx + pushl $VIRTUAL(rc_pmode) + jmp real_to_prot + .section ".text.real_call", "ax", @progbits + .code32 +rc_pmode: + /* Restore registers */ + popal + +.if64 ; /* Switch to long mode and restore registers, if applicable */ + call prot_to_long + .code64 + call long_restore_regs +.endif + /* Return and discard function parameters */ + ret $( RC_OFFSET_END - RC_OFFSET_PARAMS ) + + + /* Default real-mode global and interrupt descriptor table registers */ + .section ".data.rm_default_gdtr_idtr", "aw", @progbits +rm_default_gdtr_idtr: + .word 0 /* Global descriptor table limit */ + .long 0 /* Global descriptor table base */ + .word 0x03ff /* Interrupt descriptor table limit */ + .long 0 /* Interrupt descriptor table base */ + +/**************************************************************************** + * phys_call (protected-mode near call, 32-bit virtual return address) + * phys_call (long-mode near call, 64-bit virtual return address) + * + * Call a function with flat 32-bit physical addressing + * + * The non-segment register values will be passed directly to the + * function. The segment registers will be set for flat 32-bit + * physical addressing. The non-segment register values set by the + * function will be passed back to the caller. + * + * librm.h defines a convenient macro PHYS_CODE() for using phys_call. + * + * Parameters: + * function : virtual (sic) address of function to call + * + **************************************************************************** + */ + .struct 0 +.if64 +PHC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS +PHC_OFFSET_LREG_RETADDR:.space SIZEOF_ADDR +.endif +PHC_OFFSET_RETADDR: .space SIZEOF_ADDR +PHC_OFFSET_PARAMS: +PHC_OFFSET_FUNCTION: .space SIZEOF_ADDR +PHC_OFFSET_END: + .previous + + .section ".text.phys_call", "ax", @progbits + .CODE_DEFAULT + .globl phys_call +phys_call: +.if64 ; /* Preserve registers and switch to protected mode, if applicable */ + call long_preserve_regs + call long_to_prot + .code32 +.endif + /* Adjust function pointer to a physical address */ + pushl %ebp + movl VIRTUAL(virt_offset), %ebp + addl %ebp, ( PHC_OFFSET_FUNCTION + 4 /* saved %ebp */ )(%esp) + popl %ebp + + /* Switch to physical addresses */ + call prot_to_phys + + /* Call function */ + call *PHC_OFFSET_FUNCTION(%esp) + + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to virtual addresses */ + call phys_to_prot + +.if64 ; /* Switch to long mode and restore registers, if applicable */ + call prot_to_long + .code64 + call long_restore_regs +.endif + /* Return and discard function parameters */ + ret $( PHC_OFFSET_END - PHC_OFFSET_PARAMS ) + +/**************************************************************************** + * phys_to_long (protected-mode near call, 32-bit physical return address) + * + * Used by COMBOOT. + * + **************************************************************************** + */ + .if64 + + .section ".text.phys_to_long", "ax", @progbits + .code32 +phys_to_long: + + /* Switch to virtual addresses */ + call phys_to_prot + + /* Convert to 32-bit virtual return address */ + pushl %eax + movl VIRTUAL(virt_offset), %eax + subl %eax, 4(%esp) + popl %eax + + /* Switch to long mode and return */ + jmp prot_to_long + + /* Expose as _phys_to_virt for use by COMBOOT */ + .globl _phys_to_virt + .equ _phys_to_virt, phys_to_long + + .endif + +/**************************************************************************** + * long_to_phys (long-mode near call, 64-bit virtual return address) + * + * Used by COMBOOT. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_to_phys", "ax", @progbits + .code64 +long_to_phys: + + /* Switch to protected mode */ + call long_to_prot + .code32 + + /* Convert to 32-bit virtual return address */ + popl (%esp) + + /* Switch to physical addresses and return */ + jmp prot_to_phys + + /* Expose as _virt_to_phys for use by COMBOOT */ + .globl _virt_to_phys + .equ _virt_to_phys, long_to_phys + + .endif + +/**************************************************************************** + * flatten_real_mode (real-mode near call) + * + * Switch to flat real mode + * + **************************************************************************** + */ + .section ".text16.flatten_real_mode", "ax", @progbits + .code16 + .globl flatten_real_mode +flatten_real_mode: + /* Modify GDT to use flat real mode */ + movb $0x8f, real_cs + 6 + movb $0x8f, real_ds + 6 + /* Call dummy protected-mode function */ + virtcall flatten_dummy + /* Restore GDT */ + movb $0x00, real_cs + 6 + movb $0x00, real_ds + 6 + /* Return */ + ret + + .section ".text.flatten_dummy", "ax", @progbits + .CODE_DEFAULT +flatten_dummy: + ret + +/**************************************************************************** + * Interrupt wrapper + * + * Used by the protected-mode and long-mode interrupt vectors to call + * the interrupt() function. + * + * May be entered with either physical or virtual stack segment. + **************************************************************************** + */ + .section ".text.interrupt_wrapper", "ax", @progbits + .code32 + .globl interrupt_wrapper +interrupt_wrapper: + /* Preserve registers (excluding already-saved %eax) */ + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + pushl %ebp + + /* Expand IRQ number to whole %eax register */ + movzbl %al, %eax + +.if64 ; /* Skip transition to long mode, if applicable */ + xorl %edx, %edx + movw %cs, %bx + cmpw $LONG_CS, %bx + je 1f +.endif + /* Preserve segment registers and original %esp */ + pushl %ds + pushl %es + pushl %fs + pushl %gs + pushl %ss + pushl %esp + + /* Switch to virtual addressing */ + call intr_to_prot + + /* Pass 32-bit interrupt frame pointer in %edx */ + movl %esp, %edx + xorl %ecx, %ecx +.if64 + /* Switch to long mode */ + call prot_to_long + .code64 + +1: /* Preserve long-mode registers */ + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + /* Expand IRQ number to whole %rdi register */ + movl %eax, %edi + + /* Pass 32-bit interrupt frame pointer (if applicable) in %rsi */ + testl %edx, %edx + je 1f + movl %edx, %esi + addl virt_offset, %esi +1: + /* Pass 64-bit interrupt frame pointer in %rdx */ + movq %rsp, %rdx +.endif + /* Call interrupt handler */ + call interrupt +.if64 + /* Restore long-mode registers */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + + /* Skip transition back to protected mode, if applicable */ + cmpw $LONG_CS, %bx + je 1f + + /* Switch to protected mode */ + call long_to_prot + .code32 + cmpw $LONG_CS, %bx +.endif + /* Restore segment registers and original %esp */ + lss (%esp), %esp + popl %ss + popl %gs + popl %fs + popl %es + popl %ds + +1: /* Restore registers */ + popl %ebp + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + + /* Return from interrupt (with REX prefix if required) */ +.if64 ; jne 1f ; .byte 0x48 ; .endif +1: iret + +/**************************************************************************** + * Page tables + * + **************************************************************************** + */ + .section ".pages", "aw", @nobits + .align SIZEOF_PT + + /* Page map level 4 entries (PML4Es) + * + * This comprises + * + * - PML4E[0x000] covering [0x0000000000000000-0x0000007fffffffff] + * - PML4E[0x1ff] covering [0xffffff8000000000-0xffffffffffffffff] + * + * These point to the PDPT. This creates some aliased + * addresses within unused portions of the 64-bit address + * space, but allows us to use just a single PDPT. + * + * - PDE[...] covering arbitrary 2MB portions of I/O space + * + * These are 2MB pages created by ioremap() to cover I/O + * device addresses. + */ +pml4e: + .space SIZEOF_PT + .size pml4e, . - pml4e + + .globl io_pages + .equ io_pages, pml4e + + /* Page directory pointer table entries (PDPTEs) + * + * This comprises: + * + * - PDPTE[0x000] covering [0x0000000000000000-0x000000003fffffff] + * - PDPTE[0x001] covering [0x0000000040000000-0x000000007fffffff] + * - PDPTE[0x002] covering [0x0000000080000000-0x00000000bfffffff] + * - PDPTE[0x003] covering [0x00000000c0000000-0x00000000ffffffff] + * + * These point to the appropriate page directories (in pde_low) + * used to identity-map the whole of the 32-bit address space. + * + * - PDPTE[0x004] covering [0x0000000100000000-0x000000013fffffff] + * + * This points back to the PML4, allowing the PML4 to be + * (ab)used to hold 2MB pages used for I/O device addresses. + * + * - PDPTE[0x1ff] covering [0xffffffffc0000000-0xffffffffffffffff] + * + * This points back to the PDPT itself, allowing the PDPT to be + * (ab)used to hold PDEs covering .textdata. + * + * - PDE[N-M] covering [_textdata,_end) + * + * These are used to point to the page tables (in pte_textdata) + * used to map our .textdata section. Note that each PDE + * covers 2MB, so we are likely to use only a single PDE in + * practice. + */ +pdpte: + .space SIZEOF_PT + .size pdpte, . - pdpte + .equ pde_textdata, pdpte /* (ab)use */ + + /* Page directory entries (PDEs) for the low 4GB + * + * This comprises 2048 2MB pages to identity-map the whole of + * the 32-bit address space. + */ +pde_low: + .equ PDE_LOW_PTES, ( SIZEOF_LOW_4GB / SIZEOF_2MB_PAGE ) + .equ PDE_LOW_PTS, ( ( PDE_LOW_PTES * SIZEOF_PTE ) / SIZEOF_PT ) + .space ( PDE_LOW_PTS * SIZEOF_PT ) + .size pde_low, . - pde_low + + /* Page table entries (PTEs) for .textdata + * + * This comprises enough 4kB pages to map the whole of + * .textdata. The required number of PTEs is calculated by + * the linker script. + * + * Note that these mappings do not cover the PTEs themselves. + * This does not matter, since code running with paging + * enabled never needs to access these PTEs. + */ +pte_textdata: + /* Allocated by linker script; must be at the end of .textdata */ + + .section ".bss.pml4", "aw", @nobits +pml4: .long 0 + +/**************************************************************************** + * init_pages (protected-mode near call) + * + * Initialise the page tables ready for long mode. + * + * Parameters: + * %edi : virt_offset + **************************************************************************** + */ + .section ".text.init_pages", "ax", @progbits + .code32 +init_pages: + /* Initialise PML4Es for low 4GB and negative 2GB */ + leal ( VIRTUAL(pdpte) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + movl %eax, VIRTUAL(pml4e) + movl %eax, ( VIRTUAL(pml4e) + SIZEOF_PT - SIZEOF_PTE ) + + /* Initialise PDPTE for negative 1GB */ + movl %eax, ( VIRTUAL(pdpte) + SIZEOF_PT - SIZEOF_PTE ) + + /* Initialise PDPTE for I/O space */ + leal ( VIRTUAL(pml4e) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + movl %eax, ( VIRTUAL(pdpte) + ( PDE_LOW_PTS * SIZEOF_PTE ) ) + + /* Initialise PDPTEs for low 4GB */ + movl $PDE_LOW_PTS, %ecx + leal ( VIRTUAL(pde_low) + ( PDE_LOW_PTS * SIZEOF_PT ) + \ + ( PG_P | PG_RW | PG_US ) )(%edi), %eax +1: subl $SIZEOF_PT, %eax + movl %eax, ( VIRTUAL(pdpte) - SIZEOF_PTE )(,%ecx,SIZEOF_PTE) + loop 1b + + /* Initialise PDEs for low 4GB */ + movl $PDE_LOW_PTES, %ecx + leal ( 0 + ( PG_P | PG_RW | PG_US | PG_PS ) ), %eax +1: subl $SIZEOF_2MB_PAGE, %eax + movl %eax, ( VIRTUAL(pde_low) - SIZEOF_PTE )(,%ecx,SIZEOF_PTE) + loop 1b + + /* Initialise PDEs for .textdata */ + movl $_textdata_pdes, %ecx + leal ( VIRTUAL(_etextdata) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + movl $VIRTUAL(_textdata), %ebx + shrl $( SIZEOF_2MB_PAGE_LOG2 - SIZEOF_PTE_LOG2 ), %ebx + andl $( SIZEOF_PT - 1 ), %ebx +1: subl $SIZEOF_PT, %eax + movl %eax, (VIRTUAL(pde_textdata) - SIZEOF_PTE)(%ebx,%ecx,SIZEOF_PTE) + loop 1b + + /* Initialise PTEs for .textdata */ + movl $_textdata_ptes, %ecx + leal ( VIRTUAL(_textdata) + ( PG_P | PG_RW | PG_US ) )(%edi), %eax + addl $_textdata_paged_len, %eax +1: subl $SIZEOF_4KB_PAGE, %eax + movl %eax, ( VIRTUAL(pte_textdata) - SIZEOF_PTE )(,%ecx,SIZEOF_PTE) + loop 1b + + /* Record PML4 physical address */ + leal VIRTUAL(pml4e)(%edi), %eax + movl %eax, VIRTUAL(pml4) + + /* Return */ + ret diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c new file mode 100644 index 000000000..f9e1d261a --- /dev/null +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -0,0 +1,401 @@ +/* + * librm: a library for interfacing to real-mode code + * + * Michael Brown + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/* + * This file provides functions for managing librm. + * + */ + +/** The interrupt wrapper */ +extern char interrupt_wrapper[]; + +/** The interrupt vectors */ +static struct interrupt_vector intr_vec[NUM_INT]; + +/** The 32-bit interrupt descriptor table */ +static struct interrupt32_descriptor +idt32[NUM_INT] __attribute__ (( aligned ( 16 ) )); + +/** The 32-bit interrupt descriptor table register */ +struct idtr32 idtr32 = { + .limit = ( sizeof ( idt32 ) - 1 ), +}; + +/** The 64-bit interrupt descriptor table */ +static struct interrupt64_descriptor +idt64[NUM_INT] __attribute__ (( aligned ( 16 ) )); + +/** The interrupt descriptor table register */ +struct idtr64 idtr64 = { + .limit = ( sizeof ( idt64 ) - 1 ), +}; + +/** Length of stack dump */ +#define STACK_DUMP_LEN 128 + +/** Timer interrupt profiler */ +static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" }; + +/** Other interrupt profiler */ +static struct profiler other_irq_profiler __profiler = { .name = "irq.other" }; + +/** + * Allocate space on the real-mode stack and copy data there from a + * user buffer + * + * @v data User buffer + * @v size Size of stack data + * @ret sp New value of real-mode stack pointer + */ +uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) { + userptr_t rm_stack; + rm_sp -= size; + rm_stack = real_to_user ( rm_ss, rm_sp ); + memcpy_user ( rm_stack, 0, data, 0, size ); + return rm_sp; +}; + +/** + * Deallocate space on the real-mode stack, optionally copying back + * data to a user buffer. + * + * @v data User buffer + * @v size Size of stack data + */ +void remove_user_from_rm_stack ( userptr_t data, size_t size ) { + if ( data ) { + userptr_t rm_stack = real_to_user ( rm_ss, rm_sp ); + memcpy_user ( rm_stack, 0, data, 0, size ); + } + rm_sp += size; +}; + +/** + * Set interrupt vector + * + * @v intr Interrupt number + * @v vector Interrupt vector, or NULL to disable + */ +void set_interrupt_vector ( unsigned int intr, void *vector ) { + struct interrupt32_descriptor *idte32; + struct interrupt64_descriptor *idte64; + intptr_t addr = ( ( intptr_t ) vector ); + + /* Populate 32-bit interrupt descriptor */ + idte32 = &idt32[intr]; + idte32->segment = VIRTUAL_CS; + idte32->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); + idte32->low = ( addr >> 0 ); + idte32->high = ( addr >> 16 ); + + /* Populate 64-bit interrupt descriptor, if applicable */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + idte64 = &idt64[intr]; + idte64->segment = LONG_CS; + idte64->attr = ( vector ? + ( IDTE_PRESENT | IDTE_TYPE_IRQ64 ) : 0 ); + idte64->low = ( addr >> 0 ); + idte64->mid = ( addr >> 16 ); + idte64->high = ( ( ( uint64_t ) addr ) >> 32 ); + } +} + +/** + * Initialise interrupt descriptor table + * + */ +void init_idt ( void ) { + struct interrupt_vector *vec; + unsigned int intr; + + /* Initialise the interrupt descriptor table and interrupt vectors */ + for ( intr = 0 ; intr < NUM_INT ; intr++ ) { + vec = &intr_vec[intr]; + vec->push = PUSH_INSN; + vec->movb = MOVB_INSN; + vec->intr = intr; + vec->jmp = JMP_INSN; + vec->offset = ( ( intptr_t ) interrupt_wrapper - + ( intptr_t ) vec->next ); + set_interrupt_vector ( intr, vec ); + } + DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n", + intr_vec, sizeof ( intr_vec[0] ), + virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); + + /* Initialise the 32-bit interrupt descriptor table register */ + idtr32.base = virt_to_phys ( idt32 ); + + /* Initialise the 64-bit interrupt descriptor table register, + * if applicable. + */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) + idtr64.base = virt_to_phys ( idt64 ); +} + +/** + * Determine interrupt profiler (for debugging) + * + * @v intr Interrupt number + * @ret profiler Profiler + */ +static struct profiler * interrupt_profiler ( int intr ) { + + switch ( intr ) { + case IRQ_INT ( 0 ) : + return &timer_irq_profiler; + default: + return &other_irq_profiler; + } +} + +/** + * Display interrupt stack dump (for debugging) + * + * @v intr Interrupt number + * @v frame32 32-bit interrupt wrapper stack frame (or NULL) + * @v frame64 64-bit interrupt wrapper stack frame (or NULL) + */ +static __attribute__ (( unused )) void +interrupt_dump ( int intr, struct interrupt_frame32 *frame32, + struct interrupt_frame64 *frame64 ) { + unsigned long sp; + void *stack; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Print register dump */ + if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) { + sp = ( frame32->esp + sizeof ( *frame32 ) - + offsetof ( typeof ( *frame32 ), esp ) ); + DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n", + intr, frame32->cs, frame32->eip, frame32->ss, sp ); + DBGC ( &intr, "cs = %04x ds = %04x es = %04x fs = %04x " + "gs = %04x ss = %04x\n", frame32->cs, frame32->ds, + frame32->es, frame32->fs, frame32->gs, frame32->ss ); + DBGC ( &intr, "eax = %08x ebx = %08x ecx = %08x " + "edx = %08x flg = %08x\n", frame32->eax, frame32->ebx, + frame32->ecx, frame32->edx, frame32->eflags ); + DBGC ( &intr, "esi = %08x edi = %08x ebp = %08x " + "esp = %08lx eip = %08x\n", frame32->esi, frame32->edi, + frame32->ebp, sp, frame32->eip ); + stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) ); + } else { + DBGC ( &intr, "INT%d at %04llx:%016llx (stack " + "%04llx:%016llx):\n", intr, + ( ( unsigned long long ) frame64->cs ), + ( ( unsigned long long ) frame64->rip ), + ( ( unsigned long long ) frame64->ss ), + ( ( unsigned long long ) frame64->rsp ) ); + DBGC ( &intr, "rax = %016llx rbx = %016llx rcx = %016llx\n", + ( ( unsigned long long ) frame64->rax ), + ( ( unsigned long long ) frame64->rbx ), + ( ( unsigned long long ) frame64->rcx ) ); + DBGC ( &intr, "rdx = %016llx rsi = %016llx rdi = %016llx\n", + ( ( unsigned long long ) frame64->rdx ), + ( ( unsigned long long ) frame64->rsi ), + ( ( unsigned long long ) frame64->rdi ) ); + DBGC ( &intr, "rbp = %016llx rsp = %016llx flg = %016llx\n", + ( ( unsigned long long ) frame64->rbp ), + ( ( unsigned long long ) frame64->rsp ), + ( ( unsigned long long ) frame64->rflags ) ); + DBGC ( &intr, "r8 = %016llx r9 = %016llx r10 = %016llx\n", + ( ( unsigned long long ) frame64->r8 ), + ( ( unsigned long long ) frame64->r9 ), + ( ( unsigned long long ) frame64->r10 ) ); + DBGC ( &intr, "r11 = %016llx r12 = %016llx r13 = %016llx\n", + ( ( unsigned long long ) frame64->r11 ), + ( ( unsigned long long ) frame64->r12 ), + ( ( unsigned long long ) frame64->r13 ) ); + DBGC ( &intr, "r14 = %016llx r15 = %016llx\n", + ( ( unsigned long long ) frame64->r14 ), + ( ( unsigned long long ) frame64->r15 ) ); + sp = frame64->rsp; + stack = phys_to_virt ( sp ); + } + + /* Print stack dump */ + DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN ); +} + +/** + * Interrupt handler + * + * @v intr Interrupt number + * @v frame32 32-bit interrupt wrapper stack frame (or NULL) + * @v frame64 64-bit interrupt wrapper stack frame (or NULL) + * @v frame Interrupt wrapper stack frame + */ +void __attribute__ (( regparm ( 3 ) )) +interrupt ( int intr, struct interrupt_frame32 *frame32, + struct interrupt_frame64 *frame64 ) { + struct profiler *profiler = interrupt_profiler ( intr ); + uint32_t discard_eax; + + /* Trap CPU exceptions if debugging is enabled. Note that we + * cannot treat INT8+ as exceptions, since we are not + * permitted to rebase the PIC. + */ + if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) { + interrupt_dump ( intr, frame32, frame64 ); + DBG ( "CPU exception: dropping to emergency shell\n" ); + shell(); + } + + /* Reissue interrupt in real mode */ + profile_start ( profiler ); + __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t" + "\n1:\n\t" + "int $0x00\n\t" ) + : "=a" ( discard_eax ) : "0" ( intr ) ); + profile_stop ( profiler ); + profile_exclude ( profiler ); +} + +/** + * Map pages for I/O + * + * @v bus_addr Bus address + * @v len Length of region + * @ret io_addr I/O address + */ +static void * ioremap_pages ( unsigned long bus_addr, size_t len ) { + unsigned long start; + unsigned int count; + unsigned int stride; + unsigned int first; + unsigned int i; + size_t offset; + void *io_addr; + + DBGC ( &io_pages, "IO mapping %08lx+%zx\n", bus_addr, len ); + + /* Sanity check */ + if ( ! len ) + return NULL; + + /* Round down start address to a page boundary */ + start = ( bus_addr & ~( IO_PAGE_SIZE - 1 ) ); + offset = ( bus_addr - start ); + assert ( offset < IO_PAGE_SIZE ); + + /* Calculate number of pages required */ + count = ( ( offset + len + IO_PAGE_SIZE - 1 ) / IO_PAGE_SIZE ); + assert ( count != 0 ); + assert ( count < ( sizeof ( io_pages.page ) / + sizeof ( io_pages.page[0] ) ) ); + + /* Round up number of pages to a power of two */ + stride = ( 1 << ( fls ( count ) - 1 ) ); + assert ( count <= stride ); + + /* Allocate pages */ + for ( first = 0 ; first < ( sizeof ( io_pages.page ) / + sizeof ( io_pages.page[0] ) ) ; + first += stride ) { + + /* Calculate I/O address */ + io_addr = ( IO_BASE + ( first * IO_PAGE_SIZE ) + offset ); + + /* Check that page table entries are available */ + for ( i = first ; i < ( first + count ) ; i++ ) { + if ( io_pages.page[i] & PAGE_P ) { + io_addr = NULL; + break; + } + } + if ( ! io_addr ) + continue; + + /* Create page table entries */ + for ( i = first ; i < ( first + count ) ; i++ ) { + io_pages.page[i] = ( start | PAGE_P | PAGE_RW | + PAGE_US | PAGE_PWT | PAGE_PCD | + PAGE_PS ); + start += IO_PAGE_SIZE; + } + + /* Mark last page as being the last in this allocation */ + io_pages.page[ i - 1 ] |= PAGE_LAST; + + /* Return I/O address */ + DBGC ( &io_pages, "IO mapped %08lx+%zx to %p using PTEs " + "[%d-%d]\n", bus_addr, len, io_addr, first, + ( first + count - 1 ) ); + return io_addr; + } + + DBGC ( &io_pages, "IO could not map %08lx+%zx\n", bus_addr, len ); + return NULL; +} + +/** + * Unmap pages for I/O + * + * @v io_addr I/O address + */ +static void iounmap_pages ( volatile const void *io_addr ) { + volatile const void *invalidate = io_addr; + unsigned int first; + unsigned int i; + int is_last; + + DBGC ( &io_pages, "IO unmapping %p\n", io_addr ); + + /* Calculate first page table entry */ + first = ( ( io_addr - IO_BASE ) / IO_PAGE_SIZE ); + + /* Clear page table entries */ + for ( i = first ; ; i++ ) { + + /* Sanity check */ + assert ( io_pages.page[i] & PAGE_P ); + + /* Check if this is the last page in this allocation */ + is_last = ( io_pages.page[i] & PAGE_LAST ); + + /* Clear page table entry */ + io_pages.page[i] = 0; + + /* Invalidate TLB for this page */ + __asm__ __volatile__ ( "invlpg (%0)" : : "r" ( invalidate ) ); + invalidate += IO_PAGE_SIZE; + + /* Terminate if this was the last page */ + if ( is_last ) + break; + } + + DBGC ( &io_pages, "IO unmapped %p using PTEs [%d-%d]\n", + io_addr, first, i ); +} + +PROVIDE_UACCESS_INLINE ( librm, phys_to_user ); +PROVIDE_UACCESS_INLINE ( librm, user_to_phys ); +PROVIDE_UACCESS_INLINE ( librm, virt_to_user ); +PROVIDE_UACCESS_INLINE ( librm, user_to_virt ); +PROVIDE_UACCESS_INLINE ( librm, userptr_add ); +PROVIDE_UACCESS_INLINE ( librm, memcpy_user ); +PROVIDE_UACCESS_INLINE ( librm, memmove_user ); +PROVIDE_UACCESS_INLINE ( librm, memset_user ); +PROVIDE_UACCESS_INLINE ( librm, strlen_user ); +PROVIDE_UACCESS_INLINE ( librm, memchr_user ); +PROVIDE_IOMAP_INLINE ( pages, io_to_bus ); +PROVIDE_IOMAP ( pages, ioremap, ioremap_pages ); +PROVIDE_IOMAP ( pages, iounmap, iounmap_pages ); diff --git a/src/arch/i386/transitions/librm_test.c b/src/arch/x86/transitions/librm_test.c similarity index 78% rename from src/arch/i386/transitions/librm_test.c rename to src/arch/x86/transitions/librm_test.c index f1a517eda..77cf8022c 100644 --- a/src/arch/i386/transitions/librm_test.c +++ b/src/arch/x86/transitions/librm_test.c @@ -52,13 +52,13 @@ static struct profiler r2p_profiler __profiler = { .name = "r2p" }; /** Real-mode call profiler */ static struct profiler real_call_profiler __profiler = { .name = "real_call" }; -/** Protected-mode call profiler */ -static struct profiler prot_call_profiler __profiler = { .name = "prot_call" }; +/** Virtual call profiler */ +static struct profiler virt_call_profiler __profiler = { .name = "virt_call" }; /** - * Dummy protected-mode function + * Dummy function for profiling tests */ -static void librm_test_prot_call ( void ) { +static __asmcall void librm_test_call ( struct i386_all_regs *ix86 __unused ) { /* Do nothing */ } @@ -69,9 +69,11 @@ static void librm_test_prot_call ( void ) { static void librm_test_exec ( void ) { unsigned int i; unsigned long timestamp; - unsigned long started; - unsigned long stopped; - unsigned int discard_d; + uint32_t timestamp_lo; + uint32_t timestamp_hi; + uint32_t started; + uint32_t stopped; + uint32_t discard_d; /* Profile mode transitions. We want to profile each * direction of the transition separately, so perform an RDTSC @@ -81,8 +83,12 @@ static void librm_test_exec ( void ) { for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { profile_start ( &p2r_profiler ); __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" ) - : "=a" ( timestamp ), "=d" ( discard_d ) + : "=a" ( timestamp_lo ), + "=d" ( timestamp_hi ) : ); + timestamp = timestamp_lo; + if ( sizeof ( timestamp ) > sizeof ( timestamp_lo ) ) + timestamp |= ( ( ( uint64_t ) timestamp_hi ) << 32 ); profile_start_at ( &r2p_profiler, timestamp ); profile_stop ( &r2p_profiler ); profile_stop_at ( &p2r_profiler, timestamp ); @@ -91,24 +97,20 @@ static void librm_test_exec ( void ) { /* Profile complete real-mode call cycle */ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { profile_start ( &real_call_profiler ); - __asm__ __volatile__ ( REAL_CODE ( "" ) : : ); + __asm__ __volatile__ ( REAL_CODE ( "" ) : ); profile_stop ( &real_call_profiler ); } - /* Profile complete protected-mode call cycle */ + /* Profile complete virtual call cycle */ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" - "movl %0, %2\n\t" - "pushl %3\n\t" - "pushw %%cs\n\t" - "call prot_call\n\t" - "addw $4, %%sp\n\t" + "movl %k0, %k2\n\t" + VIRT_CALL ( librm_test_call ) "rdtsc\n\t" ) : "=a" ( stopped ), "=d" ( discard_d ), - "=r" ( started ) - : "i" ( librm_test_prot_call ) ); - profile_start_at ( &prot_call_profiler, started ); - profile_stop_at ( &prot_call_profiler, stopped ); + "=R" ( started ) : ); + profile_start_at ( &virt_call_profiler, started ); + profile_stop_at ( &virt_call_profiler, stopped ); } } diff --git a/src/arch/x86_64/Makefile b/src/arch/x86_64/Makefile index 48c0aa1af..b3064b752 100644 --- a/src/arch/x86_64/Makefile +++ b/src/arch/x86_64/Makefile @@ -7,16 +7,16 @@ CFLAGS += -fstrength-reduce -fomit-frame-pointer # CFLAGS += -falign-jumps=1 -falign-loops=1 -falign-functions=1 -# Use %rip-relative addressing wherever possible. -# -CFLAGS += -fpie - # Force 64-bit code # CFLAGS += -m64 ASFLAGS += --64 LDFLAGS += -m elf_x86_64 +# Prevent use of MMX and SSE registers +# +CFLAGS += -mno-mmx -mno-sse + # EFI requires -fshort-wchar, and nothing else currently uses wchar_t # CFLAGS += -fshort-wchar diff --git a/src/arch/x86_64/Makefile.efi b/src/arch/x86_64/Makefile.efi index 26b712780..0041bb8f0 100644 --- a/src/arch/x86_64/Makefile.efi +++ b/src/arch/x86_64/Makefile.efi @@ -1,5 +1,9 @@ # -*- makefile -*- : Force emacs to use Makefile mode +# Use %rip-relative addressing wherever possible. +# +CFLAGS += -fpie + # EFI probably doesn't guarantee us a red zone, so let's not rely on it. # CFLAGS += -mno-red-zone @@ -8,6 +12,10 @@ CFLAGS += -mno-red-zone # ELF2EFI = $(ELF2EFI64) +# Specify EFI boot file +# +EFI_BOOT_FILE = bootx64.efi + # Include generic EFI Makefile # MAKEDEPS += arch/x86/Makefile.efi diff --git a/src/arch/x86_64/Makefile.pcbios b/src/arch/x86_64/Makefile.pcbios new file mode 100644 index 000000000..ba4c8d8dc --- /dev/null +++ b/src/arch/x86_64/Makefile.pcbios @@ -0,0 +1,15 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Place .textdata in negative 2GB of address space +# +CFLAGS += -mcmodel=kernel +LDFLAGS += --section-start=.textdata=0xffffffffeb000000 + +# Assembly code does not respect a red zone. +# +CFLAGS += -mno-red-zone + +# Include generic BIOS Makefile +# +MAKEDEPS += arch/x86/Makefile.pcbios +include arch/x86/Makefile.pcbios diff --git a/src/arch/x86_64/core/gdbidt.S b/src/arch/x86_64/core/gdbidt.S new file mode 100644 index 000000000..89280bf89 --- /dev/null +++ b/src/arch/x86_64/core/gdbidt.S @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 + * + * GDB exception handlers + * + */ + +/* Size of a register */ +#define SIZEOF_REG 8 + +/* POSIX signal numbers for reporting traps to GDB */ +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGFPE 8 +#define SIGSTKFLT 16 + + .section ".text.gdbmach_interrupt", "ax", @progbits + .code64 + + .struct 0 +/* Register dump created for GDB stub */ +regs: +regs_rax: .space SIZEOF_REG +regs_rbx: .space SIZEOF_REG +regs_rcx: .space SIZEOF_REG +regs_rdx: .space SIZEOF_REG +regs_rsi: .space SIZEOF_REG +regs_rdi: .space SIZEOF_REG +regs_rbp: .space SIZEOF_REG +regs_rsp: .space SIZEOF_REG +regs_r8: .space SIZEOF_REG +regs_r9: .space SIZEOF_REG +regs_r10: .space SIZEOF_REG +regs_r11: .space SIZEOF_REG +regs_r12: .space SIZEOF_REG +regs_r13: .space SIZEOF_REG +regs_r14: .space SIZEOF_REG +regs_r15: .space SIZEOF_REG +regs_rip: .space SIZEOF_REG +regs_rflags: .space SIZEOF_REG +regs_cs: .space SIZEOF_REG +regs_ss: .space SIZEOF_REG +regs_ds: .space SIZEOF_REG +regs_es: .space SIZEOF_REG +regs_fs: .space SIZEOF_REG +regs_gs: .space SIZEOF_REG +regs_end: +/* GDB signal code */ +gdb: +gdb_code: .space SIZEOF_REG +gdb_end: +/* Long-mode exception frame */ +frame: +frame_rip: .space SIZEOF_REG +frame_cs: .space SIZEOF_REG +frame_rflags: .space SIZEOF_REG +frame_rsp: .space SIZEOF_REG +frame_ss: .space SIZEOF_REG +frame_end: + .previous + + .globl gdbmach_sigfpe +gdbmach_sigfpe: + push $SIGFPE + jmp gdbmach_interrupt + + .globl gdbmach_sigtrap +gdbmach_sigtrap: + push $SIGTRAP + jmp gdbmach_interrupt + + .globl gdbmach_sigstkflt +gdbmach_sigstkflt: + push $SIGSTKFLT + jmp gdbmach_interrupt + + .globl gdbmach_sigill +gdbmach_sigill: + push $SIGILL + jmp gdbmach_interrupt + +gdbmach_interrupt: + + /* Create register dump */ + pushq %gs + pushq %fs + pushq $0 /* %es unused in long mode */ + pushq $0 /* %ds unused in long mode */ + pushq ( frame_ss - regs_ss - SIZEOF_REG )(%rsp) + pushq ( frame_cs - regs_cs - SIZEOF_REG )(%rsp) + pushq ( frame_rflags - regs_rflags - SIZEOF_REG )(%rsp) + pushq ( frame_rip - regs_rip - SIZEOF_REG )(%rsp) + pushq %r15 + pushq %r14 + pushq %r13 + pushq %r12 + pushq %r11 + pushq %r10 + pushq %r9 + pushq %r8 + pushq ( frame_rsp - regs_rsp - SIZEOF_REG )(%rsp) + pushq %rbp + pushq %rdi + pushq %rsi + pushq %rdx + pushq %rcx + pushq %rbx + pushq %rax + + /* Call GDB stub exception handler */ + movq gdb_code(%rsp), %rdi + movq %rsp, %rsi + call gdbmach_handler + + /* Restore from register dump */ + popq %rax + popq %rbx + popq %rcx + popq %rdx + popq %rsi + popq %rdi + popq %rbp + popq ( frame_rsp - regs_rsp - SIZEOF_REG )(%rsp) + popq %r8 + popq %r9 + popq %r10 + popq %r11 + popq %r12 + popq %r13 + popq %r14 + popq %r15 + popq ( frame_rip - regs_rip - SIZEOF_REG )(%rsp) + popq ( frame_rflags - regs_rflags - SIZEOF_REG )(%rsp) + popq ( frame_cs - regs_cs - SIZEOF_REG )(%rsp) + popq ( frame_ss - regs_ss - SIZEOF_REG )(%rsp) + addq $( regs_fs - regs_ds ), %rsp /* skip %ds, %es */ + popq %fs + popq %gs + + /* Skip code */ + addq $( gdb_end - gdb_code ), %rsp /* skip code */ + + /* Return */ + iretq diff --git a/src/arch/x86_64/include/bits/compiler.h b/src/arch/x86_64/include/bits/compiler.h index f70b2e517..46985da3e 100644 --- a/src/arch/x86_64/include/bits/compiler.h +++ b/src/arch/x86_64/include/bits/compiler.h @@ -1,13 +1,15 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** Dummy relocation type */ #define RELOC_TYPE_NONE R_X86_64_NONE #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ -#define __asmcall __attribute__ (( regparm(0) )) +#define __asmcall __attribute__ (( used, regparm(0) )) /** Declare a function with libgcc implicit linkage */ #define __libgcc diff --git a/src/arch/x86_64/include/bits/hyperv.h b/src/arch/x86_64/include/bits/hyperv.h index 845c182f7..fa8bb3f93 100644 --- a/src/arch/x86_64/include/bits/hyperv.h +++ b/src/arch/x86_64/include/bits/hyperv.h @@ -44,32 +44,8 @@ hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, : "=a" ( result ), "+r" ( rcx ), "+r" ( rdx ), "+r" ( r8 ) : "m" ( hypercall ) - : "r9", "r10", "r11", "xmm0", "xmm1", "xmm2", - "xmm3", "xmm4", "xmm5" ); + : "r9", "r10", "r11" ); return result; } -/** - * Set bit atomically - * - * @v bits Bit field - * @v bit Bit to set - */ -static inline __attribute__ (( always_inline )) void -hv_set_bit ( void *bits, unsigned int bit ) { - struct { - uint64_t qword[ ( bit / 64 ) + 1 ]; - } *qwords = bits; - - /* Set bit using "lock bts". Inform compiler that any memory - * from the start of the bit field up to and including the - * qword containing this bit may be modified. (This is - * overkill but shouldn't matter in practice since we're - * unlikely to subsequently read other bits from the same bit - * field.) - */ - __asm__ __volatile__ ( "lock bts %1, %0" - : "+m" ( *qwords ) : "Ir" ( bit ) ); -} - #endif /* _BITS_HYPERV_H */ diff --git a/src/arch/x86_64/include/bits/nap.h b/src/arch/x86_64/include/bits/nap.h deleted file mode 100644 index 8b42c0a4a..000000000 --- a/src/arch/x86_64/include/bits/nap.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_NAP_H -#define _BITS_NAP_H - -/** @file - * - * x86_64-specific CPU sleeping API implementations - * - */ - -#include - -#endif /* _BITS_MAP_H */ diff --git a/src/arch/x86_64/include/bits/stdint.h b/src/arch/x86_64/include/bits/stdint.h index 9eb72e9c4..fe1f9946a 100644 --- a/src/arch/x86_64/include/bits/stdint.h +++ b/src/arch/x86_64/include/bits/stdint.h @@ -1,6 +1,8 @@ #ifndef _BITS_STDINT_H #define _BITS_STDINT_H +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + typedef __SIZE_TYPE__ size_t; typedef signed long ssize_t; typedef signed long off_t; diff --git a/src/arch/x86_64/include/bits/timer.h b/src/arch/x86_64/include/bits/timer.h deleted file mode 100644 index dfa6c270c..000000000 --- a/src/arch/x86_64/include/bits/timer.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _BITS_TIMER_H -#define _BITS_TIMER_H - -/** @file - * - * x86_64-specific timer API implementations - * - */ - -#endif /* _BITS_TIMER_H */ diff --git a/src/arch/x86_64/include/bits/umalloc.h b/src/arch/x86_64/include/bits/umalloc.h deleted file mode 100644 index 12bf949d1..000000000 --- a/src/arch/x86_64/include/bits/umalloc.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _BITS_UMALLOC_H -#define _BITS_UMALLOC_H - -/** @file - * - * x86_64-specific user memory allocation API implementations - * - */ - -#endif /* _BITS_UMALLOC_H */ diff --git a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h index 6511c1ad3..fb85b4405 100644 --- a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h @@ -33,14 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#define DHCP_ARCH_VENDOR_CLASS_ID \ - DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '9', ':', \ - 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86_64 -#define DHCP_ARCH_CLIENT_ARCHITECTURE \ - DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86_64 ) - -#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ #endif diff --git a/src/arch/x86_64/include/gdbmach.h b/src/arch/x86_64/include/gdbmach.h index 6dadbbdd3..367405fd6 100644 --- a/src/arch/x86_64/include/gdbmach.h +++ b/src/arch/x86_64/include/gdbmach.h @@ -14,16 +14,37 @@ typedef unsigned long gdbreg_t; -/* The register snapshot, this must be in sync with interrupt handler and the - * GDB protocol. */ +/* Register snapshot */ enum { - // STUB: don't expect this to work! - GDBMACH_EIP, - GDBMACH_EFLAGS, + GDBMACH_RAX, + GDBMACH_RBX, + GDBMACH_RCX, + GDBMACH_RDX, + GDBMACH_RSI, + GDBMACH_RDI, + GDBMACH_RBP, + GDBMACH_RSP, + GDBMACH_R8, + GDBMACH_R9, + GDBMACH_R10, + GDBMACH_R11, + GDBMACH_R12, + GDBMACH_R13, + GDBMACH_R14, + GDBMACH_R15, + GDBMACH_RIP, + GDBMACH_RFLAGS, + GDBMACH_CS, + GDBMACH_SS, + GDBMACH_DS, + GDBMACH_ES, + GDBMACH_FS, + GDBMACH_GS, GDBMACH_NREGS, - GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t ) }; +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + /* Breakpoint types */ enum { GDBMACH_BPMEM, @@ -33,21 +54,27 @@ enum { GDBMACH_AWATCH, }; +/* Exception vectors */ +extern void gdbmach_sigfpe ( void ); +extern void gdbmach_sigtrap ( void ); +extern void gdbmach_sigstkflt ( void ); +extern void gdbmach_sigill ( void ); + static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { - regs [ GDBMACH_EIP ] = pc; + regs[GDBMACH_RIP] = pc; } static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { - regs [ GDBMACH_EFLAGS ] &= ~( 1 << 8 ); /* Trace Flag (TF) */ - regs [ GDBMACH_EFLAGS ] |= ( step << 8 ); + regs[GDBMACH_RFLAGS] &= ~( 1 << 8 ); /* Trace Flag (TF) */ + regs[GDBMACH_RFLAGS] |= ( step << 8 ); } static inline void gdbmach_breakpoint ( void ) { __asm__ __volatile__ ( "int $3\n" ); } -extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ); - +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); extern void gdbmach_init ( void ); #endif /* GDBMACH_H */ diff --git a/src/arch/x86_64/include/limits.h b/src/arch/x86_64/include/limits.h index 8cf87b471..a1374a17f 100644 --- a/src/arch/x86_64/include/limits.h +++ b/src/arch/x86_64/include/limits.h @@ -1,6 +1,8 @@ #ifndef LIMITS_H #define LIMITS_H 1 +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /* Number of bits in a `char' */ #define CHAR_BIT 8 diff --git a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h new file mode 100644 index 000000000..e22f50b37 --- /dev/null +++ b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * 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. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 + +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ + +#endif diff --git a/src/config/cloud/aws.ipxe b/src/config/cloud/aws.ipxe new file mode 100644 index 000000000..2c96e3888 --- /dev/null +++ b/src/config/cloud/aws.ipxe @@ -0,0 +1,8 @@ +#!ipxe + +echo Amazon EC2 - iPXE boot via user-data +echo CPU: ${cpuvendor} ${cpumodel} +ifstat || +dhcp || +route || +chain -ar http://169.254.169.254/latest/user-data diff --git a/src/config/cloud/colour.h b/src/config/cloud/colour.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/console.h b/src/config/cloud/console.h new file mode 100644 index 000000000..dae18e556 --- /dev/null +++ b/src/config/cloud/console.h @@ -0,0 +1,31 @@ +/* + * Console configuration suitable for use in public cloud + * environments, or any environment where direct console access is not + * available. + * + */ + +/* Log to syslog(s) server + * + * The syslog server to be used must be specified via e.g. + * "set syslog 192.168.0.1". + */ +#define CONSOLE_SYSLOG +#define CONSOLE_SYSLOGS + +/* Log to serial port + * + * Note that the serial port output from an AWS EC2 virtual machine is + * generally available (as the "System Log") only after the instance + * has been stopped. + */ +#define CONSOLE_SERIAL + +/* Log to partition on local disk + * + * If all other log mechanisms fail then the VM boot disk containing + * the iPXE image can be detached and attached to another machine in + * the same cloud, allowing the log to be retrieved from the log + * partition. + */ +#define CONSOLE_INT13 diff --git a/src/config/cloud/crypto.h b/src/config/cloud/crypto.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/gce.ipxe b/src/config/cloud/gce.ipxe new file mode 100644 index 000000000..88e12b56b --- /dev/null +++ b/src/config/cloud/gce.ipxe @@ -0,0 +1,8 @@ +#!ipxe + +echo Google Compute Engine - iPXE boot via metadata +echo CPU: ${cpuvendor} ${cpumodel} +ifstat || +dhcp || +route || +chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot diff --git a/src/config/cloud/general.h b/src/config/cloud/general.h new file mode 100644 index 000000000..99028c147 --- /dev/null +++ b/src/config/cloud/general.h @@ -0,0 +1,4 @@ +/* Allow retrieval of metadata (such as an iPXE boot script) from + * Google Compute Engine metadata server. + */ +#define HTTP_HACK_GCE diff --git a/src/config/cloud/serial.h b/src/config/cloud/serial.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/settings.h b/src/config/cloud/settings.h new file mode 100644 index 000000000..34deeb070 --- /dev/null +++ b/src/config/cloud/settings.h @@ -0,0 +1,4 @@ +/* It can often be useful to know the CPU on which a cloud instance is + * running (e.g. to isolate problems with Azure AMD instances). + */ +#define CPUID_SETTINGS diff --git a/src/config/cloud/sideband.h b/src/config/cloud/sideband.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/cloud/usb.h b/src/config/cloud/usb.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/config.c b/src/config/config.c index 6f9e66cd4..2ca05dff7 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -182,12 +182,6 @@ REQUIRE_OBJECT ( efi_image ); #ifdef IMAGE_SDI REQUIRE_OBJECT ( sdi ); #endif -#ifdef IMAGE_PNM -REQUIRE_OBJECT ( pnm ); -#endif -#ifdef IMAGE_PNG -REQUIRE_OBJECT ( png ); -#endif /* * Drag in all requested commands @@ -254,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 @@ -278,6 +275,12 @@ REQUIRE_OBJECT ( ipstat_cmd ); #ifdef PROFSTAT_CMD REQUIRE_OBJECT ( profstat_cmd ); #endif +#ifdef NTP_CMD +REQUIRE_OBJECT ( ntp_cmd ); +#endif +#ifdef CERT_CMD +REQUIRE_OBJECT ( cert_cmd ); +#endif /* * Drag in miscellaneous objects @@ -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 diff --git a/src/config/config_asn1.c b/src/config/config_asn1.c new file mode 100644 index 000000000..c4419d04d --- /dev/null +++ b/src/config/config_asn1.c @@ -0,0 +1,39 @@ +/* + * 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 + +/** @file + * + * ASN.1 file format configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +#ifdef IMAGE_DER +REQUIRE_OBJECT ( der ); +#endif +#ifdef IMAGE_PEM +REQUIRE_OBJECT ( pem ); +#endif diff --git a/src/config/config_efi.c b/src/config/config_efi.c index 1f73dad4d..92678d12d 100644 --- a/src/config/config_efi.c +++ b/src/config/config_efi.c @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include /** @file @@ -45,3 +46,6 @@ REQUIRE_OBJECT ( efi_fbcon ); #ifdef CONSOLE_FRAMEBUFFER REQUIRE_OBJECT ( efi_fbcon ); #endif +#ifdef DOWNLOAD_PROTO_FILE +REQUIRE_OBJECT ( efi_local ); +#endif diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index de7a07c57..b5f7ddc9d 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -43,3 +43,6 @@ REQUIRE_OBJECT ( fcoe ); #ifdef NET_PROTO_STP REQUIRE_OBJECT ( stp ); #endif +#ifdef NET_PROTO_LACP +REQUIRE_OBJECT ( eth_slow ); +#endif diff --git a/src/config/config_http.c b/src/config/config_http.c index 3f198d228..4373ea2c0 100644 --- a/src/config/config_http.c +++ b/src/config/config_http.c @@ -40,6 +40,12 @@ 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 +#ifdef HTTP_HACK_GCE +REQUIRE_OBJECT ( httpgce ); +#endif diff --git a/src/config/config_infiniband.c b/src/config/config_infiniband.c index a742e7559..4da8fe219 100644 --- a/src/config/config_infiniband.c +++ b/src/config/config_infiniband.c @@ -37,3 +37,20 @@ PROVIDE_REQUIRING_SYMBOL(); #ifdef SANBOOT_PROTO_IB_SRP REQUIRE_OBJECT ( ib_srp ); #endif + +/* + * Drag in Infiniband-specific virtual network devices + */ +#ifdef VNIC_IPOIB +REQUIRE_OBJECT ( ipoib ); +#endif +#ifdef VNIC_XSIGO +REQUIRE_OBJECT ( xsigo ); +#endif + +/* + * Drag in Infiniband-specific commands + */ +#ifdef IBMGMT_CMD +REQUIRE_OBJECT ( ibmgmt_cmd ); +#endif diff --git a/src/config/config_pixbuf.c b/src/config/config_pixbuf.c new file mode 100644 index 000000000..f8ff59daf --- /dev/null +++ b/src/config/config_pixbuf.c @@ -0,0 +1,39 @@ +/* + * 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 + +/** @file + * + * Pixel buffer file format configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +#ifdef IMAGE_PNM +REQUIRE_OBJECT ( pnm ); +#endif +#ifdef IMAGE_PNG +REQUIRE_OBJECT ( png ); +#endif diff --git a/src/config/config_timer.c b/src/config/config_timer.c new file mode 100644 index 000000000..d53c39939 --- /dev/null +++ b/src/config/config_timer.c @@ -0,0 +1,51 @@ +/* + * 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 + +/** @file + * + * Timer configuration options + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in timers + */ +#ifdef TIMER_PCBIOS +REQUIRE_OBJECT ( bios_timer ); +#endif +#ifdef TIMER_RDTSC +REQUIRE_OBJECT ( rdtsc_timer ); +#endif +#ifdef TIMER_EFI +REQUIRE_OBJECT ( efi_timer ); +#endif +#ifdef TIMER_LINUX +REQUIRE_OBJECT ( linux_timer ); +#endif +#ifdef TIMER_ACPI +REQUIRE_OBJECT ( acpi_timer ); +#endif diff --git a/src/config/crypto.h b/src/config/crypto.h index bccfc04b8..1edcdce45 100644 --- a/src/config/crypto.h +++ b/src/config/crypto.h @@ -50,6 +50,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define TIMESTAMP_ERROR_MARGIN ( ( 12 * 60 + 30 ) * 60 ) +/** Default cross-signed certificate source + * + * This is the default location from which iPXE will attempt to + * download cross-signed certificates in order to complete a + * certificate chain. + */ +#define CROSSCERT "http://ca.ipxe.org/auto" + +/** Perform OCSP checks when applicable + * + * Some CAs provide non-functional OCSP servers, and some clients are + * forced to operate on networks without access to the OCSP servers. + * Allow the user to explicitly disable the use of OCSP checks. + */ +#define OCSP_CHECK + #include #include NAMED_CONFIG(crypto.h) #include diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 502bef1d9..74effa425 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -10,28 +10,46 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_EFI -#define IOAPI_X86 +#define IOMAP_VIRT #define PCIAPI_EFI #define CONSOLE_EFI #define TIMER_EFI -#define NAP_EFIX86 #define UMALLOC_EFI #define SMBIOS_EFI -#define SANBOOT_NULL +#define SANBOOT_EFI #define BOFM_EFI #define ENTROPY_EFI #define TIME_EFI #define REBOOT_EFI +#define ACPI_EFI + +#define DOWNLOAD_PROTO_FILE /* Local filesystem access */ #define IMAGE_EFI /* EFI image support */ #define IMAGE_SCRIPT /* iPXE script image support */ +#define SANBOOT_PROTO_ISCSI /* iSCSI protocol */ +#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 */ #define USB_HCD_UHCI /* UHCI USB host controller */ #define USB_EFI /* Provide EFI_USB_IO_PROTOCOL interface */ #define REBOOT_CMD /* Reboot command */ + +#if defined ( __i386__ ) || defined ( __x86_64__ ) +#define IOAPI_X86 +#define NAP_EFIX86 #define CPUID_CMD /* x86 CPU feature detection command */ +#endif + +#if defined ( __arm__ ) || defined ( __aarch64__ ) +#define IOAPI_ARM +#define NAP_EFIARM +#endif #endif /* CONFIG_DEFAULTS_EFI_H */ diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index bc5ba7851..75fd617f9 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -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 */ diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 3ed8343ce..21821c95c 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -21,6 +21,13 @@ 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 +#else +#define IOMAP_VIRT +#endif #define IMAGE_ELF /* ELF image support */ #define IMAGE_MULTIBOOT /* MultiBoot image support */ diff --git a/src/config/general.h b/src/config/general.h index ee15f6bf1..3c14a2cd0 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef NET_PROTO_IPV6 /* IPv6 protocol */ #undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */ #define NET_PROTO_STP /* Spanning Tree protocol */ +#define NET_PROTO_LACP /* Link Aggregation control protocol */ /* * PXE support @@ -57,6 +58,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ #undef DOWNLOAD_PROTO_NFS /* Network File System Protocol */ +//#undef DOWNLOAD_PROTO_FILE /* Local filesystem access */ /* * SAN boot protocols @@ -75,7 +77,9 @@ 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 */ /* * 802.11 cryptosystems and handshaking protocols @@ -109,7 +113,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define IMAGE_EFI /* EFI image support */ //#define IMAGE_SDI /* SDI image support */ //#define IMAGE_PNM /* PNM image support */ -//#define IMAGE_PNG /* PNG image support */ +#define IMAGE_PNG /* PNG image support */ +#define IMAGE_DER /* DER image support */ +#define IMAGE_PEM /* PEM image support */ /* * Command-line commands to include @@ -120,6 +126,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONFIG_CMD /* Option configuration console */ #define IFMGMT_CMD /* Interface management commands */ #define IWMGMT_CMD /* Wireless interface management commands */ +#define IBMGMT_CMD /* Infiniband management commands */ #define FCMGMT_CMD /* Fibre Channel management commands */ #define ROUTE_CMD /* Routing table management commands */ #define IMAGE_CMD /* Image management commands */ @@ -128,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 */ @@ -144,6 +152,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define CONSOLE_CMD /* Console command */ //#define IPSTAT_CMD /* IP statistics commands */ //#define PROFSTAT_CMD /* Profiling commands */ +//#define NTP_CMD /* NTP commands */ +//#define CERT_CMD /* Certificate management commands */ /* * ROM-specific options @@ -152,6 +162,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef NONPNP_HOOK_INT19 /* Hook INT19 on non-PnP BIOSes */ #define AUTOBOOT_ROM_FILTER /* Autoboot only devices matching our ROM */ +/* + * Virtual network devices + * + */ +#define VNIC_IPOIB /* Infiniband IPoIB virtual NICs */ +//#define VNIC_XSIGO /* Infiniband Xsigo virtual NICs */ + /* * Error message tables to include * @@ -176,6 +193,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef GDBUDP /* Remote GDB debugging over UDP * (both may be set) */ //#define EFI_DOWNGRADE_UX /* Downgrade UEFI user experience */ +#define TIVOLI_VMM_WORKAROUND /* Work around the Tivoli VMM's garbling of SSE + * registers when iPXE traps to it due to + * privileged instructions */ #include #include NAMED_CONFIG(general.h) diff --git a/src/config/qemu/general.h b/src/config/qemu/general.h index 30f60d3f7..a08449735 100644 --- a/src/config/qemu/general.h +++ b/src/config/qemu/general.h @@ -8,3 +8,8 @@ /* Work around missing EFI_PXE_BASE_CODE_PROTOCOL */ #define EFI_DOWNGRADE_UX + +/* The Tivoli VMM workaround causes a KVM emulation failure on hosts + * without unrestricted_guest support + */ +#undef TIVOLI_VMM_WORKAROUND diff --git a/src/config/settings.h b/src/config/settings.h index 01feaaa87..d9c86a384 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -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 #include NAMED_CONFIG(settings.h) diff --git a/src/core/acpi.c b/src/core/acpi.c index b0ccfa78d..e6912afa2 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include #include #include @@ -40,51 +42,326 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ +/** + * Compute ACPI table checksum + * + * @v table Any ACPI table + * @ret checksum 0 if checksum is good + */ +static uint8_t acpi_checksum ( userptr_t table ) { + struct acpi_header acpi; + uint8_t sum = 0; + uint8_t data = 0; + unsigned int i; + + /* 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; +} + /** * Fix up ACPI table checksum * * @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 ); + /* Update checksum */ + acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) ); +} + +/** + * Locate ACPI 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 ( 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; } - acpi->checksum -= sum; + + /* Read RSDT header */ + copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) ); + if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) { + DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n", + user_to_phys ( rsdt, 0 ) ); + DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi, + sizeof ( acpi ) ); + return UNULL; + } + len = le32_to_cpu ( acpi.length ); + if ( len < sizeof ( rsdtab->acpi ) ) { + DBGC ( rsdt, "RSDT %#08lx has invalid length:\n", + user_to_phys ( rsdt, 0 ) ); + DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi, + sizeof ( acpi ) ); + return UNULL; + } + + /* Calculate number of entries */ + count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) ); + + /* Search through entries */ + for ( i = 0 ; i < count ; i++ ) { + + /* Get table address */ + copy_from_user ( &entry, rsdt, + offsetof ( typeof ( *rsdtab ), entry[i] ), + sizeof ( entry ) ); + + /* Read table header */ + table = phys_to_user ( entry ); + copy_from_user ( &acpi.signature, table, 0, + sizeof ( acpi.signature ) ); + + /* Check table signature */ + if ( acpi.signature != cpu_to_le32 ( signature ) ) + continue; + + /* Check index */ + if ( index-- ) + continue; + + /* 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 ) ); + return table; + } + + DBGC ( rsdt, "RSDT %#08lx could not find %s\n", + user_to_phys ( rsdt, 0 ), acpi_name ( signature ) ); + return UNULL; +} + +/** + * Extract \_Sx value from DSDT/SSDT + * + * @v zsdt DSDT or SSDT + * @v signature Signature (e.g. "_S5_") + * @ret sx \_Sx value, or negative error + * + * In theory, extracting the \_Sx value from the DSDT/SSDT requires a + * full ACPI parser plus some heuristics to work around the various + * broken encodings encountered in real ACPI implementations. + * + * In practice, we can get the same result by scanning through the + * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first + * four bytes, removing any bytes with bit 3 set, and treating + * whatever is left as a little-endian value. This is one of the + * uglier hacks I have ever implemented, but it's still prettier than + * the ACPI specification itself. + */ +static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) { + struct acpi_header acpi; + union { + uint32_t dword; + uint8_t byte[4]; + } buf; + size_t offset; + size_t len; + unsigned int sx; + uint8_t *byte; + + /* Read table header */ + copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) ); + len = le32_to_cpu ( acpi.length ); + + /* Locate signature */ + for ( offset = sizeof ( acpi ) ; + ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */ + + sizeof ( buf ) /* value */ ) < len ) ; + offset++ ) { + + /* Check signature */ + copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) ); + if ( buf.dword != cpu_to_le32 ( signature ) ) + continue; + DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n", + user_to_phys ( zsdt, 0 ), acpi_name ( signature ), + offset ); + offset += sizeof ( buf ); + + /* Read first four bytes of value */ + copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ), + sizeof ( buf ) ); + DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing " + "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ), + acpi_name ( signature ), buf.byte[0], buf.byte[1], + buf.byte[2], buf.byte[3] ); + + /* Extract \Sx value. There are three potential + * encodings that we might encounter: + * + * - SLP_TYPa, SLP_TYPb, rsvd, rsvd + * + * - , SLP_TYPa, , SLP_TYPb, ... + * + * - , SLP_TYPa, SLP_TYPb, 0, 0 + * + * Since and both have bit + * 3 set, and valid SLP_TYPx must have bit 3 clear + * (since SLP_TYPx is a 3-bit field), we can just skip + * any bytes with bit 3 set. + */ + byte = &buf.byte[0]; + if ( *byte & 0x08 ) + byte++; + sx = *(byte++); + if ( *byte & 0x08 ) + byte++; + sx |= ( *byte << 8 ); + return sx; + } + + return -ENOENT; +} + +/** + * Extract \_Sx value from DSDT/SSDT + * + * @v signature Signature (e.g. "_S5_") + * @ret sx \_Sx value, or negative error + */ +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 ( FADT_SIGNATURE, 0 ); + if ( fadt ) { + copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); + dsdt = phys_to_user ( fadtab.dsdt ); + if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 ) + return sx; + } + + /* Try all SSDTs */ + for ( i = 0 ; ; i++ ) { + ssdt = acpi_find ( SSDT_SIGNATURE, i ); + if ( ! ssdt ) + break; + if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 ) + return sx; + } + + DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n", + user_to_phys ( rsdt, 0 ), acpi_name ( signature ) ); + return -ENOENT; } /****************************************************************************** * - * 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; } diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c new file mode 100644 index 000000000..7ba2e979f --- /dev/null +++ b/src/core/acpi_settings.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include + +/** 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, +}; diff --git a/src/core/debug.c b/src/core/debug.c index def5d8b09..9b2a823f5 100644 --- a/src/core/debug.c +++ b/src/core/debug.c @@ -194,8 +194,12 @@ static int dbg_autocolour ( unsigned long stream ) { * @v stream Message stream ID */ void dbg_autocolourise ( unsigned long stream ) { - dbg_printf ( "\033[%dm", - ( stream ? ( DBGCOL_MIN + dbg_autocolour ( stream ) ) :0)); + + if ( DBGCOL_MIN ) { + dbg_printf ( "\033[%dm", + ( stream ? + ( DBGCOL_MIN + dbg_autocolour ( stream ) ) : 0)); + } } /** @@ -203,5 +207,7 @@ void dbg_autocolourise ( unsigned long stream ) { * */ void dbg_decolourise ( void ) { - dbg_printf ( "\033[0m" ); + + if ( DBGCOL_MIN ) + dbg_printf ( "\033[0m" ); } diff --git a/src/core/downloader.c b/src/core/downloader.c index d745f3617..33737bfac 100644 --- a/src/core/downloader.c +++ b/src/core/downloader.c @@ -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; } @@ -136,9 +143,9 @@ static int downloader_progress ( struct downloader *downloader, * @v meta Data transfer metadata * @ret rc Return status code */ -static int downloader_xfer_deliver ( struct downloader *downloader, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { +static int downloader_deliver ( struct downloader *downloader, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { int rc; /* Add data to buffer */ @@ -160,16 +167,55 @@ static int downloader_xfer_deliver ( struct downloader *downloader, * @ret xferbuf Data transfer buffer, or NULL on error */ static struct xfer_buffer * -downloader_xfer_buffer ( struct downloader *downloader ) { +downloader_buffer ( struct downloader *downloader ) { /* Provide direct access to underlying data transfer buffer */ return &downloader->buffer; } +/** + * Redirect data transfer interface + * + * @v downloader Downloader + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +static int downloader_vredirect ( struct downloader *downloader, int type, + va_list args ) { + va_list tmp; + struct uri *uri; + int rc; + + /* Intercept redirects to a LOCATION_URI and update the image URI */ + if ( type == LOCATION_URI ) { + + /* Extract URI argument */ + va_copy ( tmp, args ); + uri = va_arg ( tmp, struct uri * ); + va_end ( tmp ); + + /* Set image URI */ + if ( ( rc = image_set_uri ( downloader->image, uri ) ) != 0 ) + goto err; + } + + /* Redirect to new location */ + if ( ( rc = xfer_vreopen ( &downloader->xfer, type, args ) ) != 0 ) + goto err; + + return 0; + + err: + downloader_finished ( downloader, rc ); + return rc; +} + /** Downloader data transfer interface operations */ static struct interface_operation downloader_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct downloader *, downloader_xfer_deliver ), - INTF_OP ( xfer_buffer, struct downloader *, downloader_xfer_buffer ), + INTF_OP ( xfer_deliver, struct downloader *, downloader_deliver ), + INTF_OP ( xfer_buffer, struct downloader *, downloader_buffer ), + INTF_OP ( xfer_vredirect, struct downloader *, downloader_vredirect ), INTF_OP ( intf_close, struct downloader *, downloader_finished ), }; diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c new file mode 100644 index 000000000..e6293099a --- /dev/null +++ b/src/core/dummy_sanboot.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include + +/** + * 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 ); diff --git a/src/core/exec.c b/src/core/exec.c index 2c2ade0a5..a13884b68 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -36,10 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include -#include -#include -#include #include /** @file @@ -573,8 +569,6 @@ static struct command_descriptor sleep_cmd = static int sleep_exec ( int argc, char **argv ) { struct sleep_options opts; unsigned int seconds; - unsigned long start; - unsigned long delay; int rc; /* Parse options */ @@ -586,14 +580,8 @@ static int sleep_exec ( int argc, char **argv ) { return rc; /* Delay for specified number of seconds */ - start = currticks(); - delay = ( seconds * TICKS_PER_SEC ); - while ( ( currticks() - start ) <= delay ) { - step(); - if ( iskey() && ( getchar() == CTRL_C ) ) - return -ECANCELED; - cpu_nap(); - } + if ( sleep ( seconds ) != 0 ) + return -ECANCELED; return 0; } diff --git a/src/core/gdbstub.c b/src/core/gdbstub.c index 6ad52d1a6..8b57ddf56 100644 --- a/src/core/gdbstub.c +++ b/src/core/gdbstub.c @@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); enum { POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */ - SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */ + SIZEOF_PAYLOAD = 512, /* buffer size of GDB payload data */ }; struct gdbstub { @@ -255,17 +255,20 @@ static void gdbstub_continue ( struct gdbstub *stub, int single_step ) { static void gdbstub_breakpoint ( struct gdbstub *stub ) { unsigned long args [ 3 ]; int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0; + int rc; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { gdbstub_send_errno ( stub, POSIX_EINVAL ); return; } - if ( gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], args [ 2 ], enable ) ) { - gdbstub_send_ok ( stub ); - } else { + if ( ( rc = gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], + args [ 2 ], enable ) ) != 0 ) { /* Not supported */ stub->len = 0; gdbstub_tx_packet ( stub ); + return; } + gdbstub_send_ok ( stub ); } static void gdbstub_rx_packet ( struct gdbstub *stub ) { diff --git a/src/core/getkey.c b/src/core/getkey.c index 0f0f8b7c3..0c280d23b 100644 --- a/src/core/getkey.c +++ b/src/core/getkey.c @@ -76,9 +76,14 @@ int getkey ( unsigned long timeout ) { if ( character != ESC ) return character; + character = getchar_timeout ( GETKEY_TIMEOUT ); + if ( character < 0 ) + return ESC; + + if ( isalpha ( character ) ) + return ( toupper ( character ) - 'A' + 1 ); + while ( ( character = getchar_timeout ( GETKEY_TIMEOUT ) ) >= 0 ) { - if ( character == '[' ) - continue; if ( isdigit ( character ) ) { n = ( ( n * 10 ) + ( character - '0' ) ); continue; diff --git a/src/core/image.c b/src/core/image.c index 529e3d72c..078ce1bb9 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -88,7 +88,6 @@ static void free_image ( struct refcnt *refcnt ) { * @ret image Executable image */ struct image * alloc_image ( struct uri *uri ) { - const char *name; struct image *image; int rc; @@ -99,23 +98,42 @@ struct image * alloc_image ( struct uri *uri ) { /* Initialise image */ ref_init ( &image->refcnt, free_image ); - if ( uri ) { - image->uri = uri_get ( uri ); - if ( uri->path ) { - name = basename ( ( char * ) uri->path ); - if ( ( rc = image_set_name ( image, name ) ) != 0 ) - goto err_set_name; - } - } + if ( uri && ( ( rc = image_set_uri ( image, uri ) ) != 0 ) ) + goto err_set_uri; return image; - err_set_name: + err_set_uri: image_put ( image ); err_alloc: return NULL; } +/** + * Set image URI + * + * @v image Image + * @v uri New image URI + * @ret rc Return status code + */ +int image_set_uri ( struct image *image, struct uri *uri ) { + const char *name; + int rc; + + /* Set name, if image does not already have one */ + if ( uri->path && ( ! ( image->name && image->name[0] ) ) ) { + name = basename ( ( char * ) uri->path ); + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + return rc; + } + + /* Update image URI */ + uri_put ( image->uri ); + image->uri = uri_get ( uri ); + + return 0; +} + /** * Set image name * @@ -173,7 +191,7 @@ static int image_probe ( struct image *image ) { image->type = type; DBGC ( image, "IMAGE %s is %s\n", image->name, type->name ); - break; + return 0; } DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, type->name, strerror ( rc ) ); @@ -463,27 +481,3 @@ int image_set_trust ( int require_trusted, int permanent ) { return 0; } - -/** - * Create pixel buffer from image - * - * @v image Image - * @v pixbuf Pixel buffer to fill in - * @ret rc Return status code - */ -int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { - int rc; - - /* Check that this image can be used to create a pixel buffer */ - if ( ! ( image->type && image->type->pixbuf ) ) - return -ENOTSUP; - - /* Try creating pixel buffer */ - if ( ( rc = image->type->pixbuf ( image, pixbuf ) ) != 0 ) { - DBGC ( image, "IMAGE %s could not create pixel buffer: %s\n", - image->name, strerror ( rc ) ); - return rc; - } - - return 0; -} diff --git a/src/core/interface.c b/src/core/interface.c index ba148c13d..402aa4541 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -79,9 +79,6 @@ struct interface null_intf = INTF_INIT ( null_intf_desc ); * The reference to the existing destination interface is dropped, a * reference to the new destination interface is obtained, and the * interface is updated to point to the new destination interface. - * - * Note that there is no "unplug" call; instead you must plug the - * interface into a null interface. */ void intf_plug ( struct interface *intf, struct interface *dest ) { DBGC ( INTF_COL ( intf ), @@ -113,7 +110,10 @@ void intf_plug_plug ( struct interface *a, struct interface *b ) { * @v intf Object interface */ void intf_unplug ( struct interface *intf ) { - intf_plug ( intf, &null_intf ); + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " unplug\n", + INTF_INTF_DBG ( intf, intf->dest ) ); + intf_put ( intf->dest ); + intf->dest = &null_intf; } /** @@ -271,6 +271,7 @@ void intf_close ( struct interface *intf, int rc ) { * unplugs the interface. */ void intf_shutdown ( struct interface *intf, int rc ) { + struct interface tmp; DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " shutting down (%s)\n", INTF_DBG ( intf ), strerror ( rc ) ); @@ -278,11 +279,50 @@ void intf_shutdown ( struct interface *intf, int rc ) { /* Block further operations */ intf_nullify ( intf ); - /* Notify destination of close */ - intf_close ( intf, rc ); + /* Transfer destination to temporary interface */ + tmp.dest = intf->dest; + intf->dest = &null_intf; - /* Unplug interface */ - intf_unplug ( intf ); + /* Notify destination of close via temporary interface */ + intf_close ( &tmp, rc ); + + /* Unplug temporary interface */ + intf_unplug ( &tmp ); +} + +/** + * Shut down multiple object interfaces + * + * @v intfs Object interfaces + * @v rc Reason for close + */ +void intfs_vshutdown ( va_list intfs, int rc ) { + struct interface *intf; + va_list tmp; + + /* Nullify all interfaces to avoid potential loops */ + va_copy ( tmp, intfs ); + while ( ( intf = va_arg ( tmp, struct interface * ) ) ) + intf_nullify ( intf ); + va_end ( tmp ); + + /* Shut down all interfaces */ + while ( ( intf = va_arg ( intfs, struct interface * ) ) ) + intf_shutdown ( intf, rc ); +} + +/** + * Shut down multiple object interfaces + * + * @v rc Reason for close + * @v ... Object interfaces + */ +void intfs_shutdown ( int rc, ... ) { + va_list intfs; + + va_start ( intfs, rc ); + intfs_vshutdown ( intfs, rc ); + va_end ( intfs ); } /** @@ -295,7 +335,6 @@ void intf_shutdown ( struct interface *intf, int rc ) { * blocked during shutdown. */ void intf_restart ( struct interface *intf, int rc ) { - struct interface_descriptor *desc = intf->desc; /* Shut down the interface */ intf_shutdown ( intf, rc ); @@ -309,7 +348,41 @@ void intf_restart ( struct interface *intf, int rc ) { * infinite loop as the intf_close() operations on each side * of the link call each other recursively. */ - intf->desc = desc; + intf_reinit ( intf ); +} + +/** + * Shut down and restart multiple object interfaces + * + * @v intfs Object interfaces + * @v rc Reason for close + */ +void intfs_vrestart ( va_list intfs, int rc ) { + struct interface *intf; + va_list tmp; + + /* Shut down all interfaces */ + va_copy ( tmp, intfs ); + intfs_vshutdown ( tmp, rc ); + va_end ( tmp ); + + /* Reinitialise all interfaces */ + while ( ( intf = va_arg ( intfs, struct interface * ) ) ) + intf_reinit ( intf ); +} + +/** + * Shut down and restart multiple object interfaces + * + * @v rc Reason for close + * @v ... Object interfaces + */ +void intfs_restart ( int rc, ... ) { + va_list intfs; + + va_start ( intfs, rc ); + intfs_vrestart ( intfs, rc ); + va_end ( intfs ); } /** diff --git a/src/core/iobuf.c b/src/core/iobuf.c index 3e52ada4f..0ee53e038 100644 --- a/src/core/iobuf.c +++ b/src/core/iobuf.c @@ -47,20 +47,45 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) { struct io_buffer *iobuf; + size_t padding; + size_t threshold; + unsigned int align_log2; void *data; - /* Align buffer length to ensure that struct io_buffer is aligned */ - len = ( len + __alignof__ ( *iobuf ) - 1 ) & - ~( __alignof__ ( *iobuf ) - 1 ); - - /* Round up alignment to the nearest power of two */ - align = ( 1 << fls ( align - 1 ) ); - - /* Allocate buffer plus descriptor as a single unit, unless - * doing so will push the total size over the alignment - * boundary. + /* Calculate padding required below alignment boundary to + * ensure that a correctly aligned inline struct io_buffer + * could fit (regardless of the requested offset). */ - if ( ( len + sizeof ( *iobuf ) ) <= align ) { + padding = ( sizeof ( *iobuf ) + __alignof__ ( *iobuf ) - 1 ); + + /* Round up requested alignment to at least the size of the + * padding, to simplify subsequent calculations. + */ + if ( align < padding ) + align = padding; + + /* Round up alignment to the nearest power of two, avoiding + * a potentially undefined shift operation. + */ + align_log2 = fls ( align - 1 ); + if ( align_log2 >= ( 8 * sizeof ( align ) ) ) + return NULL; + align = ( 1UL << align_log2 ); + + /* Calculate length threshold */ + assert ( align >= padding ); + threshold = ( align - padding ); + + /* Allocate buffer plus an inline descriptor as a single unit, + * unless doing so would push the total size over the + * alignment boundary. + */ + if ( len <= threshold ) { + + /* Round up buffer length to ensure that struct + * io_buffer is aligned. + */ + len += ( ( - len - offset ) & ( __alignof__ ( *iobuf ) - 1 ) ); /* Allocate memory for buffer plus descriptor */ data = malloc_dma_offset ( len + sizeof ( *iobuf ), align, diff --git a/src/core/iomap_virt.c b/src/core/iomap_virt.c new file mode 100644 index 000000000..c7f487274 --- /dev/null +++ b/src/core/iomap_virt.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +/** @file + * + * iPXE I/O mapping API using phys_to_virt() + * + */ + +#include + +PROVIDE_IOMAP_INLINE ( virt, ioremap ); +PROVIDE_IOMAP_INLINE ( virt, iounmap ); +PROVIDE_IOMAP_INLINE ( virt, io_to_bus ); diff --git a/src/core/lineconsole.c b/src/core/lineconsole.c index bb3bfafc9..0a72d1434 100644 --- a/src/core/lineconsole.c +++ b/src/core/lineconsole.c @@ -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; diff --git a/src/core/malloc.c b/src/core/malloc.c index d7c67823a..91c8e4d35 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -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 */ @@ -291,9 +298,17 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { */ actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ) ); + if ( ! actual_size ) { + /* The requested size is not permitted to be zero. A + * zero result at this point indicates that either the + * original requested size was zero, or that unsigned + * integer overflow has occurred. + */ + ptr = NULL; + goto done; + } assert ( actual_size >= size ); align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); - assert ( ( actual_size + align_mask ) > actual_size ); DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); @@ -302,7 +317,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { list_for_each_entry ( block, &free_blocks, list ) { pre_size = ( ( offset - virt_to_phys ( block ) ) & align_mask ); - if ( block->size < ( pre_size + actual_size ) ) + if ( ( block->size < pre_size ) || + ( ( block->size - pre_size ) < actual_size ) ) continue; post_size = ( block->size - pre_size - actual_size ); /* Split block into pre-block, block, and @@ -342,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 ) ); @@ -353,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 ); @@ -465,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(); @@ -506,6 +532,8 @@ void * realloc ( void *old_ptr, size_t new_size ) { if ( new_size ) { new_total_size = ( new_size + offsetof ( struct autosized_block, data ) ); + if ( new_total_size < new_size ) + return NULL; new_block = alloc_memblock ( new_total_size, 1, 0 ); if ( ! new_block ) return NULL; @@ -618,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; } /** @@ -645,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 */ diff --git a/src/core/memblock.c b/src/core/memblock.c deleted file mode 100644 index aecddc22c..000000000 --- a/src/core/memblock.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 ); - -/** @file - * - * Largest memory block - * - */ - -#include -#include -#include -#include - -/** - * Find largest usable memory region - * - * @ret start Start of region - * @ret len Length of region - */ -size_t largest_memblock ( userptr_t *start ) { - struct memory_map memmap; - struct memory_region *region; - physaddr_t max = ~( ( physaddr_t ) 0 ); - physaddr_t region_start; - physaddr_t region_end; - size_t region_len; - unsigned int i; - size_t len = 0; - - /* Avoid returning uninitialised data on error */ - *start = UNULL; - - /* Scan through all memory regions */ - get_memmap ( &memmap ); - for ( i = 0 ; i < memmap.count ; i++ ) { - region = &memmap.regions[i]; - DBG ( "Considering [%llx,%llx)\n", region->start, region->end ); - - /* Truncate block to maximum physical address */ - if ( region->start > max ) { - DBG ( "...starts after maximum address %lx\n", max ); - continue; - } - region_start = region->start; - if ( region->end > max ) { - DBG ( "...end truncated to maximum address %lx\n", max); - region_end = 0; /* =max, given the wraparound */ - } else { - region_end = region->end; - } - region_len = ( region_end - region_start ); - - /* Use largest block */ - if ( region_len > len ) { - DBG ( "...new best block found\n" ); - *start = phys_to_user ( region_start ); - len = region_len; - } - } - - return len; -} diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c index fab3e5f3a..c620a0343 100644 --- a/src/core/memmap_settings.c +++ b/src/core/memmap_settings.c @@ -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; + /* 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", - 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 ) ); + 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 ); diff --git a/src/core/monojob.c b/src/core/monojob.c index 817f21b2c..2f066331c 100644 --- a/src/core/monojob.c +++ b/src/core/monojob.c @@ -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 ) ); diff --git a/src/core/netbios.c b/src/core/netbios.c new file mode 100644 index 000000000..0d4e2086f --- /dev/null +++ b/src/core/netbios.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Michael Brown . + * + * 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 + * + * NetBIOS user names + * + */ + +#include +#include +#include + +/** + * Split NetBIOS [domain\]username into separate domain and username fields + * + * @v username NetBIOS [domain\]username string + * @ret domain Domain portion of string, or NULL if no domain present + * + * This function modifies the original string by removing the + * separator. The caller may restore the string using + * netbios_domain_undo(). + */ +const char * netbios_domain ( char **username ) { + char *domain_username = *username; + char *sep; + + /* Find separator, if present */ + sep = strchr ( domain_username, '\\' ); + if ( ! sep ) + return NULL; + + /* Overwrite separator with NUL terminator and update username string */ + *sep = '\0'; + *username = ( sep + 1 ); + + return domain_username; +} diff --git a/src/core/null_acpi.c b/src/core/null_acpi.c new file mode 100644 index 000000000..90c784855 --- /dev/null +++ b/src/core/null_acpi.c @@ -0,0 +1,3 @@ +#include + +PROVIDE_ACPI_INLINE ( null, acpi_find_rsdt ); diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c index 2f7522c6c..7c0680f58 100644 --- a/src/core/null_sanboot.c +++ b/src/core/null_sanboot.c @@ -26,8 +26,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -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 ); diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 66f60158c..3ddf94f3d 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -117,7 +117,7 @@ int parse_timeout ( char *text, unsigned long *value ) { return rc; /* Convert to a number of timer ticks */ - *value = ( ( value_ms * TICKS_PER_SEC ) / 1000 ); + *value = ( value_ms * TICKS_PER_MS ); return 0; } diff --git a/src/core/pixbuf.c b/src/core/pixbuf.c index 41e18f8dc..641a0fb53 100644 --- a/src/core/pixbuf.c +++ b/src/core/pixbuf.c @@ -30,7 +30,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include +#include #include +#include #include /** @@ -65,6 +67,12 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { pixbuf->height = height; pixbuf->len = ( width * height * sizeof ( uint32_t ) ); + /* Check for multiplication overflow */ + if ( ( width != 0 ) && + ( ( pixbuf->len / sizeof ( uint32_t ) ) / width ) != height ) { + goto err_overflow; + } + /* Allocate pixel data buffer */ pixbuf->data = umalloc ( pixbuf->len ); if ( ! pixbuf->data ) @@ -73,7 +81,38 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { return pixbuf; err_alloc_data: + err_overflow: pixbuf_put ( pixbuf ); err_alloc_pixbuf: return NULL; } + +/** + * Create pixel buffer from image + * + * @v image Image + * @v pixbuf Pixel buffer to fill in + * @ret rc Return status code + */ +int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { + int rc; + + /* Check that this image can be used to create a pixel buffer */ + if ( ! ( image->type && image->type->pixbuf ) ) + return -ENOTSUP; + + /* Try creating pixel buffer */ + if ( ( rc = image->type->pixbuf ( image, pixbuf ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not create pixel buffer: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/* Drag in objects via image_pixbuf() */ +REQUIRING_SYMBOL ( image_pixbuf ); + +/* Drag in pixel buffer image formats */ +REQUIRE_OBJECT ( config_pixbuf ); diff --git a/src/core/profile.c b/src/core/profile.c index 1075047b9..3655108ea 100644 --- a/src/core/profile.c +++ b/src/core/profile.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -122,8 +123,9 @@ void profile_update ( struct profiler *profiler, unsigned long sample ) { */ assert ( ( ( signed ) sample ) >= 0 ); - /* Update sample count */ - profiler->count++; + /* Update sample count, limiting to avoid signed overflow */ + if ( profiler->count < INT_MAX ) + profiler->count++; /* Adjust mean sample value scale if necessary. Skip if * sample is zero (in which case flsl(sample)-1 would diff --git a/src/core/quiesce.c b/src/core/quiesce.c new file mode 100644 index 000000000..5d2a919d0 --- /dev/null +++ b/src/core/quiesce.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 + +/** 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(); + } +} diff --git a/src/core/random.c b/src/core/random.c index a74175a79..975a03cf5 100644 --- a/src/core/random.c +++ b/src/core/random.c @@ -18,6 +18,8 @@ static int32_t rnd_seed = 0; */ void srandom ( unsigned int seed ) { rnd_seed = seed; + if ( ! rnd_seed ) + rnd_seed = 4; /* Chosen by fair dice roll */ } /** diff --git a/src/core/resolv.c b/src/core/resolv.c index 1e3182b0b..fab8def4b 100644 --- a/src/core/resolv.c +++ b/src/core/resolv.c @@ -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 diff --git a/src/core/sanboot.c b/src/core/sanboot.c new file mode 100644 index 000000000..cabc48430 --- /dev/null +++ b/src/core/sanboot.c @@ -0,0 +1,995 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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, + ¶ms ) ) != 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, +}; diff --git a/src/core/serial.c b/src/core/serial.c index 4ce025519..dd22f6731 100644 --- a/src/core/serial.c +++ b/src/core/serial.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include +#include #include #include #include diff --git a/src/core/settings.c b/src/core/settings.c index f6f62d226..3e5d416e7 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -331,6 +332,7 @@ struct settings * autovivify_child_settings ( struct settings *parent, &new_child->autovivified.refcnt ); settings = &new_child->autovivified.generic.settings; register_settings ( settings, parent, new_child->name ); + ref_put ( settings->refcnt ); return settings; } @@ -451,6 +453,8 @@ static void reprioritise_settings ( struct settings *settings ) { tmp_priority = fetch_intz_setting ( tmp, &priority_setting ); if ( priority > tmp_priority ) break; + if ( settings->order > tmp->order ) + break; } list_add_tail ( &settings->siblings, &tmp->siblings ); @@ -1474,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 int parse_setting_tag ( const char *name ) { +static uint64_t parse_setting_tag ( const char *name ) { char *tmp = ( ( char * ) name ); - unsigned int tag = 0; + uint64_t tag = 0; while ( 1 ) { tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) ); @@ -1784,7 +1788,7 @@ const struct setting_type setting_type_ipv6 __setting_type = { }; /** IPv6 settings scope */ -const struct settings_scope ipv6_scope; +const struct settings_scope dhcpv6_scope; /** * Integer setting type indices @@ -2232,6 +2236,10 @@ static int format_busdevfn_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { unsigned long busdevfn; + unsigned int seg; + unsigned int bus; + unsigned int slot; + unsigned int func; int check_len; /* Extract numeric value */ @@ -2240,9 +2248,14 @@ static int format_busdevfn_setting ( const struct setting_type *type __unused, return check_len; assert ( check_len == ( int ) raw_len ); + /* Extract PCI address components */ + seg = PCI_SEG ( busdevfn ); + bus = PCI_BUS ( busdevfn ); + slot = PCI_SLOT ( busdevfn ); + func = PCI_FUNC ( busdevfn ); + /* Format value */ - return snprintf ( buf, len, "%02lx:%02lx.%lx", PCI_BUS ( busdevfn ), - PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) ); + return snprintf ( buf, len, "%04x:%02x:%02x.%x", seg, bus, slot, func ); } /** PCI bus:dev.fn setting type */ @@ -2383,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", @@ -2416,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 @@ -2540,6 +2571,38 @@ struct builtin_setting version_builtin_setting __builtin_setting = { .fetch = version_fetch, }; +/** + * Fetch current time setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int unixtime_fetch ( void *data, size_t len ) { + uint32_t content; + + /* Return current time */ + content = htonl ( time(NULL) ); + if ( len > sizeof ( content ) ) + len = sizeof ( content ); + memcpy ( data, &content, len ); + return sizeof ( content ); +} + +/** Current time setting */ +const struct setting unixtime_setting __setting ( SETTING_MISC, unixtime ) = { + .name = "unixtime", + .description = "Seconds since the Epoch", + .type = &setting_type_uint32, + .scope = &builtin_scope, +}; + +/** Current time built-in setting */ +struct builtin_setting unixtime_builtin_setting __builtin_setting = { + .setting = &unixtime_setting, + .fetch = unixtime_fetch, +}; + /** * Fetch built-in setting * diff --git a/src/core/string.c b/src/core/string.c index 3e658e54e..5a185e635 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -81,7 +81,7 @@ void * generic_memmove ( void *dest, const void *src, size_t len ) { uint8_t *dest_bytes = ( dest + len ); if ( dest < src ) - return memcpy ( dest, src, len ); + return generic_memcpy ( dest, src, len ); while ( len-- ) *(--dest_bytes) = *(--src_bytes); return dest; diff --git a/src/core/time.c b/src/core/time.c index 29a924ebe..c353ac5bd 100644 --- a/src/core/time.c +++ b/src/core/time.c @@ -43,6 +43,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * 400. */ +/** Current system clock offset */ +signed long time_offset; + /** Days of week (for debugging) */ static const char *weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" diff --git a/src/core/timer.c b/src/core/timer.c index dbd89f12b..24745cef7 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -23,7 +23,52 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include +#include +#include +#include +#include +#include +#include +#include + +/** Current timer */ +static struct timer *timer; + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +unsigned long currticks ( void ) { + + /* Guard against use during early initialisation */ + if ( ! timer ) { + DBGC ( &timer, "TIMER currticks() called before initialisation " + "from %p\n", __builtin_return_address ( 0 ) ); + return 0; + } + + /* Use selected timer */ + return timer->currticks(); +} + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +void udelay ( unsigned long usecs ) { + + /* Guard against use during early initialisation */ + if ( ! timer ) { + DBGC ( &timer, "TIMER udelay() called before initialisation " + "from %p\n", __builtin_return_address ( 0 ) ); + return; + } + + /* Use selected timer */ + timer->udelay ( usecs ); +} /** * Delay for a fixed number of milliseconds @@ -31,17 +76,103 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v msecs Number of milliseconds for which to delay */ void mdelay ( unsigned long msecs ) { + + /* Guard against use during early initialisation */ + if ( ! timer ) { + DBGC ( &timer, "TIMER mdelay() called before initialisation " + "from %p\n", __builtin_return_address ( 0 ) ); + return; + } + + /* Delay for specified number of milliseconds */ while ( msecs-- ) udelay ( 1000 ); } /** - * Delay 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 + */ +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 ( interrupted && interrupted() ) + return secs; + cpu_nap(); + } + start = now; + } + + 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 */ -unsigned int sleep ( unsigned int secs ) { - while ( secs-- ) - mdelay ( 1000 ); - return 0; +void sleep_fixed ( unsigned int secs ) { + + sleep_interruptible ( secs, NULL ); } + +/** + * Find a working timer + * + */ +static void timer_probe ( void ) { + int rc; + + /* Use first working timer */ + for_each_table_entry ( timer, TIMERS ) { + if ( ( timer->probe == NULL ) || + ( ( rc = timer->probe() ) == 0 ) ) { + DBGC ( &timer, "TIMER using %s\n", timer->name ); + return; + } + DBGC ( &timer, "TIMER could not initialise %s: %s\n", + timer->name, strerror ( rc ) ); + } + + /* This is a fatal error */ + DBGC ( &timer, "TIMER found no working timers!\n" ); + while ( 1 ) {} +} + +/** Timer initialisation function */ +struct init_fn timer_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = timer_probe, +}; + +/* Drag in timer configuration */ +REQUIRING_SYMBOL ( timer_init_fn ); +REQUIRE_OBJECT ( config_timer ); diff --git a/src/core/uri.c b/src/core/uri.c index 4ae346856..73ad2b227 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -157,7 +157,7 @@ static int uri_character_escaped ( char c, unsigned int field ) { * the reparsing of the URI, allowing everything else * (e.g. ':', which will appear in iSCSI URIs). */ - [URI_OPAQUE] = "/#", + [URI_OPAQUE] = "#", /* User name: escape everything */ [URI_USER] = "/#:@?", /* Password: escape everything */ @@ -368,7 +368,7 @@ struct uri * parse_uri ( const char *uri_string ) { goto done; /* Identify net/absolute/relative path */ - if ( strncmp ( path, "//", 2 ) == 0 ) { + if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) { /* Net path. If this is terminated by the first '/' * of an absolute path, then we have no space for a * terminator after the authority field, so shuffle @@ -419,11 +419,11 @@ struct uri * parse_uri ( const char *uri_string ) { uri->port = tmp; } + done: /* Decode fields in-place */ for ( field = 0 ; field < URI_FIELDS ; field++ ) uri_decode_inplace ( uri, field ); - done: DBGC ( uri, "URI parsed \"%s\" to", uri_string ); uri_dump ( uri ); DBGC ( uri, "\n" ); @@ -456,10 +456,8 @@ unsigned int uri_port ( const struct uri *uri, unsigned int default_port ) { */ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { static const char prefixes[URI_FIELDS] = { - [URI_OPAQUE] = ':', [URI_PASSWORD] = ':', [URI_PORT] = ':', - [URI_PATH] = '/', [URI_QUERY] = '?', [URI_FRAGMENT] = '#', }; @@ -486,8 +484,6 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { prefix = prefixes[field]; if ( ( field == URI_HOST ) && ( uri->user != NULL ) ) prefix = '@'; - if ( ( field == URI_PATH ) && ( uri->path[0] == '/' ) ) - prefix = '\0'; if ( prefix ) { used += ssnprintf ( ( buf + used ), ( len - used ), "%c", prefix ); @@ -498,9 +494,9 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { ( buf + used ), ( len - used ) ); /* Suffix this field, if applicable */ - if ( ( field == URI_SCHEME ) && ( ! uri->opaque ) ) { + if ( field == URI_SCHEME ) { used += ssnprintf ( ( buf + used ), ( len - used ), - "://" ); + ":%s", ( uri->host ? "//" : "" ) ); } } @@ -606,7 +602,7 @@ struct uri * uri_dup ( const struct uri *uri ) { * * @v base_uri Base path * @v relative_uri Relative path - * @ret resolved_uri Resolved path + * @ret resolved_uri Resolved path, or NULL on failure * * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative * path (e.g. "initrd.gz") and produces a new path @@ -617,9 +613,8 @@ struct uri * uri_dup ( const struct uri *uri ) { */ char * resolve_path ( const char *base_path, const char *relative_path ) { - size_t base_len = ( strlen ( base_path ) + 1 ); - char base_path_copy[base_len]; - char *base_tmp = base_path_copy; + char *base_copy; + char *base_tmp; char *resolved; /* If relative path is absolute, just re-use it */ @@ -627,8 +622,12 @@ char * resolve_path ( const char *base_path, return strdup ( relative_path ); /* Create modifiable copy of path for dirname() */ - memcpy ( base_tmp, base_path, base_len ); - base_tmp = dirname ( base_tmp ); + base_copy = strdup ( base_path ); + if ( ! base_copy ) + return NULL; + + /* Strip filename portion of base path */ + base_tmp = dirname ( base_copy ); /* Process "./" and "../" elements */ while ( *relative_path == '.' ) { @@ -658,8 +657,8 @@ char * resolve_path ( const char *base_path, if ( asprintf ( &resolved, "%s%s%s", base_tmp, ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ? "" : "/" ), relative_path ) < 0 ) - return NULL; - + resolved = NULL; + free ( base_copy ); return resolved; } @@ -668,7 +667,7 @@ char * resolve_path ( const char *base_path, * * @v base_uri Base URI, or NULL * @v relative_uri Relative URI - * @ret resolved_uri Resolved URI + * @ret resolved_uri Resolved URI, or NULL on failure * * Takes a base URI (e.g. "http://ipxe.org/kernels/vmlinuz" and a * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI @@ -711,6 +710,55 @@ struct uri * resolve_uri ( const struct uri *base_uri, return new_uri; } +/** + * Construct TFTP URI from server address and filename + * + * @v sa_server Server address + * @v filename Filename + * @ret uri URI, or NULL on failure + */ +static struct uri * tftp_uri ( struct sockaddr *sa_server, + const char *filename ) { + struct sockaddr_tcpip *st_server = + ( ( struct sockaddr_tcpip * ) sa_server ); + char buf[ 6 /* "65535" + NUL */ ]; + char *path; + struct uri tmp; + struct uri *uri = NULL; + + /* Initialise TFTP URI */ + memset ( &tmp, 0, sizeof ( tmp ) ); + tmp.scheme = "tftp"; + + /* Construct TFTP server address */ + tmp.host = sock_ntoa ( sa_server ); + if ( ! tmp.host ) + goto err_host; + + /* Construct TFTP server port, if applicable */ + if ( st_server->st_port ) { + snprintf ( buf, sizeof ( buf ), "%d", + ntohs ( st_server->st_port ) ); + tmp.port = buf; + } + + /* Construct TFTP path */ + if ( asprintf ( &path, "/%s", filename ) < 0 ) + goto err_path; + tmp.path = path; + + /* Demangle URI */ + uri = uri_dup ( &tmp ); + if ( ! uri ) + goto err_uri; + + err_uri: + free ( path ); + err_path: + err_host: + return uri; +} + /** * Construct URI from server address and filename * @@ -724,10 +772,6 @@ struct uri * resolve_uri ( const struct uri *base_uri, * constructing a TFTP URI from the next-server and filename. */ struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) { - char buf[ 6 /* "65535" + NUL */ ]; - struct sockaddr_tcpip *st_server = - ( ( struct sockaddr_tcpip * ) sa_server ); - struct uri tmp; struct uri *uri; /* Fail if filename is empty */ @@ -745,17 +789,5 @@ struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) { uri_put ( uri ); /* Otherwise, construct a TFTP URI directly */ - memset ( &tmp, 0, sizeof ( tmp ) ); - tmp.scheme = "tftp"; - tmp.host = sock_ntoa ( sa_server ); - if ( ! tmp.host ) - return NULL; - if ( st_server->st_port ) { - snprintf ( buf, sizeof ( buf ), "%d", - ntohs ( st_server->st_port ) ); - tmp.port = buf; - } - tmp.path = filename; - uri = uri_dup ( &tmp ); - return uri; + return tftp_uri ( sa_server, filename ); } diff --git a/src/core/uuid.c b/src/core/uuid.c index b8d21de17..c43d4216f 100644 --- a/src/core/uuid.c +++ b/src/core/uuid.c @@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v uuid UUID * @ret string UUID in canonical form */ -char * uuid_ntoa ( const union uuid *uuid ) { +const char * uuid_ntoa ( const union uuid *uuid ) { static char buf[37]; /* "00000000-0000-0000-0000-000000000000" */ sprintf ( buf, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c index cb3bec5dd..9d3a97c2d 100644 --- a/src/core/vsprintf.c +++ b/src/core/vsprintf.c @@ -257,11 +257,13 @@ size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) { } else if ( *fmt == 's' ) { if ( length < &type_sizes[LONG_LEN] ) { ptr = va_arg ( args, char * ); + if ( ! ptr ) + ptr = ""; } else { wptr = va_arg ( args, wchar_t * ); + if ( ! wptr ) + ptr = ""; } - if ( ( ptr == NULL ) && ( wptr == NULL ) ) - ptr = ""; } else if ( *fmt == 'p' ) { intptr_t ptrval; diff --git a/src/core/xfer.c b/src/core/xfer.c index 112fee1bf..0faf3292a 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -81,12 +81,17 @@ int xfer_vredirect ( struct interface *intf, int type, va_list args ) { * xfer_vreopen(), we create a temporary interface in * order to be able to send xfer_window_changed() to * the parent. + * + * If redirection fails, then send intf_close() to the + * parent interface. */ intf_plug ( &tmp, dest ); rc = xfer_vreopen ( dest, type, args ); if ( rc == 0 ) { xfer_window_changed ( dest ); xfer_window_changed ( &tmp ); + } else { + intf_close ( &tmp, rc ); } intf_unplug ( &tmp ); } @@ -301,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 ) diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index aca12bf30..549ee4d86 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -81,23 +82,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EINFO_ENOTTY_ALGORITHM \ __einfo_uniqify ( EINFO_ENOTTY, 0x01, "Inappropriate algorithm" ) -/** - * Invalidate ASN.1 object cursor - * - * @v cursor ASN.1 object cursor - */ -void asn1_invalidate_cursor ( struct asn1_cursor *cursor ) { - static uint8_t asn1_invalid_object[] = { ASN1_END, 0 }; - - cursor->data = asn1_invalid_object; - cursor->len = 0; -} - /** * Start parsing ASN.1 object * * @v cursor ASN.1 object cursor * @v type Expected type, or ASN1_ANY + * @v extra Additional length not present within partial cursor * @ret len Length of object body, or negative error * * The object cursor will be updated to point to the start of the @@ -105,7 +95,7 @@ void asn1_invalidate_cursor ( struct asn1_cursor *cursor ) { * the length of the object body (i.e. the number of bytes until the * following object tag, if any) is returned. */ -static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { +int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { unsigned int len_len; unsigned int len; @@ -147,9 +137,9 @@ static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { cursor->data++; cursor->len--; } - if ( cursor->len < len ) { + if ( ( cursor->len + extra ) < len ) { DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n", - cursor, len, cursor->len ); + cursor, len, ( cursor->len + extra ) ); return -EINVAL_ASN1_LEN; } @@ -170,7 +160,7 @@ static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { int len; - len = asn1_start ( cursor, type ); + len = asn1_start ( cursor, type, 0 ); if ( len < 0 ) { asn1_invalidate_cursor ( cursor ); return len; @@ -197,7 +187,7 @@ int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) { int len; - len = asn1_start ( cursor, type ); + len = asn1_start ( cursor, type, 0 ); if ( len < 0 ) return len; @@ -254,7 +244,7 @@ int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) { /* Find end of object */ memcpy ( &temp, cursor, sizeof ( temp ) ); - len = asn1_start ( &temp, type ); + len = asn1_start ( &temp, type, 0 ); if ( len < 0 ) { asn1_invalidate_cursor ( cursor ); return len; @@ -749,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; @@ -849,3 +839,44 @@ int asn1_wrap ( struct asn1_builder *builder, unsigned int type ) { return 0; } + +/** + * Extract ASN.1 object from image + * + * @v image Image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +int image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Sanity check */ + assert ( offset <= image->len ); + + /* Check that this image can be used to extract an ASN.1 object */ + if ( ! ( image->type && image->type->asn1 ) ) + return -ENOTSUP; + + /* Try creating ASN.1 cursor */ + next = image->type->asn1 ( image, offset, cursor ); + if ( next < 0 ) { + rc = next; + DBGC ( image, "IMAGE %s could not extract ASN.1 object: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} + +/* Drag in objects via image_asn1() */ +REQUIRING_SYMBOL ( image_asn1 ); + +/* Drag in ASN.1 image formats */ +REQUIRE_OBJECT ( config_asn1 ); diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index 503ce499e..cdf6fb4dd 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -45,7 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CERT( _index, _path ) \ extern char stored_cert_ ## _index ## _data[]; \ extern char stored_cert_ ## _index ## _len[]; \ - __asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" \ + __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \ "\nstored_cert_" #_index "_data:\n\t" \ ".incbin \"" _path "\"\n\t" \ "\nstored_cert_" #_index "_end:\n\t" \ @@ -145,6 +145,24 @@ void certstore_add ( struct x509_certificate *cert ) { x509_name ( cert ) ); } +/** + * Remove certificate from store + * + * @v cert X.509 certificate + */ +void certstore_del ( struct x509_certificate *cert ) { + + /* Ignore attempts to remove permanent certificates */ + if ( cert->flags & X509_FL_PERMANENT ) + return; + + /* Remove certificate from store */ + DBGC ( &certstore, "CERTSTORE removed certificate %s\n", + x509_name ( cert ) ); + list_del ( &cert->store.list ); + x509_put ( cert ); +} + /** * Discard a stored certificate * @@ -157,14 +175,22 @@ static unsigned int certstore_discard ( void ) { * only reference is held by the store itself. */ list_for_each_entry_reverse ( cert, &certstore.links, store.list ) { - if ( cert->refcnt.count == 0 ) { - DBGC ( &certstore, "CERTSTORE discarded certificate " - "%s\n", x509_name ( cert ) ); - list_del ( &cert->store.list ); - x509_put ( cert ); - return 1; - } + + /* Skip certificates for which another reference is held */ + if ( cert->refcnt.count > 0 ) + continue; + + /* Skip certificates that were added at build time or + * added explicitly at run time. + */ + if ( cert->flags & ( X509_FL_PERMANENT | X509_FL_EXPLICIT ) ) + continue; + + /* Discard certificate */ + certstore_del ( cert ); + return 1; } + return 0; } @@ -214,6 +240,7 @@ static void certstore_init ( void ) { * permanent reference to it. */ certstore_add ( cert ); + cert->flags |= X509_FL_PERMANENT; DBGC ( &certstore, "CERTSTORE permanent certificate %d is %s\n", i, x509_name ( cert ) ); } diff --git a/src/crypto/drbg.c b/src/crypto/drbg.c index 5c8b5e612..a3366e806 100644 --- a/src/crypto/drbg.c +++ b/src/crypto/drbg.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c index 5acbc0258..ced6fd921 100644 --- a/src/crypto/entropy.c +++ b/src/crypto/entropy.c @@ -70,7 +70,8 @@ repetition_count_cutoff ( void ) { * where W is set at 2^(-30) (in ANS X9.82 Part 2 (October * 2011 Draft) Section 8.5.2.1.3.1). */ - max_repetitions = ( 1 + ( 30 / min_entropy_per_sample() ) ); + max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) / + min_entropy_per_sample() ) ); /* Round up to a whole number of repetitions. We don't have * the ceil() function available, so do the rounding by hand. @@ -237,7 +238,7 @@ adaptive_proportion_cutoff ( void ) { /* Look up cutoff value in cutoff table */ n = ADAPTIVE_PROPORTION_WINDOW_SIZE; - h = min_entropy_per_sample(); + h = ( min_entropy_per_sample() / MIN_ENTROPY_SCALE ); cutoff = adaptive_proportion_cutoff_lookup ( n, h ); /* Fail unless cutoff value is a build-time constant */ diff --git a/src/crypto/hash_df.c b/src/crypto/hash_df.c index c1417e683..dc0dc0ce8 100644 --- a/src/crypto/hash_df.c +++ b/src/crypto/hash_df.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c index 95a46195c..f898619c8 100644 --- a/src/crypto/hmac.c +++ b/src/crypto/hmac.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/hmac_drbg.c b/src/crypto/hmac_drbg.c index 6c1d5deb2..098297716 100644 --- a/src/crypto/hmac_drbg.c +++ b/src/crypto/hmac_drbg.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/md4.c b/src/crypto/md4.c new file mode 100644 index 000000000..f4a8d78df --- /dev/null +++ b/src/crypto/md4.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/** 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 ), +}; diff --git a/src/crypto/md5.c b/src/crypto/md5.c index f9738b0ac..185a61f35 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -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] + diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c new file mode 100644 index 000000000..870af2132 --- /dev/null +++ b/src/crypto/ntlm.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/** 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 ); +} diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index 5df55bc96..b83f4c035 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -209,10 +209,10 @@ static int ocsp_request ( struct ocsp_check *ocsp ) { static int ocsp_uri_string ( struct ocsp_check *ocsp ) { struct x509_ocsp_responder *responder = &ocsp->cert->extensions.auth_info.ocsp; - struct uri path_uri; - char *path_base64_string; - char *path_uri_string; - size_t path_len; + char *base64; + char *sep; + size_t base64_len; + size_t uri_len; size_t len; int rc; @@ -224,46 +224,44 @@ static int ocsp_uri_string ( struct ocsp_check *ocsp ) { goto err_no_uri; } - /* Base64-encode the request as the URI path */ - path_len = ( base64_encoded_len ( ocsp->request.builder.len ) - + 1 /* NUL */ ); - path_base64_string = malloc ( path_len ); - if ( ! path_base64_string ) { + /* Calculate base64-encoded request length */ + base64_len = ( base64_encoded_len ( ocsp->request.builder.len ) + + 1 /* NUL */ ); + + /* Allocate and construct the base64-encoded request */ + base64 = malloc ( base64_len ); + if ( ! base64 ) { rc = -ENOMEM; - goto err_path_base64; + goto err_alloc_base64; } base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len, - path_base64_string, path_len ); + base64, base64_len ); - /* URI-encode the Base64-encoded request */ - memset ( &path_uri, 0, sizeof ( path_uri ) ); - path_uri.path = path_base64_string; - path_uri_string = format_uri_alloc ( &path_uri ); - if ( ! path_uri_string ) { - rc = -ENOMEM; - goto err_path_uri; - } + /* Calculate URI-encoded base64-encoded request length */ + uri_len = ( uri_encode ( URI_PATH, base64, ( base64_len - 1 /* NUL */ ), + NULL, 0 ) + 1 /* NUL */ ); - /* Construct URI string */ - len = ( responder->uri.len + strlen ( path_uri_string ) + 1 /* NUL */ ); + /* Allocate and construct the URI string */ + len = ( responder->uri.len + 1 /* possible "/" */ + uri_len ); ocsp->uri_string = zalloc ( len ); if ( ! ocsp->uri_string ) { rc = -ENOMEM; - goto err_ocsp_uri; + goto err_alloc_uri; } memcpy ( ocsp->uri_string, responder->uri.data, responder->uri.len ); - strcpy ( &ocsp->uri_string[responder->uri.len], path_uri_string ); + sep = &ocsp->uri_string[ responder->uri.len - 1 ]; + if ( *sep != '/' ) + *(++sep) = '/'; + uri_encode ( URI_PATH, base64, base64_len, ( sep + 1 ), uri_len ); DBGC2 ( ocsp, "OCSP %p \"%s\" URI is %s\n", ocsp, x509_name ( ocsp->cert ), ocsp->uri_string ); /* Success */ rc = 0; - err_ocsp_uri: - free ( path_uri_string ); - err_path_uri: - free ( path_base64_string ); - err_path_base64: + err_alloc_uri: + free ( base64 ); + err_alloc_base64: err_no_uri: return rc; } @@ -284,7 +282,7 @@ int ocsp_check ( struct x509_certificate *cert, /* Sanity checks */ assert ( cert != NULL ); assert ( issuer != NULL ); - assert ( issuer->valid ); + assert ( x509_is_valid ( issuer ) ); /* Allocate and initialise check */ *ocsp = zalloc ( sizeof ( **ocsp ) ); diff --git a/src/crypto/privkey.c b/src/crypto/privkey.c index a6043bd1e..7ef04880f 100644 --- a/src/crypto/privkey.c +++ b/src/crypto/privkey.c @@ -54,7 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Raw private key data */ extern char private_key_data[]; extern char private_key_len[]; -__asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" +__asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" #ifdef PRIVATE_KEY ".incbin \"" PRIVATE_KEY "\"\n\t" @@ -69,6 +69,12 @@ struct asn1_cursor private_key = { .len = ( ( size_t ) private_key_len ), }; +/** Default private key */ +static struct asn1_cursor default_private_key = { + .data = private_key_data, + .len = ( ( size_t ) private_key_len ), +}; + /** Private key setting */ static struct setting privkey_setting __setting ( SETTING_CRYPTO, privkey ) = { .name = "privkey", @@ -92,8 +98,8 @@ static int privkey_apply_settings ( void ) { if ( ALLOW_KEY_OVERRIDE ) { /* Restore default private key */ - private_key.data = private_key_data; - private_key.len = ( ( size_t ) private_key_len ); + memcpy ( &private_key, &default_private_key, + sizeof ( private_key ) ); /* Fetch new private key, if any */ free ( key_data ); diff --git a/src/crypto/rbg.c b/src/crypto/rbg.c index 943b288c3..a5a77e3cd 100644 --- a/src/crypto/rbg.c +++ b/src/crypto/rbg.c @@ -19,6 +19,18 @@ * 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. + * + * Alternatively, you may distribute this code in source or binary + * form, with or without modification, provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the above disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the above + * disclaimer in the documentation and/or other materials provided + * with the distribution. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c index 00ea1647e..f7b9dcfb7 100644 --- a/src/crypto/rootcert.c +++ b/src/crypto/rootcert.c @@ -93,13 +93,14 @@ struct x509_root root_certificates = { * a rebuild. */ static void rootcert_init ( void ) { + static int initialised; void *external = NULL; int len; /* Allow trusted root certificates to be overridden only if * not explicitly specified at build time. */ - if ( ALLOW_TRUST_OVERRIDE ) { + if ( ALLOW_TRUST_OVERRIDE && ( ! initialised ) ) { /* Fetch copy of "trust" setting, if it exists. This * memory will never be freed. @@ -109,6 +110,9 @@ static void rootcert_init ( void ) { root_certificates.fingerprints = external; root_certificates.count = ( len / FINGERPRINT_LEN ); } + + /* Prevent subsequent modifications */ + initialised = 1; } DBGC ( &root_certificates, "ROOTCERT using %d %s certificate(s):\n", @@ -118,6 +122,6 @@ static void rootcert_init ( void ) { } /** Root certificate initialiser */ -struct init_fn rootcert_init_fn __init_fn ( INIT_LATE ) = { - .initialise = rootcert_init, +struct startup_fn rootcert_startup_fn __startup_fn ( STARTUP_LATE ) = { + .startup = rootcert_init, }; diff --git a/src/crypto/rsa.c b/src/crypto/rsa.c index 36109280d..7ac0bca59 100644 --- a/src/crypto/rsa.c +++ b/src/crypto/rsa.c @@ -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, diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 43a4ca17a..feb7e4a0a 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -39,6 +39,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include #include @@ -121,10 +123,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x0b, "No usable certificates" ) /** - * Get X.509 certificate name (for debugging) + * Get X.509 certificate display name * * @v cert X.509 certificate - * @ret name Name (for debugging) + * @ret name Display name */ const char * x509_name ( struct x509_certificate *cert ) { struct asn1_cursor *common_name = &cert->subject.common_name; @@ -1319,7 +1321,7 @@ int x509_validate ( struct x509_certificate *cert, root = &root_certificates; /* Return success if certificate has already been validated */ - if ( cert->valid ) + if ( x509_is_valid ( cert ) ) return 0; /* Fail if certificate is invalid at specified time */ @@ -1328,7 +1330,7 @@ int x509_validate ( struct x509_certificate *cert, /* Succeed if certificate is a trusted root certificate */ if ( x509_check_root ( cert, root ) == 0 ) { - cert->valid = 1; + cert->flags |= X509_FL_VALIDATED; cert->path_remaining = ( cert->extensions.basic.path_len + 1 ); return 0; } @@ -1341,7 +1343,7 @@ int x509_validate ( struct x509_certificate *cert, } /* Fail unless issuer has already been validated */ - if ( ! issuer->valid ) { + if ( ! x509_is_valid ( issuer ) ) { DBGC ( cert, "X509 %p \"%s\" ", cert, x509_name ( cert ) ); DBGC ( cert, "issuer %p \"%s\" has not yet been validated\n", issuer, x509_name ( issuer ) ); @@ -1361,8 +1363,7 @@ int x509_validate ( struct x509_certificate *cert, } /* Fail if OCSP is required */ - if ( cert->extensions.auth_info.ocsp.uri.len && - ( ! cert->extensions.auth_info.ocsp.good ) ) { + if ( ocsp_required ( cert ) ) { DBGC ( cert, "X509 %p \"%s\" requires an OCSP check\n", cert, x509_name ( cert ) ); return -EACCES_OCSP_REQUIRED; @@ -1375,7 +1376,7 @@ int x509_validate ( struct x509_certificate *cert, cert->path_remaining = max_path_remaining; /* Mark certificate as valid */ - cert->valid = 1; + cert->flags |= X509_FL_VALIDATED; DBGC ( cert, "X509 %p \"%s\" successfully validated using ", cert, x509_name ( cert ) ); @@ -1766,6 +1767,47 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time, return -EACCES_USELESS; } +/** + * Extract X.509 certificate object from image + * + * @v image Image + * @v offset Offset within image + * @ret cert X.509 certificate + * @ret next Offset to next image, or negative error + * + * On success, the caller holds a reference to the X.509 certificate, + * and is responsible for ultimately calling x509_put(). + */ +int image_x509 ( struct image *image, size_t offset, + struct x509_certificate **cert ) { + struct asn1_cursor *cursor; + int next; + int rc; + + /* Get ASN.1 object */ + next = image_asn1 ( image, offset, &cursor ); + if ( next < 0 ) { + rc = next; + goto err_asn1; + } + + /* Parse certificate */ + if ( ( rc = x509_certificate ( cursor->data, cursor->len, + cert ) ) != 0 ) + goto err_certificate; + + /* Free ASN.1 object */ + free ( cursor ); + + return next; + + x509_put ( *cert ); + err_certificate: + free ( cursor ); + err_asn1: + return rc; +} + /* Drag in objects via x509_validate() */ REQUIRING_SYMBOL ( x509_validate ); diff --git a/src/drivers/bitbash/mii_bit.c b/src/drivers/bitbash/mii_bit.c new file mode 100644 index 000000000..5f0ec04aa --- /dev/null +++ b/src/drivers/bitbash/mii_bit.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 Sylvie Barlow . + * + * 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 ); + +#include +#include +#include +#include + +/** + * Transfer bits over MII bit-bashing interface + * + * @v basher Bit basher + * @v mask Mask + * @v write Data to write + * @ret read Data read + */ +static uint32_t mii_bit_xfer ( struct bit_basher *basher, + uint32_t mask, uint32_t write ) { + uint32_t read = 0; + int bit; + + for ( ; mask ; mask >>= 1 ) { + + /* Delay */ + udelay ( 1 ); + + /* Write bit to basher */ + write_bit ( basher, MII_BIT_MDIO, ( write & mask ) ); + + /* Read bit from basher */ + bit = read_bit ( basher, MII_BIT_MDIO ); + read <<= 1; + read |= ( bit & 1 ); + + /* Set clock high */ + write_bit ( basher, MII_BIT_MDC, 1 ); + + /* Delay */ + udelay ( 1 ); + + /* Set clock low */ + write_bit ( basher, MII_BIT_MDC, 0 ); + } + return read; +} + +/** + * Read or write via MII bit-bashing interface + * + * @v basher Bit basher + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @v cmd Command + * @ret data Data read + */ +static unsigned int mii_bit_rw ( struct bit_basher *basher, + unsigned int phy, unsigned int reg, + unsigned int data, unsigned int cmd ) { + + /* Initiate drive for write */ + write_bit ( basher, MII_BIT_DRIVE, 1 ); + + /* Write start */ + mii_bit_xfer ( basher, MII_BIT_START_MASK, MII_BIT_START ); + + /* Write command */ + mii_bit_xfer ( basher, MII_BIT_CMD_MASK, cmd ); + + /* Write PHY address */ + mii_bit_xfer ( basher, MII_BIT_PHY_MASK, phy ); + + /* Write register address */ + mii_bit_xfer ( basher, MII_BIT_REG_MASK, reg ); + + /* Switch drive to read if applicable */ + write_bit ( basher, MII_BIT_DRIVE, ( cmd & MII_BIT_CMD_RW ) ); + + /* Allow space for turnaround */ + mii_bit_xfer ( basher, MII_BIT_SWITCH_MASK, MII_BIT_SWITCH ); + + /* Read or write data */ + data = mii_bit_xfer (basher, MII_BIT_DATA_MASK, data ); + + /* Initiate drive for read */ + write_bit ( basher, MII_BIT_DRIVE, 0 ); + + return data; +} + +/** + * Read from MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @ret data Data read, or negative error + */ +static int mii_bit_read ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg ) { + struct mii_bit_basher *miibit = + container_of ( mdio, struct mii_bit_basher, mdio ); + struct bit_basher *basher = &miibit->basher; + + return mii_bit_rw ( basher, phy, reg, 0, MII_BIT_CMD_READ ); +} + +/** + * Write to MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int mii_bit_write ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg, unsigned int data ) { + struct mii_bit_basher *miibit = + container_of ( mdio, struct mii_bit_basher, mdio ); + struct bit_basher *basher = &miibit->basher; + + mii_bit_rw ( basher, phy, reg, data, MII_BIT_CMD_WRITE ); + return 0; +} + +/** MII bit basher operations */ +static struct mii_operations mii_bit_op = { + .read = mii_bit_read, + .write = mii_bit_write, +}; + +/** + * Initialise bit-bashing interface + * + * @v miibit MII bit basher + */ +void init_mii_bit_basher ( struct mii_bit_basher *miibit ) { + mdio_init ( &miibit->mdio, &mii_bit_op ); +}; diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c index 6aabd766a..f9918363a 100644 --- a/src/drivers/block/ibft.c +++ b/src/drivers/block/ibft.c @@ -28,6 +28,7 @@ FILE_LICENCE ( BSD2 ); #include +#include #include #include #include @@ -38,6 +39,7 @@ FILE_LICENCE ( BSD2 ); #include #include #include +#include #include #include #include @@ -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,28 +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] ) ) ); - DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) ); + ibft_set_ipaddr_setting ( parent, &nic->dhcp, &dhcp_server_setting, 1 ); + 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 ); @@ -276,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; } @@ -298,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 */ @@ -313,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 * @@ -346,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 = \n" ); + DBG ( "iBFT target %d password = \n", target->header.index ); return 0; } @@ -381,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 = \n" ); + DBG ( "iBFT target %d reverse password = \n", + target->header.index ); return 0; } @@ -402,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; @@ -415,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, @@ -436,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, +}; diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index fd5f82b9f..f765c9762 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -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 */ diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c index 7edf69ace..ab4812519 100644 --- a/src/drivers/block/srp.c +++ b/src/drivers/block/srp.c @@ -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 ); diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 6fbedd940..06b36a770 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -175,7 +175,7 @@ void adjust_pci_device ( struct pci_device *pci ) { * @ret rc Return status code */ int pci_read_config ( struct pci_device *pci ) { - uint16_t busdevfn; + uint32_t busdevfn; uint8_t hdrtype; uint32_t tmp; @@ -203,8 +203,8 @@ int pci_read_config ( struct pci_device *pci ) { pci_read_bases ( pci ); /* Initialise generic device component */ - snprintf ( pci->dev.name, sizeof ( pci->dev.name ), - "PCI%02x:%02x.%x", PCI_BUS ( pci->busdevfn ), + snprintf ( pci->dev.name, sizeof ( pci->dev.name ), "%04x:%02x:%02x.%x", + PCI_SEG ( pci->busdevfn ), PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ) ); pci->dev.desc.bus_type = BUS_TYPE_PCI; pci->dev.desc.location = pci->busdevfn; @@ -232,7 +232,7 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { /* Determine number of PCI buses */ if ( ! end ) - end = PCI_BUSDEVFN ( pci_num_bus(), 0, 0 ); + end = PCI_BUSDEVFN ( 0, pci_num_bus(), 0, 0 ); /* Find next PCI device, if any */ for ( ; busdevfn < end ; busdevfn++ ) { diff --git a/src/drivers/bus/pci_settings.c b/src/drivers/bus/pci_settings.c index 1cb9fa5a3..98005559d 100644 --- a/src/drivers/bus/pci_settings.c +++ b/src/drivers/bus/pci_settings.c @@ -70,7 +70,7 @@ static int pci_settings_fetch ( struct settings *settings __unused, unsigned int i; /* Extract busdevfn, offset, and length from tag */ - tag_busdevfn = ( ( setting->tag >> 16 ) & 0xffff ); + tag_busdevfn = ( setting->tag >> 16 ); tag_offset = ( ( setting->tag >> 8 ) & 0xff ); tag_len = ( ( setting->tag >> 0 ) & 0xff ); diff --git a/src/drivers/bus/pciea.c b/src/drivers/bus/pciea.c new file mode 100644 index 000000000..aaa69cf4c --- /dev/null +++ b/src/drivers/bus/pciea.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 +#include +#include +#include + +/** @file + * + * PCI Enhanced Allocation + * + */ + +/** + * Locate PCI Enhanced Allocation BAR equivalent entry + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @ret offset PCI Enhanced Allocation entry offset, or negative error + */ +static int pciea_offset ( struct pci_device *pci, unsigned int bei ) { + uint8_t entries; + uint32_t desc; + unsigned int i; + int offset; + + /* Locate Enhanced Allocation capability */ + offset = pci_find_capability ( pci, PCI_CAP_ID_EA ); + if ( offset < 0 ) + return offset; + + /* Get number of entries */ + pci_read_config_byte ( pci, ( offset + PCIEA_ENTRIES ), &entries ); + entries &= PCIEA_ENTRIES_MASK; + + /* Locate first entry */ + offset += PCIEA_FIRST; + + /* Search for a matching entry */ + for ( i = 0 ; i < entries ; i++ ) { + + /* Read entry descriptor */ + pci_read_config_dword ( pci, offset, &desc ); + + /* Check for a matching entry */ + if ( ( desc & PCIEA_DESC_ENABLED ) && + ( bei == PCIEA_DESC_BEI ( desc ) ) ) + return offset; + + /* Move to next entry */ + offset += ( ( PCIEA_DESC_SIZE ( desc ) + 1 ) << 2 ); + } + + return -ENOENT; +} + +/** + * Read PCI Enhanced Allocation BAR equivalent value + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @v low_offset Offset to low dword of value + * @ret value BAR equivalent value + */ +static unsigned long pciea_bar_value ( struct pci_device *pci, unsigned int bei, + unsigned int low_offset ) { + uint32_t low; + uint32_t high; + int offset; + + /* Locate Enhanced Allocation offset for this BEI */ + offset = pciea_offset ( pci, bei ); + if ( offset < 0 ) + return 0; + + /* Read BAR equivalent */ + offset += low_offset; + pci_read_config_dword ( pci, offset, &low ); + if ( low & PCIEA_LOW_ATTR_64BIT ) { + offset += PCIEA_LOW_HIGH; + pci_read_config_dword ( pci, offset, &high ); + if ( high ) { + if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { + return ( ( ( uint64_t ) high << 32 ) | low ); + } else { + DBGC ( pci, PCI_FMT " unhandled 64-bit EA BAR " + "%08x%08x\n", + PCI_ARGS ( pci ), high, low ); + return 0; + } + } + } + return low; +} + +/** + * Find the start of a PCI Enhanced Allocation BAR equivalent + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @ret start BAR start address + * + * If the address exceeds the size of an unsigned long (i.e. if a + * 64-bit BAR has a non-zero high dword on a 32-bit machine), the + * return value will be zero. + */ +unsigned long pciea_bar_start ( struct pci_device *pci, unsigned int bei ) { + unsigned long base; + + base = pciea_bar_value ( pci, bei, PCIEA_LOW_BASE ); + return ( base & ~PCIEA_LOW_ATTR_MASK ); +} + +/** + * Find the size of a PCI Enhanced Allocation BAR equivalent + * + * @v pci PCI device + * @v bei BAR equivalent indicator + * @ret size BAR size + */ +unsigned long pciea_bar_size ( struct pci_device *pci, unsigned int bei ) { + unsigned long limit; + + limit = pciea_bar_value ( pci, bei, PCIEA_LOW_LIMIT ); + return ( limit ? ( ( limit | PCIEA_LOW_ATTR_MASK ) + 1 ) : 0 ); +} diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 82287fb86..3082d8a3d 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -3,6 +3,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +static int pci_find_capability_common ( struct pci_device *pci, + uint8_t pos, int cap ) { + uint8_t id; + int ttl = 48; + + while ( ttl-- && pos >= 0x40 ) { + pos &= ~3; + pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id ); + DBG ( "PCI Capability: %d\n", id ); + if ( id == 0xff ) + break; + if ( id == cap ) + return pos; + pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos ); + } + return 0; +} + /** * Look for a PCI capability * @@ -17,9 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int pci_find_capability ( struct pci_device *pci, int cap ) { uint16_t status; - uint8_t pos, id; + uint8_t pos; uint8_t hdr_type; - int ttl = 48; pci_read_config_word ( pci, PCI_STATUS, &status ); if ( ! ( status & PCI_STATUS_CAP_LIST ) ) @@ -36,17 +53,28 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos ); break; } - while ( ttl-- && pos >= 0x40 ) { - pos &= ~3; - pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id ); - DBG ( "PCI Capability: %d\n", id ); - if ( id == 0xff ) - break; - if ( id == cap ) - return pos; - pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos ); - } - return 0; + return pci_find_capability_common ( pci, pos, cap ); +} + +/** + * Look for another PCI capability + * + * @v pci PCI device to query + * @v pos Address of the current capability + * @v cap Capability code + * @ret address Address of capability, or 0 if not found + * + * Determine whether or not a device supports a given PCI capability + * starting the search at a given address within the device's PCI + * configuration space. Returns the address of the next capability + * structure within the device's PCI configuration space, or 0 if the + * device does not support another such capability. + */ +int pci_find_next_capability ( struct pci_device *pci, int pos, int cap ) { + uint8_t new_pos; + + pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &new_pos ); + return pci_find_capability_common ( pci, new_pos, cap ); } /** diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 63a7e46a6..d8db3849a 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -243,7 +243,6 @@ int usb_endpoint_described ( struct usb_endpoint *ep, struct usb_interface_descriptor *interface, unsigned int type, unsigned int index ) { struct usb_device *usb = ep->usb; - struct usb_port *port = usb->port; struct usb_endpoint_descriptor *desc; struct usb_endpoint_companion_descriptor *descx; unsigned int sizes; @@ -267,7 +266,7 @@ int usb_endpoint_described ( struct usb_endpoint *ep, /* Calculate interval */ if ( ( type & USB_ENDPOINT_ATTR_TYPE_MASK ) == USB_ENDPOINT_ATTR_INTERRUPT ) { - if ( port->speed >= USB_SPEED_HIGH ) { + if ( usb->speed >= USB_SPEED_HIGH ) { /* 2^(desc->interval-1) is a microframe count */ interval = ( 1 << ( desc->interval - 1 ) ); } else { @@ -485,7 +484,7 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request, assert ( iob_headroom ( iobuf ) >= sizeof ( *packet ) ); /* Fail immediately if device has been unplugged */ - if ( port->speed == USB_SPEED_NONE ) + if ( port->disconnected ) return -ENODEV; /* Reset endpoint if required */ @@ -534,7 +533,7 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc; /* Fail immediately if device has been unplugged */ - if ( port->speed == USB_SPEED_NONE ) + if ( port->disconnected ) return -ENODEV; /* Reset endpoint if required */ @@ -602,6 +601,7 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, */ int usb_prefill ( struct usb_endpoint *ep ) { struct io_buffer *iobuf; + size_t reserve = ep->reserve; size_t len = ( ep->len ? ep->len : ep->mtu ); unsigned int fill; int rc; @@ -615,11 +615,12 @@ int usb_prefill ( struct usb_endpoint *ep ) { for ( fill = 0 ; fill < ep->max ; fill++ ) { /* Allocate I/O buffer */ - iobuf = alloc_iob ( len ); + iobuf = alloc_iob ( reserve + len ); if ( ! iobuf ) { rc = -ENOMEM; goto err_alloc; } + iob_reserve ( iobuf, reserve ); /* Add to recycled buffer list */ list_add_tail ( &iobuf->list, &ep->recycled ); @@ -640,6 +641,7 @@ int usb_prefill ( struct usb_endpoint *ep ) { */ int usb_refill ( struct usb_endpoint *ep ) { struct io_buffer *iobuf; + size_t reserve = ep->reserve; size_t len = ( ep->len ? ep->len : ep->mtu ); int rc; @@ -653,9 +655,10 @@ int usb_refill ( struct usb_endpoint *ep ) { /* Get or allocate buffer */ if ( list_empty ( &ep->recycled ) ) { /* Recycled buffer list is empty; allocate new buffer */ - iobuf = alloc_iob ( len ); + iobuf = alloc_iob ( reserve + len ); if ( ! iobuf ) return -ENOMEM; + iob_reserve ( iobuf, reserve ); } else { /* Get buffer from recycled buffer list */ iobuf = list_first_entry ( &ep->recycled, @@ -840,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 @@ -861,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 ) { @@ -1002,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 ); @@ -1019,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; @@ -1187,6 +1226,11 @@ static int usb_probe ( struct usb_function *func, return -ENOENT; } + /* Record driver */ + func->driver = driver; + func->id = id; + func->dev.driver_name = id->name; + /* Probe driver */ if ( ( rc = driver->probe ( func, config ) ) != 0 ) { DBGC ( usb, "USB %s failed to probe driver %s: %s\n", @@ -1194,9 +1238,6 @@ static int usb_probe ( struct usb_function *func, return rc; } - /* Record driver */ - func->driver = driver; - func->dev.driver_name = id->name; return 0; } @@ -1492,8 +1533,9 @@ static int register_usb ( struct usb_device *usb ) { hub->name, port->address, strerror ( rc ) ); goto err_speed; } + usb->speed = port->speed; DBGC2 ( usb, "USB %s attached as %s-speed device\n", - usb->name, usb_speed_name ( port->speed ) ); + usb->name, usb_speed_name ( usb->speed ) ); /* Open device */ if ( ( rc = usb->host->open ( usb ) ) != 0 ) { @@ -1503,7 +1545,7 @@ static int register_usb ( struct usb_device *usb ) { } /* Describe control endpoint */ - mtu = USB_EP0_DEFAULT_MTU ( port->speed ); + mtu = USB_EP0_DEFAULT_MTU ( usb->speed ); usb_endpoint_describe ( &usb->control, USB_EP0_ADDRESS, USB_EP0_ATTRIBUTES, mtu, USB_EP0_BURST, USB_EP0_INTERVAL ); @@ -1554,7 +1596,7 @@ static int register_usb ( struct usb_device *usb ) { le16_to_cpu ( usb->device.product ), usb->device.class.class, usb->device.class.subclass, usb->device.class.protocol, usb_bcd ( le16_to_cpu ( usb->device.protocol ) ), - usb_speed_name ( port->speed ), usb->control.mtu ); + usb_speed_name ( usb->speed ), usb->control.mtu ); /* Configure device */ if ( ( rc = usb_autoconfigure ( usb ) ) != 0 ) @@ -1717,23 +1759,24 @@ static int usb_hotplugged ( struct usb_port *port ) { if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", hub->name, port->address, strerror ( rc ) ); - goto err_speed; + /* Treat as a disconnection */ + port->disconnected = 1; + port->speed = USB_SPEED_NONE; } /* Detach device, if applicable */ if ( port->attached && ( port->disconnected || ! port->speed ) ) usb_detached ( port ); + /* Clear any recorded disconnections */ + port->disconnected = 0; + /* Attach device, if applicable */ if ( port->speed && ( ! port->attached ) && ( ( rc = usb_attached ( port ) ) != 0 ) ) - goto err_attached; + return rc; - err_attached: - err_speed: - /* Clear any recorded disconnections */ - port->disconnected = 0; - return rc; + return 0; } /****************************************************************************** @@ -2232,12 +2275,12 @@ struct usb_port * usb_transaction_translator ( struct usb_device *usb ) { struct usb_device *parent; /* Navigate up to root hub. If we find a low-speed or - * full-speed port with a higher-speed parent device, then - * that port is the transaction translator. + * full-speed device with a higher-speed parent hub, then that + * device's port is the transaction translator. */ for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) { - if ( ( usb->port->speed <= USB_SPEED_FULL ) && - ( parent->port->speed > USB_SPEED_FULL ) ) + if ( ( usb->speed <= USB_SPEED_FULL ) && + ( parent->speed > USB_SPEED_FULL ) ) return usb->port; } diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index fbef067bc..402bf4f12 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -11,16 +11,47 @@ * */ +#include "errno.h" +#include "byteswap.h" #include "etherboot.h" #include "ipxe/io.h" -#include "ipxe/virtio-ring.h" +#include "ipxe/iomap.h" +#include "ipxe/pci.h" +#include "ipxe/reboot.h" #include "ipxe/virtio-pci.h" +#include "ipxe/virtio-ring.h" + +static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num) +{ + size_t queue_size = PAGE_MASK + vring_size(num); + size_t vdata_size = num * sizeof(void *); + + vq->queue = zalloc(queue_size + vdata_size); + if (!vq->queue) { + return -ENOMEM; + } + + /* vdata immediately follows the ring */ + vq->vdata = (void **)(vq->queue + queue_size); + + return 0; +} + +void vp_free_vq(struct vring_virtqueue *vq) +{ + if (vq->queue) { + free(vq->queue); + vq->queue = NULL; + vq->vdata = NULL; + } +} int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq) { struct vring * vr = &vq->vring; u16 num; + int rc; /* select the queue */ @@ -30,27 +61,26 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); if (!num) { - printf("ERROR: queue size is 0\n"); - return -1; - } - - if (num > MAX_QUEUE_NUM) { - printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); + DBG("VIRTIO-PCI ERROR: queue size is 0\n"); return -1; } /* check if the queue is already active */ if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { - printf("ERROR: queue already active\n"); + DBG("VIRTIO-PCI ERROR: queue already active\n"); return -1; } vq->queue_index = queue_index; /* initialize the queue */ - - vring_init(vr, num, (unsigned char*)&vq->queue); + rc = vp_alloc_vq(vq, num); + if (rc) { + DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n"); + return rc; + } + vring_init(vr, num, vq->queue); /* activate the queue * @@ -62,3 +92,347 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, return num; } + +#define CFG_POS(vdev, field) \ + (vdev->cfg_cap_pos + offsetof(struct virtio_pci_cfg_cap, field)) + +static void prep_pci_cfg_cap(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, + size_t offset, u32 length) +{ + pci_write_config_byte(vdev->pci, CFG_POS(vdev, cap.bar), region->bar); + pci_write_config_dword(vdev->pci, CFG_POS(vdev, cap.length), length); + pci_write_config_dword(vdev->pci, CFG_POS(vdev, cap.offset), + (intptr_t)(region->base + offset)); +} + +void vpm_iowrite8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u8 data, size_t offset) +{ + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + writeb(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + outb(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 1); + pci_write_config_byte(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); + break; + default: + assert(0); + break; + } +} + +void vpm_iowrite16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u16 data, size_t offset) +{ + data = cpu_to_le16(data); + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + writew(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + outw(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 2); + pci_write_config_word(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); + break; + default: + assert(0); + break; + } +} + +void vpm_iowrite32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u32 data, size_t offset) +{ + data = cpu_to_le32(data); + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + writel(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + outl(data, region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 4); + pci_write_config_dword(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); + break; + default: + assert(0); + break; + } +} + +u8 vpm_ioread8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset) +{ + uint8_t data; + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + data = readb(region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + data = inb(region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 1); + pci_read_config_byte(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); + break; + default: + assert(0); + data = 0; + break; + } + return data; +} + +u16 vpm_ioread16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset) +{ + uint16_t data; + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + data = readw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + data = inw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 2); + pci_read_config_word(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); + break; + default: + assert(0); + data = 0; + break; + } + return le16_to_cpu(data); +} + +u32 vpm_ioread32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset) +{ + uint32_t data; + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { + case VIRTIO_PCI_REGION_MEMORY: + data = readw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PORT: + data = inw(region->base + offset); + break; + case VIRTIO_PCI_REGION_PCI_CONFIG: + prep_pci_cfg_cap(vdev, region, offset, 4); + pci_read_config_dword(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); + break; + default: + assert(0); + data = 0; + break; + } + return le32_to_cpu(data); +} + +int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type) +{ + int pos; + uint8_t type, bar; + + for (pos = pci_find_capability(pci, PCI_CAP_ID_VNDR); + pos > 0; + pos = pci_find_next_capability(pci, pos, PCI_CAP_ID_VNDR)) { + + pci_read_config_byte(pci, pos + offsetof(struct virtio_pci_cap, + cfg_type), &type); + pci_read_config_byte(pci, pos + offsetof(struct virtio_pci_cap, + bar), &bar); + + /* Ignore structures with reserved BAR values */ + if (bar > 0x5) { + continue; + } + + if (type == cfg_type) { + return pos; + } + } + return 0; +} + +int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, + u32 align, u32 start, u32 size, + struct virtio_pci_region *region) +{ + u8 bar; + u32 offset, length, base_raw; + unsigned long base; + + pci_read_config_byte(pci, cap + offsetof(struct virtio_pci_cap, bar), &bar); + pci_read_config_dword(pci, cap + offsetof(struct virtio_pci_cap, offset), + &offset); + pci_read_config_dword(pci, cap + offsetof(struct virtio_pci_cap, length), + &length); + + if (length <= start) { + DBG("VIRTIO-PCI bad capability len %d (>%d expected)\n", length, start); + return -EINVAL; + } + if (length - start < minlen) { + DBG("VIRTIO-PCI bad capability len %d (>=%zd expected)\n", length, minlen); + return -EINVAL; + } + length -= start; + if (start + offset < offset) { + DBG("VIRTIO-PCI map wrap-around %d+%d\n", start, offset); + return -EINVAL; + } + offset += start; + if (offset & (align - 1)) { + DBG("VIRTIO-PCI offset %d not aligned to %d\n", offset, align); + return -EINVAL; + } + if (length > size) { + length = size; + } + + if (minlen + offset < minlen || + minlen + offset > pci_bar_size(pci, PCI_BASE_ADDRESS(bar))) { + DBG("VIRTIO-PCI map virtio %zd@%d out of range on bar %i length %ld\n", + minlen, offset, + bar, pci_bar_size(pci, PCI_BASE_ADDRESS(bar))); + return -EINVAL; + } + + region->base = NULL; + region->length = length; + region->bar = bar; + + base = pci_bar_start(pci, PCI_BASE_ADDRESS(bar)); + if (base) { + pci_read_config_dword(pci, PCI_BASE_ADDRESS(bar), &base_raw); + + if (base_raw & PCI_BASE_ADDRESS_SPACE_IO) { + /* Region accessed using port I/O */ + region->base = (void *)(base + offset); + region->flags = VIRTIO_PCI_REGION_PORT; + } else { + /* Region mapped into memory space */ + region->base = ioremap(base + offset, length); + region->flags = VIRTIO_PCI_REGION_MEMORY; + } + } + if (!region->base) { + /* Region accessed via PCI config space window */ + region->base = (void *)(intptr_t)offset; + region->flags = VIRTIO_PCI_REGION_PCI_CONFIG; + } + return 0; +} + +void virtio_pci_unmap_capability(struct virtio_pci_region *region) +{ + unsigned region_type = region->flags & VIRTIO_PCI_REGION_TYPE_MASK; + if (region_type == VIRTIO_PCI_REGION_MEMORY) { + iounmap(region->base); + } +} + +void vpm_notify(struct virtio_pci_modern_device *vdev, + struct vring_virtqueue *vq) +{ + vpm_iowrite16(vdev, &vq->notification, (u16)vq->queue_index, 0); +} + +int vpm_find_vqs(struct virtio_pci_modern_device *vdev, + unsigned nvqs, struct vring_virtqueue *vqs) +{ + unsigned i; + struct vring_virtqueue *vq; + u16 size, off; + u32 notify_offset_multiplier; + int err; + + if (nvqs > vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(num_queues))) { + return -ENOENT; + } + + /* Read notify_off_multiplier from config space. */ + pci_read_config_dword(vdev->pci, + vdev->notify_cap_pos + offsetof(struct virtio_pci_notify_cap, + notify_off_multiplier), + ¬ify_offset_multiplier); + + for (i = 0; i < nvqs; i++) { + /* Select the queue we're interested in */ + vpm_iowrite16(vdev, &vdev->common, (u16)i, COMMON_OFFSET(queue_select)); + + /* Check if queue is either not available or already active. */ + size = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_size)); + /* QEMU has a bug where queues don't revert to inactive on device + * reset. Skip checking the queue_enable field until it is fixed. + */ + if (!size /*|| vpm_ioread16(vdev, &vdev->common.queue_enable)*/) + return -ENOENT; + + if (size & (size - 1)) { + DBG("VIRTIO-PCI %p: bad queue size %d\n", vdev, size); + return -EINVAL; + } + + if (size > MAX_QUEUE_NUM) { + /* iPXE networking tends to be not perf critical so there's no + * need to accept large queue sizes. + */ + size = MAX_QUEUE_NUM; + } + + vq = &vqs[i]; + vq->queue_index = i; + + /* get offset of notification word for this vq */ + off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off)); + + err = vp_alloc_vq(vq, size); + if (err) { + DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev); + return err; + } + vring_init(&vq->vring, size, vq->queue); + + /* activate the queue */ + vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size)); + + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.desc), + COMMON_OFFSET(queue_desc_lo), + COMMON_OFFSET(queue_desc_hi)); + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.avail), + COMMON_OFFSET(queue_avail_lo), + COMMON_OFFSET(queue_avail_hi)); + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.used), + COMMON_OFFSET(queue_used_lo), + COMMON_OFFSET(queue_used_hi)); + + err = virtio_pci_map_capability(vdev->pci, + vdev->notify_cap_pos, 2, 2, + off * notify_offset_multiplier, 2, + &vq->notification); + if (err) { + return err; + } + } + + /* Select and activate all queues. Has to be done last: once we do + * this, there's no way to go back except reset. + */ + for (i = 0; i < nvqs; i++) { + vq = &vqs[i]; + vpm_iowrite16(vdev, &vdev->common, (u16)vq->queue_index, + COMMON_OFFSET(queue_select)); + vpm_iowrite16(vdev, &vdev->common, 1, COMMON_OFFSET(queue_enable)); + } + return 0; +} diff --git a/src/drivers/bus/virtio-ring.c b/src/drivers/bus/virtio-ring.c index e55b6d0ed..98e787e16 100644 --- a/src/drivers/bus/virtio-ring.c +++ b/src/drivers/bus/virtio-ring.c @@ -18,8 +18,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include "etherboot.h" #include "ipxe/io.h" -#include "ipxe/virtio-ring.h" #include "ipxe/virtio-pci.h" +#include "ipxe/virtio-ring.h" #define BUG() do { \ printf("BUG: failure at %s:%d/%s()!\n", \ @@ -122,7 +122,8 @@ void vring_add_buf(struct vring_virtqueue *vq, wmb(); } -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +void vring_kick(struct virtio_pci_modern_device *vdev, unsigned int ioaddr, + struct vring_virtqueue *vq, int num_added) { struct vring *vr = &vq->vring; @@ -130,7 +131,13 @@ void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) vr->avail->idx += num_added; mb(); - if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) - vp_notify(ioaddr, vq->queue_index); + if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) { + if (vdev) { + /* virtio 1.0 */ + vpm_notify(vdev, vq); + } else { + /* legacy virtio */ + vp_notify(ioaddr, vq->queue_index); + } + } } - diff --git a/src/drivers/infiniband/CIB_PRM.h b/src/drivers/infiniband/CIB_PRM.h new file mode 100755 index 000000000..d578f9b04 --- /dev/null +++ b/src/drivers/infiniband/CIB_PRM.h @@ -0,0 +1,1167 @@ +/* + * Copyright (C) 2013-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 ); + +#ifndef __CIB_PRM__ +#define __CIB_PRM__ + +typedef unsigned long long __be64; +typedef uint32_t __be32; +typedef uint16_t __be16; + +#define GOLAN_CMD_DATA_BLOCK_SIZE (1 << 9) +#define GOLAN_CMD_PAS_CNT (GOLAN_CMD_DATA_BLOCK_SIZE / sizeof(__be64)) +#define MAILBOX_STRIDE (1 << 10) +#define MAILBOX_MASK (MAILBOX_STRIDE - 1) + +#define GOLAN_PCI_CMD_XPORT 7 +#define CMD_OWNER_HW 0x1 +#define GOLAN_LOG_MAX_QP 0x1 +#define IB_NUM_PKEYS 0x20 + +struct health_buffer { + __be32 assert_var[5]; + __be32 rsvd0[3]; + __be32 assert_exit_ptr; + __be32 assert_callra; + __be32 rsvd1[2]; + __be32 fw_ver; + __be32 hw_id; + __be32 rsvd2; + u8 irisc_index; + u8 synd; + __be16 ext_sync; +} __attribute ( ( packed ) ); + +struct golan_hca_init_seg { + __be32 fw_rev; + __be32 cmdif_rev_fw_sub; + __be32 rsvd0[2]; + __be32 cmdq_addr_h; + __be32 cmdq_addr_l_sz; + __be32 cmd_dbell; + __be32 rsvd1[121]; + struct health_buffer health; + __be32 rsvd2[884]; + __be32 health_counter; + __be32 rsvd3[1023]; + __be64 ieee1588_clk; + __be32 ieee1588_clk_type; + __be32 clr_intx; +} __attribute ( ( packed ) ); + +enum golan_manage_pages_mode { + GOLAN_PAGES_CANT_GIVE = 0, + GOLAN_PAGES_GIVE = 1, + GOLAN_PAGES_TAKE = 2 +}; + +enum golan_qry_pages_mode { + GOLAN_BOOT_PAGES = 0x1, + GOLAN_INIT_PAGES = 0x2, + GOLAN_REG_PAGES = 0x3, +}; + +enum { + GOLAN_REG_PCAP = 0x5001, + GOLAN_REG_PMTU = 0x5003, + GOLAN_REG_PTYS = 0x5004, + GOLAN_REG_PAOS = 0x5006, + GOLAN_REG_PMAOS = 0x5012, + GOLAN_REG_PUDE = 0x5009, + GOLAN_REG_PMPE = 0x5010, + GOLAN_REG_PELC = 0x500e, + GOLAN_REG_PMLP = 0, /* TBD */ + GOLAN_REG_NODE_DESC = 0x6001, + GOLAN_REG_HOST_ENDIANESS = 0x7004, +}; + +enum { + GOLAN_CMD_OP_QUERY_HCA_CAP = 0x100, + GOLAN_CMD_OP_QUERY_ADAPTER = 0x101, + GOLAN_CMD_OP_INIT_HCA = 0x102, + GOLAN_CMD_OP_TEARDOWN_HCA = 0x103, + GOLAN_CMD_OP_ENABLE_HCA = 0x104, + GOLAN_CMD_OP_DISABLE_HCA = 0x105, + + GOLAN_CMD_OP_QUERY_PAGES = 0x107, + GOLAN_CMD_OP_MANAGE_PAGES = 0x108, + GOLAN_CMD_OP_SET_HCA_CAP = 0x109, + + GOLAN_CMD_OP_CREATE_MKEY = 0x200, + GOLAN_CMD_OP_QUERY_MKEY = 0x201, + GOLAN_CMD_OP_DESTROY_MKEY = 0x202, + GOLAN_CMD_OP_QUERY_SPECIAL_CONTEXTS = 0x203, + + GOLAN_CMD_OP_CREATE_EQ = 0x301, + GOLAN_CMD_OP_DESTROY_EQ = 0x302, + GOLAN_CMD_OP_QUERY_EQ = 0x303, + + GOLAN_CMD_OP_CREATE_CQ = 0x400, + GOLAN_CMD_OP_DESTROY_CQ = 0x401, + GOLAN_CMD_OP_QUERY_CQ = 0x402, + GOLAN_CMD_OP_MODIFY_CQ = 0x403, + + GOLAN_CMD_OP_CREATE_QP = 0x500, + GOLAN_CMD_OP_DESTROY_QP = 0x501, + GOLAN_CMD_OP_RST2INIT_QP = 0x502, + GOLAN_CMD_OP_INIT2RTR_QP = 0x503, + GOLAN_CMD_OP_RTR2RTS_QP = 0x504, + GOLAN_CMD_OP_RTS2RTS_QP = 0x505, + GOLAN_CMD_OP_SQERR2RTS_QP = 0x506, + GOLAN_CMD_OP_2ERR_QP = 0x507, + GOLAN_CMD_OP_RTS2SQD_QP = 0x508, + GOLAN_CMD_OP_SQD2RTS_QP = 0x509, + GOLAN_CMD_OP_2RST_QP = 0x50a, + GOLAN_CMD_OP_QUERY_QP = 0x50b, + GOLAN_CMD_OP_CONF_SQP = 0x50c, + GOLAN_CMD_OP_MAD_IFC = 0x50d, + GOLAN_CMD_OP_INIT2INIT_QP = 0x50e, + GOLAN_CMD_OP_SUSPEND_QP = 0x50f, + GOLAN_CMD_OP_UNSUSPEND_QP = 0x510, + GOLAN_CMD_OP_SQD2SQD_QP = 0x511, + GOLAN_CMD_OP_ALLOC_QP_COUNTER_SET = 0x512, + GOLAN_CMD_OP_DEALLOC_QP_COUNTER_SET = 0x513, + GOLAN_CMD_OP_QUERY_QP_COUNTER_SET = 0x514, + + GOLAN_CMD_OP_CREATE_PSV = 0x600, + GOLAN_CMD_OP_DESTROY_PSV = 0x601, + GOLAN_CMD_OP_QUERY_PSV = 0x602, + GOLAN_CMD_OP_QUERY_SIG_RULE_TABLE = 0x603, + GOLAN_CMD_OP_QUERY_BLOCK_SIZE_TABLE = 0x604, + + GOLAN_CMD_OP_CREATE_SRQ = 0x700, + GOLAN_CMD_OP_DESTROY_SRQ = 0x701, + GOLAN_CMD_OP_QUERY_SRQ = 0x702, + GOLAN_CMD_OP_ARM_RQ = 0x703, + GOLAN_CMD_OP_RESIZE_SRQ = 0x704, + + GOLAN_CMD_OP_QUERY_HCA_VPORT_CONTEXT = 0x762, + GOLAN_CMD_OP_QUERY_HCA_VPORT_GID = 0x764, + GOLAN_CMD_OP_QUERY_HCA_VPORT_PKEY = 0x765, + + GOLAN_CMD_OP_ALLOC_PD = 0x800, + GOLAN_CMD_OP_DEALLOC_PD = 0x801, + GOLAN_CMD_OP_ALLOC_UAR = 0x802, + GOLAN_CMD_OP_DEALLOC_UAR = 0x803, + + GOLAN_CMD_OP_ATTACH_TO_MCG = 0x806, + GOLAN_CMD_OP_DETACH_FROM_MCG = 0x807, + + + GOLAN_CMD_OP_ALLOC_XRCD = 0x80e, + GOLAN_CMD_OP_DEALLOC_XRCD = 0x80f, + + GOLAN_CMD_OP_ACCESS_REG = 0x805, +}; + +struct golan_inbox_hdr { + __be16 opcode; + u8 rsvd[4]; + __be16 opmod; +} __attribute ( ( packed ) ); + +struct golan_cmd_layout { + u8 type; + u8 rsvd0[3]; + __be32 inlen; + union { + __be64 in_ptr; + __be32 in_ptr32[2]; + }; + __be32 in[4]; + __be32 out[4]; + union { + __be64 out_ptr; + __be32 out_ptr32[2]; + }; + __be32 outlen; + u8 token; + u8 sig; + u8 rsvd1; + volatile u8 status_own; +} __attribute ( ( packed ) ); + +struct golan_outbox_hdr { + u8 status; + u8 rsvd[3]; + __be32 syndrome; +} __attribute ( ( packed ) ); + +enum { + GOLAN_DEV_CAP_FLAG_RC = 1LL << 0, + GOLAN_DEV_CAP_FLAG_UC = 1LL << 1, + GOLAN_DEV_CAP_FLAG_UD = 1LL << 2, + GOLAN_DEV_CAP_FLAG_XRC = 1LL << 3, + GOLAN_DEV_CAP_FLAG_SRQ = 1LL << 6, + GOLAN_DEV_CAP_FLAG_BAD_PKEY_CNTR = 1LL << 8, + GOLAN_DEV_CAP_FLAG_BAD_QKEY_CNTR = 1LL << 9, + GOLAN_DEV_CAP_FLAG_APM = 1LL << 17, + GOLAN_DEV_CAP_FLAG_ATOMIC = 1LL << 18, + GOLAN_DEV_CAP_FLAG_ON_DMND_PG = 1LL << 24, + GOLAN_DEV_CAP_FLAG_RESIZE_SRQ = 1LL << 32, + GOLAN_DEV_CAP_FLAG_REMOTE_FENCE = 1LL << 38, + GOLAN_DEV_CAP_FLAG_TLP_HINTS = 1LL << 39, + GOLAN_DEV_CAP_FLAG_SIG_HAND_OVER = 1LL << 40, + GOLAN_DEV_CAP_FLAG_DCT = 1LL << 41, + GOLAN_DEV_CAP_FLAG_CMDIF_CSUM = 1LL << 46, +}; + + +struct golan_hca_cap { + u8 rsvd1[16]; + u8 log_max_srq_sz; + u8 log_max_qp_sz; + __be16 log_max_qp; + u8 log_max_strq_sz; + u8 log_max_srqs; + u8 rsvd4[2]; + u8 rsvd5; + u8 log_max_cq_sz; + u8 rsvd6; + u8 log_max_cq; + u8 log_max_eq_sz; + u8 log_max_mkey; + u8 rsvd7; + u8 log_max_eq; + u8 max_indirection; + u8 log_max_mrw_sz; + u8 log_max_bsf_list_sz; + u8 log_max_klm_list_sz; + u8 rsvd_8_0; + u8 log_max_ra_req_dc; + u8 rsvd_8_1; + u8 log_max_ra_res_dc; + u8 rsvd9; + u8 log_max_ra_req_qp; + u8 rsvd10; + u8 log_max_ra_res_qp; + u8 rsvd11[4]; + __be16 max_qp_count; + __be16 pkey_table_size; + u8 rsvd13; + u8 local_ca_ack_delay; + u8 rsvd14; + u8 num_ports; + u8 log_max_msg; + u8 rsvd15[3]; + __be16 stat_rate_support; + u8 rsvd16[2]; + __be64 flags; + u8 rsvd17; + u8 uar_sz; + u8 rsvd18; + u8 log_pg_sz; + __be16 bf_log_bf_reg_size; + u8 rsvd19[4]; + __be16 max_wqe_sz_sq; + u8 rsvd20[2]; + __be16 max_wqe_sz_rq; + u8 rsvd21[2]; + __be16 max_wqe_sz_sq_dc; + u8 rsvd22[4]; + __be16 max_qp_mcg; + u8 rsvd23; + u8 log_max_mcg; + u8 rsvd24; + u8 log_max_pd; + u8 rsvd25; + u8 log_max_xrcd; + u8 rsvd26[40]; + __be32 uar_page_sz; + u8 rsvd27[28]; + u8 log_msx_atomic_size_qp; + u8 rsvd28[2]; + u8 log_msx_atomic_size_dc; + u8 rsvd29[76]; +} __attribute ( ( packed ) ); + +struct golan_query_pages_inbox { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_query_pages_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd[2]; + __be16 func_id; + __be32 num_pages; +} __attribute ( ( packed ) ); + +struct golan_cmd_query_hca_cap_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_cmd_query_hca_cap_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; + struct golan_hca_cap hca_cap; +} __attribute ( ( packed ) ); + +struct golan_cmd_set_hca_cap_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; + struct golan_hca_cap hca_cap; +} __attribute ( ( packed ) ); + +struct golan_cmd_set_hca_cap_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_cmd_init_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[2]; + __be16 profile; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_cmd_init_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +enum golan_teardown { + GOLAN_TEARDOWN_GRACEFUL = 0x0, + GOLAN_TEARDOWN_PANIC = 0x1 +}; + +struct golan_cmd_teardown_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[2]; + __be16 profile; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_cmd_teardown_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_enable_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_enable_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_disable_hca_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_disable_hca_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_inbox_data { + u8 rsvd2[16]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_inbox { + struct golan_inbox_hdr hdr; + __be16 rsvd0; + __be16 func_id; + __be32 num_entries; + struct golan_manage_pages_inbox_data data; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_outbox_data { + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_manage_pages_outbox { + struct golan_outbox_hdr hdr; + __be32 num_entries; + __be32 rsrvd; + struct golan_manage_pages_outbox_data data; +} __attribute ( ( packed ) ); + +struct golan_reg_host_endianess { + u8 he; + u8 rsvd[15]; +} __attribute ( ( packed ) ); + +struct golan_cmd_prot_block { + union { + __be64 data[GOLAN_CMD_PAS_CNT]; + u8 bdata[GOLAN_CMD_DATA_BLOCK_SIZE]; + }; + u8 rsvd0[48]; + __be64 next; + __be32 block_num; + u8 rsvd1; + u8 token; + u8 ctrl_sig; + u8 sig; +} __attribute ( ( packed ) ); + +/* MAD IFC structures */ +#define GOLAN_MAD_SIZE 256 +#define GOLAN_MAD_IFC_NO_VALIDATION 0x3 +#define GOLAN_MAD_IFC_RLID_BIT 16 + +struct golan_mad_ifc_mbox_in { + struct golan_inbox_hdr hdr; + __be16 remote_lid; + u8 rsvd0; + u8 port; + u8 rsvd1[4]; + u8 mad[GOLAN_MAD_SIZE]; +} __attribute ( ( packed ) ); + +struct golan_mad_ifc_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; + u8 mad[GOLAN_MAD_SIZE]; +} __attribute ( ( packed ) ); + +/* UAR Structures */ +struct golan_alloc_uar_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_alloc_uar_mbox_out { + struct golan_outbox_hdr hdr; + __be32 uarn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_free_uar_mbox_in { + struct golan_inbox_hdr hdr; + __be32 uarn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_free_uar_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/* Event Queue Structures */ +enum { + GOLAN_EQ_STATE_ARMED = 0x9, + GOLAN_EQ_STATE_FIRED = 0xa, + GOLAN_EQ_STATE_ALWAYS_ARMED = 0xb, +}; + + +struct golan_eq_context { + u8 status; + u8 ec_oi; + u8 st; + u8 rsvd2[7]; + __be16 page_pffset; + __be32 log_sz_usr_page; + u8 rsvd3[7]; + u8 intr; + u8 log_page_size; + u8 rsvd4[15]; + __be32 consumer_counter; + __be32 produser_counter; + u8 rsvd5[16]; +} __attribute ( ( packed ) ); + +struct golan_create_eq_mbox_in_data { + struct golan_eq_context ctx; + u8 rsvd2[8]; + __be64 events_mask; + u8 rsvd3[176]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_eq_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[3]; + u8 input_eqn; + u8 rsvd1[4]; + struct golan_create_eq_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_eq_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[3]; + u8 eq_number; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_eq_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd0[3]; + u8 eqn; + u8 rsvd1[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_eq_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/***********************************************/ +/************** Query Vport ****************/ +struct golan_query_hca_vport_context_inbox { + struct golan_inbox_hdr hdr; + __be16 other_vport : 1; + __be16 rsvd1 : 7; + __be16 port_num : 4; + __be16 rsvd2 : 4; + __be16 vport_number; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_context_data { + __be32 field_select; + __be32 rsvd1[7]; + //**** + __be16 sm_virt_aware : 1; + __be16 has_smi : 1; + __be16 has_raw : 1; + __be16 grh_required : 1; + __be16 rsvd2 : 12; + u8 port_physical_state : 4; + u8 vport_state_policy : 4; + u8 port_state : 4; + u8 vport_state : 4; + //**** + u8 rsvd3[4]; + //**** + __be32 system_image_guid[2]; + //**** + __be32 port_guid[2]; + //**** + __be32 node_guid[2]; + //**** + __be32 cap_mask1; + __be32 cap_mask1_field_select; + __be32 cap_mask2; + __be32 cap_mask2_field_select; + u8 rsvd4[16]; + __be16 lid; + u8 rsvd5 : 4; + u8 init_type_reply : 4; + u8 lmc : 3; + u8 subnet_timeout : 5; + __be16 sm_lid; + u8 sm_sl : 4; + u8 rsvd6 : 4; + u8 rsvd7; + __be16 qkey_violation_counter; + __be16 pkey_violation_counter; + u8 rsvd8[100]; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_context_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; + struct golan_query_hca_vport_context_data context_data; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_gid_inbox { + struct golan_inbox_hdr hdr; + u8 other_vport : 1; + u8 rsvd1 : 7; + u8 port_num : 4; + u8 rsvd2 : 4; + __be16 vport_number; + __be16 rsvd3; + __be16 gid_index; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_gid_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd0[4]; + __be16 gids_num; + u8 rsvd1[2]; + __be32 gid0[4]; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_pkey_inbox { + struct golan_inbox_hdr hdr; + u8 other_vport : 1; + u8 rsvd1 : 7; + u8 port_num : 4; + u8 rsvd2 : 4; + __be16 vport_number; + __be16 rsvd3; + __be16 pkey_index; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_pkey_data { + __be16 rsvd1; + __be16 pkey0; +} __attribute ( ( packed ) ); + +struct golan_query_hca_vport_pkey_outbox { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; + struct golan_query_hca_vport_pkey_data *pkey_data; +} __attribute ( ( packed ) ); + +struct golan_eqe_comp { + __be32 reserved[6]; + __be32 cqn; +} __attribute ( ( packed ) ); + +struct golan_eqe_qp_srq { + __be32 reserved[6]; + __be32 qp_srq_n; +} __attribute ( ( packed ) ); + +struct golan_eqe_cq_err { + __be32 cqn; + u8 reserved1[7]; + u8 syndrome; +} __attribute ( ( packed ) ); + +struct golan_eqe_dropped_packet { +}; + +struct golan_eqe_port_state { + u8 reserved0[8]; + u8 port; +} __attribute ( ( packed ) ); + +struct golan_eqe_gpio { + __be32 reserved0[2]; + __be64 gpio_event; +} __attribute ( ( packed ) ); + +struct golan_eqe_congestion { + u8 type; + u8 rsvd0; + u8 congestion_level; +} __attribute ( ( packed ) ); + +struct golan_eqe_stall_vl { + u8 rsvd0[3]; + u8 port_vl; +} __attribute ( ( packed ) ); + +struct golan_eqe_cmd { + __be32 vector; + __be32 rsvd[6]; +} __attribute ( ( packed ) ); + +struct golan_eqe_page_req { + u8 rsvd0[2]; + __be16 func_id; + u8 rsvd1[2]; + __be16 num_pages; + __be32 rsvd2[5]; +} __attribute ( ( packed ) ); + +union ev_data { + __be32 raw[7]; + struct golan_eqe_cmd cmd; + struct golan_eqe_comp comp; + struct golan_eqe_qp_srq qp_srq; + struct golan_eqe_cq_err cq_err; + struct golan_eqe_dropped_packet dp; + struct golan_eqe_port_state port; + struct golan_eqe_gpio gpio; + struct golan_eqe_congestion cong; + struct golan_eqe_stall_vl stall_vl; + struct golan_eqe_page_req req_pages; +} __attribute__ ((packed)); + +struct golan_eqe { + u8 rsvd0; + u8 type; + u8 rsvd1; + u8 sub_type; + __be32 rsvd2[7]; + union ev_data data; + __be16 rsvd3; + u8 signature; + u8 owner; +} __attribute__ ((packed)); + +/* Protection Domain Structures */ +struct golan_alloc_pd_mbox_in { + struct golan_inbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +struct golan_alloc_pd_mbox_out { + struct golan_outbox_hdr hdr; + __be32 pdn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_dealloc_pd_mbox_in { + struct golan_inbox_hdr hdr; + __be32 pdn; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_dealloc_pd_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/* Memory key structures */ +#define GOLAN_IB_ACCESS_LOCAL_READ (1 << 2) +#define GOLAN_IB_ACCESS_LOCAL_WRITE (1 << 3) +#define GOLAN_MKEY_LEN64 (1 << 31) +#define GOLAN_CREATE_MKEY_SEG_QPN_BIT 8 + +struct golan_mkey_seg { + /* + * This is a two bit field occupying bits 31-30. + * bit 31 is always 0, + * bit 30 is zero for regular MRs and 1 (e.g free) for UMRs that do not have tanslation + */ + u8 status; + u8 pcie_control; + u8 flags; + u8 version; + __be32 qpn_mkey7_0; + u8 rsvd1[4]; + __be32 flags_pd; + __be64 start_addr; + __be64 len; + __be32 bsfs_octo_size; + u8 rsvd2[16]; + __be32 xlt_oct_size; + u8 rsvd3[3]; + u8 log2_page_size; + u8 rsvd4[4]; +} __attribute ( ( packed ) ); + +struct golan_create_mkey_mbox_in_data { + struct golan_mkey_seg seg; + u8 rsvd1[16]; + __be32 xlat_oct_act_size; + __be32 bsf_coto_act_size; + u8 rsvd2[168]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_mkey_mbox_in { + struct golan_inbox_hdr hdr; + __be32 input_mkey_index; + u8 rsvd0[4]; + struct golan_create_mkey_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_mkey_mbox_out { + struct golan_outbox_hdr hdr; + __be32 mkey; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_mkey_mbox_in { + struct golan_inbox_hdr hdr; + __be32 mkey; + u8 rsvd[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_mkey_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd[8]; +} __attribute ( ( packed ) ); + +/* Completion Queue Structures */ +enum { + GOLAN_CQ_STATE_ARMED = 9, + GOLAN_CQ_STATE_ALWAYS_ARMED = 0xb, + GOLAN_CQ_STATE_FIRED = 0xa +}; + +enum { + GOLAN_CQE_REQ = 0, + GOLAN_CQE_RESP_WR_IMM = 1, + GOLAN_CQE_RESP_SEND = 2, + GOLAN_CQE_RESP_SEND_IMM = 3, + GOLAN_CQE_RESP_SEND_INV = 4, + GOLAN_CQE_RESIZE_CQ = 0xff, /* TBD */ + GOLAN_CQE_REQ_ERR = 13, + GOLAN_CQE_RESP_ERR = 14 +}; + +struct golan_cq_context { + u8 status; + u8 cqe_sz_flags; + u8 st; + u8 rsvd3; + u8 rsvd4[6]; + __be16 page_offset; + __be32 log_sz_usr_page; + __be16 cq_period; + __be16 cq_max_count; + __be16 rsvd20; + __be16 c_eqn; + u8 log_pg_sz; + u8 rsvd25[7]; + __be32 last_notified_index; + __be32 solicit_producer_index; + __be32 consumer_counter; + __be32 producer_counter; + u8 rsvd48[8]; + __be64 db_record_addr; +} __attribute ( ( packed ) ); + + +struct golan_create_cq_mbox_in_data { + struct golan_cq_context ctx; + u8 rsvd6[192]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_cq_mbox_in { + struct golan_inbox_hdr hdr; + __be32 input_cqn; + u8 rsvdx[4]; + struct golan_create_cq_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_cq_mbox_out { + struct golan_outbox_hdr hdr; + __be32 cqn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_cq_mbox_in { + struct golan_inbox_hdr hdr; + __be32 cqn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_cq_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_err_cqe { + u8 rsvd0[32]; + __be32 srqn; + u8 rsvd1[16]; + u8 hw_syndrom; + u8 rsvd2; + u8 vendor_err_synd; + u8 syndrome; + __be32 s_wqe_opcode_qpn; + __be16 wqe_counter; + u8 signature; + u8 op_own; +} __attribute ( ( packed ) ); + +struct golan_cqe64 { + u8 rsvd0[17]; + u8 ml_path; + u8 rsvd20[4]; + __be16 slid; + __be32 flags_rqpn; + u8 rsvd28[4]; + __be32 srqn; + __be32 imm_inval_pkey; + u8 rsvd40[4]; + __be32 byte_cnt; + __be64 timestamp; + __be32 sop_drop_qpn; + __be16 wqe_counter; + u8 signature; + u8 op_own; +} __attribute ( ( packed ) ); + +/* Queue Pair Structures */ +#define GOLAN_QP_CTX_ST_BIT 16 +#define GOLAN_QP_CTX_PM_STATE_BIT 11 +#define GOLAN_QP_CTX_FRE_BIT 11 +#define GOLAN_QP_CTX_RLKY_BIT 4 +#define GOLAN_QP_CTX_RQ_SIZE_BIT 3 +#define GOLAN_QP_CTX_SQ_SIZE_BIT 11 +#define GOLAN_QP_CTX_MTU_BIT 5 +#define GOLAN_QP_CTX_ACK_REQ_FREQ_BIT 28 + +enum { + GOLAN_QP_CTX_DONT_USE_RSRVD_LKEY = 0, + GOLAN_QP_CTX_USE_RSRVD_LKEY = 1 +}; + +enum { + GOLAN_IB_ACK_REQ_FREQ = 8, +}; + +enum golan_qp_optpar { + GOLAN_QP_PARAM_ALT_ADDR_PATH = 1 << 0, + GOLAN_QP_PARAM_RRE = 1 << 1, + GOLAN_QP_PARAM_RAE = 1 << 2, + GOLAN_QP_PARAM_RWE = 1 << 3, + GOLAN_QP_PARAM_PKEY_INDEX = 1 << 4, + GOLAN_QP_PARAM_Q_KEY = 1 << 5, + GOLAN_QP_PARAM_RNR_TIMEOUT = 1 << 6, + GOLAN_QP_PARAM_PRIMARY_ADDR_PATH = 1 << 7, + GOLAN_QP_PARAM_SRA_MAX = 1 << 8, + GOLAN_QP_PARAM_RRA_MAX = 1 << 9, + GOLAN_QP_PARAM_PM_STATE = 1 << 10, + GOLAN_QP_PARAM_RETRY_COUNT = 1 << 12, + GOLAN_QP_PARAM_RNR_RETRY = 1 << 13, + GOLAN_QP_PARAM_ACK_TIMEOUT = 1 << 14, + GOLAN_QP_PARAM_PRI_PORT = 1 << 16, + GOLAN_QP_PARAM_SRQN = 1 << 18, + GOLAN_QP_PARAM_CQN_RCV = 1 << 19, + GOLAN_QP_PARAM_DC_HS = 1 << 20, + GOLAN_QP_PARAM_DC_KEY = 1 << 21 +}; + +#define GOLAN_QP_PARAMS_INIT2RTR_MASK (GOLAN_QP_PARAM_PKEY_INDEX |\ + GOLAN_QP_PARAM_Q_KEY |\ + GOLAN_QP_PARAM_RWE |\ + GOLAN_QP_PARAM_RRE) + +#define GOLAN_QP_PARAMS_RTR2RTS_MASK (GOLAN_QP_PARAM_PM_STATE |\ + GOLAN_QP_PARAM_RNR_TIMEOUT |\ + GOLAN_QP_PARAM_Q_KEY |\ + GOLAN_QP_PARAM_RWE |\ + GOLAN_QP_PARAM_RRE) + + +enum { + GOLAN_QP_ST_RC = 0x0, + GOLAN_QP_ST_UC = 0x1, + GOLAN_QP_ST_UD = 0x2, + GOLAN_QP_ST_XRC = 0x3, + GOLAN_QP_ST_MLX = 0x4, + GOLAN_QP_ST_DC = 0x5, + GOLAN_QP_ST_QP0 = 0x7, + GOLAN_QP_ST_QP1 = 0x8, + GOLAN_QP_ST_RAW_ETHERTYPE = 0x9, + GOLAN_QP_ST_RAW_IPV6 = 0xa, + GOLAN_QP_ST_SNIFFER = 0xb, + GOLAN_QP_ST_SYNC_UMR = 0xe, + GOLAN_QP_ST_PTP_1588 = 0xd, + GOLAN_QP_ST_REG_UMR = 0xc, + GOLAN_QP_ST_MAX +}; + +enum { + GOLAN_QP_PM_MIGRATED = 0x3, + GOLAN_QP_PM_ARMED = 0x0, + GOLAN_QP_PM_REARM = 0x1 +}; + +enum { + GOLAN_QP_LAT_SENSITIVE = 1 << 28, + GOLAN_QP_ENABLE_SIG = 1 << 31 +}; + + +struct golan_qp_db { + u8 rsvd0[2]; + __be16 recv_db; + u8 rsvd1[2]; + __be16 send_db; +} __attribute ( ( packed ) ); + +enum { + GOLAN_WQE_CTRL_CQ_UPDATE = 2 << 2, /*Wissam, wtf?*/ + GOLAN_WQE_CTRL_SOLICITED = 1 << 1 +}; + +struct golan_wqe_ctrl_seg { + __be32 opmod_idx_opcode; + __be32 qpn_ds; + u8 signature; + u8 rsvd[2]; + u8 fm_ce_se; + __be32 imm; +} __attribute ( ( packed ) ); + +struct golan_av { + union { + struct { + __be32 qkey; + __be32 reserved; + } qkey; + __be64 dc_key; + } key; + __be32 dqp_dct; + u8 stat_rate_sl; + u8 fl_mlid; + __be16 rlid; + u8 reserved0[10]; + u8 tclass; + u8 hop_limit; + __be32 grh_gid_fl; + u8 rgid[16]; +} __attribute ( ( packed ) ); + +struct golan_wqe_data_seg { + __be32 byte_count; + __be32 lkey; + __be64 addr; +} __attribute ( ( packed ) ); + +struct golan_wqe_signature_seg { + u8 rsvd0[4]; + u8 signature; + u8 rsvd1[11]; +} __attribute ( ( packed ) ); + +struct golan_wqe_inline_seg { + __be32 byte_count; +} __attribute ( ( packed ) ); + +struct golan_qp_path { + u8 fl; + u8 rsvd3; + u8 free_ar; + u8 pkey_index; + u8 rsvd0; + u8 grh_mlid; + __be16 rlid; + u8 ackto_lt; + u8 mgid_index; + u8 static_rate; + u8 hop_limit; + __be32 tclass_flowlabel; + u8 rgid[16]; + u8 rsvd1[4]; + u8 sl; + u8 port; + u8 rsvd2[6]; +} __attribute ( ( packed ) ); + +struct golan_qp_context { + __be32 flags; + __be32 flags_pd; + u8 mtu_msgmax; + u8 rq_size_stride; + __be16 sq_crq_size; + __be32 qp_counter_set_usr_page; + __be32 wire_qpn; + __be32 log_pg_sz_remote_qpn; + struct golan_qp_path pri_path; + struct golan_qp_path alt_path; + __be32 params1; + u8 reserved2[4]; + __be32 next_send_psn; + __be32 cqn_send; + u8 reserved3[8]; + __be32 last_acked_psn; + __be32 ssn; + __be32 params2; + __be32 rnr_nextrecvpsn; + __be32 xrcd; + __be32 cqn_recv; + __be64 db_rec_addr; + __be32 qkey; + __be32 rq_type_srqn; + __be32 rmsn; + __be16 hw_sq_wqe_counter; + __be16 sw_sq_wqe_counter; + __be16 hw_rcyclic_byte_counter; + __be16 hw_rq_counter; + __be16 sw_rcyclic_byte_counter; + __be16 sw_rq_counter; + u8 rsvd0[5]; + u8 cgs; + u8 cs_req; + u8 cs_res; + __be64 dc_access_key; + u8 rsvd1[24]; +} __attribute ( ( packed ) ); + +struct golan_create_qp_mbox_in_data { + __be32 opt_param_mask; + u8 rsvd1[4]; + struct golan_qp_context ctx; + u8 rsvd3[16]; + __be64 pas[0]; +} __attribute ( ( packed ) ); + +struct golan_create_qp_mbox_in { + struct golan_inbox_hdr hdr; + __be32 input_qpn; + u8 rsvd0[4]; + struct golan_create_qp_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_create_qp_mbox_out { + struct golan_outbox_hdr hdr; + __be32 qpn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_qp_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + u8 rsvd0[4]; +} __attribute ( ( packed ) ); + +struct golan_destroy_qp_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_modify_qp_mbox_in_data { + __be32 optparam; + u8 rsvd0[4]; + struct golan_qp_context ctx; +} __attribute ( ( packed ) ); + +struct golan_modify_qp_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + u8 rsvd1[4]; + struct golan_modify_qp_mbox_in_data data; +} __attribute ( ( packed ) ); + +struct golan_modify_qp_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvd0[8]; +} __attribute ( ( packed ) ); + +struct golan_attach_mcg_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + __be32 rsvd; + u8 gid[16]; +} __attribute ( ( packed ) ); + +struct golan_attach_mcg_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvf[8]; +} __attribute ( ( packed ) ); + +struct golan_detach_mcg_mbox_in { + struct golan_inbox_hdr hdr; + __be32 qpn; + __be32 rsvd; + u8 gid[16]; +} __attribute ( ( packed ) ); + +struct golan_detach_mcg_mbox_out { + struct golan_outbox_hdr hdr; + u8 rsvf[8]; +} __attribute ( ( packed ) ); + + +#define MAILBOX_SIZE sizeof(struct golan_cmd_prot_block) + +#endif /* __CIB_PRM__ */ diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index 2a6c32dec..98a2b6010 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -897,26 +897,44 @@ static int arbel_create_send_wq ( struct arbel_send_work_queue *arbel_send_wq, * * @v arbel_recv_wq Receive work queue * @v num_wqes Number of work queue entries + * @v type Queue pair type * @ret rc Return status code */ static int arbel_create_recv_wq ( struct arbel_recv_work_queue *arbel_recv_wq, - unsigned int num_wqes ) { + unsigned int num_wqes, + enum ib_queue_pair_type type ) { struct arbelprm_recv_wqe *wqe; struct arbelprm_recv_wqe *next_wqe; unsigned int wqe_idx_mask; size_t nds; unsigned int i; unsigned int j; + int rc; /* Allocate work queue */ arbel_recv_wq->wqe_size = ( num_wqes * sizeof ( arbel_recv_wq->wqe[0] ) ); arbel_recv_wq->wqe = malloc_dma ( arbel_recv_wq->wqe_size, sizeof ( arbel_recv_wq->wqe[0] ) ); - if ( ! arbel_recv_wq->wqe ) - return -ENOMEM; + if ( ! arbel_recv_wq->wqe ) { + rc = -ENOMEM; + goto err_alloc_wqe; + } memset ( arbel_recv_wq->wqe, 0, arbel_recv_wq->wqe_size ); + /* Allocate GRH entries, if needed */ + if ( ( type == IB_QPT_SMI ) || ( type == IB_QPT_GSI ) || + ( type == IB_QPT_UD ) ) { + arbel_recv_wq->grh_size = ( num_wqes * + sizeof ( arbel_recv_wq->grh[0] ) ); + arbel_recv_wq->grh = malloc_dma ( arbel_recv_wq->grh_size, + sizeof ( void * ) ); + if ( ! arbel_recv_wq->grh ) { + rc = -ENOMEM; + goto err_alloc_grh; + } + } + /* Link work queue entries */ wqe_idx_mask = ( num_wqes - 1 ); nds = ( ( offsetof ( typeof ( *wqe ), data ) + @@ -935,6 +953,12 @@ static int arbel_create_recv_wq ( struct arbel_recv_work_queue *arbel_recv_wq, } return 0; + + free_dma ( arbel_recv_wq->grh, arbel_recv_wq->grh_size ); + err_alloc_grh: + free_dma ( arbel_recv_wq->wqe, arbel_recv_wq->wqe_size ); + err_alloc_wqe: + return rc; } /** @@ -985,8 +1009,8 @@ static int arbel_create_qp ( struct ib_device *ibdev, if ( ( rc = arbel_create_send_wq ( &arbel_qp->send, qp->send.num_wqes ) ) != 0 ) goto err_create_send_wq; - if ( ( rc = arbel_create_recv_wq ( &arbel_qp->recv, - qp->recv.num_wqes ) ) != 0 ) + if ( ( rc = arbel_create_recv_wq ( &arbel_qp->recv, qp->recv.num_wqes, + qp->type ) ) != 0 ) goto err_create_recv_wq; /* Send and receive work queue entries must be within the same 4GB */ @@ -1078,6 +1102,7 @@ static int arbel_create_qp ( struct ib_device *ibdev, MLX_FILL_1 ( send_db_rec, 1, res, ARBEL_UAR_RES_NONE ); MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE ); err_unsupported_address_split: + free_dma ( arbel_qp->recv.grh, arbel_qp->recv.grh_size ); free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size ); err_create_recv_wq: free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); @@ -1206,8 +1231,9 @@ static void arbel_destroy_qp ( struct ib_device *ibdev, MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE ); /* Free memory */ - free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); + free_dma ( arbel_qp->recv.grh, arbel_qp->recv.grh_size ); free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size ); + free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); free ( arbel_qp ); /* Mark queue number as free */ @@ -1477,6 +1503,8 @@ static int arbel_post_recv ( struct ib_device *ibdev, struct ib_work_queue *wq = &qp->recv; struct arbel_recv_work_queue *arbel_recv_wq = &arbel_qp->recv; struct arbelprm_recv_wqe *wqe; + struct arbelprm_wqe_segment_data_ptr *data; + struct ib_global_route_header *grh; union arbelprm_doorbell_record *db_rec; unsigned int wqe_idx_mask; @@ -1491,12 +1519,19 @@ static int arbel_post_recv ( struct ib_device *ibdev, wqe = &arbel_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv; /* Construct work queue entry */ - MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); - MLX_FILL_1 ( &wqe->data[0], 1, l_key, arbel->lkey ); - MLX_FILL_H ( &wqe->data[0], 2, - local_address_h, virt_to_bus ( iobuf->data ) ); - MLX_FILL_1 ( &wqe->data[0], 3, - local_address_l, virt_to_bus ( iobuf->data ) ); + data = &wqe->data[0]; + if ( arbel_recv_wq->grh ) { + grh = &arbel_recv_wq->grh[wq->next_idx & wqe_idx_mask]; + MLX_FILL_1 ( data, 0, byte_count, sizeof ( *grh ) ); + MLX_FILL_1 ( data, 1, l_key, arbel->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( grh ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( grh ) ); + data++; + } + MLX_FILL_1 ( data, 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( data, 1, l_key, arbel->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( iobuf->data ) ); /* Update doorbell record */ barrier(); @@ -1611,17 +1646,16 @@ static int arbel_complete ( struct ib_device *ibdev, MLX_FILL_1 ( &recv_wqe->data[0], 0, byte_count, 0 ); MLX_FILL_1 ( &recv_wqe->data[0], 1, l_key, ARBEL_INVALID_LKEY ); - assert ( len <= iob_tailroom ( iobuf ) ); - iob_put ( iobuf, len ); memset ( &recv_dest, 0, sizeof ( recv_dest ) ); recv_dest.qpn = qpn; switch ( qp->type ) { case IB_QPT_SMI: case IB_QPT_GSI: case IB_QPT_UD: - assert ( iob_len ( iobuf ) >= sizeof ( *grh ) ); - grh = iobuf->data; - iob_pull ( iobuf, sizeof ( *grh ) ); + /* Locate corresponding GRH */ + assert ( arbel_recv_wq->grh != NULL ); + grh = &arbel_recv_wq->grh[wqe_idx]; + len -= sizeof ( *grh ); /* Construct address vector */ source = &recv_source; memset ( source, 0, sizeof ( *source ) ); @@ -1642,6 +1676,8 @@ static int arbel_complete ( struct ib_device *ibdev, assert ( 0 ); return -EINVAL; } + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); /* Hand off to completion handler */ ib_complete_recv ( ibdev, qp, &recv_dest, source, iobuf, rc ); } @@ -1936,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; @@ -1958,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 { @@ -3000,6 +3037,16 @@ static int arbel_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, arbel ); arbel->pci = pci; + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BARs */ + arbel->config = ioremap ( pci_bar_start ( pci, ARBEL_PCI_CONFIG_BAR ), + ARBEL_PCI_CONFIG_BAR_SIZE ); + arbel->uar = ioremap ( ( pci_bar_start ( pci, ARBEL_PCI_UAR_BAR ) + + ARBEL_PCI_UAR_IDX * ARBEL_PCI_UAR_SIZE ), + ARBEL_PCI_UAR_SIZE ); + /* Allocate Infiniband devices */ for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) { ibdev = alloc_ibdev ( 0 ); @@ -3014,16 +3061,6 @@ static int arbel_probe ( struct pci_device *pci ) { ib_set_drvdata ( ibdev, arbel ); } - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Get PCI BARs */ - arbel->config = ioremap ( pci_bar_start ( pci, ARBEL_PCI_CONFIG_BAR ), - ARBEL_PCI_CONFIG_BAR_SIZE ); - arbel->uar = ioremap ( ( pci_bar_start ( pci, ARBEL_PCI_UAR_BAR ) + - ARBEL_PCI_UAR_IDX * ARBEL_PCI_UAR_SIZE ), - ARBEL_PCI_UAR_SIZE ); - /* Reset device */ arbel_reset ( arbel ); @@ -3072,6 +3109,8 @@ static int arbel_probe ( struct pci_device *pci ) { err_alloc_ibdev: for ( i-- ; i >= 0 ; i-- ) ibdev_put ( arbel->ibdev[i] ); + iounmap ( arbel->uar ); + iounmap ( arbel->config ); arbel_free ( arbel ); err_alloc: return rc; @@ -3090,6 +3129,8 @@ static void arbel_remove ( struct pci_device *pci ) { unregister_ibdev ( arbel->ibdev[i] ); for ( i = ( ARBEL_NUM_PORTS - 1 ) ; i >= 0 ; i-- ) ibdev_put ( arbel->ibdev[i] ); + iounmap ( arbel->uar ); + iounmap ( arbel->config ); arbel_free ( arbel ); } diff --git a/src/drivers/infiniband/arbel.h b/src/drivers/infiniband/arbel.h index 73394cd9a..8a5a996a3 100644 --- a/src/drivers/infiniband/arbel.h +++ b/src/drivers/infiniband/arbel.h @@ -237,7 +237,7 @@ struct arbelprm_rc_send_wqe { struct arbelprm_wqe_segment_data_ptr data[ARBEL_MAX_GATHER]; } __attribute__ (( packed )); -#define ARBEL_MAX_SCATTER 1 +#define ARBEL_MAX_SCATTER 2 struct arbelprm_recv_wqe { /* The autogenerated header is inconsistent between send and @@ -369,6 +369,10 @@ struct arbel_recv_work_queue { union arbel_recv_wqe *wqe; /** Size of work queue */ size_t wqe_size; + /** GRH buffers (if applicable) */ + struct ib_global_route_header *grh; + /** Size of GRB buffers */ + size_t grh_size; }; /** Number of special queue pairs */ diff --git a/src/drivers/infiniband/flexboot_nodnic.c b/src/drivers/infiniband/flexboot_nodnic.c new file mode 100644 index 000000000..c13fcefc5 --- /dev/null +++ b/src/drivers/infiniband/flexboot_nodnic.c @@ -0,0 +1,1590 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flexboot_nodnic.h" +#include "mlx_utils/include/public/mlx_types.h" +#include "mlx_utils/include/public/mlx_utils.h" +#include "mlx_utils/include/public/mlx_bail.h" +#include "mlx_nodnic/include/mlx_cmd.h" +#include "mlx_utils/include/public/mlx_memory.h" +#include "mlx_utils/include/public/mlx_pci.h" +#include "mlx_nodnic/include/mlx_device.h" +#include "mlx_nodnic/include/mlx_port.h" +#include +#include +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" +#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" + +/*************************************************************************** + * + * Completion queue operations + * + *************************************************************************** + */ +static int flexboot_nodnic_arm_cq ( struct flexboot_nodnic_port *port ) { +#ifndef DEVICE_CX3 + mlx_uint32 val32 = 0; + union arm_cq_uar cq_uar; + +#define ARM_CQ_UAR_CQ_CI_MASK 0xffffff +#define ARM_CQ_UAR_CMDSN_MASK 3 +#define ARM_CQ_UAR_CMDSN_OFFSET 28 +#define ARM_CQ_UAR_CQ_CI_OFFSET 0x20 + if ( port->port_priv.device->device_cap.support_bar_cq_ctrl ) { + cq_uar.dword[0] = cpu_to_be32((port->eth_cq->next_idx & ARM_CQ_UAR_CQ_CI_MASK) | + ((port->cmdsn++ & ARM_CQ_UAR_CMDSN_MASK) << ARM_CQ_UAR_CMDSN_OFFSET)); + cq_uar.dword[1] = cpu_to_be32(port->eth_cq->cqn); + wmb(); + writeq(cq_uar.qword, port->port_priv.device->uar.virt + ARM_CQ_UAR_CQ_CI_OFFSET); + port->port_priv.arm_cq_doorbell_record->dword[0] = cq_uar.dword[1]; + port->port_priv.arm_cq_doorbell_record->dword[1] = cq_uar.dword[0]; + } else { + val32 = ( port->eth_cq->next_idx & 0xffffff ); + if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val32 ) ) { + MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); + return MLX_FAILED; + } + } +#else + mlx_utils *utils = port->port_priv.device->utils; + nodnic_port_data_flow_gw *ptr = port->port_priv.data_flow_gw; + mlx_uint32 data = 0; + mlx_uint32 val = 0; + + if ( port->port_priv.device->device_cap.crspace_doorbells == 0 ) { + val = ( port->eth_cq->next_idx & 0xffff ); + if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) { + MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); + return MLX_FAILED; + } + } else { + /* Arming the CQ with CQ CI should be with this format - + * 16 bit - CQ CI - same endianness as the FW (don't swap bytes) + * 15 bit - reserved + * 1 bit - arm CQ - must correct the endianness with the reserved above */ + data = ( ( ( port->eth_cq->next_idx & 0xffff ) << 16 ) | 0x0080 ); + /* Write the new index and update FW that new data was submitted */ + mlx_pci_mem_write ( utils, MlxPciWidthUint32, 0, + ( mlx_uintn ) & ( ptr->armcq_cq_ci_dword ), 1, &data ); + } +#endif + return 0; +} + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int flexboot_nodnic_create_cq ( struct ib_device *ibdev , + struct ib_completion_queue *cq ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq; + mlx_status status = MLX_SUCCESS; + mlx_uint32 cqn; + + flexboot_nodnic_cq = (struct flexboot_nodnic_completion_queue *) + zalloc(sizeof(*flexboot_nodnic_cq)); + if ( flexboot_nodnic_cq == NULL ) { + status = MLX_OUT_OF_RESOURCES; + goto qp_alloc_err; + } + + status = nodnic_port_create_cq(&port->port_priv, + cq->num_cqes * + flexboot_nodnic->callbacks->get_cqe_size(), + &flexboot_nodnic_cq->nodnic_completion_queue + ); + MLX_FATAL_CHECK_STATUS(status, create_err, + "nodnic_port_create_cq failed"); + flexboot_nodnic->callbacks->cqe_set_owner( + flexboot_nodnic_cq->nodnic_completion_queue->cq_virt, + cq->num_cqes); + if ( flexboot_nodnic->device_priv.device_cap.support_bar_cq_ctrl ) { + status = nodnic_port_query(&port->port_priv, + nodnic_port_option_cq_n_index, + (mlx_uint32 *)&cqn ); + MLX_FATAL_CHECK_STATUS(status, read_cqn_err, + "failed to query cqn"); + cq->cqn = cqn; + } + + ib_cq_set_drvdata ( cq, flexboot_nodnic_cq ); + return status; +read_cqn_err: +create_err: + free(flexboot_nodnic_cq); +qp_alloc_err: + return status; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void flexboot_nodnic_destroy_cq ( struct ib_device *ibdev , + struct ib_completion_queue *cq ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq ); + + nodnic_port_destroy_cq(&port->port_priv, + flexboot_nodnic_cq->nodnic_completion_queue); + + free(flexboot_nodnic_cq); +} + +static +struct ib_work_queue * flexboot_nodnic_find_wq ( struct ib_device *ibdev , + struct ib_completion_queue *cq, + unsigned long qpn, int is_send ) { + struct ib_work_queue *wq; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct nodnic_ring *ring; + mlx_uint32 out_qpn; + list_for_each_entry ( wq, &cq->work_queues, list ) { + flexboot_nodnic_qp = ib_qp_get_drvdata ( wq->qp ); + if( wq->is_send == is_send && wq->is_send == TRUE ) { + ring = &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring; + } else if( wq->is_send == is_send && wq->is_send == FALSE ) { + ring = &flexboot_nodnic_qp->nodnic_queue_pair->receive.nodnic_ring; + } else { + continue; + } + nodnic_port_get_qpn(&port->port_priv, ring, &out_qpn); + if ( out_qpn == qpn ) + return wq; + } + return NULL; +} + +/** + * Handle completion + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @v cqe Hardware completion queue entry + * @ret rc Return status code + */ +static int flexboot_nodnic_complete ( struct ib_device *ibdev, + struct ib_completion_queue *cq, + struct cqe_data *cqe_data ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq; + struct ib_queue_pair *qp; + struct io_buffer *iobuf; + struct ib_address_vector recv_dest; + struct ib_address_vector recv_source; + unsigned long qpn; + unsigned long wqe_idx; + unsigned long wqe_idx_mask; + size_t len; + int rc = 0; + + /* Parse completion */ + qpn = cqe_data->qpn; + + if ( cqe_data->is_error == TRUE ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx syndrome %x vendor %x\n", + flexboot_nodnic, cq->cqn, cqe_data->syndrome, + cqe_data->vendor_err_syndrome ); + rc = -EIO; + /* Don't return immediately; propagate error to completer */ + } + + /* Identify work queue */ + wq = flexboot_nodnic_find_wq( ibdev, cq, qpn, cqe_data->is_send ); + if ( wq == NULL ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p CQN %#lx unknown %s QPN %#lx\n", + flexboot_nodnic, cq->cqn, + ( cqe_data->is_send ? "send" : "recv" ), qpn ); + return -EIO; + } + qp = wq->qp; + + /* Identify work queue entry */ + wqe_idx = cqe_data->wqe_counter; + wqe_idx_mask = ( wq->num_wqes - 1 ); + DBGCP ( flexboot_nodnic, + "NODNIC %p CQN %#lx QPN %#lx %s WQE %#lx completed:\n", + flexboot_nodnic, cq->cqn, qp->qpn, + ( cqe_data->is_send ? "send" : "recv" ), + wqe_idx ); + + /* Identify I/O buffer */ + iobuf = wq->iobufs[wqe_idx & wqe_idx_mask]; + if ( iobuf == NULL ) { + DBGC ( flexboot_nodnic, + "NODNIC %p CQN %#lx QPN %#lx empty %s WQE %#lx\n", + flexboot_nodnic, cq->cqn, qp->qpn, + ( cqe_data->is_send ? "send" : "recv" ), wqe_idx ); + return -EIO; + } + wq->iobufs[wqe_idx & wqe_idx_mask] = NULL; + + if ( cqe_data->is_send == TRUE ) { + /* Hand off to completion handler */ + ib_complete_send ( ibdev, qp, iobuf, rc ); + } else if ( rc != 0 ) { + /* Propagate error to receive completion handler */ + ib_complete_recv ( ibdev, qp, NULL, NULL, iobuf, rc ); + } else { + /* Set received length */ + len = cqe_data->byte_cnt; + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); + memset ( &recv_dest, 0, sizeof ( recv_dest ) ); + recv_dest.qpn = qpn; + memset ( &recv_source, 0, sizeof ( recv_source ) ); + switch ( qp->type ) { + case IB_QPT_SMI: + case IB_QPT_GSI: + case IB_QPT_UD: + case IB_QPT_RC: + break; + case IB_QPT_ETH: + break; + default: + assert ( 0 ); + return -EINVAL; + } + /* Hand off to completion handler */ + ib_complete_recv ( ibdev, qp, &recv_dest, + &recv_source, iobuf, rc ); + } + + return rc; +} +/** + * Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queues + */ +static void flexboot_nodnic_poll_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq ); + void *cqe; + mlx_size cqe_size; + struct cqe_data cqe_data; + unsigned int cqe_idx_mask; + int rc; + + cqe_size = flexboot_nodnic->callbacks->get_cqe_size(); + while ( TRUE ) { + /* Look for completion entry */ + cqe_idx_mask = ( cq->num_cqes - 1 ); + cqe = ((uint8_t *)flexboot_nodnic_cq->nodnic_completion_queue->cq_virt) + + cqe_size * (cq->next_idx & cqe_idx_mask); + + /* TODO: check fill_completion */ + flexboot_nodnic->callbacks->fill_completion(cqe, &cqe_data); + if ( cqe_data.owner ^ + ( ( cq->next_idx & cq->num_cqes ) ? 1 : 0 ) ) { + /* Entry still owned by hardware; end of poll */ + break; + } + /* Handle completion */ + rc = flexboot_nodnic_complete ( ibdev, cq, &cqe_data ); + if ( rc != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx failed to complete: %s\n", + flexboot_nodnic, cq->cqn, strerror ( rc ) ); + DBGC_HDA ( flexboot_nodnic, virt_to_phys ( cqe ), + cqe, sizeof ( *cqe ) ); + } + + /* Update completion queue's index */ + cq->next_idx++; + } +} +/*************************************************************************** + * + * Queue pair operations + * + *************************************************************************** + */ + + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int flexboot_nodnic_create_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp; + mlx_status status = MLX_SUCCESS; + + flexboot_nodnic_qp = (struct flexboot_nodnic_queue_pair *)zalloc(sizeof(*flexboot_nodnic_qp)); + if ( flexboot_nodnic_qp == NULL ) { + status = MLX_OUT_OF_RESOURCES; + goto qp_alloc_err; + } + + status = nodnic_port_create_qp(&port->port_priv, qp->type, + qp->send.num_wqes * sizeof(struct nodnic_send_wqbb), + qp->send.num_wqes, + qp->recv.num_wqes * sizeof(struct nodnic_recv_wqe), + qp->recv.num_wqes, + &flexboot_nodnic_qp->nodnic_queue_pair); + MLX_FATAL_CHECK_STATUS(status, create_err, + "nodnic_port_create_qp failed"); + ib_qp_set_drvdata ( qp, flexboot_nodnic_qp ); + return status; +create_err: + free(flexboot_nodnic_qp); +qp_alloc_err: + return status; +} + +/** + * Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int flexboot_nodnic_modify_qp ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp __unused) { + /*not needed*/ + return 0; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void flexboot_nodnic_destroy_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); + + nodnic_port_destroy_qp(&port->port_priv, qp->type, + flexboot_nodnic_qp->nodnic_queue_pair); + + free(flexboot_nodnic_qp); +} + +/*************************************************************************** + * + * Work request operations + * + *************************************************************************** + */ + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int flexboot_nodnic_post_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf) { + + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct ib_work_queue *wq = &qp->send; + struct nodnic_send_wqbb *wqbb; + nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; + struct nodnic_send_ring *send_ring = &nodnic_qp->send; + mlx_status status = MLX_SUCCESS; + unsigned int wqe_idx_mask; + unsigned long wqe_idx; + + if ( ( port->port_priv.dma_state == FALSE ) || + ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic DMA disabled\n"); + status = -ENETDOWN; + goto post_send_done; + } + + /* Allocate work queue entry */ + wqe_idx = wq->next_idx; + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wqe_idx & wqe_idx_mask] ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx send queue full\n", + flexboot_nodnic, qp->qpn ); + status = -ENOBUFS; + goto post_send_done; + } + wqbb = &send_ring->wqe_virt[wqe_idx & wqe_idx_mask]; + wq->iobufs[wqe_idx & wqe_idx_mask] = iobuf; + + assert ( flexboot_nodnic->callbacks-> + fill_send_wqe[qp->type] != NULL ); + status = flexboot_nodnic->callbacks-> + fill_send_wqe[qp->type] ( ibdev, qp, av, iobuf, + wqbb, wqe_idx ); + if ( status != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx fill send wqe failed\n", + flexboot_nodnic, qp->qpn ); + goto post_send_done; + } + + wq->next_idx++; + + status = port->port_priv.send_doorbell ( &port->port_priv, + &send_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); + if ( flexboot_nodnic->callbacks->tx_uar_send_doorbell_fn ) { + flexboot_nodnic->callbacks->tx_uar_send_doorbell_fn ( ibdev, wqbb ); + } + if ( status != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring send doorbell failed\n", flexboot_nodnic ); + } + +post_send_done: + return status; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int flexboot_nodnic_post_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct ib_work_queue *wq = &qp->recv; + nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; + struct nodnic_recv_ring *recv_ring = &nodnic_qp->receive; + struct nodnic_recv_wqe *wqe; + unsigned int wqe_idx_mask; + mlx_status status = MLX_SUCCESS; + + /* Allocate work queue entry */ + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p QPN %#lx receive queue full\n", + flexboot_nodnic, qp->qpn ); + status = -ENOBUFS; + goto post_recv_done; + } + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = &((struct nodnic_recv_wqe*)recv_ring->wqe_virt)[wq->next_idx & wqe_idx_mask]; + + MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( &wqe->data[0], 1, l_key, flexboot_nodnic->device_priv.lkey ); + MLX_FILL_H ( &wqe->data[0], 2, + local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( &wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); + + wq->next_idx++; + + status = port->port_priv.recv_doorbell ( &port->port_priv, + &recv_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); + if ( status != 0 ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring receive doorbell failed\n", flexboot_nodnic ); + } +post_recv_done: + return status; +} + +/*************************************************************************** + * + * Event queues + * + *************************************************************************** + */ + +static void flexboot_nodnic_poll_eq ( struct ib_device *ibdev ) { + struct flexboot_nodnic *flexboot_nodnic; + struct flexboot_nodnic_port *port; + struct net_device *netdev; + nodnic_port_state state = 0; + mlx_status status; + + if ( ! ibdev ) { + DBG ( "%s: ibdev = NULL!!!\n", __FUNCTION__ ); + return; + } + + flexboot_nodnic = ib_get_drvdata ( ibdev ); + port = &flexboot_nodnic->port[ibdev->port - 1]; + netdev = port->netdev; + + if ( ! netdev_is_open ( netdev ) ) { + DBG2( "%s: port %d is closed\n", __FUNCTION__, port->ibdev->port ); + return; + } + + /* we don't poll EQ. Just poll link status if it's not active */ + if ( ! netdev_link_ok ( netdev ) ) { + status = nodnic_port_get_state ( &port->port_priv, &state ); + MLX_FATAL_CHECK_STATUS(status, state_err, "nodnic_port_get_state failed"); + + if ( state == nodnic_port_state_active ) { + DBG( "%s: port %d physical link is up\n", __FUNCTION__, + port->ibdev->port ); + port->type->state_change ( flexboot_nodnic, port, 1 ); + } + } +state_err: + return; +} + +/*************************************************************************** + * + * Multicast group operations + * + *************************************************************************** + */ +static int flexboot_nodnic_mcast_attach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + mlx_mac_address mac; + mlx_status status = MLX_SUCCESS; + + switch (qp->type) { + case IB_QPT_ETH: + memcpy(&mac, &gid, sizeof(mac)); + status = nodnic_port_add_mac_filter(&port->port_priv, mac); + MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err, + "nodnic_port_add_mac_filter failed"); + break; + default: + break; + } +mac_err: + return status; +} +static void flexboot_nodnic_mcast_detach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid ) { + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + mlx_mac_address mac; + mlx_status status = MLX_SUCCESS; + + switch (qp->type) { + case IB_QPT_ETH: + memcpy(&mac, &gid, sizeof(mac)); + status = nodnic_port_remove_mac_filter(&port->port_priv, mac); + MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err, + "nodnic_port_remove_mac_filter failed"); + break; + default: + break; + } +mac_err: + return; +} +/*************************************************************************** + * + * Infiniband link-layer operations + * + *************************************************************************** + */ + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int flexboot_nodnic_ib_open ( struct ib_device *ibdev __unused) { + int rc = 0; + + /*TODO: add implementation*/ + return rc; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void flexboot_nodnic_ib_close ( struct ib_device *ibdev __unused) { + /*TODO: add implementation*/ +} + +/** + * Inform embedded subnet management agent of a received MAD + * + * @v ibdev Infiniband device + * @v mad MAD + * @ret rc Return status code + */ +static int flexboot_nodnic_inform_sma ( struct ib_device *ibdev __unused, + union ib_mad *mad __unused) { + /*TODO: add implementation*/ + return 0; +} + +/** flexboot_nodnic Infiniband operations */ +static struct ib_device_operations flexboot_nodnic_ib_operations = { + .create_cq = flexboot_nodnic_create_cq, + .destroy_cq = flexboot_nodnic_destroy_cq, + .create_qp = flexboot_nodnic_create_qp, + .modify_qp = flexboot_nodnic_modify_qp, + .destroy_qp = flexboot_nodnic_destroy_qp, + .post_send = flexboot_nodnic_post_send, + .post_recv = flexboot_nodnic_post_recv, + .poll_cq = flexboot_nodnic_poll_cq, + .poll_eq = flexboot_nodnic_poll_eq, + .open = flexboot_nodnic_ib_open, + .close = flexboot_nodnic_ib_close, + .mcast_attach = flexboot_nodnic_mcast_attach, + .mcast_detach = flexboot_nodnic_mcast_detach, + .set_port_info = flexboot_nodnic_inform_sma, + .set_pkey_table = flexboot_nodnic_inform_sma, +}; +/*************************************************************************** + * + * + * + *************************************************************************** + */ + +#define FLEX_NODNIC_TX_POLL_TOUT 500000 +#define FLEX_NODNIC_TX_POLL_USLEEP 10 + +static void flexboot_nodnic_complete_all_tx ( struct flexboot_nodnic_port *port ) { + struct ib_device *ibdev = port->ibdev; + struct ib_completion_queue *cq; + struct ib_work_queue *wq; + int keep_polling = 0; + int timeout = FLEX_NODNIC_TX_POLL_TOUT; + + list_for_each_entry ( cq, &ibdev->cqs, list ) { + do { + ib_poll_cq ( ibdev, cq ); + keep_polling = 0; + list_for_each_entry ( wq, &cq->work_queues, list ) { + if ( wq->is_send ) + keep_polling += ( wq->fill > 0 ); + } + udelay ( FLEX_NODNIC_TX_POLL_USLEEP ); + } while ( keep_polling && ( timeout-- > 0 ) ); + } +} + +static void flexboot_nodnic_port_disable_dma ( struct flexboot_nodnic_port *port ) { + nodnic_port_priv *port_priv = & ( port->port_priv ); + mlx_status status; + + if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) ) + return; + + port_priv->port_state |= NODNIC_PORT_DISABLING_DMA; + flexboot_nodnic_complete_all_tx ( port ); + if ( ( status = nodnic_port_disable_dma ( port_priv ) ) ) { + MLX_DEBUG_WARN ( port, "Failed to disable DMA %d\n", status ); + } + + port_priv->port_state &= ~NODNIC_PORT_DISABLING_DMA; +} + +/*************************************************************************** + * + * Ethernet operation + * + *************************************************************************** + */ + +/** Number of flexboot_nodnic Ethernet send work queue entries */ +#define FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES 64 + +/** Number of flexboot_nodnic Ethernet receive work queue entries */ +#define FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES 64 +/** flexboot nodnic Ethernet queue pair operations */ +static struct ib_queue_pair_operations flexboot_nodnic_eth_qp_op = { + .alloc_iob = alloc_iob, +}; + +/** + * Transmit packet via flexboot_nodnic Ethernet device + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int flexboot_nodnic_eth_transmit ( struct net_device *netdev, + struct io_buffer *iobuf) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + int rc; + + rc = ib_post_send ( ibdev, port->eth_qp, NULL, iobuf); + /* Transmit packet */ + if ( rc != 0) { + DBGC ( flexboot_nodnic, "NODNIC %p port %d could not transmit: %s\n", + flexboot_nodnic, ibdev->port, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle flexboot_nodnic Ethernet device send completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void flexboot_nodnic_eth_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, + int rc) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** + * Handle flexboot_nodnic Ethernet device receive completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void flexboot_nodnic_eth_complete_recv ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct ib_address_vector *dest __unused, + struct ib_address_vector *source, + struct io_buffer *iobuf, + int rc) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + + if ( rc != 0 ) { + DBG ( "Received packet with error\n" ); + netdev_rx_err ( netdev, iobuf, rc ); + return; + } + + if ( source == NULL ) { + DBG ( "Received packet without address vector\n" ); + netdev_rx_err ( netdev, iobuf, -ENOTTY ); + return; + } + + netdev_rx ( netdev, iobuf ); +} + +/** flexboot_nodnic Ethernet device completion operations */ +static struct ib_completion_queue_operations flexboot_nodnic_eth_cq_op = { + .complete_send = flexboot_nodnic_eth_complete_send, + .complete_recv = flexboot_nodnic_eth_complete_recv, +}; + +/** + * Poll flexboot_nodnic Ethernet device + * + * @v netdev Network device + */ +static void flexboot_nodnic_eth_poll ( struct net_device *netdev) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + + ib_poll_eq ( ibdev ); +} + +/** + * Open flexboot_nodnic Ethernet device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + mlx_status status = MLX_SUCCESS; + struct ib_completion_queue *dummy_cq = NULL; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = NULL; + 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", + __FUNCTION__, port->ibdev->port ); + return 0; + } + + port->port_priv.port_state |= NODNIC_PORT_OPENED; + + dummy_cq = zalloc ( sizeof ( struct ib_completion_queue ) ); + if ( dummy_cq == NULL ) { + DBGC ( flexboot_nodnic, "%s: Failed to allocate dummy CQ\n", __FUNCTION__ ); + status = MLX_OUT_OF_RESOURCES; + goto err_create_dummy_cq; + } + INIT_LIST_HEAD ( &dummy_cq->work_queues ); + + 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; + goto err_create_qp; + } + + ib_qp_set_ownerdata ( port->eth_qp, netdev ); + + status = nodnic_port_get_cq_size(&port->port_priv, &cq_size); + MLX_FATAL_CHECK_STATUS(status, get_cq_size_err, + "nodnic_port_get_cq_size failed"); + + 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 ); + status = MLX_OUT_OF_RESOURCES; + goto err_create_cq; + } + port->eth_qp->send.cq = port->eth_cq; + 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 ); + + status = nodnic_port_allocate_eq(&port->port_priv, + flexboot_nodnic->device_priv.device_cap.log_working_buffer_size); + MLX_FATAL_CHECK_STATUS(status, eq_alloc_err, + "nodnic_port_allocate_eq failed"); + + status = nodnic_port_init(&port->port_priv); + MLX_FATAL_CHECK_STATUS(status, init_err, + "nodnic_port_init failed"); + + /* update qp - qpn */ + flexboot_nodnic_qp = ib_qp_get_drvdata ( port->eth_qp ); + status = nodnic_port_get_qpn(&port->port_priv, + &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring, + &qpn); + MLX_FATAL_CHECK_STATUS(status, qpn_err, + "nodnic_port_get_qpn failed"); + port->eth_qp->qpn = qpn; + + /* Fill receive rings */ + ib_refill_recv ( ibdev, port->eth_qp ); + + status = nodnic_port_enable_dma(&port->port_priv); + MLX_FATAL_CHECK_STATUS(status, dma_err, + "nodnic_port_enable_dma failed"); + + if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) { + status = nodnic_port_set_promisc(&port->port_priv, TRUE); + MLX_FATAL_CHECK_STATUS(status, promisc_err, + "nodnic_port_set_promisc failed"); + } + + status = nodnic_port_get_state(&port->port_priv, &state); + MLX_FATAL_CHECK_STATUS(status, state_err, + "nodnic_port_get_state failed"); + + port->type->state_change ( + flexboot_nodnic, port, state == nodnic_port_state_active ); + + DBGC ( flexboot_nodnic, "%s: port %d opened (link is %s)\n", + __FUNCTION__, port->ibdev->port, + ( ( state == nodnic_port_state_active ) ? "Up" : "Down" ) ); + + free(dummy_cq); + return 0; +state_err: +promisc_err: +dma_err: +qpn_err: + nodnic_port_close(&port->port_priv); +init_err: + nodnic_port_free_eq(&port->port_priv); +eq_alloc_err: +err_create_cq: +get_cq_size_err: + ib_destroy_qp(ibdev, port->eth_qp ); +err_create_qp: + free(dummy_cq); +err_create_dummy_cq: + port->port_priv.port_state &= ~NODNIC_PORT_OPENED; + return status; +} + +/** + * Close flexboot_nodnic Ethernet device + * + * @v netdev Network device + */ +static void flexboot_nodnic_eth_close ( struct net_device *netdev) { + struct flexboot_nodnic_port *port = netdev->priv; + struct ib_device *ibdev = port->ibdev; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + mlx_status status = MLX_SUCCESS; + + if ( ! ( port->port_priv.port_state & NODNIC_PORT_OPENED ) ) { + DBGC ( flexboot_nodnic, "%s: port %d is already closed\n", + __FUNCTION__, port->ibdev->port ); + return; + } + + if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) { + if ( ( status = nodnic_port_set_promisc( &port->port_priv, FALSE ) ) ) { + DBGC ( flexboot_nodnic, + "nodnic_port_set_promisc failed (status = %d)\n", status ); + } + } + + flexboot_nodnic_port_disable_dma ( port ); + + port->port_priv.port_state &= ~NODNIC_PORT_OPENED; + + port->type->state_change ( flexboot_nodnic, port, FALSE ); + + /* Close port */ + status = nodnic_port_close(&port->port_priv); + if ( status != MLX_SUCCESS ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not close port: %s\n", + flexboot_nodnic, ibdev->port, strerror ( status ) ); + /* Nothing we can do about this */ + } + + ib_destroy_qp ( ibdev, port->eth_qp ); + port->eth_qp = NULL; + ib_destroy_cq ( ibdev, port->eth_cq ); + port->eth_cq = NULL; + + nodnic_port_free_eq(&port->port_priv); + + DBGC ( flexboot_nodnic, "%s: port %d closed\n", __FUNCTION__, port->ibdev->port ); +} + +void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ) { + struct flexboot_nodnic_port *port = netdev->priv; + + if ( enable ) { + if ( ( port->port_priv.port_state & NODNIC_PORT_OPENED ) && + ! ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) { + flexboot_nodnic_arm_cq ( port ); + } else { + /* do nothing */ + } + } else { + nodnic_device_clear_int( port->port_priv.device ); + } +} + +/** flexboot_nodnic Ethernet network device operations */ +static struct net_device_operations flexboot_nodnic_eth_operations = { + .open = flexboot_nodnic_eth_open, + .close = flexboot_nodnic_eth_close, + .transmit = flexboot_nodnic_eth_transmit, + .poll = flexboot_nodnic_eth_poll, +}; + +/** + * Register flexboot_nodnic Ethernet device + */ +static int flexboot_nodnic_register_netdev ( struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port) { + mlx_status status = MLX_SUCCESS; + struct net_device *netdev; + struct ib_device *ibdev = port->ibdev; + union { + uint8_t bytes[8]; + uint32_t dwords[2]; + } mac; + + /* Allocate network devices */ + netdev = alloc_etherdev ( 0 ); + if ( netdev == NULL ) { + DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not allocate net device\n", + flexboot_nodnic, ibdev->port ); + status = MLX_OUT_OF_RESOURCES; + goto alloc_err; + } + port->netdev = netdev; + netdev_init ( netdev, &flexboot_nodnic_eth_operations ); + netdev->dev = ibdev->dev; + netdev->priv = port; + + status = nodnic_port_query(&port->port_priv, + nodnic_port_option_mac_high, + &mac.dwords[0]); + MLX_FATAL_CHECK_STATUS(status, mac_err, + "failed to query mac high"); + status = nodnic_port_query(&port->port_priv, + nodnic_port_option_mac_low, + &mac.dwords[1]); + MLX_FATAL_CHECK_STATUS(status, mac_err, + "failed to query mac low"); + mac.dwords[0] = htonl(mac.dwords[0]); + mac.dwords[1] = htonl(mac.dwords[1]); + memcpy ( netdev->hw_addr, + &mac.bytes[2], ETH_ALEN); + /* Register network device */ + status = register_netdev ( netdev ); + if ( status != MLX_SUCCESS ) { + DBGC ( flexboot_nodnic, + "flexboot_nodnic %p port %d could not register network device: %s\n", + flexboot_nodnic, ibdev->port, strerror ( status ) ); + goto reg_err; + } + return status; +reg_err: +mac_err: + netdev_put ( netdev ); +alloc_err: + return status; +} + +/** + * Handle flexboot_nodnic Ethernet device port state change + */ +static void flexboot_nodnic_state_change_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused, + struct flexboot_nodnic_port *port, + int link_up ) { + struct net_device *netdev = port->netdev; + + if ( link_up ) + netdev_link_up ( netdev ); + else + netdev_link_down ( netdev ); + +} + +/** + * Unregister flexboot_nodnic Ethernet device + */ +static void flexboot_nodnic_unregister_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused, + struct flexboot_nodnic_port *port ) { + struct net_device *netdev = port->netdev; + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** flexboot_nodnic Ethernet port type */ +static struct flexboot_nodnic_port_type flexboot_nodnic_port_type_eth = { + .register_dev = flexboot_nodnic_register_netdev, + .state_change = flexboot_nodnic_state_change_netdev, + .unregister_dev = flexboot_nodnic_unregister_netdev, +}; + +/*************************************************************************** + * + * PCI interface helper functions + * + *************************************************************************** + */ +static +mlx_status +flexboot_nodnic_allocate_infiniband_devices( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + struct pci_device *pci = flexboot_nodnic_priv->pci; + struct ib_device *ibdev = NULL; + unsigned int i = 0; + + /* Allocate Infiniband devices */ + for (; i < device_priv->device_cap.num_ports; i++) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + ibdev = alloc_ibdev(0); + if (ibdev == NULL) { + status = MLX_OUT_OF_RESOURCES; + goto err_alloc_ibdev; + } + flexboot_nodnic_priv->port[i].ibdev = ibdev; + ibdev->op = &flexboot_nodnic_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = ( FLEXBOOT_NODNIC_PORT_BASE + i); + ib_set_drvdata(ibdev, flexboot_nodnic_priv); + } + return status; +err_alloc_ibdev: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) + ibdev_put ( flexboot_nodnic_priv->port[i].ibdev ); + return status; +} + +static +mlx_status +flexboot_nodnic_thin_init_ports( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + nodnic_port_priv *port_priv = NULL; + unsigned int i = 0; + + for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port_priv = &flexboot_nodnic_priv->port[i].port_priv; + status = nodnic_port_thin_init( device_priv, port_priv, i ); + MLX_FATAL_CHECK_STATUS(status, thin_init_err, + "flexboot_nodnic_thin_init_ports failed"); + } +thin_init_err: + return status; +} + + +static +mlx_status +flexboot_nodnic_set_ports_type ( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + nodnic_port_priv *port_priv = NULL; + nodnic_port_type type = NODNIC_PORT_TYPE_UNKNOWN; + unsigned int i = 0; + + for ( i = 0 ; i < device_priv->device_cap.num_ports ; i++ ) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port_priv = &flexboot_nodnic_priv->port[i].port_priv; + status = nodnic_port_get_type(port_priv, &type); + MLX_FATAL_CHECK_STATUS(status, type_err, + "nodnic_port_get_type failed"); + switch ( type ) { + case NODNIC_PORT_TYPE_ETH: + DBGC ( flexboot_nodnic_priv, "Port %d type is Ethernet\n", i ); + flexboot_nodnic_priv->port[i].type = &flexboot_nodnic_port_type_eth; + break; + case NODNIC_PORT_TYPE_IB: + DBGC ( flexboot_nodnic_priv, "Port %d type is Infiniband\n", i ); + status = MLX_UNSUPPORTED; + goto type_err; + default: + DBGC ( flexboot_nodnic_priv, "Port %d type is unknown\n", i ); + status = MLX_UNSUPPORTED; + goto type_err; + } + } +type_err: + return status; +} + +static +mlx_status +flexboot_nodnic_ports_register_dev( struct flexboot_nodnic *flexboot_nodnic_priv ) { + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + struct flexboot_nodnic_port *port = NULL; + unsigned int i = 0; + + for (; i < device_priv->device_cap.num_ports; i++) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port = &flexboot_nodnic_priv->port[i]; + status = port->type->register_dev ( flexboot_nodnic_priv, port ); + MLX_FATAL_CHECK_STATUS(status, reg_err, + "port register_dev failed"); + } +reg_err: + return status; +} + +static +mlx_status +flexboot_nodnic_ports_unregister_dev ( struct flexboot_nodnic *flexboot_nodnic_priv ) { + struct flexboot_nodnic_port *port; + nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; + int i = (device_priv->device_cap.num_ports - 1); + + for (; i >= 0; i--) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + port = &flexboot_nodnic_priv->port[i]; + port->type->unregister_dev(flexboot_nodnic_priv, port); + ibdev_put(flexboot_nodnic_priv->port[i].ibdev); + } + return MLX_SUCCESS; +} + +/*************************************************************************** + * + * flexboot nodnic interface + * + *************************************************************************** + */ +__unused static void flexboot_nodnic_enable_dma ( struct flexboot_nodnic *nodnic ) { + nodnic_port_priv *port_priv; + mlx_status status; + int i; + + for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) { + if ( ! ( nodnic->port_mask & ( i + 1 ) ) ) + continue; + port_priv = & ( nodnic->port[i].port_priv ); + if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) ) + continue; + + if ( ( status = nodnic_port_enable_dma ( port_priv ) ) ) { + MLX_DEBUG_WARN ( nodnic, "Failed to enable DMA %d\n", status ); + } + } +} + +__unused static void flexboot_nodnic_disable_dma ( struct flexboot_nodnic *nodnic ) { + int i; + + for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) { + if ( ! ( nodnic->port_mask & ( i + 1 ) ) ) + continue; + flexboot_nodnic_port_disable_dma ( & ( nodnic->port[i] ) ); + } +} + +int flexboot_nodnic_is_supported ( struct pci_device *pci ) { + mlx_utils utils; + mlx_pci_gw_buffer buffer; + mlx_status status; + int is_supported = 0; + + DBG ( "%s: start\n", __FUNCTION__ ); + + memset ( &utils, 0, sizeof ( utils ) ); + + status = mlx_utils_init ( &utils, pci ); + MLX_CHECK_STATUS ( pci, status, utils_init_err, "mlx_utils_init failed" ); + + status = mlx_pci_gw_init ( &utils ); + MLX_CHECK_STATUS ( pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" ); + + status = mlx_pci_gw_read ( &utils, PCI_GW_SPACE_NODNIC, + NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, &buffer ); + + if ( status == MLX_SUCCESS ) { + buffer >>= NODNIC_NIC_INTERFACE_SUPPORTED_BIT; + is_supported = ( buffer & 0x1 ); + } + + mlx_pci_gw_teardown( &utils ); + +pci_gw_init_err: + mlx_utils_teardown(&utils); +utils_init_err: + DBG ( "%s: NODNIC is %s supported (status = %d)\n", + __FUNCTION__, ( is_supported ? "": "not" ), status ); + return is_supported; +} + + +void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, + uint16_t high_byte ) { + union mac_addr { + struct { + uint32_t low_byte; + uint16_t high_byte; + }; + uint8_t mac_addr[ETH_ALEN]; + } mac_addr_aux; + + mac_addr_aux.high_byte = high_byte; + mac_addr_aux.low_byte = low_byte; + + mac_addr[0] = mac_addr_aux.mac_addr[5]; + mac_addr[1] = mac_addr_aux.mac_addr[4]; + mac_addr[2] = mac_addr_aux.mac_addr[3]; + mac_addr[3] = mac_addr_aux.mac_addr[2]; + mac_addr[4] = mac_addr_aux.mac_addr[1]; + mac_addr[5] = mac_addr_aux.mac_addr[0]; +} + +static mlx_status flexboot_nodnic_get_factory_mac ( + struct flexboot_nodnic *flexboot_nodnic_priv, uint8_t port __unused ) { + struct mlx_vmac_query_virt_mac virt_mac; + mlx_status status; + + memset ( & virt_mac, 0, sizeof ( virt_mac ) ); + status = mlx_vmac_query_virt_mac ( flexboot_nodnic_priv->device_priv.utils, + &virt_mac ); + if ( ! status ) { + DBGC ( flexboot_nodnic_priv, "NODNIC %p Failed to set the virtual MAC\n" + ,flexboot_nodnic_priv ); + } + + return status; +} + + +/** + * Set port masking + * + * @v flexboot_nodnic nodnic device + * @ret rc Return status code + */ +static int flexboot_nodnic_set_port_masking ( struct flexboot_nodnic *flexboot_nodnic ) { + unsigned int i; + nodnic_device_priv *device_priv = &flexboot_nodnic->device_priv; + + flexboot_nodnic->port_mask = 0; + for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { + flexboot_nodnic->port_mask |= (i + 1); + } + + if ( ! flexboot_nodnic->port_mask ) { + /* No port was enabled */ + DBGC ( flexboot_nodnic, "NODNIC %p No port was enabled for " + "booting\n", flexboot_nodnic ); + return -ENETUNREACH; + } + + return 0; +} + +int init_mlx_utils ( mlx_utils **utils, struct pci_device *pci ) { + int rc = 0; + + *utils = ( mlx_utils * ) zalloc ( sizeof ( mlx_utils ) ); + if ( *utils == NULL ) { + DBGC ( utils, "%s: Failed to allocate utils\n", __FUNCTION__ ); + rc = -1; + goto err_utils_alloc; + } + if ( mlx_utils_init ( *utils, pci ) ) { + DBGC ( utils, "%s: mlx_utils_init failed\n", __FUNCTION__ ); + rc = -1; + goto err_utils_init; + } + if ( mlx_pci_gw_init ( *utils ) ){ + DBGC ( utils, "%s: mlx_pci_gw_init failed\n", __FUNCTION__ ); + rc = -1; + goto err_cmd_init; + } + + return 0; + + mlx_pci_gw_teardown ( *utils ); +err_cmd_init: + mlx_utils_teardown ( *utils ); +err_utils_init: + free ( *utils ); +err_utils_alloc: + *utils = NULL; + + return rc; +} + +void free_mlx_utils ( mlx_utils **utils ) { + + mlx_pci_gw_teardown ( *utils ); + mlx_utils_teardown ( *utils ); + free ( *utils ); + *utils = NULL; +} + +/** + * Initialise Nodnic PCI parameters + * + * @v hermon Nodnic device + */ +static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) { + mlx_status status = MLX_SUCCESS; + struct pci_device *pci = flexboot_nodnic->pci; + nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar; + + 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; + } + /* read uar offset then allocate */ + if ( ( status = nodnic_port_set_send_uar_offset ( &flexboot_nodnic->port[0].port_priv ) ) ) { + DBGC ( flexboot_nodnic, "%s: nodnic_port_set_send_uar_offset failed," + "status = %d\n", __FUNCTION__, status ); + return -EINVAL; + } + uar->phys = ( pci_bar_start ( pci, FLEXBOOT_NODNIC_HCA_BAR ) + (mlx_uint32)uar->offset ); + uar->virt = ( void * )( ioremap ( uar->phys, FLEXBOOT_NODNIC_PAGE_SIZE ) ); + + 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 ) { + mlx_status status = MLX_SUCCESS; + struct flexboot_nodnic *flexboot_nodnic_priv = NULL; + nodnic_device_priv *device_priv = NULL; + int i = 0; + + if ( ( pci == NULL ) || ( callbacks == NULL ) ) { + DBGC ( flexboot_nodnic_priv, "%s: Bad Parameter\n", __FUNCTION__ ); + return -EINVAL; + } + + flexboot_nodnic_priv = zalloc( sizeof ( *flexboot_nodnic_priv ) ); + if ( flexboot_nodnic_priv == NULL ) { + DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate priv data\n", __FUNCTION__ ); + status = MLX_OUT_OF_RESOURCES; + goto device_err_alloc; + } + + /* Register settings + * Note that pci->priv will be the device private data */ + flexboot_nodnic_priv->pci = pci; + flexboot_nodnic_priv->callbacks = callbacks; + pci_set_drvdata ( pci, flexboot_nodnic_priv ); + + device_priv = &flexboot_nodnic_priv->device_priv; + /* init mlx utils */ + status = init_mlx_utils ( & device_priv->utils, pci ); + MLX_FATAL_CHECK_STATUS(status, err_utils_init, + "init_mlx_utils failed"); + + /* init device */ + status = nodnic_device_init( device_priv ); + MLX_FATAL_CHECK_STATUS(status, device_init_err, + "nodnic_device_init failed"); + + status = nodnic_device_get_cap( device_priv ); + 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"); + + status = flexboot_nodnic_allocate_infiniband_devices( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, err_alloc_ibdev, + "flexboot_nodnic_allocate_infiniband_devices failed"); + + /* port init */ + status = flexboot_nodnic_thin_init_ports( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, err_thin_init_ports, + "flexboot_nodnic_thin_init_ports failed"); + + if ( ( status = flexboot_nodnic_alloc_uar ( flexboot_nodnic_priv ) ) ) { + DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_alloc_uar failed" + " ( status = %d )\n",__FUNCTION__, status ); + } + + /* device reg */ + status = flexboot_nodnic_set_ports_type( flexboot_nodnic_priv ); + MLX_CHECK_STATUS( flexboot_nodnic_priv, status, err_set_ports_types, + "flexboot_nodnic_set_ports_type failed"); + + status = flexboot_nodnic_ports_register_dev( flexboot_nodnic_priv ); + MLX_FATAL_CHECK_STATUS(status, reg_err, + "flexboot_nodnic_ports_register_dev failed"); + + for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { + if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) + continue; + flexboot_nodnic_get_factory_mac ( flexboot_nodnic_priv, i ); + } + + /* Update ETH operations with IRQ function if supported */ + DBGC ( flexboot_nodnic_priv, "%s: %s IRQ function\n", + __FUNCTION__, ( callbacks->irq ? "Valid" : "No" ) ); + flexboot_nodnic_eth_operations.irq = callbacks->irq; + return 0; + + 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: +get_cap_err: + nodnic_device_teardown ( device_priv ); +device_init_err: + free_mlx_utils ( & device_priv->utils ); +err_utils_init: + free ( flexboot_nodnic_priv ); +device_err_alloc: + return status; +} + +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 ); + free( flexboot_nodnic_priv ); +} diff --git a/src/drivers/infiniband/flexboot_nodnic.h b/src/drivers/infiniband/flexboot_nodnic.h new file mode 100644 index 000000000..84a6768c1 --- /dev/null +++ b/src/drivers/infiniband/flexboot_nodnic.h @@ -0,0 +1,190 @@ +#ifndef SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_H_ +#define SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_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_nodnic/include/mlx_nodnic_data_structures.h" +#include "nodnic_prm.h" +#include +#include +#include +#include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" + +/* + * If defined, use interrupts in NODNIC driver + */ +#define NODNIC_IRQ_ENABLED + +#define FLEXBOOT_NODNIC_MAX_PORTS 2 +#define FLEXBOOT_NODNIC_PORT_BASE 1 + +#define FLEXBOOT_NODNIC_OPCODE_SEND 0xa +#define FLEXBOOT_NODNIC_HCA_BAR PCI_BASE_ADDRESS_0 //BAR 0 +#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 { + FLEXBOOT_NODNIC_PROT_IB_IPV6 = 0, + FLEXBOOT_NODNIC_PROT_ETH, + FLEXBOOT_NODNIC_PROT_IB_IPV4, + FLEXBOOT_NODNIC_PROT_FCOE +}; + +/** A flexboot nodnic port */ +struct flexboot_nodnic_port { + /** Infiniband device */ + struct ib_device *ibdev; + /** Network device */ + struct net_device *netdev; + /** nodic port */ + nodnic_port_priv port_priv; + /** Port type */ + struct flexboot_nodnic_port_type *type; + /** Ethernet completion queue */ + struct ib_completion_queue *eth_cq; + /** Ethernet queue pair */ + struct ib_queue_pair *eth_qp; + mlx_uint8 cmdsn; +}; + + +/** A flexboot nodnic queue pair */ +struct flexboot_nodnic_queue_pair { + nodnic_qp *nodnic_queue_pair; +}; + +/** A flexboot nodnic cq */ +struct flexboot_nodnic_completion_queue { + nodnic_cq *nodnic_completion_queue; +}; + +/** A flexboot_nodnic device */ +struct flexboot_nodnic { + /** PCI device */ + struct pci_device *pci; + /** nic specific data*/ + struct flexboot_nodnic_callbacks *callbacks; + /**nodnic device*/ + nodnic_device_priv device_priv; + /**flexboot_nodnic ports*/ + struct flexboot_nodnic_port port[FLEXBOOT_NODNIC_MAX_PORTS]; + /** Device open request counter */ + unsigned int open_count; + /** Port masking */ + u16 port_mask; + /** device private data */ + void *priv_data; +}; + +/** A flexboot_nodnic port type */ +struct flexboot_nodnic_port_type { + /** Register port + * + * @v flexboot_nodnic flexboot_nodnic device + * @v port flexboot_nodnic port + * @ret mlx_status Return status code + */ + mlx_status ( * register_dev ) ( + struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port + ); + /** Port state changed + * + * @v flexboot_nodnic flexboot_nodnic device + * @v port flexboot_nodnic port + * @v link_up Link is up + */ + void ( * state_change ) ( + struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port, + int link_up + ); + /** Unregister port + * + * @v flexboot_nodnic flexboot_nodnic device + * @v port flexboot_nodnic port + */ + void ( * unregister_dev ) ( + struct flexboot_nodnic *flexboot_nodnic, + struct flexboot_nodnic_port *port + ); +}; + +struct cqe_data{ + mlx_boolean owner; + mlx_uint32 qpn; + mlx_uint32 is_send; + mlx_uint32 is_error; + mlx_uint32 syndrome; + mlx_uint32 vendor_err_syndrome; + mlx_uint32 wqe_counter; + mlx_uint32 byte_cnt; +}; + +union arm_cq_uar { + struct { + //big endian + mlx_uint32 reserved0 :2; + mlx_uint32 cmdn :2; + mlx_uint32 reserved1 :3; + mlx_uint32 cmd :1; + mlx_uint32 cq_ci :24; + mlx_uint32 reserved2 :8; + mlx_uint32 cq_n :24; + }; + mlx_uint32 dword[2]; + mlx_uint64 qword; +}; + +struct flexboot_nodnic_callbacks { + mlx_status ( * fill_completion ) ( void *cqe, struct cqe_data *cqe_data ); + mlx_status ( * cqe_set_owner ) ( void *cq, unsigned int num_cqes ); + mlx_size ( * get_cqe_size ) (); + mlx_status ( * fill_send_wqe[5] ) ( + struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, + struct nodnic_send_wqbb *wqbb, + unsigned long wqe_idx + ); + void ( * irq ) ( struct net_device *netdev, int enable ); + mlx_status ( * tx_uar_send_doorbell_fn ) ( + struct ib_device *ibdev, + struct nodnic_send_wqbb *wqbb + ); +}; + +int flexboot_nodnic_probe ( struct pci_device *pci, + struct flexboot_nodnic_callbacks *callbacks, + void *drv_priv ); +void flexboot_nodnic_remove ( struct pci_device *pci ); +void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ); +int flexboot_nodnic_is_supported ( struct pci_device *pci ); +void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, + uint16_t high_byte ); +int init_mlx_utils ( mlx_utils **utils, struct pci_device *pci ); +void free_mlx_utils ( mlx_utils **utils ); +#endif /* SRC_DRIVERS_INFINIBAND_FLEXBOOT_NODNIC_FLEXBOOT_NODNIC_H_ */ diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c new file mode 100755 index 000000000..18ebfb1e9 --- /dev/null +++ b/src/drivers/infiniband/golan.c @@ -0,0 +1,2650 @@ +/* + * Copyright (C) 2013-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 +#include +#include +#include +#include +#include +#include +#include +#include "flexboot_nodnic.h" +#include +#include +#include +#include +#include +#include "mlx_utils/include/public/mlx_pci_gw.h" +#include +#include +#include "mlx_nodnic/include/mlx_port.h" +#include "nodnic_shomron_prm.h" +#include "golan.h" +#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_fw_areas ( struct golan *golan ) { + int i; + + 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; + } + } +} + +static int golan_init_fw_areas ( struct golan *golan ) { + int rc = 0, i = 0; + + if ( ! golan ) { + rc = -EINVAL; + goto err_golan_init_fw_areas_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[] = { + "RESET", + "INIT", + "RTR", + "RTS", + "SQD", + "SQE", + "ERR" +}; + +static inline int golan_check_rc_and_cmd_status ( struct golan_cmd_layout *cmd, int rc ) { + struct golan_outbox_hdr *out_hdr = ( struct golan_outbox_hdr * ) ( cmd->out ); + if ( rc == -EBUSY ) { + DBG ( "HCA is busy (rc = -EBUSY)\n" ); + return rc; + } else if ( out_hdr->status ) { + DBG("%s status = 0x%x - syndrom = 0x%x\n", __FUNCTION__, + out_hdr->status, be32_to_cpu(out_hdr->syndrome)); + return out_hdr->status; + } + return 0; +} + +#define GOLAN_CHECK_RC_AND_CMD_STATUS(_lable) \ + do { \ + if ( ( rc = golan_check_rc_and_cmd_status ( cmd, rc ) ) ) \ + goto _lable; \ + } while (0) + +#define GOLAN_PRINT_RC_AND_CMD_STATUS golan_check_rc_and_cmd_status ( cmd, rc ) + + +struct mbox { + union { + struct golan_cmd_prot_block mblock; + u8 data[MAILBOX_STRIDE]; + __be64 qdata[MAILBOX_STRIDE >> 3]; + }; +}; + +static inline uint32_t ilog2(uint32_t mem) +{ + return ( fls ( mem ) - 1 ); +} + +#define CTRL_SIG_SZ (sizeof(mailbox->mblock) - sizeof(mailbox->mblock.bdata) - 2) + +static inline u8 xor8_buf(void *buf, int len) +{ + u8 sum = 0; + int i; + u8 *ptr = buf; + + for (i = 0; i < len; ++i) + sum ^= ptr[i]; + + return sum; +} + +static inline const char *cmd_status_str(u8 status) +{ + switch (status) { + case 0x0: return "OK"; + case 0x1: return "internal error"; + case 0x2: return "bad operation"; + case 0x3: return "bad parameter"; + case 0x4: return "bad system state"; + case 0x5: return "bad resource"; + case 0x6: return "resource busy"; + case 0x8: return "limits exceeded"; + case 0x9: return "bad resource state"; + case 0xa: return "bad index"; + case 0xf: return "no resources"; + case 0x50: return "bad input length"; + case 0x51: return "bad output length"; + case 0x10: return "bad QP state"; + case 0x30: return "bad packet (discarded)"; + case 0x40: return "bad size too many outstanding CQEs"; + case 0xff: return "Command Timed Out"; + default: return "unknown status"; + } +} + +static inline uint16_t fw_rev_maj(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->fw_rev)) & 0xffff; +} + +static inline u16 fw_rev_min(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->fw_rev)) >> 16; +} + +static inline u16 fw_rev_sub(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->cmdif_rev_fw_sub)) & 0xffff; +} + +static inline u16 cmdif_rev(struct golan *golan) +{ + return be32_to_cpu(readl(&golan->iseg->cmdif_rev_fw_sub)) >> 16; +} + + +static inline struct golan_cmd_layout *get_cmd( struct golan *golan, int idx ) +{ + return golan->cmd.addr + (idx << golan->cmd.log_stride); +} + +static inline void golan_calc_sig(struct golan *golan, uint32_t cmd_idx, + uint32_t inbox_idx, uint32_t outbox_idx) +{ + struct golan_cmd_layout *cmd = get_cmd(golan, cmd_idx); + struct mbox *mailbox = NULL; + + if (inbox_idx != NO_MBOX) { + mailbox = GET_INBOX(golan, inbox_idx); + mailbox->mblock.token = cmd->token; + mailbox->mblock.ctrl_sig = ~xor8_buf(mailbox->mblock.rsvd0, + CTRL_SIG_SZ); + } + if (outbox_idx != NO_MBOX) { + mailbox = GET_OUTBOX(golan, outbox_idx); + mailbox->mblock.token = cmd->token; + mailbox->mblock.ctrl_sig = ~xor8_buf(mailbox->mblock.rsvd0, + CTRL_SIG_SZ); + } + cmd->sig = ~xor8_buf(cmd, sizeof(*cmd)); +} + +static inline void show_out_status(uint32_t *out) +{ + DBG("%x\n", be32_to_cpu(out[0])); + DBG("%x\n", be32_to_cpu(out[1])); + DBG("%x\n", be32_to_cpu(out[2])); + DBG("%x\n", be32_to_cpu(out[3])); +} +/** + * Check if CMD has finished. + */ +static inline uint32_t is_command_finished( struct golan *golan, int idx) +{ + wmb(); + return !(get_cmd( golan , idx )->status_own & CMD_OWNER_HW); +} + +/** + * Wait for Golan command completion + * + * @v golan Golan device + * @ret rc Return status code + */ +static inline int golan_cmd_wait(struct golan *golan, int idx, const char *command) +{ + unsigned int wait; + int rc = -EBUSY; + + for ( wait = GOLAN_HCR_MAX_WAIT_MS ; wait ; --wait ) { + if (is_command_finished(golan, idx)) { + rc = CMD_STATUS(golan, idx); + rmb(); + break; + } else { + mdelay ( 1 ); + } + } + if (rc) { + DBGC (golan ,"[%s]RC is %s[%x]\n", command, cmd_status_str(rc), rc); + } + + golan->cmd_bm &= ~(1 << idx); + return rc; +} + +/** + * Notify the HW that commands are ready + */ +static inline void send_command(struct golan *golan) +{ + wmb(); //Make sure the command is visible in "memory". + writel(cpu_to_be32(golan->cmd_bm) , &golan->iseg->cmd_dbell); +} + +static inline int send_command_and_wait(struct golan *golan, uint32_t cmd_idx, + uint32_t inbox_idx, uint32_t outbox_idx, const char *command) +{ + golan_calc_sig(golan, cmd_idx, inbox_idx, outbox_idx); + send_command(golan); + return golan_cmd_wait(golan, cmd_idx, command); +} + +/** + * Prepare a FW command, + * In - comamnd idx (Must be valid) + * writes the command parameters. + */ +static inline struct golan_cmd_layout *write_cmd(struct golan *golan, int idx, + uint16_t opcode, uint16_t opmod, + uint16_t inbox_idx, + uint16_t outbox_idx, uint16_t inlen, + uint16_t outlen) +{ + struct golan_cmd_layout *cmd = get_cmd(golan , idx); + struct golan_inbox_hdr *hdr = (struct golan_inbox_hdr *)cmd->in; + static uint8_t token; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->type = GOLAN_PCI_CMD_XPORT; + cmd->status_own = CMD_OWNER_HW; + cmd->outlen = cpu_to_be32(outlen); + cmd->inlen = cpu_to_be32(inlen); + hdr->opcode = cpu_to_be16(opcode); + hdr->opmod = cpu_to_be16(opmod); + + if (inbox_idx != NO_MBOX) { + memset(GET_INBOX(golan, inbox_idx), 0, MAILBOX_SIZE); + cmd->in_ptr = VIRT_2_BE64_BUS(GET_INBOX(golan, inbox_idx)); + cmd->token = ++token; + } + if (outbox_idx != NO_MBOX) { + memset(GET_OUTBOX(golan, outbox_idx), 0, MAILBOX_SIZE); + cmd->out_ptr = VIRT_2_BE64_BUS(GET_OUTBOX(golan, outbox_idx)); + } + + golan->cmd_bm |= 1 << idx; + + assert ( cmd != NULL ); + return cmd; +} + +static inline int golan_core_enable_hca(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ENABLE_HCA, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_enable_hca_mbox_in), + sizeof(struct golan_enable_hca_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + return rc; +} + +static inline void golan_disable_hca(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DISABLE_HCA, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_disable_hca_mbox_in), + sizeof(struct golan_disable_hca_mbox_out)); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; +} + +static inline int golan_set_hca_cap(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_SET_HCA_CAP, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_cmd_set_hca_cap_mbox_in), + sizeof(struct golan_cmd_set_hca_cap_mbox_out)); + + golan->caps.flags &= ~GOLAN_DEV_CAP_FLAG_CMDIF_CSUM; + DBGC( golan , "%s caps.uar_sz = %d\n", __FUNCTION__, golan->caps.uar_sz); + DBGC( golan , "%s caps.log_pg_sz = %d\n", __FUNCTION__, golan->caps.log_pg_sz); + DBGC( golan , "%s caps.log_uar_sz = %d\n", __FUNCTION__, be32_to_cpu(golan->caps.uar_page_sz)); + golan->caps.uar_page_sz = 0; + golan->caps.log_max_qp = GOLAN_LOG_MAX_QP; + + memcpy(((struct golan_hca_cap *)GET_INBOX(golan, GEN_MBOX)), + &(golan->caps), + sizeof(struct golan_hca_cap)); + + //if command failed we should reset the caps in golan->caps + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + return rc; +} + +static inline int golan_qry_hca_cap(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc = 0; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_CAP, 0x1, + NO_MBOX, GEN_MBOX, + sizeof(struct golan_cmd_query_hca_cap_mbox_in), + sizeof(struct golan_cmd_query_hca_cap_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, GEN_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_hca_cap ); + + memcpy(&(golan->caps), + ((struct golan_hca_cap *)GET_OUTBOX(golan, GEN_MBOX)), + sizeof(struct golan_hca_cap)); +err_query_hca_cap: + return rc; +} + +static inline int golan_take_pages ( struct golan *golan, uint32_t pages, __be16 func_id ) { + uint32_t out_num_entries = 0; + int size_ibox = 0; + int size_obox = 0; + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + while ( pages > 0 ) { + uint32_t pas_num = min(pages, MAX_PASE_MBOX); + struct golan_cmd_layout *cmd; + struct golan_manage_pages_inbox *in; + + 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); + + cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_TAKE, + MEM_MBOX, MEM_MBOX, + size_ibox, + size_obox); + + in = (struct golan_manage_pages_inbox *)cmd->in; /* Warning (WE CANT USE THE LAST 2 FIELDS) */ + + in->func_id = func_id; /* Already BE */ + 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_num_entries = be32_to_cpu(((struct golan_manage_pages_outbox *)(cmd->out))->num_entries); + } else { + if ( rc == -EBUSY ) { + DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" ); + } else { + DBGC (golan ,"%s: rc =0x%x[%s]<%x> syn 0x%x[0x%x] for %d pages\n", + __FUNCTION__, rc, cmd_status_str(rc), + CMD_SYND(golan, MEM_CMD_IDX), + get_cmd( golan , MEM_CMD_IDX )->status_own, + be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num); + } + return rc; + } + + pages -= out_num_entries; + } + DBGC( golan , "%s Pages handled\n", __FUNCTION__); + return rc; +} + +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; + struct golan_cmd_layout *cmd; + struct golan_manage_pages_inbox *in; + userptr_t addr = 0; + + mailbox = GET_INBOX(golan, MEM_MBOX); + 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); + + cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_MANAGE_PAGES, GOLAN_PAGES_GIVE, + MEM_MBOX, MEM_MBOX, + size_ibox, + size_obox); + + in = (struct golan_manage_pages_inbox *)cmd->in; /* Warning (WE CANT USE THE LAST 2 FIELDS) */ + + 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, + 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); + } + mailbox->mblock.data[j] = USR_2_BE64_BUS(addr); + } + + if ( ( rc = send_command_and_wait(golan, MEM_CMD_IDX, MEM_MBOX, MEM_MBOX, __FUNCTION__) ) == 0 ) { + pages -= pas_num; + golan->total_dma_pages += pas_num; + } else { + if ( rc == -EBUSY ) { + DBGC (golan ,"HCA is busy (rc = -EBUSY)\n" ); + } else { + DBGC (golan ,"%s: rc =0x%x[%s]<%x> syn 0x%x[0x%x] for %d pages\n", + __FUNCTION__, rc, cmd_status_str(rc), + CMD_SYND(golan, MEM_CMD_IDX), + get_cmd( golan , MEM_CMD_IDX )->status_own, + be32_to_cpu(CMD_SYND(golan, MEM_CMD_IDX)), pas_num); + } + goto err_send_command; + } + } + DBGC( golan , "%s Pages handled\n", __FUNCTION__); + return 0; + +err_send_command: +err_golan_alloc_fw_area: + /* Go over In box and free pages */ + /* Send Error to FW */ + /* What is next - Disable HCA? */ + DBGC (golan ,"%s Failed (rc = 0x%x)\n", __FUNCTION__, rc); + return rc; +} + +static inline int golan_handle_pages(struct golan *golan, + enum golan_qry_pages_mode qry, + enum golan_manage_pages_mode mode) +{ + struct golan_cmd_layout *cmd; + + int rc = 0; + int32_t pages; + uint16_t total_pages; + __be16 func_id; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, MEM_CMD_IDX, GOLAN_CMD_OP_QUERY_PAGES, qry, + NO_MBOX, NO_MBOX, + sizeof(struct golan_query_pages_inbox), + sizeof(struct golan_query_pages_outbox)); + + rc = send_command_and_wait(golan, MEM_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_handle_pages_query ); + + pages = be32_to_cpu(QRY_PAGES_OUT(golan, MEM_CMD_IDX)->num_pages); + + DBGC( golan , "%s pages needed: %d\n", __FUNCTION__, pages); + + func_id = QRY_PAGES_OUT(golan, MEM_CMD_IDX)->func_id; + + total_pages = (( pages >= 0 ) ? pages : ( pages * ( -1 ) )); + + if ( mode == GOLAN_PAGES_GIVE ) { + 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; + } + + if ( rc ) { + DBGC (golan , "Failed to %s pages (rc = %d) - DMA pages allocated = %d\n", + ( ( mode == GOLAN_PAGES_GIVE ) ? "give" : "take" ), rc , golan->total_dma_pages ); + return rc; + } + + return 0; + +err_handle_pages_query: + DBGC (golan ,"%s Qyery pages failed (rc = 0x%x)\n", __FUNCTION__, rc); + return rc; +} + +static inline int golan_set_access_reg ( struct golan *golan __attribute__ (( unused )), uint32_t reg __attribute__ (( unused ))) +{ +#if 0 + write_cmd(golan, _CMD_IDX, GOLAN_CMD_OP_QUERY_PAGES, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_reg_host_endianess), + sizeof(struct golan_reg_host_endianess)); + in->arg = cpu_to_be32(arg); + in->register_id = cpu_to_be16(reg_num); +#endif + DBGC (golan ," %s Not implemented yet\n", __FUNCTION__); + return 0; +} + +static inline void golan_cmd_uninit ( struct golan *golan ) +{ + free_dma(golan->mboxes.outbox, GOLAN_PAGE_SIZE); + free_dma(golan->mboxes.inbox, GOLAN_PAGE_SIZE); + free_dma(golan->cmd.addr, GOLAN_PAGE_SIZE); +} + +/** + * Initialise Golan Command Q parameters + * -- Alocate a 4kb page for the Command Q + * -- Read the stride and log num commands available + * -- Write the address to cmdq_phy_addr in iseg + * @v golan Golan device + */ +static inline int golan_cmd_init ( struct golan *golan ) +{ + int rc = 0; + uint32_t addr_l_sz; + + if (!(golan->cmd.addr = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_failed; + } + if (!(golan->mboxes.inbox = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_inbox_failed; + } + if (!(golan->mboxes.outbox = malloc_dma(GOLAN_PAGE_SIZE , GOLAN_PAGE_SIZE))) { + rc = -ENOMEM; + goto malloc_dma_outbox_failed; + } + addr_l_sz = be32_to_cpu(readl(&golan->iseg->cmdq_addr_l_sz)); + + golan->cmd.log_stride = addr_l_sz & 0xf; + golan->cmd.size = 1 << (( addr_l_sz >> 4 ) & 0xf); + + addr_l_sz = virt_to_bus(golan->cmd.addr); + writel(0 /* cpu_to_be32(golan->cmd.addr) >> 32 */, &golan->iseg->cmdq_addr_h); + writel(cpu_to_be32(addr_l_sz), &golan->iseg->cmdq_addr_l_sz); + wmb(); //Make sure the addr is visible in "memory". + + addr_l_sz = be32_to_cpu(readl(&golan->iseg->cmdq_addr_l_sz)); + + DBGC( golan , "%s Command interface was initialized\n", __FUNCTION__); + return 0; + +malloc_dma_outbox_failed: + free_dma(golan->mboxes.inbox, GOLAN_PAGE_SIZE); +malloc_dma_inbox_failed: + free_dma(golan->cmd.addr, GOLAN_PAGE_SIZE); +malloc_dma_failed: + DBGC (golan ,"%s Failed to initialize command interface (rc = 0x%x)\n", + __FUNCTION__, rc); + return rc; +} + +static inline int golan_hca_init(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + int rc = 0; + + DBGC(golan, "%s\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_INIT_HCA, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_cmd_init_hca_mbox_in), + sizeof(struct golan_cmd_init_hca_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + return rc; +} + +static inline void golan_teardown_hca(struct golan *golan, enum golan_teardown op_mod) +{ + struct golan_cmd_layout *cmd; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_TEARDOWN_HCA, op_mod, + NO_MBOX, NO_MBOX, + sizeof(struct golan_cmd_teardown_hca_mbox_in), + sizeof(struct golan_cmd_teardown_hca_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + DBGC (golan, "%s HCA teardown compleated\n", __FUNCTION__); +} + +static inline int golan_alloc_uar(struct golan *golan) +{ + struct golan_uar *uar = &golan->uar; + struct golan_cmd_layout *cmd; + struct golan_alloc_uar_mbox_out *out; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ALLOC_UAR, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_alloc_uar_mbox_in), + sizeof(struct golan_alloc_uar_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_alloc_uar_cmd ); + out = (struct golan_alloc_uar_mbox_out *) ( cmd->out ); + + uar->index = be32_to_cpu(out->uarn) & 0xffffff; + + uar->phys = (pci_bar_start(golan->pci, GOLAN_HCA_BAR) + (uar->index << GOLAN_PAGE_SHIFT)); + uar->virt = (void *)(ioremap(uar->phys, GOLAN_PAGE_SIZE)); + + DBGC( golan , "%s: UAR allocated with index 0x%x\n", __FUNCTION__, uar->index); + return 0; + +err_alloc_uar_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_dealloc_uar(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + uint32_t uar_index = golan->uar.index; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DEALLOC_UAR, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_free_uar_mbox_in), + sizeof(struct golan_free_uar_mbox_out)); + + ((struct golan_free_uar_mbox_in *)(cmd->in))->uarn = cpu_to_be32(uar_index); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + golan->uar.index = 0; + + DBGC (golan, "%s UAR (0x%x) was destroyed\n", __FUNCTION__, uar_index); +} + +static void golan_eq_update_ci(struct golan_event_queue *eq, int arm) +{ + __be32 *addr = eq->doorbell + (arm ? 0 : 2); + u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24); + writel(cpu_to_be32(val) , addr); + /* We still want ordering, just not swabbing, so add a barrier */ + wmb(); +} + +static int golan_create_eq(struct golan *golan) +{ + struct golan_event_queue *eq = &golan->eq; + struct golan_create_eq_mbox_in_data *in; + struct golan_cmd_layout *cmd; + struct golan_create_eq_mbox_out *out; + int rc, i; + + eq->cons_index = 0; + eq->size = GOLAN_NUM_EQES * sizeof(eq->eqes[0]); + eq->eqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE ); + if (!eq->eqes) { + rc = -ENOMEM; + goto err_create_eq_eqe_alloc; + } + + /* Set EQEs ownership bit to HW ownership */ + for (i = 0; i < GOLAN_NUM_EQES; ++i) { + eq->eqes[i].owner = GOLAN_EQE_HW_OWNERSHIP; + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_EQ, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_eq_mbox_in) + GOLAN_PAS_SIZE, + sizeof(struct golan_create_eq_mbox_out)); + + in = (struct golan_create_eq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + /* Fill the physical address of the page */ + 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); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_eq_cmd ); + out = (struct golan_create_eq_mbox_out *)cmd->out; + + eq->eqn = out->eq_number; + eq->doorbell = ((void *)golan->uar.virt) + GOLAN_EQ_DOORBELL_OFFSET; + + /* EQs are created in ARMED state */ + golan_eq_update_ci(eq, GOLAN_EQ_UNARMED); + + DBGC( golan , "%s: Event queue created (EQN = 0x%x)\n", __FUNCTION__, eq->eqn); + return 0; + +err_create_eq_cmd: + free_dma ( eq->eqes , GOLAN_PAGE_SIZE ); +err_create_eq_eqe_alloc: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_destory_eq(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + struct golan_destroy_eq_mbox_in *in; + uint8_t eqn = golan->eq.eqn; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_EQ, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_eq_mbox_in), + sizeof(struct golan_destroy_eq_mbox_out)); + + in = GOLAN_MBOX_IN ( cmd, in ); + in->eqn = eqn; + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + free_dma ( golan->eq.eqes , GOLAN_PAGE_SIZE ); + golan->eq.eqn = 0; + + DBGC( golan, "%s Event queue (0x%x) was destroyed\n", __FUNCTION__, eqn); +} + +static int golan_alloc_pd(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + struct golan_alloc_pd_mbox_out *out; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ALLOC_PD, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_alloc_pd_mbox_in), + sizeof(struct golan_alloc_pd_mbox_out)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_alloc_pd_cmd ); + out = (struct golan_alloc_pd_mbox_out *) ( cmd->out ); + + golan->pdn = (be32_to_cpu(out->pdn) & 0xffffff); + DBGC( golan , "%s: Protection domain created (PDN = 0x%x)\n", __FUNCTION__, + golan->pdn); + return 0; + +err_alloc_pd_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_dealloc_pd(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + uint32_t pdn = golan->pdn; + int rc; + + DBGC (golan,"%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DEALLOC_PD, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_alloc_pd_mbox_in), + sizeof(struct golan_alloc_pd_mbox_out)); + + ((struct golan_dealloc_pd_mbox_in *)(cmd->in))->pdn = cpu_to_be32(pdn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + golan->pdn = 0; + + DBGC (golan ,"%s Protection domain (0x%x) was destroyed\n", __FUNCTION__, pdn); +} + +static int golan_create_mkey(struct golan *golan) +{ + struct golan_create_mkey_mbox_in_data *in; + struct golan_cmd_layout *cmd; + struct golan_create_mkey_mbox_out *out; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_MKEY, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_mkey_mbox_in), + sizeof(struct golan_create_mkey_mbox_out)); + + in = (struct golan_create_mkey_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + in->seg.flags = GOLAN_IB_ACCESS_LOCAL_WRITE | GOLAN_IB_ACCESS_LOCAL_READ; + in->seg.flags_pd = cpu_to_be32(golan->pdn | GOLAN_MKEY_LEN64); + in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << GOLAN_CREATE_MKEY_SEG_QPN_BIT); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_mkey_cmd ); + out = (struct golan_create_mkey_mbox_out *) ( cmd->out ); + + golan->mkey = ((be32_to_cpu(out->mkey) & 0xffffff) << 8); + DBGC( golan , "%s: Got DMA Key for local access read/write (MKEY = 0x%x)\n", + __FUNCTION__, golan->mkey); + return 0; +err_create_mkey_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static void golan_destroy_mkey(struct golan *golan) +{ + struct golan_cmd_layout *cmd; + u32 mkey = golan->mkey; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_MKEY, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_mkey_mbox_in), + sizeof(struct golan_destroy_mkey_mbox_out)); + ((struct golan_destroy_mkey_mbox_in *)(cmd->in))->mkey = cpu_to_be32(mkey >> 8); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + golan->mkey = 0; + + DBGC( golan , "%s DMA Key (0x%x) for local access write was destroyed\n" + , __FUNCTION__, mkey); +} + + +/** + * Initialise Golan PCI parameters + * + * @v golan Golan device + */ +static inline void golan_pci_init(struct golan *golan) +{ + struct pci_device *pci = golan->pci; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Get HCA BAR */ + golan->iseg = ioremap ( pci_bar_start ( pci, GOLAN_HCA_BAR), + GOLAN_PCI_CONFIG_BAR_SIZE ); +} + +static inline struct golan *golan_alloc() +{ + void *golan = zalloc(sizeof(struct golan)); + if ( !golan ) + goto err_zalloc; + + return golan; + +err_zalloc: + return NULL; +} + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int golan_create_cq(struct ib_device *ibdev, + struct ib_completion_queue *cq) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_completion_queue *golan_cq; + struct golan_cmd_layout *cmd; + struct golan_create_cq_mbox_in_data *in; + struct golan_create_cq_mbox_out *out; + int rc; + unsigned int i; + + golan_cq = zalloc(sizeof(*golan_cq)); + if (!golan_cq) { + rc = -ENOMEM; + goto err_create_cq; + } + golan_cq->size = sizeof(golan_cq->cqes[0]) * cq->num_cqes; + golan_cq->doorbell_record = malloc_dma(GOLAN_CQ_DB_RECORD_SIZE, + GOLAN_CQ_DB_RECORD_SIZE); + if (!golan_cq->doorbell_record) { + rc = -ENOMEM; + goto err_create_cq_db_alloc; + } + + golan_cq->cqes = malloc_dma ( GOLAN_PAGE_SIZE, GOLAN_PAGE_SIZE ); + if (!golan_cq->cqes) { + rc = -ENOMEM; + goto err_create_cq_cqe_alloc; + } + + /* Set CQEs ownership bit to HW ownership */ + for (i = 0; i < cq->num_cqes; ++i) { + golan_cq->cqes[i].op_own = ((GOLAN_CQE_OPCODE_NOT_VALID << + GOLAN_CQE_OPCODE_BIT) | + GOLAN_CQE_HW_OWNERSHIP); + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_CQ, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_cq_mbox_in) + GOLAN_PAS_SIZE, + sizeof(struct golan_create_cq_mbox_out)); + + in = (struct golan_create_cq_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + /* Fill the physical address of the page */ + 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); + in->ctx.db_record_addr = VIRT_2_BE64_BUS(golan_cq->doorbell_record); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_cq_cmd ); + out = (struct golan_create_cq_mbox_out *) ( cmd->out ); + + cq->cqn = (be32_to_cpu(out->cqn) & 0xffffff); + + ib_cq_set_drvdata(cq, golan_cq); + + DBGC( golan , "%s CQ created successfully (CQN = 0x%lx)\n", __FUNCTION__, cq->cqn); + return 0; + +err_create_cq_cmd: + 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: + free ( golan_cq ); +err_create_cq: + DBGC (golan ,"%s out rc = 0x%x\n", __FUNCTION__, rc); + return rc; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void golan_destroy_cq(struct ib_device *ibdev, + struct ib_completion_queue *cq) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_completion_queue *golan_cq = ib_cq_get_drvdata(cq); + struct golan_cmd_layout *cmd; + uint32_t cqn = cq->cqn; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_CQ, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_cq_mbox_in), + sizeof(struct golan_destroy_cq_mbox_out)); + ((struct golan_destroy_cq_mbox_in *)(cmd->in))->cqn = cpu_to_be32(cqn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + cq->cqn = 0; + + ib_cq_set_drvdata(cq, NULL); + free_dma ( golan_cq->cqes , GOLAN_PAGE_SIZE ); + free_dma(golan_cq->doorbell_record, GOLAN_CQ_DB_RECORD_SIZE); + free(golan_cq); + + DBGC (golan, "%s CQ number 0x%x was destroyed\n", __FUNCTION__, cqn); +} + +static void golan_cq_clean(struct ib_completion_queue *cq) +{ + ib_poll_cq(cq->ibdev, cq); +} + +static int golan_qp_type_to_st(enum ib_queue_pair_type type) +{ + int qpt = type; + + switch (qpt) { + case IB_QPT_RC: + return GOLAN_QP_ST_RC; + case IB_QPT_UD: + return GOLAN_QP_ST_UD; + case IB_QPT_SMI: + return GOLAN_QP_ST_QP0; + case IB_QPT_GSI: + return GOLAN_QP_ST_QP1; + case IB_QPT_ETH: + default: + return -EINVAL; + } +} +#if 0 +static int golan_is_special_qp(enum ib_queue_pair_type type) +{ + return (type == IB_QPT_GSI || type == IB_QPT_SMI); +} +#endif +static int golan_create_qp_aux(struct ib_device *ibdev, + struct ib_queue_pair *qp, + int *qpn) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp; + struct golan_create_qp_mbox_in_data *in; + struct golan_cmd_layout *cmd; + struct golan_wqe_data_seg *data; + struct golan_create_qp_mbox_out *out; + uint32_t wqe_size_in_bytes; + uint32_t max_qp_size_in_wqes; + unsigned int i; + int rc; + + golan_qp = zalloc(sizeof(*golan_qp)); + if (!golan_qp) { + rc = -ENOMEM; + goto err_create_qp; + } + + if ( ( qp->type == IB_QPT_SMI ) || ( qp->type == IB_QPT_GSI ) || + ( qp->type == IB_QPT_UD ) ) { + golan_qp->rq.grh_size = ( qp->recv.num_wqes * + sizeof ( golan_qp->rq.grh[0] )); + } + + /* Calculate receive queue size */ + golan_qp->rq.size = qp->recv.num_wqes * GOLAN_RECV_WQE_SIZE; + if (GOLAN_RECV_WQE_SIZE > be16_to_cpu(golan->caps.max_wqe_sz_rq)) { + DBGC (golan ,"%s receive wqe size [%zd] > max wqe size [%d]\n", __FUNCTION__, + GOLAN_RECV_WQE_SIZE, be16_to_cpu(golan->caps.max_wqe_sz_rq)); + rc = -EINVAL; + goto err_create_qp_rq_size; + } + + wqe_size_in_bytes = sizeof(golan_qp->sq.wqes[0]); + /* Calculate send queue size */ + if (wqe_size_in_bytes > be16_to_cpu(golan->caps.max_wqe_sz_sq)) { + DBGC (golan ,"%s send WQE size [%d] > max WQE size [%d]\n", __FUNCTION__, + wqe_size_in_bytes, + be16_to_cpu(golan->caps.max_wqe_sz_sq)); + rc = -EINVAL; + goto err_create_qp_sq_wqe_size; + } + golan_qp->sq.size = (qp->send.num_wqes * wqe_size_in_bytes); + max_qp_size_in_wqes = (1 << ((uint32_t)(golan->caps.log_max_qp_sz))); + if (qp->send.num_wqes > max_qp_size_in_wqes) { + DBGC (golan ,"%s send wq size [%d] > max wq size [%d]\n", __FUNCTION__, + golan_qp->sq.size, max_qp_size_in_wqes); + rc = -EINVAL; + goto err_create_qp_sq_size; + } + + golan_qp->size = golan_qp->sq.size + golan_qp->rq.size; + + /* allocate dma memory for WQEs (1 page is enough) - should change it */ + 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->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]); + + if ( golan_qp->rq.grh_size ) { + golan_qp->rq.grh = ( golan_qp->wqes + + golan_qp->sq.size + + golan_qp->rq.size ); + } + + /* Invalidate all WQEs */ + data = &golan_qp->rq.wqes[0].data[0]; + for ( i = 0 ; i < ( golan_qp->rq.size / sizeof ( *data ) ); i++ ){ + data->lkey = cpu_to_be32 ( GOLAN_INVALID_LKEY ); + data++; + } + + golan_qp->doorbell_record = malloc_dma(sizeof(struct golan_qp_db), + sizeof(struct golan_qp_db)); + if (!golan_qp->doorbell_record) { + rc = -ENOMEM; + goto err_create_qp_db_alloc; + } + memset(golan_qp->doorbell_record, 0, sizeof(struct golan_qp_db)); + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_CREATE_QP, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_create_qp_mbox_in) + GOLAN_PAS_SIZE, + sizeof(struct golan_create_qp_mbox_out)); + + in = (struct golan_create_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + + /* Fill the physical address of the page */ + 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); + in->ctx.flags = cpu_to_be32((golan_qp_type_to_st(qp->type) + << GOLAN_QP_CTX_ST_BIT) | + (GOLAN_QP_PM_MIGRATED << + GOLAN_QP_CTX_PM_STATE_BIT)); +// cgs set to 0, initialy. +// atomic mode + in->ctx.rq_size_stride = ((ilog2(qp->recv.num_wqes) << + GOLAN_QP_CTX_RQ_SIZE_BIT) | + (sizeof(golan_qp->rq.wqes[0]) / GOLAN_RECV_WQE_SIZE)); + in->ctx.sq_crq_size = cpu_to_be16(ilog2(golan_qp->sq.size / GOLAN_SEND_WQE_BB_SIZE) + << GOLAN_QP_CTX_SQ_SIZE_BIT); + in->ctx.cqn_send = cpu_to_be32(qp->send.cq->cqn); + in->ctx.cqn_recv = cpu_to_be32(qp->recv.cq->cqn); + in->ctx.db_rec_addr = VIRT_2_BE64_BUS(golan_qp->doorbell_record); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_create_qp_cmd ); + out = (struct golan_create_qp_mbox_out *)cmd->out; + + *qpn = (be32_to_cpu(out->qpn) & 0xffffff); + /* + * Hardware wants QPN written in big-endian order (after + * shifting) for send doorbell. Precompute this value to save + * a little bit when posting sends. + */ + golan_qp->doorbell_qpn = cpu_to_be32(*qpn << 8); + golan_qp->state = GOLAN_IB_QPS_RESET; + + ib_qp_set_drvdata(qp, golan_qp); + + return 0; + +err_create_qp_cmd: + free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); +err_create_qp_db_alloc: + free_dma ( golan_qp->wqes, GOLAN_PAGE_SIZE ); +err_create_qp_wqe_alloc: +err_create_qp_sq_size: +err_create_qp_sq_wqe_size: +err_create_qp_rq_size: + free ( golan_qp ); +err_create_qp: + return rc; +} + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int golan_create_qp(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + int rc, qpn = -1; + + switch (qp->type) { + case IB_QPT_UD: + case IB_QPT_SMI: + case IB_QPT_GSI: + rc = golan_create_qp_aux(ibdev, qp, &qpn); + if (rc) { + DBG ( "%s Failed to create QP (rc = 0x%x)\n", __FUNCTION__, rc); + return rc; + } + qp->qpn = qpn; + + break; + case IB_QPT_ETH: + case IB_QPT_RC: + default: + DBG ( "%s unsupported QP type (0x%x)\n", __FUNCTION__, qp->type); + return -EINVAL; + } + + return 0; +} + +static int golan_modify_qp_rst_to_init(struct ib_device *ibdev, + struct ib_queue_pair *qp __unused, + struct golan_modify_qp_mbox_in_data *in) +{ + int rc = 0; + + in->ctx.qkey = cpu_to_be32((uint32_t)(qp->qkey)); + + in->ctx.pri_path.port = ibdev->port; + in->ctx.flags |= cpu_to_be32(GOLAN_QP_PM_MIGRATED << GOLAN_QP_CTX_PM_STATE_BIT); + in->ctx.pri_path.pkey_index = 0; + /* QK is 0 */ + /* QP cntr set 0 */ + return rc; +} + +static int golan_modify_qp_init_to_rtr(struct ib_device *ibdev __unused, + struct ib_queue_pair *qp __unused, + struct golan_modify_qp_mbox_in_data *in) +{ + int rc = 0; + + in->optparam = 0; + return rc; +} + +static int golan_modify_qp_rtr_to_rts(struct ib_device *ibdev __unused, + struct ib_queue_pair *qp __unused, + struct golan_modify_qp_mbox_in_data *in __unused) +{ + int rc = 0; + + in->optparam = 0; + /* In good flow psn in 0 */ + return rc; +} + +static int golan_modify_qp_to_rst(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_cmd_layout *cmd; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_2RST_QP, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_modify_qp_mbox_in), + sizeof(struct golan_modify_qp_mbox_out)); + ((struct golan_modify_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_modify_qp_2rst_cmd ); + + golan_qp->state = GOLAN_IB_QPS_RESET; + DBGC( golan , "%s QP number 0x%lx was modified to RESET\n", + __FUNCTION__, qp->qpn); + + return 0; + +err_modify_qp_2rst_cmd: + DBGC (golan ,"%s Failed to modify QP number 0x%lx (rc = 0x%x)\n", + __FUNCTION__, qp->qpn, rc); + return rc; +} + +static int (*golan_modify_qp_methods[])(struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct golan_modify_qp_mbox_in_data *in) = { + + [GOLAN_IB_QPS_RESET] = golan_modify_qp_rst_to_init, + [GOLAN_IB_QPS_INIT] = golan_modify_qp_init_to_rtr, + [GOLAN_IB_QPS_RTR] = golan_modify_qp_rtr_to_rts +}; + +static int golan_modify_qp(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_modify_qp_mbox_in_data *in; + struct golan_cmd_layout *cmd; + enum golan_ib_qp_state prev_state; + int rc; + int modify_cmd[] = {GOLAN_CMD_OP_RST2INIT_QP, + GOLAN_CMD_OP_INIT2RTR_QP, + GOLAN_CMD_OP_RTR2RTS_QP}; + + while (golan_qp->state < GOLAN_IB_QPS_RTS) { + prev_state = golan_qp->state; + cmd = write_cmd(golan, DEF_CMD_IDX, modify_cmd[golan_qp->state], 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_modify_qp_mbox_in), + sizeof(struct golan_modify_qp_mbox_out)); + + in = (struct golan_modify_qp_mbox_in_data *)GET_INBOX(golan, GEN_MBOX); + ((struct golan_modify_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + rc = golan_modify_qp_methods[golan_qp->state](ibdev, qp, in); + if (rc) { + goto err_modify_qp_fill_inbox; + } +// in->ctx.qp_counter_set_usr_page = cpu_to_be32(golan->uar.index); + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_modify_qp_cmd ); + + ++(golan_qp->state); + + DBGC( golan , "%s QP number 0x%lx was modified from %s to %s\n", + __FUNCTION__, qp->qpn, golan_qp_state_as_string[prev_state], + golan_qp_state_as_string[golan_qp->state]); + } + + DBGC( golan , "%s QP number 0x%lx is ready to receive/send packets.\n", + __FUNCTION__, qp->qpn); + return 0; + +err_modify_qp_cmd: +err_modify_qp_fill_inbox: + DBGC (golan ,"%s Failed to modify QP number 0x%lx (rc = 0x%x)\n", + __FUNCTION__, qp->qpn, rc); + return rc; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void golan_destroy_qp(struct ib_device *ibdev, + struct ib_queue_pair *qp) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_cmd_layout *cmd; + unsigned long qpn = qp->qpn; + int rc; + + DBGC (golan, "%s in\n", __FUNCTION__); + + if (golan_qp->state != GOLAN_IB_QPS_RESET) { + if (golan_modify_qp_to_rst(ibdev, qp)) { + DBGC (golan ,"%s Failed to modify QP 0x%lx to RESET\n", __FUNCTION__, + qp->qpn); + } + } + + if (qp->recv.cq) { + golan_cq_clean(qp->recv.cq); + } + if (qp->send.cq && (qp->send.cq != qp->recv.cq)) { + golan_cq_clean(qp->send.cq); + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DESTROY_QP, 0x0, + NO_MBOX, NO_MBOX, + sizeof(struct golan_destroy_qp_mbox_in), + sizeof(struct golan_destroy_qp_mbox_out)); + ((struct golan_destroy_qp_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qpn); + rc = send_command_and_wait(golan, DEF_CMD_IDX, NO_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + qp->qpn = 0; + + ib_qp_set_drvdata(qp, NULL); + free_dma(golan_qp->doorbell_record, sizeof(struct golan_qp_db)); + free_dma ( golan_qp->wqes, GOLAN_PAGE_SIZE ); + free(golan_qp); + + DBGC( golan ,"%s QP 0x%lx was destroyed\n", __FUNCTION__, qpn); +} + +/** + * Calculate transmission rate + * + * @v av Address vector + * @ret golan_rate Golan rate + */ +static unsigned int golan_rate(enum ib_rate rate) { + return (((rate >= IB_RATE_2_5) && (rate <= IB_RATE_120)) ? (rate + 5) : 0); +} + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int golan_post_send(struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct golan_send_wqe_ud *wqe = NULL; + struct golan_av *datagram = NULL; + unsigned long wqe_idx_mask; + unsigned long wqe_idx; + struct golan_wqe_data_seg *data = NULL; + struct golan_wqe_ctrl_seg *ctrl = NULL; + + + wqe_idx_mask = (qp->send.num_wqes - 1); + wqe_idx = (qp->send.next_idx & wqe_idx_mask); + if (qp->send.iobufs[wqe_idx]) { + DBGC (golan ,"%s Send queue of QPN 0x%lx is full\n", __FUNCTION__, qp->qpn); + return -ENOMEM; + } + + qp->send.iobufs[wqe_idx] = iobuf; + + // change to this + //wqe_size_in_octa_words = golan_qp->sq.wqe_size_in_wqebb >> 4; + + wqe = &golan_qp->sq.wqes[wqe_idx].ud; + + //CHECK HW OWNERSHIP BIT ??? + + memset(wqe, 0, sizeof(*wqe)); + + ctrl = &wqe->ctrl; + ctrl->opmod_idx_opcode = cpu_to_be32(GOLAN_SEND_OPCODE | + ((u32)(golan_qp->sq.next_idx) << + GOLAN_WQE_CTRL_WQE_IDX_BIT)); + ctrl->qpn_ds = cpu_to_be32(GOLAN_SEND_UD_WQE_SIZE >> 4) | + golan_qp->doorbell_qpn; + ctrl->fm_ce_se = 0x8;//10 - 0 - 0 + data = &wqe->data; + data->byte_count = cpu_to_be32(iob_len(iobuf)); + data->lkey = cpu_to_be32(golan->mkey); + data->addr = VIRT_2_BE64_BUS(iobuf->data); + + datagram = &wqe->datagram; + datagram->key.qkey.qkey = cpu_to_be32(av->qkey); + datagram->dqp_dct = cpu_to_be32((1 << 31) | av->qpn); + datagram->stat_rate_sl = ((golan_rate(av->rate) << 4) | av->sl); + datagram->fl_mlid = (ibdev->lid & 0x007f); /* take only the 7 low bits of the LID */ + datagram->rlid = cpu_to_be16(av->lid); + datagram->grh_gid_fl = cpu_to_be32(av->gid_present << 30); + memcpy(datagram->rgid, av->gid.bytes, 16 /* sizeof(datagram->rgid) */); + + /* + * Make sure that descriptors are written before + * updating doorbell record and ringing the doorbell + */ + ++(qp->send.next_idx); + 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 + + ( ( golan_qp->sq.next_idx & 0x1 ) ? DB_BUFFER0_EVEN_OFFSET + : DB_BUFFER0_ODD_OFFSET ) ); + return 0; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int golan_post_recv(struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_queue_pair *golan_qp = ib_qp_get_drvdata(qp); + struct ib_work_queue *wq = &qp->recv; + struct golan_recv_wqe_ud *wqe; + struct ib_global_route_header *grh; + struct golan_wqe_data_seg *data; + unsigned int wqe_idx_mask; + + /* Allocate work queue entry */ + wqe_idx_mask = (wq->num_wqes - 1); + if (wq->iobufs[wq->next_idx & wqe_idx_mask]) { + DBGC (golan ,"%s Receive queue of QPN 0x%lx is full\n", __FUNCTION__, qp->qpn); + return -ENOMEM; + } + + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = & golan_qp->rq.wqes[wq->next_idx & wqe_idx_mask]; + + memset(wqe, 0, sizeof(*wqe)); + data = &wqe->data[0]; + if ( golan_qp->rq.grh ) { + grh = &golan_qp->rq.grh[wq->next_idx & wqe_idx_mask]; + data->byte_count = cpu_to_be32 ( sizeof ( *grh ) ); + data->lkey = cpu_to_be32 ( golan->mkey ); + data->addr = VIRT_2_BE64_BUS ( grh ); + data++; + } + + data->byte_count = cpu_to_be32(iob_tailroom(iobuf)); + data->lkey = cpu_to_be32(golan->mkey); + data->addr = VIRT_2_BE64_BUS(iobuf->data); + + ++wq->next_idx; + + /* + * Make sure that descriptors are written before + * updating doorbell record and ringing the doorbell + */ + wmb(); + golan_qp->doorbell_record->recv_db = cpu_to_be16(qp->recv.next_idx & 0xffff); + + return 0; +} + +static int golan_query_vport_context ( struct ib_device *ibdev ) { + struct golan *golan = ib_get_drvdata ( ibdev ); + struct golan_cmd_layout *cmd; + struct golan_query_hca_vport_context_inbox *in; + struct golan_query_hca_vport_context_data *context_data; + int rc; + + cmd = write_cmd ( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_CONTEXT, + 0x0, GEN_MBOX, GEN_MBOX, + sizeof(struct golan_query_hca_vport_context_inbox), + sizeof(struct golan_query_hca_vport_context_outbox) ); + + in = GOLAN_MBOX_IN ( cmd, in ); + in->port_num = (u8)ibdev->port; + + rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_context_cmd ); + + context_data = (struct golan_query_hca_vport_context_data *)( GET_OUTBOX ( golan, GEN_MBOX ) ); + + ibdev->node_guid.dwords[0] = context_data->node_guid[0]; + ibdev->node_guid.dwords[1] = context_data->node_guid[1]; + ibdev->lid = be16_to_cpu( context_data->lid ); + ibdev->sm_lid = be16_to_cpu( context_data->sm_lid ); + ibdev->sm_sl = context_data->sm_sl; + ibdev->port_state = context_data->port_state; + + return 0; +err_query_vport_context_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + + +static int golan_query_vport_gid ( struct ib_device *ibdev ) { + struct golan *golan = ib_get_drvdata( ibdev ); + struct golan_cmd_layout *cmd; + struct golan_query_hca_vport_gid_inbox *in; + union ib_gid *ib_gid; + int rc; + + cmd = write_cmd( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_GID, + 0x0, GEN_MBOX, GEN_MBOX, + sizeof(struct golan_query_hca_vport_gid_inbox), + sizeof(struct golan_query_hca_vport_gid_outbox) ); + + in = GOLAN_MBOX_IN ( cmd, in ); + in->port_num = (u8)ibdev->port; + in->gid_index = 0; + rc = send_command_and_wait ( golan, DEF_CMD_IDX, GEN_MBOX, GEN_MBOX, __FUNCTION__ ); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_query_vport_gid_cmd ); + + ib_gid = (union ib_gid *)( GET_OUTBOX ( golan, GEN_MBOX ) ); + + memcpy ( &ibdev->gid, ib_gid, sizeof(ibdev->gid) ); + + return 0; +err_query_vport_gid_cmd: + DBGC ( golan, "%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +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_inbox *in; + int pkey_table_size_in_entries = (1 << (7 + golan->caps.pkey_table_size)); + int rc; + + cmd = write_cmd ( golan, DEF_CMD_IDX, GOLAN_CMD_OP_QUERY_HCA_VPORT_PKEY, + 0x0, GEN_MBOX, GEN_MBOX, + sizeof(struct golan_query_hca_vport_pkey_inbox), + sizeof(struct golan_outbox_hdr) + 8 + + sizeof(struct golan_query_hca_vport_pkey_data) * pkey_table_size_in_entries ); + + in = GOLAN_MBOX_IN ( cmd, in ); + in->port_num = (u8)ibdev->port; + in->pkey_index = 0xffff; + 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 ); + + return 0; +err_query_vport_pkey_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static int golan_get_ib_info ( struct ib_device *ibdev ) { + int rc; + + rc = golan_query_vport_context ( ibdev ); + if ( rc != 0 ) { + DBG ( "golan_get_ib_info: golan_query_vport_context Failed (rc = %d)\n",rc ); + goto err_query_vport_context; + } + + rc = golan_query_vport_gid ( ibdev ); + if ( rc != 0 ) { + DBG ( "golan_get_ib_info: golan_query_vport_gid Failed (rc = %d)\n",rc ); + goto err_query_vport_gid; + } + + rc = golan_query_vport_pkey ( ibdev ); + if ( rc != 0 ) { + DBG ( "golan_get_ib_info: golan_query_vport_pkey Failed (rc = %d)\n",rc ); + goto err_query_vport_pkey; + } + return rc; +err_query_vport_pkey: +err_query_vport_gid: +err_query_vport_context: + DBG ( "%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +static int golan_complete(struct ib_device *ibdev, + struct ib_completion_queue *cq, + struct golan_cqe64 *cqe64) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct ib_work_queue *wq; + struct golan_queue_pair *golan_qp; + struct ib_queue_pair *qp; + struct io_buffer *iobuf = NULL; + struct ib_address_vector recv_dest; + struct ib_address_vector recv_source; + struct ib_global_route_header *grh; + struct golan_err_cqe *err_cqe64; + int gid_present, idx; + u16 wqe_ctr; + uint8_t opcode; + static int error_state; + uint32_t qpn = be32_to_cpu(cqe64->sop_drop_qpn) & 0xffffff; + int is_send = 0; + size_t len; + + opcode = cqe64->op_own >> GOLAN_CQE_OPCODE_BIT; + DBGC2( golan , "%s completion with opcode 0x%x\n", __FUNCTION__, opcode); + + if (opcode == GOLAN_CQE_REQ || opcode == GOLAN_CQE_REQ_ERR) { + is_send = 1; + } else { + is_send = 0; + } + if (opcode == GOLAN_CQE_REQ_ERR || opcode == GOLAN_CQE_RESP_ERR) { + err_cqe64 = (struct golan_err_cqe *)cqe64; + int i = 0; + if (!error_state++) { + DBGC (golan ,"\n"); + for ( i = 0 ; i < 16 ; i += 2 ) { + DBGC (golan ,"%x %x\n", + be32_to_cpu(((uint32_t *)(err_cqe64))[i]), + be32_to_cpu(((uint32_t *)(err_cqe64))[i + 1])); + } + DBGC (golan ,"CQE with error: Syndrome(0x%x), VendorSynd(0x%x), HW_SYN(0x%x)\n", + err_cqe64->syndrome, err_cqe64->vendor_err_synd, + err_cqe64->hw_syndrom); + } + } + /* Identify work queue */ + wq = ib_find_wq(cq, qpn, is_send); + if (!wq) { + DBGC (golan ,"%s unknown %s QPN 0x%x in CQN 0x%lx\n", + __FUNCTION__, (is_send ? "send" : "recv"), qpn, cq->cqn); + return -EINVAL; + } + + qp = wq->qp; + golan_qp = ib_qp_get_drvdata ( qp ); + + wqe_ctr = be16_to_cpu(cqe64->wqe_counter); + if (is_send) { + wqe_ctr &= ((GOLAN_WQEBBS_PER_SEND_UD_WQE * wq->num_wqes) - 1); + idx = wqe_ctr / GOLAN_WQEBBS_PER_SEND_UD_WQE; + } else { + idx = wqe_ctr & (wq->num_wqes - 1); + } + + iobuf = wq->iobufs[idx]; + if (!iobuf) { + DBGC (golan ,"%s IO Buffer 0x%x not found in QPN 0x%x\n", + __FUNCTION__, idx, qpn); + return -EINVAL; + } + wq->iobufs[idx] = NULL; + + if (is_send) { + ib_complete_send(ibdev, qp, iobuf, (opcode == GOLAN_CQE_REQ_ERR)); + } else { + len = be32_to_cpu(cqe64->byte_cnt); + memset(&recv_dest, 0, sizeof(recv_dest)); + recv_dest.qpn = qpn; + /* Construct address vector */ + memset(&recv_source, 0, sizeof(recv_source)); + switch (qp->type) { + case IB_QPT_SMI: + case IB_QPT_GSI: + case IB_QPT_UD: + /* Locate corresponding GRH */ + assert ( golan_qp->rq.grh != NULL ); + grh = &golan_qp->rq.grh[ idx ]; + + recv_source.qpn = be32_to_cpu(cqe64->flags_rqpn) & 0xffffff; + recv_source.lid = be16_to_cpu(cqe64->slid); + recv_source.sl = (be32_to_cpu(cqe64->flags_rqpn) >> 24) & 0xf; + gid_present = (be32_to_cpu(cqe64->flags_rqpn) >> 28) & 3; + if (!gid_present) { + recv_dest.gid_present = recv_source.gid_present = 0; + } else { + recv_dest.gid_present = recv_source.gid_present = 1; + //if (recv_source.gid_present == 0x1) { + memcpy(&recv_source.gid, &grh->sgid, sizeof(recv_source.gid)); + memcpy(&recv_dest.gid, &grh->dgid, sizeof(recv_dest.gid)); + //} else { // recv_source.gid_present = 0x3 + /* GRH is located in the upper 64 byte of the CQE128 + * currently not supported */ + //; + //} + } + len -= sizeof ( *grh ); + break; + case IB_QPT_RC: + case IB_QPT_ETH: + default: + DBGC (golan ,"%s Unsupported QP type (0x%x)\n", __FUNCTION__, qp->type); + return -EINVAL; + } + assert(len <= iob_tailroom(iobuf)); + iob_put(iobuf, len); + ib_complete_recv(ibdev, qp, &recv_dest, &recv_source, iobuf, (opcode == GOLAN_CQE_RESP_ERR)); + } + return 0; +} + +static int golan_is_hw_ownership(struct ib_completion_queue *cq, + struct golan_cqe64 *cqe64) +{ + return ((cqe64->op_own & GOLAN_CQE_OWNER_MASK) != + ((cq->next_idx >> ilog2(cq->num_cqes)) & 1)); +} +static void golan_poll_cq(struct ib_device *ibdev, + struct ib_completion_queue *cq) +{ + unsigned int i; + int rc = 0; + unsigned int cqe_idx_mask; + struct golan_cqe64 *cqe64; + struct golan_completion_queue *golan_cq = ib_cq_get_drvdata(cq); + struct golan *golan = ib_get_drvdata(ibdev); + + for (i = 0; i < cq->num_cqes; ++i) { + /* Look for completion entry */ + cqe_idx_mask = (cq->num_cqes - 1); + cqe64 = &golan_cq->cqes[cq->next_idx & cqe_idx_mask]; + /* temporary valid only for 64 byte CQE */ + if (golan_is_hw_ownership(cq, cqe64) || + ((cqe64->op_own >> GOLAN_CQE_OPCODE_BIT) == + GOLAN_CQE_OPCODE_NOT_VALID)) { + break; /* HW ownership */ + } + + DBGC2( golan , "%s CQN 0x%lx [%ld] \n", __FUNCTION__, cq->cqn, cq->next_idx); + /* + * Make sure we read CQ entry contents after we've checked the + * ownership bit. (PRM - 6.5.3.2) + */ + rmb(); + rc = golan_complete(ibdev, cq, cqe64); + if (rc != 0) { + DBGC (golan ,"%s CQN 0x%lx failed to complete\n", __FUNCTION__, cq->cqn); + } + + /* Update completion queue's index */ + cq->next_idx++; + + /* Update doorbell record */ + *(golan_cq->doorbell_record) = cpu_to_be32(cq->next_idx & 0xffffff); + } +} + +static const char *golan_eqe_type_str(u8 type) +{ + switch (type) { + case GOLAN_EVENT_TYPE_COMP: + return "GOLAN_EVENT_TYPE_COMP"; + case GOLAN_EVENT_TYPE_PATH_MIG: + return "GOLAN_EVENT_TYPE_PATH_MIG"; + case GOLAN_EVENT_TYPE_COMM_EST: + return "GOLAN_EVENT_TYPE_COMM_EST"; + case GOLAN_EVENT_TYPE_SQ_DRAINED: + return "GOLAN_EVENT_TYPE_SQ_DRAINED"; + case GOLAN_EVENT_TYPE_SRQ_LAST_WQE: + return "GOLAN_EVENT_TYPE_SRQ_LAST_WQE"; + case GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT: + return "GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT"; + case GOLAN_EVENT_TYPE_CQ_ERROR: + return "GOLAN_EVENT_TYPE_CQ_ERROR"; + case GOLAN_EVENT_TYPE_WQ_CATAS_ERROR: + return "GOLAN_EVENT_TYPE_WQ_CATAS_ERROR"; + case GOLAN_EVENT_TYPE_PATH_MIG_FAILED: + return "GOLAN_EVENT_TYPE_PATH_MIG_FAILED"; + case GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR: + return "GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR"; + case GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR: + return "GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR"; + case GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR: + return "GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR"; + case GOLAN_EVENT_TYPE_INTERNAL_ERROR: + return "GOLAN_EVENT_TYPE_INTERNAL_ERROR"; + case GOLAN_EVENT_TYPE_PORT_CHANGE: + return "GOLAN_EVENT_TYPE_PORT_CHANGE"; + case GOLAN_EVENT_TYPE_GPIO_EVENT: + return "GOLAN_EVENT_TYPE_GPIO_EVENT"; + case GOLAN_EVENT_TYPE_REMOTE_CONFIG: + return "GOLAN_EVENT_TYPE_REMOTE_CONFIG"; + case GOLAN_EVENT_TYPE_DB_BF_CONGESTION: + return "GOLAN_EVENT_TYPE_DB_BF_CONGESTION"; + case GOLAN_EVENT_TYPE_STALL_EVENT: + return "GOLAN_EVENT_TYPE_STALL_EVENT"; + case GOLAN_EVENT_TYPE_CMD: + return "GOLAN_EVENT_TYPE_CMD"; + case GOLAN_EVENT_TYPE_PAGE_REQUEST: + return "GOLAN_EVENT_TYPE_PAGE_REQUEST"; + default: + return "Unrecognized event"; + } +} + +static const char *golan_eqe_port_subtype_str(u8 subtype) +{ + switch (subtype) { + case GOLAN_PORT_CHANGE_SUBTYPE_DOWN: + return "GOLAN_PORT_CHANGE_SUBTYPE_DOWN"; + case GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE: + return "GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE"; + case GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED: + return "GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED"; + case GOLAN_PORT_CHANGE_SUBTYPE_LID: + return "GOLAN_PORT_CHANGE_SUBTYPE_LID"; + case GOLAN_PORT_CHANGE_SUBTYPE_PKEY: + return "GOLAN_PORT_CHANGE_SUBTYPE_PKEY"; + case GOLAN_PORT_CHANGE_SUBTYPE_GUID: + return "GOLAN_PORT_CHANGE_SUBTYPE_GUID"; + case GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG: + return "GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG"; + default: + return "Unrecognized event"; + } +} + +/** + * Update Infiniband parameters using Commands + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int golan_ib_update ( struct ib_device *ibdev ) { + int rc; + + /* Get IB parameters */ + if ( ( rc = golan_get_ib_info ( ibdev ) ) != 0 ) + return rc; + + /* Notify Infiniband core of potential link state change */ + ib_link_state_changed ( ibdev ); + + return 0; +} + +static inline void golan_handle_port_event(struct golan *golan, struct golan_eqe *eqe) +{ + struct ib_device *ibdev; + u8 port; + + port = (eqe->data.port.port >> 4) & 0xf; + ibdev = golan->ports[port - 1].ibdev; + + if ( ! ib_is_open ( ibdev ) ) + return; + + switch (eqe->sub_type) { + 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: + case GOLAN_PORT_CHANGE_SUBTYPE_GUID: + case GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED: + DBGC( golan , "%s event %s(%d) (sub event %s(%d))arrived on port %d\n", + __FUNCTION__, golan_eqe_type_str(eqe->type), eqe->type, + golan_eqe_port_subtype_str(eqe->sub_type), + eqe->sub_type, port); + break; + default: + DBGC (golan ,"%s Port event with unrecognized subtype: port %d, sub_type %d\n", + __FUNCTION__, port, eqe->sub_type); + } +} + +static struct golan_eqe *golan_next_eqe_sw(struct golan_event_queue *eq) +{ + uint32_t entry = (eq->cons_index & (GOLAN_NUM_EQES - 1)); + struct golan_eqe *eqe = &(eq->eqes[entry]); + return ((eqe->owner != ((eq->cons_index >> ilog2(GOLAN_NUM_EQES)) & 1)) ? NULL : eqe); +} + + +/** + * Poll event queue + * + * @v ibdev Infiniband device + */ +static void golan_poll_eq(struct ib_device *ibdev) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_event_queue *eq = &(golan->eq); + struct golan_eqe *eqe; + u32 cqn; + int counter = 0; + + while ((eqe = golan_next_eqe_sw(eq)) && (counter < GOLAN_NUM_EQES)) { + /* + * Make sure we read EQ entry contents after we've + * checked the ownership bit. + */ + rmb(); + + DBGC( golan , "%s eqn %d, eqe type %s\n", __FUNCTION__, eq->eqn, + golan_eqe_type_str(eqe->type)); + switch (eqe->type) { + case GOLAN_EVENT_TYPE_COMP: + /* We dont need to handle completion events since we + * poll all the CQs after polling the EQ */ + break; + case GOLAN_EVENT_TYPE_PATH_MIG: + case GOLAN_EVENT_TYPE_COMM_EST: + case GOLAN_EVENT_TYPE_SQ_DRAINED: + case GOLAN_EVENT_TYPE_SRQ_LAST_WQE: + case GOLAN_EVENT_TYPE_WQ_CATAS_ERROR: + case GOLAN_EVENT_TYPE_PATH_MIG_FAILED: + case GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR: + case GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR: + case GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT: + case GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR: + DBGC( golan , "%s event %s(%d) arrived\n", __FUNCTION__, + golan_eqe_type_str(eqe->type), eqe->type); + break; + case GOLAN_EVENT_TYPE_CMD: +// golan_cmd_comp_handler(be32_to_cpu(eqe->data.cmd.vector)); + break; + case GOLAN_EVENT_TYPE_PORT_CHANGE: + golan_handle_port_event(golan, eqe); + break; + case GOLAN_EVENT_TYPE_CQ_ERROR: + cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff; + DBGC (golan ,"CQ error on CQN 0x%x, syndrom 0x%x\n", + 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 + u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id); + s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages); + + DBGC (golan ,"%s page request for func 0x%x, napges %d\n", + __FUNCTION__, func_id, npages); + 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); + break; + } + + ++eq->cons_index; + golan_eq_update_ci(eq, GOLAN_EQ_UNARMED); + ++counter; + } +} + +/** + * Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static int golan_mcast_attach(struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_cmd_layout *cmd; + int rc; + + if ( qp == NULL ) { + DBGC( golan, "%s: Invalid pointer, could not attach QPN to MCG\n", + __FUNCTION__ ); + return -EFAULT; + } + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_ATTACH_TO_MCG, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_attach_mcg_mbox_in), + sizeof(struct golan_attach_mcg_mbox_out)); + ((struct golan_attach_mcg_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + + memcpy(GET_INBOX(golan, GEN_MBOX), gid, sizeof(*gid)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_CHECK_RC_AND_CMD_STATUS( err_attach_to_mcg_cmd ); + + DBGC( golan , "%s: QPN 0x%lx was attached to MCG\n", __FUNCTION__, qp->qpn); + return 0; +err_attach_to_mcg_cmd: + DBGC (golan ,"%s [%d] out\n", __FUNCTION__, rc); + return rc; +} + +/** + * Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static void golan_mcast_detach(struct ib_device *ibdev, + struct ib_queue_pair *qp, + union ib_gid *gid) +{ + struct golan *golan = ib_get_drvdata(ibdev); + struct golan_cmd_layout *cmd; + int rc; + + cmd = write_cmd(golan, DEF_CMD_IDX, GOLAN_CMD_OP_DETACH_FROM_MCG, 0x0, + GEN_MBOX, NO_MBOX, + sizeof(struct golan_detach_mcg_mbox_in), + sizeof(struct golan_detach_mcg_mbox_out)); + ((struct golan_detach_mcg_mbox_in *)(cmd->in))->qpn = cpu_to_be32(qp->qpn); + + memcpy(GET_INBOX(golan, GEN_MBOX), gid, sizeof(*gid)); + + rc = send_command_and_wait(golan, DEF_CMD_IDX, GEN_MBOX, NO_MBOX, __FUNCTION__); + GOLAN_PRINT_RC_AND_CMD_STATUS; + + DBGC( golan , "%s: QPN 0x%lx was detached from MCG\n", __FUNCTION__, qp->qpn); +} + +/** + * Inform embedded subnet management agent of a received MAD + * + * @v ibdev Infiniband device + * @v mad MAD + * @ret rc Return status code + */ +static int golan_inform_sma(struct ib_device *ibdev, + union ib_mad *mad) +{ + if (!ibdev || !mad) { + return 1; + } + + return 0; +} + +static int golan_register_ibdev(struct golan_port *port) +{ + struct ib_device *ibdev = port->ibdev; + int rc; + + golan_get_ib_info ( ibdev ); + /* Register Infiniband device */ + if ((rc = register_ibdev(ibdev)) != 0) { + DBG ( "%s port %d could not register IB device: (rc = %d)\n", + __FUNCTION__, ibdev->port, rc); + return rc; + } + + port->netdev = ipoib_netdev( ibdev ); + + return 0; +} + +static inline void golan_bring_down(struct golan *golan) +{ + DBGC(golan, "%s: start\n", __FUNCTION__); + + if (~golan->flags & GOLAN_OPEN) { + DBGC(golan, "%s: end (already closed)\n", __FUNCTION__); + return; + } + + golan_destroy_mkey(golan); + golan_dealloc_pd(golan); + golan_destory_eq(golan); + golan_dealloc_uar(golan); + golan_teardown_hca(golan, GOLAN_TEARDOWN_GRACEFUL); + golan_handle_pages(golan, GOLAN_REG_PAGES , GOLAN_PAGES_TAKE); + golan_disable_hca(golan); + golan_cmd_uninit(golan); + golan->flags &= ~GOLAN_OPEN; + DBGC(golan, "%s: end\n", __FUNCTION__); +} + +static int golan_set_link_speed ( struct golan *golan ){ + mlx_status status; + int i = 0; + int utils_inited = 0; + + if ( ! golan->utils ) { + utils_inited = 1; + status = init_mlx_utils ( & golan->utils, golan->pci ); + MLX_CHECK_STATUS ( golan->pci, status, utils_init_err, "mlx_utils_init failed" ); + } + + for ( i = 0; i < golan->caps.num_ports; ++i ) { + status = mlx_set_link_speed ( golan->utils, i + 1, LINK_SPEED_IB, LINK_SPEED_SDR ); + MLX_CHECK_STATUS ( golan->pci, status, set_link_speed_err, "mlx_set_link_speed failed" ); + } + +set_link_speed_err: +if ( utils_inited ) + free_mlx_utils ( & golan->utils ); +utils_init_err: + return status; +} + +static inline int golan_bring_up(struct golan *golan) +{ + int rc = 0; + DBGC(golan, "%s\n", __FUNCTION__); + + if (golan->flags & GOLAN_OPEN) + return 0; + + if (( rc = golan_cmd_init(golan) )) + goto out; + + if (( rc = golan_core_enable_hca(golan) )) + goto cmd_uninit; + + /* Query for need for boot pages */ + if (( rc = golan_handle_pages(golan, GOLAN_BOOT_PAGES, GOLAN_PAGES_GIVE) )) + goto disable; + + if (( rc = golan_qry_hca_cap(golan) )) + goto pages; + + if (( rc = golan_set_hca_cap(golan) )) + goto pages; + + if (( rc = golan_handle_pages(golan, GOLAN_INIT_PAGES, GOLAN_PAGES_GIVE) )) + goto pages; + + if (( rc = golan_set_link_speed ( golan ) )) + goto pages_teardown; + + //Reg Init? + if (( rc = golan_hca_init(golan) )) + goto pages_2; + + if (( rc = golan_alloc_uar(golan) )) + goto teardown; + + if (( rc = golan_create_eq(golan) )) + goto de_uar; + + if (( rc = golan_alloc_pd(golan) )) + goto de_eq; + + if (( rc = golan_create_mkey(golan) )) + goto de_pd; + + golan->flags |= GOLAN_OPEN; + return 0; + + golan_destroy_mkey(golan); +de_pd: + golan_dealloc_pd(golan); +de_eq: + golan_destory_eq(golan); +de_uar: + golan_dealloc_uar(golan); +teardown: + golan_teardown_hca(golan, GOLAN_TEARDOWN_GRACEFUL); +pages_2: +pages_teardown: + golan_handle_pages(golan, GOLAN_INIT_PAGES, GOLAN_PAGES_TAKE); +pages: + golan_handle_pages(golan, GOLAN_BOOT_PAGES, GOLAN_PAGES_TAKE); +disable: + golan_disable_hca(golan); +cmd_uninit: + golan_cmd_uninit(golan); +out: + return rc; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void golan_ib_close ( struct ib_device *ibdev ) { + struct golan *golan = NULL; + + DBG ( "%s start\n", __FUNCTION__ ); + if ( ! ibdev ) + return; + golan = ib_get_drvdata ( ibdev ); + golan_bring_down ( golan ); + DBG ( "%s end\n", __FUNCTION__ ); +} + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int golan_ib_open ( struct ib_device *ibdev ) { + struct golan *golan = NULL; + DBG ( "%s start\n", __FUNCTION__ ); + + if ( ! ibdev ) + return -EINVAL; + golan = ib_get_drvdata ( ibdev ); + golan_bring_up ( golan ); + golan_ib_update ( ibdev ); + + DBG ( "%s end\n", __FUNCTION__ ); + return 0; +} + +/** Golan Infiniband operations */ +static struct ib_device_operations golan_ib_operations = { + .create_cq = golan_create_cq, + .destroy_cq = golan_destroy_cq, + .create_qp = golan_create_qp, + .modify_qp = golan_modify_qp, + .destroy_qp = golan_destroy_qp, + .post_send = golan_post_send, + .post_recv = golan_post_recv, + .poll_cq = golan_poll_cq, + .poll_eq = golan_poll_eq, + .open = golan_ib_open, + .close = golan_ib_close, + .mcast_attach = golan_mcast_attach, + .mcast_detach = golan_mcast_detach, + .set_port_info = golan_inform_sma, + .set_pkey_table = golan_inform_sma, +}; + +static int golan_probe_normal ( struct pci_device *pci ) { + struct golan *golan; + struct ib_device *ibdev; + struct golan_port *port; + int i; + int rc = 0; + + golan = golan_alloc(); + if ( !golan ) { + rc = -ENOMEM; + goto err_golan_alloc; + } + + /* at POST stage some BIOSes have limited available dynamic memory */ + if ( golan_init_fw_areas ( golan ) ) { + rc = -ENOMEM; + goto err_golan_golan_init_pages; + } + + /* Setup PCI bus and HCA BAR */ + pci_set_drvdata( pci, golan ); + golan->pci = pci; + golan_pci_init( golan ); + /* config command queues */ + if ( golan_bring_up( golan ) ) { + DBGC (golan ,"golan bringup failed\n"); + rc = -1; + goto err_golan_bringup; + } + + if ( ! DEVICE_IS_CIB ( pci->device ) ) { + if ( init_mlx_utils ( & golan->utils, pci ) ) { + rc = -1; + goto err_utils_init; + } + } + /* Allocate Infiniband devices */ + for (i = 0; i < golan->caps.num_ports; ++i) { + ibdev = alloc_ibdev( 0 ); + if ( !ibdev ) { + rc = -ENOMEM; + goto err_golan_probe_alloc_ibdev; + } + golan->ports[i].ibdev = ibdev; + golan->ports[i].vep_number = 0; + ibdev->op = &golan_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = (GOLAN_PORT_BASE + i); + ib_set_drvdata( ibdev, golan ); + } + + /* Register devices */ + for ( i = 0; i < golan->caps.num_ports; ++i ) { + port = &golan->ports[i]; + if ((rc = golan_register_ibdev ( port ) ) != 0 ) { + goto err_golan_probe_register_ibdev; + } + } + + golan_bring_down ( golan ); + + return 0; + + i = golan->caps.num_ports; +err_golan_probe_register_ibdev: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) + unregister_ibdev ( golan->ports[i].ibdev ); + + i = golan->caps.num_ports; +err_golan_probe_alloc_ibdev: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) + ibdev_put ( golan->ports[i].ibdev ); + if ( ! DEVICE_IS_CIB ( pci->device ) ) { + free_mlx_utils ( & golan->utils ); + } +err_utils_init: + golan_bring_down ( golan ); +err_golan_bringup: + iounmap( golan->iseg ); + golan_free_fw_areas ( golan ); +err_golan_golan_init_pages: + free ( golan ); +err_golan_alloc: + DBGC (golan ,"%s rc = %d\n", __FUNCTION__, rc); + return rc; +} + +static void golan_remove_normal ( struct pci_device *pci ) { + struct golan *golan = pci_get_drvdata(pci); + struct golan_port *port; + int i; + + DBGC(golan, "%s\n", __FUNCTION__); + + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + port = &golan->ports[i]; + unregister_ibdev ( port->ibdev ); + } + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + netdev_nullify ( golan->ports[i].netdev ); + } + for ( i = ( golan->caps.num_ports - 1 ) ; i >= 0 ; i-- ) { + ibdev_put ( golan->ports[i].ibdev ); + } + if ( ! DEVICE_IS_CIB ( pci->device ) ) { + free_mlx_utils ( & golan->utils ); + } + iounmap( golan->iseg ); + golan_free_fw_areas ( golan ); + free(golan); +} + +/*************************************************************************** + * NODNIC operations + **************************************************************************/ +static mlx_status shomron_tx_uar_send_db ( struct ib_device *ibdev, + struct nodnic_send_wqbb *wqbb ) { + mlx_status status = MLX_SUCCESS; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct shomron_nodnic_eth_send_wqe *eth_wqe = + ( struct shomron_nodnic_eth_send_wqe * )wqbb; + struct shomronprm_wqe_segment_ctrl_send *ctrl; + + 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 + + ( ( MLX_GET ( ctrl, wqe_index ) & 0x1 ) ? DB_BUFFER0_ODD_OFFSET + : DB_BUFFER0_EVEN_OFFSET ) ); +err: + return status; +} + +static mlx_status shomron_fill_eth_send_wqe ( struct ib_device *ibdev, + struct ib_queue_pair *qp, struct ib_address_vector *av __unused, + struct io_buffer *iobuf, struct nodnic_send_wqbb *wqbb, + unsigned long wqe_index ) { + mlx_status status = MLX_SUCCESS; + struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); + struct shomron_nodnic_eth_send_wqe *eth_wqe = NULL; + struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; + struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = + ib_qp_get_drvdata ( qp ); + nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; + struct nodnic_send_ring *send_ring = &nodnic_qp->send; + mlx_uint32 qpn = 0; + + eth_wqe = (struct shomron_nodnic_eth_send_wqe *)wqbb; + memset ( ( ( ( void * ) eth_wqe ) ), 0, + ( sizeof ( *eth_wqe ) ) ); + + status = nodnic_port_get_qpn(&port->port_priv, &send_ring->nodnic_ring, + &qpn); + if ( status != MLX_SUCCESS ) { + DBG("nodnic_port_get_qpn failed\n"); + goto err; + } + +#define SHOMRON_GENERATE_CQE 0x3 +#define SHOMRON_INLINE_HEADERS_SIZE 18 +#define SHOMRON_INLINE_HEADERS_OFFSET 32 + MLX_FILL_2 ( ð_wqe->ctrl, 0, opcode, FLEXBOOT_NODNIC_OPCODE_SEND, + wqe_index, wqe_index & 0xFFFF); + MLX_FILL_2 ( ð_wqe->ctrl, 1, ds, 0x4 , qpn, qpn ); + MLX_FILL_1 ( ð_wqe->ctrl, 2, + ce, SHOMRON_GENERATE_CQE /* generate completion */ + ); + MLX_FILL_2 ( ð_wqe->ctrl, 7, + inline_headers1, + cpu_to_be16(*(mlx_uint16 *)iobuf->data), + inline_headers_size, SHOMRON_INLINE_HEADERS_SIZE + ); + memcpy((void *)ð_wqe->ctrl + SHOMRON_INLINE_HEADERS_OFFSET, + iobuf->data + 2, SHOMRON_INLINE_HEADERS_SIZE - 2); + iob_pull(iobuf, SHOMRON_INLINE_HEADERS_SIZE); + MLX_FILL_1 ( ð_wqe->data[0], 0, + byte_count, iob_len ( iobuf ) ); + MLX_FILL_1 ( ð_wqe->data[0], 1, l_key, + flexboot_nodnic->device_priv.lkey ); + MLX_FILL_H ( ð_wqe->data[0], 2, + local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( ð_wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); +err: + return status; +} + +static mlx_status shomron_fill_completion( void *cqe, struct cqe_data *cqe_data ) { + union shomronprm_completion_entry *cq_entry; + uint32_t opcode; + + cq_entry = (union shomronprm_completion_entry *)cqe; + cqe_data->owner = MLX_GET ( &cq_entry->normal, owner ); + opcode = MLX_GET ( &cq_entry->normal, opcode ); +#define FLEXBOOT_NODNIC_OPCODE_CQ_SEND 0 +#define FLEXBOOT_NODNIC_OPCODE_CQ_RECV 2 +#define FLEXBOOT_NODNIC_OPCODE_CQ_SEND_ERR 13 +#define FLEXBOOT_NODNIC_OPCODE_CQ_RECV_ERR 14 + cqe_data->is_error = + ( opcode >= FLEXBOOT_NODNIC_OPCODE_CQ_RECV_ERR); + if ( cqe_data->is_error ) { + cqe_data->syndrome = MLX_GET ( &cq_entry->error, syndrome ); + cqe_data->vendor_err_syndrome = + MLX_GET ( &cq_entry->error, vendor_error_syndrome ); + cqe_data->is_send = + (opcode == FLEXBOOT_NODNIC_OPCODE_CQ_SEND_ERR); + } else { + cqe_data->is_send = + (opcode == FLEXBOOT_NODNIC_OPCODE_CQ_SEND); + cqe_data->wqe_counter = MLX_GET ( &cq_entry->normal, wqe_counter ); + cqe_data->byte_cnt = MLX_GET ( &cq_entry->normal, byte_cnt ); + + } + if ( cqe_data->is_send == TRUE ) + cqe_data->qpn = MLX_GET ( &cq_entry->normal, qpn ); + else + cqe_data->qpn = MLX_GET ( &cq_entry->normal, srqn ); + + return 0; +} + +static mlx_status shomron_cqe_set_owner ( void *cq, unsigned int num_cqes ) { + unsigned int i = 0; + union shomronprm_completion_entry *cq_list; + + cq_list = (union shomronprm_completion_entry *)cq; + for ( ; i < num_cqes ; i++ ) + MLX_FILL_1 ( &cq_list[i].normal, 15, owner, 1 ); + return 0; +} + +static mlx_size shomron_get_cqe_size () { + return sizeof ( union shomronprm_completion_entry ); +} + +struct flexboot_nodnic_callbacks shomron_nodnic_callbacks = { + .get_cqe_size = shomron_get_cqe_size, + .fill_send_wqe[IB_QPT_ETH] = shomron_fill_eth_send_wqe, + .fill_completion = shomron_fill_completion, + .cqe_set_owner = shomron_cqe_set_owner, + .irq = flexboot_nodnic_eth_irq, + .tx_uar_send_doorbell_fn = shomron_tx_uar_send_db, +}; + +static int shomron_nodnic_is_supported ( struct pci_device *pci ) { + if ( DEVICE_IS_CIB ( pci->device ) ) + return 0; + + return flexboot_nodnic_is_supported ( pci ); +} +/**************************************************************************/ + +static int golan_probe ( struct pci_device *pci ) { + int rc = -ENOTSUP; + + DBG ( "%s: start\n", __FUNCTION__ ); + + if ( ! pci ) { + DBG ( "%s: PCI is NULL\n", __FUNCTION__ ); + rc = -EINVAL; + goto probe_done; + } + + if ( shomron_nodnic_is_supported ( pci ) ) { + DBG ( "%s: Using NODNIC driver\n", __FUNCTION__ ); + rc = flexboot_nodnic_probe ( pci, &shomron_nodnic_callbacks, NULL ); + } else { + DBG ( "%s: Using normal driver\n", __FUNCTION__ ); + rc = golan_probe_normal ( pci ); + } + +probe_done: + DBG ( "%s: rc = %d\n", __FUNCTION__, rc ); + return rc; +} + +static void golan_remove ( struct pci_device *pci ) { + DBG ( "%s: start\n", __FUNCTION__ ); + + if ( ! shomron_nodnic_is_supported ( pci ) ) { + DBG ( "%s: Using normal driver remove\n", __FUNCTION__ ); + golan_remove_normal ( pci ); + return; + } + + DBG ( "%s: Using NODNIC driver remove\n", __FUNCTION__ ); + + flexboot_nodnic_remove ( pci ); + + DBG ( "%s: end\n", __FUNCTION__ ); +} + +static struct pci_device_id golan_nics[] = { + PCI_ROM ( 0x15b3, 0x1011, "ConnectIB", "ConnectIB HCA driver: DevID 4113", 0 ), + PCI_ROM ( 0x15b3, 0x1013, "ConnectX-4", "ConnectX-4 HCA driver, DevID 4115", 0 ), + PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ), + PCI_ROM ( 0x15b3, 0x1017, "ConnectX-5", "ConnectX-5 HCA driver, DevID 4119", 0 ), + PCI_ROM ( 0x15b3, 0x1019, "ConnectX-5EX", "ConnectX-5EX HCA driver, DevID 4121", 0 ), +}; + +struct pci_driver golan_driver __pci_driver = { + .ids = golan_nics, + .id_count = (sizeof(golan_nics) / sizeof(golan_nics[0])), + .probe = golan_probe, + .remove = golan_remove, +}; diff --git a/src/drivers/infiniband/golan.h b/src/drivers/infiniband/golan.h new file mode 100755 index 000000000..2fd06ecf0 --- /dev/null +++ b/src/drivers/infiniband/golan.h @@ -0,0 +1,344 @@ +#ifndef _GOLAN_H_ +#define _GOLAN_H_ + +/* + * Copyright (C) 2013-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 +#include +#include +#include +#include +#include +#include +#include "CIB_PRM.h" +#include "mlx_utils/include/public/mlx_utils.h" + +#define GOLAN_PCI_CONFIG_BAR_SIZE 0x100000//HERMON_PCI_CONFIG_BAR_SIZE //TODO: What is the BAR size? + +#define GOLAN_PAS_SIZE sizeof(uint64_t) + +#define GOLAN_INVALID_LKEY 0x00000100UL + +#define GOLAN_MAX_PORTS 2 +#define GOLAN_PORT_BASE 1 + +#define MELLANOX_VID 0x15b3 +#define GOLAN_HCA_BAR PCI_BASE_ADDRESS_0 //BAR 0 + +#define GOLAN_HCR_MAX_WAIT_MS 10000 + +#define min(a,b) ((a)<(b)?(a):(b)) + +#define GOLAN_PAGE_SHIFT 12 +#define GOLAN_PAGE_SIZE (1 << GOLAN_PAGE_SHIFT) +#define GOLAN_PAGE_MASK (GOLAN_PAGE_SIZE - 1) + +#define MAX_MBOX ( GOLAN_PAGE_SIZE / MAILBOX_STRIDE ) +#define DEF_CMD_IDX 1 +#define MEM_CMD_IDX 0 +#define NO_MBOX 0xffff +#define MEM_MBOX MEM_CMD_IDX +#define GEN_MBOX DEF_CMD_IDX + +#define CMD_IF_REV 4 + +#define MAX_PASE_MBOX ((GOLAN_CMD_PAS_CNT) - 2) + +#define CMD_STATUS( golan , idx ) ((struct golan_outbox_hdr *)(get_cmd( (golan) , (idx) )->out))->status +#define CMD_SYND( golan , idx ) ((struct golan_outbox_hdr *)(get_cmd( (golan) , (idx) )->out))->syndrome +#define QRY_PAGES_OUT( golan, idx ) ((struct golan_query_pages_outbox *)(get_cmd( (golan) , (idx) )->out)) + +#define VIRT_2_BE64_BUS( addr ) cpu_to_be64(((unsigned long long )virt_to_bus(addr))) +#define BE64_BUS_2_VIRT( addr ) bus_to_virt(be64_to_cpu(addr)) +#define USR_2_BE64_BUS( addr ) cpu_to_be64(((unsigned long long )user_to_phys(addr, 0))) +#define BE64_BUS_2_USR( addr ) be64_to_cpu(phys_to_user(addr)) + +#define GET_INBOX(golan, idx) (&(((struct mbox *)(golan->mboxes.inbox))[idx])) +#define GET_OUTBOX(golan, idx) (&(((struct mbox *)(golan->mboxes.outbox))[idx])) + +#define GOLAN_MBOX_IN( cmd_ptr, in_ptr ) ( { \ + union { \ + __be32 raw[4]; \ + typeof ( *(in_ptr) ) cooked; \ + } *u = container_of ( &(cmd_ptr)->in[0], typeof ( *u ), raw[0] ); \ + &u->cooked; } ) + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +/* Fw status fields */ +typedef enum { + NO_ERRORS = 0x0, + SIGNATURE_ERROR = 0x1, + TOKEN_ERROR = 0x2, + BAD_BLOCK_NUMBER = 0x3, + BAD_OUTPUT_POINTER = 0x4, // pointer not align to mailbox size + BAD_INPUT_POINTER = 0x5, // pointer not align to mailbox size + INTERNAL_ERROR = 0x6, + INPUT_LEN_ERROR = 0x7, // input length less than 0x8. + OUTPUT_LEN_ERROR = 0x8, // output length less than 0x8. + RESERVE_NOT_ZERO = 0x9, + BAD_CMD_TYPE = 0x10, +} return_hdr_t; + +struct golan_cmdq_md { + void *addr; + u16 log_stride; + u16 size; +}; + +struct golan_uar { + uint32_t index; + void *virt; + 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) +#define GOLAN_RECV_WQE_SIZE sizeof(struct golan_recv_wqe_ud) +#define GOLAN_WQEBBS_PER_SEND_UD_WQE DIV_ROUND_UP(GOLAN_SEND_UD_WQE_SIZE, GOLAN_SEND_WQE_BB_SIZE) +#define GOLAN_SEND_OPCODE 0x0a +#define GOLAN_WQE_CTRL_WQE_IDX_BIT 8 + +enum golan_ib_qp_state { + GOLAN_IB_QPS_RESET, + GOLAN_IB_QPS_INIT, + GOLAN_IB_QPS_RTR, + GOLAN_IB_QPS_RTS, + GOLAN_IB_QPS_SQD, + GOLAN_IB_QPS_SQE, + GOLAN_IB_QPS_ERR +}; + +struct golan_send_wqe_ud { + struct golan_wqe_ctrl_seg ctrl; + struct golan_av datagram; + struct golan_wqe_data_seg data; +}; + +union golan_send_wqe { + struct golan_send_wqe_ud ud; + uint8_t pad[GOLAN_WQEBBS_PER_SEND_UD_WQE * GOLAN_SEND_WQE_BB_SIZE]; +}; + +struct golan_recv_wqe_ud { + struct golan_wqe_data_seg data[2]; +}; + +struct golan_recv_wq { + struct golan_recv_wqe_ud *wqes; + /* WQ size in bytes */ + int size; + /* In SQ, it will be increased in wqe_size (number of WQEBBs per WQE) */ + u16 next_idx; + /** GRH buffers (if applicable) */ + struct ib_global_route_header *grh; + /** Size of GRH buffers */ + size_t grh_size; +}; + +struct golan_send_wq { + union golan_send_wqe *wqes; + /* WQ size in bytes */ + int size; + /* In SQ, it will be increased in wqe_size (number of WQEBBs per WQE) */ + u16 next_idx; +}; + +struct golan_queue_pair { + void *wqes; + int size; + struct golan_recv_wq rq; + struct golan_send_wq sq; + struct golan_qp_db *doorbell_record; + u32 doorbell_qpn; + enum golan_ib_qp_state state; +}; + +/* Completion Queue */ +#define GOLAN_CQE_OPCODE_NOT_VALID 0x0f +#define GOLAN_CQE_OPCODE_BIT 4 +#define GOLAN_CQ_DB_RECORD_SIZE sizeof(uint64_t) +#define GOLAN_CQE_OWNER_MASK 1 + +#define MANAGE_PAGES_PSA_OFFSET 0 +#define PXE_CMDIF_REF 5 + +enum { + GOLAN_CQE_SW_OWNERSHIP = 0x0, + GOLAN_CQE_HW_OWNERSHIP = 0x1 +}; + +enum { + GOLAN_CQE_SIZE_64 = 0, + GOLAN_CQE_SIZE_128 = 1 +}; + +struct golan_completion_queue { + struct golan_cqe64 *cqes; + int size; + __be64 *doorbell_record; +}; + + +/* Event 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 )| \ + (1 << GOLAN_EVENT_TYPE_COMM_EST )| \ + (1 << GOLAN_EVENT_TYPE_SQ_DRAINED )| \ + (1 << GOLAN_EVENT_TYPE_SRQ_LAST_WQE )| \ + (1 << GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT )| \ + (1 << GOLAN_EVENT_TYPE_CQ_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_WQ_CATAS_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_PATH_MIG_FAILED )| \ + (1 << GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_INTERNAL_ERROR )| \ + (1 << GOLAN_EVENT_TYPE_PORT_CHANGE )| \ + (1 << GOLAN_EVENT_TYPE_GPIO_EVENT )| \ + (1 << GOLAN_EVENT_TYPE_CLIENT_RE_REGISTER )| \ + (1 << GOLAN_EVENT_TYPE_REMOTE_CONFIG )| \ + (1 << GOLAN_EVENT_TYPE_DB_BF_CONGESTION )| \ + (1 << GOLAN_EVENT_TYPE_STALL_EVENT )| \ + (1 << GOLAN_EVENT_TYPE_PACKET_DROPPED )| \ + (1 << GOLAN_EVENT_TYPE_CMD )| \ + (1 << GOLAN_EVENT_TYPE_PAGE_REQUEST )) + +enum golan_event { + GOLAN_EVENT_TYPE_COMP = 0x0, + + GOLAN_EVENT_TYPE_PATH_MIG = 0x01, + GOLAN_EVENT_TYPE_COMM_EST = 0x02, + GOLAN_EVENT_TYPE_SQ_DRAINED = 0x03, + GOLAN_EVENT_TYPE_SRQ_LAST_WQE = 0x13, + GOLAN_EVENT_TYPE_SRQ_RQ_LIMIT = 0x14, + + GOLAN_EVENT_TYPE_CQ_ERROR = 0x04, + GOLAN_EVENT_TYPE_WQ_CATAS_ERROR = 0x05, + GOLAN_EVENT_TYPE_PATH_MIG_FAILED = 0x07, + GOLAN_EVENT_TYPE_WQ_INVAL_REQ_ERROR = 0x10, + GOLAN_EVENT_TYPE_WQ_ACCESS_ERROR = 0x11, + GOLAN_EVENT_TYPE_SRQ_CATAS_ERROR = 0x12, + + GOLAN_EVENT_TYPE_INTERNAL_ERROR = 0x08, + GOLAN_EVENT_TYPE_PORT_CHANGE = 0x09, + GOLAN_EVENT_TYPE_GPIO_EVENT = 0x15, +// GOLAN_EVENT_TYPE_CLIENT_RE_REGISTER = 0x16, + GOLAN_EVENT_TYPE_REMOTE_CONFIG = 0x19, + + GOLAN_EVENT_TYPE_DB_BF_CONGESTION = 0x1a, + GOLAN_EVENT_TYPE_STALL_EVENT = 0x1b, + + GOLAN_EVENT_TYPE_PACKET_DROPPED = 0x1f, + + GOLAN_EVENT_TYPE_CMD = 0x0a, + GOLAN_EVENT_TYPE_PAGE_REQUEST = 0x0b, + GOLAN_EVENT_TYPE_PAGE_FAULT = 0x0C, +}; + +enum golan_port_sub_event { + GOLAN_PORT_CHANGE_SUBTYPE_DOWN = 1, + GOLAN_PORT_CHANGE_SUBTYPE_ACTIVE = 4, + GOLAN_PORT_CHANGE_SUBTYPE_INITIALIZED = 5, + GOLAN_PORT_CHANGE_SUBTYPE_LID = 6, + GOLAN_PORT_CHANGE_SUBTYPE_PKEY = 7, + GOLAN_PORT_CHANGE_SUBTYPE_GUID = 8, + GOLAN_PORT_CHANGE_SUBTYPE_CLIENT_REREG = 9 +}; + + +enum { + GOLAN_EQE_SW_OWNERSHIP = 0x0, + GOLAN_EQE_HW_OWNERSHIP = 0x1 +}; + +enum { + GOLAN_EQ_UNARMED = 0, + GOLAN_EQ_ARMED = 1, +}; + +struct golan_event_queue { + uint8_t eqn; + uint64_t mask; + struct golan_eqe *eqes; + int size; + __be32 *doorbell; + uint32_t cons_index; +}; + +struct golan_port { + /** Infiniband device */ + struct ib_device *ibdev; + /** Network device */ + struct net_device *netdev; + /** VEP number */ + u8 vep_number; +}; + +struct golan_mboxes { + void *inbox; + void *outbox; +}; + +#define GOLAN_OPEN 0x1 + +struct golan { + struct pci_device *pci; + struct golan_hca_init_seg *iseg; + struct golan_cmdq_md cmd; + struct golan_hca_cap caps; /* stored as big indian*/ + struct golan_mboxes mboxes; + struct list_head pages; + uint32_t cmd_bm; + uint32_t total_dma_pages; + struct golan_uar uar; + struct golan_event_queue eq; + uint32_t pdn; + u32 mkey; + u32 flags; + 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_*/ diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index a9c728706..a1d2a3bd5 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -1111,6 +1111,8 @@ static int hermon_create_qp ( struct ib_device *ibdev, struct hermon *hermon = ib_get_drvdata ( ibdev ); struct hermon_queue_pair *hermon_qp; struct hermonprm_qp_ee_state_transitions qpctx; + struct hermonprm_wqe_segment_data_ptr *data; + unsigned int i; int rc; /* Calculate queue pair number */ @@ -1147,8 +1149,14 @@ static int hermon_create_qp ( struct ib_device *ibdev, sizeof ( hermon_qp->send.wqe[0] ) ); hermon_qp->recv.wqe_size = ( qp->recv.num_wqes * sizeof ( hermon_qp->recv.wqe[0] ) ); + if ( ( qp->type == IB_QPT_SMI ) || ( qp->type == IB_QPT_GSI ) || + ( qp->type == IB_QPT_UD ) ) { + hermon_qp->recv.grh_size = ( qp->recv.num_wqes * + sizeof ( hermon_qp->recv.grh[0] )); + } hermon_qp->wqe_size = ( hermon_qp->send.wqe_size + - hermon_qp->recv.wqe_size ); + hermon_qp->recv.wqe_size + + hermon_qp->recv.grh_size ); hermon_qp->wqe = malloc_dma ( hermon_qp->wqe_size, sizeof ( hermon_qp->send.wqe[0] ) ); if ( ! hermon_qp->wqe ) { @@ -1156,9 +1164,21 @@ static int hermon_create_qp ( struct ib_device *ibdev, goto err_alloc_wqe; } hermon_qp->send.wqe = hermon_qp->wqe; - memset ( hermon_qp->send.wqe, 0xff, hermon_qp->send.wqe_size ); hermon_qp->recv.wqe = ( hermon_qp->wqe + hermon_qp->send.wqe_size ); + if ( hermon_qp->recv.grh_size ) { + hermon_qp->recv.grh = ( hermon_qp->wqe + + hermon_qp->send.wqe_size + + hermon_qp->recv.wqe_size ); + } + + /* Initialise work queue entries */ + memset ( hermon_qp->send.wqe, 0xff, hermon_qp->send.wqe_size ); memset ( hermon_qp->recv.wqe, 0, hermon_qp->recv.wqe_size ); + data = &hermon_qp->recv.wqe[0].recv.data[0]; + for ( i = 0 ; i < ( hermon_qp->recv.wqe_size / sizeof ( *data ) ); i++){ + MLX_FILL_1 ( data, 1, l_key, HERMON_INVALID_LKEY ); + data++; + } /* Allocate MTT entries */ if ( ( rc = hermon_alloc_mtt ( hermon, hermon_qp->wqe, @@ -1633,6 +1653,8 @@ static int hermon_post_recv ( struct ib_device *ibdev, struct ib_work_queue *wq = &qp->recv; struct hermon_recv_work_queue *hermon_recv_wq = &hermon_qp->recv; struct hermonprm_recv_wqe *wqe; + struct hermonprm_wqe_segment_data_ptr *data; + struct ib_global_route_header *grh; unsigned int wqe_idx_mask; /* Allocate work queue entry */ @@ -1646,12 +1668,19 @@ static int hermon_post_recv ( struct ib_device *ibdev, wqe = &hermon_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv; /* Construct work queue entry */ - MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); - MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->lkey ); - MLX_FILL_H ( &wqe->data[0], 2, - local_address_h, virt_to_bus ( iobuf->data ) ); - MLX_FILL_1 ( &wqe->data[0], 3, - local_address_l, virt_to_bus ( iobuf->data ) ); + data = &wqe->data[0]; + if ( hermon_qp->recv.grh ) { + grh = &hermon_qp->recv.grh[wq->next_idx & wqe_idx_mask]; + MLX_FILL_1 ( data, 0, byte_count, sizeof ( *grh ) ); + MLX_FILL_1 ( data, 1, l_key, hermon->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( grh ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( grh ) ); + data++; + } + MLX_FILL_1 ( data, 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( data, 1, l_key, hermon->lkey ); + MLX_FILL_H ( data, 2, local_address_h, virt_to_bus ( iobuf->data ) ); + MLX_FILL_1 ( data, 3, local_address_l, virt_to_bus ( iobuf->data ) ); /* Update work queue's index */ wq->next_idx++; @@ -1676,6 +1705,7 @@ static int hermon_complete ( struct ib_device *ibdev, struct ib_completion_queue *cq, union hermonprm_completion_entry *cqe ) { struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_queue_pair *hermon_qp; struct ib_work_queue *wq; struct ib_queue_pair *qp; struct io_buffer *iobuf; @@ -1713,6 +1743,7 @@ static int hermon_complete ( struct ib_device *ibdev, return -EIO; } qp = wq->qp; + hermon_qp = ib_qp_get_drvdata ( qp ); /* Identify work queue entry */ wqe_idx = MLX_GET ( &cqe->normal, wqe_counter ); @@ -1738,8 +1769,6 @@ static int hermon_complete ( struct ib_device *ibdev, } else { /* Set received length */ len = MLX_GET ( &cqe->normal, byte_cnt ); - assert ( len <= iob_tailroom ( iobuf ) ); - iob_put ( iobuf, len ); memset ( &recv_dest, 0, sizeof ( recv_dest ) ); recv_dest.qpn = qpn; memset ( &recv_source, 0, sizeof ( recv_source ) ); @@ -1747,9 +1776,10 @@ static int hermon_complete ( struct ib_device *ibdev, case IB_QPT_SMI: case IB_QPT_GSI: case IB_QPT_UD: - assert ( iob_len ( iobuf ) >= sizeof ( *grh ) ); - grh = iobuf->data; - iob_pull ( iobuf, sizeof ( *grh ) ); + /* Locate corresponding GRH */ + assert ( hermon_qp->recv.grh != NULL ); + grh = &hermon_qp->recv.grh[ wqe_idx & wqe_idx_mask ]; + len -= sizeof ( *grh ); /* Construct address vector */ source = &recv_source; source->qpn = MLX_GET ( &cqe->normal, srq_rqpn ); @@ -1775,6 +1805,8 @@ static int hermon_complete ( struct ib_device *ibdev, assert ( 0 ); return -EINVAL; } + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); /* Hand off to completion handler */ ib_complete_recv ( ibdev, qp, &recv_dest, source, iobuf, rc ); } @@ -2081,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; @@ -2103,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 { @@ -3229,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 ); - 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 ); @@ -3747,24 +3776,6 @@ static void hermon_free ( struct hermon *hermon ) { free ( hermon ); } -/** - * Initialise Hermon PCI parameters - * - * @v hermon Hermon device - */ -static void hermon_pci_init ( struct hermon *hermon ) { - struct pci_device *pci = hermon->pci; - - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Get PCI BARs */ - hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR), - HERMON_PCI_CONFIG_BAR_SIZE ); - hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ), - HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE ); -} - /** * Probe PCI device * @@ -3789,8 +3800,14 @@ static int hermon_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, hermon ); hermon->pci = pci; - /* Initialise PCI parameters */ - hermon_pci_init ( hermon ); + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BARs */ + hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR ), + HERMON_PCI_CONFIG_BAR_SIZE ); + hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ), + HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE ); /* Reset device */ hermon_reset ( hermon ); @@ -3885,6 +3902,8 @@ static int hermon_probe ( struct pci_device *pci ) { err_get_cap: hermon_stop_firmware ( hermon ); err_start_firmware: + iounmap ( hermon->uar ); + iounmap ( hermon->config ); hermon_free ( hermon ); err_alloc: return rc; @@ -3910,6 +3929,8 @@ static void hermon_remove ( struct pci_device *pci ) { } for ( i = ( hermon->cap.num_ports - 1 ) ; i >= 0 ; i-- ) ibdev_put ( hermon->port[i].ibdev ); + iounmap ( hermon->uar ); + iounmap ( hermon->config ); hermon_free ( hermon ); } @@ -3933,8 +3954,12 @@ static int hermon_bofm_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, hermon ); hermon->pci = pci; - /* Initialise PCI parameters */ - hermon_pci_init ( hermon ); + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BAR */ + hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR ), + HERMON_PCI_CONFIG_BAR_SIZE ); /* Initialise BOFM device */ bofm_init ( &hermon->bofm, pci, &hermon_bofm_operations ); @@ -3949,6 +3974,7 @@ static int hermon_bofm_probe ( struct pci_device *pci ) { return 0; err_bofm_register: + iounmap ( hermon->config ); hermon_free ( hermon ); err_alloc: return rc; @@ -3963,6 +3989,7 @@ static void hermon_bofm_remove ( struct pci_device *pci ) { struct hermon *hermon = pci_get_drvdata ( pci ); bofm_unregister ( &hermon->bofm ); + iounmap ( hermon->config ); hermon_free ( hermon ); } diff --git a/src/drivers/infiniband/hermon.h b/src/drivers/infiniband/hermon.h index e0b028f26..61e285781 100644 --- a/src/drivers/infiniband/hermon.h +++ b/src/drivers/infiniband/hermon.h @@ -515,7 +515,7 @@ struct hermonprm_eth_send_wqe { struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER]; } __attribute__ (( packed )); -#define HERMON_MAX_SCATTER 1 +#define HERMON_MAX_SCATTER 2 struct hermonprm_recv_wqe { struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_SCATTER]; @@ -686,6 +686,10 @@ struct hermon_recv_work_queue { union hermon_recv_wqe *wqe; /** Size of work queue */ size_t wqe_size; + /** GRH buffers (if applicable) */ + struct ib_global_route_header *grh; + /** Size of GRH buffers */ + size_t grh_size; /** Doorbell record */ struct hermonprm_qp_db_record *doorbell; }; diff --git a/src/drivers/infiniband/linda.c b/src/drivers/infiniband/linda.c index a6ae9f529..e8d61c865 100644 --- a/src/drivers/infiniband/linda.c +++ b/src/drivers/infiniband/linda.c @@ -112,32 +112,21 @@ struct linda { * This card requires atomic 64-bit accesses. Strange things happen * if you try to use 32-bit accesses; sometimes they work, sometimes * they don't, sometimes you get random data. - * - * These accessors use the "movq" MMX instruction, and so won't work - * on really old Pentiums (which won't have PCIe anyway, so this is - * something of a moot point). */ /** * Read Linda qword register * * @v linda Linda device - * @v dwords Register buffer to read into + * @v qword Register buffer to read into * @v offset Register offset */ -static void linda_readq ( struct linda *linda, uint32_t *dwords, +static void linda_readq ( struct linda *linda, uint64_t *qword, unsigned long offset ) { - void *addr = ( linda->regs + offset ); - - __asm__ __volatile__ ( "movq (%1), %%mm0\n\t" - "movq %%mm0, (%0)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); - - DBGIO ( "[%08lx] => %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); + *qword = readq ( linda->regs + offset ); } #define linda_readq( _linda, _ptr, _offset ) \ - linda_readq ( (_linda), (_ptr)->u.dwords, (_offset) ) + linda_readq ( (_linda), (_ptr)->u.qwords, (_offset) ) #define linda_readq_array8b( _linda, _ptr, _offset, _idx ) \ linda_readq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define linda_readq_array64k( _linda, _ptr, _offset, _idx ) \ @@ -147,22 +136,15 @@ static void linda_readq ( struct linda *linda, uint32_t *dwords, * Write Linda qword register * * @v linda Linda device - * @v dwords Register buffer to write + * @v qword Register buffer to write * @v offset Register offset */ -static void linda_writeq ( struct linda *linda, const uint32_t *dwords, +static void linda_writeq ( struct linda *linda, const uint64_t *qword, unsigned long offset ) { - void *addr = ( linda->regs + offset ); - - DBGIO ( "[%08lx] <= %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); - - __asm__ __volatile__ ( "movq (%0), %%mm0\n\t" - "movq %%mm0, (%1)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); + writeq ( *qword, ( linda->regs + offset ) ); } #define linda_writeq( _linda, _ptr, _offset ) \ - linda_writeq ( (_linda), (_ptr)->u.dwords, (_offset) ) + linda_writeq ( (_linda), (_ptr)->u.qwords, (_offset) ) #define linda_writeq_array8b( _linda, _ptr, _offset, _idx ) \ linda_writeq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define linda_writeq_array64k( _linda, _ptr, _offset, _idx ) \ @@ -555,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 ) ); @@ -1289,8 +1271,15 @@ static void linda_complete_recv ( struct ib_device *ibdev, /* Completing the eager buffer described in * this header entry. */ - iob_put ( iobuf, payload_len ); - rc = ( err ? -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + if ( payload_len <= iob_tailroom ( iobuf ) ) { + iob_put ( iobuf, payload_len ); + rc = ( err ? + -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + } else { + DBGC ( linda, "Linda %p bad payload len %zd\n", + linda, payload_len ); + rc = -EPROTO; + } /* Redirect to target QP if necessary */ if ( qp != intended_qp ) { DBGC ( linda, "Linda %p redirecting QPN %ld " @@ -1301,7 +1290,7 @@ static void linda_complete_recv ( struct ib_device *ibdev, intended_qp->recv.fill++; } ib_complete_recv ( ibdev, intended_qp, &dest, &source, - iobuf, rc); + iobuf, rc ); } else { /* Completing on a skipped-over eager buffer */ ib_complete_recv ( ibdev, qp, &dest, &source, iobuf, @@ -2345,7 +2334,7 @@ static int linda_probe ( struct pci_device *pci ) { /* Fix up PCI device */ adjust_pci_device ( pci ); - /* Get PCI BARs */ + /* Map PCI BARs */ linda->regs = ioremap ( pci->membase, LINDA_BAR0_SIZE ); DBGC2 ( linda, "Linda %p has BAR at %08lx\n", linda, pci->membase ); @@ -2406,6 +2395,7 @@ static int linda_probe ( struct pci_device *pci ) { err_init_ib_serdes: err_read_eeprom: err_init_i2c: + iounmap ( linda->regs ); ibdev_put ( ibdev ); err_alloc_ibdev: return rc; @@ -2423,6 +2413,7 @@ static void linda_remove ( struct pci_device *pci ) { unregister_ibdev ( ibdev ); linda_fini_recv ( linda ); linda_fini_send ( linda ); + iounmap ( linda->regs ); ibdev_put ( ibdev ); } diff --git a/src/drivers/infiniband/linda.h b/src/drivers/infiniband/linda.h index 46a920a17..44c7686f4 100644 --- a/src/drivers/infiniband/linda.h +++ b/src/drivers/infiniband/linda.h @@ -33,8 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#define BITOPS_LITTLE_ENDIAN -#include +#define PSEUDOBIT_LITTLE_ENDIAN +#include #include "qib_7220_regs.h" struct ib_device; diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h new file mode 100644 index 000000000..e1e89b4c3 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_cmd.h @@ -0,0 +1,43 @@ +#ifndef NODNIC_CMD_H_ +#define NODNIC_CMD_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_nodnic_data_structures.h" +#include "../../mlx_utils/include/public/mlx_utils.h" +#include "../../mlx_utils/include/public/mlx_pci_gw.h" + +mlx_status +nodnic_cmd_read( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ); + +mlx_status +nodnic_cmd_write( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ); + +#endif /* STUB_NODNIC_CMD_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_device.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_device.h new file mode 100644 index 000000000..b0cc7f723 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_device.h @@ -0,0 +1,80 @@ +#ifndef NODNIC_DEVICE_H_ +#define NODNIC_DEVICE_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_nodnic_data_structures.h" + +#define NODIC_SUPPORTED_REVISION 1 +//Initialization segment +#define NODNIC_CMDQ_PHY_ADDR_HIGH_OFFSET 0x10 +#define NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET 0x14 +#define NODNIC_NIC_INTERFACE_OFFSET 0x14 +#define NODNIC_INITIALIZING_OFFSET 0x1fc +#define NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET 0x1fc +#define NODNIC_LOCATION_OFFSET 0x240 + +#define NODNIC_CMDQ_PHY_ADDR_LOW_MASK 0xFFFFE000 +#define NODNIC_NIC_INTERFACE_SUPPORTED_MASK 0x4000000 + +#define NODNIC_NIC_INTERFACE_BIT 9 +#define NODNIC_DISABLE_INTERFACE_BIT 8 +#define NODNIC_NIC_INTERFACE_SUPPORTED_BIT 26 +#define NODNIC_INITIALIZING_BIT 31 + +#define NODNIC_NIC_DISABLE_INT_OFFSET 0x100c + +//nodnic segment +#define NODNIC_REVISION_OFFSET 0x0 +#define NODNIC_HARDWARE_FORMAT_OFFSET 0x0 + + + +mlx_status +nodnic_device_init( + IN nodnic_device_priv *device_priv + ); + +mlx_status +nodnic_device_teardown( + IN nodnic_device_priv *device_priv + ); + + +mlx_status +nodnic_device_get_cap( + IN nodnic_device_priv *device_priv + ); + +mlx_status +nodnic_device_clear_int ( + IN nodnic_device_priv *device_priv + ); + +mlx_status +nodnic_device_get_fw_version( + IN nodnic_device_priv *device_priv, + OUT mlx_uint16 *fw_ver_minor, + OUT mlx_uint16 *fw_ver_sub_minor, + OUT mlx_uint16 *fw_ver_major + ); +#endif /* STUB_NODNIC_DEVICE_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h new file mode 100644 index 000000000..61f2c5736 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_nodnic_data_structures.h @@ -0,0 +1,231 @@ +#ifndef NODNIC_NODNICDATASTRUCTURES_H_ +#define NODNIC_NODNICDATASTRUCTURES_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/include/public/mlx_utils.h" + +/* todo: fix coding convention */ +#define NODNIC_MEMORY_ALIGN 0x1000 + +#define NODNIC_MAX_MAC_FILTERS 5 +#define NODNIC_MAX_MGID_FILTERS 4 + +typedef struct _nodnic_device_priv nodnic_device_priv; +typedef struct _nodnic_port_priv nodnic_port_priv; +typedef struct _nodnic_device_capabilites nodnic_device_capabilites; +typedef struct _nodnic_qp nodnic_qp; +typedef struct _nodnic_cq nodnic_cq; +typedef struct _nodnic_eq nodnic_eq; +typedef struct _nodnic_qp_db nodnic_qp_db; +typedef struct _nodnic_arm_cq_db nodnic_arm_cq_db; + +/* NODNIC Port states + * Bit 0 - port open/close + * Bit 1 - port is [not] in disabling DMA + * 0 - closed and not disabling DMA + * 1 - opened and not disabling DMA + * 3 - opened and disabling DMA + */ +#define NODNIC_PORT_OPENED 0b00000001 +#define NODNIC_PORT_DISABLING_DMA 0b00000010 + +typedef enum { + ConnectX3 = 0, + Connectx4 +}nodnic_hardware_format; + + +typedef enum { + NODNIC_QPT_SMI, + NODNIC_QPT_GSI, + NODNIC_QPT_UD, + NODNIC_QPT_RC, + NODNIC_QPT_ETH, +}nodnic_queue_pair_type; +typedef enum { + NODNIC_PORT_TYPE_IB = 0, + NODNIC_PORT_TYPE_ETH, + NODNIC_PORT_TYPE_UNKNOWN, +}nodnic_port_type; + + +#define RECV_WQE_SIZE 16 +#define NODNIC_WQBB_SIZE 64 +/** A nodnic send wqbb */ +struct nodnic_send_wqbb { + mlx_uint8 force_align[NODNIC_WQBB_SIZE]; +}; + +struct nodnic_doorbell { + mlx_physical_address doorbell_physical; + mlx_void *map; + nodnic_qp_db *qp_doorbell_record; +}; +struct nodnic_ring { + mlx_uint32 offset; + /** Work queue entries */ + /* TODO: add to memory entity */ + mlx_physical_address wqe_physical; + mlx_void *map; + /** Size of work queue */ + mlx_size wq_size; + /** Next work queue entry index + * + * This is the index of the next entry to be filled (i.e. the + * first empty entry). This value is not bounded by num_wqes; + * users must logical-AND with (num_wqes-1) to generate an + * array index. + */ + mlx_uint32 num_wqes; + mlx_uint32 qpn; + mlx_uint32 next_idx; + struct nodnic_doorbell recv_doorbell; + struct nodnic_doorbell send_doorbell; +}; + +struct nodnic_send_ring{ + struct nodnic_ring nodnic_ring; + struct nodnic_send_wqbb *wqe_virt; +}; + + +struct nodnic_recv_ring{ + struct nodnic_ring nodnic_ring; + void *wqe_virt; +}; +struct _nodnic_qp{ + nodnic_queue_pair_type type; + struct nodnic_send_ring send; + struct nodnic_recv_ring receive; +}; + +struct _nodnic_cq{ + /** cq entries */ + mlx_void *cq_virt; + mlx_physical_address cq_physical; + mlx_void *map; + /** cq */ + mlx_size cq_size; + struct nodnic_doorbell arm_cq_doorbell; +}; + +struct _nodnic_eq{ + mlx_void *eq_virt; + mlx_physical_address eq_physical; + mlx_void *map; + mlx_size eq_size; +}; +struct _nodnic_device_capabilites{ + mlx_boolean support_mac_filters; + mlx_boolean support_promisc_filter; + mlx_boolean support_promisc_multicast_filter; + mlx_uint8 log_working_buffer_size; + mlx_uint8 log_pkey_table_size; + mlx_boolean num_ports; // 0 - single port, 1 - dual port + mlx_uint8 log_max_ring_size; +#ifdef DEVICE_CX3 + mlx_uint8 crspace_doorbells; +#endif + mlx_uint8 support_rx_pi_dma; + mlx_uint8 support_uar_tx_db; + mlx_uint8 support_bar_cq_ctrl; + mlx_uint8 log_uar_page_size; +}; + +#ifdef DEVICE_CX3 +/* This is the structure of the data in the scratchpad + * Read/Write data from/to its field using PCI accesses only */ +typedef struct _nodnic_port_data_flow_gw nodnic_port_data_flow_gw; +struct _nodnic_port_data_flow_gw { + mlx_uint32 send_doorbell; + mlx_uint32 recv_doorbell; + mlx_uint32 reserved2[2]; + mlx_uint32 armcq_cq_ci_dword; + mlx_uint32 dma_en; +} __attribute__ ((packed)); +#endif + +typedef struct _nodnic_uar_priv{ + mlx_uint8 inited; + mlx_uint64 offset; + void *virt; + unsigned long phys; +} nodnic_uar; + +struct _nodnic_device_priv{ + mlx_boolean is_initiailzied; + mlx_utils *utils; + + //nodnic structure offset in init segment + mlx_uint32 device_offset; + + nodnic_device_capabilites device_cap; + + mlx_uint8 nodnic_revision; + nodnic_hardware_format hardware_format; + mlx_uint32 pd; + mlx_uint32 lkey; + mlx_uint64 device_guid; + nodnic_port_priv *ports; +#ifdef DEVICE_CX3 + mlx_void *crspace_clear_int; +#endif + nodnic_uar uar; +}; + +struct _nodnic_port_priv{ + nodnic_device_priv *device; + mlx_uint32 port_offset; + mlx_uint8 port_state; + mlx_boolean network_state; + mlx_boolean dma_state; + nodnic_port_type port_type; + mlx_uint8 port_num; + nodnic_eq eq; + mlx_mac_address mac_filters[5]; + nodnic_arm_cq_db *arm_cq_doorbell_record; + mlx_status (*send_doorbell)( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index); + mlx_status (*recv_doorbell)( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index); + mlx_status (*set_dma)( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value); +#ifdef DEVICE_CX3 + nodnic_port_data_flow_gw *data_flow_gw; +#endif +}; + +struct _nodnic_qp_db { + mlx_uint32 recv_db; + mlx_uint32 send_db; +} __attribute ( ( packed ) ); + +struct _nodnic_arm_cq_db { + mlx_uint32 dword[2]; +} __attribute ( ( packed ) ); +#endif /* STUB_NODNIC_NODNICDATASTRUCTURES_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h b/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h new file mode 100644 index 000000000..bb3026729 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/include/mlx_port.h @@ -0,0 +1,242 @@ +#ifndef NODNIC_PORT_H_ +#define NODNIC_PORT_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_nodnic_data_structures.h" + +#define NODNIC_PORT_MAC_FILTERS_OFFSET 0x10 + +typedef enum { + nodnic_port_option_link_type = 0, + nodnic_port_option_mac_low, + nodnic_port_option_mac_high, + nodnic_port_option_log_cq_size, + nodnic_port_option_reset_needed, + nodnic_port_option_mac_filters_en, + nodnic_port_option_port_state, + nodnic_port_option_network_en, + nodnic_port_option_dma_en, + nodnic_port_option_eq_addr_low, + nodnic_port_option_eq_addr_high, + nodnic_port_option_cq_addr_low, + nodnic_port_option_cq_addr_high, + nodnic_port_option_port_management_change_event, + nodnic_port_option_port_promisc_en, + nodnic_port_option_arm_cq, + nodnic_port_option_port_promisc_multicast_en, +#ifdef DEVICE_CX3 + nodnic_port_option_crspace_en, +#endif + nodnic_port_option_send_ring0_uar_index, + nodnic_port_option_send_ring1_uar_index, + nodnic_port_option_cq_n_index, +}nodnic_port_option; + +struct nodnic_port_data_entry{ + nodnic_port_option option; + mlx_uint32 offset; + mlx_uint8 align; + mlx_uint32 mask; +}; + +struct nodnic_qp_data_entry{ + nodnic_queue_pair_type type; + mlx_uint32 send_offset; + mlx_uint32 recv_offset; +}; + + +typedef enum { + nodnic_port_state_down = 0, + nodnic_port_state_initialize, + nodnic_port_state_armed, + nodnic_port_state_active, +}nodnic_port_state; + +mlx_status +nodnic_port_get_state( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_state *state + ); + +mlx_status +nodnic_port_get_type( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_type *type + ); + +mlx_status +nodnic_port_query( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + OUT mlx_uint32 *out + ); + +mlx_status +nodnic_port_set( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + IN mlx_uint32 in + ); + +mlx_status +nodnic_port_create_cq( + IN nodnic_port_priv *port_priv, + IN mlx_size cq_size, + OUT nodnic_cq **cq + ); + +mlx_status +nodnic_port_destroy_cq( + IN nodnic_port_priv *port_priv, + IN nodnic_cq *cq + ); + +mlx_status +nodnic_port_create_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type, + IN mlx_size send_wq_size, + IN mlx_uint32 send_wqe_num, + IN mlx_size receive_wq_size, + IN mlx_uint32 recv_wqe_num, + OUT nodnic_qp **qp + ); + +mlx_status +nodnic_port_destroy_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type, + IN nodnic_qp *qp + ); +mlx_status +nodnic_port_get_qpn( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + OUT mlx_uint32 *qpn + ); +mlx_status +nodnic_port_update_ring_doorbell( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ); +mlx_status +nodnic_port_get_cq_size( + IN nodnic_port_priv *port_priv, + OUT mlx_uint64 *cq_size + ); + +mlx_status +nodnic_port_allocate_eq( + IN nodnic_port_priv *port_priv, + IN mlx_uint8 log_eq_size + ); +mlx_status +nodnic_port_free_eq( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_add_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); + +mlx_status +nodnic_port_remove_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); +mlx_status +nodnic_port_add_mgid_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); + +mlx_status +nodnic_port_remove_mgid_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ); +mlx_status +nodnic_port_thin_init( + IN nodnic_device_priv *device_priv, + IN nodnic_port_priv *port_priv, + IN mlx_uint8 port_index + ); + +mlx_status +nodnic_port_set_promisc( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ); + +mlx_status +nodnic_port_set_promisc_multicast( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ); + +mlx_status +nodnic_port_init( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_close( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_enable_dma( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_disable_dma( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_read_reset_needed( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *reset_needed + ); + +mlx_status +nodnic_port_read_port_management_change_event( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *change_event + ); +mlx_status +nodnic_port_set_send_uar_offset( + IN nodnic_port_priv *port_priv + ); + +mlx_status +nodnic_port_update_tx_db_func( + IN nodnic_device_priv *device_priv, + IN nodnic_port_priv *port_priv + ); +#endif /* STUB_NODNIC_PORT_H_ */ diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c new file mode 100644 index 000000000..69f85358b --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_cmd.c @@ -0,0 +1,77 @@ +/* + * 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 "../include/mlx_cmd.h" +#include "../../mlx_utils/include/public/mlx_pci_gw.h" +#include "../../mlx_utils/include/public/mlx_bail.h" +#include "../../mlx_utils/include/public/mlx_pci.h" +#include "../../mlx_utils/include/public/mlx_logging.h" + +mlx_status +nodnic_cmd_read( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_utils *utils = NULL; + + if ( device_priv == NULL || buffer == NULL ) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + utils = device_priv->utils; + + status = mlx_pci_gw_read(utils, PCI_GW_SPACE_NODNIC, address, buffer); + MLX_CHECK_STATUS(device_priv, status, read_error,"mlx_pci_gw_read failed"); + +read_error: +bad_param: + return status; +} + +mlx_status +nodnic_cmd_write( + IN nodnic_device_priv *device_priv, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_utils *utils = NULL; + + + if ( device_priv == NULL ) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + utils = device_priv->utils; + + + status = mlx_pci_gw_write(utils, PCI_GW_SPACE_NODNIC, address, buffer); + MLX_CHECK_STATUS(device_priv, status, write_error,"mlx_pci_gw_write failed"); +write_error: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c new file mode 100644 index 000000000..65655457c --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_device.c @@ -0,0 +1,363 @@ +/* + * 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 "../include/mlx_device.h" +#include "../include/mlx_cmd.h" +#include "../../mlx_utils/include/public/mlx_bail.h" +#include "../../mlx_utils/include/public/mlx_pci.h" +#include "../../mlx_utils/include/public/mlx_memory.h" +#include "../../mlx_utils/include/public/mlx_logging.h" + +#define CHECK_BIT(field, offset) (((field) & ((mlx_uint32)1 << (offset))) != 0) + +static +mlx_status +check_nodnic_interface_supported( + IN nodnic_device_priv* device_priv, + OUT mlx_boolean *out + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 output = 0; + status = nodnic_cmd_read(device_priv, NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, + &output); + MLX_FATAL_CHECK_STATUS(status, read_error, "failed to read nic_interface_supported"); + *out = CHECK_BIT(output, NODNIC_NIC_INTERFACE_SUPPORTED_BIT); +read_error: + return status; +} + +static +mlx_status +wait_for_device_initialization( + IN nodnic_device_priv* device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 try = 0; + mlx_uint32 buffer = 0; + +#define CHECK_DEVICE_INIT_TRIES 10 + for( ; try < CHECK_DEVICE_INIT_TRIES ; try++){ + status = nodnic_cmd_read(device_priv, NODNIC_INITIALIZING_OFFSET, &buffer); + MLX_CHECK_STATUS(device_priv, status, read_error, "failed to read initializing"); + if( !CHECK_BIT(buffer, NODNIC_INITIALIZING_BIT)){ + goto init_done; + } + mlx_utils_delay_in_ms(100); + } + status = MLX_FAILED; +read_error: +init_done: + return status; +} + +static +mlx_status +disable_nodnic_inteface( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + + buffer = (1 << NODNIC_DISABLE_INTERFACE_BIT); + status = nodnic_cmd_write(device_priv, NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET, buffer); + MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface"); + + status = wait_for_device_initialization(device_priv); + MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device"); +init_err: +write_err: + return status; +} +static +mlx_status +nodnic_device_start_nodnic( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + mlx_boolean nodnic_supported = 0; + + status = wait_for_device_initialization(device_priv); + MLX_FATAL_CHECK_STATUS(status, wait_for_fw_err, "failed to initialize device"); + + status = check_nodnic_interface_supported(device_priv, &nodnic_supported); + MLX_FATAL_CHECK_STATUS(status, read_err,"failed to check nic_interface_supported"); + + if( nodnic_supported == 0 ){ + status = MLX_UNSUPPORTED; + goto nodnic_unsupported; + } + buffer = (1 << NODNIC_NIC_INTERFACE_BIT); + status = nodnic_cmd_write(device_priv, NODNIC_NIC_INTERFACE_OFFSET, buffer); + MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface"); + + status = wait_for_device_initialization(device_priv); + MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device"); +init_err: +read_err: +write_err: +nodnic_unsupported: +wait_for_fw_err: + return status; +} + +static +mlx_status +nodnic_device_get_nodnic_data( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + + status = nodnic_cmd_read(device_priv, NODNIC_LOCATION_OFFSET, &device_priv->device_offset); + MLX_FATAL_CHECK_STATUS(status, nodnic_offset_read_err, "failed to read nodnic offset"); + + status = nodnic_cmd_read(device_priv, + device_priv->device_offset + NODNIC_REVISION_OFFSET, &buffer); + MLX_FATAL_CHECK_STATUS(status, nodnic_revision_read_err, "failed to read nodnic revision"); + + device_priv->nodnic_revision = (buffer >> 24) & 0xFF; + if( device_priv->nodnic_revision != NODIC_SUPPORTED_REVISION ){ + MLX_DEBUG_ERROR(device_priv, "nodnic revision not supported\n"); + status = MLX_UNSUPPORTED; + goto unsupported_revision; + } + + status = nodnic_cmd_read(device_priv, + device_priv->device_offset + NODNIC_HARDWARE_FORMAT_OFFSET, &buffer); + MLX_FATAL_CHECK_STATUS(status, nodnic_hardware_format_read_err, "failed to read nodnic revision"); + device_priv->hardware_format = (buffer >> 16) & 0xFF; + + return status; + +unsupported_revision: +nodnic_hardware_format_read_err: +nodnic_offset_read_err: +nodnic_revision_read_err: + disable_nodnic_inteface(device_priv); + return status; +} + +mlx_status +nodnic_device_clear_int ( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 disable = 1; +#ifndef DEVICE_CX3 + 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; + mlx_uint64 clear_int = (mlx_uintn)(device_priv->crspace_clear_int); + mlx_uint32 swapped = 0; + + if (device_priv->device_cap.crspace_doorbells == 0) { + 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 { + /* Write the new index and update FW that new data was submitted */ + disable = 0x80000000; + mlx_memory_cpu_to_be32(utils, disable, &swapped); + mlx_pci_mem_write (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped); + mlx_pci_mem_read (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped); + } +#endif +clear_int_done: + return status; +} + +mlx_status +nodnic_device_init( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( device_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto parm_err; + } + status = nodnic_device_start_nodnic(device_priv); + MLX_FATAL_CHECK_STATUS(status, start_nodnic_err, "nodnic_device_start_nodnic failed"); + + status = nodnic_device_get_nodnic_data(device_priv); + MLX_FATAL_CHECK_STATUS(status, data_err, "nodnic_device_get_nodnic_data failed"); + return status; +data_err: +start_nodnic_err: +parm_err: + return status; +} + +mlx_status +nodnic_device_teardown( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + status = disable_nodnic_inteface(device_priv); + MLX_FATAL_CHECK_STATUS(status, disable_failed, "failed to disable nodnic interface"); +disable_failed: + return status; +} + +mlx_status +nodnic_device_get_cap( + IN nodnic_device_priv *device_priv + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_capabilites *device_cap = NULL; + mlx_uint32 buffer = 0; + mlx_uint64 guid_l = 0; + mlx_uint64 guid_h = 0; + if( device_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto parm_err; + } + + device_cap = &device_priv->device_cap; + + //get device capabilities + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x0, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic first dword"); + +#define NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET 15 +#define NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET 14 +#define NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET 13 +#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET 8 +#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK 0x7 +#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET 4 +#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK 0xF +#define NODNIC_DEVICE_NUM_PORTS_OFFSET 0 + device_cap->support_mac_filters = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET); + + device_cap->support_promisc_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET); + + device_cap->support_promisc_multicast_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET); + + device_cap->log_working_buffer_size = + (buffer >> NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET) & NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK; + + device_cap->log_pkey_table_size = + (buffer >> NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK; + + device_cap->num_ports = CHECK_BIT(buffer, NODNIC_DEVICE_NUM_PORTS_OFFSET) + 1; + +#ifdef DEVICE_CX3 +#define NODNIC_DEVICE_CRSPACE_DB_OFFSET 12 + device_cap->crspace_doorbells = CHECK_BIT(buffer, NODNIC_DEVICE_CRSPACE_DB_OFFSET); +#endif + + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x4, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic second dword"); + +#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET 24 +#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK 0x3F +#define NODNIC_DEVICE_PD_MASK 0xFFFFFF + device_cap->log_max_ring_size = + (buffer >> NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET) & NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK; + + //get device magic numbers + device_priv->pd = buffer & NODNIC_DEVICE_PD_MASK; + + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x8, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic third dword"); + device_priv->lkey = buffer; + +#ifdef DEVICE_CX3 + if ( device_cap->crspace_doorbells ) { + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x18, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic_crspace_clear_int address"); + device_priv->crspace_clear_int = device_priv->utils->config + buffer; + } +#endif + + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x10, (mlx_uint32*)&guid_h); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_h"); + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x14, (mlx_uint32*)&guid_l); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_l"); + device_priv->device_guid = guid_l | (guid_h << 32); + +#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET 31 +#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_MASK 0x1 +#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET 29 +#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_MASK 0x1 +#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET 27 +#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_MASK 0x1 + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x1c, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic support_rx_pi_dma"); + if ( sizeof ( mlx_uintn ) == sizeof ( mlx_uint32 ) ) { + device_cap->support_rx_pi_dma = FALSE; + device_cap->support_uar_tx_db = FALSE; + device_cap->support_bar_cq_ctrl = FALSE; + } else { + device_cap->support_rx_pi_dma = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET); + device_cap->support_uar_tx_db = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET); + device_cap->support_bar_cq_ctrl = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET); + } + +#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET 0 +#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK 0xFF + status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x20, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic log_uar_page_size"); + device_cap->log_uar_page_size = ( buffer >> NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK; +read_err: +parm_err: + return status; +} + +mlx_status +nodnic_device_get_fw_version( + IN nodnic_device_priv *device_priv, + OUT mlx_uint16 *fw_ver_minor, + OUT mlx_uint16 *fw_ver_sub_minor, + OUT mlx_uint16 *fw_ver_major + ){ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + + if( device_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto parm_err; + } + + status = nodnic_cmd_read(device_priv, 0x0, &buffer); + MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision major and minor"); + + *fw_ver_minor = (mlx_uint16)(buffer >> 16); + *fw_ver_major = (mlx_uint16)buffer; + + status = nodnic_cmd_read(device_priv, 0x4, &buffer); + MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision sub minor"); + + *fw_ver_sub_minor = (mlx_uint16)buffer; +read_err: +parm_err: + return status; +} diff --git a/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c b/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c new file mode 100644 index 000000000..efbd8ddf7 --- /dev/null +++ b/src/drivers/infiniband/mlx_nodnic/src/mlx_port.c @@ -0,0 +1,1370 @@ +/* + * 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 "../include/mlx_port.h" +#include "../include/mlx_cmd.h" +#include "../../mlx_utils/include/public/mlx_memory.h" +#include "../../mlx_utils/include/public/mlx_pci.h" +#include "../../mlx_utils/include/public/mlx_bail.h" + +#define PortDataEntry( _option, _offset, _align, _mask) { \ + .option = _option, \ + .offset = _offset, \ + .align = _align, \ + .mask = _mask, \ + } + +#define QpDataEntry( _type, _send_offset, _recv_offset) { \ + .type = _type, \ + .send_offset = _send_offset, \ + .recv_offset = _recv_offset, \ + } + + +struct nodnic_port_data_entry nodnic_port_data_table[] = { + PortDataEntry(nodnic_port_option_link_type, 0x0, 4, 0x1), + PortDataEntry(nodnic_port_option_mac_low, 0xc, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_mac_high, 0x8, 0, 0xFFFF), + PortDataEntry(nodnic_port_option_log_cq_size, 0x6c, 0, 0x3F), + PortDataEntry(nodnic_port_option_reset_needed, 0x0, 31, 0x1), + PortDataEntry(nodnic_port_option_mac_filters_en, 0x4, 0, 0x1F), + PortDataEntry(nodnic_port_option_port_state, 0x0, 0, 0xF), + PortDataEntry(nodnic_port_option_network_en, 0x4, 31, 0x1), + PortDataEntry(nodnic_port_option_dma_en, 0x4, 30, 0x1), + PortDataEntry(nodnic_port_option_eq_addr_low, 0x74, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_eq_addr_high, 0x70, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_cq_addr_low, 0x6c, 12, 0xFFFFF), + PortDataEntry(nodnic_port_option_cq_addr_high, 0x68, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_port_management_change_event, 0x0, 30, 0x1), + PortDataEntry(nodnic_port_option_port_promisc_en, 0x4, 29, 0x1), +#ifndef DEVICE_CX3 + PortDataEntry(nodnic_port_option_arm_cq, 0x78, 8, 0xffffff), +#else + PortDataEntry(nodnic_port_option_arm_cq, 0x78, 8, 0xffff), +#endif + PortDataEntry(nodnic_port_option_port_promisc_multicast_en, 0x4, 28, 0x1), +#ifdef DEVICE_CX3 + PortDataEntry(nodnic_port_option_crspace_en, 0x4, 27, 0x1), +#endif + PortDataEntry(nodnic_port_option_send_ring0_uar_index, 0x108, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_send_ring1_uar_index, 0x10c, 0, 0xFFFFFFFF), + PortDataEntry(nodnic_port_option_cq_n_index, 0x118, 0, 0xFFFFFF), +}; + +#define MAX_QP_DATA_ENTRIES 5 +struct nodnic_qp_data_entry nodnic_qp_data_teable[MAX_QP_DATA_ENTRIES] = { + QpDataEntry(NODNIC_QPT_SMI, 0, 0), + QpDataEntry(NODNIC_QPT_GSI, 0, 0), + QpDataEntry(NODNIC_QPT_UD, 0, 0), + QpDataEntry(NODNIC_QPT_RC, 0, 0), + QpDataEntry(NODNIC_QPT_ETH, 0x80, 0xC0), +}; + +#define MAX_NODNIC_PORTS 2 +int nodnic_port_offset_table[MAX_NODNIC_PORTS] = { + 0x100, //port 1 offset + 0x280, //port 1 offset +}; + +mlx_status +nodnic_port_get_state( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_state *state + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + + status = nodnic_port_query(port_priv, + nodnic_port_option_port_state, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + *state = (nodnic_port_state)out; +query_err: + return status; +} +mlx_status +nodnic_port_get_type( + IN nodnic_port_priv *port_priv, + OUT nodnic_port_type *type + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + + if ( port_priv->port_type == NODNIC_PORT_TYPE_UNKNOWN){ + status = nodnic_port_query(port_priv, + nodnic_port_option_link_type, &out); + MLX_FATAL_CHECK_STATUS(status, query_err, + "nodnic_port_query failed"); + port_priv->port_type = (nodnic_port_type)out; + } + *type = port_priv->port_type; +query_err: + return status; +} + +mlx_status +nodnic_port_query( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + OUT mlx_uint32 *out + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + struct nodnic_port_data_entry *data_entry; + mlx_uint32 buffer = 0; + if( port_priv == NULL || out == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + device_priv = port_priv->device; + + data_entry = &nodnic_port_data_table[option]; + + status = nodnic_cmd_read(device_priv, + port_priv->port_offset + data_entry->offset , &buffer); + MLX_CHECK_STATUS(device_priv, status, read_err, + "nodnic_cmd_read failed"); + *out = (buffer >> data_entry->align) & data_entry->mask; +read_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_set( + IN nodnic_port_priv *port_priv, + IN nodnic_port_option option, + IN mlx_uint32 in + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + struct nodnic_port_data_entry *data_entry; + mlx_uint32 buffer = 0; + + if( port_priv == NULL ){ + MLX_DEBUG_FATAL_ERROR("port_priv is NULL\n"); + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + device_priv = port_priv->device; + data_entry = &nodnic_port_data_table[option]; + + if( in > data_entry->mask ){ + MLX_DEBUG_FATAL_ERROR("in > data_entry->mask (%d > %d)\n", + in, data_entry->mask); + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + status = nodnic_cmd_read(device_priv, + port_priv->port_offset + data_entry->offset, &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, + "nodnic_cmd_read failed"); + buffer = buffer & ~(data_entry->mask << data_entry->align); + buffer = buffer | (in << data_entry->align); + status = nodnic_cmd_write(device_priv, + port_priv->port_offset + data_entry->offset, buffer); + MLX_FATAL_CHECK_STATUS(status, write_err, + "nodnic_cmd_write failed"); +write_err: +read_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_set_send_uar_offset( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + + if ( ! port_priv->device->device_cap.support_uar_tx_db ) { + MLX_DEBUG_INFO1 ( port_priv, "nodnic_port_set_send_uar_offset: tx db using uar is not supported \n"); + status = MLX_UNSUPPORTED; + goto uar_not_supported; + } + + status = nodnic_port_query(port_priv, + nodnic_port_option_send_ring0_uar_index, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + port_priv->device->uar.offset = out << port_priv->device->device_cap.log_uar_page_size; +uar_not_supported: +query_err: + return status; +} + +mlx_status +nodnic_port_read_reset_needed( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *reset_needed + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + status = nodnic_port_query(port_priv, + nodnic_port_option_reset_needed, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + *reset_needed = (mlx_boolean)out; +query_err: + return status; +} + +mlx_status +nodnic_port_read_port_management_change_event( + IN nodnic_port_priv *port_priv, + OUT mlx_boolean *change_event + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + status = nodnic_port_query(port_priv, + nodnic_port_option_port_management_change_event, &out); + MLX_CHECK_STATUS(port_priv->device, status, query_err, + "nodnic_port_query failed"); + *change_event = (mlx_boolean)out; +query_err: + return status; +} + +static +mlx_status +nodnic_port_allocate_dbr_dma ( + IN nodnic_port_priv *port_priv, + IN struct nodnic_doorbell *nodnic_db, + IN mlx_uint32 dbr_addr_low_ofst, + IN mlx_uint32 dbr_addr_high_ofst, + IN void **dbr_addr, + IN mlx_size size, + IN void **map + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint64 address = 0; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || nodnic_db == NULL ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + status = mlx_memory_alloc_dma(device_priv->utils, + size, + NODNIC_MEMORY_ALIGN, + (void **)dbr_addr + ); + MLX_FATAL_CHECK_STATUS(status, alloc_db_record_err, + "doorbell record dma allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + (void *)(*dbr_addr), + size, + &nodnic_db->doorbell_physical, + map//nodnic_ring->map + ); + MLX_FATAL_CHECK_STATUS(status, map_db_record_err, + "doorbell record map dma error"); + + address = (mlx_uint64)nodnic_db->doorbell_physical; + status = nodnic_cmd_write(device_priv, + dbr_addr_low_ofst, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set doorbell addr low"); + + address = address >> 32; + status = nodnic_cmd_write(device_priv, + dbr_addr_high_ofst, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set doorbell addr high"); + + return status; + +set_err: + mlx_memory_ummap_dma(device_priv->utils, *map); +map_db_record_err: + mlx_memory_free_dma(device_priv->utils, size, + (void **)dbr_addr); +alloc_db_record_err: +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_cq_dbr_dma_init( + IN nodnic_port_priv *port_priv, + OUT nodnic_cq **cq + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + if ( ! device_priv->device_cap.support_bar_cq_ctrl ) { + status = MLX_UNSUPPORTED; + goto uar_arm_cq_db_unsupported; + } + +#define NODNIC_PORT_ARM_CQ_DBR_ADDR_LOW_OFFSET 0x114 +#define NODNIC_PORT_ARM_CQ_DBR_ADDR_HIGH_OFFSET 0x110 + + status = nodnic_port_allocate_dbr_dma ( port_priv,&(*cq)->arm_cq_doorbell, + port_priv->port_offset + NODNIC_PORT_ARM_CQ_DBR_ADDR_LOW_OFFSET, + port_priv->port_offset + NODNIC_PORT_ARM_CQ_DBR_ADDR_HIGH_OFFSET, + (void **)&port_priv->arm_cq_doorbell_record , + sizeof(nodnic_arm_cq_db), + (void **)&((*cq)->arm_cq_doorbell.map)); + MLX_FATAL_CHECK_STATUS(status, alloc_dbr_dma_err, + "failed to allocate doorbell record dma"); + return status; + +alloc_dbr_dma_err: +uar_arm_cq_db_unsupported: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_create_cq( + IN nodnic_port_priv *port_priv, + IN mlx_size cq_size, + OUT nodnic_cq **cq + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + mlx_uint64 address = 0; + if( port_priv == NULL || cq == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + + status = mlx_memory_zalloc(device_priv->utils, + sizeof(nodnic_cq),(mlx_void **)cq); + MLX_FATAL_CHECK_STATUS(status, alloc_err, + "cq priv allocation error"); + + (*cq)->cq_size = cq_size; + status = mlx_memory_alloc_dma(device_priv->utils, + (*cq)->cq_size, NODNIC_MEMORY_ALIGN, + &(*cq)->cq_virt); + MLX_FATAL_CHECK_STATUS(status, dma_alloc_err, + "cq allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + (*cq)->cq_virt, + (*cq)->cq_size, + &(*cq)->cq_physical, + &(*cq)->map); + MLX_FATAL_CHECK_STATUS(status, cq_map_err, + "cq map error"); + + status = nodnic_port_cq_dbr_dma_init(port_priv,cq); + + /* update cq address */ +#define NODIC_CQ_ADDR_HIGH 0x68 +#define NODIC_CQ_ADDR_LOW 0x6c + address = (mlx_uint64)(*cq)->cq_physical; + status = nodnic_port_set(port_priv, nodnic_port_option_cq_addr_low, + (mlx_uint32)(address) >> 12); + MLX_FATAL_CHECK_STATUS(status, dma_set_addr_low_err, + "cq set addr low error"); + address = address >> 32; + status = nodnic_port_set(port_priv, nodnic_port_option_cq_addr_high, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, dma_set_addr_high_err, + "cq set addr high error"); + return status; +dma_set_addr_high_err: +dma_set_addr_low_err: + mlx_memory_ummap_dma(device_priv->utils, (*cq)->map); +cq_map_err: + mlx_memory_free_dma(device_priv->utils, (*cq)->cq_size, + (void **)&((*cq)->cq_virt)); +dma_alloc_err: + mlx_memory_free(device_priv->utils, (void **)cq); +alloc_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_destroy_cq( + IN nodnic_port_priv *port_priv, + IN nodnic_cq *cq + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || cq == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + device_priv = port_priv->device; + + if ( device_priv->device_cap.support_bar_cq_ctrl ){ + status = mlx_memory_ummap_dma(device_priv->utils, + cq->arm_cq_doorbell.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + sizeof(nodnic_arm_cq_db), + (void **)&(port_priv->arm_cq_doorbell_record)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + } + + mlx_memory_ummap_dma(device_priv->utils, cq->map); + + mlx_memory_free_dma(device_priv->utils, cq->cq_size, + (void **)&(cq->cq_virt)); + + mlx_memory_free(device_priv->utils, (void **)&cq); +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_allocate_ring_db_dma ( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *nodnic_ring, + IN struct nodnic_doorbell *nodnic_db + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL || nodnic_ring == NULL || nodnic_db == NULL ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } +#define NODNIC_RING_DBR_ADDR_LOW_OFFSET 0x1C +#define NODNIC_RING_DBR_ADDR_HIGH_OFFSET 0x18 + status = nodnic_port_allocate_dbr_dma ( port_priv,nodnic_db, + nodnic_ring->offset + NODNIC_RING_DBR_ADDR_LOW_OFFSET, + nodnic_ring->offset + NODNIC_RING_DBR_ADDR_HIGH_OFFSET, + (void **)&nodnic_db->qp_doorbell_record, + sizeof(nodnic_qp_db), + (void **)&nodnic_ring->map ); + MLX_FATAL_CHECK_STATUS(status, alloc_dbr_dma_err, + "failed to allocate doorbell record dma"); + + return status; +alloc_dbr_dma_err: +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_rx_pi_dma_alloc( + IN nodnic_port_priv *port_priv, + OUT nodnic_qp **qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || qp == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + + if ( ! device_priv->device_cap.support_rx_pi_dma ) { + goto rx_pi_dma_unsupported; + } + + if ( device_priv->device_cap.support_rx_pi_dma ) { + status = nodnic_port_allocate_ring_db_dma(port_priv, + &(*qp)->receive.nodnic_ring,&(*qp)->receive.nodnic_ring.recv_doorbell); + MLX_FATAL_CHECK_STATUS(status, dma_alloc_err, + "rx doorbell dma allocation error"); + } + + return status; + +dma_alloc_err: +rx_pi_dma_unsupported: +invalid_parm: + return status; +} + +static +mlx_status +nodnic_port_send_db_dma( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ) +{ + mlx_uint32 swapped = 0; + mlx_uint32 index32 = index; + mlx_memory_cpu_to_be32(port_priv->device->utils, index32, &swapped); + ring->send_doorbell.qp_doorbell_record->send_db = swapped; + + return MLX_SUCCESS; +} + +static +mlx_status +nodnic_port_tx_dbr_dma_init( + IN nodnic_port_priv *port_priv, + OUT nodnic_qp **qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL || qp == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + + if ( ! device_priv->device_cap.support_uar_tx_db || ! device_priv->uar.offset ) { + status = MLX_UNSUPPORTED; + goto uar_tx_db_unsupported; + } + status = nodnic_port_allocate_ring_db_dma(port_priv, + &(*qp)->send.nodnic_ring,&(*qp)->send.nodnic_ring.send_doorbell); + MLX_FATAL_CHECK_STATUS(status, dma_alloc_err, + "tx doorbell dma allocation error"); + port_priv->send_doorbell = nodnic_port_send_db_dma; + + return status; + +dma_alloc_err: +uar_tx_db_unsupported: +invalid_parm: + + return status; +} + +mlx_status +nodnic_port_create_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type, + IN mlx_size send_wq_size, + IN mlx_uint32 send_wqe_num, + IN mlx_size receive_wq_size, + IN mlx_uint32 recv_wqe_num, + OUT nodnic_qp **qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + mlx_uint32 max_ring_size = 0; + mlx_uint64 address = 0; + mlx_uint32 log_size = 0; + if( port_priv == NULL || qp == NULL){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + device_priv = port_priv->device; + max_ring_size = (1 << device_priv->device_cap.log_max_ring_size); + if( send_wq_size > max_ring_size || + receive_wq_size > max_ring_size ){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + status = mlx_memory_zalloc(device_priv->utils, + sizeof(nodnic_qp),(mlx_void **)qp); + MLX_FATAL_CHECK_STATUS(status, alloc_err, + "qp allocation error"); + + if( nodnic_qp_data_teable[type].send_offset == 0 || + nodnic_qp_data_teable[type].recv_offset == 0){ + status = MLX_INVALID_PARAMETER; + goto invalid_type; + } + + (*qp)->send.nodnic_ring.offset = port_priv->port_offset + + nodnic_qp_data_teable[type].send_offset; + (*qp)->receive.nodnic_ring.offset = port_priv->port_offset + + nodnic_qp_data_teable[type].recv_offset; + + status = mlx_memory_alloc_dma(device_priv->utils, + send_wq_size, NODNIC_MEMORY_ALIGN, + (void*)&(*qp)->send.wqe_virt); + MLX_FATAL_CHECK_STATUS(status, send_alloc_err, + "send wq allocation error"); + + status = mlx_memory_alloc_dma(device_priv->utils, + receive_wq_size, NODNIC_MEMORY_ALIGN, + &(*qp)->receive.wqe_virt); + MLX_FATAL_CHECK_STATUS(status, receive_alloc_err, + "receive wq allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + (*qp)->send.wqe_virt, + send_wq_size, + &(*qp)->send.nodnic_ring.wqe_physical, + &(*qp)->send.nodnic_ring.map); + MLX_FATAL_CHECK_STATUS(status, send_map_err, + "send wq map error"); + + status = mlx_memory_map_dma(device_priv->utils, + (*qp)->receive.wqe_virt, + receive_wq_size, + &(*qp)->receive.nodnic_ring.wqe_physical, + &(*qp)->receive.nodnic_ring.map); + MLX_FATAL_CHECK_STATUS(status, receive_map_err, + "receive wq map error"); + + status = nodnic_port_rx_pi_dma_alloc(port_priv,qp); + MLX_FATAL_CHECK_STATUS(status, rx_pi_dma_alloc_err, + "receive db dma error"); + + status = nodnic_port_tx_dbr_dma_init(port_priv,qp); + + + (*qp)->send.nodnic_ring.wq_size = send_wq_size; + (*qp)->send.nodnic_ring.num_wqes = send_wqe_num; + (*qp)->receive.nodnic_ring.wq_size = receive_wq_size; + (*qp)->receive.nodnic_ring.num_wqes = recv_wqe_num; + + /* Set Ownership bit in Send/receive queue (0 - recv ; 1 - send) */ + mlx_memory_set(device_priv->utils, (*qp)->send.wqe_virt, 0xff, send_wq_size ); + mlx_memory_set(device_priv->utils, (*qp)->receive.wqe_virt, 0, recv_wqe_num ); + + /* update send ring */ +#define NODIC_RING_QP_ADDR_HIGH 0x0 +#define NODIC_RING_QP_ADDR_LOW 0x4 + address = (mlx_uint64)(*qp)->send.nodnic_ring.wqe_physical; + status = nodnic_cmd_write(device_priv, (*qp)->send.nodnic_ring.offset + + NODIC_RING_QP_ADDR_HIGH, + (mlx_uint32)(address >> 32)); + MLX_FATAL_CHECK_STATUS(status, write_send_addr_err, + "send address write error 1"); + mlx_utils_ilog2((*qp)->send.nodnic_ring.wq_size, &log_size); + address = address | log_size; + status = nodnic_cmd_write(device_priv, (*qp)->send.nodnic_ring.offset + + NODIC_RING_QP_ADDR_LOW, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, write_send_addr_err, + "send address write error 2"); + /* update receive ring */ + address = (mlx_uint64)(*qp)->receive.nodnic_ring.wqe_physical; + status = nodnic_cmd_write(device_priv, (*qp)->receive.nodnic_ring.offset + + NODIC_RING_QP_ADDR_HIGH, + (mlx_uint32)(address >> 32)); + MLX_FATAL_CHECK_STATUS(status, write_recv_addr_err, + "receive address write error 1"); + mlx_utils_ilog2((*qp)->receive.nodnic_ring.wq_size, &log_size); + address = address | log_size; + status = nodnic_cmd_write(device_priv, (*qp)->receive.nodnic_ring.offset + + NODIC_RING_QP_ADDR_LOW, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, write_recv_addr_err, + "receive address write error 2"); + + return status; +write_recv_addr_err: +write_send_addr_err: + mlx_memory_ummap_dma(device_priv->utils, (*qp)->receive.nodnic_ring.map); +rx_pi_dma_alloc_err: +receive_map_err: + mlx_memory_ummap_dma(device_priv->utils, (*qp)->send.nodnic_ring.map); +send_map_err: + mlx_memory_free_dma(device_priv->utils, receive_wq_size, + &((*qp)->receive.wqe_virt)); +receive_alloc_err: + mlx_memory_free_dma(device_priv->utils, send_wq_size, + (void **)&((*qp)->send.wqe_virt)); +send_alloc_err: +invalid_type: + mlx_memory_free(device_priv->utils, (void **)qp); +alloc_err: +invalid_parm: + return status; +} + +mlx_status +nodnic_port_destroy_qp( + IN nodnic_port_priv *port_priv, + IN nodnic_queue_pair_type type __attribute__((unused)), + IN nodnic_qp *qp + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = port_priv->device; + + status = mlx_memory_ummap_dma(device_priv->utils, + qp->receive.nodnic_ring.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_ummap_dma(device_priv->utils, qp->send.nodnic_ring.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + if ( device_priv->device_cap.support_rx_pi_dma ){ + status = mlx_memory_ummap_dma(device_priv->utils, + qp->receive.nodnic_ring.recv_doorbell.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + sizeof(nodnic_qp_db), + (void **)&(qp->receive.nodnic_ring.recv_doorbell.qp_doorbell_record)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + } + + if ( device_priv->device_cap.support_uar_tx_db || ! device_priv->uar.offset){ + status = mlx_memory_ummap_dma(device_priv->utils, + qp->send.nodnic_ring.send_doorbell.map); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_ummap_dma failed (Status = %d)\n", status); + } + + status = mlx_memory_free_dma(device_priv->utils, + sizeof(nodnic_qp_db), + (void **)&(qp->send.nodnic_ring.send_doorbell.qp_doorbell_record)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + } + + status = mlx_memory_free_dma(device_priv->utils, + qp->receive.nodnic_ring.wq_size, + (void **)&(qp->receive.wqe_virt)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + status = mlx_memory_free_dma(device_priv->utils, + qp->send.nodnic_ring.wq_size, + (void **)&(qp->send.wqe_virt)); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free_dma failed (Status = %d)\n", status); + } + status = mlx_memory_free(device_priv->utils, (void **)&qp); + if( status != MLX_SUCCESS){ + MLX_DEBUG_ERROR(device_priv, "mlx_memory_free failed (Status = %d)\n", status); + } + return status; +} + +mlx_status +nodnic_port_get_qpn( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + OUT mlx_uint32 *qpn + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + if( ring == NULL || qpn == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + if( ring->qpn != 0 ){ + *qpn = ring->qpn; + goto success; + } +#define NODNIC_RING_QPN_OFFSET 0xc +#define NODNIC_RING_QPN_MASK 0xFFFFFF + status = nodnic_cmd_read(port_priv->device, + ring->offset + NODNIC_RING_QPN_OFFSET, + &buffer); + MLX_FATAL_CHECK_STATUS(status, read_err, + "nodnic_cmd_read failed"); + ring->qpn = buffer & NODNIC_RING_QPN_MASK; + *qpn = ring->qpn; +read_err: +success: +bad_param: + return status; +} + +#ifdef DEVICE_CX3 +static +mlx_status +nodnic_port_send_db_connectx3( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring __attribute__((unused)), + IN mlx_uint16 index + ) +{ + nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; + mlx_uint32 index32 = index; + mlx_pci_mem_write(port_priv->device->utils, MlxPciWidthUint32, 0, + (mlx_uintn)&(ptr->send_doorbell), 1, &index32); + return MLX_SUCCESS; +} + +static +mlx_status +nodnic_port_recv_db_connectx3( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring __attribute__((unused)), + IN mlx_uint16 index + ) +{ + nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; + mlx_uint32 index32 = index; + mlx_pci_mem_write(port_priv->device->utils, MlxPciWidthUint32, 0, + (mlx_uintn)&(ptr->recv_doorbell), 1, &index32); + return MLX_SUCCESS; +} +#endif +static +mlx_status +nodnic_port_recv_db_dma( + IN nodnic_port_priv *port_priv __attribute__((unused)), + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ) +{ + mlx_uint32 swapped = 0; + mlx_uint32 index32 = index; + mlx_memory_cpu_to_be32(port_priv->device->utils, index32, &swapped); + ring->recv_doorbell.qp_doorbell_record->recv_db = swapped; + return MLX_SUCCESS; +} + +mlx_status +nodnic_port_update_ring_doorbell( + IN nodnic_port_priv *port_priv, + IN struct nodnic_ring *ring, + IN mlx_uint16 index + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = 0; + if( ring == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } +#define NODNIC_RING_RING_OFFSET 0x8 + buffer = (mlx_uint32)((index & 0xFFFF)<< 8); + status = nodnic_cmd_write(port_priv->device, + ring->offset + NODNIC_RING_RING_OFFSET, + buffer); + MLX_CHECK_STATUS(port_priv->device, status, write_err, + "nodnic_cmd_write failed"); +write_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_get_cq_size( + IN nodnic_port_priv *port_priv, + OUT mlx_uint64 *cq_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 out = 0; + status = nodnic_port_query(port_priv, nodnic_port_option_log_cq_size, &out); + MLX_FATAL_CHECK_STATUS(status, query_err, + "nodnic_port_query failed"); + *cq_size = 1 << out; +query_err: + return status; +} + +mlx_status +nodnic_port_allocate_eq( + IN nodnic_port_priv *port_priv, + IN mlx_uint8 log_eq_size + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + mlx_uint64 address = 0; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + device_priv = port_priv->device; + port_priv->eq.eq_size = ( ( 1 << log_eq_size ) * 1024 ); /* Size is in KB */ + status = mlx_memory_alloc_dma(device_priv->utils, + port_priv->eq.eq_size, + NODNIC_MEMORY_ALIGN, + &port_priv->eq.eq_virt); + MLX_FATAL_CHECK_STATUS(status, alloc_err, + "eq allocation error"); + + status = mlx_memory_map_dma(device_priv->utils, + port_priv->eq.eq_virt, + port_priv->eq.eq_size, + &port_priv->eq.eq_physical, + &port_priv->eq.map); + MLX_FATAL_CHECK_STATUS(status, map_err, + "eq map error"); + + address = port_priv->eq.eq_physical; + status = nodnic_port_set(port_priv, nodnic_port_option_eq_addr_low, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set eq addr low"); + address = (address >> 32); + status = nodnic_port_set(port_priv, nodnic_port_option_eq_addr_high, + (mlx_uint32)address); + MLX_FATAL_CHECK_STATUS(status, set_err, + "failed to set eq addr high"); + return status; +set_err: + mlx_memory_ummap_dma(device_priv->utils, port_priv->eq.map); +map_err: + mlx_memory_free_dma(device_priv->utils, + port_priv->eq.eq_size, + (void **)&(port_priv->eq.eq_virt)); +alloc_err: +bad_param: + return status; +} +mlx_status +nodnic_port_free_eq( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device_priv = NULL; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + device_priv = port_priv->device; + mlx_memory_ummap_dma(device_priv->utils, port_priv->eq.map); + + mlx_memory_free_dma(device_priv->utils, + port_priv->eq.eq_size, + (void **)&(port_priv->eq.eq_virt)); + +bad_param: + return status; +} + +mlx_status +nodnic_port_add_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device= NULL;; + mlx_uint8 index = 0; + mlx_uint32 out = 0; + mlx_uint32 mac_filters_en = 0; + mlx_uint32 address = 0; + mlx_mac_address zero_mac; + mlx_utils *utils = NULL; + + if( port_priv == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + device = port_priv->device; + utils = device->utils; + + mlx_memory_set(utils, &zero_mac, 0, sizeof(zero_mac)); + /* check if mac already exists */ + for( ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { + mlx_memory_cmp(utils, &port_priv->mac_filters[index], &mac, + sizeof(mac), &out); + if ( out == 0 ){ + status = MLX_FAILED; + goto already_exists; + } + } + + /* serch for available mac filter slot */ + for (index = 0 ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { + mlx_memory_cmp(utils, &port_priv->mac_filters[index], &zero_mac, + sizeof(zero_mac), &out); + if ( out == 0 ){ + break; + } + } + if ( index >= NODNIC_MAX_MAC_FILTERS ){ + status = MLX_FAILED; + goto mac_list_full; + } + + status = nodnic_port_query(port_priv, nodnic_port_option_mac_filters_en, + &mac_filters_en); + MLX_CHECK_STATUS(device, status , query_err, + "nodnic_port_query failed"); + if(mac_filters_en & (1 << index)){ + status = MLX_FAILED; + goto mac_list_full; + } + port_priv->mac_filters[index] = mac; + + // set mac filter + address = port_priv->port_offset + NODNIC_PORT_MAC_FILTERS_OFFSET + + (0x8 * index); + + status = nodnic_cmd_write(device, address, mac.high ); + MLX_CHECK_STATUS(device, status, write_err, "set mac high failed"); + status = nodnic_cmd_write(device, address + 0x4, mac.low ); + MLX_CHECK_STATUS(device, status, write_err, "set mac low failed"); + + // enable mac filter + mac_filters_en = mac_filters_en | (1 << index); + status = nodnic_port_set(port_priv, nodnic_port_option_mac_filters_en, + mac_filters_en); + MLX_CHECK_STATUS(device, status , set_err, + "nodnic_port_set failed"); +set_err: +write_err: +query_err: +mac_list_full: +already_exists: +bad_param: + return status; +} + +mlx_status +nodnic_port_remove_mac_filter( + IN nodnic_port_priv *port_priv, + IN mlx_mac_address mac + ) +{ + mlx_status status = MLX_SUCCESS; + nodnic_device_priv *device= NULL;; + mlx_uint8 index = 0; + mlx_uint32 out = 0; + mlx_uint32 mac_filters_en = 0; + mlx_mac_address zero_mac; + mlx_utils *utils = NULL; + + if( port_priv == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + device = port_priv->device; + utils = device->utils; + + mlx_memory_set(utils, &zero_mac, 0, sizeof(zero_mac)); + /* serch for mac filter */ + for( ; index < NODNIC_MAX_MAC_FILTERS ; index ++) { + mlx_memory_cmp(utils, &port_priv->mac_filters[index], &mac, + sizeof(mac), &out); + if ( out == 0 ){ + break; + } + } + if ( index == NODNIC_MAX_MAC_FILTERS ){ + status = MLX_FAILED; + goto mac_not_found; + } + + status = nodnic_port_query(port_priv, nodnic_port_option_mac_filters_en, + &mac_filters_en); + MLX_CHECK_STATUS(device, status , query_err, + "nodnic_port_query failed"); + if((mac_filters_en & (1 << index)) == 0){ + status = MLX_FAILED; + goto mac_not_en; + } + port_priv->mac_filters[index] = zero_mac; + + // disable mac filter + mac_filters_en = mac_filters_en & ~(1 << index); + status = nodnic_port_set(port_priv, nodnic_port_option_mac_filters_en, + mac_filters_en); + MLX_CHECK_STATUS(device, status , set_err, + "nodnic_port_set failed"); +set_err: +query_err: +mac_not_en: +mac_not_found: +bad_param: + return status; +} + +static +mlx_status +nodnic_port_set_network( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + mlx_status status = MLX_SUCCESS; + /*mlx_uint32 network_valid = 0; + mlx_uint8 try = 0;*/ + + status = nodnic_port_set(port_priv, nodnic_port_option_network_en, value); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); + port_priv->network_state = value; +set_err: + return status; +} + +#ifdef DEVICE_CX3 +static +mlx_status +nodnic_port_set_dma_connectx3( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + mlx_utils *utils = port_priv->device->utils; + nodnic_port_data_flow_gw *ptr = port_priv->data_flow_gw; + mlx_uint32 data = (value ? 0xffffffff : 0x0); + mlx_pci_mem_write(utils, MlxPciWidthUint32, 0, + (mlx_uintn)&(ptr->dma_en), 1, &data); + return MLX_SUCCESS; +} +#endif + +static +mlx_status +nodnic_port_set_dma( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + return nodnic_port_set(port_priv, nodnic_port_option_dma_en, value); +} + +static +mlx_status +nodnic_port_check_and_set_dma( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ) +{ + mlx_status status = MLX_SUCCESS; + if ( port_priv->dma_state == value ) { + MLX_DEBUG_WARN(port_priv->device, + "nodnic_port_check_and_set_dma: already %s\n", + (value ? "enabled" : "disabled")); + status = MLX_SUCCESS; + goto set_out; + } + + status = port_priv->set_dma(port_priv, value); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); + port_priv->dma_state = value; +set_err: +set_out: + return status; +} + + +mlx_status +nodnic_port_set_promisc( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ){ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = value; + + status = nodnic_port_set(port_priv, nodnic_port_option_port_promisc_en, buffer); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); +set_err: + return status; +} + +mlx_status +nodnic_port_set_promisc_multicast( + IN nodnic_port_priv *port_priv, + IN mlx_boolean value + ){ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer = value; + + status = nodnic_port_set(port_priv, nodnic_port_option_port_promisc_multicast_en, buffer); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_set failed"); +set_err: + return status; +} + +mlx_status +nodnic_port_init( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_set_network(port_priv, TRUE); + MLX_FATAL_CHECK_STATUS(status, set_err, + "nodnic_port_set_network failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_close( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_set_network(port_priv, FALSE); + MLX_FATAL_CHECK_STATUS(status, set_err, + "nodnic_port_set_network failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_enable_dma( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_check_and_set_dma(port_priv, TRUE); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_check_and_set_dma failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_disable_dma( + IN nodnic_port_priv *port_priv + ) +{ + mlx_status status = MLX_SUCCESS; + + if( port_priv == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nodnic_port_check_and_set_dma(port_priv, FALSE); + MLX_CHECK_STATUS(port_priv->device, status, set_err, + "nodnic_port_check_and_set_dma failed"); +set_err: +bad_param: + return status; +} + +mlx_status +nodnic_port_thin_init( + IN nodnic_device_priv *device_priv, + IN nodnic_port_priv *port_priv, + IN mlx_uint8 port_index + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_boolean reset_needed = 0; +#ifdef DEVICE_CX3 + mlx_uint32 offset; +#endif + + if( device_priv == NULL || port_priv == NULL || port_index > 1){ + status = MLX_INVALID_PARAMETER; + goto invalid_parm; + } + + port_priv->device = device_priv; + + port_priv->port_offset = device_priv->device_offset + + nodnic_port_offset_table[port_index]; + + port_priv->port_num = port_index + 1; + + port_priv->send_doorbell = nodnic_port_update_ring_doorbell; + port_priv->recv_doorbell = nodnic_port_update_ring_doorbell; + port_priv->set_dma = nodnic_port_set_dma; +#ifdef DEVICE_CX3 + if (device_priv->device_cap.crspace_doorbells) { + status = nodnic_cmd_read(device_priv, (port_priv->port_offset + 0x100), + &offset); + if (status != MLX_SUCCESS) { + return status; + } else { + port_priv->data_flow_gw = (nodnic_port_data_flow_gw *) + (device_priv->utils->config + offset); + } + if ( nodnic_port_set ( port_priv, nodnic_port_option_crspace_en, 1 ) ) { + return MLX_FAILED; + } + port_priv->send_doorbell = nodnic_port_send_db_connectx3; + port_priv->recv_doorbell = nodnic_port_recv_db_connectx3; + port_priv->set_dma = nodnic_port_set_dma_connectx3; + } +#endif + if ( device_priv->device_cap.support_rx_pi_dma ) { + port_priv->recv_doorbell = nodnic_port_recv_db_dma; + } + + /* clear reset_needed */ + nodnic_port_read_reset_needed(port_priv, &reset_needed); + + port_priv->port_type = NODNIC_PORT_TYPE_UNKNOWN; +invalid_parm: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h new file mode 100644 index 000000000..1f8ba89ea --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_memory_priv.h @@ -0,0 +1,113 @@ +#ifndef MLXUTILS_INCLUDE_PRIVATE_MEMORYPRIV_H_ +#define MLXUTILS_INCLUDE_PRIVATE_MEMORYPRIV_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/include/public/mlx_utils.h" + +mlx_status +mlx_memory_alloc_priv( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_zalloc_priv( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free_priv( + IN mlx_utils *utils, + IN mlx_void *ptr + ); +mlx_status +mlx_memory_alloc_dma_priv( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free_dma_priv( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_void *ptr + ); +mlx_status +mlx_memory_map_dma_priv( + IN mlx_utils *utils, + IN mlx_void *addr , + IN mlx_size number_of_bytes, + OUT mlx_physical_address *phys_addr, + OUT mlx_void **mapping + ); + +mlx_status +mlx_memory_ummap_dma_priv( + IN mlx_utils *utils, + IN mlx_void *mapping + ); + +mlx_status +mlx_memory_cmp_priv( + IN mlx_utils *utils, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ); + +mlx_status +mlx_memory_set_priv( + IN mlx_utils *utils, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ); + +mlx_status +mlx_memory_cpy_priv( + IN mlx_utils *utils, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ); + +mlx_status +mlx_memory_cpu_to_be32_priv( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); + +mlx_status +mlx_memory_be32_to_cpu_priv( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); +#endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_MEMORYPRIV_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h new file mode 100644 index 000000000..cf35e5b73 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_pci_priv.h @@ -0,0 +1,77 @@ +#ifndef STUB_MLXUTILS_INCLUDE_PRIVATE_PCIPRIV_H_ +#define STUB_MLXUTILS_INCLUDE_PRIVATE_PCIPRIV_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 "../../include/public/mlx_pci.h" +#include "../../include/public/mlx_utils.h" + +mlx_status +mlx_pci_init_priv( + IN mlx_utils *utils + ); + +mlx_status +mlx_pci_teardown_priv( + IN mlx_utils *utils + ); + +mlx_status +mlx_pci_read_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_write_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_read_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_write_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + + +#endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_PCIPRIV_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h b/src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h new file mode 100644 index 000000000..268b76fad --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/private/mlx_utils_priv.h @@ -0,0 +1,68 @@ +#ifndef SRC_DRIVERS_INFINIBAND_MLX_UTILS_INCLUDE_PRIVATE_MLX_UTILS_PRIV_H_ +#define SRC_DRIVERS_INFINIBAND_MLX_UTILS_INCLUDE_PRIVATE_MLX_UTILS_PRIV_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 "../../include/public/mlx_utils.h" + +mlx_status +mlx_utils_delay_in_ms_priv( + IN mlx_uint32 msecs + ); + +mlx_status +mlx_utils_delay_in_us_priv( + IN mlx_uint32 usecs + ); + +mlx_status +mlx_utils_ilog2_priv( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ); + +mlx_status +mlx_utils_init_lock_priv( + OUT void **lock + ); + +mlx_status +mlx_utils_free_lock_priv( + IN void *lock + ); + +mlx_status +mlx_utils_acquire_lock_priv ( + IN void *lock + ); + +mlx_status +mlx_utils_release_lock_priv ( + IN void *lock + ); + +mlx_status +mlx_utils_rand_priv ( + IN mlx_utils *utils, + OUT mlx_uint32 *rand_num + ); +#endif /* SRC_DRIVERS_INFINIBAND_MLX_UTILS_INCLUDE_PRIVATE_MLX_UTILS_PRIV_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h new file mode 100644 index 000000000..a4f4b37b1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_bail.h @@ -0,0 +1,47 @@ +#ifndef INCLUDE_PUBLIC_MLXBAIL_H_ +#define INCLUDE_PUBLIC_MLXBAIL_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_types.h" + +#define MLX_BAIL_ERROR(id, status,message) MLX_CHECK_STATUS(id, status, bail, message) + +#define MLX_FATAL_CHECK_STATUS(status, label, message) \ + do { \ + if (status != MLX_SUCCESS) { \ + MLX_DEBUG_FATAL_ERROR(message " (Status = %d)\n", status); \ + goto label; \ + } \ + } while (0) + +#define MLX_CHECK_STATUS(id, status, label, message) \ + do { \ + if (status != MLX_SUCCESS) { \ + MLX_DEBUG_ERROR(id, message " (Status = %d)\n", status);\ + goto label; \ + } \ + } while (0) + + + +#endif /* INCLUDE_PUBLIC_MLXBAIL_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h new file mode 100644 index 000000000..1ed423daf --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_icmd.h @@ -0,0 +1,63 @@ +#ifndef MLXUTILS_INCLUDE_PUBLIC_MLX_ICMD_H_ +#define MLXUTILS_INCLUDE_PUBLIC_MLX_ICMD_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_ICMD_MB_ADDR 0x100000 +#define MLX_ICMD_MB_SIZE_ADDR 0x1000 +#define MLX_ICMD_CTRL_ADDR 0x0 + +#define MLX_ICMD_SEMAPHORE_ADDR 0x0 + +#define MLX_ICMD_SEMAPHORE_ID 1234 + +enum { + FLASH_REG_ACCESS = 0x9001, + GET_FW_INFO = 0x8007, + QUERY_VIRTUAL_MAC = 0x9003, + SET_VIRTUAL_MAC = 0x9004, + QUERY_WOL_ROL = 0x9005, + SET_WOL_ROL = 0x9006, + OCBB_INIT = 0x9007, + OCBB_QUERY_HEADER_STATUS = 0x9008, + OCBB_QUERY_ETOC_STATUS = 0x9009, + OCBB_QUERY_SET_EVENT = 0x900A, + OCSD_INIT = 0xf004, +}; + +struct mlx_icmd_ocsd { + mlx_uint32 reserved; + mlx_uint64 address; +}; + +mlx_status +mlx_icmd_send_command( + IN mlx_utils *utils, + IN mlx_uint16 opcode, + IN OUT mlx_void* data, + IN mlx_uint32 write_data_size, + IN mlx_uint32 read_data_size + ); + +#endif /* MLXUTILS_INCLUDE_PUBLIC_MLX_ICMD_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h new file mode 100644 index 000000000..7ff06bbf5 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_logging.h @@ -0,0 +1,47 @@ +#ifndef PUBLIC_INCLUDE_MLX_LOGGER_H_ +#define PUBLIC_INCLUDE_MLX_LOGGER_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_flexboot/include/mlx_logging_priv.h" + +#define MLX_PRINT(...) MLX_PRINT_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_FATAL_ERROR(...) MLX_DEBUG_FATAL_ERROR_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_ERROR(...) MLX_DEBUG_ERROR_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_WARN(...) MLX_DEBUG_WARN_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_INFO1(...) MLX_DEBUG_INFO1_PRIVATE(__VA_ARGS__) +#define MLX_DEBUG_INFO2(...) MLX_DEBUG_INFO2_PRIVATE(__VA_ARGS__) +#define MLX_DBG_ERROR(...) MLX_DBG_ERROR_PRIVATE(__VA_ARGS__) +#define MLX_DBG_WARN(...) MLX_DBG_WARN_PRIVATE(__VA_ARGS__) +#define MLX_DBG_INFO1(...) MLX_DBG_INFO1_PRIVATE(__VA_ARGS__) +#define MLX_DBG_INFO2(...) MLX_DBG_INFO2_PRIVATE(__VA_ARGS__) + +#define MLX_TRACE_1_START() MLX_DBG_INFO1_PRIVATE("Start\n") +#define MLX_TRACE_1_END() MLX_DBG_INFO1_PRIVATE("End\n") +#define MLX_TRACE_1_END_STATUS(status) MLX_DBG_INFO1_PRIVATE("End (%s=%d)\n", #status,status) +#define MLX_TRACE_2_START() MLX_DBG_INFO2_PRIVATE("Start\n") +#define MLX_TRACE_2_END() MLX_DBG_INFO2_PRIVATE("End\n") +#define MLX_TRACE_2_END_STATUS(status) MLX_DBG_INFO2_PRIVATE("End (%s=%d)\n", #status,status) + + + +#endif /* PUBLIC_INCLUDE_MLX_LOGGER_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h new file mode 100644 index 000000000..056756666 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_memory.h @@ -0,0 +1,115 @@ +#ifndef MLXUTILS_INCLUDE_PUBLIC_MEMORY_H_ +#define MLXUTILS_INCLUDE_PUBLIC_MEMORY_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" + + +mlx_status +mlx_memory_alloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_zalloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free( + IN mlx_utils *utils, + IN mlx_void **ptr + ); +mlx_status +mlx_memory_alloc_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ); + +mlx_status +mlx_memory_free_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_void **ptr + ); +mlx_status +mlx_memory_map_dma( + IN mlx_utils *utils, + IN mlx_void *Addr , + IN mlx_size NumberOfBytes, + OUT mlx_physical_address *PhysAddr, + OUT mlx_void **Mapping + ); + +mlx_status +mlx_memory_ummap_dma( + IN mlx_utils *utils, + IN mlx_void *Mapping + ); + +mlx_status +mlx_memory_cmp( + IN mlx_utils *utils, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ); + +mlx_status +mlx_memory_set( + IN mlx_utils *utils, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ); + +mlx_status +mlx_memory_cpy( + IN mlx_utils *utils, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ); + +mlx_status +mlx_memory_cpu_to_be32( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); + +mlx_status +mlx_memory_be32_to_cpu( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ); + +#endif /* STUB_MLXUTILS_INCLUDE_PUBLIC_MEMORY_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h new file mode 100644 index 000000000..60eb55d52 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci.h @@ -0,0 +1,83 @@ +#ifndef STUB_MLXUTILS_INCLUDE_PUBLIC_PCI_H_ +#define STUB_MLXUTILS_INCLUDE_PUBLIC_PCI_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 { + MlxPciWidthUint8 = 0, + MlxPciWidthUint16, + MlxPciWidthUint32, + MlxPciWidthUint64, +} mlx_pci_width; + +mlx_status +mlx_pci_init( + IN mlx_utils *utils + ); + +mlx_status +mlx_pci_teardown( + IN mlx_utils *utils + ); + +mlx_status +mlx_pci_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ); + +mlx_status +mlx_pci_mem_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ); + + +#endif /* STUB_MLXUTILS_INCLUDE_PUBLIC_PCI_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h new file mode 100644 index 000000000..c074a22e5 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_pci_gw.h @@ -0,0 +1,81 @@ +#ifndef INCLUDE_PUBLIC_MLX_PCI_GW_H_ +#define INCLUDE_PUBLIC_MLX_PCI_GW_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 PCI_GW_FIRST_CAPABILITY_POINTER_OFFSET 0x34 + +#define PCI_GW_CAPABILITY_ID 0x9 + +#define PCI_GW_CAPABILITY_ID_OFFSET 0x0 +#define PCI_GW_CAPABILITY_NEXT_POINTER_OFFSET 0x1 +#define PCI_GW_CAPABILITY_SPACE_OFFSET 0x4 +#define PCI_GW_CAPABILITY_STATUS_OFFSET 0x7 +#define PCI_GW_CAPABILITY_COUNTER_OFFSET 0x8 +#define PCI_GW_CAPABILITY_SEMAPHORE_OFFSET 0xC +#define PCI_GW_CAPABILITY_ADDRESS_OFFSET 0x10 +#define PCI_GW_CAPABILITY_FLAG_OFFSET 0x10 +#define PCI_GW_CAPABILITY_DATA_OFFSET 0x14 + +#define PCI_GW_SEMPHORE_TRIES 3000000 +#define PCI_GW_GET_OWNERSHIP_TRIES 5000 +#define PCI_GW_READ_FLAG_TRIES 3000000 + +#define PCI_GW_WRITE_FLAG 0x80000000 + +#define PCI_GW_SPACE_NODNIC 0x4 +#define PCI_GW_SPACE_ALL_ICMD 0x3 +#define PCI_GW_SPACE_SEMAPHORE 0xa +#define PCI_GW_SPACE_CR0 0x2 + +typedef mlx_uint32 mlx_pci_gw_buffer; + + +mlx_status +mlx_pci_gw_init( + IN mlx_utils *utils + ); +mlx_status +mlx_pci_gw_teardown( + IN mlx_utils *utils + ); +mlx_status +mlx_pci_gw_read( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ); + +mlx_status +mlx_pci_gw_write( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ); + + + +#endif /* INCLUDE_PUBLIC_MLX_PCI_GW_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_types.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_types.h new file mode 100644 index 000000000..9c66567a3 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_types.h @@ -0,0 +1,27 @@ +#ifndef INCLUDE_PUBLIC_MLXTYPES_H_ +#define INCLUDE_PUBLIC_MLXTYPES_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_flexboot/include/mlx_types_priv.h" + +#endif /* INCLUDE_PUBLIC_MLXBAIL_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h b/src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h new file mode 100644 index 000000000..46ad97c3b --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/include/public/mlx_utils.h @@ -0,0 +1,106 @@ +#ifndef MLXUTILS_INCLUDE_PUBLIC_MLXUTILS_H_ +#define MLXUTILS_INCLUDE_PUBLIC_MLXUTILS_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_logging.h" +#include "mlx_types.h" + +#define IN +#define OUT + +typedef mlx_uint16 mlx_pci_gw_space; + +typedef struct{ + mlx_uint32 pci_cmd_offset; + mlx_pci_gw_space space; +} __attribute__ (( packed )) mlx_pci_gw; + +typedef struct { + mlx_boolean icmd_opened; + mlx_boolean took_semaphore; + mlx_uint32 max_cmd_size; +} __attribute__ (( packed )) mlx_icmd ; + +typedef struct{ + mlx_pci *pci; + mlx_pci_gw pci_gw; + mlx_icmd icmd; + void *lock; +#ifdef DEVICE_CX3 + /* ACCESS to BAR0 */ + void *config; +#endif +} __attribute__ (( packed )) mlx_utils; + +mlx_status +mlx_utils_init( + IN mlx_utils *utils, + IN mlx_pci *pci + ); + +mlx_status +mlx_utils_teardown( + IN mlx_utils *utils + ); +mlx_status +mlx_utils_delay_in_ms( + IN mlx_uint32 msecs + ); + +mlx_status +mlx_utils_delay_in_us( + IN mlx_uint32 usecs + ); + +mlx_status +mlx_utils_ilog2( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ); + +mlx_status +mlx_utils_init_lock( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_free_lock( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_acquire_lock ( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_release_lock ( + IN OUT mlx_utils *utils + ); + +mlx_status +mlx_utils_rand ( + IN mlx_utils *utils, + OUT mlx_uint32 *rand_num + ); +#endif /* STUB_MLXUTILS_INCLUDE_PUBLIC_MLXUTILS_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c new file mode 100644 index 000000000..ba56e72f2 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.c @@ -0,0 +1,54 @@ +/* + * 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_lib/mlx_blink_leds/mlx_blink_leds.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_blink_leds( + IN mlx_utils *utils, + IN mlx_uint16 secs + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_led_control led_control; + mlx_uint32 reg_status; + + if (utils == NULL ) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + mlx_memory_set(utils, &led_control, 0, sizeof(led_control)); + led_control.beacon_duration = secs; + status = mlx_reg_access(utils, REG_ID_MLCR, REG_ACCESS_WRITE, &led_control, sizeof(led_control), + ®_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; +} + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h new file mode 100644 index 000000000..886645fe2 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_blink_leds/mlx_blink_leds.h @@ -0,0 +1,46 @@ +#ifndef MLX_BLINK_LEDS_H_ +#define MLX_BLINK_LEDS_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_lib/mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_utils.h" + +struct mlx_led_control { + mlx_uint32 reserved1 :16; + mlx_uint32 port :8; + mlx_uint32 bla :8; +/* -------------- */ + mlx_uint32 beacon_duration :16; + mlx_uint32 reserved2 :16; +/* -------------- */ + mlx_uint32 beacon_remain :16; + mlx_uint32 reserved3 :16; +}; + +mlx_status +mlx_blink_leds( + IN mlx_utils *utils, + IN mlx_uint16 secs + ); + +#endif /* MLX_NVCONFIG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c new file mode 100644 index 000000000..d31553024 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.c @@ -0,0 +1,180 @@ +/* + * 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_lib/mlx_link_speed/mlx_link_speed.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_set_link_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + IN LINK_SPEED speed + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_link_speed link_speed; + mlx_uint32 reg_status; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &link_speed, 0, sizeof(link_speed)); + + link_speed.loacl_port = port_num; + link_speed.proto_mask = 1 << type; + + status = mlx_reg_access(utils, REG_ID_PTYS, REG_ACCESS_READ, &link_speed, + sizeof(link_speed), ®_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; + } + switch (speed) { + case LINK_SPEED_1GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_1GB_MASK; + break; + case LINK_SPEED_10GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_10GB_MASK; + break; + case LINK_SPEED_40GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_40GB_MASK; + break; + case LINK_SPEED_100GB: + link_speed.eth_proto_admin = link_speed.eth_proto_capability & LINK_SPEED_100GB_MASK; + break; + case LINK_SPEED_SDR: + link_speed.ib_proto_admin = link_speed.ib_proto_capability & LINK_SPEED_SDR_MASK; + break; + case LINK_SPEED_DEFAULT: + if (type == LINK_SPEED_ETH) { + link_speed.eth_proto_admin = link_speed.eth_proto_capability; + } else { + link_speed.ib_proto_admin = link_speed.ib_proto_capability; + } + break; + } + status = mlx_reg_access(utils, REG_ID_PTYS, REG_ACCESS_WRITE, &link_speed, + sizeof(link_speed), ®_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; +} + +mlx_status +mlx_get_max_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + OUT mlx_uint64 *speed + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_link_speed link_speed; + mlx_uint32 reg_status; + mlx_uint64 speed_giga = 0; + mlx_uint8 lanes_number = 1; + + *speed = 0; + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &link_speed, 0, sizeof(link_speed)); + + link_speed.loacl_port = port_num; + link_speed.proto_mask = 1 << type; + + status = mlx_reg_access(utils, REG_ID_PTYS, REG_ACCESS_READ, &link_speed, + sizeof(link_speed), ®_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; + } + + if ( type == LINK_SPEED_ETH ) { + if ( link_speed.eth_proto_capability & LINK_SPEED_100GB_MASK ) { + speed_giga = 100; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_56GB_MASK ) { + speed_giga = 56; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_50GB_MASK ) { + speed_giga = 50; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_40GB_MASK ) { + speed_giga = 40; + } else if (link_speed.eth_proto_capability & LINK_SPEED_25GB_MASK) { + speed_giga = 25; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_20GB_MASK ) { + speed_giga = 20; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_10GB_MASK) { + speed_giga = 10; + } else if ( link_speed.eth_proto_capability & LINK_SPEED_1GB_MASK ) { + speed_giga = 1; + } + } else { + if ( link_speed.ib_proto_capability & LINK_SPEED_EDR_MASK ) { + speed_giga = 25; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_EDR20_MASK ) { + speed_giga = 20; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_FDR_MASK ) { + speed_giga = 14; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_QDR_MASK ) { + speed_giga = 10; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_DDR_MASK ) { + speed_giga = 5; + } else if ( link_speed.ib_proto_capability & LINK_SPEED_SDR_MASK ) { + speed_giga = 2.5; + } + if ( link_speed.ib_link_width_capability & LINK_SPEED_WITDH_12_MASK ) { + lanes_number = 12; + } else if ( link_speed.ib_link_width_capability & LINK_SPEED_WITDH_8_MASK ) { + lanes_number = 8; + } else if (link_speed.ib_link_width_capability & LINK_SPEED_WITDH_4_MASK ) { + lanes_number = 4; + } else if (link_speed.ib_link_width_capability & LINK_SPEED_WITDH_2_MASK ) { + lanes_number = 2; + } else if (link_speed.ib_link_width_capability & LINK_SPEED_WITDH_1_MASK ) { + lanes_number = 1; + } + speed_giga = speed_giga * lanes_number; + } + // Return data in bits + *speed = speed_giga * GIGA_TO_BIT; +reg_err: +bad_param: + return status; +} + + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h new file mode 100644 index 000000000..cb167d6ae --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_link_speed/mlx_link_speed.h @@ -0,0 +1,150 @@ +#ifndef MLX_LINK_SPEED_H_ +#define MLX_LINK_SPEED_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_lib/mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_utils.h" + +#define LINK_SPEED_100GB_MASK (ETH_SPEED_ENABLE_MASK_100GBASECR4 | ETH_SPEED_ENABLE_MASK_100GBASESR4 | ETH_SPEED_ENABLE_MASK_100GBASEKR4 | ETH_SPEED_ENABLE_MASK_100GBASELR4) +#define LINK_SPEED_56GB_MASK (ETH_SPEED_ENABLE_MASK_56GBASER4) +#define LINK_SPEED_50GB_MASK (ETH_SPEED_ENABLE_MASK_50GBASECR2 | ETH_SPEED_ENABLE_MASK_50GBASEKR2) +#define LINK_SPEED_40GB_MASK (ETH_SPEED_ENABLE_MASK_40GBASECR4 | ETH_SPEED_ENABLE_MASK_40GBASEKR4 | ETH_SPEED_ENABLE_MASK_40GBASESR4 | ETH_SPEED_ENABLE_MASK_40GBASELR4) +#define LINK_SPEED_25GB_MASK (ETH_SPEED_ENABLE_MASK_25GBASECR | ETH_SPEED_ENABLE_MASK_25GBASEKR | ETH_SPEED_ENABLE_MASK_25GBASESR) +#define LINK_SPEED_20GB_MASK (ETH_SPEED_ENABLE_MASK_20GBASER2) +#define LINK_SPEED_10GB_MASK (ETH_SPEED_ENABLE_MASK_10GBASECR | ETH_SPEED_ENABLE_MASK_10GBASESR | ETH_SPEED_ENABLE_MASK_10GBASELR | ETH_SPEED_ENABLE_MASK_10GBASEKR) +#define LINK_SPEED_1GB_MASK (ETH_SPEED_ENABLE_MASK_1000BASECX | ETH_SPEED_ENABLE_MASK_1000BASEKX | ETH_SPEED_ENABLE_MASK_100BaseTX | ETH_SPEED_ENABLE_MASK_1000BASET) + +#define LINK_SPEED_SDR_MASK 0x1 +#define LINK_SPEED_DDR_MASK 0x2 +#define LINK_SPEED_QDR_MASK 0xC +#define LINK_SPEED_FDR_MASK 0x10 +#define LINK_SPEED_EDR20_MASK 0x200 +#define LINK_SPEED_EDR_MASK 0x20 + +#define LINK_SPEED_WITDH_1_MASK 0x1 +#define LINK_SPEED_WITDH_2_MASK 0x2 +#define LINK_SPEED_WITDH_4_MASK 0x4 +#define LINK_SPEED_WITDH_8_MASK 0x8 +#define LINK_SPEED_WITDH_12_MASK 0x10 + +#define GIGA_TO_BIT 0x40000000 + +enum { + ETH_SPEED_ENABLE_MASK_1000BASECX = 0x0001, + ETH_SPEED_ENABLE_MASK_1000BASEKX = 0x0002, + ETH_SPEED_ENABLE_MASK_10GBASECX4 = 0x0004, + ETH_SPEED_ENABLE_MASK_10GBASEKX4 = 0x0008, + ETH_SPEED_ENABLE_MASK_10GBASEKR = 0x0010, + ETH_SPEED_ENABLE_MASK_20GBASER2 = 0x0020, + ETH_SPEED_ENABLE_MASK_40GBASECR4 = 0x0040, + ETH_SPEED_ENABLE_MASK_40GBASEKR4 = 0x0080, + ETH_SPEED_ENABLE_MASK_56GBASER4 = 0x0100, + ETH_SPEED_ENABLE_MASK_10GBASECR = 0x1000, + ETH_SPEED_ENABLE_MASK_10GBASESR = 0x2000, + ETH_SPEED_ENABLE_MASK_10GBASELR = 0x4000, + ETH_SPEED_ENABLE_MASK_40GBASESR4 = 0x8000, + ETH_SPEED_ENABLE_MASK_40GBASELR4 = 0x10000, + ETH_SPEED_ENABLE_MASK_50GBASEKR4 = 0x80000, + ETH_SPEED_ENABLE_MASK_100GBASECR4 = 0x100000, + ETH_SPEED_ENABLE_MASK_100GBASESR4 = 0x200000, + ETH_SPEED_ENABLE_MASK_100GBASEKR4 = 0x400000, + ETH_SPEED_ENABLE_MASK_100GBASELR4 = 0x800000, + ETH_SPEED_ENABLE_MASK_100BaseTX = 0x1000000, + ETH_SPEED_ENABLE_MASK_1000BASET = 0x2000000, + ETH_SPEED_ENABLE_MASK_10GBASET = 0x4000000, + ETH_SPEED_ENABLE_MASK_25GBASECR = 0x8000000, + ETH_SPEED_ENABLE_MASK_25GBASEKR = 0x10000000, + ETH_SPEED_ENABLE_MASK_25GBASESR = 0x20000000, + ETH_SPEED_ENABLE_MASK_50GBASECR2 = 0x40000000, + ETH_SPEED_ENABLE_MASK_50GBASEKR2 = 0x80000000, + ETH_SPEED_ENABLE_MASK_BAD = 0xffff, +}; + + +typedef enum { + LINK_SPEED_IB = 0, + LINK_SPEED_FC, + LINK_SPEED_ETH, +} LINK_SPEED_TYPE; + +typedef enum { + LINK_SPEED_1GB = 0, + LINK_SPEED_10GB, + LINK_SPEED_40GB, + LINK_SPEED_100GB, + LINK_SPEED_SDR, + LINK_SPEED_DEFAULT, +} LINK_SPEED; + +struct mlx_link_speed { + mlx_uint32 proto_mask :3; + mlx_uint32 reserved1 :13; + mlx_uint32 loacl_port :8; + mlx_uint32 reserved2 :8; + /* -------------- */ + mlx_uint32 reserved3 :32; + /* -------------- */ + mlx_uint32 reserved4 :32; + /* -------------- */ + mlx_uint32 eth_proto_capability :32; + /* -------------- */ + mlx_uint32 ib_proto_capability :16; + mlx_uint32 ib_link_width_capability :16; + /* -------------- */ + mlx_uint32 reserved5 :32; + /* -------------- */ + mlx_uint32 eth_proto_admin :32; + /* -------------- */ + mlx_uint32 ib_proto_admin :16; + mlx_uint32 ib_link_width_admin :16; + /* -------------- */ + mlx_uint32 reserved6 :32; + /* -------------- */ + mlx_uint32 eth_proto_oper :32; + /* -------------- */ + mlx_uint32 ib_proto_oper :16; + mlx_uint32 ib_link_width_oper :16; + /* -------------- */ + mlx_uint32 reserved7 :32; + /* -------------- */ + mlx_uint32 eth_proto_lp_advertise :32; + mlx_uint32 reserved[3]; +}; + +mlx_status +mlx_set_link_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + IN LINK_SPEED speed + ); + +mlx_status +mlx_get_max_speed( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + IN LINK_SPEED_TYPE type, + OUT mlx_uint64 *speed + ); + +#endif /* MLX_LINK_SPEED_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c new file mode 100644 index 000000000..f0af1ecfa --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.c @@ -0,0 +1,94 @@ +/* + * 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_mtu.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_get_max_mtu( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + OUT mlx_uint32 *max_mtu + ) +{ + mlx_status status = MLX_SUCCESS; + struct mlx_mtu mtu; + mlx_uint32 reg_status; + *max_mtu = 0; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &mtu, 0, sizeof(mtu)); + + mtu.local_port = port_num; + + status = mlx_reg_access(utils, REG_ID_PMTU, REG_ACCESS_READ, &mtu, + sizeof(mtu), ®_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; + } + // Return data in bits + *max_mtu = mtu.max_mtu * BYTE_TO_BIT; +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), ®_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; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h new file mode 100644 index 000000000..bd3ded3f1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h @@ -0,0 +1,58 @@ +#ifndef MLX_MTU_H_ +#define MLX_MTU_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 "../../include/public/mlx_utils.h" +#include "../../mlx_lib/mlx_reg_access/mlx_reg_access.h" + +#define BYTE_TO_BIT 0x8 + +struct mlx_mtu { + mlx_uint32 reserved1 :16; + mlx_uint32 local_port :8; + mlx_uint32 reserved2 :8; + /* -------------- */ + mlx_uint32 reserved3 :16; + mlx_uint32 max_mtu :16; + /* -------------- */ + mlx_uint32 reserved4 :16; + mlx_uint32 admin_mtu :16; + /* -------------- */ + mlx_uint32 reserved5 :16; + mlx_uint32 oper_mtu :16; +}; + +mlx_status +mlx_get_max_mtu( + IN mlx_utils *utils, + IN mlx_uint8 port_num, + 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_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c new file mode 100644 index 000000000..028ba5ce6 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.c @@ -0,0 +1,302 @@ +/* + * 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_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" + +#define TlvMappingEntry( _tlv_type, _real_tlv_type, _class_code, _fw_reset_needed) { \ + .tlv_type = _tlv_type, \ + .real_tlv_type = _real_tlv_type, \ + .class_code = _class_code, \ + .fw_reset_needed = _fw_reset_needed, \ + } + +struct nvconfig_tlv_mapping nvconfig_tlv_mapping[] = { + TlvMappingEntry(0x10, 0x10, NVRAM_TLV_CLASS_HOST, TRUE), + TlvMappingEntry(0x12, 0x12, NVRAM_TLV_CLASS_PHYSICAL_PORT, TRUE), + TlvMappingEntry(0x80, 0x80, NVRAM_TLV_CLASS_GLOBAL, TRUE), + TlvMappingEntry(0x81, 0x81, NVRAM_TLV_CLASS_GLOBAL, TRUE), + TlvMappingEntry(0x100, 0x100, NVRAM_TLV_CLASS_GLOBAL, TRUE), + 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(0x2021, 0x221, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2023, 0x223, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2006, 0x206, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2100, 0x230, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2101, 0x231, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2102, 0x232, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2103, 0x233, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2104, 0x234, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2105, 0x235, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2106, 0x236, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2107, 0x237, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2108, 0x238, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2109, 0x239, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x210A, 0x23A, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2022, 0x222, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2200, 0x240, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2201, 0x241, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2202, 0x242, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2203, 0x243, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2204, 0x244, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2205, 0x245, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2207, 0x247, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2002, 0x202, NVRAM_TLV_CLASS_HOST, FALSE), + TlvMappingEntry(0x2004, 0x204, NVRAM_TLV_CLASS_HOST, FALSE), + 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), +}; + +static +mlx_status +nvconfig_set_fw_reset_level( + IN mlx_utils *utils, + IN mlx_uint16 tlv_type + ) +{ +#define WARM_REBOOT_RESET ((mlx_uint64)0x1 << 38) + mlx_status status = MLX_SUCCESS; + mlx_uint32 reg_status; + mlx_uint64 mfrl = WARM_REBOOT_RESET ; + mlx_uint8 index = 0; + mlx_boolean reset_needed = FALSE; + + for (index = 0 ; nvconfig_tlv_mapping[index].tlv_type != 0 ; index++) { + if (nvconfig_tlv_mapping[index].tlv_type == tlv_type) { + reset_needed = nvconfig_tlv_mapping[index].fw_reset_needed; + } + } + + if (reset_needed == FALSE) { + goto no_fw_reset_needed; + } + status = mlx_reg_access(utils, REG_ID_MFRL, REG_ACCESS_WRITE, &mfrl, sizeof(mfrl), + ®_status); + MLX_CHECK_STATUS(utils, status, reg_err, "mlx_reg_access failed "); + + if (reg_status != 0) { + MLX_DEBUG_ERROR(utils,"nvconfig_set_fw_reset_level failed with status = %d\n", reg_status); + status = MLX_FAILED; + goto reg_err; + } +reg_err: +no_fw_reset_needed: + return status; +} + + +static +mlx_status +nvconfig_get_tlv_type_and_class( + IN mlx_uint16 tlv_type, + OUT mlx_uint16 *real_tlv_type, + OUT NVRAM_CLASS_CODE *class_code + ) +{ + mlx_uint8 index = 0; + for ( ; nvconfig_tlv_mapping[index].tlv_type != 0 ; index ++) { + if ( nvconfig_tlv_mapping[index].tlv_type == tlv_type) { + *real_tlv_type = nvconfig_tlv_mapping[index].real_tlv_type; + *class_code = nvconfig_tlv_mapping[index].class_code; + return MLX_SUCCESS; + } + } + return MLX_NOT_FOUND; +} +static +void +nvconfig_fill_tlv_type( + IN mlx_uint8 port, + IN NVRAM_CLASS_CODE class_code, + IN mlx_uint16 tlv_type, + OUT union nvconfig_tlv_type *nvconfig_tlv_type + ) +{ + switch (class_code) { + case NVRAM_TLV_CLASS_GLOBAL: + nvconfig_tlv_type->global.param_class = NVRAM_TLV_CLASS_GLOBAL; + nvconfig_tlv_type->global.param_idx = tlv_type; + break; + case NVRAM_TLV_CLASS_HOST: + nvconfig_tlv_type->per_host.param_class = NVRAM_TLV_CLASS_HOST; + nvconfig_tlv_type->per_host.param_idx = tlv_type; + break; + case NVRAM_TLV_CLASS_PHYSICAL_PORT: + nvconfig_tlv_type->per_port.param_class = NVRAM_TLV_CLASS_PHYSICAL_PORT; + nvconfig_tlv_type->per_port.param_idx = tlv_type; + nvconfig_tlv_type->per_port.port = port; + break; + } +} +mlx_status +nvconfig_query_capability( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + OUT mlx_boolean *read_supported, + OUT mlx_boolean *write_supported + ) +{ + mlx_status status = MLX_SUCCESS; + struct nvconfig_nvqc nvqc; + mlx_uint32 reg_status; + NVRAM_CLASS_CODE class_code; + mlx_uint16 real_tlv_type; + + if (utils == NULL || read_supported == NULL || write_supported == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nvconfig_get_tlv_type_and_class(tlv_type, &real_tlv_type, &class_code); + MLX_CHECK_STATUS(utils, status, tlv_not_supported, "tlv not supported"); + + mlx_memory_set(utils, &nvqc, 0, sizeof(nvqc)); + nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nvqc.tlv_type); + + status = mlx_reg_access(utils, REG_ID_NVQC, REG_ACCESS_READ, &nvqc, sizeof(nvqc), + ®_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; + } + *read_supported = nvqc.support_rd; + *write_supported = nvqc.support_wr; +reg_err: +tlv_not_supported: +bad_param: + return status; +} + +mlx_status +nvconfig_nvdata_invalidate( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type + ) +{ + mlx_status status = MLX_SUCCESS; + struct nvconfig_header nv_header; + mlx_uint32 reg_status; + NVRAM_CLASS_CODE class_code; + mlx_uint16 real_tlv_type; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nvconfig_get_tlv_type_and_class(tlv_type, &real_tlv_type, &class_code); + MLX_CHECK_STATUS(utils, status, tlv_not_supported, "tlv not supported"); + + mlx_memory_set(utils, &nv_header, 0, sizeof(nv_header)); + nvconfig_fill_tlv_type(port, class_code, real_tlv_type, &nv_header.tlv_type); + + status = mlx_reg_access(utils, REG_ID_NVDI, REG_ACCESS_WRITE, &nv_header, sizeof(nv_header), + ®_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: +tlv_not_supported: +bad_param: + return status; +} + +mlx_status +nvconfig_nvdata_access( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + 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 + ) +{ + mlx_status status = MLX_SUCCESS; + struct nvconfig_nvda nvda; + mlx_uint32 reg_status; + mlx_uint32 real_size_to_read; + mlx_uint32 index; + NVRAM_CLASS_CODE class_code; + mlx_uint16 real_tlv_type; + mlx_size data_size_align_to_dword; + + if (utils == NULL || data == NULL || data_size > NVCONFIG_MAX_TLV_SIZE) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = nvconfig_get_tlv_type_and_class(tlv_type, &real_tlv_type, &class_code); + MLX_CHECK_STATUS(utils, status, tlv_not_supported, "tlv not supported"); + + 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.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); + + mlx_memory_cpy(utils, nvda.data, data, data_size); + for (index = 0 ; index * 4 < NVCONFIG_MAX_TLV_SIZE ; index++) { + mlx_memory_be32_to_cpu(utils,(((mlx_uint32 *)nvda.data)[index]), ((mlx_uint32 *)nvda.data) + index); + } + status = mlx_reg_access(utils, REG_ID_NVDA, opt, &nvda, + data_size_align_to_dword + sizeof(nvda.nv_header), ®_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; + } + for (index = 0 ; index * 4 < NVCONFIG_MAX_TLV_SIZE ; index++) { + mlx_memory_cpu_to_be32(utils,(((mlx_uint32 *)nvda.data)[index]), ((mlx_uint32 *)nvda.data) + index); + } + if (opt == REG_ACCESS_READ) { + real_size_to_read = (nvda.nv_header.length > data_size) ? data_size : + nvda.nv_header.length; + mlx_memory_cpy(utils, data, nvda.data, real_size_to_read); + *version = nvda.nv_header.version; + } else { + nvconfig_set_fw_reset_level(utils, tlv_type); + } +reg_err: +tlv_not_supported: +bad_param: + return status; +} + + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h new file mode 100644 index 000000000..3058c781a --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h @@ -0,0 +1,166 @@ +#ifndef MLX_NVCONFIG_H_ +#define MLX_NVCONFIG_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_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_utils.h" + +typedef enum { + NVRAM_TLV_CLASS_GLOBAL = 0, + NVRAM_TLV_CLASS_PHYSICAL_PORT = 1, + 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; + mlx_uint32 param_class :8; +}; + +struct nvconfig_tlv_type_per_host { + mlx_uint32 param_idx :10; + mlx_uint32 function :8; + mlx_uint32 host :6; + mlx_uint32 param_class :8; +}; + +struct nvconfig_tlv_type_global { + mlx_uint32 param_idx :24; + mlx_uint32 param_class :8; +}; + +struct nvconfig_tlv_mapping{ + mlx_uint16 tlv_type; + mlx_uint16 real_tlv_type; + NVRAM_CLASS_CODE class_code; + mlx_boolean fw_reset_needed; +}; + +union nvconfig_tlv_type { + struct nvconfig_tlv_type_per_port per_port; + struct nvconfig_tlv_type_per_host per_host; + struct nvconfig_tlv_type_global global; +}; + + +struct nvconfig_nvqc { + union nvconfig_tlv_type tlv_type; +/* -------------- */ + mlx_uint32 support_rd :1; /*the configuration item is supported and can be read */ + mlx_uint32 support_wr :1; /*the configuration item is supported and can be updated */ + mlx_uint32 reserved1 :2; + mlx_uint32 version :4; /*The maximum version of the configuration item currently supported by the firmware. */ + mlx_uint32 reserved2 :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 writer_id :5; + mlx_uint32 reserved1 :1; + + 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 reserved2 :2; + mlx_uint32 header_type :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 + +struct nvconfig_nvda { + struct nvconfig_header nv_header; + mlx_uint8 data[NVCONFIG_MAX_TLV_SIZE]; +}; + +struct nv_conf_cap { + /** WOL En/Dis **/ + mlx_uint8 wol_en; + /** VPI En/Dis **/ + mlx_uint8 vpi_en; +}; + +struct mlx_nvconfig_virt_net_addr { + mlx_uint32 reserved1 :29; + mlx_uint32 erase_on_powerup:1; + mlx_uint32 reserverd2 :1; + mlx_uint32 virtual_mac_en :1; + mlx_uint32 virtual_mac_high; + mlx_uint32 virtual_mac_low; +}; + + +mlx_status +nvconfig_query_capability( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + OUT mlx_boolean *read_supported, + OUT mlx_boolean *write_supported + ); + + +mlx_status +nvconfig_nvdata_invalidate( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type + ); + +mlx_status +nvconfig_nvdata_access( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + 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 + ); + +#endif /* MLX_NVCONFIG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c new file mode 100644 index 000000000..ca5a65914 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.c @@ -0,0 +1,519 @@ +/* + * 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_lib/mlx_nvconfig/mlx_nvconfig.h" +#include "../../include/public/mlx_memory.h" +#include "../../include/public/mlx_bail.h" +#include "../../mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" + +struct tlv_default { + mlx_uint16 tlv_type; + mlx_size data_size; + mlx_status (*set_defaults)( IN void *data, IN int status, + OUT void *def_struct); +}; + +#define TlvDefaultEntry( _tlv_type, _data_size, _set_defaults) { \ + .tlv_type = _tlv_type, \ + .data_size = sizeof ( _data_size ), \ + .set_defaults = _set_defaults, \ + } + +static +mlx_status +nvconfig_get_boot_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_nic_boot_conf *nic_boot_conf = + (union mlx_nvconfig_nic_boot_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + /* boot_option_rom_en is deprecated - enabled always */ + port_conf_def->boot_option_rom_en = DEFAULT_OPTION_ROM_EN; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + port_conf_def->boot_vlan = nic_boot_conf->vlan_id; + port_conf_def->boot_protocol = nic_boot_conf->legacy_boot_prot; + port_conf_def->boot_retry_count = nic_boot_conf->boot_retry_count; + port_conf_def->boot_vlan_en = nic_boot_conf->en_vlan; + + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->boot_vlan = DEFAULT_BOOT_VLAN; + port_conf_def->boot_protocol = DEFAULT_BOOT_PROTOCOL; + + return status; +} + +static +mlx_status +nvconfig_get_boot_ext_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_nic_boot_ext_conf *nic_boot_ext_conf = + (union mlx_nvconfig_nic_boot_ext_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + port_conf_def->linkup_timeout = nic_boot_ext_conf->linkup_timeout; + port_conf_def->ip_ver = nic_boot_ext_conf->ip_ver; + port_conf_def->undi_network_wait_to = nic_boot_ext_conf->undi_network_wait_to; + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->linkup_timeout = DEFAULT_BOOT_LINK_UP_TO; + port_conf_def->ip_ver = DEFAULT_BOOT_IP_VER; + port_conf_def->undi_network_wait_to = DEFAULT_BOOT_UNDI_NETWORK_WAIT_TO; + return status; +} + +static +mlx_status +nvconfig_get_iscsi_init_dhcp_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_iscsi_init_dhcp_conf *iscsi_init_dhcp_conf = + (union mlx_nvconfig_iscsi_init_dhcp_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + port_conf_def->iscsi_dhcp_params_en = iscsi_init_dhcp_conf->dhcp_iscsi_en; + port_conf_def->iscsi_ipv4_dhcp_en = iscsi_init_dhcp_conf->ipv4_dhcp_en; + + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->iscsi_dhcp_params_en = DEFAULT_ISCSI_DHCP_PARAM_EN; + port_conf_def->iscsi_ipv4_dhcp_en = DEFAULT_ISCSI_IPV4_DHCP_EN; + + return status; +} + +static +mlx_status +nvconfig_get_ib_boot_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_nic_ib_boot_conf *ib_boot_conf = + (union mlx_nvconfig_nic_ib_boot_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->boot_pkey = ib_boot_conf->boot_pkey; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_wol_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_wol_conf *wol_conf = (union mlx_nvconfig_wol_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->en_wol_magic = wol_conf->en_wol_magic; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_iscsi_gen_default_conf( + IN void *data, + IN int status, + OUT void *def_struct) +{ + union mlx_nvconfig_iscsi_general *iscsi_gen = + (union mlx_nvconfig_iscsi_general *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->iscsi_boot_to_target = iscsi_gen->boot_to_target; + port_conf_def->iscsi_vlan_en = iscsi_gen->vlan_en; + port_conf_def->iscsi_tcp_timestamps_en = iscsi_gen->tcp_timestamps_en; + port_conf_def->iscsi_chap_mutual_auth_en = iscsi_gen->chap_mutual_auth_en; + port_conf_def->iscsi_chap_auth_en = iscsi_gen->chap_auth_en; + port_conf_def->iscsi_lun_busy_retry_count = iscsi_gen->lun_busy_retry_count; + port_conf_def->iscsi_link_up_delay_time = iscsi_gen->link_up_delay_time; + port_conf_def->iscsi_drive_num = iscsi_gen->drive_num; + + return MLX_SUCCESS; + +nvdata_access_err: + port_conf_def->iscsi_drive_num = DEFAULT_ISCSI_DRIVE_NUM; + return status; +} + +static +mlx_status +nvconfig_get_ib_dhcp_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_ib_dhcp_conf *ib_dhcp = + (union mlx_nvconfig_ib_dhcp_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->client_identifier = ib_dhcp->client_identifier; + port_conf_def->mac_admin_bit = ib_dhcp->mac_admin_bit; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_ocsd_ocbb_default_conf( IN void *data, + IN int status, OUT void *def_struct) { + union mlx_nvconfig_ocsd_ocbb_conf *ocsd_ocbb = + (union mlx_nvconfig_ocsd_ocbb_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->ocsd_ocbb_en = ocsd_ocbb->ocsd_ocbb_en; + + return MLX_SUCCESS; + +nvdata_access_err: + conf_def->ocsd_ocbb_en = DEFAULT_OCSD_OCBB_EN; + + return status; +} + +static +mlx_status +nvconfig_get_vpi_link_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_vpi_link_conf *vpi_link = + (union mlx_nvconfig_vpi_link_conf *) data; + struct mlx_nvconfig_port_conf_defaults *port_conf_def = + (struct mlx_nvconfig_port_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + port_conf_def->network_link_type = vpi_link->network_link_type; + port_conf_def->default_link_type = vpi_link->default_link_type; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_rom_banner_to_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_rom_banner_timeout_conf *rom_banner_timeout_conf = + (union mlx_nvconfig_rom_banner_timeout_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->flexboot_menu_to = rom_banner_timeout_conf->rom_banner_to; + + return MLX_SUCCESS; + +nvdata_access_err: + conf_def->flexboot_menu_to = DEFAULT_FLEXBOOT_MENU_TO; + + return status; +} + +static +mlx_status +nvconfig_get_nv_virt_caps_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_virt_caps *nv_virt_caps = + (union mlx_nvconfig_virt_caps *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->max_vfs = nv_virt_caps->max_vfs_per_pf; + + return MLX_SUCCESS; + +nvdata_access_err: + conf_def->max_vfs = DEFAULT_MAX_VFS; + + return status; +} + +static +mlx_status +nvconfig_get_nv_virt_default_conf( + IN void *data, + IN int status, + OUT void *def_struct + ) +{ + union mlx_nvconfig_virt_conf *nv_virt_conf = + (union mlx_nvconfig_virt_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "nvconfig_nvdata_default_access failed "); + conf_def->total_vfs = nv_virt_conf->num_of_vfs; + conf_def->sriov_en = nv_virt_conf->virt_mode; + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_get_rom_cap_default_conf( IN void *data, + IN int status, OUT void *def_struct) { + union mlx_nvconfig_rom_cap_conf *rom_cap_conf = + (union mlx_nvconfig_rom_cap_conf *) data; + struct mlx_nvconfig_conf_defaults *conf_def = + (struct mlx_nvconfig_conf_defaults *) def_struct; + + MLX_CHECK_STATUS(NULL, status, nvdata_access_err, + "TLV not found. Using hard-coded defaults "); + conf_def->boot_ip_ver_en = rom_cap_conf->boot_ip_ver_en; + + return MLX_SUCCESS; + +nvdata_access_err: + rom_cap_conf->boot_ip_ver_en = DEFAULT_BOOT_IP_VERSION_EN; + + return status; +} + +static struct tlv_default tlv_port_defaults[] = { + TlvDefaultEntry(BOOT_SETTINGS_TYPE, union mlx_nvconfig_nic_boot_conf, &nvconfig_get_boot_default_conf), + TlvDefaultEntry(BOOT_SETTINGS_EXT_TYPE, union mlx_nvconfig_nic_boot_ext_conf, &nvconfig_get_boot_ext_default_conf), + TlvDefaultEntry(ISCSI_INITIATOR_DHCP_CONF_TYPE, union mlx_nvconfig_iscsi_init_dhcp_conf, &nvconfig_get_iscsi_init_dhcp_default_conf), + TlvDefaultEntry(IB_BOOT_SETTING_TYPE, union mlx_nvconfig_nic_ib_boot_conf, &nvconfig_get_ib_boot_default_conf), + TlvDefaultEntry(WAKE_ON_LAN_TYPE, union mlx_nvconfig_wol_conf, &nvconfig_get_wol_default_conf), + TlvDefaultEntry(ISCSI_GENERAL_SETTINGS_TYPE, union mlx_nvconfig_iscsi_general, &nvconfig_get_iscsi_gen_default_conf), + TlvDefaultEntry(IB_DHCP_SETTINGS_TYPE, union mlx_nvconfig_ib_dhcp_conf, &nvconfig_get_ib_dhcp_default_conf), + TlvDefaultEntry(VPI_LINK_TYPE, union mlx_nvconfig_vpi_link_conf, &nvconfig_get_vpi_link_default_conf), +}; + +static struct tlv_default tlv_general_defaults[] = { + TlvDefaultEntry(BANNER_TO_TYPE, union mlx_nvconfig_rom_banner_timeout_conf, &nvconfig_get_rom_banner_to_default_conf), + TlvDefaultEntry(GLOPAL_PCI_CAPS_TYPE, union mlx_nvconfig_virt_caps, &nvconfig_get_nv_virt_caps_default_conf), + TlvDefaultEntry(GLOPAL_PCI_SETTINGS_TYPE, union mlx_nvconfig_virt_conf, &nvconfig_get_nv_virt_default_conf), + TlvDefaultEntry(OCSD_OCBB_TYPE, union mlx_nvconfig_ocsd_ocbb_conf, &nvconfig_get_ocsd_ocbb_default_conf), + TlvDefaultEntry(NV_ROM_CAP_TYPE, union mlx_nvconfig_rom_cap_conf, &nvconfig_get_rom_cap_default_conf), +}; + +static +mlx_status +nvconfig_nvdata_default_access( + IN mlx_utils *utils, + IN mlx_uint8 port, + IN mlx_uint16 tlv_type, + IN mlx_size data_size, + OUT mlx_void *data + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 index; + mlx_uint8 version = 0; + + status = nvconfig_nvdata_access(utils, port, tlv_type, REG_ACCESS_READ, + 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++) { + mlx_memory_be32_to_cpu(utils, (((mlx_uint32 *) data)[index]), + ((mlx_uint32 *) data) + index); + } + +nvdata_access_err: + return status; +} + +static +mlx_status +nvconfig_nvdata_read_default_value( + IN mlx_utils *utils, + IN mlx_uint8 modifier, + IN struct tlv_default *def, + OUT void *def_struct + ) +{ + mlx_status status = MLX_SUCCESS; + void *data = NULL; + + status = mlx_memory_zalloc(utils, def->data_size,&data); + MLX_CHECK_STATUS(utils, status, memory_err, + "mlx_memory_zalloc failed "); + status = nvconfig_nvdata_default_access(utils, modifier, def->tlv_type, + def->data_size, data); + def->set_defaults(data, status, def_struct); + mlx_memory_free(utils, &data); + +memory_err: + return status; +} + +static +void +nvconfig_nvdata_read_default_values( + IN mlx_utils *utils, + IN mlx_uint8 modifier, + IN struct tlv_default defaults_table[], + IN mlx_uint8 defaults_table_size, + OUT void *def_strct + ) +{ + struct tlv_default *defs; + unsigned int i; + + for (i = 0; i < defaults_table_size; i++) { + defs = &defaults_table[i]; + nvconfig_nvdata_read_default_value(utils, modifier, defs, def_strct); + } +} + +mlx_status +nvconfig_read_port_default_values( + IN mlx_utils *utils, + IN mlx_uint8 port, + OUT struct mlx_nvconfig_port_conf_defaults *port_conf_def + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL || port_conf_def == NULL) { + status = MLX_INVALID_PARAMETER; + MLX_DEBUG_ERROR(utils,"bad params."); + goto bad_param; + } + mlx_memory_set(utils, port_conf_def, 0, sizeof(*port_conf_def)); + nvconfig_nvdata_read_default_values(utils, port, tlv_port_defaults, + (sizeof(tlv_port_defaults)/sizeof(tlv_port_defaults[0])), + port_conf_def); + +bad_param: + return status; +} + +mlx_status +nvconfig_read_general_default_values( + IN mlx_utils *utils, + OUT struct mlx_nvconfig_conf_defaults *conf_def + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL || conf_def == NULL) { + status = MLX_INVALID_PARAMETER; + MLX_DEBUG_ERROR(utils,"bad params."); + goto bad_param; + } + mlx_memory_set(utils, conf_def, 0, sizeof(*conf_def)); + nvconfig_nvdata_read_default_values(utils, 0, tlv_general_defaults, + (sizeof(tlv_general_defaults)/sizeof(tlv_general_defaults[0])), + conf_def); + +bad_param: + return status; +} + +mlx_status +nvconfig_read_rom_ini_values( + IN mlx_utils *utils, + OUT struct mlx_nvcofnig_romini *rom_ini + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 version = 0; + mlx_uint32 index; + + if (utils == NULL || rom_ini == NULL) { + status = MLX_INVALID_PARAMETER; + MLX_DEBUG_ERROR(utils,"bad params."); + goto bad_param; + } + mlx_memory_set(utils, rom_ini, 0, sizeof(*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; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h new file mode 100644 index 000000000..48699c358 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h @@ -0,0 +1,100 @@ +#ifndef MLX_NVCONFIG_DEFAULTS_H_ +#define MLX_NVCONFIG_DEFAULTS_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_nvconfig_prm.h" +/* + * Default values + */ +#define DEFAULT_FLEXBOOT_MENU_TO 4 +#define DEFAULT_MAX_VFS 8 +#define DEFAULT_BOOT_PROTOCOL 1 +#define DEFAULT_OPTION_ROM_EN 1 +#define DEFAULT_BOOT_VLAN 1 +#define DEFAULT_ISCSI_DHCP_PARAM_EN 1 +#define DEFAULT_ISCSI_IPV4_DHCP_EN 1 +#define DEFAULT_ISCSI_DRIVE_NUM 0x80 +#define DEFAULT_OCSD_OCBB_EN 1 +#define DEFAULT_BOOT_IP_VER 0 +#define DEFAULT_BOOT_LINK_UP_TO 0 +#define DEFAULT_BOOT_UNDI_NETWORK_WAIT_TO 30 +#define DEFAULT_BOOT_IP_VERSION_EN 1 + +struct mlx_nvconfig_port_conf_defaults { + mlx_uint8 pptx; + mlx_uint8 pprx; + mlx_boolean boot_option_rom_en; + mlx_boolean boot_vlan_en; + mlx_uint8 boot_retry_count; + mlx_uint8 boot_protocol; + mlx_uint8 boot_vlan; + mlx_uint8 boot_pkey; + mlx_boolean en_wol_magic; + mlx_uint8 network_link_type; + mlx_uint8 iscsi_boot_to_target; + mlx_boolean iscsi_vlan_en; + mlx_boolean iscsi_tcp_timestamps_en; + mlx_boolean iscsi_chap_mutual_auth_en; + mlx_boolean iscsi_chap_auth_en; + mlx_boolean iscsi_dhcp_params_en; + mlx_boolean iscsi_ipv4_dhcp_en; + mlx_uint8 iscsi_lun_busy_retry_count; + mlx_uint8 iscsi_link_up_delay_time; + mlx_uint8 iscsi_drive_num; + mlx_uint8 client_identifier; + mlx_uint8 mac_admin_bit; + mlx_uint8 default_link_type; + mlx_uint8 linkup_timeout; + mlx_uint8 ip_ver; + mlx_uint8 undi_network_wait_to; +}; + +struct mlx_nvconfig_conf_defaults { + mlx_uint8 max_vfs; + mlx_uint8 total_vfs; + mlx_uint8 sriov_en; + mlx_uint8 maximum_uar_bar_size; + mlx_uint8 uar_bar_size; + mlx_uint8 flexboot_menu_to; + mlx_boolean ocsd_ocbb_en; + mlx_boolean boot_ip_ver_en; +}; + +mlx_status +nvconfig_read_port_default_values( + IN mlx_utils *utils, + IN mlx_uint8 port, + OUT struct mlx_nvconfig_port_conf_defaults *port_conf_def + ); + +mlx_status +nvconfig_read_general_default_values( + IN mlx_utils *utils, + OUT struct mlx_nvconfig_conf_defaults *conf_def + ); + +mlx_status +nvconfig_read_rom_ini_values( + IN mlx_utils *utils, + OUT struct mlx_nvcofnig_romini *rom_ini + ); +#endif /* MLX_NVCONFIG_DEFAULTS_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h new file mode 100644 index 000000000..7fd52accc --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_prm.h @@ -0,0 +1,331 @@ +#ifndef MLX_NVCONFIG_PRM_H_ +#define MLX_NVCONFIG_PRM_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 "../../include/public/mlx_types.h" + +enum { + WAKE_ON_LAN_TYPE = 0x10, + VIRTUALIZATION_TYPE = 0x11, + VPI_LINK_TYPE = 0x12, + BOOT_SETTINGS_EXT_TYPE = 0x2001, + BANNER_TO_TYPE = 0x2010, + OCSD_OCBB_TYPE = 0x2011, + FLOW_CONTROL_TYPE = 0x2020, + BOOT_SETTINGS_TYPE = 0x2021, + NV_ROM_FLEXBOOT_DEBUG = 0x2004, + + ISCSI_GENERAL_SETTINGS_TYPE = 0x2100, + IB_BOOT_SETTING_TYPE = 0x2022, + IB_DHCP_SETTINGS_TYPE = 0x2023, + GLOPAL_PCI_SETTINGS_TYPE = 0x80, + GLOPAL_PCI_CAPS_TYPE = 0x81, + GLOBAL_ROM_INI_TYPE = 0x100, + NV_VIRT_NET_ADDR = 0x110, + + // Types for iSCSI strings + DHCP_VEND_ID = 0x2101, + ISCSI_INITIATOR_IPV4_ADDR = 0x2102, + ISCSI_INITIATOR_SUBNET = 0x2103, + ISCSI_INITIATOR_IPV4_GATEWAY = 0x2104, + ISCSI_INITIATOR_IPV4_PRIM_DNS = 0x2105, + ISCSI_INITIATOR_IPV4_SECDNS = 0x2106, + ISCSI_INITIATOR_NAME = 0x2107, + ISCSI_INITIATOR_CHAP_ID = 0x2108, + ISCSI_INITIATOR_CHAP_PWD = 0x2109, + ISCSI_INITIATOR_DHCP_CONF_TYPE = 0x210a, + + CONNECT_FIRST_TGT = 0x2200, + FIRST_TGT_IP_ADDRESS = 0x2201, + FIRST_TGT_TCP_PORT = 0x2202, + FIRST_TGT_BOOT_LUN = 0x2203, + FIRST_TGT_ISCSI_NAME = 0x2204, + FIRST_TGT_CHAP_ID = 0x2205, + FIRST_TGT_CHAP_PWD = 0x2207, + NV_ROM_DEBUG_LEVEL = 0x2002, + NV_ROM_CAP_TYPE = 0x101, +}; + +union mlx_nvconfig_nic_boot_conf { + struct { + mlx_uint32 vlan_id : 12; + mlx_uint32 link_speed : 4; + mlx_uint32 legacy_boot_prot : 8; + mlx_uint32 boot_retry_count : 3; + mlx_uint32 boot_strap_type : 3; + mlx_uint32 en_vlan : 1; + mlx_uint32 en_option_rom : 1; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_nic_boot_ext_conf { + struct { + mlx_uint32 linkup_timeout : 8; + mlx_uint32 ip_ver : 2; + mlx_uint32 reserved0 : 6; + mlx_uint32 undi_network_wait_to : 8; + mlx_uint32 reserved1 : 8; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_rom_banner_timeout_conf { + struct { + mlx_uint32 rom_banner_to : 4; + mlx_uint32 reserved : 28; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_virt_conf { + struct { + mlx_uint32 reserved0 :24; + mlx_uint32 pf_bar_size_valid :1; + mlx_uint32 vf_bar_size_valid :1; + mlx_uint32 num_pf_msix_valid :1; + mlx_uint32 num_vf_msix_valid :1; + mlx_uint32 num_pfs_valid :1; + mlx_uint32 fpp_valid :1; + mlx_uint32 full_vf_qos_valid :1; + mlx_uint32 sriov_valid :1; + /*-------------------*/ + mlx_uint32 num_of_vfs :16; + mlx_uint32 num_of_pfs :4; + mlx_uint32 reserved1 :9; + mlx_uint32 fpp_en :1; + mlx_uint32 full_vf_qos :1; + mlx_uint32 virt_mode :1; //sriov_en + /*-------------------*/ + mlx_uint32 log_pf_uar_bar_size :6; + mlx_uint32 log_vf_uar_bar_size :6; + mlx_uint32 num_pf_msix :10; + mlx_uint32 num_vf_msix :10; + }; + mlx_uint32 dword[3]; +}; + +union mlx_nvconfig_virt_caps { + struct { + mlx_uint32 reserved0 :24; + mlx_uint32 max_vfs_per_pf_valid :1; + mlx_uint32 max_total_msix_valid :1; + mlx_uint32 max_total_bar_valid :1; + mlx_uint32 num_pfs_supported :1; + mlx_uint32 num_vf_msix_supported :1; + mlx_uint32 num_pf_msix_supported :1; + mlx_uint32 vf_bar_size_supported :1; + mlx_uint32 pf_bar_size_supported :1; + /*-------------------*/ + mlx_uint32 max_vfs_per_pf :16; + mlx_uint32 max_num_pfs :4; + mlx_uint32 reserved1 :9; + mlx_uint32 fpp_support :1; + mlx_uint32 vf_qos_control_support :1; + mlx_uint32 sriov_support :1; + /*-------------------*/ + mlx_uint32 max_log_pf_uar_bar_size :6; + mlx_uint32 max_log_vf_uar_bar_size :6; + mlx_uint32 max_num_pf_msix :10; + mlx_uint32 max_num_vf_msix :10; + /*-------------------*/ + mlx_uint32 max_total_msix; + /*-------------------*/ + mlx_uint32 max_total_bar; + }; + mlx_uint32 dword[5]; +}; + +union mlx_nvconfig_iscsi_init_dhcp_conf { + struct { + mlx_uint32 reserved0 :30; + mlx_uint32 dhcp_iscsi_en :1; + mlx_uint32 ipv4_dhcp_en :1; + + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_nic_ib_boot_conf { + struct { + mlx_uint32 boot_pkey : 16; + mlx_uint32 reserved0 : 16; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_wol_conf { + struct { + mlx_uint32 reserved0 :9; + mlx_uint32 en_wol_passwd :1; + mlx_uint32 en_wol_magic :1; + mlx_uint32 reserved1 :21; + mlx_uint32 reserved2 :32; + }; + mlx_uint32 dword[2]; +}; + +union mlx_nvconfig_iscsi_general { + struct { + mlx_uint32 reserved0 :22; + mlx_uint32 boot_to_target :2; + mlx_uint32 reserved1 :2; + mlx_uint32 vlan_en :1; + mlx_uint32 tcp_timestamps_en :1; + mlx_uint32 chap_mutual_auth_en :1; + mlx_uint32 chap_auth_en :1; + mlx_uint32 reserved2 :2; + /*-------------------*/ + mlx_uint32 vlan :12; + mlx_uint32 reserved3 :20; + /*-------------------*/ + mlx_uint32 lun_busy_retry_count:8; + mlx_uint32 link_up_delay_time :8; + mlx_uint32 drive_num :8; + mlx_uint32 reserved4 :8; + }; + mlx_uint32 dword[3]; +}; + +union mlx_nvconfig_ib_dhcp_conf { + struct { + mlx_uint32 reserved :24; + mlx_uint32 client_identifier :4; + mlx_uint32 mac_admin_bit :4; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_ocsd_ocbb_conf { + struct { + mlx_uint32 reserved :31; + mlx_uint32 ocsd_ocbb_en :1; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_vpi_link_conf { + struct { + mlx_uint32 network_link_type :2; + mlx_uint32 default_link_type :2; + mlx_uint32 reserved :28; + }; + mlx_uint32 dword; +}; + +struct mlx_nvcofnig_romini { + mlx_uint32 reserved0 :1; + mlx_uint32 shared_memory_en :1; + mlx_uint32 hii_vpi_en :1; + mlx_uint32 tech_enum :1; + mlx_uint32 reserved1 :4; + mlx_uint32 static_component_name_string :1; + mlx_uint32 hii_iscsi_configuration :1; + mlx_uint32 hii_ibm_aim :1; + mlx_uint32 hii_platform_setup :1; + mlx_uint32 hii_bdf_decimal :1; + mlx_uint32 hii_read_only :1; + mlx_uint32 reserved2 :10; + mlx_uint32 mac_enum :1; + mlx_uint32 port_enum :1; + mlx_uint32 flash_en :1; + mlx_uint32 fmp_en :1; + mlx_uint32 bofm_en :1; + mlx_uint32 platform_to_driver_en:1; + mlx_uint32 hii_en :1; + mlx_uint32 undi_en :1; + /* -------------- */ + mlx_uint64 dhcp_user_class; + /* -------------- */ + mlx_uint32 reserved3 :10; + mlx_uint32 ucm_single_port :1; + mlx_uint32 tivoli_wa_en :1; + mlx_uint32 dhcp_pxe_discovery_control_dis :1; + mlx_uint32 hii_flexaddr_override:1; + mlx_uint32 hii_flexaddr_setting :1; + mlx_uint32 guided_ops :1; + mlx_uint32 hii_type :4; + mlx_uint32 hii_mriname2 :1; + mlx_uint32 hii_aim_ucm_ver2 :1; + mlx_uint32 uri_boot_retry_delay :4; + mlx_uint32 uri_boot_retry :4; + mlx_uint32 option_rom_debug :1; + mlx_uint32 promiscuous_vlan :1; + +} __attribute__ ((packed)); + +union mlx_nvconfig_debug_conf { + struct { + mlx_uint32 dbg_log_en :1; + mlx_uint32 reserved1 :31; + /***************************************************/ + mlx_uint32 stp_dbg_lvl :2; + mlx_uint32 romprefix_dbg_lvl :2; + mlx_uint32 dhcp_dbg_lvl :2; + mlx_uint32 dhcpv6_dbg_lvl :2; + mlx_uint32 arp_dbg_lvl :2; + mlx_uint32 neighbor_dbg_lvl :2; + mlx_uint32 ndp_dbg_lvl :2; + mlx_uint32 uri_dbg_lvl :2; + mlx_uint32 driver_dbg_lvl :2; + mlx_uint32 nodnic_dbg_lvl :2; + mlx_uint32 nodnic_cmd_dbg_lvl :2; + mlx_uint32 nodnic_device_dbg_lvl :2; + mlx_uint32 nodnic_port_dbg_lvl :2; + mlx_uint32 netdevice_dbg_lvl :2; + mlx_uint32 tftp_dbg_lvl :2; + mlx_uint32 udp_dbg_lvl :2; + /***************************************************/ + mlx_uint32 tcp_dbg_lvl :2; + mlx_uint32 tcpip_dbg_lvl :2; + mlx_uint32 ipv4_dbg_lvl :2; + mlx_uint32 ipv6_dbg_lvl :2; + mlx_uint32 drv_set_dbg_lvl :2; + mlx_uint32 stat_update_dbg_lvl :2; + mlx_uint32 pxe_undi_dbg_lvl :2; + mlx_uint32 reserved2 :18; + }; + mlx_uint32 dword[3]; +}; + +union mlx_nvconfig_flexboot_debug { + struct { + mlx_uint32 reserved0 :29; + mlx_uint32 panic_behavior :2; + mlx_uint32 boot_to_shell :1; + }; + mlx_uint32 dword; +}; + +union mlx_nvconfig_rom_cap_conf { + struct { + mlx_uint32 reserved0 :28; + mlx_uint32 uefi_logs_en :1; + mlx_uint32 flexboot_debug_en :1; + mlx_uint32 boot_debug_log_en :1; + mlx_uint32 boot_ip_ver_en :1; + }; + mlx_uint32 dword; +}; + +#endif /* MLX_NVCONFIG_PRM_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c new file mode 100644 index 000000000..143ab1b0e --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.c @@ -0,0 +1,90 @@ +/* + * 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_lib/mlx_reg_access/mlx_reg_access.h" +#include "../../include/public/mlx_icmd.h" +#include "../../include/public/mlx_bail.h" +#include "../../include/public/mlx_memory.h" + +static +mlx_status +init_operation_tlv( + IN struct mail_box_tlv *mail_box_tlv, + IN mlx_uint16 reg_id, + IN REG_ACCESS_OPT reg_opt + ) +{ +#define TLV_OPERATION 1 + mail_box_tlv->operation_tlv.Type = TLV_OPERATION; +#define MAD_CLASS_REG_ACCESS 1 + mail_box_tlv->operation_tlv.cls = MAD_CLASS_REG_ACCESS; +#define TLV_OPERATION_SIZE 4 + mail_box_tlv->operation_tlv.len = TLV_OPERATION_SIZE; + mail_box_tlv->operation_tlv.method = reg_opt; + mail_box_tlv->operation_tlv.register_id = reg_id; + return MLX_SUCCESS; +} + +mlx_status +mlx_reg_access( + IN mlx_utils *utils, + IN mlx_uint16 reg_id, + IN REG_ACCESS_OPT reg_opt, + IN OUT mlx_void *reg_data, + IN mlx_size reg_size, + OUT mlx_uint32 *reg_status + ) +{ + mlx_status status = MLX_SUCCESS; + struct mail_box_tlv mail_box_tlv; + + if (utils == NULL || reg_data == NULL || reg_status == NULL + || reg_size > REG_ACCESS_MAX_REG_SIZE) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_memory_set(utils, &mail_box_tlv, 0, sizeof(mail_box_tlv)); + + init_operation_tlv(&mail_box_tlv, reg_id, reg_opt); + +#define REG_ACCESS_TLV_REG 3 +#define REG_TLV_HEADER_LEN 4 +#define OP_TLV_SIZE 16 + mail_box_tlv.reg_tlv.Type = REG_ACCESS_TLV_REG; + mail_box_tlv.reg_tlv.len = ((reg_size + REG_TLV_HEADER_LEN + 3) >> 2); // length is in dwords round up + mlx_memory_cpy(utils, &mail_box_tlv.reg_tlv.data, reg_data, reg_size); + + reg_size += OP_TLV_SIZE + REG_TLV_HEADER_LEN; + + status = mlx_icmd_send_command(utils, FLASH_REG_ACCESS, &mail_box_tlv, reg_size, reg_size); + MLX_CHECK_STATUS(utils, status, icmd_err, "failed to send icmd"); + + mlx_memory_cpy(utils, reg_data, &mail_box_tlv.reg_tlv.data, + reg_size - (OP_TLV_SIZE + REG_TLV_HEADER_LEN)); + + *reg_status = mail_box_tlv.operation_tlv.status; +icmd_err: +bad_param: + return status; +} + + diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h new file mode 100644 index 000000000..ca7ca2f84 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_reg_access/mlx_reg_access.h @@ -0,0 +1,77 @@ +/* + * 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 ); + +#ifndef MLX_REG_ACCESS_H_ +#define MLX_REG_ACCESS_H_ + +#include "../../include/public/mlx_icmd.h" + +#define REG_ACCESS_MAX_REG_SIZE 236 + +typedef enum { + REG_ACCESS_READ = 1, + REG_ACCESS_WRITE = 2, +} REG_ACCESS_OPT; + +#define REG_ID_NVDA 0x9024 +#define REG_ID_NVDI 0x9025 +#define REG_ID_NVIA 0x9029 +#define REG_ID_MLCR 0x902b +#define REG_ID_NVQC 0x9030 +#define REG_ID_MFRL 0x9028 +#define REG_ID_PTYS 0x5004 +#define REG_ID_PMTU 0x5003 + +struct operation_tlv { + mlx_uint32 reserved0 :8; /* bit_offset:0 */ /* element_size: 8 */ + mlx_uint32 status :7; /* bit_offset:8 */ /* element_size: 7 */ + mlx_uint32 dr :1; /* bit_offset:15 */ /* element_size: 1 */ + mlx_uint32 len :11; /* bit_offset:16 */ /* element_size: 11 */ + mlx_uint32 Type :5; /* bit_offset:27 */ /* element_size: 5 */ + mlx_uint32 cls :8; /* bit_offset:32 */ /* element_size: 8 */ + mlx_uint32 method :7; /* bit_offset:40 */ /* element_size: 7 */ + mlx_uint32 r :1; /* bit_offset:47 */ /* element_size: 1 */ + mlx_uint32 register_id :16; /* bit_offset:48 */ /* element_size: 16 */ + mlx_uint64 tid ; /* bit_offset:64 */ /* element_size: 64 */ +}; + +struct reg_tlv { + mlx_uint32 reserved0 :16; /* bit_offset:0 */ /* element_size: 16 */ + mlx_uint32 len :11; /* bit_offset:16 */ /* element_size: 11 */ + mlx_uint32 Type :5; /* bit_offset:27 */ /* element_size: 5 */ + mlx_uint8 data[REG_ACCESS_MAX_REG_SIZE]; +}; + +struct mail_box_tlv { + struct operation_tlv operation_tlv; + struct reg_tlv reg_tlv; +}; +mlx_status +mlx_reg_access( + IN mlx_utils *utils, + IN mlx_uint16 reg_id, + IN REG_ACCESS_OPT reg_opt, + IN OUT mlx_void *reg_data, + IN mlx_size reg_size, + OUT mlx_uint32 *reg_status + ); + +#endif /* MLX_REG_ACCESS_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c new file mode 100644 index 000000000..65d04c967 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.c @@ -0,0 +1,74 @@ +/* + * 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_lib/mlx_vmac/mlx_vmac.h" +#include "../../include/public/mlx_icmd.h" +#include "../../include/public/mlx_bail.h" + +mlx_status +mlx_vmac_query_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_query_virt_mac *virt_mac + ) +{ + mlx_status status = MLX_SUCCESS; + if (utils == NULL || virt_mac == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + QUERY_VIRTUAL_MAC, + virt_mac, + 0, + sizeof(*virt_mac) + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} + +mlx_status +mlx_vmac_set_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_set_virt_mac *virt_mac + ) +{ + mlx_status status = MLX_SUCCESS; + if (utils == NULL || virt_mac == NULL) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + status = mlx_icmd_send_command( + utils, + SET_VIRTUAL_MAC, + virt_mac, + sizeof(*virt_mac), + 0 + ); + MLX_CHECK_STATUS(utils, status, icmd_err, "mlx_icmd_send_command failed"); +icmd_err: +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h new file mode 100644 index 000000000..2214d9189 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h @@ -0,0 +1,60 @@ +#ifndef MLX_VMAC_H_ +#define MLX_VMAC_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 "../../include/public/mlx_utils.h" + +struct mlx_vmac_query_virt_mac { + mlx_uint32 reserved0 :30; + mlx_uint32 mac_aux_v :1; + mlx_uint32 virtual_mac_en :1; + mlx_uint32 parmanent_mac_high :16; + mlx_uint32 reserved1 :16; + mlx_uint32 parmanent_mac_low :32; + mlx_uint32 virtual_mac_high :16; + mlx_uint32 Reserved2 :16; + mlx_uint32 virtual_mac_low :32; +}; + +struct mlx_vmac_set_virt_mac { + mlx_uint32 Reserved0 :30; + mlx_uint32 mac_aux_v :1; + mlx_uint32 virtual_mac_en :1; + mlx_uint32 reserved1 :32; + mlx_uint32 reserved2 :32; + mlx_uint32 virtual_mac_high; + mlx_uint32 virtual_mac_low; +}; + +mlx_status +mlx_vmac_query_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_query_virt_mac *virt_mac + ); + +mlx_status +mlx_vmac_set_virt_mac ( + IN mlx_utils *utils, + OUT struct mlx_vmac_set_virt_mac *virt_mac + ); +#endif /* MLX_VMAC_H_ */ diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c new file mode 100644 index 000000000..e4ab5f0ad --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_icmd.c @@ -0,0 +1,371 @@ +/* + * 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 "../../include/public/mlx_bail.h" +#include "../../include/public/mlx_icmd.h" +#include "../../include/public/mlx_pci_gw.h" +#include "../../include/public/mlx_utils.h" + +static +mlx_status +mlx_icmd_get_semaphore( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 retries = 0; + mlx_uint32 semaphore_id; + mlx_uint32 buffer; + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_utils_rand(utils, &semaphore_id); + MLX_CHECK_STATUS(utils, status, rand_err, "failed to get random number"); +#define ICMD_GET_SEMAPHORE_TRIES 2560 + for (retries = 0 ; retries < ICMD_GET_SEMAPHORE_TRIES ; retries++) { + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd semaphore"); + if (buffer != 0) { + mlx_utils_delay_in_ms(10); + continue; + } + mlx_pci_gw_write( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, semaphore_id); + MLX_CHECK_STATUS(utils, status, set_err, "failed to set icmd semaphore"); + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd semaphore"); + if (semaphore_id == buffer) { + status = MLX_SUCCESS; + utils->icmd.took_semaphore = TRUE; + break; + } + mlx_utils_delay_in_ms(10); + } + if (semaphore_id != buffer) { + status = MLX_FAILED; + } +read_err: +set_err: +rand_err: +invalid_param: + return status; +} +static +mlx_status +mlx_icmd_clear_semaphore( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + if (utils->icmd.took_semaphore == FALSE) { + goto semaphore_not_taken; + } + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_SEMAPHORE, + MLX_ICMD_SEMAPHORE_ADDR, 0); + MLX_CHECK_STATUS(utils, status, read_err, "failed to clear icmd semaphore"); + + utils->icmd.took_semaphore = FALSE; +read_err: +semaphore_not_taken: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_init( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + if (utils->icmd.icmd_opened == TRUE) { + goto already_opened; + } + + utils->icmd.took_semaphore = FALSE; + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_MB_SIZE_ADDR, &utils->icmd.max_cmd_size); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd mail box size"); + + utils->icmd.icmd_opened = TRUE; +read_err: +already_opened: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_set_opcode( + IN mlx_utils *utils, + IN mlx_uint16 opcode + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + +#define MLX_ICMD_OPCODE_ALIGN 16 +#define MLX_ICMD_OPCODE_MASK 0xffff + + buffer = buffer & ~(MLX_ICMD_OPCODE_MASK << MLX_ICMD_OPCODE_ALIGN); + buffer = buffer | (opcode << MLX_ICMD_OPCODE_ALIGN); + + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, buffer); + MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd ctrl"); +write_err: +read_err: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_go( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer; + mlx_uint32 busy; + mlx_uint32 wait_iteration = 0; + + if (utils == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + +#define MLX_ICMD_BUSY_ALIGN 0 +#define MLX_ICMD_BUSY_MASK 0x1 + + busy = (buffer >> MLX_ICMD_BUSY_ALIGN) & MLX_ICMD_BUSY_MASK; + if (busy != 0) { + status = MLX_FAILED; + goto already_busy; + } + + buffer = buffer | (1 << MLX_ICMD_BUSY_ALIGN); + + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, buffer); + MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd ctrl"); + +#define MLX_ICMD_BUSY_MAX_ITERATIONS 1024 + do { + if (++wait_iteration > MLX_ICMD_BUSY_MAX_ITERATIONS) { + status = MLX_FAILED; + MLX_DEBUG_ERROR(utils, "ICMD time out"); + goto busy_timeout; + } + + mlx_utils_delay_in_ms(10); + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + busy = (buffer >> MLX_ICMD_BUSY_ALIGN) & MLX_ICMD_BUSY_MASK; + } while (busy != 0); + +busy_timeout: +write_err: +already_busy: +read_err: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_get_status( + IN mlx_utils *utils, + OUT mlx_uint32 *out_status + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 buffer; + + if (utils == NULL || out_status == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_CTRL_ADDR, &buffer); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl"); + +#define MLX_ICMD_STATUS_ALIGN 8 +#define MLX_ICMD_STATUS_MASK 0xff + + *out_status = (buffer >> MLX_ICMD_STATUS_ALIGN) & MLX_ICMD_STATUS_MASK; + +read_err: +invalid_param: + return status; +} + +static +mlx_status +mlx_icmd_write_buffer( + IN mlx_utils *utils, + IN mlx_void* data, + IN mlx_uint32 data_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 data_offset = 0; + mlx_size dword_size = sizeof(mlx_uint32); + + if (utils == NULL || data == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + for (data_offset = 0 ; data_offset*dword_size < data_size ; data_offset++) { + status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_MB_ADDR + data_offset*dword_size, + ((mlx_uint32*)data)[data_offset]); + MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd MB"); + } +write_err: +invalid_param: + return status; +} + + +static +mlx_status +mlx_icmd_read_buffer( + IN mlx_utils *utils, + OUT mlx_void* data, + IN mlx_uint32 data_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 data_offset = 0; + mlx_size dword_size = sizeof(mlx_uint32); + + if (utils == NULL || data == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + + for (data_offset = 0 ; data_offset*dword_size < data_size ; data_offset++) { + status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD, + MLX_ICMD_MB_ADDR + data_offset*dword_size, + (mlx_uint32*)data + data_offset); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd MB"); + } +read_err: +invalid_param: + return status; +} +mlx_status +mlx_icmd_send_command( + IN mlx_utils *utils, + IN mlx_uint16 opcode, + IN OUT mlx_void* data, + IN mlx_uint32 write_data_size, + IN mlx_uint32 read_data_size + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 icmd_status = 0; + + if (utils == NULL || data == NULL) { + status = MLX_INVALID_PARAMETER; + goto invalid_param; + } + status = mlx_icmd_init(utils); + MLX_CHECK_STATUS(utils, status, open_err, "failed to open icmd"); + + if (write_data_size > utils->icmd.max_cmd_size || + read_data_size > utils->icmd.max_cmd_size) { + status = MLX_INVALID_PARAMETER; + goto size_err; + } + + status = mlx_icmd_get_semaphore(utils); + MLX_CHECK_STATUS(utils, status, semaphore_err, "failed to get icmd semaphore"); + + status = mlx_icmd_set_opcode(utils, opcode); + MLX_CHECK_STATUS(utils, status, opcode_err, "failed to set icmd opcode"); + + if (write_data_size != 0) { + status = mlx_icmd_write_buffer(utils, data, write_data_size); + MLX_CHECK_STATUS(utils, status, opcode_err, "failed to write icmd MB"); + } + + status = mlx_icmd_go(utils); + MLX_CHECK_STATUS(utils, status, go_err, "failed to activate icmd"); + + status = mlx_icmd_get_status(utils, &icmd_status); + MLX_CHECK_STATUS(utils, status, get_status_err, "failed to set icmd opcode"); + + if (icmd_status != 0) { + MLX_DEBUG_ERROR(utils, "icmd failed with status = %d\n", icmd_status); + status = MLX_FAILED; + goto icmd_failed; + } + if (read_data_size != 0) { + status = mlx_icmd_read_buffer(utils, data, read_data_size); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd MB"); + } +read_err: +icmd_failed: +get_status_err: +go_err: +opcode_err: + mlx_icmd_clear_semaphore(utils); +semaphore_err: +size_err: +open_err: +invalid_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c new file mode 100644 index 000000000..5aa5a53d2 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_memory.c @@ -0,0 +1,238 @@ +/* + * 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 +#include "../../include/private/mlx_memory_priv.h" +#include "../../include/public/mlx_memory.h" + +mlx_status +mlx_memory_alloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = NULL; + if ( utils == NULL || size == 0 || *ptr != NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_alloc_priv(utils, size, ptr); +bad_param: + return status; +} + +mlx_status +mlx_memory_zalloc( + IN mlx_utils *utils, + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = NULL; + if ( utils == NULL || size == 0 || *ptr != NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_zalloc_priv(utils, size, ptr); +bad_param: + return status; +} + +mlx_status +mlx_memory_free( + IN mlx_utils *utils, + IN mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || ptr == NULL || *ptr == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_free_priv(utils, *ptr); + *ptr = NULL; +bad_param: + return status; +} +mlx_status +mlx_memory_alloc_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = NULL; + if ( utils == NULL || size == 0 || *ptr != NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_alloc_dma_priv(utils, size, align, ptr); +bad_param: + return status; +} + +mlx_status +mlx_memory_free_dma( + IN mlx_utils *utils, + IN mlx_size size , + IN mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || size == 0 || ptr == NULL || *ptr == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_free_dma_priv(utils, size, *ptr); + *ptr = NULL; +bad_param: + return status; +} + +mlx_status +mlx_memory_map_dma( + IN mlx_utils *utils, + IN mlx_void *addr , + IN mlx_size number_of_bytes, + OUT mlx_physical_address *phys_addr, + OUT mlx_void **mapping + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || phys_addr == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_map_dma_priv(utils, addr, number_of_bytes, phys_addr, mapping); +bad_param: + return status; +} + +mlx_status +mlx_memory_ummap_dma( + IN mlx_utils *utils, + IN mlx_void *mapping + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_ummap_dma_priv(utils, mapping); +bad_param: + return status; +} + +mlx_status +mlx_memory_cmp( + IN mlx_utils *utils, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || first_block == NULL || second_block == NULL || + out == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_cmp_priv(utils, first_block, second_block, size, out); +bad_param: + return status; +} + +mlx_status +mlx_memory_set( + IN mlx_utils *utils, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || block == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_set_priv(utils, block, value, size); +bad_param: + return status; +} + +mlx_status +mlx_memory_cpy( + IN mlx_utils *utils, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || destination_buffer == NULL || source_buffer == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_cpy_priv(utils, destination_buffer, source_buffer, length); +bad_param: + return status; +} + +mlx_status +mlx_memory_cpu_to_be32( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || destination == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_cpu_to_be32_priv(utils, source, destination); +bad_param: + return status; +} + +mlx_status +mlx_memory_be32_to_cpu( + IN mlx_utils *utils, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + if ( utils == NULL || destination == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + status = mlx_memory_be32_to_cpu_priv(utils, source, destination); +bad_param: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c new file mode 100644 index 000000000..d4ff1b9a1 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci.c @@ -0,0 +1,133 @@ +/* + * 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 + +#include "../../include/private/mlx_pci_priv.h" +#include "../../include/public/mlx_pci.h" + +mlx_status +mlx_pci_init( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_init_priv(utils); +bail: + return status; +} + +mlx_status +mlx_pci_teardown( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_teardown_priv(utils); +bail: + return status; +} + +mlx_status +mlx_pci_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_read_priv(utils, width, offset, count, buffer); +bail: + return status; +} + +mlx_status +mlx_pci_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_write_priv(utils, width, offset, count, buffer); +bail: + return status; +} + +mlx_status +mlx_pci_mem_read( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_mem_read_priv(utils, width,bar_index, offset, count, buffer); +bail: + return status; +} + +mlx_status +mlx_pci_mem_write( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint8 bar_index, + IN mlx_uint64 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if( utils == NULL || count == 0){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + status = mlx_pci_mem_write_priv(utils, width, bar_index, offset, count, buffer); +bail: + return status; +} diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c new file mode 100644 index 000000000..30c1e644e --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_pci_gw.c @@ -0,0 +1,392 @@ +/* + * 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 "../../include/public/mlx_pci_gw.h" +#include "../../include/public/mlx_bail.h" +#include "../../include/public/mlx_pci.h" +#include "../../include/public/mlx_logging.h" + +/* Lock/unlock GW on each VSEC access */ +#undef VSEC_DEBUG + +static +mlx_status +mlx_pci_gw_check_capability_id( + IN mlx_utils *utils, + IN mlx_uint8 cap_pointer, + OUT mlx_boolean *bool + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 offset = cap_pointer + PCI_GW_CAPABILITY_ID_OFFSET; + mlx_uint8 id = 0; + status = mlx_pci_read(utils, MlxPciWidthUint8, offset, + 1, &id); + MLX_CHECK_STATUS(utils, status, read_err,"failed to read capability id"); + *bool = ( id == PCI_GW_CAPABILITY_ID ); +read_err: + return status; +} + +static +mlx_status +mlx_pci_gw_get_ownership( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset; + mlx_uint32 semaphore = 0; + mlx_uint32 counter = 0; + mlx_uint32 get_semaphore_try = 0; + mlx_uint32 get_ownership_try = 0; + + for( ; get_ownership_try < PCI_GW_GET_OWNERSHIP_TRIES; get_ownership_try ++){ + for( ; get_semaphore_try <= PCI_GW_SEMPHORE_TRIES ; get_semaphore_try++){ + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &semaphore); + MLX_CHECK_STATUS(utils, status, read_err,"failed to read semaphore"); + if( semaphore == 0 ){ + break; + } + mlx_utils_delay_in_us(10); + } + if( semaphore != 0 ){ + status = MLX_FAILED; + goto semaphore_err; + } + + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_COUNTER_OFFSET, + 1, &counter); + MLX_CHECK_STATUS(utils, status, read_err, "failed to read counter"); + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &counter); + MLX_CHECK_STATUS(utils, status, write_err,"failed to write semaphore"); + + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &semaphore); + MLX_CHECK_STATUS(utils, status, read_err,"failed to read semaphore"); + if( counter == semaphore ){ + break; + } + } + if( counter != semaphore ){ + status = MLX_FAILED; + } +write_err: +read_err: +semaphore_err: + return status; +} + +static +mlx_status +mlx_pci_gw_free_ownership( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset; + mlx_uint32 value = 0; + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_SEMAPHORE_OFFSET, + 1, &value); + MLX_CHECK_STATUS(utils, status, write_err,"failed to write semaphore"); +write_err: + return status; +} + +static +mlx_status +mlx_pci_gw_set_space( + IN mlx_utils *utils, + IN mlx_pci_gw_space space + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset;; + mlx_uint8 space_status = 0; + + /* set nodnic*/ + status = mlx_pci_write(utils, MlxPciWidthUint16, cap_offset + PCI_GW_CAPABILITY_SPACE_OFFSET, 1, &space); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability space"); + + status = mlx_pci_read(utils, MlxPciWidthUint8, cap_offset + PCI_GW_CAPABILITY_STATUS_OFFSET, 1, &space_status); + MLX_CHECK_STATUS(utils, status, read_error,"failed to read capability status"); + if( (space_status & 0x20) == 0){ + status = MLX_FAILED; + goto space_unsupported; + } +read_error: +space_unsupported: + return status; +} + +static +mlx_status +mlx_pci_gw_wait_for_flag_value( + IN mlx_utils *utils, + IN mlx_boolean value + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint32 try = 0; + mlx_uint32 cap_offset = utils->pci_gw.pci_cmd_offset; + mlx_uint32 flag = 0; + + for(; try < PCI_GW_READ_FLAG_TRIES ; try ++ ) { + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_FLAG_OFFSET, 1, &flag); + MLX_CHECK_STATUS(utils, status, read_error, "failed to read capability flag"); + if( ((flag & 0x80000000) != 0) == value ){ + goto flag_valid; + } + mlx_utils_delay_in_us(10); + } + status = MLX_FAILED; +flag_valid: +read_error: + return status; +} +static +mlx_status +mlx_pci_gw_search_capability( + IN mlx_utils *utils, + OUT mlx_uint32 *cap_offset + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 cap_pointer = 0; + mlx_boolean is_capability = FALSE; + + if( cap_offset == NULL || utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + //get first capability pointer + status = mlx_pci_read(utils, MlxPciWidthUint8, PCI_GW_FIRST_CAPABILITY_POINTER_OFFSET, + 1, &cap_pointer); + MLX_CHECK_STATUS(utils, status, read_err, + "failed to read capability pointer"); + + //search the right capability + while( cap_pointer != 0 ){ + status = mlx_pci_gw_check_capability_id(utils, cap_pointer, &is_capability); + MLX_CHECK_STATUS(utils, status, check_err + ,"failed to check capability id"); + + if( is_capability == TRUE ){ + *cap_offset = cap_pointer; + break; + } + + status = mlx_pci_read(utils, MlxPciWidthUint8, cap_pointer + + PCI_GW_CAPABILITY_NEXT_POINTER_OFFSET , + 1, &cap_pointer); + MLX_CHECK_STATUS(utils, status, read_err, + "failed to read capability pointer"); + } + if( is_capability != TRUE ){ + status = MLX_NOT_FOUND; + } +check_err: +read_err: +bad_param: + return status; +} + +mlx_status +mlx_pci_gw_init( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_pci_gw *pci_gw = NULL; + + if( utils == NULL){ + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + pci_gw = &utils->pci_gw; + + status = mlx_pci_gw_search_capability(utils, &pci_gw->pci_cmd_offset); + MLX_CHECK_STATUS(utils, status, cap_err, + "mlx_pci_gw_search_capability failed"); + +#if ! defined ( VSEC_DEBUG ) + status = mlx_pci_gw_get_ownership(utils); + MLX_CHECK_STATUS(utils, status, ownership_err,"failed to get ownership"); +ownership_err: +#endif +cap_err: +bad_param: + return status; +} + +mlx_status +mlx_pci_gw_teardown( + IN mlx_utils *utils __attribute__ ((unused)) + ) +{ +#if ! defined ( VSEC_DEBUG ) + mlx_pci_gw_free_ownership(utils); +#endif + return MLX_SUCCESS; +} + +mlx_status +mlx_pci_gw_read( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + OUT mlx_pci_gw_buffer *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_pci_gw *pci_gw = NULL; + mlx_uint32 cap_offset = 0; + + if (utils == NULL || buffer == NULL || utils->pci_gw.pci_cmd_offset == 0) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_utils_acquire_lock(utils); + + pci_gw = &utils->pci_gw; + cap_offset = pci_gw->pci_cmd_offset; + +#if ! defined ( VSEC_DEBUG ) + if (pci_gw->space != space) { + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; + } +#else + status = mlx_pci_gw_get_ownership(utils); + MLX_CHECK_STATUS(utils, status, ownership_err,"failed to get ownership"); + + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; +#endif + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_ADDRESS_OFFSET, 1, &address); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability address"); + +#if defined ( DEVICE_CX3 ) + /* WA for PCI issue (race) */ + mlx_utils_delay_in_us ( 10 ); +#endif + + status = mlx_pci_gw_wait_for_flag_value(utils, TRUE); + MLX_CHECK_STATUS(utils, status, read_error, "flag failed to change"); + + status = mlx_pci_read(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_DATA_OFFSET, 1, buffer); + MLX_CHECK_STATUS(utils, status, read_error,"failed to read capability data"); + +#if defined ( VSEC_DEBUG ) + status = mlx_pci_gw_free_ownership(utils); + MLX_CHECK_STATUS(utils, status, free_err, + "mlx_pci_gw_free_ownership failed"); +free_err: + mlx_utils_release_lock(utils); + return status; +#endif +read_error: +space_error: +#if defined ( VSEC_DEBUG ) + mlx_pci_gw_free_ownership(utils); +ownership_err: +#endif +mlx_utils_release_lock(utils); +bad_param: + return status; +} + +mlx_status +mlx_pci_gw_write( + IN mlx_utils *utils, + IN mlx_pci_gw_space space, + IN mlx_uint32 address, + IN mlx_pci_gw_buffer buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_pci_gw *pci_gw = NULL; + mlx_uint32 cap_offset = 0; + mlx_uint32 fixed_address = address | PCI_GW_WRITE_FLAG; + + if (utils == NULL || utils->pci_gw.pci_cmd_offset == 0) { + status = MLX_INVALID_PARAMETER; + goto bad_param; + } + + mlx_utils_acquire_lock(utils); + + pci_gw = &utils->pci_gw; + cap_offset = pci_gw->pci_cmd_offset; + +#if ! defined ( VSEC_DEBUG ) + if (pci_gw->space != space) { + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; + } +#else + status = mlx_pci_gw_get_ownership(utils); + MLX_CHECK_STATUS(utils, status, ownership_err,"failed to get ownership"); + + status = mlx_pci_gw_set_space(utils, space); + MLX_CHECK_STATUS(utils, status, space_error,"failed to set space"); + pci_gw->space = space; +#endif + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_DATA_OFFSET, 1, &buffer); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability data"); + + status = mlx_pci_write(utils, MlxPciWidthUint32, cap_offset + PCI_GW_CAPABILITY_ADDRESS_OFFSET, 1, &fixed_address); + MLX_CHECK_STATUS(utils, status, read_error,"failed to write capability address"); + + status = mlx_pci_gw_wait_for_flag_value(utils, FALSE); + MLX_CHECK_STATUS(utils, status, read_error, "flag failed to change"); +#if defined ( VSEC_DEBUG ) + status = mlx_pci_gw_free_ownership(utils); + MLX_CHECK_STATUS(utils, status, free_err, + "mlx_pci_gw_free_ownership failed"); +free_err: +mlx_utils_release_lock(utils); + return status; +#endif +read_error: +space_error: +#if defined ( VSEC_DEBUG ) + mlx_pci_gw_free_ownership(utils); +ownership_err: +#endif +mlx_utils_release_lock(utils); +bad_param: + return status; +} + + + diff --git a/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c b/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c new file mode 100644 index 000000000..7ae35355d --- /dev/null +++ b/src/drivers/infiniband/mlx_utils/src/public/mlx_utils.c @@ -0,0 +1,122 @@ +/* + * 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 + +#include "../../include/private/mlx_utils_priv.h" +#include "../../include/public/mlx_pci.h" +#include "../../include/public/mlx_utils.h" +mlx_status +mlx_utils_init( + IN mlx_utils *utils, + IN mlx_pci *pci + ) +{ + mlx_status status = MLX_SUCCESS; + if( pci == NULL || utils == NULL ){ + status = MLX_INVALID_PARAMETER; + goto bail; + } + utils->pci = pci; + status = mlx_pci_init(utils); + status = mlx_utils_init_lock(utils); +bail: + return status; +} + +mlx_status +mlx_utils_teardown( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_utils_free_lock(utils); + mlx_pci_teardown(utils); + return status; +} + +mlx_status +mlx_utils_delay_in_ms( + IN mlx_uint32 msecs + ) +{ + mlx_utils_delay_in_ms_priv(msecs); + return MLX_SUCCESS; +} +mlx_status +mlx_utils_delay_in_us( + IN mlx_uint32 usecs + ) +{ + mlx_utils_delay_in_us_priv(usecs); + return MLX_SUCCESS; +} +mlx_status +mlx_utils_ilog2( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ) +{ + mlx_utils_ilog2_priv(i, log); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_init_lock( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_init_lock_priv(&(utils->lock)); + +} + +mlx_status +mlx_utils_free_lock( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_free_lock_priv(utils->lock); +} + +mlx_status +mlx_utils_acquire_lock ( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_acquire_lock_priv(utils->lock); +} + +mlx_status +mlx_utils_release_lock ( + IN OUT mlx_utils *utils + ) +{ + return mlx_utils_release_lock_priv(utils->lock); +} + +mlx_status +mlx_utils_rand ( + IN mlx_utils *utils, + OUT mlx_uint32 *rand_num + ) +{ + return mlx_utils_rand_priv(utils, rand_num); +} diff --git a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h new file mode 100644 index 000000000..3acc1d9d8 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_logging_priv.h @@ -0,0 +1,62 @@ +/* + * DebugPriv.h + * + * Created on: Jan 19, 2015 + * Author: maord + */ + +#ifndef STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ +#define STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ + +#include +#include + +#define MLX_DEBUG_FATAL_ERROR_PRIVATE(...) do { \ + printf("%s: ",__func__); \ + printf(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_ERROR_PRIVATE(id, ...) do { \ + DBGC(id, "%s: ",__func__); \ + DBGC(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_WARN_PRIVATE(id, ...) do { \ + DBGC(id, "%s: ",__func__); \ + DBGC(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_INFO1_PRIVATE(id, ...) do { \ + DBGC(id, "%s: ",__func__); \ + DBGC(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DEBUG_INFO2_PRIVATE(id, ...) do { \ + DBGC2(id, "%s: ",__func__); \ + DBGC2(id, __VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_ERROR_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_WARN_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_INFO1_PRIVATE(...) do { \ + DBG("%s: ",__func__); \ + DBG(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_DBG_INFO2_PRIVATE(...) do { \ + DBG2("%s: ",__func__); \ + DBG2(__VA_ARGS__); \ + } while ( 0 ) + +#define MLX_PRINT_PRIVATE(...) printf(__VA_ARGS__) + + +#endif /* STUB_MLXUTILS_INCLUDE_PRIVATE_FLEXBOOT_DEBUG_H_ */ diff --git a/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h new file mode 100644 index 000000000..fe0d5c056 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/include/mlx_types_priv.h @@ -0,0 +1,60 @@ +/* + * types.h + * + * Created on: Jan 18, 2015 + * Author: maord + */ + +#ifndef A_MLXUTILS_INCLUDE_PUBLIC_TYPES_H_ +#define A_MLXUTILS_INCLUDE_PUBLIC_TYPES_H_ +#include +//#include +#include + +#define MLX_SUCCESS 0 +#define MLX_OUT_OF_RESOURCES (-1) +//(-ENOMEM) +#define MLX_INVALID_PARAMETER (-2) +//(-EINVAL) +#define MLX_UNSUPPORTED (-3) +//(-ENOSYS) +#define MLX_NOT_FOUND (-4) + +#define MLX_FAILED (-5) + +#undef TRUE +#define TRUE 1 +#undef FALSE +#define FALSE !TRUE + +typedef int mlx_status; + +typedef uint8_t mlx_uint8; +typedef uint16_t mlx_uint16; +typedef uint32_t mlx_uint32; +typedef uint64_t mlx_uint64; +typedef unsigned long mlx_uintn; + +typedef int8_t mlx_int8; +typedef int16_t mlx_int16;; +typedef int32_t mlx_int32; +typedef int64_t mlx_int64; +typedef uint8_t mlx_boolean; + +typedef struct pci_device mlx_pci; + +typedef size_t mlx_size; + +typedef void mlx_void; + +#define MAC_ADDR_LEN 6 +typedef unsigned long mlx_physical_address; +typedef union { + struct { + uint32_t low; + uint32_t high; + } __attribute__ (( packed )); + uint8_t addr[MAC_ADDR_LEN]; +} mlx_mac_address; + +#endif /* A_MLXUTILS_INCLUDE_PUBLIC_TYPES_H_ */ diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c new file mode 100644 index 000000000..cb9e759bf --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_memory_priv.c @@ -0,0 +1,172 @@ +/* + * MemoryPriv.c + * + * Created on: Jan 21, 2015 + * Author: maord + */ + +#include +#include +#include +#include +#include "../../mlx_utils/include/private/mlx_memory_priv.h" + + +mlx_status +mlx_memory_alloc_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = malloc(size); + if(*ptr == NULL){ + status = MLX_OUT_OF_RESOURCES; + } + return status; +} + +mlx_status +mlx_memory_zalloc_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = zalloc(size); + if(*ptr == NULL){ + status = MLX_OUT_OF_RESOURCES; + } + return status; +} + +mlx_status +mlx_memory_free_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_void *ptr + ) +{ + mlx_status status = MLX_SUCCESS; + free(ptr); + return status; +} +mlx_status +mlx_memory_alloc_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size , + IN mlx_size align, + OUT mlx_void **ptr + ) +{ + mlx_status status = MLX_SUCCESS; + *ptr = malloc_dma(size, align); + if (*ptr == NULL) { + status = MLX_OUT_OF_RESOURCES; + } else { + memset(*ptr, 0, size); + } + return status; +} + +mlx_status +mlx_memory_free_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_size size , + IN mlx_void *ptr + ) +{ + mlx_status status = MLX_SUCCESS; + free_dma(ptr, size); + return status; +} +mlx_status +mlx_memory_map_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_void *addr , + IN mlx_size number_of_bytes __attribute__ ((unused)), + OUT mlx_physical_address *phys_addr, + OUT mlx_void **mapping __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; + *phys_addr = virt_to_bus(addr); + return status; +} + +mlx_status +mlx_memory_ummap_dma_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_void *mapping __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; + return status; +} + +mlx_status +mlx_memory_cmp_priv( + IN mlx_utils *utils __unused, + IN mlx_void *first_block, + IN mlx_void *second_block, + IN mlx_size size, + OUT mlx_uint32 *out + ) +{ + mlx_status status = MLX_SUCCESS; + *out = memcmp(first_block, second_block, size); + return status; +} + +mlx_status +mlx_memory_set_priv( + IN mlx_utils *utils __unused, + IN mlx_void *block, + IN mlx_int32 value, + IN mlx_size size + ) +{ + mlx_status status = MLX_SUCCESS; + memset(block, value, size); + return status; +} + +mlx_status +mlx_memory_cpy_priv( + IN mlx_utils *utils __unused, + OUT mlx_void *destination_buffer, + IN mlx_void *source_buffer, + IN mlx_size length + ) +{ + mlx_status status = MLX_SUCCESS; + memcpy(destination_buffer, source_buffer, length); + return status; +} + +mlx_status +mlx_memory_cpu_to_be32_priv( + IN mlx_utils *utils __unused, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + *destination = cpu_to_be32(source); + return status; +} + + +mlx_status +mlx_memory_be32_to_cpu_priv( + IN mlx_utils *utils __unused, + IN mlx_uint32 source, + IN mlx_uint32 *destination + ) +{ + mlx_status status = MLX_SUCCESS; + *destination = be32_to_cpu(source); + return status; +} + diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c new file mode 100644 index 000000000..b474a4a63 --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_pci_priv.c @@ -0,0 +1,195 @@ +/* + * MlxPciPriv.c + * + * Created on: Jan 21, 2015 + * Author: maord + */ + +#include + +#include "../../mlx_utils/include/private/mlx_pci_priv.h" + + +static +mlx_status +mlx_pci_config_byte( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_uint32 offset, + IN OUT mlx_uint8 *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if (read) { + status = pci_read_config_byte(utils->pci, offset, buffer); + }else { + status = pci_write_config_byte(utils->pci, offset, *buffer); + } + return status; +} + +static +mlx_status +mlx_pci_config_word( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_uint32 offset, + IN OUT mlx_uint16 *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if (read) { + status = pci_read_config_word(utils->pci, offset, buffer); + }else { + status = pci_write_config_word(utils->pci, offset, *buffer); + } + return status; +} + +static +mlx_status +mlx_pci_config_dword( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_uint32 offset, + IN OUT mlx_uint32 *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + if (read) { + status = pci_read_config_dword(utils->pci, offset, buffer); + }else { + status = pci_write_config_dword(utils->pci, offset, *buffer); + } + return status; +} +static +mlx_status +mlx_pci_config( + IN mlx_utils *utils, + IN mlx_boolean read, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + mlx_uint8 *tmp = (mlx_uint8*)buffer; + mlx_uintn iteration = 0; + if( width == MlxPciWidthUint64) { + width = MlxPciWidthUint32; + count = count * 2; + } + + for(;iteration < count ; iteration++) { + switch (width){ + case MlxPciWidthUint8: + status = mlx_pci_config_byte(utils, read , offset++, tmp++); + break; + case MlxPciWidthUint16: + status = mlx_pci_config_word(utils, read , offset, (mlx_uint16*)tmp); + tmp += 2; + offset += 2; + break; + case MlxPciWidthUint32: + status = mlx_pci_config_dword(utils, read , offset, (mlx_uint32*)tmp); + tmp += 4; + offset += 4; + break; + default: + status = MLX_INVALID_PARAMETER; + } + if(status != MLX_SUCCESS) { + goto config_error; + } + } +config_error: + return status; +} +mlx_status +mlx_pci_init_priv( + IN mlx_utils *utils + ) +{ + mlx_status status = MLX_SUCCESS; + adjust_pci_device ( utils->pci ); +#ifdef DEVICE_CX3 + utils->config = ioremap ( pci_bar_start ( utils->pci, PCI_BASE_ADDRESS_0), + 0x100000 ); +#endif + return status; +} + +mlx_status +mlx_pci_teardown_priv( + IN mlx_utils *utils __attribute__ ((unused)) + ) +{ + mlx_status status = MLX_SUCCESS; +#ifdef DEVICE_CX3 + iounmap( utils->config ); +#endif + return status; +} + +mlx_status +mlx_pci_read_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + OUT mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + status = mlx_pci_config(utils, TRUE, width, offset, count, buffer); + return status; +} + +mlx_status +mlx_pci_write_priv( + IN mlx_utils *utils, + IN mlx_pci_width width, + IN mlx_uint32 offset, + IN mlx_uintn count, + IN mlx_void *buffer + ) +{ + mlx_status status = MLX_SUCCESS; + status = mlx_pci_config(utils, FALSE, width, offset, count, buffer); + return status; +} + +mlx_status +mlx_pci_mem_read_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_pci_width width __attribute__ ((unused)), + IN mlx_uint8 bar_index __attribute__ ((unused)), + IN mlx_uint64 offset, + IN mlx_uintn count __attribute__ ((unused)), + OUT mlx_void *buffer + ) +{ + if (buffer == NULL || width != MlxPciWidthUint32) + return MLX_INVALID_PARAMETER; + *((mlx_uint32 *)buffer) = readl(offset); + return MLX_SUCCESS; +} + +mlx_status +mlx_pci_mem_write_priv( + IN mlx_utils *utils __attribute__ ((unused)), + IN mlx_pci_width width __attribute__ ((unused)), + IN mlx_uint8 bar_index __attribute__ ((unused)), + IN mlx_uint64 offset, + IN mlx_uintn count __attribute__ ((unused)), + IN mlx_void *buffer + ) +{ + if (buffer == NULL || width != MlxPciWidthUint32) + return MLX_INVALID_PARAMETER; + barrier(); + writel(*((mlx_uint32 *)buffer), offset); + return MLX_SUCCESS; +} diff --git a/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c new file mode 100644 index 000000000..5fca406fc --- /dev/null +++ b/src/drivers/infiniband/mlx_utils_flexboot/src/mlx_utils_priv.c @@ -0,0 +1,83 @@ +/* + * MlxUtilsPriv.c + * + * Created on: Jan 25, 2015 + * Author: maord + */ + +#include +#include +#include +#include "../../mlx_utils/include/private/mlx_utils_priv.h" + +mlx_status +mlx_utils_delay_in_ms_priv( + IN mlx_uint32 msecs + ) +{ + mdelay(msecs); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_delay_in_us_priv( + IN mlx_uint32 usecs + ) +{ + udelay(usecs); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_ilog2_priv( + IN mlx_uint32 i, + OUT mlx_uint32 *log + ) +{ + *log = ( fls ( i ) - 1 ); + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_init_lock_priv( + OUT void **lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_free_lock_priv( + IN void *lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_acquire_lock_priv ( + IN void *lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_release_lock_priv ( + IN void *lock __unused + ) +{ + return MLX_SUCCESS; +} + +mlx_status +mlx_utils_rand_priv ( + IN mlx_utils *utils __unused, + OUT mlx_uint32 *rand_num + ) +{ + do { + *rand_num = rand(); + } while ( *rand_num == 0 ); + return MLX_SUCCESS; +} diff --git a/src/drivers/infiniband/nodnic_prm.h b/src/drivers/infiniband/nodnic_prm.h new file mode 100644 index 000000000..5e0fa9890 --- /dev/null +++ b/src/drivers/infiniband/nodnic_prm.h @@ -0,0 +1,47 @@ +/* + * 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 ); + +#ifndef SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_PRM_H_ +#define SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_PRM_H_ + +#include "mlx_bitops.h" + +struct nodnic_wqe_segment_data_ptr_st { /* Little Endian */ + pseudo_bit_t byte_count[0x0001f]; + pseudo_bit_t always0[0x00001]; +/* -------------- */ + pseudo_bit_t l_key[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_h[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_l[0x00020]; +/* -------------- */ +}; + +struct MLX_DECLARE_STRUCT ( nodnic_wqe_segment_data_ptr ); + +#define HERMON_MAX_SCATTER 1 + +struct nodnic_recv_wqe { + struct nodnic_wqe_segment_data_ptr data[HERMON_MAX_SCATTER]; +} __attribute__ (( packed )); + +#endif /* SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_PRM_H_ */ diff --git a/src/drivers/infiniband/nodnic_shomron_prm.h b/src/drivers/infiniband/nodnic_shomron_prm.h new file mode 100644 index 000000000..85cd97187 --- /dev/null +++ b/src/drivers/infiniband/nodnic_shomron_prm.h @@ -0,0 +1,143 @@ +/* + * 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 ); + +#ifndef SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_SHOMRON_PRM_H_ +#define SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_SHOMRON_PRM_H_ + + + +#include "nodnic_prm.h" + + +#define SHOMRON_MAX_GATHER 1 + +/* Send wqe segment ctrl */ + +struct shomronprm_wqe_segment_ctrl_send_st { /* Little Endian */ + pseudo_bit_t opcode[0x00008]; + pseudo_bit_t wqe_index[0x00010]; + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t ds[0x00006]; /* descriptor (wqe) size in 16bytes chunk */ + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t qpn[0x00018]; +/* -------------- */ + pseudo_bit_t reserved3[0x00002]; + pseudo_bit_t ce[0x00002]; + pseudo_bit_t reserved4[0x0001c]; +/* -------------- */ + pseudo_bit_t reserved5[0x00040]; +/* -------------- */ + pseudo_bit_t mss[0x0000e]; + pseudo_bit_t reserved6[0x0000e]; + pseudo_bit_t cs13_inner[0x00001]; + pseudo_bit_t cs14_inner[0x00001]; + pseudo_bit_t cs13[0x00001]; + pseudo_bit_t cs14[0x00001]; +/* -------------- */ + pseudo_bit_t reserved7[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers1[0x00010]; + pseudo_bit_t inline_headers_size[0x0000a]; //sum size of inline_hdr1+inline_hdrs (0x10) + pseudo_bit_t reserved8[0x00006]; +/* -------------- */ + pseudo_bit_t inline_headers2[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers3[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers4[0x00020]; +/* -------------- */ + pseudo_bit_t inline_headers5[0x00020]; +}; + + + +/* Completion Queue Entry Format #### michal - fixed by gdror */ + +struct shomronprm_completion_queue_entry_st { /* Little Endian */ + + pseudo_bit_t reserved1[0x00080]; +/* -------------- */ + pseudo_bit_t reserved2[0x00010]; + pseudo_bit_t ml_path[0x00007]; + pseudo_bit_t reserved3[0x00009]; +/* -------------- */ + pseudo_bit_t slid[0x00010]; + pseudo_bit_t reserved4[0x00010]; +/* -------------- */ + pseudo_bit_t rqpn[0x00018]; + pseudo_bit_t sl[0x00004]; + pseudo_bit_t l3_hdr[0x00002]; + pseudo_bit_t reserved5[0x00002]; +/* -------------- */ + pseudo_bit_t reserved10[0x00020]; +/* -------------- */ + pseudo_bit_t srqn[0x00018]; + pseudo_bit_t reserved11[0x0008]; +/* -------------- */ + pseudo_bit_t pkey_index[0x00020]; +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t byte_cnt[0x00020]; +/* -------------- */ + pseudo_bit_t reserved7[0x00040]; +/* -------------- */ + pseudo_bit_t qpn[0x00018]; + pseudo_bit_t rx_drop_counter[0x00008]; +/* -------------- */ + pseudo_bit_t owner[0x00001]; + pseudo_bit_t reserved8[0x00003]; + pseudo_bit_t opcode[0x00004]; + pseudo_bit_t reserved9[0x00008]; + pseudo_bit_t wqe_counter[0x00010]; +}; + + +/* Completion with Error CQE #### michal - gdror fixed */ + +struct shomronprm_completion_with_error_st { /* Little Endian */ + pseudo_bit_t reserved1[0x001a0]; + /* -------------- */ + pseudo_bit_t syndrome[0x00008]; + pseudo_bit_t vendor_error_syndrome[0x00008]; + pseudo_bit_t reserved2[0x00010]; + /* -------------- */ + pseudo_bit_t reserved3[0x00040]; +}; + + +struct MLX_DECLARE_STRUCT ( shomronprm_wqe_segment_ctrl_send ); +struct MLX_DECLARE_STRUCT ( shomronprm_completion_queue_entry ); +struct MLX_DECLARE_STRUCT ( shomronprm_completion_with_error ); + +struct shomron_nodnic_eth_send_wqe { + struct shomronprm_wqe_segment_ctrl_send ctrl; + struct nodnic_wqe_segment_data_ptr data[SHOMRON_MAX_GATHER]; +} __attribute__ (( packed )); + +union shomronprm_completion_entry { + struct shomronprm_completion_queue_entry normal; + struct shomronprm_completion_with_error error; +} __attribute__ (( packed )); + + +#endif /* SRC_DRIVERS_INFINIBAND_MLX_NODNIC_INCLUDE_PRM_NODNIC_SHOMRON_PRM_H_ */ diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c index e22f2349a..18011c19a 100644 --- a/src/drivers/infiniband/qib7322.c +++ b/src/drivers/infiniband/qib7322.c @@ -137,32 +137,21 @@ struct qib7322 { * This card requires atomic 64-bit accesses. Strange things happen * if you try to use 32-bit accesses; sometimes they work, sometimes * they don't, sometimes you get random data. - * - * These accessors use the "movq" MMX instruction, and so won't work - * on really old Pentiums (which won't have PCIe anyway, so this is - * something of a moot point). */ /** * Read QIB7322 qword register * * @v qib7322 QIB7322 device - * @v dwords Register buffer to read into + * @v qword Register buffer to read into * @v offset Register offset */ -static void qib7322_readq ( struct qib7322 *qib7322, uint32_t *dwords, +static void qib7322_readq ( struct qib7322 *qib7322, uint64_t *qword, unsigned long offset ) { - void *addr = ( qib7322->regs + offset ); - - __asm__ __volatile__ ( "movq (%1), %%mm0\n\t" - "movq %%mm0, (%0)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); - - DBGIO ( "[%08lx] => %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); + *qword = readq ( qib7322->regs + offset ); } #define qib7322_readq( _qib7322, _ptr, _offset ) \ - qib7322_readq ( (_qib7322), (_ptr)->u.dwords, (_offset) ) + qib7322_readq ( (_qib7322), (_ptr)->u.qwords, (_offset) ) #define qib7322_readq_array8b( _qib7322, _ptr, _offset, _idx ) \ qib7322_readq ( (_qib7322), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define qib7322_readq_array64k( _qib7322, _ptr, _offset, _idx ) \ @@ -174,22 +163,15 @@ static void qib7322_readq ( struct qib7322 *qib7322, uint32_t *dwords, * Write QIB7322 qword register * * @v qib7322 QIB7322 device - * @v dwords Register buffer to write + * @v qword Register buffer to write * @v offset Register offset */ -static void qib7322_writeq ( struct qib7322 *qib7322, const uint32_t *dwords, +static void qib7322_writeq ( struct qib7322 *qib7322, const uint64_t *qword, unsigned long offset ) { - void *addr = ( qib7322->regs + offset ); - - DBGIO ( "[%08lx] <= %08x%08x\n", - virt_to_phys ( addr ), dwords[1], dwords[0] ); - - __asm__ __volatile__ ( "movq (%0), %%mm0\n\t" - "movq %%mm0, (%1)\n\t" - : : "r" ( dwords ), "r" ( addr ) : "memory" ); + writeq ( *qword, ( qib7322->regs + offset ) ); } #define qib7322_writeq( _qib7322, _ptr, _offset ) \ - qib7322_writeq ( (_qib7322), (_ptr)->u.dwords, (_offset) ) + qib7322_writeq ( (_qib7322), (_ptr)->u.qwords, (_offset) ) #define qib7322_writeq_array8b( _qib7322, _ptr, _offset, _idx ) \ qib7322_writeq ( (_qib7322), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) #define qib7322_writeq_array64k( _qib7322, _ptr, _offset, _idx ) \ @@ -693,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 ) ); @@ -1525,8 +1507,15 @@ static void qib7322_complete_recv ( struct ib_device *ibdev, /* Completing the eager buffer described in * this header entry. */ - iob_put ( iobuf, payload_len ); - rc = ( err ? -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + if ( payload_len <= iob_tailroom ( iobuf ) ) { + iob_put ( iobuf, payload_len ); + rc = ( err ? + -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + } else { + DBGC ( qib7322, "QIB7322 %p bad payload len " + "%zd\n", qib7322, payload_len ); + rc = -EPROTO; + } /* Redirect to target QP if necessary */ if ( qp != intended_qp ) { DBGC2 ( qib7322, "QIB7322 %p redirecting QPN " @@ -1537,7 +1526,7 @@ static void qib7322_complete_recv ( struct ib_device *ibdev, intended_qp->recv.fill++; } ib_complete_recv ( ibdev, intended_qp, &dest, &source, - iobuf, rc); + iobuf, rc ); } else { /* Completing on a skipped-over eager buffer */ ib_complete_recv ( ibdev, qp, &dest, &source, iobuf, @@ -2307,7 +2296,7 @@ static int qib7322_probe ( struct pci_device *pci ) { /* Fix up PCI device */ adjust_pci_device ( pci ); - /* Get PCI BARs */ + /* Map PCI BARs */ qib7322->regs = ioremap ( pci->membase, QIB7322_BAR0_SIZE ); DBGC2 ( qib7322, "QIB7322 %p has BAR at %08lx\n", qib7322, pci->membase ); @@ -2402,6 +2391,7 @@ static int qib7322_probe ( struct pci_device *pci ) { err_init_recv: err_read_eeprom: err_init_i2c: + iounmap ( qib7322->regs ); free ( qib7322 ); err_alloc_qib7322: return rc; @@ -2424,6 +2414,7 @@ static void qib7322_remove ( struct pci_device *pci ) { ibdev_put ( qib7322->ibdev[i] ); qib7322_fini_send ( qib7322 ); qib7322_fini_recv ( qib7322 ); + iounmap ( qib7322->regs ); free ( qib7322 ); } diff --git a/src/drivers/infiniband/qib7322.h b/src/drivers/infiniband/qib7322.h index 72797b240..dab95cfc0 100644 --- a/src/drivers/infiniband/qib7322.h +++ b/src/drivers/infiniband/qib7322.h @@ -33,8 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#define BITOPS_LITTLE_ENDIAN -#include +#define PSEUDOBIT_LITTLE_ENDIAN +#include #include "qib_7322_regs.h" /** A QIB7322 GPIO register */ diff --git a/src/drivers/linux/af_packet.c b/src/drivers/linux/af_packet.c new file mode 100644 index 000000000..65aafc5b1 --- /dev/null +++ b/src/drivers/linux/af_packet.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2016 David Decotigny + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ +#define _SYS_SOCKET_H +#define __GLIBC__ 2 +#include +#include +#include +#include +#undef __GLIBC__ +#include + +/* linux-specifc syscall params */ +#define LINUX_AF_PACKET 17 +#define LINUX_SOCK_RAW 3 +#define LINUX_SIOCGIFINDEX 0x8933 +#define LINUX_SIOCGIFHWADDR 0x8927 + +#define RX_BUF_SIZE 1536 + +/** @file + * + * The AF_PACKET driver. + * + * Bind to an existing linux network interface. + */ + +struct af_packet_nic { + /** Linux network interface name */ + char * ifname; + /** Packet socket descriptor */ + int fd; + /** ifindex */ + int ifindex; +}; + +/** Open the linux interface */ +static int af_packet_nic_open ( struct net_device * netdev ) +{ + struct af_packet_nic * nic = netdev->priv; + struct sockaddr_ll socket_address; + struct ifreq if_data; + int ret; + + nic->fd = linux_socket(LINUX_AF_PACKET, LINUX_SOCK_RAW, + htons(ETH_P_ALL)); + if (nic->fd < 0) { + DBGC(nic, "af_packet %p socket(AF_PACKET) = %d (%s)\n", + nic, nic->fd, linux_strerror(linux_errno)); + return nic->fd; + } + + /* resolve ifindex of ifname */ + memset(&if_data, 0, sizeof(if_data)); + strncpy(if_data.ifr_name, nic->ifname, sizeof(if_data.ifr_name)); + ret = linux_ioctl(nic->fd, LINUX_SIOCGIFINDEX, &if_data); + if (ret < 0) { + DBGC(nic, "af_packet %p ioctl(SIOCGIFINDEX) = %d (%s)\n", + nic, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + nic->ifindex = if_data.ifr_ifindex; + + /* bind to interface */ + memset(&socket_address, 0, sizeof(socket_address)); + socket_address.sll_family = LINUX_AF_PACKET; + socket_address.sll_ifindex = nic->ifindex; + socket_address.sll_protocol = htons(ETH_P_ALL); + ret = linux_bind(nic->fd, (void *) &socket_address, + sizeof(socket_address)); + if (ret == -1) { + DBGC(nic, "af_packet %p bind() = %d (%s)\n", + nic, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + /* Set nonblocking mode to make af_packet_nic_poll() easier */ + ret = linux_fcntl(nic->fd, F_SETFL, O_NONBLOCK); + if (ret != 0) { + DBGC(nic, "af_packet %p fcntl(%d, ...) = %d (%s)\n", + nic, nic->fd, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + return 0; +} + +/** Close the packet socket */ +static void af_packet_nic_close ( struct net_device *netdev ) +{ + struct af_packet_nic * nic = netdev->priv; + linux_close(nic->fd); +} + +/** + * Transmit an ethernet packet. + * + * The packet can be written to the socket and marked as complete immediately. + */ +static int af_packet_nic_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) +{ + struct af_packet_nic * nic = netdev->priv; + struct sockaddr_ll socket_address; + const struct ethhdr * eh; + int rc; + + memset(&socket_address, 0, sizeof(socket_address)); + socket_address.sll_family = LINUX_AF_PACKET; + socket_address.sll_ifindex = nic->ifindex; + socket_address.sll_halen = ETH_ALEN; + + eh = iobuf->data; + memcpy(socket_address.sll_addr, eh->h_dest, ETH_ALEN); + + rc = linux_sendto(nic->fd, iobuf->data, iobuf->tail - iobuf->data, + 0, (struct sockaddr *)&socket_address, + sizeof(socket_address)); + + DBGC2(nic, "af_packet %p wrote %d bytes\n", nic, rc); + netdev_tx_complete(netdev, iobuf); + + return 0; +} + +/** Poll for new packets */ +static void af_packet_nic_poll ( struct net_device *netdev ) +{ + struct af_packet_nic * nic = netdev->priv; + struct pollfd pfd; + struct io_buffer * iobuf; + int r; + + pfd.fd = nic->fd; + pfd.events = POLLIN; + if (linux_poll(&pfd, 1, 0) == -1) { + DBGC(nic, "af_packet %p poll failed (%s)\n", + nic, linux_strerror(linux_errno)); + return; + } + if ((pfd.revents & POLLIN) == 0) + return; + + /* At this point we know there is at least one new packet to be read */ + + iobuf = alloc_iob(RX_BUF_SIZE); + if (! iobuf) + goto allocfail; + + while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) { + DBGC2(nic, "af_packet %p read %d bytes\n", nic, r); + + iob_put(iobuf, r); + netdev_rx(netdev, iobuf); + + iobuf = alloc_iob(RX_BUF_SIZE); + if (! iobuf) + goto allocfail; + } + + free_iob(iobuf); + return; + +allocfail: + DBGC(nic, "af_packet %p alloc_iob failed\n", nic); +} + +/** + * Set irq. + * + * Not used on linux, provide a dummy implementation. + */ +static void af_packet_nic_irq ( struct net_device *netdev, int enable ) +{ + struct af_packet_nic *nic = netdev->priv; + + DBGC(nic, "af_packet %p irq enable = %d\n", nic, enable); +} + + +static int af_packet_update_properties ( struct net_device *netdev ) +{ + struct af_packet_nic *nic = netdev->priv; + struct ifreq if_data; + int ret; + + /* retrieve default MAC address */ + int fd = linux_socket(LINUX_AF_PACKET, LINUX_SOCK_RAW, 0); + if (fd < 0) { + DBGC(nic, "af_packet %p cannot create raw socket (%s)\n", + nic, linux_strerror(linux_errno)); + return fd; + } + + /* retrieve host's MAC address */ + memset(&if_data, 0, sizeof(if_data)); + strncpy(if_data.ifr_name, nic->ifname, sizeof(if_data.ifr_name)); + ret = linux_ioctl(fd, LINUX_SIOCGIFHWADDR, &if_data); + if (ret < 0) { + DBGC(nic, "af_packet %p cannot get mac addr (%s)\n", + nic, linux_strerror(linux_errno)); + linux_close(fd); + return ret; + } + + linux_close(fd); + /* struct sockaddr = { u16 family, u8 pad[14] (equiv. sa_data) }; */ + memcpy(netdev->ll_addr, if_data.ifr_hwaddr.pad, ETH_ALEN); + return 0; +} + +/** AF_PACKET operations */ +static struct net_device_operations af_packet_nic_operations = { + .open = af_packet_nic_open, + .close = af_packet_nic_close, + .transmit = af_packet_nic_transmit, + .poll = af_packet_nic_poll, + .irq = af_packet_nic_irq, +}; + +/** Handle a device request for the af_packet driver */ +static int af_packet_nic_probe ( struct linux_device *device, + struct linux_device_request *request ) +{ + struct linux_setting *if_setting; + struct net_device *netdev; + struct af_packet_nic *nic; + int rc; + + netdev = alloc_etherdev(sizeof(*nic)); + if (! netdev) + return -ENOMEM; + + netdev_init(netdev, &af_packet_nic_operations); + nic = netdev->priv; + linux_set_drvdata(device, netdev); + netdev->dev = &device->dev; + + memset(nic, 0, sizeof(*nic)); + + /* Look for the mandatory if setting */ + if_setting = linux_find_setting("if", &request->settings); + + /* No if setting */ + if (! if_setting) { + printf("af_packet missing a mandatory if setting\n"); + rc = -EINVAL; + goto err_settings; + } + + nic->ifname = if_setting->value; + snprintf ( device->dev.name, sizeof ( device->dev.name ), "%s", + nic->ifname ); + device->dev.desc.bus_type = BUS_TYPE_TAP; + af_packet_update_properties(netdev); + if_setting->applied = 1; + + /* Apply rest of the settings */ + linux_apply_settings(&request->settings, &netdev->settings.settings); + + /* Register network device */ + if ((rc = register_netdev(netdev)) != 0) + goto err_register; + + netdev_link_up(netdev); + + return 0; + +err_settings: + unregister_netdev(netdev); +err_register: + netdev_nullify(netdev); + netdev_put(netdev); + return rc; +} + +/** Remove the device */ +static void af_packet_nic_remove ( struct linux_device *device ) +{ + struct net_device *netdev = linux_get_drvdata(device); + unregister_netdev(netdev); + netdev_nullify(netdev); + netdev_put(netdev); +} + +/** AF_PACKET linux_driver */ +struct linux_driver af_packet_nic_driver __linux_driver = { + .name = "af_packet", + .probe = af_packet_nic_probe, + .remove = af_packet_nic_remove, + .can_probe = 1, +}; diff --git a/src/drivers/linux/tap.c b/src/drivers/linux/tap.c index 979436654..db3b7955b 100644 --- a/src/drivers/linux/tap.c +++ b/src/drivers/linux/tap.c @@ -31,6 +31,7 @@ #include /* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ +#define _SYS_SOCKET_H #define __GLIBC__ 2 #include #undef __GLIBC__ @@ -39,6 +40,7 @@ #include #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); diff --git a/src/drivers/net/3c595.c b/src/drivers/net/3c595.c index 2338c54b7..92d38cfc5 100644 --- a/src/drivers/net/3c595.c +++ b/src/drivers/net/3c595.c @@ -391,7 +391,7 @@ vxsetlink(void) { int i, j; char *reason, *warning; - static char prev_conn = -1; + static signed char prev_conn = -1; if (prev_conn == -1) { prev_conn = vx_connector; diff --git a/src/drivers/net/3c5x9.c b/src/drivers/net/3c5x9.c index 4d9bc8d9e..d7c09f77c 100644 --- a/src/drivers/net/3c5x9.c +++ b/src/drivers/net/3c5x9.c @@ -108,7 +108,7 @@ static void t509_enable ( struct nic *nic ) { else if (connector == utp) { GO_WINDOW(nic->ioaddr,4); outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE); - sleep(2); /* Give time for media to negotiate */ + mdelay(2000); /* Give time for media to negotiate */ GO_WINDOW(nic->ioaddr,1); } diff --git a/src/drivers/net/acm.c b/src/drivers/net/acm.c new file mode 100644 index 000000000..16dab4be8 --- /dev/null +++ b/src/drivers/net/acm.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "acm.h" + +/** @file + * + * USB RNDIS driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler acm_intr_profiler __profiler = + { .name = "acm.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler acm_in_profiler __profiler = + { .name = "acm.in" }; + +/** Bulk OUT profiler */ +static struct profiler acm_out_profiler __profiler = + { .name = "acm.out" }; + +/****************************************************************************** + * + * USB RNDIS communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void acm_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct acm_device *acm = container_of ( ep, struct acm_device, + usbnet.intr ); + struct rndis_device *rndis = acm->rndis; + struct usb_setup_packet *message; + + /* Profile completions */ + profile_start ( &acm_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Drop packets with errors */ + if ( rc != 0 ) { + DBGC ( acm, "ACM %p interrupt failed: %s\n", + acm, strerror ( rc ) ); + DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( iob_len ( iobuf ) < sizeof ( *message ) ) { + DBGC ( acm, "ACM %p underlength interrupt:\n", acm ); + DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + message = iobuf->data; + + /* Parse message header */ + switch ( message->request ) { + + case cpu_to_le16 ( CDC_RESPONSE_AVAILABLE ) : + case cpu_to_le16 ( 0x0001 ) : /* qemu seems to use this value */ + acm->responded = 1; + break; + + default: + DBGC ( acm, "ACM %p unrecognised interrupt:\n", acm ); + DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -ENOTSUP; + goto error; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &acm_intr_profiler ); + + return; + + error: + rndis_rx_err ( rndis, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations acm_intr_operations = { + .complete = acm_intr_complete, +}; + +/****************************************************************************** + * + * USB RNDIS data interface + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void acm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct acm_device *acm = container_of ( ep, struct acm_device, + usbnet.in ); + struct rndis_device *rndis = acm->rndis; + + /* Profile receive completions */ + profile_start ( &acm_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the RNDIS device */ + if ( rc != 0 ) { + DBGC ( acm, "ACM %p bulk IN failed: %s\n", + acm, strerror ( rc ) ); + goto error; + } + + /* Hand off to RNDIS */ + rndis_rx ( rndis, iob_disown ( iobuf ) ); + + profile_stop ( &acm_in_profiler ); + return; + + error: + rndis_rx_err ( rndis, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations acm_in_operations = { + .complete = acm_in_complete, +}; + +/** + * Transmit packet + * + * @v acm USB RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int acm_out_transmit ( struct acm_device *acm, + struct io_buffer *iobuf ) { + int rc; + + /* Profile transmissions */ + profile_start ( &acm_out_profiler ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &acm->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &acm_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void acm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct acm_device *acm = container_of ( ep, struct acm_device, + usbnet.out ); + struct rndis_device *rndis = acm->rndis; + + /* Report TX completion */ + rndis_tx_complete_err ( rndis, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations acm_out_operations = { + .complete = acm_out_complete, +}; + +/****************************************************************************** + * + * USB RNDIS control interface + * + ****************************************************************************** + */ + +/** + * Send control packet + * + * @v acm USB RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int acm_control_transmit ( struct acm_device *acm, + struct io_buffer *iobuf ) { + struct rndis_device *rndis = acm->rndis; + struct usb_device *usb = acm->usb; + int rc; + + /* Send packet as an encapsulated command */ + if ( ( rc = cdc_send_encapsulated_command ( usb, acm->usbnet.comms, + iobuf->data, + iob_len ( iobuf ) ) ) != 0){ + DBGC ( acm, "ACM %p could not send encapsulated command: %s\n", + acm, strerror ( rc ) ); + return rc; + } + + /* Complete packet immediately */ + rndis_tx_complete ( rndis, iobuf ); + + return 0; +} + +/** + * Receive control packet + * + * @v acm USB RNDIS device + * @ret rc Return status code + */ +static int acm_control_receive ( struct acm_device *acm ) { + struct rndis_device *rndis = acm->rndis; + struct usb_device *usb = acm->usb; + struct io_buffer *iobuf; + struct rndis_header *header; + size_t mtu = ACM_RESPONSE_MTU; + size_t len; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( mtu ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get encapsulated response */ + if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms, + iobuf->data, mtu ) ) != 0 ){ + DBGC ( acm, "ACM %p could not get encapsulated response: %s\n", + acm, strerror ( rc ) ); + goto err_get_response; + } + + /* Fix up buffer length */ + header = iobuf->data; + len = le32_to_cpu ( header->len ); + if ( len > mtu ) { + DBGC ( acm, "ACM %p overlength encapsulated response\n", acm ); + DBGC_HDA ( acm, 0, iobuf->data, mtu ); + rc = -EPROTO; + goto err_len; + } + iob_put ( iobuf, len ); + + /* Hand off to RNDIS */ + rndis_rx ( rndis, iob_disown ( iobuf ) ); + + return 0; + + err_len: + err_get_response: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/****************************************************************************** + * + * RNDIS interface + * + ****************************************************************************** + */ + +/** + * Open RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int acm_open ( struct rndis_device *rndis ) { + struct acm_device *acm = rndis->priv; + int rc; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &acm->usbnet ) ) != 0 ) + goto err_open; + + return 0; + + usbnet_close ( &acm->usbnet ); + err_open: + return rc; +} + +/** + * Close RNDIS device + * + * @v rndis RNDIS device + */ +static void acm_close ( struct rndis_device *rndis ) { + struct acm_device *acm = rndis->priv; + + /* Close USB network device */ + usbnet_close ( &acm->usbnet ); +} + +/** + * Transmit packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int acm_transmit ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct acm_device *acm = rndis->priv; + struct rndis_header *header = iobuf->data; + + /* Sanity check */ + assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); + assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); + + /* Transmit packet via appropriate mechanism */ + if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) { + return acm_out_transmit ( acm, iobuf ); + } else { + return acm_control_transmit ( acm, iobuf ); + } +} + +/** + * Poll for completed and received packets + * + * @v rndis RNDIS device + */ +static void acm_poll ( struct rndis_device *rndis ) { + struct acm_device *acm = rndis->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( acm->bus ); + + /* Refill rings */ + if ( ( rc = usbnet_refill ( &acm->usbnet ) ) != 0 ) + rndis_rx_err ( rndis, NULL, rc ); + + /* Retrieve encapsulated response, if applicable */ + if ( acm->responded ) { + + /* Clear flag */ + acm->responded = 0; + + /* Get encapsulated response */ + if ( ( rc = acm_control_receive ( acm ) ) != 0 ) + rndis_rx_err ( rndis, NULL, rc ); + } +} + +/** USB RNDIS operations */ +static struct rndis_operations acm_operations = { + .open = acm_open, + .close = acm_close, + .transmit = acm_transmit, + .poll = acm_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int acm_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct rndis_device *rndis; + struct acm_device *acm; + int rc; + + /* Allocate and initialise structure */ + rndis = alloc_rndis ( sizeof ( *acm ) ); + if ( ! rndis ) { + rc = -ENOMEM; + goto err_alloc; + } + rndis_init ( rndis, &acm_operations ); + rndis->netdev->dev = &func->dev; + acm = rndis->priv; + acm->usb = usb; + acm->bus = usb->port->hub->bus; + acm->rndis = rndis; + usbnet_init ( &acm->usbnet, func, &acm_intr_operations, + &acm_in_operations, &acm_out_operations ); + usb_refill_init ( &acm->usbnet.intr, 0, 0, ACM_INTR_MAX_FILL ); + usb_refill_init ( &acm->usbnet.in, 0, ACM_IN_MTU, ACM_IN_MAX_FILL ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &acm->usbnet, config ) ) != 0 ) { + DBGC ( acm, "ACM %p could not describe: %s\n", + acm, strerror ( rc ) ); + goto err_describe; + } + + /* Register RNDIS device */ + if ( ( rc = register_rndis ( rndis ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, acm ); + return 0; + + unregister_rndis ( rndis ); + err_register: + err_describe: + free_rndis ( rndis ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void acm_remove ( struct usb_function *func ) { + struct acm_device *acm = usb_func_get_drvdata ( func ); + struct rndis_device *rndis = acm->rndis; + + /* Unregister RNDIS device */ + unregister_rndis ( rndis ); + + /* Free RNDIS device */ + free_rndis ( rndis ); +} + +/** USB CDC-ACM device IDs */ +static struct usb_device_id cdc_acm_ids[] = { + { + .name = "cdc-acm", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + }, +}; + +/** USB CDC-ACM driver */ +struct usb_driver cdc_acm_driver __usb_driver = { + .ids = cdc_acm_ids, + .id_count = ( sizeof ( cdc_acm_ids ) / sizeof ( cdc_acm_ids[0] ) ), + .class = USB_CLASS_ID ( USB_CLASS_CDC, USB_SUBCLASS_CDC_ACM, + USB_PROTOCOL_ACM_RNDIS ), + .score = USB_SCORE_DEPRECATED, + .probe = acm_probe, + .remove = acm_remove, +}; + +/** USB RF-RNDIS device IDs */ +static struct usb_device_id rf_rndis_ids[] = { + { + .name = "rf-rndis", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + }, +}; + +/** USB RF-RNDIS driver */ +struct usb_driver rf_rndis_driver __usb_driver = { + .ids = rf_rndis_ids, + .id_count = ( sizeof ( rf_rndis_ids ) / sizeof ( rf_rndis_ids[0] ) ), + .class = USB_CLASS_ID ( USB_CLASS_WIRELESS, USB_SUBCLASS_WIRELESS_RADIO, + USB_PROTOCOL_RADIO_RNDIS ), + .score = USB_SCORE_DEPRECATED, + .probe = acm_probe, + .remove = acm_remove, +}; diff --git a/src/drivers/net/acm.h b/src/drivers/net/acm.h new file mode 100644 index 000000000..d4944967b --- /dev/null +++ b/src/drivers/net/acm.h @@ -0,0 +1,69 @@ +#ifndef _ACM_H +#define _ACM_H + +/** @file + * + * USB RNDIS Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** CDC-ACM subclass */ +#define USB_SUBCLASS_CDC_ACM 0x02 + +/** CDC-ACM RNDIS device protocol */ +#define USB_PROTOCOL_ACM_RNDIS 0xff + +/** Class code for wireless devices */ +#define USB_CLASS_WIRELESS 0xe0 + +/** Radio frequency device subclass */ +#define USB_SUBCLASS_WIRELESS_RADIO 0x01 + +/** Radio frequency RNDIS device protocol */ +#define USB_PROTOCOL_RADIO_RNDIS 0x03 + +/** A USB RNDIS network device */ +struct acm_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** RNDIS device */ + struct rndis_device *rndis; + /** USB network device */ + struct usbnet_device usbnet; + + /** An encapsulated response is available */ + int responded; +}; + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define ACM_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define ACM_IN_MAX_FILL 8 + +/** Bulk IN buffer size + * + * This is a policy decision. + */ +#define ACM_IN_MTU 2048 + +/** Encapsulated response buffer size + * + * This is a policy decision. + */ +#define ACM_RESPONSE_MTU 128 + +#endif /* _ACM_H */ diff --git a/src/drivers/net/ath/ath.h b/src/drivers/net/ath/ath.h index 42ad59f78..589bb5634 100644 --- a/src/drivers/net/ath/ath.h +++ b/src/drivers/net/ath/ath.h @@ -26,7 +26,6 @@ FILE_LICENCE ( BSD2 ); #include /* 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)) @@ -101,8 +100,6 @@ static inline u32 get_unaligned_le32(const void *p) */ #define ATH_KEYMAX 128 /* max key cache size we handle */ -static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - struct ath_ani { int caldone; unsigned int longcal_timer; @@ -229,10 +226,6 @@ struct ath_common { int btcoex_enabled; }; -struct io_buffer *ath_rxbuf_alloc(struct ath_common *common, - u32 len, - u32 *iob_addr); - void ath_hw_setbssidmask(struct ath_common *common); int ath_hw_keyreset(struct ath_common *common, u16 entry); void ath_hw_cycle_counters_update(struct ath_common *common); diff --git a/src/drivers/net/ath/ath5k/ath5k.c b/src/drivers/net/ath/ath5k/ath5k.c index 92c4ffdf4..a500175a7 100644 --- a/src/drivers/net/ath/ath5k/ath5k.c +++ b/src/drivers/net/ath/ath5k/ath5k.c @@ -85,46 +85,6 @@ static struct pci_device_id ath5k_nics[] = { PCI_ROM(0x168c, 0x001d, "ath2417", "Atheros 2417 Nala", AR5K_AR5212), }; -/* Known SREVs */ -static const struct ath5k_srev_name srev_names[] = { - { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 }, - { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 }, - { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A }, - { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B }, - { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 }, - { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 }, - { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 }, - { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A }, - { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 }, - { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 }, - { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 }, - { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 }, - { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 }, - { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 }, - { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 }, - { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 }, - { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 }, - { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 }, - { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN }, - { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 }, - { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 }, - { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A }, - { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 }, - { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 }, - { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A }, - { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B }, - { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 }, - { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A }, - { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B }, - { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 }, - { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 }, - { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 }, - { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 }, - { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 }, - { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 }, - { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, -}; - #define ATH5K_SPMBL_NO 1 #define ATH5K_SPMBL_YES 2 #define ATH5K_SPMBL_BOTH 3 @@ -1421,7 +1381,7 @@ ath5k_poll(struct net80211_device *dev) unsigned int counter = 1000; if (currticks() - sc->last_calib_ticks > - ATH5K_CALIB_INTERVAL * ticks_per_sec()) { + ATH5K_CALIB_INTERVAL * TICKS_PER_SEC) { ath5k_calibrate(sc); sc->last_calib_ticks = currticks(); } diff --git a/src/drivers/net/ath/ath5k/ath5k.h b/src/drivers/net/ath/ath5k/ath5k.h index 30e2024c6..fa62e8ce5 100644 --- a/src/drivers/net/ath/ath5k/ath5k.h +++ b/src/drivers/net/ath/ath5k/ath5k.h @@ -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" diff --git a/src/drivers/net/ath/ath5k/ath5k_desc.c b/src/drivers/net/ath/ath5k/ath5k_desc.c index 30fe1c777..816d26ede 100644 --- a/src/drivers/net/ath/ath5k/ath5k_desc.c +++ b/src/drivers/net/ath/ath5k/ath5k_desc.c @@ -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 |= diff --git a/src/drivers/net/ath/ath5k/ath5k_phy.c b/src/drivers/net/ath/ath5k/ath5k_phy.c index 7891d39ea..c2a66a4d3 100644 --- a/src/drivers/net/ath/ath5k/ath5k_phy.c +++ b/src/drivers/net/ath/ath5k/ath5k_phy.c @@ -1219,12 +1219,12 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, /* Update radio registers */ ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | - AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); + AR5K_REG_SM(-1U, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | AR5K_PHY_AGCCOARSE_LO)) | - AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | - AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); + AR5K_REG_SM(-1U, AR5K_PHY_AGCCOARSE_HI) | + AR5K_REG_SM(-127U, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | AR5K_PHY_ADCSAT_THR)) | diff --git a/src/drivers/net/ath/ath5k/ath5k_reset.c b/src/drivers/net/ath/ath5k/ath5k_reset.c index 2f36a4e9a..73765a7b0 100644 --- a/src/drivers/net/ath/ath5k/ath5k_reset.c +++ b/src/drivers/net/ath/ath5k/ath5k_reset.c @@ -134,14 +134,6 @@ static int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, return 0; } - -/* - * index into rates for control rates, we can set it up like this because - * this is only used for AR5212 and we know it supports G mode - */ -static const unsigned int control_rates[] = - { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 }; - /** * ath5k_hw_write_rate_duration - fill rate code to duration table * diff --git a/src/drivers/net/ath/ath9k/ar9002_initvals.h b/src/drivers/net/ath/ath9k/ar9002_initvals.h index d7a5ac09f..f9a92c9b7 100644 --- a/src/drivers/net/ath/ath9k/ar9002_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9002_initvals.h @@ -16,7 +16,7 @@ FILE_LICENCE ( BSD2 ); -static const u32 ar9280Modes_9280_2[][6] = { +static __unused const u32 ar9280Modes_9280_2[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180}, @@ -65,7 +65,7 @@ static const u32 ar9280Modes_9280_2[][6] = { {0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000}, }; -static const u32 ar9280Common_9280_2[][2] = { +static __unused const u32 ar9280Common_9280_2[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, @@ -409,7 +409,7 @@ static const u32 ar9280Common_9280_2[][2] = { {0x00007898, 0x2a850160}, }; -static const u32 ar9280Modes_fast_clock_9280_2[][3] = { +static __unused const u32 ar9280Modes_fast_clock_9280_2[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -426,7 +426,7 @@ static const u32 ar9280Modes_fast_clock_9280_2[][3] = { {0x00009918, 0x0000000b, 0x00000016}, }; -static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { +static __unused const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290}, {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304}, @@ -559,7 +559,7 @@ static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = { {0x0000a848, 0x00001066, 0x00001066, 0x00001055, 0x00001055, 0x00001055}, }; -static const u32 ar9280Modes_original_rxgain_9280_2[][6] = { +static __unused const u32 ar9280Modes_original_rxgain_9280_2[][6] = { {0x00009a00, 0x00008184, 0x00008184, 0x00008000, 0x00008000, 0x00008000}, {0x00009a04, 0x00008188, 0x00008188, 0x00008000, 0x00008000, 0x00008000}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00008000, 0x00008000, 0x00008000}, @@ -692,7 +692,7 @@ static const u32 ar9280Modes_original_rxgain_9280_2[][6] = { {0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063}, }; -static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { +static __unused const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290}, {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304}, @@ -825,7 +825,7 @@ static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { {0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a}, }; -static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { +static __unused const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { {0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652}, {0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce}, {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -859,7 +859,7 @@ static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { {0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480}, }; -static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { +static __unused const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { {0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652}, {0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce}, {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -893,7 +893,7 @@ static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { {0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480}, }; -static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { +static __unused const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -907,7 +907,7 @@ static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { +static __unused const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -921,7 +921,7 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -935,7 +935,7 @@ static const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -949,7 +949,7 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285Modes_9285_1_2[][6] = { +static __unused const u32 ar9285Modes_9285_1_2[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180}, @@ -1254,7 +1254,7 @@ static const u32 ar9285Modes_9285_1_2[][6] = { {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e}, }; -static const u32 ar9285Common_9285_1_2[][2] = { +static __unused const u32 ar9285Common_9285_1_2[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020045}, @@ -1574,7 +1574,7 @@ static const u32 ar9285Common_9285_1_2[][2] = { {0x00007870, 0x10142c00}, }; -static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { +static __unused const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000}, @@ -1614,7 +1614,7 @@ static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; -static const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = { +static __unused const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000}, @@ -1654,7 +1654,7 @@ static const u32 ar9285Modes_original_tx_gain_9285_1_2[][6] = { {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, }; -static const u32 ar9285Modes_XE2_0_normal_power[][6] = { +static __unused const u32 ar9285Modes_XE2_0_normal_power[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000}, @@ -1694,7 +1694,7 @@ static const u32 ar9285Modes_XE2_0_normal_power[][6] = { {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, }; -static const u32 ar9285Modes_XE2_0_high_power[][6] = { +static __unused const u32 ar9285Modes_XE2_0_high_power[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000}, @@ -1734,7 +1734,7 @@ static const u32 ar9285Modes_XE2_0_high_power[][6] = { {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; -static const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -1748,7 +1748,7 @@ static const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { +static __unused const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -1762,7 +1762,7 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9287Modes_9287_1_1[][6] = { +static __unused const u32 ar9287Modes_9287_1_1[][6] = { {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38, 0x00001180}, @@ -1808,7 +1808,7 @@ static const u32 ar9287Modes_9287_1_1[][6] = { {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9287Common_9287_1_1[][2] = { +static __unused const u32 ar9287Common_9287_1_1[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, @@ -2177,21 +2177,21 @@ static const u32 ar9287Common_9287_1_1[][2] = { {0x000078b8, 0x2a850160}, }; -static const u32 ar9287Common_normal_cck_fir_coeff_9287_1_1[][2] = { +static __unused const u32 ar9287Common_normal_cck_fir_coeff_9287_1_1[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00fffeff}, {0x0000a1f8, 0x00f5f9ff}, {0x0000a1fc, 0xb79f6427}, }; -static const u32 ar9287Common_japan_2484_cck_fir_coeff_9287_1_1[][2] = { +static __unused const u32 ar9287Common_japan_2484_cck_fir_coeff_9287_1_1[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00000000}, {0x0000a1f8, 0xefff0301}, {0x0000a1fc, 0xca9228ee}, }; -static const u32 ar9287Modes_tx_gain_9287_1_1[][6] = { +static __unused const u32 ar9287Modes_tx_gain_9287_1_1[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002, 0x00004002}, {0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004, 0x00008004}, @@ -2239,7 +2239,7 @@ static const u32 ar9287Modes_tx_gain_9287_1_1[][6] = { {0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000, 0x0a1aa000}, }; -static const u32 ar9287Modes_rx_gain_9287_1_1[][6] = { +static __unused const u32 ar9287Modes_rx_gain_9287_1_1[][6] = { {0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120}, {0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124}, {0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128}, @@ -2500,7 +2500,7 @@ static const u32 ar9287Modes_rx_gain_9287_1_1[][6] = { {0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067}, }; -static const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { +static __unused const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -2514,7 +2514,7 @@ static const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { +static __unused const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, @@ -2528,7 +2528,7 @@ static const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9271Modes_9271[][6] = { +static __unused const u32 ar9271Modes_9271[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180}, @@ -2834,7 +2834,7 @@ static const u32 ar9271Modes_9271[][6] = { {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e}, }; -static const u32 ar9271Common_9271[][2] = { +static __unused const u32 ar9271Common_9271[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020045}, @@ -3163,26 +3163,26 @@ static const u32 ar9271Common_9271[][2] = { {0x0000d384, 0xf3307ff0}, }; -static const u32 ar9271Common_normal_cck_fir_coeff_9271[][2] = { +static __unused const u32 ar9271Common_normal_cck_fir_coeff_9271[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00fffeff}, {0x0000a1f8, 0x00f5f9ff}, {0x0000a1fc, 0xb79f6427}, }; -static const u32 ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { +static __unused const u32 ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00000000}, {0x0000a1f8, 0xefff0301}, {0x0000a1fc, 0xca9228ee}, }; -static const u32 ar9271Modes_9271_1_0_only[][6] = { +static __unused const u32 ar9271Modes_9271_1_0_only[][6] = { {0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, }; -static const u32 ar9271Modes_9271_ANI_reg[][6] = { +static __unused const u32 ar9271Modes_9271_ANI_reg[][6] = { {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2}, {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e}, {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e}, @@ -3193,7 +3193,7 @@ static const u32 ar9271Modes_9271_ANI_reg[][6] = { {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, }; -static const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = { +static __unused const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000}, @@ -3229,7 +3229,7 @@ static const u32 ar9271Modes_normal_power_tx_gain_9271[][6] = { {0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd}, }; -static const u32 ar9271Modes_high_power_tx_gain_9271[][6] = { +static __unused const u32 ar9271Modes_high_power_tx_gain_9271[][6] = { {0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000}, {0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000}, diff --git a/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h b/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h index e8ac70da5..b1303bbaa 100644 --- a/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9003_2p2_initvals.h @@ -19,7 +19,7 @@ /* AR9003 2.2 */ -static const u32 ar9300_2p2_radio_postamble[][5] = { +static __unused const u32 ar9300_2p2_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31}, {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800}, @@ -32,7 +32,7 @@ static const u32 ar9300_2p2_radio_postamble[][5] = { {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; -static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, @@ -138,7 +138,7 @@ static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9300Modes_fast_clock_2p2[][3] = { +static __unused const u32 ar9300Modes_fast_clock_2p2[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -151,7 +151,7 @@ static const u32 ar9300Modes_fast_clock_2p2[][3] = { {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9300_2p2_radio_core[][2] = { +static __unused const u32 ar9300_2p2_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, @@ -295,7 +295,7 @@ static const u32 ar9300_2p2_radio_core[][2] = { {0x00016bd4, 0x00000000}, }; -static const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { +static __unused const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x02000101}, {0x0000a004, 0x02000102}, @@ -555,7 +555,7 @@ static const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { {0x0000b1fc, 0x00000776}, }; -static const u32 ar9300_2p2_mac_postamble[][5] = { +static __unused const u32 ar9300_2p2_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, @@ -567,12 +567,12 @@ static const u32 ar9300_2p2_mac_postamble[][5] = { {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -static const u32 ar9300_2p2_soc_postamble[][5] = { +static __unused const u32 ar9300_2p2_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; -static const u32 ar9200_merlin_2p2_radio_core[][2] = { +static __unused const u32 ar9200_merlin_2p2_radio_core[][2] = { /* Addr allmodes */ {0x00007800, 0x00040000}, {0x00007804, 0xdb005012}, @@ -614,7 +614,7 @@ static const u32 ar9200_merlin_2p2_radio_core[][2] = { {0x00007894, 0x5a108000}, }; -static const u32 ar9300_2p2_baseband_postamble[][5] = { +static __unused const u32 ar9300_2p2_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, @@ -670,7 +670,7 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = { {0x0000c284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; -static const u32 ar9300_2p2_baseband_core[][2] = { +static __unused const u32 ar9300_2p2_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, @@ -833,7 +833,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = { {0x0000c420, 0x00000000}, }; -static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, @@ -939,7 +939,7 @@ static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { {0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c}, }; -static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, @@ -1045,7 +1045,7 @@ static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9300Common_rx_gain_table_2p2[][2] = { +static __unused const u32 ar9300Common_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1305,7 +1305,7 @@ static const u32 ar9300Common_rx_gain_table_2p2[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { +static __unused const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, @@ -1411,7 +1411,7 @@ static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9300_2p2_mac_core[][2] = { +static __unused const u32 ar9300_2p2_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, @@ -1570,7 +1570,7 @@ static const u32 ar9300_2p2_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { +static __unused const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1830,7 +1830,7 @@ static const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9300_2p2_soc_preamble[][2] = { +static __unused const u32 ar9300_2p2_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007008, 0x00000000}, @@ -1840,21 +1840,21 @@ static const u32 ar9300_2p2_soc_preamble[][2] = { {0x00007048, 0x00000008}, }; -static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = { +static __unused const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x0821265e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; -static const u32 ar9300PciePhy_clkreq_enable_L1_2p2[][2] = { +static __unused const u32 ar9300PciePhy_clkreq_enable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x08253e5e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; -static const u32 ar9300PciePhy_clkreq_disable_L1_2p2[][2] = { +static __unused const u32 ar9300PciePhy_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x08213e5e}, {0x00004040, 0x0008003b}, diff --git a/src/drivers/net/ath/ath9k/ar9340_initvals.h b/src/drivers/net/ath/ath9k/ar9340_initvals.h index 815a8af1b..784080b16 100644 --- a/src/drivers/net/ath/ath9k/ar9340_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9340_initvals.h @@ -17,7 +17,7 @@ #ifndef INITVALS_9340_H #define INITVALS_9340_H -static const u32 ar9340_1p0_radio_postamble[][5] = { +static __unused const u32 ar9340_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646800, 0xa4646800, 0xa4646800, 0xa4646800}, {0x0001610c, 0x08000000, 0x08000000, 0x00000000, 0x00000000}, @@ -26,7 +26,7 @@ static const u32 ar9340_1p0_radio_postamble[][5] = { {0x00016540, 0x10804000, 0x10804000, 0x50804000, 0x50804000}, }; -static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -99,7 +99,7 @@ static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_fast_clock_1p0[][3] = { +static __unused const u32 ar9340Modes_fast_clock_1p0[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -112,7 +112,7 @@ static const u32 ar9340Modes_fast_clock_1p0[][3] = { {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9340_1p0_radio_core[][2] = { +static __unused const u32 ar9340_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, @@ -218,13 +218,13 @@ static const u32 ar9340_1p0_radio_core[][2] = { {0x000167d4, 0x00000000}, }; -static const u32 ar9340_1p0_radio_core_40M[][2] = { +static __unused const u32 ar9340_1p0_radio_core_40M[][2] = { {0x0001609c, 0x02566f3a}, {0x000160ac, 0xa4647c00}, {0x000160b0, 0x01885f5a}, }; -static const u32 ar9340_1p0_mac_postamble[][5] = { +static __unused const u32 ar9340_1p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, @@ -236,12 +236,12 @@ static const u32 ar9340_1p0_mac_postamble[][5] = { {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -static const u32 ar9340_1p0_soc_postamble[][5] = { +static __unused const u32 ar9340_1p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; -static const u32 ar9340_1p0_baseband_postamble[][5] = { +static __unused const u32 ar9340_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a022e, 0x206a022e}, @@ -288,7 +288,7 @@ static const u32 ar9340_1p0_baseband_postamble[][5] = { {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; -static const u32 ar9340_1p0_baseband_core[][2] = { +static __unused const u32 ar9340_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, @@ -464,7 +464,7 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000b420, 0x00000000}, }; -static const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, @@ -537,7 +537,7 @@ static const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, @@ -609,7 +609,7 @@ static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { {0x00016444, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, {0x00016448, 0x8e481266, 0x8e481266, 0x8e481266, 0x8e481266}, }; -static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, @@ -683,7 +683,7 @@ static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { }; -static const u32 ar9340Common_rx_gain_table_1p0[][2] = { +static __unused const u32 ar9340Common_rx_gain_table_1p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -943,7 +943,7 @@ static const u32 ar9340Common_rx_gain_table_1p0[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1016,7 +1016,7 @@ static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { +static __unused const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1089,7 +1089,7 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24927266, 0x24927266, 0x8e482266, 0x8e482266}, }; -static const u32 ar9340_1p0_mac_core[][2] = { +static __unused const u32 ar9340_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, @@ -1253,7 +1253,7 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { +static __unused const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1513,7 +1513,7 @@ static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9340_1p0_soc_preamble[][2] = { +static __unused const u32 ar9340_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007008, 0x00000000}, diff --git a/src/drivers/net/ath/ath9k/ar9485_initvals.h b/src/drivers/net/ath/ath9k/ar9485_initvals.h index 611ea6ce8..c854398aa 100644 --- a/src/drivers/net/ath/ath9k/ar9485_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9485_initvals.h @@ -17,7 +17,7 @@ #ifndef INITVALS_9485_H #define INITVALS_9485_H -static const u32 ar9485_1_1_mac_core[][2] = { +static __unused const u32 ar9485_1_1_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, @@ -179,7 +179,7 @@ static const u32 ar9485_1_1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9485_1_1_baseband_core[][2] = { +static __unused const u32 ar9485_1_1_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, @@ -316,7 +316,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x0000a7dc, 0x00000000}, }; -static const u32 ar9485Common_1_1[][2] = { +static __unused const u32 ar9485Common_1_1[][2] = { /* Addr allmodes */ {0x00007010, 0x00000022}, {0x00007020, 0x00000000}, @@ -324,7 +324,7 @@ static const u32 ar9485Common_1_1[][2] = { {0x00007038, 0x000004c2}, }; -static const u32 ar9485_1_1_baseband_postamble[][5] = { +static __unused const u32 ar9485_1_1_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, @@ -369,7 +369,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -442,7 +442,7 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -515,7 +515,7 @@ static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_1_1_radio_postamble[][2] = { +static __unused const u32 ar9485_1_1_radio_postamble[][2] = { /* Addr allmodes */ {0x0001609c, 0x0b283f31}, {0x000160ac, 0x24611800}, @@ -524,7 +524,7 @@ static const u32 ar9485_1_1_radio_postamble[][2] = { {0x00016140, 0x10804008}, }; -static const u32 ar9485_1_1_mac_postamble[][5] = { +static __unused const u32 ar9485_1_1_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, @@ -536,7 +536,7 @@ static const u32 ar9485_1_1_mac_postamble[][5] = { {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -static const u32 ar9485_1_1_radio_core[][2] = { +static __unused const u32 ar9485_1_1_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, @@ -601,14 +601,14 @@ static const u32 ar9485_1_1_radio_core[][2] = { {0x00016c44, 0x12000000}, }; -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10052e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { +static __unused const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -681,7 +681,7 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_1_1[][2] = { +static __unused const u32 ar9485_1_1[][2] = { /* Addr allmodes */ {0x0000a580, 0x00000000}, {0x0000a584, 0x00000000}, @@ -701,7 +701,7 @@ static const u32 ar9485_1_1[][2] = { {0x0000a5bc, 0x00000000}, }; -static const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -774,14 +774,14 @@ static const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10013e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485_1_1_soc_preamble[][2] = { +static __unused const u32 ar9485_1_1_soc_preamble[][2] = { /* Addr allmodes */ {0x00004014, 0xba280400}, {0x00004090, 0x00aa10aa}, @@ -793,14 +793,14 @@ static const u32 ar9485_1_1_soc_preamble[][2] = { {0x00007048, 0x00000002}, }; -static const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = { +static __unused const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = { /* Addr allmodes */ {0x0000a398, 0x00000000}, {0x0000a39c, 0x6f7f0301}, {0x0000a3a0, 0xca9228ee}, }; -static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { +static __unused const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, @@ -873,21 +873,21 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { +static __unused const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { /* Addr 5G_HT2 5G_HT40 */ {0x00009e00, 0x03721821, 0x03721821}, {0x0000a230, 0x0000400b, 0x00004016}, {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10012e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485_common_rx_gain_1_1[][2] = { +static __unused const u32 ar9485_common_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1019,14 +1019,14 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a1fc, 0x00000296}, }; -static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { +static __unused const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x10053e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; -static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { +static __unused const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, diff --git a/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c b/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c index 2b6c133cb..a98e4bb66 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c @@ -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: diff --git a/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c b/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c index 72203ba48..65cfad597 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar9002_phy.c @@ -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; diff --git a/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c b/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c index 2244b775a..b66358b92 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c @@ -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: diff --git a/src/drivers/net/ath/ath9k/ath9k_eeprom.c b/src/drivers/net/ath/ath9k/ath9k_eeprom.c index f552acaa3..a20423790 100644 --- a/src/drivers/net/ath/ath9k/ath9k_eeprom.c +++ b/src/drivers/net/ath/ath9k/ath9k_eeprom.c @@ -368,10 +368,9 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, if (match) { if (AR_SREV_9287(ah)) { - /* FIXME: array overrun? */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_9287[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_9287[idxL].pwrPdg[i], data_9287[idxL].vpdPdg[i], @@ -381,7 +380,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, } else if (eeprom_4k) { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_4k[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_4k[idxL].pwrPdg[i], data_4k[idxL].vpdPdg[i], @@ -391,7 +390,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, } else { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_def[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_def[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_def[idxL].pwrPdg[i], data_def[idxL].vpdPdg[i], diff --git a/src/drivers/net/ath/ath9k/ath9k_init.c b/src/drivers/net/ath/ath9k/ath9k_init.c index 03de7701a..98a0d6d59 100644 --- a/src/drivers/net/ath/ath9k/ath9k_init.c +++ b/src/drivers/net/ath/ath9k/ath9k_init.c @@ -22,6 +22,7 @@ FILE_LICENCE ( BSD2 ); #include #include #include +#include #include "ath9k.h" @@ -349,7 +350,7 @@ static void ath9k_init_misc(struct ath_softc *sc) ath9k_hw_set_diversity(sc->sc_ah, 1); sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); - memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + memcpy(common->bssidmask, eth_broadcast, ETH_ALEN); } static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, diff --git a/src/drivers/net/ath/ath9k/ath9k_recv.c b/src/drivers/net/ath/ath9k/ath9k_recv.c index ba363c676..0ffe9d45a 100644 --- a/src/drivers/net/ath/ath9k/ath9k_recv.c +++ b/src/drivers/net/ath/ath9k/ath9k_recv.c @@ -98,7 +98,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct io_buffer *iob; - u32 *iob_addr = NULL; struct ath_buf *bf; int error = 0; @@ -122,15 +121,14 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) } list_for_each_entry(bf, &sc->rx.rxbuf, list) { - iob = ath_rxbuf_alloc(common, common->rx_bufsize, - iob_addr); + iob = alloc_iob_raw ( common->rx_bufsize, common->cachelsz, 0 ); if (iob == NULL) { error = -ENOMEM; goto err; } bf->bf_mpdu = iob; - bf->bf_buf_addr = *iob_addr; + bf->bf_buf_addr = virt_to_bus ( iob->data ); } sc->rx.rxlink = NULL; @@ -433,7 +431,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, int hp __unused) { struct ath_buf *bf; struct io_buffer *iob = NULL, *requeue_iob; - u32 *requeue_iob_addr = NULL; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); /* @@ -476,7 +473,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, int hp __unused) /* Ensure we always have an iob to requeue once we are done * processing the current buffer's iob */ - requeue_iob = ath_rxbuf_alloc(common, common->rx_bufsize, requeue_iob_addr); + requeue_iob = alloc_iob_raw ( common->rx_bufsize, + common->cachelsz, 0 ); /* If there is no memory we ignore the current RX'd frame, * tell hardware it can give us a new frame using the old @@ -491,7 +489,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, int hp __unused) /* We will now give hardware our shiny new allocated iob */ bf->bf_mpdu = requeue_iob; - bf->bf_buf_addr = *requeue_iob_addr; + bf->bf_buf_addr = virt_to_bus ( requeue_iob->data ); /* * change the default rx antenna if rx diversity chooses the diff --git a/src/drivers/net/ath/ath_main.c b/src/drivers/net/ath/ath_main.c deleted file mode 100644 index 85d159a36..000000000 --- a/src/drivers/net/ath/ath_main.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2009 Atheros Communications Inc. - * - * Modified for iPXE by Scott K Logan July 2011 - * Original from Linux kernel 3.0.1 - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include "ath.h" - -struct io_buffer *ath_rxbuf_alloc(struct ath_common *common, - u32 len, - u32 *iob_addr) -{ - struct io_buffer *iob; - u32 off; - - /* - * Cache-line-align. This is important (for the - * 5210 at least) as not doing so causes bogus data - * in rx'd frames. - */ - - /* Note: the kernel can allocate a value greater than - * what we ask it to give us. We really only need 4 KB as that - * is this hardware supports and in fact we need at least 3849 - * as that is the MAX AMSDU size this hardware supports. - * Unfortunately this means we may get 8 KB here from the - * kernel... and that is actually what is observed on some - * systems :( */ - iob = alloc_iob(len + common->cachelsz - 1); - if (iob != NULL) { - *iob_addr = virt_to_bus(iob->data); - off = ((unsigned long) iob->data) % common->cachelsz; - if (off != 0) - { - iob_reserve(iob, common->cachelsz - off); - *iob_addr += common->cachelsz - off; - } - } else { - DBG("ath: iobuffer alloc of size %d failed\n", len); - return NULL; - } - - return iob; -} diff --git a/src/drivers/net/axge.c b/src/drivers/net/axge.c new file mode 100644 index 000000000..ab59a8be7 --- /dev/null +++ b/src/drivers/net/axge.c @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "axge.h" + +/** @file + * + * Asix 10/100/1000 USB Ethernet driver + * + * Large chunks of functionality are undocumented in the available + * datasheets. The gaps are deduced from combinations of the Linux + * driver, the FreeBSD driver, and experimentation with the hardware. + */ + +/** Interrupt completion profiler */ +static struct profiler axge_intr_profiler __profiler = + { .name = "axge.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler axge_in_profiler __profiler = + { .name = "axge.in" }; + +/** Bulk OUT profiler */ +static struct profiler axge_out_profiler __profiler = + { .name = "axge.out" }; + +/** Default bulk IN configuration + * + * The Linux and FreeBSD drivers have set of magic constants which are + * chosen based on both the Ethernet and USB link speeds. + * + * Experimentation shows that setting the "timer" value to zero seems + * to prevent the device from ever coalescing multiple packets into a + * single bulk IN transfer. This allows us to get away with using a + * 2kB receive I/O buffer and a zerocopy receive path. + */ +static struct axge_bulk_in_control axge_bicr = { + .ctrl = 7, + .timer = cpu_to_le16 ( 0 ), + .size = 0, + .ifg = 0, +}; + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Read register + * + * @v asix AXGE device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline int axge_read_register ( struct axge_device *axge, + unsigned int offset, void *data, + size_t len ) { + + return usb_control ( axge->usb, AXGE_READ_MAC_REGISTER, + offset, len, data, len ); +} + +/** + * Read one-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value to fill in + * @ret rc Return status code + */ +static inline int axge_read_byte ( struct axge_device *axge, + unsigned int offset, uint8_t *value ) { + + return axge_read_register ( axge, offset, value, sizeof ( *value ) ); +} + +/** + * Read two-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value to fill in + * @ret rc Return status code + */ +static inline int axge_read_word ( struct axge_device *axge, + unsigned int offset, uint16_t *value ) { + + return axge_read_register ( axge, offset, value, sizeof ( *value ) ); +} + +/** + * Read four-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value to fill in + * @ret rc Return status code + */ +static inline int axge_read_dword ( struct axge_device *axge, + unsigned int offset, uint32_t *value ) { + + return axge_read_register ( axge, offset, value, sizeof ( *value ) ); +} + +/** + * Write register + * + * @v asix AXGE device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline int axge_write_register ( struct axge_device *axge, + unsigned int offset, void *data, + size_t len ) { + + return usb_control ( axge->usb, AXGE_WRITE_MAC_REGISTER, + offset, len, data, len ); +} + +/** + * Write one-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value + * @ret rc Return status code + */ +static inline int axge_write_byte ( struct axge_device *axge, + unsigned int offset, uint8_t value ) { + + return axge_write_register ( axge, offset, &value, sizeof ( value )); +} + +/** + * Write two-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value + * @ret rc Return status code + */ +static inline int axge_write_word ( struct axge_device *axge, + unsigned int offset, uint16_t value ) { + + return axge_write_register ( axge, offset, &value, sizeof ( value )); +} + +/** + * Write one-byte register + * + * @v asix AXGE device + * @v offset Register offset + * @v value Value + * @ret rc Return status code + */ +static inline int axge_write_dword ( struct axge_device *axge, + unsigned int offset, uint32_t value ) { + + return axge_write_register ( axge, offset, &value, sizeof ( value )); +} + +/****************************************************************************** + * + * Link status + * + ****************************************************************************** + */ + +/** + * Get link status + * + * @v asix AXGE device + * @ret rc Return status code + */ +static int axge_check_link ( struct axge_device *axge ) { + struct net_device *netdev = axge->netdev; + uint8_t plsr; + int rc; + + /* Read physical link status register */ + if ( ( rc = axge_read_byte ( axge, AXGE_PLSR, &plsr ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not read PLSR: %s\n", + axge, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + if ( plsr & AXGE_PLSR_EPHY_ANY ) { + DBGC ( axge, "AXGE %p link up (PLSR %02x)\n", axge, plsr ); + netdev_link_up ( netdev ); + } else { + DBGC ( axge, "AXGE %p link down (PLSR %02x)\n", axge, plsr ); + netdev_link_down ( netdev ); + } + + return 0; +} + +/****************************************************************************** + * + * AXGE communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void axge_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct axge_device *axge = container_of ( ep, struct axge_device, + usbnet.intr ); + struct net_device *netdev = axge->netdev; + struct axge_interrupt *intr; + size_t len = iob_len ( iobuf ); + unsigned int link_ok; + + /* Profile completions */ + profile_start ( &axge_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Drop packets with errors */ + if ( rc != 0 ) { + DBGC ( axge, "AXGE %p interrupt failed: %s\n", + axge, strerror ( rc ) ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( len < sizeof ( *intr ) ) { + DBGC ( axge, "AXGE %p underlength interrupt:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + intr = iobuf->data; + + /* Check magic signature */ + if ( intr->magic != cpu_to_le16 ( AXGE_INTR_MAGIC ) ) { + DBGC ( axge, "AXGE %p malformed interrupt:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + + /* Extract link status */ + link_ok = ( intr->link & cpu_to_le16 ( AXGE_INTR_LINK_PPLS ) ); + if ( link_ok && ! netdev_link_ok ( netdev ) ) { + DBGC ( axge, "AXGE %p link up\n", axge ); + netdev_link_up ( netdev ); + } else if ( netdev_link_ok ( netdev ) && ! link_ok ) { + DBGC ( axge, "AXGE %p link down\n", axge ); + netdev_link_down ( netdev ); + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &axge_intr_profiler ); + + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations axge_intr_operations = { + .complete = axge_intr_complete, +}; + +/****************************************************************************** + * + * AXGE data interface + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void axge_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct axge_device *axge = container_of ( ep, struct axge_device, + usbnet.in ); + struct net_device *netdev = axge->netdev; + struct axge_rx_footer *ftr; + struct axge_rx_descriptor *desc; + struct io_buffer *pkt; + unsigned int count; + unsigned int offset; + size_t len; + size_t padded_len; + + /* Profile receive completions */ + profile_start ( &axge_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( axge, "AXGE %p bulk IN failed: %s\n", + axge, strerror ( rc ) ); + goto error; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *ftr ) ) { + DBGC ( axge, "AXGE %p underlength bulk IN:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + + /* Parse ftr, strip ftr and descriptors */ + iob_unput ( iobuf, sizeof ( *ftr ) ); + ftr = ( iobuf->data + iob_len ( iobuf ) ); + count = le16_to_cpu ( ftr->count ); + if ( count == 0 ) { + DBGC ( axge, "AXGE %p zero-packet bulk IN:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + offset = le16_to_cpu ( ftr->offset ); + if ( ( iob_len ( iobuf ) < offset ) || + ( ( iob_len ( iobuf ) - offset ) < ( count * sizeof ( *desc ) ) )){ + DBGC ( axge, "AXGE %p malformed bulk IN footer:\n", axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + desc = ( iobuf->data + offset ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - offset ) ); + + /* Process packets */ + for ( ; count-- ; desc++ ) { + + /* Parse descriptor */ + len = ( le16_to_cpu ( desc->len_flags ) & AXGE_RX_LEN_MASK ); + padded_len = ( ( len + AXGE_RX_LEN_PAD_ALIGN - 1 ) & + ~( AXGE_RX_LEN_PAD_ALIGN - 1 ) ); + if ( iob_len ( iobuf ) < padded_len ) { + DBGC ( axge, "AXGE %p malformed bulk IN descriptor:\n", + axge ); + DBGC_HDA ( axge, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + + /* Check for previous dropped packets */ + if ( desc->len_flags & cpu_to_le16 ( AXGE_RX_CRC_ERROR ) ) + netdev_rx_err ( netdev, NULL, -EIO ); + if ( desc->len_flags & cpu_to_le16 ( AXGE_RX_DROP_ERROR ) ) + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + + /* Allocate new I/O buffer, if applicable */ + if ( count ) { + + /* More packets remain: allocate a new buffer */ + pkt = alloc_iob ( AXGE_IN_RESERVE + len ); + if ( ! pkt ) { + /* Record error and continue */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + iob_pull ( iobuf, padded_len ); + continue; + } + iob_reserve ( pkt, AXGE_IN_RESERVE ); + memcpy ( iob_put ( pkt, len ), iobuf->data, len ); + iob_pull ( iobuf, padded_len ); + + } else { + + /* This is the last (or only) packet: use this buffer */ + iob_unput ( iobuf, ( padded_len - len ) ); + pkt = iob_disown ( iobuf ); + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( pkt ) ); + } + + assert ( iobuf == NULL ); + profile_stop ( &axge_in_profiler ); + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations axge_in_operations = { + .complete = axge_in_complete, +}; + +/** + * Transmit packet + * + * @v asix AXGE device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int axge_out_transmit ( struct axge_device *axge, + struct io_buffer *iobuf ) { + struct axge_tx_header *hdr; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile transmissions */ + profile_start ( &axge_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *hdr ) ) ) != 0 ) + return rc; + hdr = iob_push ( iobuf, sizeof ( *hdr ) ); + hdr->len = cpu_to_le32 ( len ); + hdr->wtf = 0; + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &axge->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &axge_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void axge_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct axge_device *axge = container_of ( ep, struct axge_device, + usbnet.out ); + struct net_device *netdev = axge->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations axge_out_operations = { + .complete = axge_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int axge_open ( struct net_device *netdev ) { + struct axge_device *axge = netdev->priv; + uint16_t rcr; + int rc; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &axge->usbnet ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not open: %s\n", + axge, strerror ( rc ) ); + goto err_open; + } + + /* Set MAC address */ + if ( ( rc = axge_write_register ( axge, AXGE_NIDR, + netdev->ll_addr, ETH_ALEN ) ) !=0){ + DBGC ( axge, "AXGE %p could not set MAC address: %s\n", + axge, strerror ( rc ) ); + goto err_write_mac; + } + + /* Enable receiver */ + rcr = cpu_to_le16 ( AXGE_RCR_PRO | AXGE_RCR_AMALL | + AXGE_RCR_AB | AXGE_RCR_SO ); + if ( ( rc = axge_write_word ( axge, AXGE_RCR, rcr ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not write RCR: %s\n", + axge, strerror ( rc ) ); + goto err_write_rcr; + } + + /* Update link status */ + axge_check_link ( axge ); + + return 0; + + axge_write_word ( axge, AXGE_RCR, 0 ); + err_write_rcr: + err_write_mac: + usbnet_close ( &axge->usbnet ); + err_open: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void axge_close ( struct net_device *netdev ) { + struct axge_device *axge = netdev->priv; + + /* Disable receiver */ + axge_write_word ( axge, AXGE_RCR, 0 ); + + /* Close USB network device */ + usbnet_close ( &axge->usbnet ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int axge_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct axge_device *axge = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = axge_out_transmit ( axge, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void axge_poll ( struct net_device *netdev ) { + struct axge_device *axge = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( axge->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &axge->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** AXGE network device operations */ +static struct net_device_operations axge_operations = { + .open = axge_open, + .close = axge_close, + .transmit = axge_transmit, + .poll = axge_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int axge_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct axge_device *axge; + uint16_t epprcr; + uint16_t msr; + uint8_t csr; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *axge ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &axge_operations ); + netdev->dev = &func->dev; + axge = netdev->priv; + memset ( axge, 0, sizeof ( *axge ) ); + axge->usb = usb; + axge->bus = usb->port->hub->bus; + axge->netdev = netdev; + usbnet_init ( &axge->usbnet, func, &axge_intr_operations, + &axge_in_operations, &axge_out_operations ); + usb_refill_init ( &axge->usbnet.intr, 0, 0, AXGE_INTR_MAX_FILL ); + usb_refill_init ( &axge->usbnet.in, AXGE_IN_RESERVE, + AXGE_IN_MTU, AXGE_IN_MAX_FILL ); + DBGC ( axge, "AXGE %p on %s\n", axge, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &axge->usbnet, config ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not describe: %s\n", + axge, strerror ( rc ) ); + goto err_describe; + } + + /* Fetch MAC address */ + if ( ( rc = axge_read_register ( axge, AXGE_NIDR, netdev->hw_addr, + ETH_ALEN ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not fetch MAC address: %s\n", + axge, strerror ( rc ) ); + goto err_read_mac; + } + + /* Power up PHY */ + if ( ( rc = axge_write_word ( axge, AXGE_EPPRCR, 0 ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not write EPPRCR: %s\n", + axge, strerror ( rc ) ); + goto err_write_epprcr_off; + } + epprcr = cpu_to_le16 ( AXGE_EPPRCR_IPRL ); + if ( ( rc = axge_write_word ( axge, AXGE_EPPRCR, epprcr ) ) != 0){ + DBGC ( axge, "AXGE %p could not write EPPRCR: %s\n", + axge, strerror ( rc ) ); + goto err_write_epprcr_on; + } + mdelay ( AXGE_EPPRCR_DELAY_MS ); + + /* Select clocks */ + csr = ( AXGE_CSR_BCS | AXGE_CSR_ACS ); + if ( ( rc = axge_write_byte ( axge, AXGE_CSR, csr ) ) != 0){ + DBGC ( axge, "AXGE %p could not write CSR: %s\n", + axge, strerror ( rc ) ); + goto err_write_csr; + } + mdelay ( AXGE_CSR_DELAY_MS ); + + /* Configure bulk IN pipeline */ + if ( ( rc = axge_write_register ( axge, AXGE_BICR, &axge_bicr, + sizeof ( axge_bicr ) ) ) != 0 ){ + DBGC ( axge, "AXGE %p could not write BICR: %s\n", + axge, strerror ( rc ) ); + goto err_write_bicr; + } + + /* Set medium status */ + msr = cpu_to_le16 ( AXGE_MSR_GM | AXGE_MSR_FD | AXGE_MSR_RFC | + AXGE_MSR_TFC | AXGE_MSR_RE ); + if ( ( rc = axge_write_word ( axge, AXGE_MSR, msr ) ) != 0 ) { + DBGC ( axge, "AXGE %p could not write MSR: %s\n", + axge, strerror ( rc ) ); + goto err_write_msr; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + /* Update link status */ + axge_check_link ( axge ); + + usb_func_set_drvdata ( func, axge ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_write_msr: + err_write_bicr: + err_write_csr: + err_write_epprcr_on: + err_write_epprcr_off: + err_read_mac: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void axge_remove ( struct usb_function *func ) { + struct axge_device *axge = usb_func_get_drvdata ( func ); + struct net_device *netdev = axge->netdev; + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** AXGE device IDs */ +static struct usb_device_id axge_ids[] = { + { + .name = "ax88179", + .vendor = 0x0b95, + .product = 0x1790, + }, + { + .name = "ax88178a", + .vendor = 0x0b95, + .product = 0x178a, + }, + { + .name = "dub1312", + .vendor = 0x2001, + .product = 0x4a00, + }, + { + .name = "axge-sitecom", + .vendor = 0x0df6, + .product = 0x0072, + }, + { + .name = "axge-samsung", + .vendor = 0x04e8, + .product = 0xa100, + }, + { + .name = "onelinkdock", + .vendor = 0x17ef, + .product = 0x304b, + }, +}; + +/** AXGE driver */ +struct usb_driver axge_driver __usb_driver = { + .ids = axge_ids, + .id_count = ( sizeof ( axge_ids ) / sizeof ( axge_ids[0] ) ), + .class = USB_CLASS_ID ( USB_ANY_ID, USB_ANY_ID, USB_ANY_ID ), + .score = USB_SCORE_NORMAL, + .probe = axge_probe, + .remove = axge_remove, +}; diff --git a/src/drivers/net/axge.h b/src/drivers/net/axge.h new file mode 100644 index 000000000..65bf911c5 --- /dev/null +++ b/src/drivers/net/axge.h @@ -0,0 +1,174 @@ +#ifndef _AXGE_H +#define _AXGE_H + +/** @file + * + * Asix 10/100/1000 USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** Read MAC register */ +#define AXGE_READ_MAC_REGISTER \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + +/** Write MAC register */ +#define AXGE_WRITE_MAC_REGISTER \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + +/** Physical Link Status Register */ +#define AXGE_PLSR 0x02 +#define AXGE_PLSR_EPHY_10 0x10 /**< Ethernet at 10Mbps */ +#define AXGE_PLSR_EPHY_100 0x20 /**< Ethernet at 100Mbps */ +#define AXGE_PLSR_EPHY_1000 0x40 /**< Ethernet at 1000Mbps */ +#define AXGE_PLSR_EPHY_ANY \ + ( AXGE_PLSR_EPHY_10 | \ + AXGE_PLSR_EPHY_100 | \ + AXGE_PLSR_EPHY_1000 ) + +/** RX Control Register */ +#define AXGE_RCR 0x0b +#define AXGE_RCR_PRO 0x0001 /**< Promiscuous mode */ +#define AXGE_RCR_AMALL 0x0002 /**< Accept all multicasts */ +#define AXGE_RCR_AB 0x0008 /**< Accept broadcasts */ +#define AXGE_RCR_SO 0x0080 /**< Start operation */ + +/** Node ID Register */ +#define AXGE_NIDR 0x10 + +/** Medium Status Register */ +#define AXGE_MSR 0x22 +#define AXGE_MSR_GM 0x0001 /**< Gigabit mode */ +#define AXGE_MSR_FD 0x0002 /**< Full duplex */ +#define AXGE_MSR_RFC 0x0010 /**< RX flow control enable */ +#define AXGE_MSR_TFC 0x0020 /**< TX flow control enable */ +#define AXGE_MSR_RE 0x0100 /**< Receive enable */ + +/** Ethernet PHY Power and Reset Control Register */ +#define AXGE_EPPRCR 0x26 +#define AXGE_EPPRCR_IPRL 0x0020 /**< Undocumented */ + +/** Delay after initialising EPPRCR */ +#define AXGE_EPPRCR_DELAY_MS 200 + +/** Bulk IN Control Register (undocumented) */ +#define AXGE_BICR 0x2e + +/** Bulk IN Control (undocumented) */ +struct axge_bulk_in_control { + /** Control */ + uint8_t ctrl; + /** Timer */ + uint16_t timer; + /** Size */ + uint8_t size; + /** Inter-frame gap */ + uint8_t ifg; +} __attribute__ (( packed )); + +/** Clock Select Register (undocumented) */ +#define AXGE_CSR 0x33 +#define AXGE_CSR_BCS 0x01 /**< Undocumented */ +#define AXGE_CSR_ACS 0x02 /**< Undocumented */ + +/** Delay after initialising CSR */ +#define AXGE_CSR_DELAY_MS 100 + +/** Transmit packet header */ +struct axge_tx_header { + /** Packet length */ + uint32_t len; + /** Answers on a postcard, please */ + uint32_t wtf; +} __attribute__ (( packed )); + +/** Receive packet footer */ +struct axge_rx_footer { + /** Packet count */ + uint16_t count; + /** Header offset */ + uint16_t offset; +} __attribute__ (( packed )); + +/** Receive packet descriptor */ +struct axge_rx_descriptor { + /** Checksum information */ + uint16_t check; + /** Length and error flags */ + uint16_t len_flags; +} __attribute__ (( packed )); + +/** Receive packet length mask */ +#define AXGE_RX_LEN_MASK 0x1fff + +/** Receive packet length alignment */ +#define AXGE_RX_LEN_PAD_ALIGN 8 + +/** Receive packet CRC error */ +#define AXGE_RX_CRC_ERROR 0x2000 + +/** Receive packet dropped error */ +#define AXGE_RX_DROP_ERROR 0x8000 + +/** Interrupt data */ +struct axge_interrupt { + /** Magic signature */ + uint16_t magic; + /** Link state */ + uint16_t link; + /** PHY register MR01 */ + uint16_t mr01; + /** PHY register MR05 */ + uint16_t mr05; +} __attribute__ (( packed )); + +/** Interrupt magic signature */ +#define AXGE_INTR_MAGIC 0x00a1 + +/** Link is up */ +#define AXGE_INTR_LINK_PPLS 0x0001 + +/** An AXGE network device */ +struct axge_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; +}; + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define AXGE_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define AXGE_IN_MAX_FILL 8 + +/** Bulk IN buffer size + * + * This is a policy decision. + */ +#define AXGE_IN_MTU 2048 + +/** Amount of space to reserve at start of bulk IN buffers + * + * This is required to allow for protocols such as ARP which may reuse + * a received I/O buffer for transmission. + */ +#define AXGE_IN_RESERVE sizeof ( struct axge_tx_header ) + +#endif /* _AXGE_H */ diff --git a/src/drivers/net/dm96xx.c b/src/drivers/net/dm96xx.c index 817a84a29..61b957be9 100644 --- a/src/drivers/net/dm96xx.c +++ b/src/drivers/net/dm96xx.c @@ -532,8 +532,8 @@ static int dm96xx_probe ( struct usb_function *func, dm96xx->netdev = netdev; usbnet_init ( &dm96xx->usbnet, func, &dm96xx_intr_operations, &dm96xx_in_operations, &dm96xx_out_operations ); - usb_refill_init ( &dm96xx->usbnet.intr, 0, DM96XX_INTR_MAX_FILL ); - usb_refill_init ( &dm96xx->usbnet.in, DM96XX_IN_MTU, + usb_refill_init ( &dm96xx->usbnet.intr, 0, 0, DM96XX_INTR_MAX_FILL ); + usb_refill_init ( &dm96xx->usbnet.in, 0, DM96XX_IN_MTU, DM96XX_IN_MAX_FILL ); DBGC ( dm96xx, "DM96XX %p on %s\n", dm96xx, func->name ); diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 371611d54..847a45b85 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -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; } @@ -437,8 +442,8 @@ static int ecm_probe ( struct usb_function *func, ecm->netdev = netdev; usbnet_init ( &ecm->usbnet, func, &ecm_intr_operations, &ecm_in_operations, &ecm_out_operations ); - usb_refill_init ( &ecm->usbnet.intr, 0, ECM_INTR_MAX_FILL ); - usb_refill_init ( &ecm->usbnet.in, ECM_IN_MTU, ECM_IN_MAX_FILL ); + usb_refill_init ( &ecm->usbnet.intr, 0, 0, ECM_INTR_MAX_FILL ); + usb_refill_init ( &ecm->usbnet.in, 0, ECM_IN_MTU, ECM_IN_MAX_FILL ); DBGC ( ecm, "ECM %p on %s\n", ecm, func->name ); /* Describe USB network device */ diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index d68b36cc3..2d87e0c63 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -137,6 +138,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define PCI_MAX_BAR 6 +/** An NII memory mapping */ +struct nii_mapping { + /** List of mappings */ + struct list_head list; + /** Mapped address */ + UINT64 addr; + /** Mapping cookie created by PCI I/O protocol */ + VOID *mapping; +}; + /** An NII NIC */ struct nii_nic { /** EFI device */ @@ -179,6 +190,9 @@ struct nii_nic { struct io_buffer *txbuf; /** Current receive buffer */ struct io_buffer *rxbuf; + + /** Mapping list */ + struct list_head mappings; }; /** Maximum number of received packets per poll */ @@ -202,7 +216,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 +244,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 ) { @@ -280,7 +294,19 @@ static int nii_pci_open ( struct nii_nic *nii ) { */ static void nii_pci_close ( struct nii_nic *nii ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct nii_mapping *map; + struct nii_mapping *tmp; + /* Remove any stale mappings */ + list_for_each_entry_safe ( map, tmp, &nii->mappings, list ) { + DBGC ( nii, "NII %s removing stale mapping %#llx\n", + nii->dev.name, ( ( unsigned long long ) map->addr ) ); + nii->pci_io->Unmap ( nii->pci_io, map->mapping ); + list_del ( &map->list ); + free ( map ); + } + + /* Close protocols */ bs->CloseProtocol ( nii->pci_device, &efi_pci_io_protocol_guid, efi_image_handle, nii->efidev->device ); } @@ -331,6 +357,139 @@ static EFIAPI VOID nii_io ( UINT64 unique_id, UINT8 op, UINT8 len, UINT64 addr, } } +/** + * Map callback + * + * @v unique_id NII NIC + * @v addr Address of memory to be mapped + * @v len Length of memory to be mapped + * @v dir Direction of data flow + * @v mapped Device mapped address to fill in + */ +static EFIAPI VOID nii_map ( UINT64 unique_id, UINT64 addr, UINT32 len, + UINT32 dir, UINT64 mapped ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + EFI_PHYSICAL_ADDRESS *phys = ( ( void * ) ( intptr_t ) mapped ); + EFI_PCI_IO_PROTOCOL_OPERATION op; + struct nii_mapping *map; + UINTN count = len; + EFI_STATUS efirc; + int rc; + + /* Return a zero mapped address on failure */ + *phys = 0; + + /* Determine PCI mapping operation */ + switch ( dir ) { + case TO_AND_FROM_DEVICE: + op = EfiPciIoOperationBusMasterCommonBuffer; + break; + case FROM_DEVICE: + op = EfiPciIoOperationBusMasterWrite; + break; + case TO_DEVICE: + op = EfiPciIoOperationBusMasterRead; + break; + default: + DBGC ( nii, "NII %s unsupported mapping direction %d\n", + nii->dev.name, dir ); + goto err_dir; + } + + /* Allocate a mapping record */ + map = zalloc ( sizeof ( *map ) ); + if ( ! map ) + goto err_alloc; + map->addr = addr; + + /* Create map */ + if ( ( efirc = nii->pci_io->Map ( nii->pci_io, op, + ( ( void * ) ( intptr_t ) addr ), + &count, phys, &map->mapping ) ) != 0){ + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s map operation failed: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_map; + } + + /* Add to list of mappings */ + list_add ( &map->list, &nii->mappings ); + DBGC2 ( nii, "NII %s mapped %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) *phys ) ); + return; + + list_del ( &map->list ); + err_map: + free ( map ); + err_alloc: + err_dir: + return; +} + +/** + * Unmap callback + * + * @v unique_id NII NIC + * @v addr Address of mapped memory + * @v len Length of mapped memory + * @v dir Direction of data flow + * @v mapped Device mapped address + */ +static EFIAPI VOID nii_unmap ( UINT64 unique_id, UINT64 addr, UINT32 len, + UINT32 dir __unused, UINT64 mapped ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + struct nii_mapping *map; + + /* Locate mapping record */ + list_for_each_entry ( map, &nii->mappings, list ) { + if ( map->addr == addr ) { + nii->pci_io->Unmap ( nii->pci_io, map->mapping ); + list_del ( &map->list ); + free ( map ); + DBGC2 ( nii, "NII %s unmapped %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) mapped ) ); + return; + } + } + + DBGC ( nii, "NII %s non-existent mapping %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) mapped ) ); +} + +/** + * Sync callback + * + * @v unique_id NII NIC + * @v addr Address of mapped memory + * @v len Length of mapped memory + * @v dir Direction of data flow + * @v mapped Device mapped address + */ +static EFIAPI VOID nii_sync ( UINT64 unique_id __unused, UINT64 addr, + UINT32 len, UINT32 dir, UINT64 mapped ) { + const void *src; + void *dst; + + /* Do nothing if this is an identity mapping */ + if ( addr == mapped ) + return; + + /* Determine direction */ + if ( dir == FROM_DEVICE ) { + src = ( ( void * ) ( intptr_t ) mapped ); + dst = ( ( void * ) ( intptr_t ) addr ); + } else { + src = ( ( void * ) ( intptr_t ) addr ); + dst = ( ( void * ) ( intptr_t ) mapped ); + } + + /* Copy data */ + memcpy ( dst, src, len ); +} + /** * Delay callback * @@ -402,7 +561,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 +575,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 +588,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; @@ -491,6 +658,9 @@ static int nii_start_undi ( struct nii_nic *nii ) { cpb.Delay = ( ( intptr_t ) nii_delay ); cpb.Block = ( ( intptr_t ) nii_block ); cpb.Mem_IO = ( ( intptr_t ) nii_io ); + cpb.Map_Mem = ( ( intptr_t ) nii_map ); + cpb.UnMap_Mem = ( ( intptr_t ) nii_unmap ); + cpb.Sync_Mem = ( ( intptr_t ) nii_sync ); cpb.Unique_ID = ( ( intptr_t ) nii ); /* Issue command */ @@ -632,22 +802,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 +1122,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 */ @@ -1059,6 +1225,7 @@ int nii_start ( struct efi_device *efidev ) { netdev_init ( netdev, &nii_operations ); nii = netdev->priv; nii->efidev = efidev; + INIT_LIST_HEAD ( &nii->mappings ); netdev->ll_broadcast = nii->broadcast; efidev_set_drvdata ( efidev, netdev ); diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c index 0d876b636..88474b0be 100644 --- a/src/drivers/net/efi/snpnet.c +++ b/src/drivers/net/efi/snpnet.c @@ -191,6 +191,7 @@ static void snpnet_poll_tx ( struct net_device *netdev ) { int rc; /* Get status */ + txbuf = NULL; if ( ( efirc = snp->snp->GetStatus ( snp->snp, &irq, &txbuf ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not get status: %s\n", diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c new file mode 100644 index 000000000..8d29979bb --- /dev/null +++ b/src/drivers/net/ena.c @@ -0,0 +1,1015 @@ +/* + * Copyright (C) 2018 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ena.h" + +/** @file + * + * Amazon ENA network driver + * + */ + +/** + * Get direction name (for debugging) + * + * @v direction Direction + * @ret name Direction name + */ +static const char * ena_direction ( unsigned int direction ) { + + switch ( direction ) { + case ENA_SQ_TX: return "TX"; + case ENA_SQ_RX: return "RX"; + default: return ""; + } +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_reset ( struct ena_nic *ena ) { + uint32_t stat; + unsigned int i; + + /* Trigger reset */ + writel ( ENA_CTRL_RESET, ( ena->regs + ENA_CTRL ) ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < ENA_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if device is ready */ + stat = readl ( ena->regs + ENA_STAT ); + if ( stat & ENA_STAT_READY ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ena, "ENA %p timed out waiting for reset (status %#08x)\n", + ena, stat ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** + * Set queue base address + * + * @v ena ENA device + * @v offset Register offset + * @v address Base address + */ +static inline void ena_set_base ( struct ena_nic *ena, unsigned int offset, + void *base ) { + physaddr_t phys = virt_to_bus ( base ); + + /* Program base address registers */ + writel ( ( phys & 0xffffffffUL ), + ( ena->regs + offset + ENA_BASE_LO ) ); + if ( sizeof ( phys ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) phys ) >> 32 ), + ( ena->regs + offset + ENA_BASE_HI ) ); + } else { + writel ( 0, ( ena->regs + offset + ENA_BASE_HI ) ); + } +} + +/** + * Set queue capabilities + * + * @v ena ENA device + * @v offset Register offset + * @v count Number of entries + * @v size Size of each entry + */ +static inline __attribute__ (( always_inline )) void +ena_set_caps ( struct ena_nic *ena, unsigned int offset, unsigned int count, + size_t size ) { + + /* Program capabilities register */ + writel ( ENA_CAPS ( count, size ), ( ena->regs + offset ) ); +} + +/** + * Clear queue capabilities + * + * @v ena ENA device + * @v offset Register offset + */ +static inline __attribute__ (( always_inline )) void +ena_clear_caps ( struct ena_nic *ena, unsigned int offset ) { + + /* Clear capabilities register */ + writel ( 0, ( ena->regs + offset ) ); +} + +/** + * Create admin queues + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_create_admin ( struct ena_nic *ena ) { + size_t aq_len = ( ENA_AQ_COUNT * sizeof ( ena->aq.req[0] ) ); + size_t acq_len = ( ENA_ACQ_COUNT * sizeof ( ena->acq.rsp[0] ) ); + int rc; + + /* Allocate admin completion queue */ + ena->acq.rsp = malloc_dma ( acq_len, acq_len ); + if ( ! ena->acq.rsp ) { + rc = -ENOMEM; + goto err_alloc_acq; + } + memset ( ena->acq.rsp, 0, acq_len ); + + /* Allocate admin queue */ + ena->aq.req = malloc_dma ( aq_len, aq_len ); + if ( ! ena->aq.req ) { + rc = -ENOMEM; + goto err_alloc_aq; + } + memset ( ena->aq.req, 0, aq_len ); + + /* Program queue addresses and capabilities */ + ena_set_base ( ena, ENA_ACQ_BASE, ena->acq.rsp ); + ena_set_caps ( ena, ENA_ACQ_CAPS, ENA_ACQ_COUNT, + sizeof ( ena->acq.rsp[0] ) ); + ena_set_base ( ena, ENA_AQ_BASE, ena->aq.req ); + ena_set_caps ( ena, ENA_AQ_CAPS, ENA_AQ_COUNT, + sizeof ( ena->aq.req[0] ) ); + + DBGC ( ena, "ENA %p AQ [%08lx,%08lx) ACQ [%08lx,%08lx)\n", + ena, virt_to_phys ( ena->aq.req ), + ( virt_to_phys ( ena->aq.req ) + aq_len ), + virt_to_phys ( ena->acq.rsp ), + ( virt_to_phys ( ena->acq.rsp ) + acq_len ) ); + return 0; + + ena_clear_caps ( ena, ENA_AQ_CAPS ); + ena_clear_caps ( ena, ENA_ACQ_CAPS ); + free_dma ( ena->aq.req, aq_len ); + err_alloc_aq: + free_dma ( ena->acq.rsp, acq_len ); + err_alloc_acq: + return rc; +} + +/** + * Destroy admin queues + * + * @v ena ENA device + */ +static void ena_destroy_admin ( struct ena_nic *ena ) { + size_t aq_len = ( ENA_AQ_COUNT * sizeof ( ena->aq.req[0] ) ); + size_t acq_len = ( ENA_ACQ_COUNT * sizeof ( ena->acq.rsp[0] ) ); + + /* Clear queue capabilities */ + ena_clear_caps ( ena, ENA_AQ_CAPS ); + ena_clear_caps ( ena, ENA_ACQ_CAPS ); + wmb(); + + /* Free queues */ + free_dma ( ena->aq.req, aq_len ); + free_dma ( ena->acq.rsp, acq_len ); + DBGC ( ena, "ENA %p AQ and ACQ destroyed\n", ena ); +} + +/** + * Get next available admin queue request + * + * @v ena ENA device + * @ret req Admin queue request + */ +static union ena_aq_req * ena_admin_req ( struct ena_nic *ena ) { + union ena_aq_req *req; + unsigned int index; + + /* Get next request */ + index = ( ena->aq.prod % ENA_AQ_COUNT ); + req = &ena->aq.req[index]; + + /* Initialise request */ + memset ( ( ( ( void * ) req ) + sizeof ( req->header ) ), 0, + ( sizeof ( *req ) - sizeof ( req->header ) ) ); + req->header.id = ena->aq.prod; + + /* Increment producer counter */ + ena->aq.prod++; + + return req; +} + +/** + * Issue admin queue request + * + * @v ena ENA device + * @v req Admin queue request + * @v rsp Admin queue response to fill in + * @ret rc Return status code + */ +static int ena_admin ( struct ena_nic *ena, union ena_aq_req *req, + union ena_acq_rsp **rsp ) { + unsigned int index; + unsigned int i; + int rc; + + /* Locate response */ + index = ( ena->acq.cons % ENA_ACQ_COUNT ); + *rsp = &ena->acq.rsp[index]; + + /* Mark request as ready */ + req->header.flags ^= ENA_AQ_PHASE; + wmb(); + DBGC2 ( ena, "ENA %p admin request %#x:\n", + ena, le16_to_cpu ( req->header.id ) ); + DBGC2_HDA ( ena, virt_to_phys ( req ), req, sizeof ( *req ) ); + + /* Ring doorbell */ + writel ( ena->aq.prod, ( ena->regs + ENA_AQ_DB ) ); + + /* Wait for response */ + for ( i = 0 ; i < ENA_ADMIN_MAX_WAIT_MS ; i++ ) { + + /* Check for response */ + if ( ( (*rsp)->header.flags ^ ena->acq.phase ) & ENA_ACQ_PHASE){ + mdelay ( 1 ); + continue; + } + DBGC2 ( ena, "ENA %p admin response %#x:\n", + ena, le16_to_cpu ( (*rsp)->header.id ) ); + DBGC2_HDA ( ena, virt_to_phys ( *rsp ), *rsp, sizeof ( **rsp )); + + /* Increment consumer counter */ + ena->acq.cons++; + if ( ( ena->acq.cons % ENA_ACQ_COUNT ) == 0 ) + ena->acq.phase ^= ENA_ACQ_PHASE; + + /* Check command identifier */ + if ( (*rsp)->header.id != req->header.id ) { + DBGC ( ena, "ENA %p admin response %#x mismatch:\n", + ena, le16_to_cpu ( (*rsp)->header.id ) ); + rc = -EILSEQ; + goto err; + } + + /* Check status */ + if ( (*rsp)->header.status != 0 ) { + DBGC ( ena, "ENA %p admin response %#x status %d:\n", + ena, le16_to_cpu ( (*rsp)->header.id ), + (*rsp)->header.status ); + rc = -EIO; + goto err; + } + + /* Success */ + return 0; + } + + rc = -ETIMEDOUT; + DBGC ( ena, "ENA %p timed out waiting for admin request %#x:\n", + ena, le16_to_cpu ( req->header.id ) ); + err: + DBGC_HDA ( ena, virt_to_phys ( req ), req, sizeof ( *req ) ); + DBGC_HDA ( ena, virt_to_phys ( *rsp ), *rsp, sizeof ( **rsp ) ); + return rc; +} + +/** + * Create submission queue + * + * @v ena ENA device + * @v sq Submission queue + * @v cq Corresponding completion queue + * @ret rc Return status code + */ +static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, + struct ena_cq *cq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Allocate submission queue entries */ + sq->sqe.raw = malloc_dma ( sq->len, ENA_ALIGN ); + if ( ! sq->sqe.raw ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( sq->sqe.raw, 0, sq->len ); + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_CREATE_SQ; + req->create_sq.direction = sq->direction; + req->create_sq.policy = cpu_to_le16 ( ENA_SQ_HOST_MEMORY | + ENA_SQ_CONTIGUOUS ); + req->create_sq.cq_id = cpu_to_le16 ( cq->id ); + req->create_sq.count = cpu_to_le16 ( sq->count ); + req->create_sq.address = cpu_to_le64 ( virt_to_bus ( sq->sqe.raw ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + goto err_admin; + + /* Parse response */ + sq->id = le16_to_cpu ( rsp->create_sq.id ); + sq->doorbell = le32_to_cpu ( rsp->create_sq.doorbell ); + + /* Reset producer counter and phase */ + sq->prod = 0; + sq->phase = ENA_SQE_PHASE; + + DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) db +%04x CQ%d\n", + ena, ena_direction ( sq->direction ), sq->id, + virt_to_phys ( sq->sqe.raw ), + ( virt_to_phys ( sq->sqe.raw ) + sq->len ), + sq->doorbell, cq->id ); + return 0; + + err_admin: + free_dma ( sq->sqe.raw, sq->len ); + err_alloc: + return rc; +} + +/** + * Destroy submission queue + * + * @v ena ENA device + * @v sq Submission queue + * @ret rc Return status code + */ +static int ena_destroy_sq ( struct ena_nic *ena, struct ena_sq *sq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_DESTROY_SQ; + req->destroy_sq.id = cpu_to_le16 ( sq->id ); + req->destroy_sq.direction = sq->direction; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Free submission queue entries */ + free_dma ( sq->sqe.raw, sq->len ); + + DBGC ( ena, "ENA %p %s SQ%d destroyed\n", + ena, ena_direction ( sq->direction ), sq->id ); + return 0; +} + +/** + * Create completion queue + * + * @v ena ENA device + * @v cq Completion queue + * @ret rc Return status code + */ +static int ena_create_cq ( struct ena_nic *ena, struct ena_cq *cq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Allocate completion queue entries */ + cq->cqe.raw = malloc_dma ( cq->len, ENA_ALIGN ); + if ( ! cq->cqe.raw ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( cq->cqe.raw, 0, cq->len ); + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_CREATE_CQ; + req->create_cq.size = cq->size; + req->create_cq.count = cpu_to_le16 ( cq->requested ); + req->create_cq.address = cpu_to_le64 ( virt_to_bus ( cq->cqe.raw ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + goto err_admin; + + /* Parse response */ + cq->id = le16_to_cpu ( rsp->create_cq.id ); + cq->actual = le16_to_cpu ( rsp->create_cq.count ); + cq->doorbell = le32_to_cpu ( rsp->create_cq.doorbell ); + cq->mask = ( cq->actual - 1 ); + if ( cq->actual != cq->requested ) { + DBGC ( ena, "ENA %p CQ%d requested %d actual %d\n", + ena, cq->id, cq->requested, cq->actual ); + } + + /* Reset consumer counter and phase */ + cq->cons = 0; + cq->phase = ENA_CQE_PHASE; + + DBGC ( ena, "ENA %p CQ%d at [%08lx,%08lx) db +%04x\n", + ena, cq->id, virt_to_phys ( cq->cqe.raw ), + ( virt_to_phys ( cq->cqe.raw ) + cq->len ), cq->doorbell ); + return 0; + + err_admin: + free_dma ( cq->cqe.raw, cq->len ); + err_alloc: + return rc; +} + +/** + * Destroy completion queue + * + * @v ena ENA device + * @v cq Completion queue + * @ret rc Return status code + */ +static int ena_destroy_cq ( struct ena_nic *ena, struct ena_cq *cq ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_DESTROY_CQ; + req->destroy_cq.id = cpu_to_le16 ( cq->id ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Free completion queue entries */ + free_dma ( cq->cqe.raw, cq->len ); + + DBGC ( ena, "ENA %p CQ%d destroyed\n", ena, cq->id ); + return 0; +} + +/** + * Create queue pair + * + * @v ena ENA device + * @v qp Queue pair + * @ret rc Return status code + */ +static int ena_create_qp ( struct ena_nic *ena, struct ena_qp *qp ) { + int rc; + + /* Create completion queue */ + if ( ( rc = ena_create_cq ( ena, &qp->cq ) ) != 0 ) + goto err_create_cq; + + /* Create submission queue */ + if ( ( rc = ena_create_sq ( ena, &qp->sq, &qp->cq ) ) != 0 ) + goto err_create_sq; + + return 0; + + ena_destroy_sq ( ena, &qp->sq ); + err_create_sq: + ena_destroy_cq ( ena, &qp->cq ); + err_create_cq: + return rc; +} + +/** + * Destroy queue pair + * + * @v ena ENA device + * @v qp Queue pair + * @ret rc Return status code + */ +static int ena_destroy_qp ( struct ena_nic *ena, struct ena_qp *qp ) { + + /* Destroy submission queue */ + ena_destroy_sq ( ena, &qp->sq ); + + /* Destroy completion queue */ + ena_destroy_cq ( ena, &qp->cq ); + + return 0; +} + +/** + * Get device attributes + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ena_get_device_attributes ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + union ena_aq_req *req; + union ena_acq_rsp *rsp; + union ena_feature *feature; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_GET_FEATURE; + req->get_feature.id = ENA_DEVICE_ATTRIBUTES; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Parse response */ + feature = &rsp->get_feature.feature; + memcpy ( netdev->hw_addr, feature->device.mac, ETH_ALEN ); + netdev->max_pkt_len = le32_to_cpu ( feature->device.mtu ); + + DBGC ( ena, "ENA %p MAC %s MTU %zd\n", + ena, eth_ntoa ( netdev->hw_addr ), netdev->max_pkt_len ); + return 0; +} + +/** + * Get statistics (for debugging) + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_get_stats ( struct ena_nic *ena ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + struct ena_get_stats_rsp *stats; + int rc; + + /* Do nothing unless debug messages are enabled */ + if ( ! DBG_LOG ) + return 0; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_GET_STATS; + req->get_stats.type = ENA_STATS_TYPE_BASIC; + req->get_stats.scope = ENA_STATS_SCOPE_ETH; + req->get_stats.device = ENA_DEVICE_MINE; + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + /* Parse response */ + stats = &rsp->get_stats; + DBGC ( ena, "ENA %p TX bytes %#llx packets %#llx\n", ena, + ( ( unsigned long long ) le64_to_cpu ( stats->tx_bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( stats->tx_packets ) ) ); + DBGC ( ena, "ENA %p RX bytes %#llx packets %#llx drops %#llx\n", ena, + ( ( unsigned long long ) le64_to_cpu ( stats->rx_bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( stats->rx_packets ) ), + ( ( unsigned long long ) le64_to_cpu ( stats->rx_drops ) ) ); + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Refill receive queue + * + * @v netdev Network device + */ +static void ena_refill_rx ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + struct io_buffer *iobuf; + struct ena_rx_sqe *sqe; + unsigned int index; + physaddr_t address; + size_t len = netdev->max_pkt_len; + unsigned int refilled = 0; + + /* Refill queue */ + while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ENA_RX_COUNT ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next submission queue entry */ + index = ( ena->rx.sq.prod % ENA_RX_COUNT ); + sqe = &ena->rx.sq.sqe.rx[index]; + + /* Construct submission queue entry */ + address = virt_to_bus ( iobuf->data ); + sqe->len = cpu_to_le16 ( len ); + sqe->id = cpu_to_le16 ( ena->rx.sq.prod ); + sqe->address = cpu_to_le64 ( address ); + wmb(); + sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | + ena->rx.sq.phase ); + + /* Increment producer counter */ + ena->rx.sq.prod++; + if ( ( ena->rx.sq.prod % ENA_RX_COUNT ) == 0 ) + ena->rx.sq.phase ^= ENA_SQE_PHASE; + + /* Record I/O buffer */ + assert ( ena->rx_iobuf[index] == NULL ); + ena->rx_iobuf[index] = iobuf; + + DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, sqe->id, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + refilled++; + } + + /* Ring doorbell, if applicable */ + if ( refilled ) { + wmb(); + writel ( ena->rx.sq.prod, ( ena->regs + ena->rx.sq.doorbell ) ); + } +} + +/** + * Discard unused receive I/O buffers + * + * @v ena ENA device + */ +static void ena_empty_rx ( struct ena_nic *ena ) { + unsigned int i; + + for ( i = 0 ; i < ENA_RX_COUNT ; i++ ) { + if ( ena->rx_iobuf[i] ) + free_iob ( ena->rx_iobuf[i] ); + ena->rx_iobuf[i] = NULL; + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ena_open ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + int rc; + + /* Create transmit queue pair */ + if ( ( rc = ena_create_qp ( ena, &ena->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive queue pair */ + if ( ( rc = ena_create_qp ( ena, &ena->rx ) ) != 0 ) + goto err_create_rx; + + /* Refill receive queue */ + ena_refill_rx ( netdev ); + + return 0; + + ena_destroy_qp ( ena, &ena->rx ); + err_create_rx: + ena_destroy_qp ( ena, &ena->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ena_close ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + + /* Dump statistics (for debugging) */ + ena_get_stats ( ena ); + + /* Destroy receive queue pair */ + ena_destroy_qp ( ena, &ena->rx ); + + /* Discard any unused receive buffers */ + ena_empty_rx ( ena ); + + /* Destroy transmit queue pair */ + ena_destroy_qp ( ena, &ena->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct ena_nic *ena = netdev->priv; + struct ena_tx_sqe *sqe; + unsigned int index; + physaddr_t address; + size_t len; + + /* Get next submission queue entry */ + if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ENA_TX_COUNT ) { + DBGC ( ena, "ENA %p out of transmit descriptors\n", ena ); + return -ENOBUFS; + } + index = ( ena->tx.sq.prod % ENA_TX_COUNT ); + sqe = &ena->tx.sq.sqe.tx[index]; + + /* Construct submission queue entry */ + address = virt_to_bus ( iobuf->data ); + len = iob_len ( iobuf ); + sqe->len = cpu_to_le16 ( len ); + sqe->id = ena->tx.sq.prod; + sqe->address = cpu_to_le64 ( address ); + wmb(); + sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | + ena->tx.sq.phase ); + wmb(); + + /* Increment producer counter */ + ena->tx.sq.prod++; + if ( ( ena->tx.sq.prod % ENA_TX_COUNT ) == 0 ) + ena->tx.sq.phase ^= ENA_SQE_PHASE; + + /* Ring doorbell */ + writel ( ena->tx.sq.prod, ( ena->regs + ena->tx.sq.doorbell ) ); + + DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, sqe->id, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + return 0; +} + +/** + * Poll for completed transmissions + * + * @v netdev Network device + */ +static void ena_poll_tx ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + struct ena_tx_cqe *cqe; + unsigned int index; + + /* Check for completed packets */ + while ( ena->tx.cq.cons != ena->tx.sq.prod ) { + + /* Get next completion queue entry */ + index = ( ena->tx.cq.cons & ena->tx.cq.mask ); + cqe = &ena->tx.cq.cqe.tx[index]; + + /* Stop if completion queue entry is empty */ + if ( ( cqe->flags ^ ena->tx.cq.phase ) & ENA_CQE_PHASE ) + return; + DBGC2 ( ena, "ENA %p TX %d complete\n", ena, + ( le16_to_cpu ( cqe->id ) >> 2 /* Don't ask */ ) ); + + /* Increment consumer counter */ + ena->tx.cq.cons++; + if ( ! ( ena->tx.cq.cons & ena->tx.cq.mask ) ) + ena->tx.cq.phase ^= ENA_CQE_PHASE; + + /* Complete transmit */ + netdev_tx_complete_next ( netdev ); + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void ena_poll_rx ( struct net_device *netdev ) { + struct ena_nic *ena = netdev->priv; + struct ena_rx_cqe *cqe; + struct io_buffer *iobuf; + unsigned int index; + size_t len; + + /* Check for received packets */ + while ( ena->rx.cq.cons != ena->rx.sq.prod ) { + + /* Get next completion queue entry */ + index = ( ena->rx.cq.cons % ENA_RX_COUNT ); + cqe = &ena->rx.cq.cqe.rx[index]; + + /* Stop if completion queue entry is empty */ + if ( ( cqe->flags ^ ena->rx.cq.phase ) & ENA_CQE_PHASE ) + return; + + /* Increment consumer counter */ + ena->rx.cq.cons++; + if ( ! ( ena->rx.cq.cons & ena->rx.cq.mask ) ) + ena->rx.cq.phase ^= ENA_CQE_PHASE; + + /* Populate I/O buffer */ + iobuf = ena->rx_iobuf[index]; + ena->rx_iobuf[index] = NULL; + len = le16_to_cpu ( cqe->len ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + DBGC2 ( ena, "ENA %p RX %d complete (length %zd)\n", + ena, le16_to_cpu ( cqe->id ), len ); + netdev_rx ( netdev, iobuf ); + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void ena_poll ( struct net_device *netdev ) { + + /* Poll for transmit completions */ + ena_poll_tx ( netdev ); + + /* Poll for receive completions */ + ena_poll_rx ( netdev ); + + /* Refill receive ring */ + ena_refill_rx ( netdev ); +} + +/** ENA network device operations */ +static struct net_device_operations ena_operations = { + .open = ena_open, + .close = ena_close, + .transmit = ena_transmit, + .poll = ena_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ena_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct ena_nic *ena; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *ena ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ena_operations ); + ena = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( ena, 0, sizeof ( *ena ) ); + ena->acq.phase = ENA_ACQ_PHASE; + ena_cq_init ( &ena->tx.cq, ENA_TX_COUNT, + sizeof ( ena->tx.cq.cqe.tx[0] ) ); + ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, + sizeof ( ena->tx.sq.sqe.tx[0] ) ); + ena_cq_init ( &ena->rx.cq, ENA_RX_COUNT, + sizeof ( ena->rx.cq.cqe.rx[0] ) ); + ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, + sizeof ( ena->rx.sq.sqe.rx[0] ) ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + ena->regs = ioremap ( pci->membase, ENA_BAR_SIZE ); + if ( ! ena->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the NIC */ + if ( ( rc = ena_reset ( ena ) ) != 0 ) + goto err_reset; + + /* Create admin queues */ + if ( ( rc = ena_create_admin ( ena ) ) != 0 ) + goto err_create_admin; + + /* Fetch MAC address */ + if ( ( rc = ena_get_device_attributes ( netdev ) ) != 0 ) + goto err_get_device_attributes; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Mark as link up, since we have no way to test link state on + * this hardware. + */ + netdev_link_up ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_get_device_attributes: + ena_destroy_admin ( ena ); + err_create_admin: + ena_reset ( ena ); + err_reset: + iounmap ( ena->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void ena_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct ena_nic *ena = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Destroy admin queues */ + ena_destroy_admin ( ena ); + + /* Reset card */ + ena_reset ( ena ); + + /* Free network device */ + iounmap ( ena->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** ENA PCI device IDs */ +static struct pci_device_id ena_nics[] = { + PCI_ROM ( 0x1d0f, 0xec20, "ena-vf", "ENA VF", 0 ), + PCI_ROM ( 0x1d0f, 0xec21, "ena-vf-llq", "ENA VF (LLQ)", 0 ), +}; + +/** ENA PCI driver */ +struct pci_driver ena_driver __pci_driver = { + .ids = ena_nics, + .id_count = ( sizeof ( ena_nics ) / sizeof ( ena_nics[0] ) ), + .probe = ena_probe, + .remove = ena_remove, +}; diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h new file mode 100644 index 000000000..0496fc6bd --- /dev/null +++ b/src/drivers/net/ena.h @@ -0,0 +1,588 @@ +#ifndef _ENA_H +#define _ENA_H + +/** @file + * + * Amazon ENA network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** BAR size */ +#define ENA_BAR_SIZE 16384 + +/** Queue alignment */ +#define ENA_ALIGN 4096 + +/** Number of admin queue entries */ +#define ENA_AQ_COUNT 2 + +/** Number of admin completion queue entries */ +#define ENA_ACQ_COUNT 2 + +/** Number of transmit queue entries */ +#define ENA_TX_COUNT 16 + +/** Number of receive queue entries */ +#define ENA_RX_COUNT 16 + +/** Base address low register offset */ +#define ENA_BASE_LO 0x0 + +/** Base address high register offset */ +#define ENA_BASE_HI 0x4 + +/** Capability register value */ +#define ENA_CAPS( count, size ) ( ( (size) << 16 ) | ( (count) << 0 ) ) + +/** Admin queue base address register */ +#define ENA_AQ_BASE 0x10 + +/** Admin queue capabilities register */ +#define ENA_AQ_CAPS 0x18 + +/** Admin completion queue base address register */ +#define ENA_ACQ_BASE 0x20 + +/** Admin completion queue capabilities register */ +#define ENA_ACQ_CAPS 0x28 + +/** Admin queue doorbell register */ +#define ENA_AQ_DB 0x2c + +/** Maximum time to wait for admin requests */ +#define ENA_ADMIN_MAX_WAIT_MS 5000 + +/** Device control register */ +#define ENA_CTRL 0x54 +#define ENA_CTRL_RESET 0x00000001UL /**< Reset */ + +/** Maximum time to wait for reset */ +#define ENA_RESET_MAX_WAIT_MS 1000 + +/** Device status register */ +#define ENA_STAT 0x58 +#define ENA_STAT_READY 0x00000001UL /**< Ready */ + +/** Admin queue entry header */ +struct ena_aq_header { + /** Request identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved; + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; +} __attribute__ (( packed )); + +/** Admin queue ownership phase flag */ +#define ENA_AQ_PHASE 0x01 + +/** Admin completion queue entry header */ +struct ena_acq_header { + /** Request identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Extended status */ + uint16_t ext; + /** Consumer index */ + uint16_t cons; +} __attribute__ (( packed )); + +/** Admin completion queue ownership phase flag */ +#define ENA_ACQ_PHASE 0x01 + +/** Device attributes */ +#define ENA_DEVICE_ATTRIBUTES 1 + +/** Device attributes */ +struct ena_device_attributes { + /** Implementation */ + uint32_t implementation; + /** Device version */ + uint32_t version; + /** Supported features */ + uint32_t features; + /** Reserved */ + uint8_t reserved_a[4]; + /** Physical address width */ + uint32_t physical; + /** Virtual address width */ + uint32_t virtual; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Reserved */ + uint8_t reserved_b[2]; + /** Maximum MTU */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Feature */ +union ena_feature { + /** Device attributes */ + struct ena_device_attributes device; +}; + +/** Submission queue direction */ +enum ena_sq_direction { + /** Transmit */ + ENA_SQ_TX = 0x20, + /** Receive */ + ENA_SQ_RX = 0x40, +}; + +/** Create submission queue */ +#define ENA_CREATE_SQ 1 + +/** Create submission queue request */ +struct ena_create_sq_req { + /** Header */ + struct ena_aq_header header; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved_a; + /** Policy */ + uint16_t policy; + /** Completion queue identifier */ + uint16_t cq_id; + /** Number of entries */ + uint16_t count; + /** Base address */ + uint64_t address; + /** Writeback address */ + uint64_t writeback; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Submission queue policy */ +enum ena_sq_policy { + /** Use host memory */ + ENA_SQ_HOST_MEMORY = 0x0001, + /** Memory is contiguous */ + ENA_SQ_CONTIGUOUS = 0x0100, +}; + +/** Create submission queue response */ +struct ena_create_sq_rsp { + /** Header */ + struct ena_acq_header header; + /** Submission queue identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved[2]; + /** Doorbell register offset */ + uint32_t doorbell; + /** LLQ descriptor ring offset */ + uint32_t llq_desc; + /** LLQ header offset */ + uint32_t llq_data; +} __attribute__ (( packed )); + +/** Destroy submission queue */ +#define ENA_DESTROY_SQ 2 + +/** Destroy submission queue request */ +struct ena_destroy_sq_req { + /** Header */ + struct ena_aq_header header; + /** Submission queue identifier */ + uint16_t id; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** Destroy submission queue response */ +struct ena_destroy_sq_rsp { + /** Header */ + struct ena_acq_header header; +} __attribute__ (( packed )); + +/** Create completion queue */ +#define ENA_CREATE_CQ 3 + +/** Create completion queue request */ +struct ena_create_cq_req { + /** Header */ + struct ena_aq_header header; + /** Interrupts enabled */ + uint8_t intr; + /** Entry size (in 32-bit words) */ + uint8_t size; + /** Number of entries */ + uint16_t count; + /** MSI-X vector */ + uint32_t vector; + /** Base address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Create completion queue response */ +struct ena_create_cq_rsp { + /** Header */ + struct ena_acq_header header; + /** Completion queue identifier */ + uint16_t id; + /** Actual number of entries */ + uint16_t count; + /** NUMA node register offset */ + uint32_t node; + /** Doorbell register offset */ + uint32_t doorbell; + /** Interrupt unmask register offset */ + uint32_t intr; +} __attribute__ (( packed )); + +/** Destroy completion queue */ +#define ENA_DESTROY_CQ 4 + +/** Destroy completion queue request */ +struct ena_destroy_cq_req { + /** Header */ + struct ena_aq_header header; + /** Completion queue identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved[2]; +} __attribute__ (( packed )); + +/** Destroy completion queue response */ +struct ena_destroy_cq_rsp { + /** Header */ + struct ena_acq_header header; +} __attribute__ (( packed )); + +/** Get feature */ +#define ENA_GET_FEATURE 8 + +/** Get feature request */ +struct ena_get_feature_req { + /** Header */ + struct ena_aq_header header; + /** Length */ + uint32_t len; + /** Address */ + uint64_t address; + /** Flags */ + uint8_t flags; + /** Feature identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved[2]; +} __attribute__ (( packed )); + +/** Get feature response */ +struct ena_get_feature_rsp { + /** Header */ + struct ena_acq_header header; + /** Feature */ + union ena_feature feature; +} __attribute__ (( packed )); + +/** Get statistics */ +#define ENA_GET_STATS 11 + +/** Get statistics request */ +struct ena_get_stats_req { + /** Header */ + struct ena_aq_header header; + /** Reserved */ + uint8_t reserved_a[12]; + /** Type */ + uint8_t type; + /** Scope */ + uint8_t scope; + /** Reserved */ + uint8_t reserved_b[2]; + /** Queue ID */ + uint16_t queue; + /** Device ID */ + uint16_t device; +} __attribute__ (( packed )); + +/** Basic statistics */ +#define ENA_STATS_TYPE_BASIC 0 + +/** Ethernet statistics */ +#define ENA_STATS_SCOPE_ETH 1 + +/** My device */ +#define ENA_DEVICE_MINE 0xffff + +/** Get statistics response */ +struct ena_get_stats_rsp { + /** Header */ + struct ena_acq_header header; + /** Transmit byte count */ + uint64_t tx_bytes; + /** Transmit packet count */ + uint64_t tx_packets; + /** Receive byte count */ + uint64_t rx_bytes; + /** Receive packet count */ + uint64_t rx_packets; + /** Receive drop count */ + uint64_t rx_drops; +} __attribute__ (( packed )); + +/** Admin queue request */ +union ena_aq_req { + /** Header */ + struct ena_aq_header header; + /** Create submission queue */ + struct ena_create_sq_req create_sq; + /** Destroy submission queue */ + struct ena_destroy_sq_req destroy_sq; + /** Create completion queue */ + struct ena_create_cq_req create_cq; + /** Destroy completion queue */ + struct ena_destroy_cq_req destroy_cq; + /** Get feature */ + struct ena_get_feature_req get_feature; + /** Get statistics */ + struct ena_get_stats_req get_stats; + /** Padding */ + uint8_t pad[64]; +}; + +/** Admin completion queue response */ +union ena_acq_rsp { + /** Header */ + struct ena_acq_header header; + /** Create submission queue */ + struct ena_create_sq_rsp create_sq; + /** Destroy submission queue */ + struct ena_destroy_sq_rsp destroy_sq; + /** Create completion queue */ + struct ena_create_cq_rsp create_cq; + /** Destroy completion queue */ + struct ena_destroy_cq_rsp destroy_cq; + /** Get feature */ + struct ena_get_feature_rsp get_feature; + /** Get statistics */ + struct ena_get_stats_rsp get_stats; + /** Padding */ + uint8_t pad[64]; +}; + +/** Admin queue */ +struct ena_aq { + /** Requests */ + union ena_aq_req *req; + /** Producer counter */ + unsigned int prod; +}; + +/** Admin completion queue */ +struct ena_acq { + /** Responses */ + union ena_acq_rsp *rsp; + /** Consumer counter */ + unsigned int cons; + /** Phase */ + unsigned int phase; +}; + +/** Transmit submission queue entry */ +struct ena_tx_sqe { + /** Length */ + uint16_t len; + /** Reserved */ + uint8_t reserved_a; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[3]; + /** Request identifier */ + uint8_t id; + /** Address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Receive submission queue entry */ +struct ena_rx_sqe { + /** Length */ + uint16_t len; + /** Reserved */ + uint8_t reserved_a; + /** Flags */ + uint8_t flags; + /** Request identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[2]; + /** Address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Submission queue ownership phase flag */ +#define ENA_SQE_PHASE 0x01 + +/** This is the first descriptor */ +#define ENA_SQE_FIRST 0x04 + +/** This is the last descriptor */ +#define ENA_SQE_LAST 0x08 + +/** Request completion */ +#define ENA_SQE_CPL 0x10 + +/** Transmit completion queue entry */ +struct ena_tx_cqe { + /** Request identifier */ + uint16_t id; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[2]; + /** Consumer index */ + uint16_t cons; +} __attribute__ (( packed )); + +/** Receive completion queue entry */ +struct ena_rx_cqe { + /** Reserved */ + uint8_t reserved_a[3]; + /** Flags */ + uint8_t flags; + /** Length */ + uint16_t len; + /** Request identifier */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Completion queue ownership phase flag */ +#define ENA_CQE_PHASE 0x01 + +/** Submission queue */ +struct ena_sq { + /** Entries */ + union { + /** Transmit submission queue entries */ + struct ena_tx_sqe *tx; + /** Receive submission queue entries */ + struct ena_rx_sqe *rx; + /** Raw data */ + void *raw; + } sqe; + /** Doorbell register offset */ + unsigned int doorbell; + /** Total length of entries */ + size_t len; + /** Producer counter */ + unsigned int prod; + /** Phase */ + unsigned int phase; + /** Submission queue identifier */ + uint16_t id; + /** Direction */ + uint8_t direction; + /** Number of entries */ + uint8_t count; +}; + +/** + * Initialise submission queue + * + * @v sq Submission queue + * @v direction Direction + * @v count Number of entries + * @v size Size of each entry + */ +static inline __attribute__ (( always_inline )) void +ena_sq_init ( struct ena_sq *sq, unsigned int direction, unsigned int count, + size_t size ) { + + sq->len = ( count * size ); + sq->direction = direction; + sq->count = count; +} + +/** Completion queue */ +struct ena_cq { + /** Entries */ + union { + /** Transmit completion queue entries */ + struct ena_tx_cqe *tx; + /** Receive completion queue entries */ + struct ena_rx_cqe *rx; + /** Raw data */ + void *raw; + } cqe; + /** Doorbell register offset */ + unsigned int doorbell; + /** Total length of entries */ + size_t len; + /** Consumer counter */ + unsigned int cons; + /** Phase */ + unsigned int phase; + /** Completion queue identifier */ + uint16_t id; + /** Entry size (in 32-bit words) */ + uint8_t size; + /** Requested number of entries */ + uint8_t requested; + /** Actual number of entries */ + uint8_t actual; + /** Actual number of entries minus one */ + uint8_t mask; +}; + +/** + * Initialise completion queue + * + * @v cq Completion queue + * @v count Number of entries + * @v size Size of each entry + */ +static inline __attribute__ (( always_inline )) void +ena_cq_init ( struct ena_cq *cq, unsigned int count, size_t size ) { + + cq->len = ( count * size ); + cq->size = ( size / sizeof ( uint32_t ) ); + cq->requested = count; +} + +/** Queue pair */ +struct ena_qp { + /** Submission queue */ + struct ena_sq sq; + /** Completion queue */ + struct ena_cq cq; +}; + +/** An ENA network card */ +struct ena_nic { + /** Registers */ + void *regs; + /** Admin queue */ + struct ena_aq aq; + /** Admin completion queue */ + struct ena_acq acq; + /** Transmit queue */ + struct ena_qp tx; + /** Receive queue */ + struct ena_qp rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[ENA_RX_COUNT]; +}; + +#endif /* _ENA_H */ diff --git a/src/drivers/net/eoib.c b/src/drivers/net/eoib.c new file mode 100644 index 000000000..ba2912953 --- /dev/null +++ b/src/drivers/net/eoib.c @@ -0,0 +1,893 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Ethernet over Infiniband + * + */ + +/** Number of EoIB send work queue entries */ +#define EOIB_NUM_SEND_WQES 8 + +/** Number of EoIB receive work queue entries */ +#define EOIB_NUM_RECV_WQES 4 + +/** Number of EoIB completion queue entries */ +#define EOIB_NUM_CQES 16 + +/** Link status for "broadcast join in progress" */ +#define EINPROGRESS_JOINING __einfo_error ( EINFO_EINPROGRESS_JOINING ) +#define EINFO_EINPROGRESS_JOINING __einfo_uniqify \ + ( EINFO_EINPROGRESS, 0x01, "Joining" ) + +/** Human-readable message for the link status */ +struct errortab eoib_errors[] __errortab = { + __einfo_errortab ( EINFO_EINPROGRESS_JOINING ), +}; + +/** List of EoIB devices */ +static LIST_HEAD ( eoib_devices ); + +static struct net_device_operations eoib_operations; + +/**************************************************************************** + * + * EoIB peer cache + * + **************************************************************************** + */ + +/** An EoIB peer cache entry */ +struct eoib_peer { + /** List of EoIB peer cache entries */ + struct list_head list; + /** Ethernet MAC */ + uint8_t mac[ETH_ALEN]; + /** Infiniband address vector */ + struct ib_address_vector av; +}; + +/** + * Find EoIB peer cache entry + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @ret peer EoIB peer, or NULL if not found + */ +static struct eoib_peer * eoib_find_peer ( struct eoib_device *eoib, + const uint8_t *mac ) { + struct eoib_peer *peer; + + /* Find peer cache entry */ + list_for_each_entry ( peer, &eoib->peers, list ) { + if ( memcmp ( mac, peer->mac, sizeof ( peer->mac ) ) == 0 ) { + /* Move peer to start of list */ + list_del ( &peer->list ); + list_add ( &peer->list, &eoib->peers ); + return peer; + } + } + + return NULL; +} + +/** + * Create EoIB peer cache entry + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @ret peer EoIB peer, or NULL on error + */ +static struct eoib_peer * eoib_create_peer ( struct eoib_device *eoib, + const uint8_t *mac ) { + struct eoib_peer *peer; + + /* Allocate and initialise peer cache entry */ + peer = zalloc ( sizeof ( *peer ) ); + if ( peer ) { + memcpy ( peer->mac, mac, sizeof ( peer->mac ) ); + list_add ( &peer->list, &eoib->peers ); + } + return peer; +} + +/** + * Flush EoIB peer cache + * + * @v eoib EoIB device + */ +static void eoib_flush_peers ( struct eoib_device *eoib ) { + struct eoib_peer *peer; + struct eoib_peer *tmp; + + list_for_each_entry_safe ( peer, tmp, &eoib->peers, list ) { + list_del ( &peer->list ); + free ( peer ); + } +} + +/** + * Discard some entries from the peer cache + * + * @ret discarded Number of cached items discarded + */ +static unsigned int eoib_discard ( void ) { + struct net_device *netdev; + struct eoib_device *eoib; + struct eoib_peer *peer; + unsigned int discarded = 0; + + /* Try to discard one cache entry for each EoIB device */ + for_each_netdev ( netdev ) { + + /* Skip non-EoIB devices */ + if ( netdev->op != &eoib_operations ) + continue; + eoib = netdev->priv; + + /* Discard least recently used cache entry (if any) */ + list_for_each_entry_reverse ( peer, &eoib->peers, list ) { + list_del ( &peer->list ); + free ( peer ); + discarded++; + break; + } + } + + return discarded; +} + +/** EoIB cache discarder */ +struct cache_discarder eoib_discarder __cache_discarder ( CACHE_EXPENSIVE ) = { + .discard = eoib_discard, +}; + +/** + * Find destination address vector + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @ret av Address vector, or NULL to send as broadcast + */ +static struct ib_address_vector * eoib_tx_av ( struct eoib_device *eoib, + const uint8_t *mac ) { + struct ib_device *ibdev = eoib->ibdev; + struct eoib_peer *peer; + int rc; + + /* If this is a broadcast or multicast MAC address, then send + * this packet as a broadcast. + */ + if ( is_multicast_ether_addr ( mac ) ) { + DBGCP ( eoib, "EoIB %s %s TX multicast\n", + eoib->name, eth_ntoa ( mac ) ); + return NULL; + } + + /* If we have no peer cache entry, then create one and send + * this packet as a broadcast. + */ + peer = eoib_find_peer ( eoib, mac ); + if ( ! peer ) { + DBGC ( eoib, "EoIB %s %s TX unknown\n", + eoib->name, eth_ntoa ( mac ) ); + eoib_create_peer ( eoib, mac ); + return NULL; + } + + /* If we have not yet recorded a received GID and QPN for this + * peer cache entry, then send this packet as a broadcast. + */ + if ( ! peer->av.gid_present ) { + DBGCP ( eoib, "EoIB %s %s TX not yet recorded\n", + eoib->name, eth_ntoa ( mac ) ); + return NULL; + } + + /* If we have not yet resolved a path to this peer, then send + * this packet as a broadcast. + */ + if ( ( rc = ib_resolve_path ( ibdev, &peer->av ) ) != 0 ) { + DBGCP ( eoib, "EoIB %s %s TX not yet resolved\n", + eoib->name, eth_ntoa ( mac ) ); + return NULL; + } + + /* Force use of GRH even for local destinations */ + peer->av.gid_present = 1; + + /* We have a fully resolved peer: send this packet as a + * unicast. + */ + DBGCP ( eoib, "EoIB %s %s TX " IB_GID_FMT " QPN %#lx\n", eoib->name, + eth_ntoa ( mac ), IB_GID_ARGS ( &peer->av.gid ), peer->av.qpn ); + return &peer->av; +} + +/** + * Record source address vector + * + * @v eoib EoIB device + * @v mac Ethernet MAC + * @v lid Infiniband LID + */ +static void eoib_rx_av ( struct eoib_device *eoib, const uint8_t *mac, + const struct ib_address_vector *av ) { + const union ib_gid *gid = &av->gid; + unsigned long qpn = av->qpn; + struct eoib_peer *peer; + + /* Sanity checks */ + if ( ! av->gid_present ) { + DBGC ( eoib, "EoIB %s %s RX with no GID\n", + eoib->name, eth_ntoa ( mac ) ); + return; + } + + /* Find peer cache entry (if any) */ + peer = eoib_find_peer ( eoib, mac ); + if ( ! peer ) { + DBGCP ( eoib, "EoIB %s %s RX " IB_GID_FMT " (ignored)\n", + eoib->name, eth_ntoa ( mac ), IB_GID_ARGS ( gid ) ); + return; + } + + /* Some dubious EoIB implementations utilise an Ethernet-to- + * EoIB gateway that will send packets from the wrong QPN. + */ + if ( eoib_has_gateway ( eoib ) && + ( memcmp ( gid, &eoib->gateway.gid, sizeof ( *gid ) ) == 0 ) ) { + qpn = eoib->gateway.qpn; + } + + /* Do nothing if peer cache entry is complete and correct */ + if ( ( peer->av.lid == av->lid ) && ( peer->av.qpn == qpn ) ) { + DBGCP ( eoib, "EoIB %s %s RX unchanged\n", + eoib->name, eth_ntoa ( mac ) ); + return; + } + + /* Update peer cache entry */ + peer->av.qpn = qpn; + peer->av.qkey = eoib->broadcast.qkey; + peer->av.gid_present = 1; + memcpy ( &peer->av.gid, gid, sizeof ( peer->av.gid ) ); + DBGC ( eoib, "EoIB %s %s RX " IB_GID_FMT " QPN %#lx\n", eoib->name, + eth_ntoa ( mac ), IB_GID_ARGS ( &peer->av.gid ), peer->av.qpn ); +} + +/**************************************************************************** + * + * EoIB network device + * + **************************************************************************** + */ + +/** + * Transmit packet via EoIB network device + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int eoib_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct eoib_device *eoib = netdev->priv; + struct eoib_header *eoib_hdr; + struct ethhdr *ethhdr; + struct ib_address_vector *av; + size_t zlen; + + /* Sanity checks */ + assert ( iob_len ( iobuf ) >= sizeof ( *ethhdr ) ); + assert ( iob_headroom ( iobuf ) >= sizeof ( *eoib_hdr ) ); + + /* Look up destination address vector */ + ethhdr = iobuf->data; + av = eoib_tx_av ( eoib, ethhdr->h_dest ); + + /* Prepend EoIB header */ + eoib_hdr = iob_push ( iobuf, sizeof ( *eoib_hdr ) ); + eoib_hdr->magic = htons ( EOIB_MAGIC ); + eoib_hdr->reserved = 0; + + /* Pad buffer to minimum Ethernet frame size */ + zlen = ( sizeof ( *eoib_hdr ) + ETH_ZLEN ); + assert ( zlen <= IOB_ZLEN ); + if ( iob_len ( iobuf ) < zlen ) + iob_pad ( iobuf, zlen ); + + /* If we have no unicast address then send as a broadcast, + * with a duplicate sent to the gateway if applicable. + */ + if ( ! av ) { + av = &eoib->broadcast; + if ( eoib_has_gateway ( eoib ) ) + eoib->duplicate ( eoib, iobuf ); + } + + /* Post send work queue entry */ + return ib_post_send ( eoib->ibdev, eoib->qp, av, iobuf ); +} + +/** + * Handle EoIB send completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void eoib_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ) { + struct eoib_device *eoib = ib_qp_get_ownerdata ( qp ); + + netdev_tx_complete_err ( eoib->netdev, iobuf, rc ); +} + +/** + * Handle EoIB receive completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v dest Destination address vector, or NULL + * @v source Source address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void eoib_complete_recv ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct ib_address_vector *dest __unused, + struct ib_address_vector *source, + struct io_buffer *iobuf, int rc ) { + struct eoib_device *eoib = ib_qp_get_ownerdata ( qp ); + struct net_device *netdev = eoib->netdev; + struct eoib_header *eoib_hdr; + struct ethhdr *ethhdr; + + /* Record errors */ + if ( rc != 0 ) { + netdev_rx_err ( netdev, iobuf, rc ); + return; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *eoib_hdr ) + sizeof ( *ethhdr ) )){ + DBGC ( eoib, "EoIB %s received packet too short to " + "contain EoIB and Ethernet headers\n", eoib->name ); + DBGC_HD ( eoib, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + return; + } + if ( ! source ) { + DBGC ( eoib, "EoIB %s received packet without address " + "vector\n", eoib->name ); + netdev_rx_err ( netdev, iobuf, -ENOTTY ); + return; + } + + /* Strip EoIB header */ + iob_pull ( iobuf, sizeof ( *eoib_hdr ) ); + + /* Update neighbour cache entry, if any */ + ethhdr = iobuf->data; + eoib_rx_av ( eoib, ethhdr->h_source, source ); + + /* Hand off to network layer */ + netdev_rx ( netdev, iobuf ); +} + +/** EoIB completion operations */ +static struct ib_completion_queue_operations eoib_cq_op = { + .complete_send = eoib_complete_send, + .complete_recv = eoib_complete_recv, +}; + +/** EoIB queue pair operations */ +static struct ib_queue_pair_operations eoib_qp_op = { + .alloc_iob = alloc_iob, +}; + +/** + * Poll EoIB network device + * + * @v netdev Network device + */ +static void eoib_poll ( struct net_device *netdev ) { + struct eoib_device *eoib = netdev->priv; + struct ib_device *ibdev = eoib->ibdev; + + /* Poll Infiniband device */ + ib_poll_eq ( ibdev ); + + /* Poll the retry timers (required for EoIB multicast join) */ + retry_poll(); +} + +/** + * Handle EoIB broadcast multicast group join completion + * + * @v membership Multicast group membership + * @v rc Status code + */ +static void eoib_join_complete ( struct ib_mc_membership *membership, int rc ) { + struct eoib_device *eoib = + container_of ( membership, struct eoib_device, membership ); + + /* Record join status as link status */ + netdev_link_err ( eoib->netdev, rc ); +} + +/** + * Join EoIB broadcast multicast group + * + * @v eoib EoIB device + * @ret rc Return status code + */ +static int eoib_join_broadcast_group ( struct eoib_device *eoib ) { + int rc; + + /* Join multicast group */ + if ( ( rc = ib_mcast_join ( eoib->ibdev, eoib->qp, + &eoib->membership, &eoib->broadcast, + eoib->mask, eoib_join_complete ) ) != 0 ) { + DBGC ( eoib, "EoIB %s could not join broadcast group: %s\n", + eoib->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Leave EoIB broadcast multicast group + * + * @v eoib EoIB device + */ +static void eoib_leave_broadcast_group ( struct eoib_device *eoib ) { + + /* Leave multicast group */ + ib_mcast_leave ( eoib->ibdev, eoib->qp, &eoib->membership ); +} + +/** + * Handle link status change + * + * @v eoib EoIB device + */ +static void eoib_link_state_changed ( struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + struct ib_device *ibdev = eoib->ibdev; + int rc; + + /* Leave existing broadcast group */ + if ( eoib->qp ) + eoib_leave_broadcast_group ( eoib ); + + /* Update broadcast GID based on potentially-new partition key */ + eoib->broadcast.gid.words[2] = htons ( ibdev->pkey | IB_PKEY_FULL ); + + /* Set net device link state to reflect Infiniband link state */ + rc = ib_link_rc ( ibdev ); + netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) ); + + /* Join new broadcast group */ + if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && eoib->qp && + ( ( rc = eoib_join_broadcast_group ( eoib ) ) != 0 ) ) { + DBGC ( eoib, "EoIB %s could not rejoin broadcast group: " + "%s\n", eoib->name, strerror ( rc ) ); + netdev_link_err ( netdev, rc ); + return; + } +} + +/** + * Open EoIB network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int eoib_open ( struct net_device *netdev ) { + struct eoib_device *eoib = netdev->priv; + struct ib_device *ibdev = eoib->ibdev; + int rc; + + /* Open IB device */ + if ( ( rc = ib_open ( ibdev ) ) != 0 ) { + DBGC ( eoib, "EoIB %s could not open %s: %s\n", + eoib->name, ibdev->name, strerror ( rc ) ); + goto err_ib_open; + } + + /* Allocate completion queue */ + 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 */ + 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, &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 ); + + /* Fill receive rings */ + ib_refill_recv ( ibdev, eoib->qp ); + + /* Fake a link status change to join the broadcast group */ + eoib_link_state_changed ( eoib ); + + return 0; + + ib_destroy_qp ( ibdev, eoib->qp ); + eoib->qp = NULL; + err_create_qp: + ib_destroy_cq ( ibdev, eoib->cq ); + eoib->cq = NULL; + err_create_cq: + ib_close ( ibdev ); + err_ib_open: + return rc; +} + +/** + * Close EoIB network device + * + * @v netdev Network device + */ +static void eoib_close ( struct net_device *netdev ) { + struct eoib_device *eoib = netdev->priv; + struct ib_device *ibdev = eoib->ibdev; + + /* Flush peer cache */ + eoib_flush_peers ( eoib ); + + /* Leave broadcast group */ + eoib_leave_broadcast_group ( eoib ); + + /* Tear down the queues */ + ib_destroy_qp ( ibdev, eoib->qp ); + eoib->qp = NULL; + ib_destroy_cq ( ibdev, eoib->cq ); + eoib->cq = NULL; + + /* Close IB device */ + ib_close ( ibdev ); +} + +/** EoIB network device operations */ +static struct net_device_operations eoib_operations = { + .open = eoib_open, + .close = eoib_close, + .transmit = eoib_transmit, + .poll = eoib_poll, +}; + +/** + * Create EoIB device + * + * @v ibdev Infiniband device + * @v hw_addr Ethernet MAC + * @v broadcast Broadcast address vector + * @v name Interface name (or NULL to use default) + * @ret rc Return status code + */ +int eoib_create ( struct ib_device *ibdev, const uint8_t *hw_addr, + struct ib_address_vector *broadcast, const char *name ) { + struct net_device *netdev; + struct eoib_device *eoib; + int rc; + + /* Allocate network device */ + netdev = alloc_etherdev ( sizeof ( *eoib ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &eoib_operations ); + eoib = netdev->priv; + netdev->dev = ibdev->dev; + eoib->netdev = netdev; + eoib->ibdev = ibdev_get ( ibdev ); + memcpy ( &eoib->broadcast, broadcast, sizeof ( eoib->broadcast ) ); + INIT_LIST_HEAD ( &eoib->peers ); + + /* Set MAC address */ + memcpy ( netdev->hw_addr, hw_addr, ETH_ALEN ); + + /* Set interface name, if applicable */ + if ( name ) + snprintf ( netdev->name, sizeof ( netdev->name ), "%s", name ); + eoib->name = netdev->name; + + /* Add to list of EoIB devices */ + list_add_tail ( &eoib->list, &eoib_devices ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + DBGC ( eoib, "EoIB %s created for %s MAC %s\n", + eoib->name, ibdev->name, eth_ntoa ( hw_addr ) ); + DBGC ( eoib, "EoIB %s broadcast GID " IB_GID_FMT "\n", + eoib->name, IB_GID_ARGS ( &broadcast->gid ) ); + return 0; + + unregister_netdev ( netdev ); + err_register: + list_del ( &eoib->list ); + ibdev_put ( ibdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Find EoIB device + * + * @v ibdev Infiniband device + * @v hw_addr Original Ethernet MAC + * @ret eoib EoIB device + */ +struct eoib_device * eoib_find ( struct ib_device *ibdev, + const uint8_t *hw_addr ) { + struct eoib_device *eoib; + + list_for_each_entry ( eoib, &eoib_devices, list ) { + if ( ( eoib->ibdev == ibdev ) && + ( memcmp ( eoib->netdev->hw_addr, hw_addr, + ETH_ALEN ) == 0 ) ) + return eoib; + } + return NULL; +} + +/** + * Remove EoIB device + * + * @v eoib EoIB device + */ +void eoib_destroy ( struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Remove from list of network devices */ + list_del ( &eoib->list ); + + /* Drop reference to Infiniband device */ + ibdev_put ( eoib->ibdev ); + + /* Free network device */ + DBGC ( eoib, "EoIB %s destroyed\n", eoib->name ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** + * Probe EoIB device + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int eoib_probe ( struct ib_device *ibdev __unused ) { + + /* EoIB devices are not created automatically */ + return 0; +} + +/** + * Handle device or link status change + * + * @v ibdev Infiniband device + */ +static void eoib_notify ( struct ib_device *ibdev ) { + struct eoib_device *eoib; + + /* Handle link status change for any attached EoIB devices */ + list_for_each_entry ( eoib, &eoib_devices, list ) { + if ( eoib->ibdev != ibdev ) + continue; + eoib_link_state_changed ( eoib ); + } +} + +/** + * Remove EoIB device + * + * @v ibdev Infiniband device + */ +static void eoib_remove ( struct ib_device *ibdev ) { + struct eoib_device *eoib; + struct eoib_device *tmp; + + /* Remove any attached EoIB devices */ + list_for_each_entry_safe ( eoib, tmp, &eoib_devices, list ) { + if ( eoib->ibdev != ibdev ) + continue; + eoib_destroy ( eoib ); + } +} + +/** EoIB driver */ +struct ib_driver eoib_driver __ib_driver = { + .name = "EoIB", + .probe = eoib_probe, + .notify = eoib_notify, + .remove = eoib_remove, +}; + +/**************************************************************************** + * + * EoIB heartbeat packets + * + **************************************************************************** + */ + +/** + * Silently ignore incoming EoIB heartbeat packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @v flags Packet flags + * @ret rc Return status code + */ +static int eoib_heartbeat_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + const void *ll_dest __unused, + const void *ll_source __unused, + unsigned int flags __unused ) { + free_iob ( iobuf ); + return 0; +} + +/** + * Transcribe EoIB heartbeat address + * + * @v net_addr EoIB heartbeat address + * @ret string "" + * + * This operation is meaningless for the EoIB heartbeat protocol. + */ +static const char * eoib_heartbeat_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** EoIB heartbeat network protocol */ +struct net_protocol eoib_heartbeat_protocol __net_protocol = { + .name = "EoIB", + .net_proto = htons ( EOIB_MAGIC ), + .rx = eoib_heartbeat_rx, + .ntoa = eoib_heartbeat_ntoa, +}; + +/**************************************************************************** + * + * EoIB gateway + * + **************************************************************************** + * + * Some dubious EoIB implementations require all broadcast traffic to + * be sent twice: once to the actual broadcast group, and once as a + * unicast to the EoIB-to-Ethernet gateway. This somewhat curious + * design arises since the EoIB-to-Ethernet gateway hardware lacks the + * ability to attach a queue pair to a multicast GID (or LID), and so + * cannot receive traffic sent to the broadcast group. + * + */ + +/** + * Transmit duplicate packet to the EoIB gateway + * + * @v eoib EoIB device + * @v original Original I/O buffer + */ +static void eoib_duplicate ( struct eoib_device *eoib, + struct io_buffer *original ) { + struct net_device *netdev = eoib->netdev; + struct ib_device *ibdev = eoib->ibdev; + struct ib_address_vector *av = &eoib->gateway; + size_t len = iob_len ( original ); + struct io_buffer *copy; + int rc; + + /* Create copy of I/O buffer */ + copy = alloc_iob ( len ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_alloc; + } + memcpy ( iob_put ( copy, len ), original->data, len ); + + /* Append to network device's transmit queue */ + list_add_tail ( ©->list, &original->list ); + + /* Resolve path to gateway */ + if ( ( rc = ib_resolve_path ( ibdev, av ) ) != 0 ) { + DBGC ( eoib, "EoIB %s no path to gateway: %s\n", + eoib->name, strerror ( rc ) ); + goto err_path; + } + + /* Force use of GRH even for local destinations */ + av->gid_present = 1; + + /* Post send work queue entry */ + if ( ( rc = ib_post_send ( eoib->ibdev, eoib->qp, av, copy ) ) != 0 ) + goto err_post_send; + + return; + + err_post_send: + err_path: + list_del ( ©->list ); + err_alloc: + netdev_tx_err ( netdev, copy, rc ); +} + +/** + * Set EoIB gateway + * + * @v eoib EoIB device + * @v av Address vector, or NULL to clear gateway + */ +void eoib_set_gateway ( struct eoib_device *eoib, + struct ib_address_vector *av ) { + + if ( av ) { + DBGC ( eoib, "EoIB %s using gateway " IB_GID_FMT "\n", + eoib->name, IB_GID_ARGS ( &av->gid ) ); + memcpy ( &eoib->gateway, av, sizeof ( eoib->gateway ) ); + eoib->duplicate = eoib_duplicate; + } else { + DBGC ( eoib, "EoIB %s not using gateway\n", eoib->name ); + eoib->duplicate = NULL; + } +} diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 29d117443..2cd41d4ca 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -4006,7 +4006,7 @@ efab_init_mac ( struct efab_nic *efab ) * because we want to use it, or because we're about * to reset the mac anyway */ - sleep ( 2 ); + mdelay ( 2000 ); if ( ! efab->link_up ) { EFAB_ERR ( "!\n" ); diff --git a/src/drivers/net/exanic.c b/src/drivers/net/exanic.c new file mode 100644 index 000000000..287e14e8d --- /dev/null +++ b/src/drivers/net/exanic.c @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, +}; diff --git a/src/drivers/net/exanic.h b/src/drivers/net/exanic.h new file mode 100644 index 000000000..041b9e21a --- /dev/null +++ b/src/drivers/net/exanic.h @@ -0,0 +1,261 @@ +#ifndef _EXANIC_H +#define _EXANIC_H + +/** @file + * + * Exablaze ExaNIC driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/** 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 */ diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c index 79938cbbb..7f044b192 100644 --- a/src/drivers/net/forcedeth.c +++ b/src/drivers/net/forcedeth.c @@ -1176,7 +1176,7 @@ nv_mgmt_get_version ( struct forcedeth_private *priv ) ioaddr + NvRegTransmitterControl ); start = currticks(); - while ( currticks() > start + 5 * ticks_per_sec() ) { + while ( currticks() > start + 5 * TICKS_PER_SEC ) { data_ready2 = readl ( ioaddr + NvRegTransmitterControl ); if ( ( data_ready & NVREG_XMITCTL_DATA_READY ) != ( data_ready2 & NVREG_XMITCTL_DATA_READY ) ) { diff --git a/src/drivers/net/forcedeth.h b/src/drivers/net/forcedeth.h index e1cf6f71a..3a372dc1b 100644 --- a/src/drivers/net/forcedeth.h +++ b/src/drivers/net/forcedeth.h @@ -36,8 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #ifndef _FORCEDETH_H_ #define _FORCEDETH_H_ -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - struct ring_desc { u32 buf; u32 flaglen; diff --git a/src/drivers/net/icplus.c b/src/drivers/net/icplus.c new file mode 100644 index 000000000..4bed92427 --- /dev/null +++ b/src/drivers/net/icplus.c @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2018 Sylvie Barlow . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "icplus.h" + +/** @file + * + * IC+ network driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v icp IC+ device + * @ret rc Return status code + */ +static int icplus_reset ( struct icplus_nic *icp ) { + uint32_t asicctrl; + unsigned int i; + + /* Trigger reset */ + writel ( ( ICP_ASICCTRL_GLOBALRESET | ICP_ASICCTRL_DMA | + ICP_ASICCTRL_FIFO | ICP_ASICCTRL_NETWORK | ICP_ASICCTRL_HOST | + ICP_ASICCTRL_AUTOINIT ), ( icp->regs + ICP_ASICCTRL ) ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < ICP_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if device is ready */ + asicctrl = readl ( icp->regs + ICP_ASICCTRL ); + if ( ! ( asicctrl & ICP_ASICCTRL_RESETBUSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( icp, "ICPLUS %p timed out waiting for reset (asicctrl %#08x)\n", + icp, asicctrl ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * EEPROM interface + * + ****************************************************************************** + */ + +/** + * Read data from EEPROM + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int icplus_read_eeprom ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ) { + struct icplus_nic *icp = + container_of ( nvs, struct icplus_nic, eeprom ); + unsigned int i; + uint16_t eepromctrl; + uint16_t *data_word = data; + + /* Sanity check. We advertise a blocksize of one word, so + * should only ever receive single-word requests. + */ + assert ( len == sizeof ( *data_word ) ); + + /* Initiate read */ + writew ( ( ICP_EEPROMCTRL_OPCODE_READ | + ICP_EEPROMCTRL_ADDRESS ( address ) ), + ( icp->regs + ICP_EEPROMCTRL ) ); + + /* Wait for read to complete */ + for ( i = 0 ; i < ICP_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* If read is not complete, delay 1ms and retry */ + eepromctrl = readw ( icp->regs + ICP_EEPROMCTRL ); + if ( eepromctrl & ICP_EEPROMCTRL_BUSY ) { + mdelay ( 1 ); + continue; + } + + /* Extract data */ + *data_word = cpu_to_le16 ( readw ( icp->regs + ICP_EEPROMDATA )); + return 0; + } + + DBGC ( icp, "ICPLUS %p timed out waiting for EEPROM read\n", icp ); + return -ETIMEDOUT; +} + +/** + * Write data to EEPROM + * + * @v nvs NVS device + * @v address Address to which to write + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int icplus_write_eeprom ( struct nvs_device *nvs, + unsigned int address __unused, + const void *data __unused, + size_t len __unused ) { + struct icplus_nic *icp = + container_of ( nvs, struct icplus_nic, eeprom ); + + DBGC ( icp, "ICPLUS %p EEPROM write not supported\n", icp ); + return -ENOTSUP; +} + +/** + * Initialise EEPROM + * + * @v icp IC+ device + */ +static void icplus_init_eeprom ( struct icplus_nic *icp ) { + + /* The hardware supports only single-word reads */ + icp->eeprom.word_len_log2 = ICP_EEPROM_WORD_LEN_LOG2; + icp->eeprom.size = ICP_EEPROM_MIN_SIZE_WORDS; + icp->eeprom.block_size = 1; + icp->eeprom.read = icplus_read_eeprom; + icp->eeprom.write = icplus_write_eeprom; +} + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** Pin mapping for MII bit-bashing interface */ +static const uint8_t icplus_mii_bits[] = { + [MII_BIT_MDC] = ICP_PHYCTRL_MGMTCLK, + [MII_BIT_MDIO] = ICP_PHYCTRL_MGMTDATA, + [MII_BIT_DRIVE] = ICP_PHYCTRL_MGMTDIR, +}; + +/** + * Read input bit + * + * @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 icplus_mii_read_bit ( struct bit_basher *basher, + unsigned int bit_id ) { + struct icplus_nic *icp = container_of ( basher, struct icplus_nic, + miibit.basher ); + uint8_t mask = icplus_mii_bits[bit_id]; + uint8_t reg; + + DBG_DISABLE ( DBGLVL_IO ); + reg = readb ( icp->regs + ICP_PHYCTRL ); + DBG_ENABLE ( DBGLVL_IO ); + return ( reg & mask ); +} + +/** + * Set/clear output bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + */ +static void icplus_mii_write_bit ( struct bit_basher *basher, + unsigned int bit_id, unsigned long data ) { + struct icplus_nic *icp = container_of ( basher, struct icplus_nic, + miibit.basher ); + uint8_t mask = icplus_mii_bits[bit_id]; + uint8_t reg; + + DBG_DISABLE ( DBGLVL_IO ); + reg = readb ( icp->regs + ICP_PHYCTRL ); + reg &= ~mask; + reg |= ( data & mask ); + writeb ( reg, icp->regs + ICP_PHYCTRL ); + readb ( icp->regs + ICP_PHYCTRL ); /* Ensure write reaches chip */ + DBG_ENABLE ( DBGLVL_IO ); +} + +/** MII bit-bashing interface */ +static struct bit_basher_operations icplus_basher_ops = { + .read = icplus_mii_read_bit, + .write = icplus_mii_write_bit, +}; + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Configure PHY + * + * @v icp IC+ device + * @ret rc Return status code + */ +static int icplus_init_phy ( struct icplus_nic *icp ) { + uint32_t asicctrl; + int rc; + + /* Find PHY address */ + if ( ( rc = mii_find ( &icp->mii ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not find PHY address: %s\n", + icp, strerror ( rc ) ); + return rc; + } + + /* Configure PHY to advertise 1000Mbps if applicable */ + asicctrl = readl ( icp->regs + ICP_ASICCTRL ); + if ( asicctrl & ICP_ASICCTRL_PHYSPEED1000 ) { + if ( ( rc = mii_write ( &icp->mii, MII_CTRL1000, + ADVERTISE_1000FULL ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not advertise 1000Mbps: " + "%s\n", icp, strerror ( rc ) ); + return rc; + } + } + + /* Reset PHY */ + if ( ( rc = mii_reset ( &icp->mii ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not reset PHY: %s\n", + icp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Check link state + * + * @v netdev Network device + */ +static void icplus_check_link ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + uint8_t phyctrl; + + /* Read link status */ + phyctrl = readb ( icp->regs + ICP_PHYCTRL ); + DBGC ( icp, "ICPLUS %p PHY control is %02x\n", icp, phyctrl ); + + /* Update network device */ + if ( phyctrl & ICP_PHYCTRL_LINKSPEED ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Set descriptor ring base address + * + * @v icp IC+ device + * @v offset Register offset + * @v address Base address + */ +static inline void icplus_set_base ( struct icplus_nic *icp, unsigned int offset, + void *base ) { + physaddr_t phys = virt_to_bus ( base ); + + /* Program base address registers */ + writel ( ( phys & 0xffffffffUL ), + ( icp->regs + offset + ICP_BASE_LO ) ); + if ( sizeof ( phys ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) phys ) >> 32 ), + ( icp->regs + offset + ICP_BASE_HI ) ); + } else { + writel ( 0, ( icp->regs + offset + ICP_BASE_HI ) ); + } +} + +/** + * Create descriptor ring + * + * @v icp IC+ device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int icplus_create_ring ( struct icplus_nic *icp, struct icplus_ring *ring ) { + size_t len = ( sizeof ( ring->entry[0] ) * ICP_NUM_DESC ); + int rc; + unsigned int i; + struct icplus_descriptor *desc; + struct icplus_descriptor *next; + + /* Allocate descriptor ring */ + ring->entry = malloc_dma ( len, ICP_ALIGN ); + if ( ! ring->entry ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Initialise descriptor ring */ + memset ( ring->entry, 0, len ); + for ( i = 0 ; i < ICP_NUM_DESC ; i++ ) { + desc = &ring->entry[i]; + next = &ring->entry[ ( i + 1 ) % ICP_NUM_DESC ]; + desc->next = cpu_to_le64 ( virt_to_bus ( next ) ); + desc->flags = ( ICP_TX_UNALIGN | ICP_TX_INDICATE ); + desc->control = ( ICP_TX_SOLE_FRAG | ICP_DONE ); + } + + /* Reset transmit producer & consumer counters */ + ring->prod = 0; + ring->cons = 0; + + DBGC ( icp, "ICP %p %s ring at [%#08lx,%#08lx)\n", + icp, ( ( ring->listptr == ICP_TFDLISTPTR ) ? "TX" : "RX" ), + virt_to_bus ( ring->entry ), + ( virt_to_bus ( ring->entry ) + len ) ); + return 0; + + free_dma ( ring->entry, len ); + ring->entry = NULL; + err_alloc: + return rc; +} + +/** + * Destroy descriptor ring + * + * @v icp IC+ device + * @v ring Descriptor ring + */ +static void icplus_destroy_ring ( struct icplus_nic *icp __unused, + struct icplus_ring *ring ) { + size_t len = ( sizeof ( ring->entry[0] ) * ICP_NUM_DESC ); + + /* Free descriptor ring */ + free_dma ( ring->entry, len ); + ring->entry = NULL; +} + +/** + * Refill receive descriptor ring + * + * @v icp IC+ device + */ +void icplus_refill_rx ( struct icplus_nic *icp ) { + struct icplus_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + physaddr_t address; + unsigned int refilled = 0; + + /* Refill ring */ + while ( ( icp->rx.prod - icp->rx.cons ) < ICP_NUM_DESC ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( ICP_RX_MAX_LEN ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rx_idx = ( icp->rx.prod++ % ICP_NUM_DESC ); + desc = &icp->rx.entry[rx_idx]; + + /* Populate receive descriptor */ + address = virt_to_bus ( iobuf->data ); + desc->data.address = cpu_to_le64 ( address ); + desc->data.len = cpu_to_le16 ( ICP_RX_MAX_LEN ); + wmb(); + desc->control = 0; + + /* Record I/O buffer */ + assert ( icp->rx_iobuf[rx_idx] == NULL ); + icp->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( icp, "ICP %p RX %d is [%llx,%llx)\n", icp, rx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + ICP_RX_MAX_LEN ) ); + refilled++; + } + + /* Push descriptors to card, if applicable */ + if ( refilled ) { + wmb(); + writew ( ICP_DMACTRL_RXPOLLNOW, icp->regs + ICP_DMACTRL ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int icplus_open ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + int rc; + + /* Create transmit descriptor ring */ + if ( ( rc = icplus_create_ring ( icp, &icp->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = icplus_create_ring ( icp, &icp->rx ) ) != 0 ) + goto err_create_rx; + + /* Program descriptor base address */ + icplus_set_base ( icp, icp->tx.listptr, icp->tx.entry ); + icplus_set_base ( icp, icp->rx.listptr, icp->rx.entry ); + + /* Enable receive mode */ + writew ( ( ICP_RXMODE_UNICAST | ICP_RXMODE_MULTICAST | + ICP_RXMODE_BROADCAST | ICP_RXMODE_ALLFRAMES ), + icp->regs + ICP_RXMODE ); + + /* Enable transmitter and receiver */ + writel ( ( ICP_MACCTRL_TXENABLE | ICP_MACCTRL_RXENABLE | + ICP_MACCTRL_DUPLEX ), icp->regs + ICP_MACCTRL ); + + /* Fill receive ring */ + icplus_refill_rx ( icp ); + + /* Check link state */ + icplus_check_link ( netdev ); + + return 0; + + icplus_reset ( icp ); + icplus_destroy_ring ( icp, &icp->rx ); + err_create_rx: + icplus_destroy_ring ( icp, &icp->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void icplus_close ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + unsigned int i; + + /* Perform global reset */ + icplus_reset ( icp ); + + /* Destroy receive descriptor ring */ + icplus_destroy_ring ( icp, &icp->rx ); + + /* Destroy transmit descriptor ring */ + icplus_destroy_ring ( icp, &icp->tx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < ICP_NUM_DESC ; i++ ) { + if ( icp->rx_iobuf[i] ) + free_iob ( icp->rx_iobuf[i] ); + icp->rx_iobuf[i] = NULL; + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int icplus_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct icplus_nic *icp = netdev->priv; + struct icplus_descriptor *desc; + unsigned int tx_idx; + physaddr_t address; + + /* Check if ring is full */ + if ( ( icp->tx.prod - icp->tx.cons ) >= ICP_NUM_DESC ) { + DBGC ( icp, "ICP %p out of transmit descriptors\n", icp ); + return -ENOBUFS; + } + + /* Find TX descriptor entry to use */ + tx_idx = ( icp->tx.prod++ % ICP_NUM_DESC ); + desc = &icp->tx.entry[tx_idx]; + + /* Fill in TX descriptor */ + address = virt_to_bus ( iobuf->data ); + desc->data.address = cpu_to_le64 ( address ); + desc->data.len = cpu_to_le16 ( iob_len ( iobuf ) ); + wmb(); + desc->control = ICP_TX_SOLE_FRAG; + wmb(); + + /* Ring doorbell */ + writew ( ICP_DMACTRL_TXPOLLNOW, icp->regs + ICP_DMACTRL ); + + DBGC2 ( icp, "ICP %p TX %d is [%llx,%llx)\n", icp, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + DBGC2_HDA ( icp, virt_to_phys ( desc ), desc, sizeof ( *desc ) ); + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void icplus_poll_tx ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + struct icplus_descriptor *desc; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( icp->tx.cons != icp->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( icp->tx.cons % ICP_NUM_DESC ); + desc = &icp->tx.entry[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( ! ( desc->control & ICP_DONE ) ) + return; + + /* Complete TX descriptor */ + DBGC2 ( icp, "ICP %p TX %d complete\n", icp, tx_idx ); + netdev_tx_complete_next ( netdev ); + icp->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void icplus_poll_rx ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + struct icplus_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( icp->rx.cons != icp->rx.prod ) { + + /* Get next transmit descriptor */ + rx_idx = ( icp->rx.cons % ICP_NUM_DESC ); + desc = &icp->rx.entry[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( ! ( desc->control & ICP_DONE ) ) + return; + + /* Populate I/O buffer */ + iobuf = icp->rx_iobuf[rx_idx]; + icp->rx_iobuf[rx_idx] = NULL; + len = le16_to_cpu ( desc->len ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( desc->flags & ( ICP_RX_ERR_OVERRUN | ICP_RX_ERR_RUNT | + ICP_RX_ERR_ALIGN | ICP_RX_ERR_FCS | + ICP_RX_ERR_OVERSIZED | ICP_RX_ERR_LEN ) ) { + DBGC ( icp, "ICP %p RX %d error (length %zd, " + "flags %02x)\n", icp, rx_idx, len, desc->flags ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } else { + DBGC2 ( icp, "ICP %p RX %d complete (length " + "%zd)\n", icp, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } + icp->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void icplus_poll ( struct net_device *netdev ) { + struct icplus_nic *icp = netdev->priv; + uint16_t intstatus; + uint32_t txstatus; + + /* Check for interrupts */ + intstatus = readw ( icp->regs + ICP_INTSTATUS ); + + /* Poll for TX completions, if applicable */ + if ( intstatus & ICP_INTSTATUS_TXCOMPLETE ) { + txstatus = readl ( icp->regs + ICP_TXSTATUS ); + if ( txstatus & ICP_TXSTATUS_ERROR ) + DBGC ( icp, "ICP %p TX error: %08x\n", icp, txstatus ); + icplus_poll_tx ( netdev ); + } + + /* Poll for RX completions, if applicable */ + if ( intstatus & ICP_INTSTATUS_RXDMACOMPLETE ) { + writew ( ICP_INTSTATUS_RXDMACOMPLETE, icp->regs + ICP_INTSTATUS ); + icplus_poll_rx ( netdev ); + } + + /* Check link state, if applicable */ + if ( intstatus & ICP_INTSTATUS_LINKEVENT ) { + writew ( ICP_INTSTATUS_LINKEVENT, icp->regs + ICP_INTSTATUS ); + icplus_check_link ( netdev ); + } + + /* Refill receive ring */ + icplus_refill_rx ( icp ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void icplus_irq ( struct net_device *netdev, int enable ) { + struct icplus_nic *icp = netdev->priv; + + DBGC ( icp, "ICPLUS %p does not yet support interrupts\n", icp ); + ( void ) enable; +} + +/** IC+ network device operations */ +static struct net_device_operations icplus_operations = { + .open = icplus_open, + .close = icplus_close, + .transmit = icplus_transmit, + .poll = icplus_poll, + .irq = icplus_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int icplus_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct icplus_nic *icp; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *icp ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &icplus_operations ); + icp = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( icp, 0, sizeof ( *icp ) ); + icp->miibit.basher.op = &icplus_basher_ops; + init_mii_bit_basher ( &icp->miibit ); + mii_init ( &icp->mii, &icp->miibit.mdio, 0 ); + icp->tx.listptr = ICP_TFDLISTPTR; + icp->rx.listptr = ICP_RFDLISTPTR; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + icp->regs = ioremap ( pci->membase, ICP_BAR_SIZE ); + if ( ! icp->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the NIC */ + if ( ( rc = icplus_reset ( icp ) ) != 0 ) + goto err_reset; + + /* Initialise EEPROM */ + icplus_init_eeprom ( icp ); + + /* Read EEPROM MAC address */ + if ( ( rc = nvs_read ( &icp->eeprom, ICP_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) { + DBGC ( icp, "ICPLUS %p could not read EEPROM MAC address: %s\n", + icp, strerror ( rc ) ); + goto err_eeprom; + } + + /* Configure PHY */ + if ( ( rc = icplus_init_phy ( icp ) ) != 0 ) + goto err_phy; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + icplus_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_phy: + err_eeprom: + icplus_reset ( icp ); + err_reset: + iounmap ( icp->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void icplus_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct icplus_nic *icp = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + icplus_reset ( icp ); + + /* Free network device */ + iounmap ( icp->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** IC+ PCI device IDs */ +static struct pci_device_id icplus_nics[] = { + PCI_ROM ( 0x13f0, 0x1023, "ip1000a", "IP1000A", 0 ), +}; + +/** IC+ PCI driver */ +struct pci_driver icplus_driver __pci_driver = { + .ids = icplus_nics, + .id_count = ( sizeof ( icplus_nics ) / sizeof ( icplus_nics[0] ) ), + .probe = icplus_probe, + .remove = icplus_remove, +}; diff --git a/src/drivers/net/icplus.h b/src/drivers/net/icplus.h new file mode 100644 index 000000000..35fa422ad --- /dev/null +++ b/src/drivers/net/icplus.h @@ -0,0 +1,206 @@ +#ifndef _ICPLUS_H +#define _ICPLUS_H + +/** @file + * + * IC+ network driver + * + */ + +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** BAR size */ +#define ICP_BAR_SIZE 0x200 + +/** Alignment requirement */ +#define ICP_ALIGN 0x8 + +/** Base address low register offset */ +#define ICP_BASE_LO 0x0 + +/** Base address high register offset */ +#define ICP_BASE_HI 0x4 + +/** ASIC control register (double word) */ +#define ICP_ASICCTRL 0x30 +#define ICP_ASICCTRL_PHYSPEED1000 0x00000040UL /**< PHY speed 1000 */ +#define ICP_ASICCTRL_GLOBALRESET 0x00010000UL /**< Global reset */ +#define ICP_ASICCTRL_DMA 0x00080000UL /**< DMA */ +#define ICP_ASICCTRL_FIFO 0x00100000UL /**< FIFO */ +#define ICP_ASICCTRL_NETWORK 0x00200000UL /**< Network */ +#define ICP_ASICCTRL_HOST 0x00400000UL /**< Host */ +#define ICP_ASICCTRL_AUTOINIT 0x00800000UL /**< Auto init */ +#define ICP_ASICCTRL_RESETBUSY 0x04000000UL /**< Reset busy */ + +/** Maximum time to wait for reset */ +#define ICP_RESET_MAX_WAIT_MS 1000 + +/** DMA control register (word/double word) */ +#define ICP_DMACTRL 0x00 +#define ICP_DMACTRL_RXPOLLNOW 0x0010 /**< Receive poll now */ +#define ICP_DMACTRL_TXPOLLNOW 0x1000 /**< Transmit poll now */ + +/** EEPROM control register (word) */ +#define ICP_EEPROMCTRL 0x4a +#define ICP_EEPROMCTRL_ADDRESS( x ) ( (x) << 0 ) /**< Address */ +#define ICP_EEPROMCTRL_OPCODE( x ) ( (x) << 8 ) /**< Opcode */ +#define ICP_EEPROMCTRL_OPCODE_READ \ + ICP_EEPROMCTRL_OPCODE ( 2 ) /**< Read register */ +#define ICP_EEPROMCTRL_BUSY 0x8000 /**< EEPROM busy */ + +/** Maximum time to wait for reading EEPROM */ +#define ICP_EEPROM_MAX_WAIT_MS 1000 + +/** EEPROM word length */ +#define ICP_EEPROM_WORD_LEN_LOG2 1 + +/** Minimum EEPROM size, in words */ +#define ICP_EEPROM_MIN_SIZE_WORDS 0x20 + +/** Address of MAC address within EEPROM */ +#define ICP_EEPROM_MAC 0x10 + +/** EEPROM data register (word) */ +#define ICP_EEPROMDATA 0x48 + +/** Interupt status register (word) */ +#define ICP_INTSTATUS 0x5e +#define ICP_INTSTATUS_TXCOMPLETE 0x0004 /**< TX complete */ +#define ICP_INTSTATUS_LINKEVENT 0x0100 /**< Link event */ +#define ICP_INTSTATUS_RXDMACOMPLETE 0x0400 /**< RX DMA complete */ + +/** MAC control register (double word) */ +#define ICP_MACCTRL 0x6c +#define ICP_MACCTRL_DUPLEX 0x00000020UL /**< Duplex select */ +#define ICP_MACCTRL_TXENABLE 0x01000000UL /**< TX enable */ +#define ICP_MACCTRL_TXDISABLE 0x02000000UL /**< TX disable */ +#define ICP_MACCTRL_RXENABLE 0x08000000UL /**< RX enable */ +#define ICP_MACCTRL_RXDISABLE 0x10000000UL /**< RX disable */ + +/** PHY control register (byte) */ +#define ICP_PHYCTRL 0x76 +#define ICP_PHYCTRL_MGMTCLK 0x01 /**< Management clock */ +#define ICP_PHYCTRL_MGMTDATA 0x02 /**< Management data */ +#define ICP_PHYCTRL_MGMTDIR 0x04 /**< Management direction */ +#define ICP_PHYCTRL_LINKSPEED 0xc0 /**< Link speed */ + +/** Receive mode register (word) */ +#define ICP_RXMODE 0x88 +#define ICP_RXMODE_UNICAST 0x0001 /**< Receive unicast */ +#define ICP_RXMODE_MULTICAST 0x0002 /**< Receice multicast */ +#define ICP_RXMODE_BROADCAST 0x0004 /**< Receive broadcast */ +#define ICP_RXMODE_ALLFRAMES 0x0008 /**< Receive all frames */ + +/** List pointer receive register */ +#define ICP_RFDLISTPTR 0x1c + +/** List pointer transmit register */ +#define ICP_TFDLISTPTR 0x10 + +/** Transmit status register */ +#define ICP_TXSTATUS 0x60 +#define ICP_TXSTATUS_ERROR 0x00000001UL /**< TX error */ + +/** Data fragment */ +union icplus_fragment { + /** Address of data */ + uint64_t address; + /** Length */ + struct { + /** Reserved */ + uint8_t reserved[6]; + /** Length of data */ + uint16_t len; + }; +}; + +/** Transmit or receive descriptor */ +struct icplus_descriptor { + /** Address of next descriptor */ + uint64_t next; + /** Actual length */ + uint16_t len; + /** Flags */ + uint8_t flags; + /** Control */ + uint8_t control; + /** VLAN */ + uint16_t vlan; + /** Reserved */ + uint16_t reserved_a; + /** Data buffer */ + union icplus_fragment data; + /** Reserved */ + uint8_t reserved_b[8]; +}; + +/** Descriptor complete */ +#define ICP_DONE 0x80 + +/** Transmit alignment disabled */ +#define ICP_TX_UNALIGN 0x01 + +/** Request transmit completion */ +#define ICP_TX_INDICATE 0x40 + +/** Sole transmit fragment */ +#define ICP_TX_SOLE_FRAG 0x01 + +/** Recieve frame overrun error */ +#define ICP_RX_ERR_OVERRUN 0x01 + +/** Receive runt frame error */ +#define ICP_RX_ERR_RUNT 0x02 + +/** Receive alignment error */ +#define ICP_RX_ERR_ALIGN 0x04 + +/** Receive FCS error */ +#define ICP_RX_ERR_FCS 0x08 + +/** Receive oversized frame error */ +#define ICP_RX_ERR_OVERSIZED 0x10 + +/** Recieve length error */ +#define ICP_RX_ERR_LEN 0x20 + +/** Descriptor ring */ +struct icplus_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Ring entries */ + struct icplus_descriptor *entry; + /* List pointer register */ + unsigned int listptr; +}; + +/** Number of descriptors */ +#define ICP_NUM_DESC 4 + +/** Maximum receive packet length */ +#define ICP_RX_MAX_LEN ETH_FRAME_LEN + +/** An IC+ network card */ +struct icplus_nic { + /** Registers */ + void *regs; + /** EEPROM */ + struct nvs_device eeprom; + /** MII bit bashing interface */ + struct mii_bit_basher miibit; + /** MII device */ + struct mii_device mii; + /** Transmit descriptor ring */ + struct icplus_ring tx; + /** Receive descriptor ring */ + struct icplus_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[ICP_NUM_DESC]; +}; + +#endif /* _ICPLUS_H */ diff --git a/src/drivers/net/igbvf/igbvf_vf.c b/src/drivers/net/igbvf/igbvf_vf.c index f2dac8be7..f841d5e3d 100644 --- a/src/drivers/net/igbvf/igbvf_vf.c +++ b/src/drivers/net/igbvf/igbvf_vf.c @@ -357,6 +357,7 @@ s32 igbvf_promisc_set_vf(struct e1000_hw *hw, enum e1000_promisc_type type) break; case e1000_promisc_enabled: msgbuf |= E1000_VF_SET_PROMISC_MULTICAST; + /* Fall through */ case e1000_promisc_unicast: msgbuf |= E1000_VF_SET_PROMISC_UNICAST; case e1000_promisc_disabled: diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 2326bc528..a2e6d535b 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -268,6 +268,12 @@ static int intel_reset ( struct intel_nic *intel ) { uint32_t pba; uint32_t ctrl; uint32_t status; + uint32_t orig_ctrl; + uint32_t orig_status; + + /* Record initial control and status register values */ + orig_ctrl = ctrl = readl ( intel->regs + INTEL_CTRL ); + orig_status = readl ( intel->regs + INTEL_STATUS ); /* Force RX and TX packet buffer allocation, to work around an * errata in ICH devices. @@ -285,24 +291,35 @@ static int intel_reset ( struct intel_nic *intel ) { } /* Always reset MAC. Required to reset the TX and RX rings. */ - ctrl = readl ( intel->regs + INTEL_CTRL ); writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); /* Set a sensible default configuration */ - ctrl |= ( INTEL_CTRL_SLU | INTEL_CTRL_ASDE ); + if ( ! ( intel->flags & INTEL_NO_ASDE ) ) + ctrl |= INTEL_CTRL_ASDE; + ctrl |= INTEL_CTRL_SLU; ctrl &= ~( INTEL_CTRL_LRST | INTEL_CTRL_FRCSPD | INTEL_CTRL_FRCDPLX ); writel ( ctrl, intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); - /* If link is already up, do not attempt to reset the PHY. On - * some models (notably ICH), performing a PHY reset seems to - * drop the link speed to 10Mbps. + /* On some models (notably ICH), the PHY reset mechanism + * appears to be broken. In particular, the PHY_CTRL register + * will be correctly loaded from NVM but the values will not + * be propagated to the "OEM bits" PHY register. This + * typically has the effect of dropping the link speed to + * 10Mbps. + * + * Work around this problem by skipping the PHY reset if + * either (a) the link is already up, or (b) this particular + * NIC is known to be broken. */ status = readl ( intel->regs + INTEL_STATUS ); - if ( status & INTEL_STATUS_LU ) { - DBGC ( intel, "INTEL %p MAC reset (ctrl %08x)\n", - intel, ctrl ); + if ( ( intel->flags & INTEL_NO_PHY_RST ) || + ( status & INTEL_STATUS_LU ) ) { + DBGC ( intel, "INTEL %p %sMAC reset (%08x/%08x was " + "%08x/%08x)\n", intel, + ( ( intel->flags & INTEL_NO_PHY_RST ) ? "forced " : "" ), + ctrl, status, orig_ctrl, orig_status ); return 0; } @@ -314,8 +331,10 @@ static int intel_reset ( struct intel_nic *intel ) { /* PHY reset is not self-clearing on all models */ writel ( ctrl, intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); + status = readl ( intel->regs + INTEL_STATUS ); - DBGC ( intel, "INTEL %p MAC+PHY reset (ctrl %08x)\n", intel, ctrl ); + DBGC ( intel, "INTEL %p MAC+PHY reset (%08x/%08x was %08x/%08x)\n", + intel, ctrl, status, orig_ctrl, orig_status ); return 0; } @@ -415,6 +434,61 @@ void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr, ****************************************************************************** */ +/** + * Disable descriptor ring + * + * @v intel Intel device + * @v reg Register block + * @ret rc Return status code + */ +static int intel_disable_ring ( struct intel_nic *intel, unsigned int reg ) { + uint32_t dctl; + unsigned int i; + + /* Disable ring */ + writel ( 0, ( intel->regs + reg + INTEL_xDCTL ) ); + + /* Wait for disable to complete */ + for ( i = 0 ; i < INTEL_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if ring is disabled */ + dctl = readl ( intel->regs + reg + INTEL_xDCTL ); + if ( ! ( dctl & INTEL_xDCTL_ENABLE ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intel, "INTEL %p ring %05x timed out waiting for disable " + "(dctl %08x)\n", intel, reg, dctl ); + return -ETIMEDOUT; +} + +/** + * Reset descriptor ring + * + * @v intel Intel device + * @v reg Register block + * @ret rc Return status code + */ +void intel_reset_ring ( struct intel_nic *intel, unsigned int reg ) { + + /* Disable ring. Ignore errors and continue to reset the ring anyway */ + intel_disable_ring ( intel, reg ); + + /* Clear ring length */ + writel ( 0, ( intel->regs + reg + INTEL_xDLEN ) ); + + /* Clear ring address */ + writel ( 0, ( intel->regs + reg + INTEL_xDBAH ) ); + writel ( 0, ( intel->regs + reg + INTEL_xDBAL ) ); + + /* Reset head and tail pointers */ + writel ( 0, ( intel->regs + reg + INTEL_xDH ) ); + writel ( 0, ( intel->regs + reg + INTEL_xDT ) ); +} + /** * Create descriptor ring * @@ -475,12 +549,8 @@ int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ) { */ void intel_destroy_ring ( struct intel_nic *intel, struct intel_ring *ring ) { - /* Clear ring length */ - writel ( 0, ( intel->regs + ring->reg + INTEL_xDLEN ) ); - - /* Clear ring address */ - writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAL ) ); - writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAH ) ); + /* Reset ring */ + intel_reset_ring ( intel, ring->reg ); /* Free descriptor ring */ free_dma ( ring->desc, ring->len ); @@ -565,10 +635,23 @@ void intel_empty_rx ( struct intel_nic *intel ) { static int intel_open ( struct net_device *netdev ) { struct intel_nic *intel = netdev->priv; union intel_receive_address mac; + uint32_t fextnvm11; uint32_t tctl; uint32_t rctl; int rc; + /* Set undocumented bit in FEXTNVM11 to work around an errata + * in i219 devices that will otherwise cause a complete + * datapath hang at the next device reset. + */ + if ( intel->flags & INTEL_RST_HANG ) { + DBGC ( intel, "INTEL %p WARNING: applying reset hang " + "workaround\n", intel ); + fextnvm11 = readl ( intel->regs + INTEL_FEXTNVM11 ); + fextnvm11 |= INTEL_FEXTNVM11_WTF; + writel ( fextnvm11, intel->regs + INTEL_FEXTNVM11 ); + } + /* Create transmit descriptor ring */ if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 ) goto err_create_tx; @@ -1029,7 +1112,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x10f5, "82567lm", "82567LM", 0 ), PCI_ROM ( 0x8086, 0x10f6, "82574l", "82574L", 0 ), PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", INTEL_PBS_ERRATA ), - PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", 0 ), + PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x1503, "82579v", "82579V", 0 ), PCI_ROM ( 0x8086, 0x150a, "82576ns", "82576NS", 0 ), PCI_ROM ( 0x8086, 0x150c, "82583v", "82583V", 0 ), @@ -1042,22 +1125,32 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1518, "82576ns", "82576NS SerDes", 0 ), PCI_ROM ( 0x8086, 0x1521, "i350", "I350", 0 ), PCI_ROM ( 0x8086, 0x1522, "i350-f", "I350 Fiber", 0 ), - PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", 0 ), + PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x1524, "i350-2", "I350", 0 ), PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ), PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ), PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ), PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ), - PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", 0 ), + PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), + PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ), PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), - PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), - PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ), - PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", 0 ), + PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ), + PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index ce9e3f467..9d740efc3 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -99,8 +99,8 @@ struct intel_descriptor { #define INTEL_IRQ_TXQE 0x00000002UL /**< Transmit queue empty */ #define INTEL_IRQ_LSC 0x00000004UL /**< Link status change */ #define INTEL_IRQ_RXDMT0 0x00000010UL /**< Receive queue low */ +#define INTEL_IRQ_RXO 0x00000040UL /**< Receive overrun */ #define INTEL_IRQ_RXT0 0x00000080UL /**< Receive timer */ -#define INTEL_IRQ_RXO 0x00000400UL /**< Receive overrun */ /** Interrupt Mask Set/Read Register */ #define INTEL_IMS 0x000d0UL @@ -185,6 +185,9 @@ struct intel_descriptor { #define INTEL_xDCTL 0x28 #define INTEL_xDCTL_ENABLE 0x02000000UL /**< Queue enable */ +/** Maximum time to wait for queue disable, in milliseconds */ +#define INTEL_DISABLE_MAX_WAIT_MS 100 + /** Receive Address Low */ #define INTEL_RAL0 0x05400UL @@ -192,6 +195,10 @@ struct intel_descriptor { #define INTEL_RAH0 0x05404UL #define INTEL_RAH0_AV 0x80000000UL /**< Address valid */ +/** Future Extended NVM register 11 */ +#define INTEL_FEXTNVM11 0x05bbcUL +#define INTEL_FEXTNVM11_WTF 0x00002000UL /**< Don't ask */ + /** Receive address */ union intel_receive_address { struct { @@ -301,8 +308,17 @@ enum intel_flags { INTEL_PBS_ERRATA = 0x0001, /** VMware missing interrupt workaround required */ INTEL_VMWARE = 0x0002, + /** PHY reset is broken */ + INTEL_NO_PHY_RST = 0x0004, + /** ASDE is broken */ + INTEL_NO_ASDE = 0x0008, + /** Reset may cause a complete device hang */ + INTEL_RST_HANG = 0x0010, }; +/** The i219 has a seriously broken reset mechanism */ +#define INTEL_I219 ( INTEL_NO_PHY_RST | INTEL_RST_HANG ) + /** * Dump diagnostic information * @@ -328,6 +344,7 @@ extern void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr, size_t len ); extern void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr, size_t len ); +extern void intel_reset_ring ( struct intel_nic *intel, unsigned int reg ); extern int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ); extern void intel_destroy_ring ( struct intel_nic *intel, diff --git a/src/drivers/net/intelvf.h b/src/drivers/net/intelvf.h index d2f98d874..ab404698f 100644 --- a/src/drivers/net/intelvf.h +++ b/src/drivers/net/intelvf.h @@ -37,6 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Set MTU mailbox message */ #define INTELVF_MSG_TYPE_SET_MTU 0x00000005UL +/** Get queue configuration message */ +#define INTELVF_MSG_TYPE_GET_QUEUES 0x00000009UL + /** Control ("ping") mailbox message */ #define INTELVF_MSG_TYPE_CONTROL 0x00000100UL @@ -78,6 +81,44 @@ struct intelvf_msg_mtu { uint32_t mtu; } __attribute__ (( packed )); +/** Queue configuration mailbox message (API v1.1+ only) */ +struct intelvf_msg_queues { + /** Message header */ + uint32_t hdr; + /** Maximum number of transmit queues */ + uint32_t tx; + /** Maximum number of receive queues */ + uint32_t rx; + /** VLAN hand-waving thing + * + * This is labelled IXGBE_VF_TRANS_VLAN in the Linux driver. + * + * A comment in the Linux PF driver describes it as "notify VF + * of need for VLAN tag stripping, and correct queue". It + * will be filled with a non-zero value if the PF is enforcing + * the use of a single VLAN tag. It will also be filled with + * a non-zero value if the PF is using multiple traffic + * classes. + * + * The Linux VF driver seems to treat this field as being + * simply the number of traffic classes, and gives it no + * VLAN-related interpretation. + * + * If the PF is enforcing the use of a single VLAN tag for the + * VF, then the VLAN tag will be transparently inserted in + * transmitted packets (via the PFVMVIR register) but will + * still be visible in received packets. The Linux VF driver + * handles this unexpected VLAN tag by simply ignoring any + * unrecognised VLAN tags. + * + * We choose to strip and ignore the VLAN tag if this field + * has a non-zero value. + */ + uint32_t vlan_thing; + /** Default queue */ + uint32_t dflt; +} __attribute__ (( packed )); + /** Mailbox message */ union intelvf_msg { /** Message header */ @@ -88,6 +129,8 @@ union intelvf_msg { struct intelvf_msg_version version; /** MTU message */ struct intelvf_msg_mtu mtu; + /** Queue configuration message */ + struct intelvf_msg_queues queues; /** Raw dwords */ uint32_t dword[0]; }; diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index 982b74f12..afd744eda 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -473,6 +473,8 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ), PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ), + PCI_ROM ( 0x8086, 0x1563, "x550t2", "X550-T2", 0 ), + PCI_ROM ( 0x8086, 0x15e5, "x553", "X553", 0 ), }; /** PCI driver */ diff --git a/src/drivers/net/intelx.h b/src/drivers/net/intelx.h index 6383dfcad..d7f3b78e8 100644 --- a/src/drivers/net/intelx.h +++ b/src/drivers/net/intelx.h @@ -71,6 +71,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Receive Descriptor register block */ #define INTELX_RD 0x01000UL +/** Receive Descriptor Control Register */ +#define INTELX_RXDCTL_VME 0x40000000UL /**< Strip VLAN tag */ + /** Split Receive Control Register */ #define INTELX_SRRCTL 0x02100UL #define INTELX_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */ diff --git a/src/drivers/net/intelxvf.c b/src/drivers/net/intelxvf.c index 05e34c127..2caeec27e 100644 --- a/src/drivers/net/intelxvf.c +++ b/src/drivers/net/intelxvf.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include "intelx.h" #include "intelxvf.h" /** @file @@ -156,6 +157,47 @@ static int intelxvf_mbox_version ( struct intel_nic *intel, return 0; } +/** + * Get queue configuration + * + * @v intel Intel device + * @v vlan_thing VLAN hand-waving thing to fill in + * @ret rc Return status code + */ +static int intelxvf_mbox_queues ( struct intel_nic *intel, int *vlan_thing ) { + union intelvf_msg msg; + int rc; + + /* Send queue configuration message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_GET_QUEUES; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p get queue configuration failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) !=INTELVF_MSG_TYPE_GET_QUEUES){ + DBGC ( intel, "INTEL %p get queue configuration unexpected " + "response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to get the queue configuration */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p get queue configuration refused\n", + intel ); + return -EPERM; + } + + /* Extract VLAN hand-waving thing */ + *vlan_thing = msg.queues.vlan_thing; + + return 0; +} + /****************************************************************************** * * Network device interface @@ -171,8 +213,11 @@ static int intelxvf_mbox_version ( struct intel_nic *intel, */ static int intelxvf_open ( struct net_device *netdev ) { struct intel_nic *intel = netdev->priv; + uint32_t rxdctl; uint32_t srrctl; uint32_t dca_rxctrl; + unsigned int i; + int vlan_thing; int rc; /* Reset the function */ @@ -208,6 +253,28 @@ static int intelxvf_open ( struct net_device *netdev ) { goto err_mbox_set_mtu; } + /* Reset all descriptor rings */ + for ( i = 0 ; i < INTELXVF_NUM_RINGS ; i++ ) { + intel_reset_ring ( intel, INTELXVF_TD ( i ) ); + intel_reset_ring ( intel, INTELXVF_RD ( i ) ); + } + + /* Reset packet split receive type register */ + writel ( 0, intel->regs + INTELXVF_PSRTYPE ); + + /* Get queue configuration. Ignore failures, since the host + * may not support this message. + */ + vlan_thing = 0; + intelxvf_mbox_queues ( intel, &vlan_thing ); + if ( vlan_thing ) { + DBGC ( intel, "INTEL %p stripping VLAN tags (thing=%d)\n", + intel, vlan_thing ); + rxdctl = readl ( intel->regs + INTELXVF_RD(0) + INTEL_xDCTL ); + rxdctl |= INTELX_RXDCTL_VME; + writel ( rxdctl, intel->regs + INTELXVF_RD(0) + INTEL_xDCTL ); + } + /* Create transmit descriptor ring */ if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 ) goto err_create_tx; @@ -226,9 +293,12 @@ static int intelxvf_open ( struct net_device *netdev ) { /* Configure receive buffer sizes and set receive descriptor type */ srrctl = readl ( intel->regs + INTELXVF_SRRCTL ); srrctl &= ~( INTELXVF_SRRCTL_BSIZE_MASK | + INTELXVF_SRRCTL_BHDRSIZE_MASK | INTELXVF_SRRCTL_DESCTYPE_MASK ); srrctl |= ( INTELXVF_SRRCTL_BSIZE_DEFAULT | - INTELXVF_SRRCTL_DESCTYPE_DEFAULT ); + INTELXVF_SRRCTL_BHDRSIZE_DEFAULT | + INTELXVF_SRRCTL_DESCTYPE_DEFAULT | + INTELXVF_SRRCTL_DROP_EN ); writel ( srrctl, intel->regs + INTELXVF_SRRCTL ); /* Clear "must-be-zero" bit for direct cache access (DCA). We @@ -377,9 +447,9 @@ static int intelxvf_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intel, 0, sizeof ( *intel ) ); intel_init_mbox ( &intel->mbox, INTELXVF_MBCTRL, INTELXVF_MBMEM ); - intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD, + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD(0), intel_describe_tx_adv ); - intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD, + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD(0), intel_describe_rx ); /* Fix up PCI device */ diff --git a/src/drivers/net/intelxvf.h b/src/drivers/net/intelxvf.h index ad046a65c..4663272aa 100644 --- a/src/drivers/net/intelxvf.h +++ b/src/drivers/net/intelxvf.h @@ -55,8 +55,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Mailbox Control Register */ #define INTELXVF_MBCTRL 0x02fcUL +/** Packet Split Receive Type */ +#define INTELXVF_PSRTYPE 0x0300UL + /** Receive Descriptor register block */ -#define INTELXVF_RD 0x1000UL +#define INTELXVF_RD(n) ( 0x1000UL + ( 0x40 * (n) ) ) /** RX DCA Control Register */ #define INTELXVF_DCA_RXCTRL 0x100cUL @@ -67,9 +70,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define INTELXVF_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */ #define INTELXVF_SRRCTL_BSIZE_DEFAULT INTELXVF_SRRCTL_BSIZE ( 0x02 ) #define INTELXVF_SRRCTL_BSIZE_MASK INTELXVF_SRRCTL_BSIZE ( 0x1f ) +#define INTELXVF_SRRCTL_BHDRSIZE(kb) ( (kb) << 8 ) /**< Header size */ +#define INTELXVF_SRRCTL_BHDRSIZE_DEFAULT INTELXVF_SRRCTL_BHDRSIZE ( 0x04 ) +#define INTELXVF_SRRCTL_BHDRSIZE_MASK INTELXVF_SRRCTL_BHDRSIZE ( 0x0f ) #define INTELXVF_SRRCTL_DESCTYPE(typ) ( (typ) << 25 ) /**< Descriptor type */ #define INTELXVF_SRRCTL_DESCTYPE_DEFAULT INTELXVF_SRRCTL_DESCTYPE ( 0x00 ) #define INTELXVF_SRRCTL_DESCTYPE_MASK INTELXVF_SRRCTL_DESCTYPE ( 0x07 ) +#define INTELXVF_SRRCTL_DROP_EN 0x10000000UL /** Good Packets Received Count */ #define INTELXVF_GPRC 0x101c @@ -84,7 +91,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define INTELXVF_MPRC 0x1034 /** Transmit Descriptor register block */ -#define INTELXVF_TD 0x2000UL +#define INTELXVF_TD(n) ( 0x2000UL + ( 0x40 * (n) ) ) /** Good Packets Transmitted Count */ #define INTELXVF_GPTC 0x201c @@ -101,4 +108,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** API version 1.1 */ #define INTELXVF_MSG_VERSION_1_1 0x00000002UL +/** Number of queues */ +#define INTELXVF_NUM_RINGS 8 + #endif /* _INTELXVF_H */ diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 6552d764e..33c7ddccd 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -65,13 +65,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); "Missing REMAC for IPv4 packet (ARP sent)" ) /** Number of IPoIB send work queue entries */ -#define IPOIB_NUM_SEND_WQES 2 +#define IPOIB_NUM_SEND_WQES 8 /** Number of IPoIB receive work queue entries */ #define IPOIB_NUM_RECV_WQES 4 /** Number of IPoIB completion entries */ -#define IPOIB_NUM_CQES 8 +#define IPOIB_NUM_CQES 16 + +/** An IPoIB broadcast address */ +struct ipoib_broadcast { + /** MAC address */ + struct ipoib_mac mac; + /** Address vector */ + struct ib_address_vector av; + /** Multicast group membership */ + struct ib_mc_membership membership; +}; /** An IPoIB device */ struct ipoib_device { @@ -79,22 +89,16 @@ struct ipoib_device { struct net_device *netdev; /** Underlying Infiniband device */ struct ib_device *ibdev; + /** List of IPoIB devices */ + struct list_head list; /** Completion queue */ struct ib_completion_queue *cq; /** Queue pair */ struct ib_queue_pair *qp; /** Local MAC */ struct ipoib_mac mac; - /** Broadcast MAC */ - struct ipoib_mac broadcast; - /** Joined to IPv4 broadcast multicast group - * - * This flag indicates whether or not we have initiated the - * join to the IPv4 broadcast multicast group. - */ - int broadcast_joined; - /** IPv4 broadcast multicast group membership */ - struct ib_mc_membership broadcast_membership; + /** Broadcast address */ + struct ipoib_broadcast broadcast; /** REMAC cache */ struct list_head peers; }; @@ -116,6 +120,9 @@ struct errortab ipoib_errors[] __errortab = { __einfo_errortab ( EINFO_EINPROGRESS_JOINING ), }; +/** List of all IPoIB devices */ +static LIST_HEAD ( ipoib_devices ); + static struct net_device_operations ipoib_operations; /**************************************************************************** @@ -150,7 +157,7 @@ static struct ipoib_mac * ipoib_find_remac ( struct ipoib_device *ipoib, * multicasts as broadcasts for simplicity. */ if ( is_multicast_ether_addr ( remac ) ) - return &ipoib->broadcast; + return &ipoib->broadcast.mac; /* Try to find via REMAC cache */ list_for_each_entry ( peer, &ipoib->peers, list ) { @@ -496,8 +503,10 @@ static int ipoib_transmit ( struct net_device *netdev, struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipoib_hdr *ipoib_hdr; + struct ipoib_remac *remac; struct ipoib_mac *mac; - struct ib_address_vector dest; + struct ib_address_vector *dest; + struct ib_address_vector av; uint16_t net_proto; int rc; @@ -515,12 +524,32 @@ static int ipoib_transmit ( struct net_device *netdev, /* Strip eIPoIB header */ ethhdr = iobuf->data; + remac = ( ( struct ipoib_remac * ) ethhdr->h_dest ); net_proto = ethhdr->h_protocol; iob_pull ( iobuf, sizeof ( *ethhdr ) ); /* Identify destination address */ - mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) ); - if ( ! mac ) { + if ( is_multicast_ether_addr ( remac ) ) { + + /* Transmit multicasts as broadcasts, for simplicity */ + dest = &ipoib->broadcast.av; + + } else if ( ( mac = ipoib_find_remac ( ipoib, remac ) ) ) { + + /* Construct address vector from IPoIB MAC */ + dest = &av; + memset ( dest, 0, sizeof ( *dest ) ); + dest->qpn = ( ntohl ( mac->flags__qpn ) & IB_QPN_MASK ); + dest->qkey = ipoib->broadcast.av.qkey; + dest->gid_present = 1; + memcpy ( &dest->gid, &mac->gid, sizeof ( dest->gid ) ); + if ( ( rc = ib_resolve_path ( ibdev, dest ) ) != 0 ) { + /* Path not resolved yet */ + return rc; + } + + } else { + /* Generate a new ARP request (if possible) to trigger * population of the REMAC cache entry. */ @@ -557,17 +586,8 @@ static int ipoib_transmit ( struct net_device *netdev, ipoib_hdr->proto = net_proto; ipoib_hdr->reserved = 0; - /* Construct address vector */ - memset ( &dest, 0, sizeof ( dest ) ); - dest.qpn = ( ntohl ( mac->flags__qpn ) & IB_QPN_MASK ); - dest.gid_present = 1; - memcpy ( &dest.gid, &mac->gid, sizeof ( dest.gid ) ); - if ( ( rc = ib_resolve_path ( ibdev, &dest ) ) != 0 ) { - /* Path not resolved yet */ - return rc; - } - - return ib_post_send ( ibdev, ipoib->qp, &dest, iobuf ); + /* Transmit packet */ + return ib_post_send ( ibdev, ipoib->qp, dest, iobuf ); } /** @@ -651,9 +671,8 @@ static void ipoib_complete_recv ( struct ib_device *ibdev __unused, ethhdr->h_protocol = net_proto; /* Construct destination address */ - if ( dest->gid_present && ( memcmp ( &dest->gid, &ipoib->broadcast.gid, - sizeof ( dest->gid ) ) == 0 ) ) { - /* Broadcast GID; use the Ethernet broadcast address */ + if ( dest->gid_present && IB_GID_MULTICAST ( &dest->gid ) ) { + /* Multicast GID: use the Ethernet broadcast address */ memcpy ( ðhdr->h_dest, eth_broadcast, sizeof ( ethhdr->h_dest ) ); } else { @@ -727,18 +746,13 @@ static void ipoib_poll ( struct net_device *netdev ) { /** * Handle IPv4 broadcast multicast group join completion * - * @v ibdev Infiniband device - * @v qp Queue pair * @v membership Multicast group membership * @v rc Status code - * @v mad Response MAD (or NULL on error) */ -void ipoib_join_complete ( struct ib_device *ibdev __unused, - struct ib_queue_pair *qp __unused, - struct ib_mc_membership *membership, int rc, - union ib_mad *mad __unused ) { +void ipoib_join_complete ( struct ib_mc_membership *membership, int rc ) { struct ipoib_device *ipoib = container_of ( membership, - struct ipoib_device, broadcast_membership ); + struct ipoib_device, + broadcast.membership ); /* Record join status as link status */ netdev_link_err ( ipoib->netdev, rc ); @@ -753,15 +767,15 @@ void ipoib_join_complete ( struct ib_device *ibdev __unused, static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { int rc; + /* Join multicast group */ if ( ( rc = ib_mcast_join ( ipoib->ibdev, ipoib->qp, - &ipoib->broadcast_membership, - &ipoib->broadcast.gid, + &ipoib->broadcast.membership, + &ipoib->broadcast.av, 0, ipoib_join_complete ) ) != 0 ) { DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n", ipoib, strerror ( rc ) ); return rc; } - ipoib->broadcast_joined = 1; return 0; } @@ -773,21 +787,19 @@ static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { */ static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) { - if ( ipoib->broadcast_joined ) { - ib_mcast_leave ( ipoib->ibdev, ipoib->qp, - &ipoib->broadcast_membership ); - ipoib->broadcast_joined = 0; - } + /* Leave multicast group */ + ib_mcast_leave ( ipoib->ibdev, ipoib->qp, + &ipoib->broadcast.membership ); } /** * Handle link status change * - * @v ibdev Infiniband device + * @v ipoib IPoIB device */ -static void ipoib_link_state_changed ( struct ib_device *ibdev ) { - struct net_device *netdev = ib_get_ownerdata ( ibdev ); - struct ipoib_device *ipoib = netdev->priv; +static void ipoib_link_state_changed ( struct ipoib_device *ipoib ) { + struct ib_device *ibdev = ipoib->ibdev; + struct net_device *netdev = ipoib->netdev; int rc; /* Leave existing broadcast group */ @@ -798,10 +810,17 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) { memcpy ( &ipoib->mac.gid.s.prefix, &ibdev->gid.s.prefix, sizeof ( ipoib->mac.gid.s.prefix ) ); - /* Update broadcast GID based on potentially-new partition key */ - ipoib->broadcast.gid.words[2] = + /* Update broadcast MAC GID based on potentially-new partition key */ + ipoib->broadcast.mac.gid.words[2] = htons ( ibdev->pkey | IB_PKEY_FULL ); + /* Construct broadcast address vector from broadcast MAC address */ + memset ( &ipoib->broadcast.av, 0, sizeof ( ipoib->broadcast.av ) ); + ipoib->broadcast.av.qpn = IB_QPN_BROADCAST; + ipoib->broadcast.av.gid_present = 1; + memcpy ( &ipoib->broadcast.av.gid, &ipoib->broadcast.mac.gid, + sizeof ( ipoib->broadcast.av.gid ) ); + /* Set net device link state to reflect Infiniband link state */ rc = ib_link_rc ( ibdev ); netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) ); @@ -835,22 +854,20 @@ static int ipoib_open ( struct net_device *netdev ) { } /* Allocate completion queue */ - ipoib->cq = ib_create_cq ( ibdev, IPOIB_NUM_CQES, &ipoib_cq_op ); - if ( ! ipoib->cq ) { - DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n", - ipoib ); - rc = -ENOMEM; + if ( ( rc = ib_create_cq ( ibdev, IPOIB_NUM_CQES, &ipoib_cq_op, + &ipoib->cq ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not create completion queue: " + "%s\n", ipoib, strerror ( rc ) ); goto err_create_cq; } /* Allocate queue pair */ - ipoib->qp = ib_create_qp ( ibdev, IB_QPT_UD, IPOIB_NUM_SEND_WQES, + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_UD, IPOIB_NUM_SEND_WQES, ipoib->cq, IPOIB_NUM_RECV_WQES, ipoib->cq, - &ipoib_qp_op ); - if ( ! ipoib->qp ) { - DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n", - ipoib ); - rc = -ENOMEM; + &ipoib_qp_op, netdev->name, + &ipoib->qp ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not create queue pair: %s\n", + ipoib, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( ipoib->qp, ipoib ); @@ -862,7 +879,7 @@ static int ipoib_open ( struct net_device *netdev ) { ib_refill_recv ( ibdev, ipoib->qp ); /* Fake a link status change to join the broadcast group */ - ipoib_link_state_changed ( ibdev ); + ipoib_link_state_changed ( ipoib ); return 0; @@ -928,7 +945,6 @@ static int ipoib_probe ( struct ib_device *ibdev ) { return -ENOMEM; netdev_init ( netdev, &ipoib_operations ); ipoib = netdev->priv; - ib_set_ownerdata ( ibdev, netdev ); netdev->dev = ibdev->dev; memset ( ipoib, 0, sizeof ( *ipoib ) ); ipoib->netdev = netdev; @@ -938,14 +954,18 @@ static int ipoib_probe ( struct ib_device *ibdev ) { /* Extract hardware address */ memcpy ( netdev->hw_addr, &ibdev->gid.s.guid, sizeof ( ibdev->gid.s.guid ) ); + memcpy ( netdev->ll_addr, ibdev->lemac, ETH_ALEN ); /* Set local MAC address */ memcpy ( &ipoib->mac.gid.s.guid, &ibdev->gid.s.guid, sizeof ( ipoib->mac.gid.s.guid ) ); /* Set default broadcast MAC address */ - memcpy ( &ipoib->broadcast, &ipoib_broadcast, - sizeof ( ipoib->broadcast ) ); + memcpy ( &ipoib->broadcast.mac, &ipoib_broadcast, + sizeof ( ipoib->broadcast.mac ) ); + + /* Add to list of IPoIB devices */ + list_add_tail ( &ipoib->list, &ipoib_devices ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) @@ -953,29 +973,74 @@ static int ipoib_probe ( struct ib_device *ibdev ) { return 0; + unregister_netdev ( netdev ); err_register_netdev: + list_del ( &ipoib->list ); netdev_nullify ( netdev ); netdev_put ( netdev ); return rc; } +/** + * Handle device or link status change + * + * @v ibdev Infiniband device + */ +static void ipoib_notify ( struct ib_device *ibdev ) { + struct ipoib_device *ipoib; + + /* Handle link status change for any attached IPoIB devices */ + list_for_each_entry ( ipoib, &ipoib_devices, list ) { + if ( ipoib->ibdev != ibdev ) + continue; + ipoib_link_state_changed ( ipoib ); + } +} + /** * Remove IPoIB device * * @v ibdev Infiniband device */ static void ipoib_remove ( struct ib_device *ibdev ) { - struct net_device *netdev = ib_get_ownerdata ( ibdev ); + struct ipoib_device *ipoib; + struct ipoib_device *tmp; + struct net_device *netdev; - unregister_netdev ( netdev ); - netdev_nullify ( netdev ); - netdev_put ( netdev ); + /* Remove any attached IPoIB devices */ + list_for_each_entry_safe ( ipoib, tmp, &ipoib_devices, list ) { + if ( ipoib->ibdev != ibdev ) + continue; + netdev = ipoib->netdev; + unregister_netdev ( netdev ); + list_del ( &ipoib->list ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + } } /** IPoIB driver */ struct ib_driver ipoib_driver __ib_driver = { .name = "IPoIB", .probe = ipoib_probe, - .notify = ipoib_link_state_changed, + .notify = ipoib_notify, .remove = ipoib_remove, }; + +/** + * Find IPoIB network device + * + * @v ibdev Infiniband device + * @ret netdev IPoIB network device, or NULL if not found + */ +struct net_device * ipoib_netdev ( struct ib_device *ibdev ) { + struct ipoib_device *ipoib; + + /* Find matching IPoIB device */ + list_for_each_entry ( ipoib, &ipoib_devices, list ) { + if ( ipoib->ibdev != ibdev ) + continue; + return ipoib->netdev; + } + return NULL; +} diff --git a/src/drivers/net/lan78xx.c b/src/drivers/net/lan78xx.c new file mode 100644 index 000000000..3f705203e --- /dev/null +++ b/src/drivers/net/lan78xx.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include "lan78xx.h" + +/** @file + * + * Microchip LAN78xx USB Ethernet driver + * + */ + +/****************************************************************************** + * + * MAC address + * + ****************************************************************************** + */ + +/** + * Fetch MAC address from EEPROM + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int lan78xx_eeprom_fetch_mac ( struct smscusb_device *smscusb ) { + uint32_t hw_cfg; + uint32_t orig_hw_cfg; + int rc; + + /* Read original HW_CFG value */ + if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG, &hw_cfg ) ) != 0 ) + goto err_read_hw_cfg; + orig_hw_cfg = hw_cfg; + + /* Temporarily disable LED0 and LED1 (which share physical + * pins with EEDO and EECLK respectively). + */ + hw_cfg &= ~( LAN78XX_HW_CFG_LED0_EN | LAN78XX_HW_CFG_LED1_EN ); + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG, hw_cfg ) ) != 0 ) + goto err_write_hw_cfg; + + /* Fetch MAC address from EEPROM */ + if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb, + LAN78XX_E2P_BASE ) ) != 0 ) + goto err_fetch_mac; + + err_fetch_mac: + smscusb_writel ( smscusb, LAN78XX_HW_CFG, orig_hw_cfg ); + err_write_hw_cfg: + err_read_hw_cfg: + return rc; +} + +/** + * Fetch MAC address + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + int rc; + + /* Read MAC address from EEPROM, if present */ + if ( ( rc = lan78xx_eeprom_fetch_mac ( smscusb ) ) == 0 ) + return 0; + + /* Read MAC address from OTP, if present */ + if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 ) + return 0; + + /* Otherwise, generate a random MAC address */ + eth_random_addr ( netdev->hw_addr ); + DBGC ( smscusb, "LAN78XX %p using random MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int lan78xx_reset ( struct smscusb_device *smscusb ) { + uint32_t hw_cfg; + unsigned int i; + int rc; + + /* Reset device */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG, + LAN78XX_HW_CFG_LRST ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + for ( i = 0 ; i < LAN78XX_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset has completed */ + if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( ! ( hw_cfg & LAN78XX_HW_CFG_LRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "LAN78XX %p timed out waiting for reset\n", + smscusb ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int lan78xx_open ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + uint32_t usb_cfg0; + int rc; + + /* Clear stored interrupt status */ + smscusb->int_sts = 0; + + /* Configure bulk IN empty response */ + if ( ( rc = smscusb_readl ( smscusb, LAN78XX_USB_CFG0, + &usb_cfg0 ) ) != 0 ) + goto err_usb_cfg0_read; + usb_cfg0 |= LAN78XX_USB_CFG0_BIR; + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_USB_CFG0, + usb_cfg0 ) ) != 0 ) + goto err_usb_cfg0_write; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) { + DBGC ( smscusb, "LAN78XX %p could not open: %s\n", + smscusb, strerror ( rc ) ); + goto err_open; + } + + /* Configure interrupt endpoint */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_INT_EP_CTL, + ( LAN78XX_INT_EP_CTL_RDFO_EN | + LAN78XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + goto err_int_ep_ctl; + + /* Configure bulk IN delay */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_BULK_IN_DLY, + LAN78XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + goto err_bulk_in_dly; + + /* Configure receive filters */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_RFE_CTL, + ( LAN78XX_RFE_CTL_AB | + LAN78XX_RFE_CTL_AM | + LAN78XX_RFE_CTL_AU ) ) ) != 0 ) + goto err_rfe_ctl; + + /* Configure receive FIFO */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_RX_CTL, + ( LAN78XX_FCT_RX_CTL_EN | + LAN78XX_FCT_RX_CTL_BAD ) ) ) != 0 ) + goto err_fct_rx_ctl; + + /* Configure transmit FIFO */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_TX_CTL, + LAN78XX_FCT_TX_CTL_EN ) ) != 0 ) + goto err_fct_tx_ctl; + + /* Configure receive datapath */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_RX, + ( LAN78XX_MAC_RX_MAX_SIZE_DEFAULT | + LAN78XX_MAC_RX_FCS | + LAN78XX_MAC_RX_EN ) ) ) != 0 ) + goto err_mac_rx; + + /* Configure transmit datapath */ + if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_TX, + LAN78XX_MAC_TX_EN ) ) != 0 ) + goto err_mac_tx; + + /* Set MAC address */ + if ( ( rc = smscusb_set_address ( smscusb, + LAN78XX_RX_ADDR_BASE ) ) != 0 ) + goto err_set_address; + + /* Set MAC address perfect filter */ + if ( ( rc = smscusb_set_filter ( smscusb, + LAN78XX_ADDR_FILT_BASE ) ) != 0 ) + goto err_set_filter; + + /* Enable PHY interrupts and update link status */ + if ( ( rc = smscusb_mii_open ( smscusb, LAN78XX_MII_PHY_INTR_MASK, + ( LAN78XX_PHY_INTR_ENABLE | + LAN78XX_PHY_INTR_LINK | + LAN78XX_PHY_INTR_ANEG_ERR | + LAN78XX_PHY_INTR_ANEG_DONE ) ) ) != 0 ) + goto err_mii_open; + + return 0; + + err_mii_open: + err_set_filter: + err_set_address: + err_mac_tx: + err_mac_rx: + err_fct_tx_ctl: + err_fct_rx_ctl: + err_rfe_ctl: + err_bulk_in_dly: + err_int_ep_ctl: + usbnet_close ( &smscusb->usbnet ); + err_open: + err_usb_cfg0_write: + err_usb_cfg0_read: + lan78xx_reset ( smscusb ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void lan78xx_close ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &smscusb->usbnet ); + + /* Dump statistics (for debugging) */ + if ( DBG_LOG ) + smsc75xx_dump_statistics ( smscusb ); + + /* Reset device */ + lan78xx_reset ( smscusb ); +} + +/** LAN78xx network device operations */ +static struct net_device_operations lan78xx_operations = { + .open = lan78xx_open, + .close = lan78xx_close, + .transmit = smsc75xx_transmit, + .poll = smsc75xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int lan78xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct net_device *netdev; + struct smscusb_device *smscusb; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *smscusb ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &lan78xx_operations ); + netdev->dev = &func->dev; + smscusb = netdev->priv; + memset ( smscusb, 0, sizeof ( *smscusb ) ); + smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations ); + smscusb_mii_init ( smscusb, LAN78XX_MII_BASE, + LAN78XX_MII_PHY_INTR_SOURCE ); + usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU, + SMSC75XX_IN_MAX_FILL ); + DBGC ( smscusb, "LAN78XX %p on %s\n", smscusb, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) { + DBGC ( smscusb, "LAN78XX %p could not describe: %s\n", + smscusb, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = lan78xx_reset ( smscusb ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = lan78xx_fetch_mac ( smscusb ) ) != 0 ) + goto err_fetch_mac; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_fetch_mac: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void lan78xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** LAN78xx device IDs */ +static struct usb_device_id lan78xx_ids[] = { + { + .name = "lan7800", + .vendor = 0x0424, + .product = 0x7800, + }, + { + .name = "lan7850", + .vendor = 0x0424, + .product = 0x7850, + }, +}; + +/** LAN78xx driver */ +struct usb_driver lan78xx_driver __usb_driver = { + .ids = lan78xx_ids, + .id_count = ( sizeof ( lan78xx_ids ) / sizeof ( lan78xx_ids[0] ) ), + .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ), + .score = USB_SCORE_NORMAL, + .probe = lan78xx_probe, + .remove = lan78xx_remove, +}; diff --git a/src/drivers/net/lan78xx.h b/src/drivers/net/lan78xx.h new file mode 100644 index 000000000..6ae17238e --- /dev/null +++ b/src/drivers/net/lan78xx.h @@ -0,0 +1,97 @@ +#ifndef _LAN78XX_H +#define _LAN78XX_H + +/** @file + * + * Microchip LAN78xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "smscusb.h" +#include "smsc75xx.h" + +/** Hardware configuration register */ +#define LAN78XX_HW_CFG 0x0010 +#define LAN78XX_HW_CFG_LED1_EN 0x00200000UL /**< LED1 enable */ +#define LAN78XX_HW_CFG_LED0_EN 0x00100000UL /**< LED1 enable */ +#define LAN78XX_HW_CFG_LRST 0x00000002UL /**< Soft lite reset */ + +/** Interrupt endpoint control register */ +#define LAN78XX_INT_EP_CTL 0x0098 +#define LAN78XX_INT_EP_CTL_RDFO_EN 0x00400000UL /**< RX FIFO overflow */ +#define LAN78XX_INT_EP_CTL_PHY_EN 0x00020000UL /**< PHY interrupt */ + +/** Bulk IN delay register */ +#define LAN78XX_BULK_IN_DLY 0x0094 +#define LAN78XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ + +/** EEPROM register base */ +#define LAN78XX_E2P_BASE 0x0040 + +/** USB configuration register 0 */ +#define LAN78XX_USB_CFG0 0x0080 +#define LAN78XX_USB_CFG0_BIR 0x00000040UL /**< Bulk IN use NAK */ + +/** Receive filtering engine control register */ +#define LAN78XX_RFE_CTL 0x00b0 +#define LAN78XX_RFE_CTL_AB 0x00000400UL /**< Accept broadcast */ +#define LAN78XX_RFE_CTL_AM 0x00000200UL /**< Accept multicast */ +#define LAN78XX_RFE_CTL_AU 0x00000100UL /**< Accept unicast */ + +/** FIFO controller RX FIFO control register */ +#define LAN78XX_FCT_RX_CTL 0x00c0 +#define LAN78XX_FCT_RX_CTL_EN 0x80000000UL /**< FCT RX enable */ +#define LAN78XX_FCT_RX_CTL_BAD 0x02000000UL /**< Store bad frames */ + +/** FIFO controller TX FIFO control register */ +#define LAN78XX_FCT_TX_CTL 0x00c4 +#define LAN78XX_FCT_TX_CTL_EN 0x80000000UL /**< FCT TX enable */ + +/** MAC receive register */ +#define LAN78XX_MAC_RX 0x0104 +#define LAN78XX_MAC_RX_MAX_SIZE(mtu) ( (mtu) << 16 ) /**< Max frame size */ +#define LAN78XX_MAC_RX_MAX_SIZE_DEFAULT \ + LAN78XX_MAC_RX_MAX_SIZE ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) +#define LAN78XX_MAC_RX_FCS 0x00000010UL /**< FCS stripping */ +#define LAN78XX_MAC_RX_EN 0x00000001UL /**< RX enable */ + +/** MAC transmit register */ +#define LAN78XX_MAC_TX 0x0108 +#define LAN78XX_MAC_TX_EN 0x00000001UL /**< TX enable */ + +/** MAC receive address register base */ +#define LAN78XX_RX_ADDR_BASE 0x0118 + +/** MII register base */ +#define LAN78XX_MII_BASE 0x0120 + +/** PHY interrupt mask MII register */ +#define LAN78XX_MII_PHY_INTR_MASK 25 + +/** PHY interrupt source MII register */ +#define LAN78XX_MII_PHY_INTR_SOURCE 26 + +/** PHY interrupt: global enable */ +#define LAN78XX_PHY_INTR_ENABLE 0x8000 + +/** PHY interrupt: link state change */ +#define LAN78XX_PHY_INTR_LINK 0x2000 + +/** PHY interrupt: auto-negotiation failure */ +#define LAN78XX_PHY_INTR_ANEG_ERR 0x0800 + +/** PHY interrupt: auto-negotiation complete */ +#define LAN78XX_PHY_INTR_ANEG_DONE 0x0400 + +/** MAC address perfect filter register base */ +#define LAN78XX_ADDR_FILT_BASE 0x0400 + +/** OTP register base */ +#define LAN78XX_OTP_BASE 0x1000 + +/** Maximum time to wait for reset (in milliseconds) */ +#define LAN78XX_RESET_MAX_WAIT_MS 100 + +#endif /* _LAN78XX_H */ diff --git a/src/drivers/net/mii.c b/src/drivers/net/mii.c index 9b297029a..87605f0cb 100644 --- a/src/drivers/net/mii.c +++ b/src/drivers/net/mii.c @@ -37,10 +37,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Restart autonegotiation * - * @v mii MII interface + * @v mii MII device * @ret rc Return status code */ -int mii_restart ( struct mii_interface *mii ) { +int mii_restart ( struct mii_device *mii ) { int bmcr; int rc; @@ -66,12 +66,12 @@ int mii_restart ( struct mii_interface *mii ) { } /** - * Reset MII interface + * Reset MII device * - * @v mii MII interface + * @v mii MII device * @ret rc Return status code */ -int mii_reset ( struct mii_interface *mii ) { +int mii_reset ( struct mii_device *mii ) { unsigned int i; int bmcr; int rc; @@ -119,11 +119,11 @@ int mii_reset ( struct mii_interface *mii ) { /** * Update link status via MII * - * @v mii MII interface + * @v mii MII device * @v netdev Network device * @ret rc Return status code */ -int mii_check_link ( struct mii_interface *mii, struct net_device *netdev ) { +int mii_check_link ( struct mii_device *mii, struct net_device *netdev ) { int bmsr; int link; int rc; @@ -147,3 +147,28 @@ int mii_check_link ( struct mii_interface *mii, struct net_device *netdev ) { return 0; } + +/** + * Find PHY address + * + * @v mii MII device + * @ret rc Return status code + */ +int mii_find ( struct mii_device *mii ) { + unsigned int address; + int id; + + /* Try all possible PHY addresses */ + for ( address = 0 ; address <= MII_MAX_PHY_ADDRESS ; address++ ) { + mii->address = address; + id = mii_read ( mii, MII_PHYSID1 ); + if ( ( id > 0x0000 ) && ( id < 0xffff ) ) { + DBGC ( mii, "MII %p found PHY at address %d\n", + mii, address ); + return 0; + } + } + + DBGC ( mii, "MII %p failed to find an address\n", mii ); + return -ENOENT; +} diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c index fed77a00f..1837291f7 100644 --- a/src/drivers/net/ncm.c +++ b/src/drivers/net/ncm.c @@ -186,7 +186,7 @@ static int ncm_in_prefill ( struct ncm_device *ncm ) { count = NCM_IN_MIN_COUNT; if ( ( count * mtu ) > NCM_IN_MAX_SIZE ) continue; - usb_refill_init ( &ncm->usbnet.in, mtu, count ); + usb_refill_init ( &ncm->usbnet.in, 0, mtu, count ); if ( ( rc = usb_prefill ( &ncm->usbnet.in ) ) != 0 ) { DBGC ( ncm, "NCM %p could not prefill %dx %zd-byte " "buffers for bulk IN\n", ncm, count, mtu ); @@ -575,7 +575,7 @@ static int ncm_probe ( struct usb_function *func, ncm->netdev = netdev; usbnet_init ( &ncm->usbnet, func, &ncm_intr_operations, &ncm_in_operations, &ncm_out_operations ); - usb_refill_init ( &ncm->usbnet.intr, 0, NCM_INTR_COUNT ); + usb_refill_init ( &ncm->usbnet.intr, 0, 0, NCM_INTR_COUNT ); DBGC ( ncm, "NCM %p on %s\n", ncm, func->name ); /* Describe USB network device */ diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 2f4bbf2a0..b6205542b 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -511,15 +511,12 @@ static void netfront_refill_rx ( struct net_device *netdev ) { struct xen_device *xendev = netfront->xendev; struct io_buffer *iobuf; struct netif_rx_request *request; + unsigned int refilled = 0; int notify; int rc; - /* Do nothing if ring is already full */ - if ( netfront_ring_is_full ( &netfront->rx ) ) - return; - /* Refill ring */ - do { + while ( netfront_ring_fill ( &netfront->rx ) < NETFRONT_RX_FILL ) { /* Allocate I/O buffer */ iobuf = alloc_iob ( PAGE_SIZE ); @@ -543,13 +540,17 @@ static void netfront_refill_rx ( struct net_device *netdev ) { /* Move to next descriptor */ netfront->rx_fring.req_prod_pvt++; + refilled++; - } while ( ! netfront_ring_is_full ( &netfront->rx ) ); + } /* Push new descriptors and notify backend if applicable */ - RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, notify ); - if ( notify ) - netfront_send_event ( netfront ); + if ( refilled ) { + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, + notify ); + if ( notify ) + netfront_send_event ( netfront ); + } } /** diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h index 38fd0a77e..c95ed2645 100644 --- a/src/drivers/net/netfront.h +++ b/src/drivers/net/netfront.h @@ -16,7 +16,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define NETFRONT_NUM_TX_DESC 16 /** Number of receive ring entries */ -#define NETFRONT_NUM_RX_DESC 8 +#define NETFRONT_NUM_RX_DESC 32 + +/** Receive ring fill level + * + * The xen-netback driver from kernels 3.18 to 4.2 inclusive have a + * bug (CA-163395) which prevents packet reception if fewer than 18 + * receive descriptors are available. This was fixed in upstream + * kernel commit d5d4852 ("xen-netback: require fewer guest Rx slots + * when not using GSO"). + * + * We provide 18 receive descriptors to avoid unpleasant silent + * failures on these kernel versions. + */ +#define NETFRONT_RX_FILL 18 /** Grant reference indices */ enum netfront_ref_index { @@ -88,6 +101,21 @@ netfront_init_ring ( struct netfront_ring *ring, const char *ref_key, ring->ids = ids; } +/** + * Calculate descriptor ring fill level + * + * @v ring Descriptor ring + * @v fill Fill level + */ +static inline __attribute__ (( always_inline )) unsigned int +netfront_ring_fill ( struct netfront_ring *ring ) { + unsigned int fill_level; + + fill_level = ( ring->id_prod - ring->id_cons ); + assert ( fill_level <= ring->count ); + return fill_level; +} + /** * Check whether or not descriptor ring is full * @@ -96,11 +124,8 @@ netfront_init_ring ( struct netfront_ring *ring, const char *ref_key, */ static inline __attribute__ (( always_inline )) int netfront_ring_is_full ( struct netfront_ring *ring ) { - unsigned int fill_level; - fill_level = ( ring->id_prod - ring->id_cons ); - assert ( fill_level <= ring->count ); - return ( fill_level >= ring->count ); + return ( netfront_ring_fill ( ring ) >= ring->count ); } /** @@ -112,7 +137,7 @@ netfront_ring_is_full ( struct netfront_ring *ring ) { static inline __attribute__ (( always_inline )) int netfront_ring_is_empty ( struct netfront_ring *ring ) { - return ( ring->id_prod == ring->id_cons ); + return ( netfront_ring_fill ( ring ) == 0 ); } /** A netfront NIC */ diff --git a/src/drivers/net/netvsc.c b/src/drivers/net/netvsc.c index d269cd63e..5be52fb8e 100644 --- a/src/drivers/net/netvsc.c +++ b/src/drivers/net/netvsc.c @@ -259,6 +259,15 @@ static int netvsc_revoke_buffer ( struct netvsc_device *netvsc, struct netvsc_revoke_buffer_message msg; int rc; + /* If the buffer's GPADL is obsolete (i.e. was created before + * the most recent Hyper-V reset), then we will never receive + * a response to the revoke message. Since the GPADL is + * already destroyed as far as the hypervisor is concerned, no + * further action is required. + */ + if ( netvsc_is_obsolete ( netvsc ) ) + return 0; + /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( buffer->revoke_type ); @@ -474,6 +483,14 @@ static int netvsc_transmit ( struct rndis_device *rndis, uint64_t xid; int rc; + /* If the device is obsolete (i.e. was opened before the most + * recent Hyper-V reset), then we will never receive transmit + * completions. Fail transmissions immediately to minimise + * the delay in closing and reopening the device. + */ + if ( netvsc_is_obsolete ( netvsc ) ) + return -EPIPE; + /* Sanity check */ assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); @@ -823,6 +840,35 @@ static int netvsc_probe ( struct vmbus_device *vmdev ) { return rc; } +/** + * Reset device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ +static int netvsc_reset ( struct vmbus_device *vmdev ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + struct net_device *netdev = rndis->netdev; + int rc; + + /* A closed device holds no NetVSC (or RNDIS) state, so there + * is nothing to reset. + */ + if ( ! netdev_is_open ( netdev ) ) + return 0; + + /* Close and reopen device to reset any stale state */ + netdev_close ( netdev ); + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not reopen: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + /** * Remove device * @@ -844,5 +890,6 @@ struct vmbus_driver netvsc_driver __vmbus_driver = { .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ), .probe = netvsc_probe, + .reset = netvsc_reset, .remove = netvsc_remove, }; diff --git a/src/drivers/net/netvsc.h b/src/drivers/net/netvsc.h index 39eeb891c..93192357f 100644 --- a/src/drivers/net/netvsc.h +++ b/src/drivers/net/netvsc.h @@ -362,4 +362,19 @@ struct netvsc_device { int wait_rc; }; +/** + * Check if NetVSC device is obsolete + * + * @v netvsc NetVSC device + * @v is_obsolete NetVSC device is obsolete + * + * Check if NetVSC device is obsolete (i.e. was opened before the most + * recent Hyper-V reset). + */ +static inline __attribute__ (( always_inline )) int +netvsc_is_obsolete ( struct netvsc_device *netvsc ) { + + return vmbus_gpadl_is_obsolete ( netvsc->rx.gpadl ); +} + #endif /* _NETVSC_H */ diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c index 26633a248..2635aaca2 100644 --- a/src/drivers/net/pcnet32.c +++ b/src/drivers/net/pcnet32.c @@ -414,8 +414,7 @@ pcnet32_chip_detect ( struct pcnet32_private *priv ) if (fset) { a->write_bcr ( ioaddr, 18, ( a->read_bcr ( ioaddr, 18 ) | 0x0860 ) ); - a->write_csr ( ioaddr, 80, - ( a->read_csr ( ioaddr, 80 ) & 0x0C00) | 0x0C00 ); + a->write_csr ( ioaddr, 80, 0x0c00 ); } priv->full_duplex = fdx; diff --git a/src/drivers/net/pcnet32.h b/src/drivers/net/pcnet32.h index 5e4492ef9..f06d7fd00 100644 --- a/src/drivers/net/pcnet32.h +++ b/src/drivers/net/pcnet32.h @@ -23,8 +23,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #ifndef _PCNET32_H_ #define _PCNET32_H_ -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - /* * Set the number of Tx and Rx buffers, using Log_2(# buffers). * Set default values to 16 Tx buffers and 32 Rx buffers. diff --git a/src/drivers/net/phantom/phantom.c b/src/drivers/net/phantom/phantom.c index 38b66743c..781049ff4 100644 --- a/src/drivers/net/phantom/phantom.c +++ b/src/drivers/net/phantom/phantom.c @@ -2060,6 +2060,7 @@ static int phantom_probe ( struct pci_device *pci ) { struct net_device *netdev; struct phantom_nic *phantom; struct settings *parent_settings; + unsigned int busdevfn; int rc; /* Allocate Phantom device */ @@ -2090,19 +2091,20 @@ static int phantom_probe ( struct pci_device *pci ) { * B2 will have this fixed; remove this hack when B1 is no * longer in use. */ - if ( PCI_FUNC ( pci->busdevfn ) == 0 ) { + busdevfn = pci->busdevfn; + if ( PCI_FUNC ( busdevfn ) == 0 ) { unsigned int i; for ( i = 0 ; i < 8 ; i++ ) { uint32_t temp; pci->busdevfn = - PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), - PCI_SLOT ( pci->busdevfn ), i ); + PCI_BUSDEVFN ( PCI_SEG ( busdevfn ), + PCI_BUS ( busdevfn ), + PCI_SLOT ( busdevfn ), i ); pci_read_config_dword ( pci, 0xc8, &temp ); pci_read_config_dword ( pci, 0xc8, &temp ); pci_write_config_dword ( pci, 0xc8, 0xf1000 ); } - pci->busdevfn = PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), - PCI_SLOT ( pci->busdevfn ), 0 ); + pci->busdevfn = busdevfn; } /* Initialise the command PEG */ diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index 022b59324..310b9f96a 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -242,12 +242,15 @@ static int realtek_init_eeprom ( struct net_device *netdev ) { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int realtek_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct realtek_nic *rtl = container_of ( mii, struct realtek_nic, mii ); +static int realtek_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { + struct realtek_nic *rtl = + container_of ( mdio, struct realtek_nic, mdio ); unsigned int i; uint32_t value; @@ -279,14 +282,17 @@ static int realtek_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int realtek_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data) { - struct realtek_nic *rtl = container_of ( mii, struct realtek_nic, mii ); +static int realtek_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, + unsigned int data ) { + struct realtek_nic *rtl = + container_of ( mdio, struct realtek_nic, mdio ); unsigned int i; /* Fail if PHYAR register is not present */ @@ -1158,7 +1164,8 @@ static int realtek_probe ( struct pci_device *pci ) { } /* Initialise and reset MII interface */ - mii_init ( &rtl->mii, &realtek_mii_operations ); + mdio_init ( &rtl->mdio, &realtek_mii_operations ); + mii_init ( &rtl->mii, &rtl->mdio, 0 ); if ( ( rc = realtek_phy_reset ( rtl ) ) != 0 ) goto err_phy_reset; diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h index b1ce7f98f..4d13784c4 100644 --- a/src/drivers/net/realtek.h +++ b/src/drivers/net/realtek.h @@ -283,7 +283,9 @@ struct realtek_nic { /** Non-volatile options */ struct nvo_block nvo; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** Legacy datapath mode */ int legacy; diff --git a/src/drivers/net/rhine.c b/src/drivers/net/rhine.c index 42bc124e0..a1dc58725 100644 --- a/src/drivers/net/rhine.c +++ b/src/drivers/net/rhine.c @@ -49,12 +49,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int rhine_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); +static int rhine_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { + struct rhine_nic *rhn = container_of ( mdio, struct rhine_nic, mdio ); unsigned int timeout = RHINE_TIMEOUT_US; uint8_t cr; @@ -80,14 +82,16 @@ static int rhine_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int rhine_mii_write ( struct mii_interface *mii, unsigned int reg, +static int rhine_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, unsigned int data ) { - struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); + struct rhine_nic *rhn = container_of ( mdio, struct rhine_nic, mdio ); unsigned int timeout = RHINE_TIMEOUT_US; uint8_t cr; @@ -719,15 +723,15 @@ static int rhine_probe ( struct pci_device *pci ) { netdev->hw_addr[i] = readb ( rhn->regs + RHINE_MAC + i ); /* Initialise and reset MII interface */ - mii_init ( &rhn->mii, &rhine_mii_operations ); + mdio_init ( &rhn->mdio, &rhine_mii_operations ); + mii_init ( &rhn->mii, &rhn->mdio, 0 ); if ( ( rc = mii_reset ( &rhn->mii ) ) != 0 ) { DBGC ( rhn, "RHINE %p could not reset MII: %s\n", rhn, strerror ( rc ) ); goto err_mii_reset; } DBGC ( rhn, "RHINE PHY vendor %04x device %04x\n", - rhine_mii_read ( &rhn->mii, 0x02 ), - rhine_mii_read ( &rhn->mii, 0x03 ) ); + mii_read ( &rhn->mii, 0x02 ), mii_read ( &rhn->mii, 0x03 ) ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) diff --git a/src/drivers/net/rhine.h b/src/drivers/net/rhine.h index b26f9ae78..eef49ec98 100644 --- a/src/drivers/net/rhine.h +++ b/src/drivers/net/rhine.h @@ -237,7 +237,9 @@ struct rhine_nic { uint8_t cr1; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** Transmit descriptor ring */ struct rhine_ring tx; diff --git a/src/drivers/net/rtl818x/rtl8185_rtl8225.c b/src/drivers/net/rtl818x/rtl8185_rtl8225.c index ae92531ce..31a740e64 100644 --- a/src/drivers/net/rtl818x/rtl8185_rtl8225.c +++ b/src/drivers/net/rtl818x/rtl8185_rtl8225.c @@ -30,7 +30,6 @@ FILE_LICENCE(GPL2_ONLY); #define RTL8225_ANAPARAM2_OFF 0x840dec11 #define min(a,b) (((a)<(b))?(a):(b)) -#define ARRAY_SIZE(a) (int)(sizeof(a)/sizeof((a)[0])) static inline void rtl8225_write_phy_ofdm(struct net80211_device *dev, u8 addr, u8 data) @@ -323,7 +322,7 @@ static void rtl8225_rf_set_tx_power(struct net80211_device *dev, int channel) static void rtl8225_rf_init(struct net80211_device *dev) { struct rtl818x_priv *priv = dev->priv; - int i; + unsigned int i; rtl818x_set_anaparam(priv, RTL8225_ANAPARAM_ON); @@ -552,7 +551,7 @@ static const u16 rtl8225z2_rxgain[] = { static void rtl8225z2_rf_init(struct net80211_device *dev) { struct rtl818x_priv *priv = dev->priv; - int i; + unsigned int i; rtl818x_set_anaparam(priv, RTL8225_ANAPARAM_ON); diff --git a/src/drivers/net/rtl818x/rtl818x.c b/src/drivers/net/rtl818x/rtl818x.c index 8b3c206d4..f5082084e 100644 --- a/src/drivers/net/rtl818x/rtl818x.c +++ b/src/drivers/net/rtl818x/rtl818x.c @@ -663,7 +663,8 @@ int rtl818x_probe(struct pci_device *pdev ) hwinfo = zalloc(sizeof(*hwinfo)); if (!hwinfo) { DBG("rtl818x: hwinfo alloc failed\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_alloc_hwinfo; } adjust_pci_device(pdev); @@ -671,7 +672,8 @@ int rtl818x_probe(struct pci_device *pdev ) dev = net80211_alloc(sizeof(*priv)); if (!dev) { DBG("rtl818x: net80211 alloc failed\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_alloc_dev; } priv = dev->priv; @@ -816,7 +818,9 @@ int rtl818x_probe(struct pci_device *pdev ) err_free_dev: pci_set_drvdata(pdev, NULL); net80211_free(dev); + err_alloc_dev: free(hwinfo); + err_alloc_hwinfo: return err; } diff --git a/src/drivers/net/sfc/ef10_regs.h b/src/drivers/net/sfc/ef10_regs.h new file mode 100644 index 000000000..0510e8fff --- /dev/null +++ b/src/drivers/net/sfc/ef10_regs.h @@ -0,0 +1,364 @@ +/**************************************************************************** + * + * Driver for Solarflare network controllers and boards + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ + +#ifndef EFX_EF10_REGS_H +#define EFX_EF10_REGS_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** \file ef10_regs.h + * EF10 hardware architecture definitions + * + * EF10 hardware architecture definitions have a name prefix following + * the format: + * + * E__ + * + * The following strings are used: + * + * MMIO register Host memory structure + * Address R + * Bitfield RF SF + * Enumerator FE SE + * + * is the first revision to which the definition applies: + * + * D: Huntington A0 + * + * If the definition has been changed or removed in later revisions + * then is the last revision to which the definition applies; + * otherwise it is "Z". + */ + +/************************************************************************** + * + * EF10 registers and descriptors + * + ************************************************************************** + */ + +/* BIU_HW_REV_ID_REG: */ +#define ER_DZ_BIU_HW_REV_ID 0x00000000 +#define ERF_DZ_HW_REV_ID_LBN 0 +#define ERF_DZ_HW_REV_ID_WIDTH 32 + +/* BIU_MC_SFT_STATUS_REG: */ +#define ER_DZ_BIU_MC_SFT_STATUS 0x00000010 +#define ER_DZ_BIU_MC_SFT_STATUS_STEP 4 +#define ER_DZ_BIU_MC_SFT_STATUS_ROWS 8 +#define ERF_DZ_MC_SFT_STATUS_LBN 0 +#define ERF_DZ_MC_SFT_STATUS_WIDTH 32 + +/* BIU_INT_ISR_REG: */ +#define ER_DZ_BIU_INT_ISR 0x00000090 +#define ERF_DZ_ISR_REG_LBN 0 +#define ERF_DZ_ISR_REG_WIDTH 32 + +/* MC_DB_LWRD_REG: */ +#define ER_DZ_MC_DB_LWRD 0x00000200 +#define ERF_DZ_MC_DOORBELL_L_LBN 0 +#define ERF_DZ_MC_DOORBELL_L_WIDTH 32 + +/* MC_DB_HWRD_REG: */ +#define ER_DZ_MC_DB_HWRD 0x00000204 +#define ERF_DZ_MC_DOORBELL_H_LBN 0 +#define ERF_DZ_MC_DOORBELL_H_WIDTH 32 + +/* EVQ_RPTR_REG: */ +#define ER_DZ_EVQ_RPTR 0x00000400 +#define ER_DZ_EVQ_RPTR_STEP 8192 +#define ER_DZ_EVQ_RPTR_ROWS 2048 +#define ERF_DZ_EVQ_RPTR_VLD_LBN 15 +#define ERF_DZ_EVQ_RPTR_VLD_WIDTH 1 +#define ERF_DZ_EVQ_RPTR_LBN 0 +#define ERF_DZ_EVQ_RPTR_WIDTH 15 + +/* EVQ_TMR_REG: */ +#define ER_DZ_EVQ_TMR 0x00000420 +#define ER_DZ_EVQ_TMR_STEP 8192 +#define ER_DZ_EVQ_TMR_ROWS 2048 +#define ERF_DZ_TC_TIMER_MODE_LBN 14 +#define ERF_DZ_TC_TIMER_MODE_WIDTH 2 +#define ERF_DZ_TC_TIMER_VAL_LBN 0 +#define ERF_DZ_TC_TIMER_VAL_WIDTH 14 + +/* RX_DESC_UPD_REG: */ +#define ER_DZ_RX_DESC_UPD 0x00000830 +#define ER_DZ_RX_DESC_UPD_STEP 8192 +#define ER_DZ_RX_DESC_UPD_ROWS 2048 +#define ERF_DZ_RX_DESC_WPTR_LBN 0 +#define ERF_DZ_RX_DESC_WPTR_WIDTH 12 + +/* TX_DESC_UPD_REG: */ +#define ER_DZ_TX_DESC_UPD 0x00000a10 +#define ER_DZ_TX_DESC_UPD_STEP 8192 +#define ER_DZ_TX_DESC_UPD_ROWS 2048 +#define ERF_DZ_RSVD_LBN 76 +#define ERF_DZ_RSVD_WIDTH 20 +#define ERF_DZ_TX_DESC_WPTR_LBN 64 +#define ERF_DZ_TX_DESC_WPTR_WIDTH 12 +#define ERF_DZ_TX_DESC_HWORD_LBN 32 +#define ERF_DZ_TX_DESC_HWORD_WIDTH 32 +#define ERF_DZ_TX_DESC_LWORD_LBN 0 +#define ERF_DZ_TX_DESC_LWORD_WIDTH 32 + +/* DRIVER_EV */ +#define ESF_DZ_DRV_CODE_LBN 60 +#define ESF_DZ_DRV_CODE_WIDTH 4 +#define ESF_DZ_DRV_SUB_CODE_LBN 56 +#define ESF_DZ_DRV_SUB_CODE_WIDTH 4 +#define ESE_DZ_DRV_TIMER_EV 3 +#define ESE_DZ_DRV_START_UP_EV 2 +#define ESE_DZ_DRV_WAKE_UP_EV 1 +#define ESF_DZ_DRV_SUB_DATA_LBN 0 +#define ESF_DZ_DRV_SUB_DATA_WIDTH 56 +#define ESF_DZ_DRV_EVQ_ID_LBN 0 +#define ESF_DZ_DRV_EVQ_ID_WIDTH 14 +#define ESF_DZ_DRV_TMR_ID_LBN 0 +#define ESF_DZ_DRV_TMR_ID_WIDTH 14 + +/* EVENT_ENTRY */ +#define ESF_DZ_EV_CODE_LBN 60 +#define ESF_DZ_EV_CODE_WIDTH 4 +#define ESE_DZ_EV_CODE_MCDI_EV 12 +#define ESE_DZ_EV_CODE_DRIVER_EV 5 +#define ESE_DZ_EV_CODE_TX_EV 2 +#define ESE_DZ_EV_CODE_RX_EV 0 +#define ESE_DZ_OTHER other +#define ESF_DZ_EV_DATA_LBN 0 +#define ESF_DZ_EV_DATA_WIDTH 60 + +/* MC_EVENT */ +#define ESF_DZ_MC_CODE_LBN 60 +#define ESF_DZ_MC_CODE_WIDTH 4 +#define ESF_DZ_MC_OVERRIDE_HOLDOFF_LBN 59 +#define ESF_DZ_MC_OVERRIDE_HOLDOFF_WIDTH 1 +#define ESF_DZ_MC_DROP_EVENT_LBN 58 +#define ESF_DZ_MC_DROP_EVENT_WIDTH 1 +#define ESF_DZ_MC_SOFT_LBN 0 +#define ESF_DZ_MC_SOFT_WIDTH 58 + +/* RX_EVENT */ +#define ESF_DZ_RX_CODE_LBN 60 +#define ESF_DZ_RX_CODE_WIDTH 4 +#define ESF_DZ_RX_OVERRIDE_HOLDOFF_LBN 59 +#define ESF_DZ_RX_OVERRIDE_HOLDOFF_WIDTH 1 +#define ESF_DZ_RX_DROP_EVENT_LBN 58 +#define ESF_DZ_RX_DROP_EVENT_WIDTH 1 +#define ESF_DZ_RX_EV_RSVD2_LBN 54 +#define ESF_DZ_RX_EV_RSVD2_WIDTH 4 +#define ESF_DZ_RX_EV_SOFT2_LBN 52 +#define ESF_DZ_RX_EV_SOFT2_WIDTH 2 +#define ESF_DZ_RX_DSC_PTR_LBITS_LBN 48 +#define ESF_DZ_RX_DSC_PTR_LBITS_WIDTH 4 +#define ESF_DZ_RX_L4_CLASS_LBN 45 +#define ESF_DZ_RX_L4_CLASS_WIDTH 3 +#define ESE_DZ_L4_CLASS_RSVD7 7 +#define ESE_DZ_L4_CLASS_RSVD6 6 +#define ESE_DZ_L4_CLASS_RSVD5 5 +#define ESE_DZ_L4_CLASS_RSVD4 4 +#define ESE_DZ_L4_CLASS_RSVD3 3 +#define ESE_DZ_L4_CLASS_UDP 2 +#define ESE_DZ_L4_CLASS_TCP 1 +#define ESE_DZ_L4_CLASS_UNKNOWN 0 +#define ESF_DZ_RX_L3_CLASS_LBN 42 +#define ESF_DZ_RX_L3_CLASS_WIDTH 3 +#define ESE_DZ_L3_CLASS_RSVD7 7 +#define ESE_DZ_L3_CLASS_IP6_FRAG 6 +#define ESE_DZ_L3_CLASS_ARP 5 +#define ESE_DZ_L3_CLASS_IP4_FRAG 4 +#define ESE_DZ_L3_CLASS_FCOE 3 +#define ESE_DZ_L3_CLASS_IP6 2 +#define ESE_DZ_L3_CLASS_IP4 1 +#define ESE_DZ_L3_CLASS_UNKNOWN 0 +#define ESF_DZ_RX_ETH_TAG_CLASS_LBN 39 +#define ESF_DZ_RX_ETH_TAG_CLASS_WIDTH 3 +#define ESE_DZ_ETH_TAG_CLASS_RSVD7 7 +#define ESE_DZ_ETH_TAG_CLASS_RSVD6 6 +#define ESE_DZ_ETH_TAG_CLASS_RSVD5 5 +#define ESE_DZ_ETH_TAG_CLASS_RSVD4 4 +#define ESE_DZ_ETH_TAG_CLASS_RSVD3 3 +#define ESE_DZ_ETH_TAG_CLASS_VLAN2 2 +#define ESE_DZ_ETH_TAG_CLASS_VLAN1 1 +#define ESE_DZ_ETH_TAG_CLASS_NONE 0 +#define ESF_DZ_RX_ETH_BASE_CLASS_LBN 36 +#define ESF_DZ_RX_ETH_BASE_CLASS_WIDTH 3 +#define ESE_DZ_ETH_BASE_CLASS_LLC_SNAP 2 +#define ESE_DZ_ETH_BASE_CLASS_LLC 1 +#define ESE_DZ_ETH_BASE_CLASS_ETH2 0 +#define ESF_DZ_RX_MAC_CLASS_LBN 35 +#define ESF_DZ_RX_MAC_CLASS_WIDTH 1 +#define ESE_DZ_MAC_CLASS_MCAST 1 +#define ESE_DZ_MAC_CLASS_UCAST 0 +#define ESF_DZ_RX_EV_SOFT1_LBN 32 +#define ESF_DZ_RX_EV_SOFT1_WIDTH 3 +#define ESF_DZ_RX_EV_RSVD1_LBN 31 +#define ESF_DZ_RX_EV_RSVD1_WIDTH 1 +#define ESF_DZ_RX_ABORT_LBN 30 +#define ESF_DZ_RX_ABORT_WIDTH 1 +#define ESF_DZ_RX_ECC_ERR_LBN 29 +#define ESF_DZ_RX_ECC_ERR_WIDTH 1 +#define ESF_DZ_RX_CRC1_ERR_LBN 28 +#define ESF_DZ_RX_CRC1_ERR_WIDTH 1 +#define ESF_DZ_RX_CRC0_ERR_LBN 27 +#define ESF_DZ_RX_CRC0_ERR_WIDTH 1 +#define ESF_DZ_RX_TCPUDP_CKSUM_ERR_LBN 26 +#define ESF_DZ_RX_TCPUDP_CKSUM_ERR_WIDTH 1 +#define ESF_DZ_RX_IPCKSUM_ERR_LBN 25 +#define ESF_DZ_RX_IPCKSUM_ERR_WIDTH 1 +#define ESF_DZ_RX_ECRC_ERR_LBN 24 +#define ESF_DZ_RX_ECRC_ERR_WIDTH 1 +#define ESF_DZ_RX_QLABEL_LBN 16 +#define ESF_DZ_RX_QLABEL_WIDTH 5 +#define ESF_DZ_RX_PARSE_INCOMPLETE_LBN 15 +#define ESF_DZ_RX_PARSE_INCOMPLETE_WIDTH 1 +#define ESF_DZ_RX_CONT_LBN 14 +#define ESF_DZ_RX_CONT_WIDTH 1 +#define ESF_DZ_RX_BYTES_LBN 0 +#define ESF_DZ_RX_BYTES_WIDTH 14 + +/* RX_KER_DESC */ +#define ESF_DZ_RX_KER_RESERVED_LBN 62 +#define ESF_DZ_RX_KER_RESERVED_WIDTH 2 +#define ESF_DZ_RX_KER_BYTE_CNT_LBN 48 +#define ESF_DZ_RX_KER_BYTE_CNT_WIDTH 14 +#define ESF_DZ_RX_KER_BUF_ADDR_LBN 0 +#define ESF_DZ_RX_KER_BUF_ADDR_WIDTH 48 + +/* TX_CSUM_TSTAMP_DESC */ +#define ESF_DZ_TX_DESC_IS_OPT_LBN 63 +#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1 +#define ESF_DZ_TX_OPTION_TYPE_LBN 60 +#define ESF_DZ_TX_OPTION_TYPE_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_TSO 7 +#define ESE_DZ_TX_OPTION_DESC_VLAN 6 +#define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TIMESTAMP_LBN 5 +#define ESF_DZ_TX_TIMESTAMP_WIDTH 1 +#define ESF_DZ_TX_OPTION_CRC_MODE_LBN 2 +#define ESF_DZ_TX_OPTION_CRC_MODE_WIDTH 3 +#define ESE_DZ_TX_OPTION_CRC_FCOIP_MPA 5 +#define ESE_DZ_TX_OPTION_CRC_FCOIP_FCOE 4 +#define ESE_DZ_TX_OPTION_CRC_ISCSI_HDR_AND_PYLD 3 +#define ESE_DZ_TX_OPTION_CRC_ISCSI_HDR 2 +#define ESE_DZ_TX_OPTION_CRC_FCOE 1 +#define ESE_DZ_TX_OPTION_CRC_OFF 0 +#define ESF_DZ_TX_OPTION_UDP_TCP_CSUM_LBN 1 +#define ESF_DZ_TX_OPTION_UDP_TCP_CSUM_WIDTH 1 +#define ESF_DZ_TX_OPTION_IP_CSUM_LBN 0 +#define ESF_DZ_TX_OPTION_IP_CSUM_WIDTH 1 + +/* TX_EVENT */ +#define ESF_DZ_TX_CODE_LBN 60 +#define ESF_DZ_TX_CODE_WIDTH 4 +#define ESF_DZ_TX_OVERRIDE_HOLDOFF_LBN 59 +#define ESF_DZ_TX_OVERRIDE_HOLDOFF_WIDTH 1 +#define ESF_DZ_TX_DROP_EVENT_LBN 58 +#define ESF_DZ_TX_DROP_EVENT_WIDTH 1 +#define ESF_DZ_TX_EV_RSVD_LBN 48 +#define ESF_DZ_TX_EV_RSVD_WIDTH 10 +#define ESF_DZ_TX_SOFT2_LBN 32 +#define ESF_DZ_TX_SOFT2_WIDTH 16 +#define ESF_DZ_TX_CAN_MERGE_LBN 31 +#define ESF_DZ_TX_CAN_MERGE_WIDTH 1 +#define ESF_DZ_TX_SOFT1_LBN 24 +#define ESF_DZ_TX_SOFT1_WIDTH 7 +#define ESF_DZ_TX_QLABEL_LBN 16 +#define ESF_DZ_TX_QLABEL_WIDTH 5 +#define ESF_DZ_TX_DESCR_INDX_LBN 0 +#define ESF_DZ_TX_DESCR_INDX_WIDTH 16 + +/* TX_KER_DESC */ +#define ESF_DZ_TX_KER_TYPE_LBN 63 +#define ESF_DZ_TX_KER_TYPE_WIDTH 1 +#define ESF_DZ_TX_KER_CONT_LBN 62 +#define ESF_DZ_TX_KER_CONT_WIDTH 1 +#define ESF_DZ_TX_KER_BYTE_CNT_LBN 48 +#define ESF_DZ_TX_KER_BYTE_CNT_WIDTH 14 +#define ESF_DZ_TX_KER_BUF_ADDR_LBN 0 +#define ESF_DZ_TX_KER_BUF_ADDR_WIDTH 48 + +/* TX_PIO_DESC */ +#define ESF_DZ_TX_PIO_TYPE_LBN 63 +#define ESF_DZ_TX_PIO_TYPE_WIDTH 1 +#define ESF_DZ_TX_PIO_OPT_LBN 60 +#define ESF_DZ_TX_PIO_OPT_WIDTH 3 +#define ESF_DZ_TX_PIO_CONT_LBN 59 +#define ESF_DZ_TX_PIO_CONT_WIDTH 1 +#define ESF_DZ_TX_PIO_BYTE_CNT_LBN 32 +#define ESF_DZ_TX_PIO_BYTE_CNT_WIDTH 12 +#define ESF_DZ_TX_PIO_BUF_ADDR_LBN 0 +#define ESF_DZ_TX_PIO_BUF_ADDR_WIDTH 12 + +/* TX_TSO_DESC */ +#define ESF_DZ_TX_DESC_IS_OPT_LBN 63 +#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1 +#define ESF_DZ_TX_OPTION_TYPE_LBN 60 +#define ESF_DZ_TX_OPTION_TYPE_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_TSO 7 +#define ESE_DZ_TX_OPTION_DESC_VLAN 6 +#define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TSO_TCP_FLAGS_LBN 48 +#define ESF_DZ_TX_TSO_TCP_FLAGS_WIDTH 8 +#define ESF_DZ_TX_TSO_IP_ID_LBN 32 +#define ESF_DZ_TX_TSO_IP_ID_WIDTH 16 +#define ESF_DZ_TX_TSO_TCP_SEQNO_LBN 0 +#define ESF_DZ_TX_TSO_TCP_SEQNO_WIDTH 32 + +/*************************************************************************/ + +/* TX_DESC_UPD_REG: Transmit descriptor update register. + * We may write just one dword of these registers. + */ +#define ER_DZ_TX_DESC_UPD_DWORD (ER_DZ_TX_DESC_UPD + 2 * 4) +#define ERF_DZ_TX_DESC_WPTR_DWORD_LBN (ERF_DZ_TX_DESC_WPTR_LBN - 2 * 32) +#define ERF_DZ_TX_DESC_WPTR_DWORD_WIDTH ERF_DZ_TX_DESC_WPTR_WIDTH + +/* The workaround for bug 35388 requires multiplexing writes through + * the TX_DESC_UPD_DWORD address. + * TX_DESC_UPD: 0ppppppppppp (bit 11 lost) + * EVQ_RPTR: 1000hhhhhhhh, 1001llllllll (split into high and low bits) + * EVQ_TMR: 11mmvvvvvvvv (bits 8:13 of value lost) + */ +#define ER_DD_EVQ_INDIRECT ER_DZ_TX_DESC_UPD_DWORD +#define ERF_DD_EVQ_IND_RPTR_FLAGS_LBN 8 +#define ERF_DD_EVQ_IND_RPTR_FLAGS_WIDTH 4 +#define EFE_DD_EVQ_IND_RPTR_FLAGS_HIGH 8 +#define EFE_DD_EVQ_IND_RPTR_FLAGS_LOW 9 +#define ERF_DD_EVQ_IND_RPTR_LBN 0 +#define ERF_DD_EVQ_IND_RPTR_WIDTH 8 +#define ERF_DD_EVQ_IND_TIMER_FLAGS_LBN 10 +#define ERF_DD_EVQ_IND_TIMER_FLAGS_WIDTH 2 +#define EFE_DD_EVQ_IND_TIMER_FLAGS 3 +#define ERF_DD_EVQ_IND_TIMER_MODE_LBN 8 +#define ERF_DD_EVQ_IND_TIMER_MODE_WIDTH 2 +#define ERF_DD_EVQ_IND_TIMER_VAL_LBN 0 +#define ERF_DD_EVQ_IND_TIMER_VAL_WIDTH 8 + +/* TX_PIOBUF + * PIO buffer aperture (paged) + */ +#define ER_DZ_TX_PIOBUF 4096 +#define ER_DZ_TX_PIOBUF_SIZE 2048 + +/* RX packet prefix */ +#define ES_DZ_RX_PREFIX_HASH_OFST 0 +#define ES_DZ_RX_PREFIX_VLAN1_OFST 4 +#define ES_DZ_RX_PREFIX_VLAN2_OFST 6 +#define ES_DZ_RX_PREFIX_PKTLEN_OFST 8 +#define ES_DZ_RX_PREFIX_TSTAMP_OFST 10 +#define ES_DZ_RX_PREFIX_SIZE 14 + +#endif /* EFX_EF10_REGS_H */ diff --git a/src/drivers/net/sfc/efx_bitfield.h b/src/drivers/net/sfc/efx_bitfield.h new file mode 100644 index 000000000..f1e9b932b --- /dev/null +++ b/src/drivers/net/sfc/efx_bitfield.h @@ -0,0 +1,555 @@ +/**************************************************************************** + * + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ + +#ifndef EFX_BITFIELD_H +#define EFX_BITFIELD_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** \file efx_bitfield.h + * Efx bitfield access + * + * Efx NICs make extensive use of bitfields up to 128 bits + * wide. Since there is no native 128-bit datatype on most systems, + * and since 64-bit datatypes are inefficient on 32-bit systems and + * vice versa, we wrap accesses in a way that uses the most efficient + * datatype. + * + * The NICs are PCI devices and therefore little-endian. Since most + * of the quantities that we deal with are DMAed to/from host memory, + * we define our datatypes (efx_oword_t, efx_qword_t and + * efx_dword_t) to be little-endian. + */ + +/* Lowest bit numbers and widths */ +#define EFX_DUMMY_FIELD_LBN 0 +#define EFX_DUMMY_FIELD_WIDTH 0 +#define EFX_WORD_0_LBN 0 +#define EFX_WORD_0_WIDTH 16 +#define EFX_WORD_1_LBN 16 +#define EFX_WORD_1_WIDTH 16 +#define EFX_DWORD_0_LBN 0 +#define EFX_DWORD_0_WIDTH 32 +#define EFX_DWORD_1_LBN 32 +#define EFX_DWORD_1_WIDTH 32 +#define EFX_DWORD_2_LBN 64 +#define EFX_DWORD_2_WIDTH 32 +#define EFX_DWORD_3_LBN 96 +#define EFX_DWORD_3_WIDTH 32 +#define EFX_QWORD_0_LBN 0 +#define EFX_QWORD_0_WIDTH 64 + +/* Specified attribute (e.g. LBN) of the specified field */ +#define EFX_VAL(field, attribute) field ## _ ## attribute +/* Low bit number of the specified field */ +#define EFX_LOW_BIT(field) EFX_VAL(field, LBN) +/* Bit width of the specified field */ +#define EFX_WIDTH(field) EFX_VAL(field, WIDTH) +/* High bit number of the specified field */ +#define EFX_HIGH_BIT(field) (EFX_LOW_BIT(field) + EFX_WIDTH(field) - 1) +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 64 bits. + */ +#define EFX_MASK64(width) \ + ((width) == 64 ? ~((u64) 0) : \ + (((((u64) 1) << (width))) - 1)) + +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 32 bits. Use + * EFX_MASK64 for higher width fields. + */ +#define EFX_MASK32(width) \ + ((width) == 32 ? ~((u32) 0) : \ + (((((u32) 1) << (width))) - 1)) + +/** A doubleword (4 byte) datatype - little-endian in HW */ +typedef union efx_dword { + __le32 u32[1]; +} efx_dword_t; + +/** A quadword (8 byte) datatype - little-endian in HW */ +typedef union efx_qword { + __le64 u64[1]; + __le32 u32[2]; + efx_dword_t dword[2]; +} efx_qword_t; + +/** An octword (eight-word, so 16 byte) datatype - little-endian in HW */ +typedef union efx_oword { + __le64 u64[2]; + efx_qword_t qword[2]; + __le32 u32[4]; + efx_dword_t dword[4]; +} efx_oword_t; + +/* Format string and value expanders for printk */ +#define EFX_DWORD_FMT "%08x" +#define EFX_QWORD_FMT "%08x:%08x" +#define EFX_OWORD_FMT "%08x:%08x:%08x:%08x" +#define EFX_DWORD_VAL(dword) \ + ((unsigned int) le32_to_cpu((dword).u32[0])) +#define EFX_QWORD_VAL(qword) \ + ((unsigned int) le32_to_cpu((qword).u32[1])), \ + ((unsigned int) le32_to_cpu((qword).u32[0])) +#define EFX_OWORD_VAL(oword) \ + ((unsigned int) le32_to_cpu((oword).u32[3])), \ + ((unsigned int) le32_to_cpu((oword).u32[2])), \ + ((unsigned int) le32_to_cpu((oword).u32[1])), \ + ((unsigned int) le32_to_cpu((oword).u32[0])) + +/* + * Extract bit field portion [low,high) from the native-endian element + * which contains bits [min,max). + * + * For example, suppose "element" represents the high 32 bits of a + * 64-bit value, and we wish to extract the bits belonging to the bit + * field occupying bits 28-45 of this 64-bit value. + * + * Then EFX_EXTRACT ( element, 32, 63, 28, 45 ) would give + * + * ( element ) << 4 + * + * The result will contain the relevant bits filled in in the range + * [0,high-low), with garbage in bits [high-low+1,...). + */ +#define EFX_EXTRACT_NATIVE(native_element, min, max, low, high) \ + ((low) > (max) || (high) < (min) ? 0 : \ + (low) > (min) ? \ + (native_element) >> ((low) - (min)) : \ + (native_element) << ((min) - (low))) + +/* + * Extract bit field portion [low,high) from the 64-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT64(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high) + +/* + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT32(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high) + +#define EFX_EXTRACT_OWORD64(oword, low, high) \ + ((EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ + EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) & \ + EFX_MASK64((high) + 1 - (low))) + +#define EFX_EXTRACT_QWORD64(qword, low, high) \ + (EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) & \ + EFX_MASK64((high) + 1 - (low))) + +#define EFX_EXTRACT_OWORD32(oword, low, high) \ + ((EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ + EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ + EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) & \ + EFX_MASK32((high) + 1 - (low))) + +#define EFX_EXTRACT_QWORD32(qword, low, high) \ + ((EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) & \ + EFX_MASK32((high) + 1 - (low))) + +#define EFX_EXTRACT_DWORD(dword, low, high) \ + (EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) & \ + EFX_MASK32((high) + 1 - (low))) + +#define EFX_OWORD_FIELD64(oword, field) \ + EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_QWORD_FIELD64(qword, field) \ + EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_OWORD_FIELD32(oword, field) \ + EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_QWORD_FIELD32(qword, field) \ + EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_DWORD_FIELD(dword, field) \ + EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_OWORD_IS_ZERO64(oword) \ + (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) + +#define EFX_QWORD_IS_ZERO64(qword) \ + (((qword).u64[0]) == (__force __le64) 0) + +#define EFX_OWORD_IS_ZERO32(oword) \ + (((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \ + == (__force __le32) 0) + +#define EFX_QWORD_IS_ZERO32(qword) \ + (((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0) + +#define EFX_DWORD_IS_ZERO(dword) \ + (((dword).u32[0]) == (__force __le32) 0) + +#define EFX_OWORD_IS_ALL_ONES64(oword) \ + (((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0)) + +#define EFX_QWORD_IS_ALL_ONES64(qword) \ + ((qword).u64[0] == ~((__force __le64) 0)) + +#define EFX_OWORD_IS_ALL_ONES32(oword) \ + (((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \ + == ~((__force __le32) 0)) + +#define EFX_QWORD_IS_ALL_ONES32(qword) \ + (((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0)) + +#define EFX_DWORD_IS_ALL_ONES(dword) \ + ((dword).u32[0] == ~((__force __le32) 0)) + +#if BITS_PER_LONG == 64 +#define EFX_OWORD_FIELD EFX_OWORD_FIELD64 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD64 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO64 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO64 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES64 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES64 +#else +#define EFX_OWORD_FIELD EFX_OWORD_FIELD32 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD32 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO32 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO32 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES32 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES32 +#endif + +/* + * Construct bit field portion + * + * Creates the portion of the bit field [low,high) that lies within + * the range [min,max). + */ +#define EFX_INSERT_NATIVE64(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u64) (value)) << (low - min)) : \ + (((u64) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE32(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u32) (value)) << (low - min)) : \ + (((u32) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE(min, max, low, high, value) \ + ((((max - min) >= 32) || ((high - low) >= 32)) ? \ + EFX_INSERT_NATIVE64(min, max, low, high, value) : \ + EFX_INSERT_NATIVE32(min, max, low, high, value)) + +/* + * Construct bit field portion + * + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define EFX_INSERT_FIELD_NATIVE(min, max, field, value) \ + EFX_INSERT_NATIVE(min, max, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +/* + * Construct bit field + * + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define EFX_INSERT_FIELDS_NATIVE(min, max, \ + field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7, \ + field8, value8, \ + field9, value9, \ + field10, value10) \ + (EFX_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field10, (value10))) + +#define EFX_INSERT_FIELDS64(...) \ + cpu_to_le64(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_INSERT_FIELDS32(...) \ + cpu_to_le32(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_POPULATE_OWORD64(oword, ...) do { \ + (oword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + (oword).u64[1] = EFX_INSERT_FIELDS64(64, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD64(qword, ...) do { \ + (qword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_OWORD32(oword, ...) do { \ + (oword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (oword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + (oword).u32[2] = EFX_INSERT_FIELDS32(64, 95, __VA_ARGS__); \ + (oword).u32[3] = EFX_INSERT_FIELDS32(96, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD32(qword, ...) do { \ + (qword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (qword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_DWORD(dword, ...) do { \ + (dword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + } while (0) + +#if BITS_PER_LONG == 64 +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD64 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD64 +#else +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD32 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD32 +#endif + +/* Populate an octword field with various numbers of arguments */ +#define EFX_POPULATE_OWORD_10 EFX_POPULATE_OWORD +#define EFX_POPULATE_OWORD_9(oword, ...) \ + EFX_POPULATE_OWORD_10(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_8(oword, ...) \ + EFX_POPULATE_OWORD_9(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_7(oword, ...) \ + EFX_POPULATE_OWORD_8(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_6(oword, ...) \ + EFX_POPULATE_OWORD_7(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_5(oword, ...) \ + EFX_POPULATE_OWORD_6(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_4(oword, ...) \ + EFX_POPULATE_OWORD_5(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_3(oword, ...) \ + EFX_POPULATE_OWORD_4(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_2(oword, ...) \ + EFX_POPULATE_OWORD_3(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_1(oword, ...) \ + EFX_POPULATE_OWORD_2(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_OWORD(oword) \ + EFX_POPULATE_OWORD_1(oword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_OWORD(oword) \ + EFX_POPULATE_OWORD_4(oword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff, \ + EFX_DWORD_2, 0xffffffff, \ + EFX_DWORD_3, 0xffffffff) + +/* Populate a quadword field with various numbers of arguments */ +#define EFX_POPULATE_QWORD_10 EFX_POPULATE_QWORD +#define EFX_POPULATE_QWORD_9(qword, ...) \ + EFX_POPULATE_QWORD_10(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_8(qword, ...) \ + EFX_POPULATE_QWORD_9(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_7(qword, ...) \ + EFX_POPULATE_QWORD_8(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_6(qword, ...) \ + EFX_POPULATE_QWORD_7(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_5(qword, ...) \ + EFX_POPULATE_QWORD_6(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_4(qword, ...) \ + EFX_POPULATE_QWORD_5(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_3(qword, ...) \ + EFX_POPULATE_QWORD_4(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_2(qword, ...) \ + EFX_POPULATE_QWORD_3(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_1(qword, ...) \ + EFX_POPULATE_QWORD_2(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_QWORD(qword) \ + EFX_POPULATE_QWORD_1(qword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_QWORD(qword) \ + EFX_POPULATE_QWORD_2(qword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff) + +/* Populate a dword field with various numbers of arguments */ +#define EFX_POPULATE_DWORD_10 EFX_POPULATE_DWORD +#define EFX_POPULATE_DWORD_9(dword, ...) \ + EFX_POPULATE_DWORD_10(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_8(dword, ...) \ + EFX_POPULATE_DWORD_9(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_7(dword, ...) \ + EFX_POPULATE_DWORD_8(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_6(dword, ...) \ + EFX_POPULATE_DWORD_7(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_5(dword, ...) \ + EFX_POPULATE_DWORD_6(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_4(dword, ...) \ + EFX_POPULATE_DWORD_5(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_3(dword, ...) \ + EFX_POPULATE_DWORD_4(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_2(dword, ...) \ + EFX_POPULATE_DWORD_3(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_1(dword, ...) \ + EFX_POPULATE_DWORD_2(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xffffffff) + +/* + * Modify a named field within an already-populated structure. Used + * for read-modify-write operations. + * + */ +#define EFX_INVERT_OWORD(oword) do { \ + (oword).u64[0] = ~((oword).u64[0]); \ + (oword).u64[1] = ~((oword).u64[1]); \ + } while (0) + +#define EFX_AND_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] & (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] & (mask).u64[1]; \ + } while (0) + +#define EFX_AND_QWORD(qword, from, mask) \ + (qword).u64[0] = (from).u64[0] & (mask).u64[0] + +#define EFX_OR_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] | (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] | (mask).u64[1]; \ + } while (0) + +#define EFX_INSERT64(min, max, low, high, value) \ + cpu_to_le64(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INSERT32(min, max, low, high, value) \ + cpu_to_le32(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INPLACE_MASK64(min, max, low, high) \ + EFX_INSERT64(min, max, low, high, EFX_MASK64((high) + 1 - (low))) + +#define EFX_INPLACE_MASK32(min, max, low, high) \ + EFX_INSERT32(min, max, low, high, EFX_MASK32((high) + 1 - (low))) + +#define EFX_SET_OWORD64(oword, low, high, value) do { \ + (oword).u64[0] = (((oword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ + (oword).u64[1] = (((oword).u64[1] \ + & ~EFX_INPLACE_MASK64(64, 127, low, high)) \ + | EFX_INSERT64(64, 127, low, high, value)); \ + } while (0) + +#define EFX_SET_QWORD64(qword, low, high, value) do { \ + (qword).u64[0] = (((qword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD32(oword, low, high, value) do { \ + (oword).u32[0] = (((oword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + (oword).u32[1] = (((oword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + (oword).u32[2] = (((oword).u32[2] \ + & ~EFX_INPLACE_MASK32(64, 95, low, high)) \ + | EFX_INSERT32(64, 95, low, high, value)); \ + (oword).u32[3] = (((oword).u32[3] \ + & ~EFX_INPLACE_MASK32(96, 127, low, high)) \ + | EFX_INSERT32(96, 127, low, high, value)); \ + } while (0) + +#define EFX_SET_QWORD32(qword, low, high, value) do { \ + (qword).u32[0] = (((qword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + (qword).u32[1] = (((qword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_DWORD32(dword, low, high, value) do { \ + (dword).u32[0] = (((dword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD_FIELD64(oword, field, value) \ + EFX_SET_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD64(qword, field, value) \ + EFX_SET_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_OWORD_FIELD32(oword, field, value) \ + EFX_SET_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD32(qword, field, value) \ + EFX_SET_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_DWORD_FIELD(dword, field, value) \ + EFX_SET_DWORD32(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + + + +#if BITS_PER_LONG == 64 +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64 +#else +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD32 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32 +#endif + +/* Used to avoid compiler warnings about shift range exceeding width + * of the data types when dma_addr_t is only 32 bits wide. + */ +#define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t)) +#define EFX_DMA_TYPE_WIDTH(width) \ + (((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH) + + +/* Static initialiser */ +#define EFX_OWORD32(a, b, c, d) \ + { .u32 = { cpu_to_le32(a), cpu_to_le32(b), \ + cpu_to_le32(c), cpu_to_le32(d) } } + +#endif /* EFX_BITFIELD_H */ diff --git a/src/drivers/net/sfc/efx_common.c b/src/drivers/net/sfc/efx_common.c new file mode 100644 index 000000000..79a994355 --- /dev/null +++ b/src/drivers/net/sfc/efx_common.c @@ -0,0 +1,97 @@ +/************************************************************************** + * + * Driver datapath common code for Solarflare network cards + * + * Written by Shradha Shah + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * Copyright 2006-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efx_common.h" +#include "efx_bitfield.h" +#include "mc_driver_pcol.h" + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/******************************************************************************* + * + * + * Low-level hardware access + * + * + ******************************************************************************/ + +void +efx_writel(struct efx_nic *efx, efx_dword_t *value, unsigned int reg) +{ + DBGCIO(efx, "Writing partial register %x with " EFX_DWORD_FMT "\n", + reg, EFX_DWORD_VAL(*value)); + _efx_writel(efx, value->u32[0], reg); +} + +void +efx_readl(struct efx_nic *efx, efx_dword_t *value, unsigned int reg) +{ + value->u32[0] = _efx_readl(efx, reg); + DBGCIO(efx, "Read from register %x, got " EFX_DWORD_FMT "\n", + reg, EFX_DWORD_VAL(*value)); +} + +/******************************************************************************* + * + * + * Inititialization and Close + * + * + ******************************************************************************/ +void efx_probe(struct net_device *netdev, enum efx_revision revision) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct pci_device *pci = container_of(netdev->dev, + struct pci_device, dev); + + efx->netdev = netdev; + efx->revision = revision; + + /* MMIO bar */ + efx->mmio_start = pci_bar_start(pci, PCI_BASE_ADDRESS_2); + efx->mmio_len = pci_bar_size(pci, PCI_BASE_ADDRESS_2); + efx->membase = ioremap(efx->mmio_start, efx->mmio_len); + + DBGCP(efx, "BAR of %lx bytes at phys %lx mapped at %p\n", + efx->mmio_len, efx->mmio_start, efx->membase); + + /* Enable PCI access */ + adjust_pci_device(pci); +} + +void efx_remove(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + + iounmap(efx->membase); + efx->membase = NULL; +} diff --git a/src/drivers/net/sfc/efx_common.h b/src/drivers/net/sfc/efx_common.h new file mode 100644 index 000000000..3487966ce --- /dev/null +++ b/src/drivers/net/sfc/efx_common.h @@ -0,0 +1,232 @@ +/************************************************************************** + * + * GPL common net driver for Solarflare network cards + * + * Written by Michael Brown + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * Copyright Solarflare Communications Inc. 2013-2017 + * + * 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. + * + * 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. + * + ***************************************************************************/ +#ifndef EFX_COMMON_H +#define EFX_COMMON_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define __packed __attribute__((__packed__)) +#define __force /*nothing*/ + +typedef uint16_t __le16; +typedef uint32_t __le32; +typedef uint64_t __le64; + +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct{int: -!!(e); })) +#define BUILD_BUG_ON(e) ((void)BUILD_BUG_ON_ZERO(e)) + +#include +#include +#include +#include "efx_bitfield.h" +#include "mcdi.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/************************************************************************** + * + * Hardware data structures and sizing + * + ***************************************************************************/ +typedef efx_qword_t efx_rx_desc_t; +typedef efx_qword_t efx_tx_desc_t; +typedef efx_qword_t efx_event_t; + +#define EFX_BUF_ALIGN 4096 +#define EFX_RXD_SIZE 512 +#define EFX_RXD_MASK (EFX_RXD_SIZE - 1) +#define EFX_TXD_SIZE 512 +#define EFX_TXD_MASK (EFX_TXD_SIZE - 1) +#define EFX_EVQ_SIZE 512 +#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) + +/* There is space for 512 rx descriptors available. This number can be + * anything between 1 and 512 in powers of 2. This value will affect the + * network performance. During a test we were able to push 239 descriptors + * before we ran out of space. + */ +#define EFX_NUM_RX_DESC 64 +#define EFX_NUM_RX_DESC_MASK (EFX_NUM_RX_DESC - 1) + +/* The packet size is usually 1500 bytes hence we choose 1600 as the buf size, + * which is (1500+metadata) + */ +#define EFX_RX_BUF_SIZE 1600 + +/* Settings for the state field in efx_nic. + */ +#define EFX_STATE_POLLING 1 + +typedef unsigned long long dma_addr_t; + +/** A buffer table allocation backing a tx dma, rx dma or eventq */ +struct efx_special_buffer { + dma_addr_t dma_addr; + int id; +}; + +/** A transmit queue */ +struct efx_tx_queue { + /* The hardware ring */ + efx_tx_desc_t *ring; + + /* The software ring storing io_buffers. */ + struct io_buffer *buf[EFX_TXD_SIZE]; + + /* The buffer table reservation pushed to hardware */ + struct efx_special_buffer entry; + + /* Software descriptor write ptr */ + unsigned int write_ptr; + + /* Hardware descriptor read ptr */ + unsigned int read_ptr; +}; + +/** A receive queue */ +struct efx_rx_queue { + /* The hardware ring */ + efx_rx_desc_t *ring; + + /* The software ring storing io_buffers */ + struct io_buffer *buf[EFX_NUM_RX_DESC]; + + /* The buffer table reservation pushed to hardware */ + struct efx_special_buffer entry; + + /* Descriptor write ptr, into both the hardware and software rings */ + unsigned int write_ptr; + + /* Hardware completion ptr */ + unsigned int read_ptr; + + /* The value of RX_CONT in the previous RX event */ + unsigned int rx_cont_prev; +}; + +/** An event queue */ +struct efx_ev_queue { + /* The hardware ring to push to hardware. + * Must be the first entry in the structure. + */ + efx_event_t *ring; + + /* The buffer table reservation pushed to hardware */ + struct efx_special_buffer entry; + + /* Pointers into the ring */ + unsigned int read_ptr; +}; + +/* Hardware revisions */ +enum efx_revision { + EFX_HUNTINGTON, +}; + +/** Hardware access */ +struct efx_nic { + struct net_device *netdev; + enum efx_revision revision; + const struct efx_nic_type *type; + + int port; + u32 state; + + /** Memory and IO base */ + void *membase; + unsigned long mmio_start; + unsigned long mmio_len; + + /* Buffer table allocation head */ + int buffer_head; + + /* Queues */ + struct efx_rx_queue rxq; + struct efx_tx_queue txq; + struct efx_ev_queue evq; + + unsigned int rx_prefix_size; + + /** INT_REG_KER */ + int int_en; + efx_oword_t int_ker __aligned; + + /* Set to true if firmware supports the workaround for bug35388 */ + bool workaround_35388; + +}; + + +/** Efx device type definition */ +struct efx_nic_type { + int (*mcdi_rpc)(struct efx_nic *efx, unsigned int cmd, + const efx_dword_t *inbuf, size_t inlen, + efx_dword_t *outbuf, size_t outlen, + size_t *outlen_actual, bool quiet); +}; + +extern const struct efx_nic_type hunt_nic_type; + +#define EFX_MAC_FRAME_LEN(_mtu) \ + (((_mtu) \ + + /* EtherII already included */ \ + + 4 /* FCS */ \ + /* No VLAN supported */ \ + + 16 /* bug16772 */ \ + + 7) & ~7) + +/******************************************************************************* + * + * + * Hardware API + * + * + ******************************************************************************/ +static inline void _efx_writel(struct efx_nic *efx, uint32_t value, + unsigned int reg) +{ + writel((value), (efx)->membase + (reg)); +} + +static inline uint32_t _efx_readl(struct efx_nic *efx, unsigned int reg) +{ + return readl((efx)->membase + (reg)); +} + +#define efx_writel_table(efx, value, index, reg) \ + efx_writel(efx, value, (reg) + ((index) * reg##_STEP)) + +#define efx_writel_page(efx, value, index, reg) \ + efx_writel(efx, value, (reg) + ((index) * 0x2000)) + +/* Hardware access */ +extern void efx_writel(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg); +extern void efx_readl(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg); + +/* Initialisation */ +extern void efx_probe(struct net_device *netdev, enum efx_revision rev); +extern void efx_remove(struct net_device *netdev); + +#endif /* EFX_COMMON_H */ diff --git a/src/drivers/net/sfc/efx_hunt.c b/src/drivers/net/sfc/efx_hunt.c new file mode 100644 index 000000000..07dd7dfea --- /dev/null +++ b/src/drivers/net/sfc/efx_hunt.c @@ -0,0 +1,510 @@ +/************************************************************************** + * + * Driver datapath for Solarflare network cards + * + * Written by Shradha Shah + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efx_hunt.h" +#include "efx_bitfield.h" +#include "ef10_regs.h" + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +void efx_hunt_free_special_buffer(void *buf, int bytes) +{ + free_dma(buf, bytes); +} + +static void *efx_hunt_alloc_special_buffer(int bytes, + struct efx_special_buffer *entry) +{ + void *buffer; + dma_addr_t dma_addr; + + /* Allocate the buffer, aligned on a buffer address boundary. This + * buffer will be passed into an MC_CMD_INIT_*Q command to setup the + * appropriate type of queue via MCDI. + */ + buffer = malloc_dma(bytes, EFX_BUF_ALIGN); + if (!buffer) + return NULL; + + entry->dma_addr = dma_addr = virt_to_bus(buffer); + assert((dma_addr & (EFX_BUF_ALIGN - 1)) == 0); + + /* Buffer table entries aren't allocated, so set id to zero */ + entry->id = 0; + DBGP("Allocated 0x%x bytes at %p\n", bytes, buffer); + + return buffer; +} + +/******************************************************************************* + * + * + * TX + * + * + ******************************************************************************/ +static void +efx_hunt_build_tx_desc(efx_tx_desc_t *txd, struct io_buffer *iob) +{ + dma_addr_t dma_addr; + + dma_addr = virt_to_bus(iob->data); + + EFX_POPULATE_QWORD_4(*txd, + ESF_DZ_TX_KER_TYPE, 0, + ESF_DZ_TX_KER_CONT, 0, + ESF_DZ_TX_KER_BYTE_CNT, iob_len(iob), + ESF_DZ_TX_KER_BUF_ADDR, dma_addr); +} + +static void +efx_hunt_notify_tx_desc(struct efx_nic *efx) +{ + struct efx_tx_queue *txq = &efx->txq; + int ptr = txq->write_ptr & EFX_TXD_MASK; + efx_dword_t reg; + + EFX_POPULATE_DWORD_1(reg, ERF_DZ_TX_DESC_WPTR_DWORD, ptr); + efx_writel_page(efx, ®, 0, ER_DZ_TX_DESC_UPD_DWORD); +} + +int +efx_hunt_transmit(struct net_device *netdev, struct io_buffer *iob) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_tx_queue *txq = &efx->txq; + int fill_level, space; + efx_tx_desc_t *txd; + int buf_id; + + fill_level = txq->write_ptr - txq->read_ptr; + space = EFX_TXD_SIZE - fill_level - 1; + if (space < 1) + return -ENOBUFS; + + /* Save the iobuffer for later completion */ + buf_id = txq->write_ptr & EFX_TXD_MASK; + assert(txq->buf[buf_id] == NULL); + txq->buf[buf_id] = iob; + + DBGCIO(efx, "tx_buf[%d] for iob %p data %p len %zd\n", + buf_id, iob, iob->data, iob_len(iob)); + + /* Form the descriptor, and push it to hardware */ + txd = txq->ring + buf_id; + efx_hunt_build_tx_desc(txd, iob); + ++txq->write_ptr; + efx_hunt_notify_tx_desc(efx); + + return 0; +} + +static void +efx_hunt_transmit_done(struct efx_nic *efx, int id) +{ + struct efx_tx_queue *txq = &efx->txq; + unsigned int read_ptr, stop; + + /* Complete all buffers from read_ptr up to and including id */ + read_ptr = txq->read_ptr & EFX_TXD_MASK; + stop = (id + 1) & EFX_TXD_MASK; + + while (read_ptr != stop) { + struct io_buffer *iob = txq->buf[read_ptr]; + + assert(iob); + /* Complete the tx buffer */ + if (iob) + netdev_tx_complete(efx->netdev, iob); + DBGCIO(efx, "tx_buf[%d] for iob %p done\n", read_ptr, iob); + txq->buf[read_ptr] = NULL; + + ++txq->read_ptr; + read_ptr = txq->read_ptr & EFX_TXD_MASK; + } +} + +int efx_hunt_tx_init(struct net_device *netdev, dma_addr_t *dma_addr) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_tx_queue *txq = &efx->txq; + size_t bytes; + + /* Allocate hardware transmit queue */ + bytes = sizeof(efx_tx_desc_t) * EFX_TXD_SIZE; + txq->ring = efx_hunt_alloc_special_buffer(bytes, &txq->entry); + if (!txq->ring) + return -ENOMEM; + + txq->read_ptr = txq->write_ptr = 0; + *dma_addr = txq->entry.dma_addr; + return 0; +} + +/******************************************************************************* + * + * + * RX + * + * + ******************************************************************************/ +static void +efx_hunt_build_rx_desc(efx_rx_desc_t *rxd, struct io_buffer *iob) +{ + dma_addr_t dma_addr = virt_to_bus(iob->data); + + EFX_POPULATE_QWORD_2(*rxd, + ESF_DZ_RX_KER_BYTE_CNT, EFX_RX_BUF_SIZE, + ESF_DZ_RX_KER_BUF_ADDR, dma_addr); +} + +static void +efx_hunt_notify_rx_desc(struct efx_nic *efx) +{ + struct efx_rx_queue *rxq = &efx->rxq; + int ptr = rxq->write_ptr & EFX_RXD_MASK; + efx_dword_t reg; + + EFX_POPULATE_DWORD_1(reg, ERF_DZ_RX_DESC_WPTR, ptr); + efx_writel_page(efx, ®, 0, ER_DZ_RX_DESC_UPD); +} + +static void +efx_hunt_rxq_fill(struct efx_nic *efx) +{ + struct efx_rx_queue *rxq = &efx->rxq; + int fill_level = rxq->write_ptr - rxq->read_ptr; + int space = EFX_NUM_RX_DESC - fill_level - 1; + int pushed = 0; + + while (space) { + int buf_id = rxq->write_ptr & (EFX_NUM_RX_DESC - 1); + int desc_id = rxq->write_ptr & EFX_RXD_MASK; + struct io_buffer *iob; + efx_rx_desc_t *rxd; + + assert(rxq->buf[buf_id] == NULL); + iob = alloc_iob(EFX_RX_BUF_SIZE); + if (!iob) + break; + + DBGCP(efx, "pushing rx_buf[%d] iob %p data %p\n", + buf_id, iob, iob->data); + + rxq->buf[buf_id] = iob; + rxd = rxq->ring + desc_id; + efx_hunt_build_rx_desc(rxd, iob); + ++rxq->write_ptr; + ++pushed; + --space; + } + + /* Push the ptr to hardware */ + if (pushed > 0) { + efx_hunt_notify_rx_desc(efx); + + DBGCP(efx, "pushed %d rx buffers to fill level %d\n", + pushed, rxq->write_ptr - rxq->read_ptr); + } +} + +static void +efx_hunt_receive(struct efx_nic *efx, unsigned int id, int len, int drop) +{ + struct efx_rx_queue *rxq = &efx->rxq; + unsigned int read_ptr = rxq->read_ptr & EFX_RXD_MASK; + unsigned int buf_ptr = rxq->read_ptr & EFX_NUM_RX_DESC_MASK; + struct io_buffer *iob; + + /* id is the lower 4 bits of the desc index + 1 in huntington*/ + /* hence anding with 15 */ + assert((id & 15) == ((read_ptr + (len != 0)) & 15)); + + /* Pop this rx buffer out of the software ring */ + iob = rxq->buf[buf_ptr]; + rxq->buf[buf_ptr] = NULL; + + DBGCIO(efx, "popping rx_buf[%d] iob %p data %p with %d bytes %s %x\n", + read_ptr, iob, iob->data, len, drop ? "bad" : "ok", drop); + + /* Pass the packet up if required */ + if (drop) + netdev_rx_err(efx->netdev, iob, EBADMSG); + else { + iob_put(iob, len); + iob_pull(iob, efx->rx_prefix_size); + netdev_rx(efx->netdev, iob); + } + + ++rxq->read_ptr; +} + +int efx_hunt_rx_init(struct net_device *netdev, dma_addr_t *dma_addr) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_rx_queue *rxq = &efx->rxq; + size_t bytes; + + /* Allocate hardware receive queue */ + bytes = sizeof(efx_rx_desc_t) * EFX_RXD_SIZE; + rxq->ring = efx_hunt_alloc_special_buffer(bytes, &rxq->entry); + if (rxq->ring == NULL) + return -ENOMEM; + + rxq->read_ptr = rxq->write_ptr = 0; + *dma_addr = rxq->entry.dma_addr; + return 0; +} + +/******************************************************************************* + * + * + * Event queues and interrupts + * + * + ******************************************************************************/ +int efx_hunt_ev_init(struct net_device *netdev, dma_addr_t *dma_addr) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_ev_queue *evq = &efx->evq; + size_t bytes; + + /* Allocate the hardware event queue */ + bytes = sizeof(efx_event_t) * EFX_EVQ_SIZE; + evq->ring = efx_hunt_alloc_special_buffer(bytes, &evq->entry); + if (evq->ring == NULL) + return -ENOMEM; + + memset(evq->ring, 0xff, bytes); + evq->read_ptr = 0; + *dma_addr = evq->entry.dma_addr; + return 0; +} + +static void +efx_hunt_clear_interrupts(struct efx_nic *efx) +{ + efx_dword_t reg; + /* read the ISR */ + efx_readl(efx, ®, ER_DZ_BIU_INT_ISR); +} + +/** + * See if an event is present + * + * @v event EFX event structure + * @ret True An event is pending + * @ret False No event is pending + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int +efx_hunt_event_present(efx_event_t *event) +{ + return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | + EFX_DWORD_IS_ALL_ONES(event->dword[1]))); +} + +static void +efx_hunt_evq_read_ack(struct efx_nic *efx) +{ + struct efx_ev_queue *evq = &efx->evq; + efx_dword_t reg; + + if (efx->workaround_35388) { + EFX_POPULATE_DWORD_2(reg, ERF_DD_EVQ_IND_RPTR_FLAGS, + EFE_DD_EVQ_IND_RPTR_FLAGS_HIGH, + ERF_DD_EVQ_IND_RPTR, + evq->read_ptr >> ERF_DD_EVQ_IND_RPTR_WIDTH); + efx_writel_page(efx, ®, 0, ER_DD_EVQ_INDIRECT); + EFX_POPULATE_DWORD_2(reg, ERF_DD_EVQ_IND_RPTR_FLAGS, + EFE_DD_EVQ_IND_RPTR_FLAGS_LOW, + ERF_DD_EVQ_IND_RPTR, evq->read_ptr & + ((1 << ERF_DD_EVQ_IND_RPTR_WIDTH) - 1)); + efx_writel_page(efx, ®, 0, ER_DD_EVQ_INDIRECT); + } else { + EFX_POPULATE_DWORD_1(reg, ERF_DZ_EVQ_RPTR, evq->read_ptr); + efx_writel_table(efx, ®, 0, ER_DZ_EVQ_RPTR); + } +} + +static unsigned int +efx_hunt_handle_event(struct efx_nic *efx, efx_event_t *evt) +{ + struct efx_rx_queue *rxq = &efx->rxq; + int ev_code, desc_ptr, len; + int next_ptr_lbits, packet_drop; + int rx_cont; + + /* Decode event */ + ev_code = EFX_QWORD_FIELD(*evt, ESF_DZ_EV_CODE); + + switch (ev_code) { + case ESE_DZ_EV_CODE_TX_EV: + desc_ptr = EFX_QWORD_FIELD(*evt, ESF_DZ_TX_DESCR_INDX); + efx_hunt_transmit_done(efx, desc_ptr); + break; + + case ESE_DZ_EV_CODE_RX_EV: + len = EFX_QWORD_FIELD(*evt, ESF_DZ_RX_BYTES); + next_ptr_lbits = EFX_QWORD_FIELD(*evt, ESF_DZ_RX_DSC_PTR_LBITS); + rx_cont = EFX_QWORD_FIELD(*evt, ESF_DZ_RX_CONT); + + /* We don't expect to receive scattered packets, so drop the + * packet if RX_CONT is set on the current or previous event, or + * if len is zero. + */ + packet_drop = (len == 0) | (rx_cont << 1) | + (rxq->rx_cont_prev << 2); + efx_hunt_receive(efx, next_ptr_lbits, len, packet_drop); + rxq->rx_cont_prev = rx_cont; + return 1; + + default: + DBGCP(efx, "Unknown event type %d\n", ev_code); + break; + } + return 0; +} + +void efx_hunt_poll(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_ev_queue *evq = &efx->evq; + efx_event_t *evt; + int budget = 10; + + /* Read the event queue by directly looking for events + * (we don't even bother to read the eventq write ptr) + */ + evt = evq->ring + evq->read_ptr; + while (efx_hunt_event_present(evt) && (budget > 0)) { + DBGCP(efx, "Event at index 0x%x address %p is " + EFX_QWORD_FMT "\n", evq->read_ptr, + evt, EFX_QWORD_VAL(*evt)); + + budget -= efx_hunt_handle_event(efx, evt); + + /* Clear the event */ + EFX_SET_QWORD(*evt); + + /* Move to the next event. We don't ack the event + * queue until the end + */ + evq->read_ptr = ((evq->read_ptr + 1) & EFX_EVQ_MASK); + evt = evq->ring + evq->read_ptr; + } + + /* Push more rx buffers if needed */ + efx_hunt_rxq_fill(efx); + + /* Clear any pending interrupts */ + efx_hunt_clear_interrupts(efx); + + /* Ack the event queue if interrupts are enabled */ + if (efx->int_en) + efx_hunt_evq_read_ack(efx); +} + +void efx_hunt_irq(struct net_device *netdev, int enable) +{ + struct efx_nic *efx = netdev_priv(netdev); + + efx->int_en = enable; + + /* If interrupts are enabled, prime the event queue. Otherwise ack any + * pending interrupts + */ + if (enable) + efx_hunt_evq_read_ack(efx); + else if (efx->netdev->state & NETDEV_OPEN) + efx_hunt_clear_interrupts(efx); +} + +/******************************************************************************* + * + * + * Initialization and Close + * + * + ******************************************************************************/ +int efx_hunt_open(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + efx_dword_t cmd; + + /* Set interrupt moderation to 0*/ + EFX_POPULATE_DWORD_2(cmd, + ERF_DZ_TC_TIMER_MODE, 0, + ERF_DZ_TC_TIMER_VAL, 0); + efx_writel_page(efx, &cmd, 0, ER_DZ_EVQ_TMR); + + /* Ack the eventq */ + if (efx->int_en) + efx_hunt_evq_read_ack(efx); + + /* Push receive buffers */ + efx_hunt_rxq_fill(efx); + + return 0; +} + +void efx_hunt_close(struct net_device *netdev) +{ + struct efx_nic *efx = netdev_priv(netdev); + struct efx_rx_queue *rxq = &efx->rxq; + struct efx_tx_queue *txq = &efx->txq; + int i; + + /* Complete outstanding descriptors */ + for (i = 0; i < EFX_NUM_RX_DESC; i++) { + if (rxq->buf[i]) { + free_iob(rxq->buf[i]); + rxq->buf[i] = NULL; + } + } + + for (i = 0; i < EFX_TXD_SIZE; i++) { + if (txq->buf[i]) { + netdev_tx_complete(efx->netdev, txq->buf[i]); + txq->buf[i] = NULL; + } + } + + /* Clear interrupts */ + efx_hunt_clear_interrupts(efx); +} diff --git a/src/drivers/net/sfc/efx_hunt.h b/src/drivers/net/sfc/efx_hunt.h new file mode 100644 index 000000000..b8377bf20 --- /dev/null +++ b/src/drivers/net/sfc/efx_hunt.h @@ -0,0 +1,75 @@ +/************************************************************************** + * + * GPL net driver for Solarflare network cards + * + * Written by Shradha Shah + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ + +#ifndef EFX_HUNT_H +#define EFX_HUNT_H + +#include "efx_common.h" + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/************************************************************************** + * + * Hardware data structures and sizing + * + ***************************************************************************/ + +#define EFX_EV_SIZE(_nevs) ((_nevs) * sizeof(efx_qword_t)) +#define EFX_EVQ_NBUFS(_nevs) (EFX_EV_SIZE(_nevs) / EFX_BUF_ALIGN) + +#define EFX_RXQ_SIZE(_ndescs) ((_ndescs) * sizeof(efx_qword_t)) +#define EFX_RXQ_NBUFS(_ndescs) (EFX_RXQ_SIZE(_ndescs) / EFX_BUF_ALIGN) + +#define EFX_TXQ_SIZE(_ndescs) ((_ndescs) * sizeof(efx_qword_t)) +#define EFX_TXQ_NBUFS(_ndescs) (EFX_TXQ_SIZE(_ndescs) / EFX_BUF_ALIGN) + +/** MCDI request structure */ +struct efx_mcdi_req_s { + unsigned int emr_cmd; + efx_dword_t *emr_in_buf; + size_t emr_in_length; + int emr_rc; + efx_dword_t *emr_out_buf; + size_t emr_out_length; + size_t emr_out_length_used; +}; + +/******************************************************************************* + * + * + * Hardware API + * + * + ******************************************************************************/ + +extern void efx_hunt_free_special_buffer(void *buf, int bytes); + +/* Data path entry points */ +extern int efx_hunt_transmit(struct net_device *netdev, struct io_buffer *iob); +extern void efx_hunt_poll(struct net_device *netdev); +extern void efx_hunt_irq(struct net_device *netdev, int enable); + +/* Initialisation */ +extern int efx_hunt_ev_init(struct net_device *netdev, dma_addr_t *dma_addr); +extern int efx_hunt_rx_init(struct net_device *netdev, dma_addr_t *dma_addr); +extern int efx_hunt_tx_init(struct net_device *netdev, dma_addr_t *dma_addr); +extern int efx_hunt_open(struct net_device *netdev); +extern void efx_hunt_close(struct net_device *netdev); + +#endif /* EFX_HUNT_H */ diff --git a/src/drivers/net/sfc/mc_driver_pcol.h b/src/drivers/net/sfc/mc_driver_pcol.h new file mode 100644 index 000000000..e1174bd77 --- /dev/null +++ b/src/drivers/net/sfc/mc_driver_pcol.h @@ -0,0 +1,2281 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ +#ifndef SFC_MCDI_PCOL_H +#define SFC_MCDI_PCOL_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** \file mc_driver_pcol.h + * This file is a subset of the MCDI headers generated from the yml files. + */ + +/* The current version of the MCDI protocol. + * + * Note that the ROM burnt into the card only talks V0, so at the very + * least every driver must support version 0 and MCDI_PCOL_VERSION + */ +#ifdef WITH_MCDI_V2 +#define MCDI_PCOL_VERSION 2 +#else +#define MCDI_PCOL_VERSION 1 +#endif + +/* Unused commands: 0x23, 0x27, 0x30, 0x31 */ + +/* MCDI version 1 + * + * Each MCDI request starts with an MCDI_HEADER, which is a 32bit + * structure, filled in by the client. + * + * 0 7 8 16 20 22 23 24 31 + * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS | + * | | | + * | | \--- Response + * | \------- Error + * \------------------------------ Resync (always set) + * + * The client writes it's request into MC shared memory, and rings the + * doorbell. Each request is completed by either by the MC writing + * back into shared memory, or by writing out an event. + * + * All MCDI commands support completion by shared memory response. Each + * request may also contain additional data (accounted for by HEADER.LEN), + * and some response's may also contain additional data (again, accounted + * for by HEADER.LEN). + * + * Some MCDI commands support completion by event, in which any associated + * response data is included in the event. + * + * The protocol requires one response to be delivered for every request, a + * request should not be sent unless the response for the previous request + * has been received (either by polling shared memory, or by receiving + * an event). + */ + +/** Request/Response structure */ +#define MCDI_HEADER_OFST 0 +#define MCDI_HEADER_CODE_LBN 0 +#define MCDI_HEADER_CODE_WIDTH 7 +#define MCDI_HEADER_RESYNC_LBN 7 +#define MCDI_HEADER_RESYNC_WIDTH 1 +#define MCDI_HEADER_DATALEN_LBN 8 +#define MCDI_HEADER_DATALEN_WIDTH 8 +#define MCDI_HEADER_SEQ_LBN 16 +#define MCDI_HEADER_SEQ_WIDTH 4 +#define MCDI_HEADER_RSVD_LBN 20 +#define MCDI_HEADER_RSVD_WIDTH 1 +#define MCDI_HEADER_NOT_EPOCH_LBN 21 +#define MCDI_HEADER_NOT_EPOCH_WIDTH 1 +#define MCDI_HEADER_ERROR_LBN 22 +#define MCDI_HEADER_ERROR_WIDTH 1 +#define MCDI_HEADER_RESPONSE_LBN 23 +#define MCDI_HEADER_RESPONSE_WIDTH 1 +#define MCDI_HEADER_XFLAGS_LBN 24 +#define MCDI_HEADER_XFLAGS_WIDTH 8 +/* Request response using event */ +#define MCDI_HEADER_XFLAGS_EVREQ 0x01 +/* Request (and signal) early doorbell return */ +#define MCDI_HEADER_XFLAGS_DBRET 0x02 + +/* Maximum number of payload bytes */ +#define MCDI_CTL_SDU_LEN_MAX_V1 0xfc +#define MCDI_CTL_SDU_LEN_MAX_V2 0x400 + +#ifdef WITH_MCDI_V2 +#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V2 +#else +#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V1 +#endif + + +/* The MC can generate events for two reasons: + * - To advance a shared memory request if XFLAGS_EVREQ was set + * - As a notification (link state, i2c event), controlled + * via MC_CMD_LOG_CTRL + * + * Both events share a common structure: + * + * 0 32 33 36 44 52 60 + * | Data | Cont | Level | Src | Code | Rsvd | + * | + * \ There is another event pending in this notification + * + * If Code==CMDDONE, then the fields are further interpreted as: + * + * - LEVEL==INFO Command succeeded + * - LEVEL==ERR Command failed + * + * 0 8 16 24 32 + * | Seq | Datalen | Errno | Rsvd | + * + * These fields are taken directly out of the standard MCDI header, i.e., + * LEVEL==ERR, Datalen == 0 => Reboot + * + * Events can be squirted out of the UART (using LOG_CTRL) without a + * MCDI header. An event can be distinguished from a MCDI response by + * examining the first byte which is 0xc0. This corresponds to the + * non-existent MCDI command MC_CMD_DEBUG_LOG. + * + * 0 7 8 + * | command | Resync | = 0xc0 + * + * Since the event is written in big-endian byte order, this works + * providing bits 56-63 of the event are 0xc0. + * + * 56 60 63 + * | Rsvd | Code | = 0xc0 + * + * Which means for convenience the event code is 0xc for all MC + * generated events. + */ +#define FSE_AZ_EV_CODE_MCDI_EVRESPONSE 0xc + + +/* Operation not permitted. */ +#define MC_CMD_ERR_EPERM 1 +/* Non-existent command target */ +#define MC_CMD_ERR_ENOENT 2 +/* assert() has killed the MC */ +#define MC_CMD_ERR_EINTR 4 +/* I/O failure */ +#define MC_CMD_ERR_EIO 5 +/* Already exists */ +#define MC_CMD_ERR_EEXIST 6 +/* Try again */ +#define MC_CMD_ERR_EAGAIN 11 +/* Out of memory */ +#define MC_CMD_ERR_ENOMEM 12 +/* Caller does not hold required locks */ +#define MC_CMD_ERR_EACCES 13 +/* Resource is currently unavailable (e.g. lock contention) */ +#define MC_CMD_ERR_EBUSY 16 +/* No such device */ +#define MC_CMD_ERR_ENODEV 19 +/* Invalid argument to target */ +#define MC_CMD_ERR_EINVAL 22 +/* Broken pipe */ +#define MC_CMD_ERR_EPIPE 32 +/* Read-only */ +#define MC_CMD_ERR_EROFS 30 +/* Out of range */ +#define MC_CMD_ERR_ERANGE 34 +/* Non-recursive resource is already acquired */ +#define MC_CMD_ERR_EDEADLK 35 +/* Operation not implemented */ +#define MC_CMD_ERR_ENOSYS 38 +/* Operation timed out */ +#define MC_CMD_ERR_ETIME 62 +/* Link has been severed */ +#define MC_CMD_ERR_ENOLINK 67 +/* Protocol error */ +#define MC_CMD_ERR_EPROTO 71 +/* Operation not supported */ +#define MC_CMD_ERR_ENOTSUP 95 +/* Address not available */ +#define MC_CMD_ERR_EADDRNOTAVAIL 99 +/* Not connected */ +#define MC_CMD_ERR_ENOTCONN 107 +/* Operation already in progress */ +#define MC_CMD_ERR_EALREADY 114 + +/* Resource allocation failed. */ +#define MC_CMD_ERR_ALLOC_FAIL 0x1000 +/* V-adaptor not found. */ +#define MC_CMD_ERR_NO_VADAPTOR 0x1001 +/* EVB port not found. */ +#define MC_CMD_ERR_NO_EVB_PORT 0x1002 +/* V-switch not found. */ +#define MC_CMD_ERR_NO_VSWITCH 0x1003 +/* Too many VLAN tags. */ +#define MC_CMD_ERR_VLAN_LIMIT 0x1004 +/* Bad PCI function number. */ +#define MC_CMD_ERR_BAD_PCI_FUNC 0x1005 +/* Invalid VLAN mode. */ +#define MC_CMD_ERR_BAD_VLAN_MODE 0x1006 +/* Invalid v-switch type. */ +#define MC_CMD_ERR_BAD_VSWITCH_TYPE 0x1007 +/* Invalid v-port type. */ +#define MC_CMD_ERR_BAD_VPORT_TYPE 0x1008 +/* MAC address exists. */ +#define MC_CMD_ERR_MAC_EXIST 0x1009 +/* Slave core not present */ +#define MC_CMD_ERR_SLAVE_NOT_PRESENT 0x100a +/* The datapath is disabled. */ +#define MC_CMD_ERR_DATAPATH_DISABLED 0x100b +/* The requesting client is not a function */ +#define MC_CMD_ERR_CLIENT_NOT_FN 0x100c +/* The requested operation might require the + * command to be passed between MCs, and the + * transport doesn't support that. Should + * only ever been seen over the UART. + */ +#define MC_CMD_ERR_TRANSPORT_NOPROXY 0x100d +/* VLAN tag(s) exists */ +#define MC_CMD_ERR_VLAN_EXIST 0x100e +/* No MAC address assigned to an EVB port */ +#define MC_CMD_ERR_NO_MAC_ADDR 0x100f +/* Notifies the driver that the request has been relayed + * to an admin function for authorization. The driver should + * wait for a PROXY_RESPONSE event and then resend its request. + * This error code is followed by a 32-bit handle that + * helps matching it with the respective PROXY_RESPONSE event. + */ +#define MC_CMD_ERR_PROXY_PENDING 0x1010 +#define MC_CMD_ERR_PROXY_PENDING_HANDLE_OFST 4 +/* The request cannot be passed for authorization because + * another request from the same function is currently being + * authorized. The drvier should try again later. + */ +#define MC_CMD_ERR_PROXY_INPROGRESS 0x1011 +/* Returned by MC_CMD_PROXY_COMPLETE if the caller is not the function + * that has enabled proxying or BLOCK_INDEX points to a function that + * doesn't await an authorization. + */ +#define MC_CMD_ERR_PROXY_UNEXPECTED 0x1012 +/* This code is currently only used internally in FW. Its meaning is that + * an operation failed due to lack of SR-IOV privilege. + * Normally it is translated to EPERM by send_cmd_err(), + * but it may also be used to trigger some special mechanism + * for handling such case, e.g. to relay the failed request + * to a designated admin function for authorization. + */ +#define MC_CMD_ERR_NO_PRIVILEGE 0x1013 +/* Workaround 26807 could not be turned on/off because some functions + * have already installed filters. See the comment at + * MC_CMD_WORKAROUND_BUG26807. + */ +#define MC_CMD_ERR_FILTERS_PRESENT 0x1014 +/* The clock whose frequency you've attempted to set set + * doesn't exist on this NIC + */ +#define MC_CMD_ERR_NO_CLOCK 0x1015 +/* Returned by MC_CMD_TESTASSERT if the action that should + * have caused an assertion failed to do so. + */ +#define MC_CMD_ERR_UNREACHABLE 0x1016 +/* This command needs to be processed in the background but there were no + * resources to do so. Send it again after a command has completed. + */ +#define MC_CMD_ERR_QUEUE_FULL 0x1017 + +#define MC_CMD_ERR_CODE_OFST 0 + + +#ifdef WITH_MCDI_V2 + +/* Version 2 adds an optional argument to error returns: the errno value + * may be followed by the (0-based) number of the first argument that + * could not be processed. + */ +#define MC_CMD_ERR_ARG_OFST 4 + +/* No space */ +#define MC_CMD_ERR_ENOSPC 28 + +#endif + +/* MCDI_EVENT structuredef */ +#define MCDI_EVENT_LEN 8 +#define MCDI_EVENT_CONT_LBN 32 +#define MCDI_EVENT_CONT_WIDTH 1 +#define MCDI_EVENT_LEVEL_LBN 33 +#define MCDI_EVENT_LEVEL_WIDTH 3 +/* enum: Info. */ +#define MCDI_EVENT_LEVEL_INFO 0x0 +/* enum: Warning. */ +#define MCDI_EVENT_LEVEL_WARN 0x1 +/* enum: Error. */ +#define MCDI_EVENT_LEVEL_ERR 0x2 +/* enum: Fatal. */ +#define MCDI_EVENT_LEVEL_FATAL 0x3 +#define MCDI_EVENT_DATA_OFST 0 +#define MCDI_EVENT_CMDDONE_SEQ_LBN 0 +#define MCDI_EVENT_CMDDONE_SEQ_WIDTH 8 +#define MCDI_EVENT_CMDDONE_DATALEN_LBN 8 +#define MCDI_EVENT_CMDDONE_DATALEN_WIDTH 8 +#define MCDI_EVENT_CMDDONE_ERRNO_LBN 16 +#define MCDI_EVENT_CMDDONE_ERRNO_WIDTH 8 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_LBN 0 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_WIDTH 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4 +/* enum: 100Mbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_100M 0x1 +/* enum: 1Gbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_1G 0x2 +/* enum: 10Gbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_10G 0x3 +/* enum: 40Gbs */ +#define MCDI_EVENT_LINKCHANGE_SPEED_40G 0x4 +#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20 +#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_MONITOR_LBN 0 +#define MCDI_EVENT_SENSOREVT_MONITOR_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_STATE_LBN 8 +#define MCDI_EVENT_SENSOREVT_STATE_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_VALUE_LBN 16 +#define MCDI_EVENT_SENSOREVT_VALUE_WIDTH 16 +#define MCDI_EVENT_FWALERT_DATA_LBN 8 +#define MCDI_EVENT_FWALERT_DATA_WIDTH 24 +#define MCDI_EVENT_FWALERT_REASON_LBN 0 +#define MCDI_EVENT_FWALERT_REASON_WIDTH 8 +/* enum: SRAM Access. */ +#define MCDI_EVENT_FWALERT_REASON_SRAM_ACCESS 0x1 +#define MCDI_EVENT_FLR_VF_LBN 0 +#define MCDI_EVENT_FLR_VF_WIDTH 8 +#define MCDI_EVENT_TX_ERR_TXQ_LBN 0 +#define MCDI_EVENT_TX_ERR_TXQ_WIDTH 12 +#define MCDI_EVENT_TX_ERR_TYPE_LBN 12 +#define MCDI_EVENT_TX_ERR_TYPE_WIDTH 4 +/* enum: Descriptor loader reported failure */ +#define MCDI_EVENT_TX_ERR_DL_FAIL 0x1 +/* enum: Descriptor ring empty and no EOP seen for packet */ +#define MCDI_EVENT_TX_ERR_NO_EOP 0x2 +/* enum: Overlength packet */ +#define MCDI_EVENT_TX_ERR_2BIG 0x3 +/* enum: Malformed option descriptor */ +#define MCDI_EVENT_TX_BAD_OPTDESC 0x5 +/* enum: Option descriptor part way through a packet */ +#define MCDI_EVENT_TX_OPT_IN_PKT 0x8 +/* enum: DMA or PIO data access error */ +#define MCDI_EVENT_TX_ERR_BAD_DMA_OR_PIO 0x9 +#define MCDI_EVENT_TX_ERR_INFO_LBN 16 +#define MCDI_EVENT_TX_ERR_INFO_WIDTH 16 +#define MCDI_EVENT_TX_FLUSH_TO_DRIVER_LBN 12 +#define MCDI_EVENT_TX_FLUSH_TO_DRIVER_WIDTH 1 +#define MCDI_EVENT_TX_FLUSH_TXQ_LBN 0 +#define MCDI_EVENT_TX_FLUSH_TXQ_WIDTH 12 +#define MCDI_EVENT_PTP_ERR_TYPE_LBN 0 +#define MCDI_EVENT_PTP_ERR_TYPE_WIDTH 8 +/* enum: PLL lost lock */ +#define MCDI_EVENT_PTP_ERR_PLL_LOST 0x1 +/* enum: Filter overflow (PDMA) */ +#define MCDI_EVENT_PTP_ERR_FILTER 0x2 +/* enum: FIFO overflow (FPGA) */ +#define MCDI_EVENT_PTP_ERR_FIFO 0x3 +/* enum: Merge queue overflow */ +#define MCDI_EVENT_PTP_ERR_QUEUE 0x4 +#define MCDI_EVENT_AOE_ERR_TYPE_LBN 0 +#define MCDI_EVENT_AOE_ERR_TYPE_WIDTH 8 +/* enum: AOE failed to load - no valid image? */ +#define MCDI_EVENT_AOE_NO_LOAD 0x1 +/* enum: AOE FC reported an exception */ +#define MCDI_EVENT_AOE_FC_ASSERT 0x2 +/* enum: AOE FC watchdogged */ +#define MCDI_EVENT_AOE_FC_WATCHDOG 0x3 +/* enum: AOE FC failed to start */ +#define MCDI_EVENT_AOE_FC_NO_START 0x4 +/* enum: Generic AOE fault - likely to have been reported via other means too + * but intended for use by aoex driver. + */ +#define MCDI_EVENT_AOE_FAULT 0x5 +/* enum: Results of reprogramming the CPLD (status in AOE_ERR_DATA) */ +#define MCDI_EVENT_AOE_CPLD_REPROGRAMMED 0x6 +/* enum: AOE loaded successfully */ +#define MCDI_EVENT_AOE_LOAD 0x7 +/* enum: AOE DMA operation completed (LSB of HOST_HANDLE in AOE_ERR_DATA) */ +#define MCDI_EVENT_AOE_DMA 0x8 +/* enum: AOE byteblaster connected/disconnected (Connection status in + * AOE_ERR_DATA) + */ +#define MCDI_EVENT_AOE_BYTEBLASTER 0x9 +/* enum: DDR ECC status update */ +#define MCDI_EVENT_AOE_DDR_ECC_STATUS 0xa +/* enum: PTP status update */ +#define MCDI_EVENT_AOE_PTP_STATUS 0xb +/* enum: FPGA header incorrect */ +#define MCDI_EVENT_AOE_FPGA_LOAD_HEADER_ERR 0xc +/* enum: FPGA Powered Off due to error in powering up FPGA */ +#define MCDI_EVENT_AOE_FPGA_POWER_OFF 0xd +/* enum: AOE FPGA load failed due to MC to MUM communication failure */ +#define MCDI_EVENT_AOE_FPGA_LOAD_FAILED 0xe +/* enum: Notify that invalid flash type detected */ +#define MCDI_EVENT_AOE_INVALID_FPGA_FLASH_TYPE 0xf +/* enum: Notify that the attempt to run FPGA Controller firmware timedout */ +#define MCDI_EVENT_AOE_FC_RUN_TIMEDOUT 0x10 +/* enum: Failure to probe one or more FPGA boot flash chips */ +#define MCDI_EVENT_AOE_FPGA_BOOT_FLASH_INVALID 0x11 +/* enum: FPGA boot-flash contains an invalid image header */ +#define MCDI_EVENT_AOE_FPGA_BOOT_FLASH_HDR_INVALID 0x12 +/* enum: Failed to program clocks required by the FPGA */ +#define MCDI_EVENT_AOE_FPGA_CLOCKS_PROGRAM_FAILED 0x13 +/* enum: Notify that FPGA Controller is alive to serve MCDI requests */ +#define MCDI_EVENT_AOE_FC_RUNNING 0x14 +#define MCDI_EVENT_AOE_ERR_DATA_LBN 8 +#define MCDI_EVENT_AOE_ERR_DATA_WIDTH 8 +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_INFO_LBN 8 +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_INFO_WIDTH 8 +/* enum: FC Assert happened, but the register information is not available */ +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_SEEN 0x0 +/* enum: The register information for FC Assert is ready for readinng by driver + */ +#define MCDI_EVENT_AOE_ERR_FC_ASSERT_DATA_READY 0x1 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_HEADER_VERIFY_FAILED_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_HEADER_VERIFY_FAILED_WIDTH 8 +/* enum: Reading from NV failed */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_NV_READ_FAIL 0x0 +/* enum: Invalid Magic Number if FPGA header */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_MAGIC_FAIL 0x1 +/* enum: Invalid Silicon type detected in header */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_SILICON_TYPE 0x2 +/* enum: Unsupported VRatio */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_VRATIO 0x3 +/* enum: Unsupported DDR Type */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_TYPE 0x4 +/* enum: DDR Voltage out of supported range */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_VOLTAGE 0x5 +/* enum: Unsupported DDR speed */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_SPEED 0x6 +/* enum: Unsupported DDR size */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_SIZE 0x7 +/* enum: Unsupported DDR rank */ +#define MCDI_EVENT_AOE_ERR_FPGA_HEADER_DDR_RANK 0x8 +#define MCDI_EVENT_AOE_ERR_CODE_INVALID_FPGA_FLASH_TYPE_INFO_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_INVALID_FPGA_FLASH_TYPE_INFO_WIDTH 8 +/* enum: Primary boot flash */ +#define MCDI_EVENT_AOE_FLASH_TYPE_BOOT_PRIMARY 0x0 +/* enum: Secondary boot flash */ +#define MCDI_EVENT_AOE_FLASH_TYPE_BOOT_SECONDARY 0x1 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_POWER_OFF_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_POWER_OFF_WIDTH 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_LOAD_FAILED_LBN 8 +#define MCDI_EVENT_AOE_ERR_CODE_FPGA_LOAD_FAILED_WIDTH 8 +#define MCDI_EVENT_RX_ERR_RXQ_LBN 0 +#define MCDI_EVENT_RX_ERR_RXQ_WIDTH 12 +#define MCDI_EVENT_RX_ERR_TYPE_LBN 12 +#define MCDI_EVENT_RX_ERR_TYPE_WIDTH 4 +#define MCDI_EVENT_RX_ERR_INFO_LBN 16 +#define MCDI_EVENT_RX_ERR_INFO_WIDTH 16 +#define MCDI_EVENT_RX_FLUSH_TO_DRIVER_LBN 12 +#define MCDI_EVENT_RX_FLUSH_TO_DRIVER_WIDTH 1 +#define MCDI_EVENT_RX_FLUSH_RXQ_LBN 0 +#define MCDI_EVENT_RX_FLUSH_RXQ_WIDTH 12 +#define MCDI_EVENT_MC_REBOOT_COUNT_LBN 0 +#define MCDI_EVENT_MC_REBOOT_COUNT_WIDTH 16 +#define MCDI_EVENT_MUM_ERR_TYPE_LBN 0 +#define MCDI_EVENT_MUM_ERR_TYPE_WIDTH 8 +/* enum: MUM failed to load - no valid image? */ +#define MCDI_EVENT_MUM_NO_LOAD 0x1 +/* enum: MUM f/w reported an exception */ +#define MCDI_EVENT_MUM_ASSERT 0x2 +/* enum: MUM not kicking watchdog */ +#define MCDI_EVENT_MUM_WATCHDOG 0x3 +#define MCDI_EVENT_MUM_ERR_DATA_LBN 8 +#define MCDI_EVENT_MUM_ERR_DATA_WIDTH 8 +#define MCDI_EVENT_DATA_LBN 0 +#define MCDI_EVENT_DATA_WIDTH 32 +#define MCDI_EVENT_SRC_LBN 36 +#define MCDI_EVENT_SRC_WIDTH 8 +#define MCDI_EVENT_EV_CODE_LBN 60 +#define MCDI_EVENT_EV_CODE_WIDTH 4 +#define MCDI_EVENT_CODE_LBN 44 +#define MCDI_EVENT_CODE_WIDTH 8 +/* enum: Event generated by host software */ +#define MCDI_EVENT_SW_EVENT 0x0 +/* enum: Bad assert. */ +#define MCDI_EVENT_CODE_BADSSERT 0x1 +/* enum: PM Notice. */ +#define MCDI_EVENT_CODE_PMNOTICE 0x2 +/* enum: Command done. */ +#define MCDI_EVENT_CODE_CMDDONE 0x3 +/* enum: Link change. */ +#define MCDI_EVENT_CODE_LINKCHANGE 0x4 +/* enum: Sensor Event. */ +#define MCDI_EVENT_CODE_SENSOREVT 0x5 +/* enum: Schedule error. */ +#define MCDI_EVENT_CODE_SCHEDERR 0x6 +/* enum: Reboot. */ +#define MCDI_EVENT_CODE_REBOOT 0x7 +/* enum: Mac stats DMA. */ +#define MCDI_EVENT_CODE_MAC_STATS_DMA 0x8 +/* enum: Firmware alert. */ +#define MCDI_EVENT_CODE_FWALERT 0x9 +/* enum: Function level reset. */ +#define MCDI_EVENT_CODE_FLR 0xa +/* enum: Transmit error */ +#define MCDI_EVENT_CODE_TX_ERR 0xb +/* enum: Tx flush has completed */ +#define MCDI_EVENT_CODE_TX_FLUSH 0xc +/* enum: PTP packet received timestamp */ +#define MCDI_EVENT_CODE_PTP_RX 0xd +/* enum: PTP NIC failure */ +#define MCDI_EVENT_CODE_PTP_FAULT 0xe +/* enum: PTP PPS event */ +#define MCDI_EVENT_CODE_PTP_PPS 0xf +/* enum: Rx flush has completed */ +#define MCDI_EVENT_CODE_RX_FLUSH 0x10 +/* enum: Receive error */ +#define MCDI_EVENT_CODE_RX_ERR 0x11 +/* enum: AOE fault */ +#define MCDI_EVENT_CODE_AOE 0x12 +/* enum: Network port calibration failed (VCAL). */ +#define MCDI_EVENT_CODE_VCAL_FAIL 0x13 +/* enum: HW PPS event */ +#define MCDI_EVENT_CODE_HW_PPS 0x14 +/* enum: The MC has rebooted (huntington and later, siena uses CODE_REBOOT and + * a different format) + */ +#define MCDI_EVENT_CODE_MC_REBOOT 0x15 +/* enum: the MC has detected a parity error */ +#define MCDI_EVENT_CODE_PAR_ERR 0x16 +/* enum: the MC has detected a correctable error */ +#define MCDI_EVENT_CODE_ECC_CORR_ERR 0x17 +/* enum: the MC has detected an uncorrectable error */ +#define MCDI_EVENT_CODE_ECC_FATAL_ERR 0x18 +/* enum: The MC has entered offline BIST mode */ +#define MCDI_EVENT_CODE_MC_BIST 0x19 +/* enum: PTP tick event providing current NIC time */ +#define MCDI_EVENT_CODE_PTP_TIME 0x1a +/* enum: MUM fault */ +#define MCDI_EVENT_CODE_MUM 0x1b +/* enum: notify the designated PF of a new authorization request */ +#define MCDI_EVENT_CODE_PROXY_REQUEST 0x1c +/* enum: notify a function that awaits an authorization that its request has + * been processed and it may now resend the command + */ +#define MCDI_EVENT_CODE_PROXY_RESPONSE 0x1d +/* enum: Artificial event generated by host and posted via MC for test + * purposes. + */ +#define MCDI_EVENT_CODE_TESTGEN 0xfa +#define MCDI_EVENT_CMDDONE_DATA_OFST 0 +#define MCDI_EVENT_CMDDONE_DATA_LBN 0 +#define MCDI_EVENT_CMDDONE_DATA_WIDTH 32 +#define MCDI_EVENT_LINKCHANGE_DATA_OFST 0 +#define MCDI_EVENT_LINKCHANGE_DATA_LBN 0 +#define MCDI_EVENT_LINKCHANGE_DATA_WIDTH 32 +#define MCDI_EVENT_SENSOREVT_DATA_OFST 0 +#define MCDI_EVENT_SENSOREVT_DATA_LBN 0 +#define MCDI_EVENT_SENSOREVT_DATA_WIDTH 32 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_OFST 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_LBN 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_WIDTH 32 +#define MCDI_EVENT_TX_ERR_DATA_OFST 0 +#define MCDI_EVENT_TX_ERR_DATA_LBN 0 +#define MCDI_EVENT_TX_ERR_DATA_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the seconds field of + * timestamp + */ +#define MCDI_EVENT_PTP_SECONDS_OFST 0 +#define MCDI_EVENT_PTP_SECONDS_LBN 0 +#define MCDI_EVENT_PTP_SECONDS_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the major field of + * timestamp + */ +#define MCDI_EVENT_PTP_MAJOR_OFST 0 +#define MCDI_EVENT_PTP_MAJOR_LBN 0 +#define MCDI_EVENT_PTP_MAJOR_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the nanoseconds field + * of timestamp + */ +#define MCDI_EVENT_PTP_NANOSECONDS_OFST 0 +#define MCDI_EVENT_PTP_NANOSECONDS_LBN 0 +#define MCDI_EVENT_PTP_NANOSECONDS_WIDTH 32 +/* For CODE_PTP_RX, CODE_PTP_PPS and CODE_HW_PPS events the minor field of + * timestamp + */ +#define MCDI_EVENT_PTP_MINOR_OFST 0 +#define MCDI_EVENT_PTP_MINOR_LBN 0 +#define MCDI_EVENT_PTP_MINOR_WIDTH 32 +/* For CODE_PTP_RX events, the lowest four bytes of sourceUUID from PTP packet + */ +#define MCDI_EVENT_PTP_UUID_OFST 0 +#define MCDI_EVENT_PTP_UUID_LBN 0 +#define MCDI_EVENT_PTP_UUID_WIDTH 32 +#define MCDI_EVENT_RX_ERR_DATA_OFST 0 +#define MCDI_EVENT_RX_ERR_DATA_LBN 0 +#define MCDI_EVENT_RX_ERR_DATA_WIDTH 32 +#define MCDI_EVENT_PAR_ERR_DATA_OFST 0 +#define MCDI_EVENT_PAR_ERR_DATA_LBN 0 +#define MCDI_EVENT_PAR_ERR_DATA_WIDTH 32 +#define MCDI_EVENT_ECC_CORR_ERR_DATA_OFST 0 +#define MCDI_EVENT_ECC_CORR_ERR_DATA_LBN 0 +#define MCDI_EVENT_ECC_CORR_ERR_DATA_WIDTH 32 +#define MCDI_EVENT_ECC_FATAL_ERR_DATA_OFST 0 +#define MCDI_EVENT_ECC_FATAL_ERR_DATA_LBN 0 +#define MCDI_EVENT_ECC_FATAL_ERR_DATA_WIDTH 32 +/* For CODE_PTP_TIME events, the major value of the PTP clock */ +#define MCDI_EVENT_PTP_TIME_MAJOR_OFST 0 +#define MCDI_EVENT_PTP_TIME_MAJOR_LBN 0 +#define MCDI_EVENT_PTP_TIME_MAJOR_WIDTH 32 +/* For CODE_PTP_TIME events, bits 19-26 of the minor value of the PTP clock */ +#define MCDI_EVENT_PTP_TIME_MINOR_26_19_LBN 36 +#define MCDI_EVENT_PTP_TIME_MINOR_26_19_WIDTH 8 +/* For CODE_PTP_TIME events where report sync status is enabled, indicates + * whether the NIC clock has ever been set + */ +#define MCDI_EVENT_PTP_TIME_NIC_CLOCK_VALID_LBN 36 +#define MCDI_EVENT_PTP_TIME_NIC_CLOCK_VALID_WIDTH 1 +/* For CODE_PTP_TIME events where report sync status is enabled, indicates + * whether the NIC and System clocks are in sync + */ +#define MCDI_EVENT_PTP_TIME_HOST_NIC_IN_SYNC_LBN 37 +#define MCDI_EVENT_PTP_TIME_HOST_NIC_IN_SYNC_WIDTH 1 +/* For CODE_PTP_TIME events where report sync status is enabled, bits 21-26 of + * the minor value of the PTP clock + */ +#define MCDI_EVENT_PTP_TIME_MINOR_26_21_LBN 38 +#define MCDI_EVENT_PTP_TIME_MINOR_26_21_WIDTH 6 +#define MCDI_EVENT_PROXY_REQUEST_BUFF_INDEX_OFST 0 +#define MCDI_EVENT_PROXY_REQUEST_BUFF_INDEX_LBN 0 +#define MCDI_EVENT_PROXY_REQUEST_BUFF_INDEX_WIDTH 32 +#define MCDI_EVENT_PROXY_RESPONSE_HANDLE_OFST 0 +#define MCDI_EVENT_PROXY_RESPONSE_HANDLE_LBN 0 +#define MCDI_EVENT_PROXY_RESPONSE_HANDLE_WIDTH 32 +/* Zero means that the request has been completed or authorized, and the driver + * should resend it. A non-zero value means that the authorization has been + * denied, and gives the reason. Typically it will be EPERM. + */ +#define MCDI_EVENT_PROXY_RESPONSE_RC_LBN 36 +#define MCDI_EVENT_PROXY_RESPONSE_RC_WIDTH 8 + +/* EVB_PORT_ID structuredef */ +#define EVB_PORT_ID_LEN 4 +#define EVB_PORT_ID_PORT_ID_OFST 0 +/* enum: An invalid port handle. */ +#define EVB_PORT_ID_NULL 0x0 +/* enum: The port assigned to this function.. */ +#define EVB_PORT_ID_ASSIGNED 0x1000000 +/* enum: External network port 0 */ +#define EVB_PORT_ID_MAC0 0x2000000 +/* enum: External network port 1 */ +#define EVB_PORT_ID_MAC1 0x2000001 +/* enum: External network port 2 */ +#define EVB_PORT_ID_MAC2 0x2000002 +/* enum: External network port 3 */ +#define EVB_PORT_ID_MAC3 0x2000003 +#define EVB_PORT_ID_PORT_ID_LBN 0 +#define EVB_PORT_ID_PORT_ID_WIDTH 32 + + +/***********************************/ +/* MC_CMD_DRV_ATTACH + * Inform MCPU that this port is managed on the host (i.e. driver active). For + * Huntington, also request the preferred datapath firmware to use if possible + * (it may not be possible for this request to be fulfilled; the driver must + * issue a subsequent MC_CMD_GET_CAPABILITIES command to determine which + * features are actually available). The FIRMWARE_ID field is ignored by older + * platforms. + */ +#define MC_CMD_DRV_ATTACH 0x1c +#undef MC_CMD_0x1c_PRIVILEGE_CTG + +#define MC_CMD_0x1c_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_DRV_ATTACH_IN msgrequest */ +#define MC_CMD_DRV_ATTACH_IN_LEN 12 +/* new state to set if UPDATE=1 */ +#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_OFST 0 +#define MC_CMD_DRV_ATTACH_LBN 0 +#define MC_CMD_DRV_ATTACH_WIDTH 1 +#define MC_CMD_DRV_PREBOOT_LBN 1 +#define MC_CMD_DRV_PREBOOT_WIDTH 1 +/* 1 to set new state, or 0 to just report the existing state */ +#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4 +/* preferred datapath firmware (for Huntington; ignored for Siena) */ +#define MC_CMD_DRV_ATTACH_IN_FIRMWARE_ID_OFST 8 +/* enum: Prefer to use full featured firmware */ +#define MC_CMD_FW_FULL_FEATURED 0x0 +/* enum: Prefer to use firmware with fewer features but lower latency */ +#define MC_CMD_FW_LOW_LATENCY 0x1 +/* enum: Prefer to use firmware for SolarCapture packed stream mode */ +#define MC_CMD_FW_PACKED_STREAM 0x2 +/* enum: Prefer to use firmware with fewer features and simpler TX event + * batching but higher TX packet rate + */ +#define MC_CMD_FW_HIGH_TX_RATE 0x3 +/* enum: Reserved value */ +#define MC_CMD_FW_PACKED_STREAM_HASH_MODE_1 0x4 +/* enum: Prefer to use firmware with additional "rules engine" filtering + * support + */ +#define MC_CMD_FW_RULES_ENGINE 0x5 +/* enum: Only this option is allowed for non-admin functions */ +#define MC_CMD_FW_DONT_CARE 0xffffffff + +/* MC_CMD_DRV_ATTACH_OUT msgresponse */ +#define MC_CMD_DRV_ATTACH_OUT_LEN 4 +/* previous or existing state, see the bitmask at NEW_STATE */ +#define MC_CMD_DRV_ATTACH_OUT_OLD_STATE_OFST 0 + +/* MC_CMD_DRV_ATTACH_EXT_OUT msgresponse */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_LEN 8 +/* previous or existing state, see the bitmask at NEW_STATE */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_OLD_STATE_OFST 0 +/* Flags associated with this function */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FUNC_FLAGS_OFST 4 +/* enum: Labels the lowest-numbered function visible to the OS */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY 0x0 +/* enum: The function can control the link state of the physical port it is + * bound to. + */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL 0x1 +/* enum: The function can perform privileged operations */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED 0x2 +/* enum: The function does not have an active port associated with it. The port + * refers to the Sorrento external FPGA port. + */ +#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT 0x3 + + +/***********************************/ +/* MC_CMD_ENTITY_RESET + * Generic per-resource reset. There is no equivalent for per-board reset. + * Locks required: None; Return code: 0, ETIME. NOTE: This command is an + * extended version of the deprecated MC_CMD_PORT_RESET with added fields. + */ +#define MC_CMD_ENTITY_RESET 0x20 +/* MC_CMD_0x20_PRIVILEGE_CTG SRIOV_CTG_GENERAL */ + +/* MC_CMD_ENTITY_RESET_IN msgrequest */ +#define MC_CMD_ENTITY_RESET_IN_LEN 4 +/* Optional flags field. Omitting this will perform a "legacy" reset action + * (TBD). + */ +#define MC_CMD_ENTITY_RESET_IN_FLAG_OFST 0 +#define MC_CMD_ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET_LBN 0 +#define MC_CMD_ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET_WIDTH 1 + +/* MC_CMD_ENTITY_RESET_OUT msgresponse */ +#define MC_CMD_ENTITY_RESET_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_GET_PHY_CFG + * Report PHY configuration. This guarantees to succeed even if the PHY is in a + * 'zombie' state. Locks required: None + */ +#define MC_CMD_GET_PHY_CFG 0x24 +#undef MC_CMD_0x24_PRIVILEGE_CTG + +#define MC_CMD_0x24_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_PHY_CFG_IN msgrequest */ +#define MC_CMD_GET_PHY_CFG_IN_LEN 0 + +/* MC_CMD_GET_PHY_CFG_OUT msgresponse */ +#define MC_CMD_GET_PHY_CFG_OUT_LEN 72 +/* flags */ +#define MC_CMD_GET_PHY_CFG_OUT_FLAGS_OFST 0 +#define MC_CMD_GET_PHY_CFG_OUT_PRESENT_LBN 0 +#define MC_CMD_GET_PHY_CFG_OUT_PRESENT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN 2 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_LBN 3 +#define MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_POWEROFF_LBN 4 +#define MC_CMD_GET_PHY_CFG_OUT_POWEROFF_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_TXDIS_LBN 5 +#define MC_CMD_GET_PHY_CFG_OUT_TXDIS_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_LBN 6 +#define MC_CMD_GET_PHY_CFG_OUT_BIST_WIDTH 1 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_TYPE_OFST 4 +/* Bitmask of supported capabilities */ +#define MC_CMD_GET_PHY_CFG_OUT_SUPPORTED_CAP_OFST 8 +#define MC_CMD_PHY_CAP_10HDX_LBN 1 +#define MC_CMD_PHY_CAP_10HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10FDX_LBN 2 +#define MC_CMD_PHY_CAP_10FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100HDX_LBN 3 +#define MC_CMD_PHY_CAP_100HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100FDX_LBN 4 +#define MC_CMD_PHY_CAP_100FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000HDX_LBN 5 +#define MC_CMD_PHY_CAP_1000HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000FDX_LBN 6 +#define MC_CMD_PHY_CAP_1000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10000FDX_LBN 7 +#define MC_CMD_PHY_CAP_10000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_PAUSE_LBN 8 +#define MC_CMD_PHY_CAP_PAUSE_WIDTH 1 +#define MC_CMD_PHY_CAP_ASYM_LBN 9 +#define MC_CMD_PHY_CAP_ASYM_WIDTH 1 +#define MC_CMD_PHY_CAP_AN_LBN 10 +#define MC_CMD_PHY_CAP_AN_WIDTH 1 +#define MC_CMD_PHY_CAP_40000FDX_LBN 11 +#define MC_CMD_PHY_CAP_40000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_DDM_LBN 12 +#define MC_CMD_PHY_CAP_DDM_WIDTH 1 +#define MC_CMD_PHY_CAP_100000FDX_LBN 13 +#define MC_CMD_PHY_CAP_100000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_25000FDX_LBN 14 +#define MC_CMD_PHY_CAP_25000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_50000FDX_LBN 15 +#define MC_CMD_PHY_CAP_50000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_BASER_FEC_LBN 16 +#define MC_CMD_PHY_CAP_BASER_FEC_WIDTH 1 +#define MC_CMD_PHY_CAP_BASER_FEC_REQ_LBN 17 +#define MC_CMD_PHY_CAP_BASER_FEC_REQ_WIDTH 1 +#define MC_CMD_PHY_CAP_RS_FEC_LBN 17 +#define MC_CMD_PHY_CAP_RS_FEC_WIDTH 1 +#define MC_CMD_PHY_CAP_RS_FEC_REQ_LBN 18 +#define MC_CMD_PHY_CAP_RS_FEC_REQ_WIDTH 1 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_CHANNEL_OFST 12 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_PRT_OFST 16 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_STATS_MASK_OFST 20 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_NAME_OFST 24 +#define MC_CMD_GET_PHY_CFG_OUT_NAME_LEN 20 +/* ?? */ +#define MC_CMD_GET_PHY_CFG_OUT_MEDIA_TYPE_OFST 44 +/* enum: Xaui. */ +#define MC_CMD_MEDIA_XAUI 0x1 +/* enum: CX4. */ +#define MC_CMD_MEDIA_CX4 0x2 +/* enum: KX4. */ +#define MC_CMD_MEDIA_KX4 0x3 +/* enum: XFP Far. */ +#define MC_CMD_MEDIA_XFP 0x4 +/* enum: SFP+. */ +#define MC_CMD_MEDIA_SFP_PLUS 0x5 +/* enum: 10GBaseT. */ +#define MC_CMD_MEDIA_BASE_T 0x6 +/* enum: QSFP+. */ +#define MC_CMD_MEDIA_QSFP_PLUS 0x7 +#define MC_CMD_GET_PHY_CFG_OUT_MMD_MASK_OFST 48 +/* enum: Native clause 22 */ +#define MC_CMD_MMD_CLAUSE22 0x0 +#define MC_CMD_MMD_CLAUSE45_PMAPMD 0x1 /* enum */ +#define MC_CMD_MMD_CLAUSE45_WIS 0x2 /* enum */ +#define MC_CMD_MMD_CLAUSE45_PCS 0x3 /* enum */ +#define MC_CMD_MMD_CLAUSE45_PHYXS 0x4 /* enum */ +#define MC_CMD_MMD_CLAUSE45_DTEXS 0x5 /* enum */ +#define MC_CMD_MMD_CLAUSE45_TC 0x6 /* enum */ +#define MC_CMD_MMD_CLAUSE45_AN 0x7 /* enum */ +/* enum: Clause22 proxied over clause45 by PHY. */ +#define MC_CMD_MMD_CLAUSE45_C22EXT 0x1d +#define MC_CMD_MMD_CLAUSE45_VEND1 0x1e /* enum */ +#define MC_CMD_MMD_CLAUSE45_VEND2 0x1f /* enum */ +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_OFST 52 +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN 20 + + +/***********************************/ +/* MC_CMD_GET_LINK + * Read the unified MAC/PHY link state. Locks required: None Return code: 0, + * ETIME. + */ +#define MC_CMD_GET_LINK 0x29 +#undef MC_CMD_0x29_PRIVILEGE_CTG + +#define MC_CMD_0x29_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_LINK_IN msgrequest */ +#define MC_CMD_GET_LINK_IN_LEN 0 + +/* MC_CMD_GET_LINK_OUT msgresponse */ +#define MC_CMD_GET_LINK_OUT_LEN 28 +/* near-side advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_CAP_OFST 0 +/* link-partner advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4 +/* Autonegotiated speed in mbit/s. The link may still be down even if this + * reads non-zero. + */ +#define MC_CMD_GET_LINK_OUT_LINK_SPEED_OFST 8 +/* Current loopback setting. */ +#define MC_CMD_GET_LINK_OUT_LOOPBACK_MODE_OFST 12 +/* Enum values, see field(s): */ +/* MC_CMD_GET_LOOPBACK_MODES/MC_CMD_GET_LOOPBACK_MODES_OUT/100M */ +#define MC_CMD_GET_LINK_OUT_FLAGS_OFST 16 +#define MC_CMD_GET_LINK_OUT_LINK_UP_LBN 0 +#define MC_CMD_GET_LINK_OUT_LINK_UP_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN 1 +#define MC_CMD_GET_LINK_OUT_FULL_DUPLEX_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_BPX_LINK_LBN 2 +#define MC_CMD_GET_LINK_OUT_BPX_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_PHY_LINK_LBN 3 +#define MC_CMD_GET_LINK_OUT_PHY_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_RX_LBN 6 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_RX_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_TX_LBN 7 +#define MC_CMD_GET_LINK_OUT_LINK_FAULT_TX_WIDTH 1 +/* This returns the negotiated flow control value. */ +#define MC_CMD_GET_LINK_OUT_FCNTL_OFST 20 +/* Enum values, see field(s): */ +/* MC_CMD_SET_MAC/MC_CMD_SET_MAC_IN/FCNTL */ +#define MC_CMD_GET_LINK_OUT_MAC_FAULT_OFST 24 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_PENDING_RECONFIG_LBN 3 +#define MC_CMD_MAC_FAULT_PENDING_RECONFIG_WIDTH 1 + + +/***********************************/ +/* MC_CMD_SET_MAC + * Set MAC configuration. Locks required: None. Return code: 0, EINVAL + */ +#define MC_CMD_SET_MAC 0x2c +#undef MC_CMD_0x2c_PRIVILEGE_CTG + +#define MC_CMD_0x2c_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_SET_MAC_IN msgrequest */ +#define MC_CMD_SET_MAC_IN_LEN 28 +/* The MTU is the MTU programmed directly into the XMAC/GMAC (inclusive of + * EtherII, VLAN, bug16011 padding). + */ +#define MC_CMD_SET_MAC_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_IN_ADDR_LEN 8 +#define MC_CMD_SET_MAC_IN_ADDR_LO_OFST 8 +#define MC_CMD_SET_MAC_IN_ADDR_HI_OFST 12 +#define MC_CMD_SET_MAC_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_FCNTL_OFST 20 +/* enum: Flow control is off. */ +#define MC_CMD_FCNTL_OFF 0x0 +/* enum: Respond to flow control. */ +#define MC_CMD_FCNTL_RESPOND 0x1 +/* enum: Respond to and Issue flow control. */ +#define MC_CMD_FCNTL_BIDIR 0x2 +/* enum: Auto neg flow control. */ +#define MC_CMD_FCNTL_AUTO 0x3 +/* enum: Priority flow control (eftest builds only). */ +#define MC_CMD_FCNTL_QBB 0x4 +/* enum: Issue flow control. */ +#define MC_CMD_FCNTL_GENERATE 0x5 +#define MC_CMD_SET_MAC_IN_FLAGS_OFST 24 +#define MC_CMD_SET_MAC_IN_FLAG_INCLUDE_FCS_LBN 0 +#define MC_CMD_SET_MAC_IN_FLAG_INCLUDE_FCS_WIDTH 1 + +/* MC_CMD_SET_MAC_EXT_IN msgrequest */ +#define MC_CMD_SET_MAC_EXT_IN_LEN 32 +/* The MTU is the MTU programmed directly into the XMAC/GMAC (inclusive of + * EtherII, VLAN, bug16011 padding). + */ +#define MC_CMD_SET_MAC_EXT_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_EXT_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_LEN 8 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_LO_OFST 8 +#define MC_CMD_SET_MAC_EXT_IN_ADDR_HI_OFST 12 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_EXT_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_FCNTL_OFST 20 +/* enum: Flow control is off. */ +/* MC_CMD_FCNTL_OFF 0x0 */ +/* enum: Respond to flow control. */ +/* MC_CMD_FCNTL_RESPOND 0x1 */ +/* enum: Respond to and Issue flow control. */ +/* MC_CMD_FCNTL_BIDIR 0x2 */ +/* enum: Auto neg flow control. */ +/* MC_CMD_FCNTL_AUTO 0x3 */ +/* enum: Priority flow control (eftest builds only). */ +/* MC_CMD_FCNTL_QBB 0x4 */ +/* enum: Issue flow control. */ +/* MC_CMD_FCNTL_GENERATE 0x5 */ +#define MC_CMD_SET_MAC_EXT_IN_FLAGS_OFST 24 +#define MC_CMD_SET_MAC_EXT_IN_FLAG_INCLUDE_FCS_LBN 0 +#define MC_CMD_SET_MAC_EXT_IN_FLAG_INCLUDE_FCS_WIDTH 1 +/* Select which parameters to configure. A parameter will only be modified if + * the corresponding control flag is set. If SET_MAC_ENHANCED is not set in + * capabilities then this field is ignored (and all flags are assumed to be + * set). + */ +#define MC_CMD_SET_MAC_EXT_IN_CONTROL_OFST 28 +#define MC_CMD_SET_MAC_EXT_IN_CFG_MTU_LBN 0 +#define MC_CMD_SET_MAC_EXT_IN_CFG_MTU_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_DRAIN_LBN 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_DRAIN_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_REJECT_LBN 2 +#define MC_CMD_SET_MAC_EXT_IN_CFG_REJECT_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCNTL_LBN 3 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCNTL_WIDTH 1 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCS_LBN 4 +#define MC_CMD_SET_MAC_EXT_IN_CFG_FCS_WIDTH 1 + +/* MC_CMD_SET_MAC_OUT msgresponse */ +#define MC_CMD_SET_MAC_OUT_LEN 0 + +/* MC_CMD_SET_MAC_V2_OUT msgresponse */ +#define MC_CMD_SET_MAC_V2_OUT_LEN 4 +/* MTU as configured after processing the request. See comment at + * MC_CMD_SET_MAC_IN/MTU. To query MTU without doing any changes, set CONTROL + * to 0. + */ +#define MC_CMD_SET_MAC_V2_OUT_MTU_OFST 0 + + +/***********************************/ +/* MC_CMD_REBOOT + * Reboot the MC. + * + * The AFTER_ASSERTION flag is intended to be used when the driver notices an + * assertion failure (at which point it is expected to perform a complete tear + * down and reinitialise), to allow both ports to reset the MC once in an + * atomic fashion. + * + * Production mc firmwares are generally compiled with REBOOT_ON_ASSERT=1, + * which means that they will automatically reboot out of the assertion + * handler, so this is in practise an optional operation. It is still + * recommended that drivers execute this to support custom firmwares with + * REBOOT_ON_ASSERT=0. + * + * Locks required: NONE Returns: Nothing. You get back a response with ERR=1, + * DATALEN=0 + */ +#define MC_CMD_REBOOT 0x3d +#undef MC_CMD_0x3d_PRIVILEGE_CTG + +#define MC_CMD_0x3d_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_REBOOT_IN msgrequest */ +#define MC_CMD_REBOOT_IN_LEN 4 +#define MC_CMD_REBOOT_IN_FLAGS_OFST 0 +#define MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION 0x1 /* enum */ + +/* MC_CMD_REBOOT_OUT msgresponse */ +#define MC_CMD_REBOOT_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_REBOOT_MODE + * Set the mode for the next MC reboot. Locks required: NONE. Sets the reboot + * mode to the specified value. Returns the old mode. + */ +#define MC_CMD_REBOOT_MODE 0x3f +#undef MC_CMD_0x3f_PRIVILEGE_CTG + +#define MC_CMD_0x3f_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_REBOOT_MODE_IN msgrequest */ +#define MC_CMD_REBOOT_MODE_IN_LEN 4 +#define MC_CMD_REBOOT_MODE_IN_VALUE_OFST 0 +/* enum: Normal. */ +#define MC_CMD_REBOOT_MODE_NORMAL 0x0 +/* enum: Power-on Reset. */ +#define MC_CMD_REBOOT_MODE_POR 0x2 +/* enum: Snapper. */ +#define MC_CMD_REBOOT_MODE_SNAPPER 0x3 +/* enum: snapper fake POR */ +#define MC_CMD_REBOOT_MODE_SNAPPER_POR 0x4 +#define MC_CMD_REBOOT_MODE_IN_FAKE_LBN 7 +#define MC_CMD_REBOOT_MODE_IN_FAKE_WIDTH 1 + +/* MC_CMD_REBOOT_MODE_OUT msgresponse */ +#define MC_CMD_REBOOT_MODE_OUT_LEN 4 +#define MC_CMD_REBOOT_MODE_OUT_VALUE_OFST 0 + + +/***********************************/ +/* MC_CMD_WORKAROUND + * Enable/Disable a given workaround. The mcfw will return EINVAL if it doesn't + * understand the given workaround number - which should not be treated as a + * hard error by client code. This op does not imply any semantics about each + * workaround, that's between the driver and the mcfw on a per-workaround + * basis. Locks required: None. Returns: 0, EINVAL . + */ +#define MC_CMD_WORKAROUND 0x4a +#undef MC_CMD_0x4a_PRIVILEGE_CTG + +#define MC_CMD_0x4a_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_WORKAROUND_IN msgrequest */ +#define MC_CMD_WORKAROUND_IN_LEN 8 +/* The enums here must correspond with those in MC_CMD_GET_WORKAROUND. */ +#define MC_CMD_WORKAROUND_IN_TYPE_OFST 0 +/* enum: Bug 17230 work around. */ +#define MC_CMD_WORKAROUND_BUG17230 0x1 +/* enum: Bug 35388 work around (unsafe EVQ writes). */ +#define MC_CMD_WORKAROUND_BUG35388 0x2 +/* enum: Bug35017 workaround (A64 tables must be identity map) */ +#define MC_CMD_WORKAROUND_BUG35017 0x3 +/* enum: Bug 41750 present (MC_CMD_TRIGGER_INTERRUPT won't work) */ +#define MC_CMD_WORKAROUND_BUG41750 0x4 +/* enum: Bug 42008 present (Interrupts can overtake associated events). Caution + * - before adding code that queries this workaround, remember that there's + * released Monza firmware that doesn't understand MC_CMD_WORKAROUND_BUG42008, + * and will hence (incorrectly) report that the bug doesn't exist. + */ +#define MC_CMD_WORKAROUND_BUG42008 0x5 +/* enum: Bug 26807 features present in firmware (multicast filter chaining) + * This feature cannot be turned on/off while there are any filters already + * present. The behaviour in such case depends on the acting client's privilege + * level. If the client has the admin privilege, then all functions that have + * filters installed will be FLRed and the FLR_DONE flag will be set. Otherwise + * the command will fail with MC_CMD_ERR_FILTERS_PRESENT. + */ +#define MC_CMD_WORKAROUND_BUG26807 0x6 +/* enum: Bug 61265 work around (broken EVQ TMR writes). */ +#define MC_CMD_WORKAROUND_BUG61265 0x7 +/* 0 = disable the workaround indicated by TYPE; any non-zero value = enable + * the workaround + */ +#define MC_CMD_WORKAROUND_IN_ENABLED_OFST 4 + +/* MC_CMD_WORKAROUND_OUT msgresponse */ +#define MC_CMD_WORKAROUND_OUT_LEN 0 + +/* MC_CMD_WORKAROUND_EXT_OUT msgresponse: This response format will be used + * when (TYPE == MC_CMD_WORKAROUND_BUG26807) + */ +#define MC_CMD_WORKAROUND_EXT_OUT_LEN 4 +#define MC_CMD_WORKAROUND_EXT_OUT_FLAGS_OFST 0 +#define MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_LBN 0 +#define MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_WIDTH 1 + + +/***********************************/ +/* MC_CMD_GET_MAC_ADDRESSES + * Returns the base MAC, count and stride for the requesting function + */ +#define MC_CMD_GET_MAC_ADDRESSES 0x55 +#undef MC_CMD_0x55_PRIVILEGE_CTG + +#define MC_CMD_0x55_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_MAC_ADDRESSES_IN msgrequest */ +#define MC_CMD_GET_MAC_ADDRESSES_IN_LEN 0 + +/* MC_CMD_GET_MAC_ADDRESSES_OUT msgresponse */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_LEN 16 +/* Base MAC address */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE_OFST 0 +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE_LEN 6 +/* Padding */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_RESERVED_OFST 6 +#define MC_CMD_GET_MAC_ADDRESSES_OUT_RESERVED_LEN 2 +/* Number of allocated MAC addresses */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_COUNT_OFST 8 +/* Spacing of allocated MAC addresses */ +#define MC_CMD_GET_MAC_ADDRESSES_OUT_MAC_STRIDE_OFST 12 + + +/***********************************/ +/* MC_CMD_GET_WORKAROUNDS + * Read the list of all implemented and all currently enabled workarounds. The + * enums here must correspond with those in MC_CMD_WORKAROUND. + */ +#define MC_CMD_GET_WORKAROUNDS 0x59 +#undef MC_CMD_0x59_PRIVILEGE_CTG + +#define MC_CMD_0x59_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_WORKAROUNDS_OUT msgresponse */ +#define MC_CMD_GET_WORKAROUNDS_OUT_LEN 8 +/* Each workaround is represented by a single bit according to the enums below. + */ +#define MC_CMD_GET_WORKAROUNDS_OUT_IMPLEMENTED_OFST 0 +#define MC_CMD_GET_WORKAROUNDS_OUT_ENABLED_OFST 4 +/* enum: Bug 17230 work around. */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG17230 0x2 +/* enum: Bug 35388 work around (unsafe EVQ writes). */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG35388 0x4 +/* enum: Bug35017 workaround (A64 tables must be identity map) */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG35017 0x8 +/* enum: Bug 41750 present (MC_CMD_TRIGGER_INTERRUPT won't work) */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG41750 0x10 +/* enum: Bug 42008 present (Interrupts can overtake associated events). Caution + * - before adding code that queries this workaround, remember that there's + * released Monza firmware that doesn't understand MC_CMD_WORKAROUND_BUG42008, + * and will hence (incorrectly) report that the bug doesn't exist. + */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG42008 0x20 +/* enum: Bug 26807 features present in firmware (multicast filter chaining) */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG26807 0x40 +/* enum: Bug 61265 work around (broken EVQ TMR writes). */ +#define MC_CMD_GET_WORKAROUNDS_OUT_BUG61265 0x80 + + +/***********************************/ +/* MC_CMD_V2_EXTN + * Encapsulation for a v2 extended command + */ +#define MC_CMD_V2_EXTN 0x7f + +/* MC_CMD_V2_EXTN_IN msgrequest */ +#define MC_CMD_V2_EXTN_IN_LEN 4 +/* the extended command number */ +#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_LBN 0 +#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_WIDTH 15 +#define MC_CMD_V2_EXTN_IN_UNUSED_LBN 15 +#define MC_CMD_V2_EXTN_IN_UNUSED_WIDTH 1 +/* the actual length of the encapsulated command (which is not in the v1 + * header) + */ +#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_LBN 16 +#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_WIDTH 10 +#define MC_CMD_V2_EXTN_IN_UNUSED2_LBN 26 +#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 2 +/* Type of command/response */ +#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_LBN 28 +#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_WIDTH 4 +/* enum: MCDI command directed to or response originating from the MC. */ +#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_MC 0x0 +/* enum: MCDI command directed to a TSA controller. MCDI responses of this type + * are not defined. + */ +#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_TSA 0x1 + + +/***********************************/ +/* MC_CMD_INIT_EVQ + * Set up an event queue according to the supplied parameters. The IN arguments + * end with an address for each 4k of host memory required to back the EVQ. + */ +#define MC_CMD_INIT_EVQ 0x80 +#undef MC_CMD_0x80_PRIVILEGE_CTG + +#define MC_CMD_0x80_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_INIT_EVQ_IN msgrequest */ +#define MC_CMD_INIT_EVQ_IN_LENMIN 44 +#define MC_CMD_INIT_EVQ_IN_LENMAX 548 +#define MC_CMD_INIT_EVQ_IN_LEN(num) (36+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_EVQ_IN_SIZE_OFST 0 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_EVQ_IN_INSTANCE_OFST 4 +/* The initial timer value. The load value is ignored if the timer mode is DIS. + */ +#define MC_CMD_INIT_EVQ_IN_TMR_LOAD_OFST 8 +/* The reload value is ignored in one-shot modes */ +#define MC_CMD_INIT_EVQ_IN_TMR_RELOAD_OFST 12 +/* tbd */ +#define MC_CMD_INIT_EVQ_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_EVQ_IN_FLAG_INTERRUPTING_LBN 0 +#define MC_CMD_INIT_EVQ_IN_FLAG_INTERRUPTING_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_RPTR_DOS_LBN 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_RPTR_DOS_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_INT_ARMD_LBN 2 +#define MC_CMD_INIT_EVQ_IN_FLAG_INT_ARMD_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_CUT_THRU_LBN 3 +#define MC_CMD_INIT_EVQ_IN_FLAG_CUT_THRU_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_RX_MERGE_LBN 4 +#define MC_CMD_INIT_EVQ_IN_FLAG_RX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_TX_MERGE_LBN 5 +#define MC_CMD_INIT_EVQ_IN_FLAG_TX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_FLAG_USE_TIMER_LBN 6 +#define MC_CMD_INIT_EVQ_IN_FLAG_USE_TIMER_WIDTH 1 +#define MC_CMD_INIT_EVQ_IN_TMR_MODE_OFST 20 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS 0x0 +/* enum: Immediate */ +#define MC_CMD_INIT_EVQ_IN_TMR_IMMED_START 0x1 +/* enum: Triggered */ +#define MC_CMD_INIT_EVQ_IN_TMR_TRIG_START 0x2 +/* enum: Hold-off */ +#define MC_CMD_INIT_EVQ_IN_TMR_INT_HLDOFF 0x3 +/* Target EVQ for wakeups if in wakeup mode. */ +#define MC_CMD_INIT_EVQ_IN_TARGET_EVQ_OFST 24 +/* Target interrupt if in interrupting mode (note union with target EVQ). Use + * MC_CMD_RESOURCE_INSTANCE_ANY unless a specific one required for test + * purposes. + */ +#define MC_CMD_INIT_EVQ_IN_IRQ_NUM_OFST 24 +/* Event Counter Mode. */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_OFST 28 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS 0x0 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_RX 0x1 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_TX 0x2 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_IN_COUNT_MODE_RXTX 0x3 +/* Event queue packet count threshold. */ +#define MC_CMD_INIT_EVQ_IN_COUNT_THRSHLD_OFST 32 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_OFST 36 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_LO_OFST 36 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_HI_OFST 40 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_EVQ_IN_DMA_ADDR_MAXNUM 64 + +/* MC_CMD_INIT_EVQ_OUT msgresponse */ +#define MC_CMD_INIT_EVQ_OUT_LEN 4 +/* Only valid if INTRFLAG was true */ +#define MC_CMD_INIT_EVQ_OUT_IRQ_OFST 0 + +/* MC_CMD_INIT_EVQ_V2_IN msgrequest */ +#define MC_CMD_INIT_EVQ_V2_IN_LENMIN 44 +#define MC_CMD_INIT_EVQ_V2_IN_LENMAX 548 +#define MC_CMD_INIT_EVQ_V2_IN_LEN(num) (36+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_EVQ_V2_IN_SIZE_OFST 0 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_EVQ_V2_IN_INSTANCE_OFST 4 +/* The initial timer value. The load value is ignored if the timer mode is DIS. + */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_LOAD_OFST 8 +/* The reload value is ignored in one-shot modes */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_RELOAD_OFST 12 +/* tbd */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INTERRUPTING_LBN 0 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INTERRUPTING_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RPTR_DOS_LBN 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RPTR_DOS_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INT_ARMD_LBN 2 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_INT_ARMD_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_CUT_THRU_LBN 3 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_CUT_THRU_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RX_MERGE_LBN 4 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_RX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TX_MERGE_LBN 5 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_USE_TIMER_LBN 6 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_USE_TIMER_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_LBN 7 +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_WIDTH 4 +/* enum: All initialisation flags specified by host. */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_MANUAL 0x0 +/* enum: MEDFORD only. Certain initialisation flags specified by host may be + * over-ridden by firmware based on licenses and firmware variant in order to + * provide the lowest latency achievable. See + * MC_CMD_INIT_EVQ_V2/MC_CMD_INIT_EVQ_V2_OUT/FLAGS for list of affected flags. + */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_LOW_LATENCY 0x1 +/* enum: MEDFORD only. Certain initialisation flags specified by host may be + * over-ridden by firmware based on licenses and firmware variant in order to + * provide the best throughput achievable. See + * MC_CMD_INIT_EVQ_V2/MC_CMD_INIT_EVQ_V2_OUT/FLAGS for list of affected flags. + */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_THROUGHPUT 0x2 +/* enum: MEDFORD only. Certain initialisation flags may be over-ridden by + * firmware based on licenses and firmware variant. See + * MC_CMD_INIT_EVQ_V2/MC_CMD_INIT_EVQ_V2_OUT/FLAGS for list of affected flags. + */ +#define MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO 0x3 +#define MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_OFST 20 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_DIS 0x0 +/* enum: Immediate */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_IMMED_START 0x1 +/* enum: Triggered */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_TRIG_START 0x2 +/* enum: Hold-off */ +#define MC_CMD_INIT_EVQ_V2_IN_TMR_INT_HLDOFF 0x3 +/* Target EVQ for wakeups if in wakeup mode. */ +#define MC_CMD_INIT_EVQ_V2_IN_TARGET_EVQ_OFST 24 +/* Target interrupt if in interrupting mode (note union with target EVQ). Use + * MC_CMD_RESOURCE_INSTANCE_ANY unless a specific one required for test + * purposes. + */ +#define MC_CMD_INIT_EVQ_V2_IN_IRQ_NUM_OFST 24 +/* Event Counter Mode. */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_OFST 28 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_DIS 0x0 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_RX 0x1 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_TX 0x2 +/* enum: Disabled */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_RXTX 0x3 +/* Event queue packet count threshold. */ +#define MC_CMD_INIT_EVQ_V2_IN_COUNT_THRSHLD_OFST 32 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_OFST 36 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_LO_OFST 36 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_HI_OFST 40 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_MAXNUM 64 + +/* MC_CMD_INIT_EVQ_V2_OUT msgresponse */ +#define MC_CMD_INIT_EVQ_V2_OUT_LEN 8 +/* Only valid if INTRFLAG was true */ +#define MC_CMD_INIT_EVQ_V2_OUT_IRQ_OFST 0 +/* Actual configuration applied on the card */ +#define MC_CMD_INIT_EVQ_V2_OUT_FLAGS_OFST 4 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_CUT_THRU_LBN 0 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_CUT_THRU_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RX_MERGE_LBN 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_TX_MERGE_LBN 2 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_TX_MERGE_WIDTH 1 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RXQ_FORCE_EV_MERGING_LBN 3 +#define MC_CMD_INIT_EVQ_V2_OUT_FLAG_RXQ_FORCE_EV_MERGING_WIDTH 1 + +/* QUEUE_CRC_MODE structuredef */ +#define QUEUE_CRC_MODE_LEN 1 +#define QUEUE_CRC_MODE_MODE_LBN 0 +#define QUEUE_CRC_MODE_MODE_WIDTH 4 +/* enum: No CRC. */ +#define QUEUE_CRC_MODE_NONE 0x0 +/* enum: CRC Fiber channel over ethernet. */ +#define QUEUE_CRC_MODE_FCOE 0x1 +/* enum: CRC (digest) iSCSI header only. */ +#define QUEUE_CRC_MODE_ISCSI_HDR 0x2 +/* enum: CRC (digest) iSCSI header and payload. */ +#define QUEUE_CRC_MODE_ISCSI 0x3 +/* enum: CRC Fiber channel over IP over ethernet. */ +#define QUEUE_CRC_MODE_FCOIPOE 0x4 +/* enum: CRC MPA. */ +#define QUEUE_CRC_MODE_MPA 0x5 +#define QUEUE_CRC_MODE_SPARE_LBN 4 +#define QUEUE_CRC_MODE_SPARE_WIDTH 4 + + +/***********************************/ +/* MC_CMD_INIT_RXQ + * set up a receive queue according to the supplied parameters. The IN + * arguments end with an address for each 4k of host memory required to back + * the RXQ. + */ +#define MC_CMD_INIT_RXQ 0x81 +#undef MC_CMD_0x81_PRIVILEGE_CTG + +#define MC_CMD_0x81_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_INIT_RXQ_IN msgrequest: Legacy RXQ_INIT request. Use extended version + * in new code. + */ +#define MC_CMD_INIT_RXQ_IN_LENMIN 36 +#define MC_CMD_INIT_RXQ_IN_LENMAX 252 +#define MC_CMD_INIT_RXQ_IN_LEN(num) (28+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_RXQ_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to INIT_EVQ + */ +#define MC_CMD_INIT_RXQ_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_RXQ_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_RXQ_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_RXQ_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_RXQ_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_RXQ_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_HDR_SPLIT_LBN 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_HDR_SPLIT_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_TIMESTAMP_LBN 2 +#define MC_CMD_INIT_RXQ_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_CRC_MODE_LBN 3 +#define MC_CMD_INIT_RXQ_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_RXQ_IN_FLAG_CHAIN_LBN 7 +#define MC_CMD_INIT_RXQ_IN_FLAG_CHAIN_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_PREFIX_LBN 8 +#define MC_CMD_INIT_RXQ_IN_FLAG_PREFIX_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_FLAG_DISABLE_SCATTER_LBN 9 +#define MC_CMD_INIT_RXQ_IN_FLAG_DISABLE_SCATTER_WIDTH 1 +#define MC_CMD_INIT_RXQ_IN_UNUSED_LBN 10 +#define MC_CMD_INIT_RXQ_IN_UNUSED_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_RXQ_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_RXQ_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_RXQ_IN_DMA_ADDR_MAXNUM 28 + +/* MC_CMD_INIT_RXQ_EXT_IN msgrequest: Extended RXQ_INIT with additional mode + * flags + */ +#define MC_CMD_INIT_RXQ_EXT_IN_LEN 544 +/* Size, in entries */ +#define MC_CMD_INIT_RXQ_EXT_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to INIT_EVQ + */ +#define MC_CMD_INIT_RXQ_EXT_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_RXQ_EXT_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_RXQ_EXT_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_RXQ_EXT_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_HDR_SPLIT_LBN 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_HDR_SPLIT_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_TIMESTAMP_LBN 2 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_CRC_MODE_LBN 3 +#define MC_CMD_INIT_RXQ_EXT_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_CHAIN_LBN 7 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_CHAIN_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_PREFIX_LBN 8 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_PREFIX_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_DISABLE_SCATTER_LBN 9 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_DISABLE_SCATTER_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_MODE_LBN 10 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_MODE_WIDTH 4 +/* enum: One packet per descriptor (for normal networking) */ +#define MC_CMD_INIT_RXQ_EXT_IN_SINGLE_PACKET 0x0 +/* enum: Pack multiple packets into large descriptors (for SolarCapture) */ +#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM 0x1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_SNAPSHOT_MODE_LBN 14 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_SNAPSHOT_MODE_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE_LBN 15 +#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE_WIDTH 3 +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_1M 0x0 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_512K 0x1 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_256K 0x2 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_128K 0x3 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_64K 0x4 /* enum */ +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES_LBN 18 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES_WIDTH 1 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_FORCE_EV_MERGING_LBN 19 +#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_FORCE_EV_MERGING_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_RXQ_EXT_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_RXQ_EXT_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_RXQ_EXT_IN_DMA_ADDR_NUM 64 +/* Maximum length of packet to receive, if SNAPSHOT_MODE flag is set */ +#define MC_CMD_INIT_RXQ_EXT_IN_SNAPSHOT_LENGTH_OFST 540 + +/* MC_CMD_INIT_RXQ_OUT msgresponse */ +#define MC_CMD_INIT_RXQ_OUT_LEN 0 + +/* MC_CMD_INIT_RXQ_EXT_OUT msgresponse */ +#define MC_CMD_INIT_RXQ_EXT_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_INIT_TXQ + */ +#define MC_CMD_INIT_TXQ 0x82 +#undef MC_CMD_0x82_PRIVILEGE_CTG + +#define MC_CMD_0x82_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_INIT_TXQ_IN msgrequest: Legacy INIT_TXQ request. Use extended version + * in new code. + */ +#define MC_CMD_INIT_TXQ_IN_LENMIN 36 +#define MC_CMD_INIT_TXQ_IN_LENMAX 252 +#define MC_CMD_INIT_TXQ_IN_LEN(num) (28+8*(num)) +/* Size, in entries */ +#define MC_CMD_INIT_TXQ_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to + * INIT_EVQ. + */ +#define MC_CMD_INIT_TXQ_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_TXQ_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_TXQ_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_TXQ_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_TXQ_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_TXQ_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_IP_CSUM_DIS_LBN 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_IP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_CSUM_DIS_LBN 2 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_UDP_ONLY_LBN 3 +#define MC_CMD_INIT_TXQ_IN_FLAG_TCP_UDP_ONLY_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_CRC_MODE_LBN 4 +#define MC_CMD_INIT_TXQ_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_TXQ_IN_FLAG_TIMESTAMP_LBN 8 +#define MC_CMD_INIT_TXQ_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_PACER_BYPASS_LBN 9 +#define MC_CMD_INIT_TXQ_IN_FLAG_PACER_BYPASS_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_IP_CSUM_EN_LBN 10 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_IP_CSUM_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_TCP_CSUM_EN_LBN 11 +#define MC_CMD_INIT_TXQ_IN_FLAG_INNER_TCP_CSUM_EN_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_TXQ_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_TXQ_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_TXQ_IN_DMA_ADDR_MAXNUM 28 + +/* MC_CMD_INIT_TXQ_EXT_IN msgrequest: Extended INIT_TXQ with additional mode + * flags + */ +#define MC_CMD_INIT_TXQ_EXT_IN_LEN 544 +/* Size, in entries */ +#define MC_CMD_INIT_TXQ_EXT_IN_SIZE_OFST 0 +/* The EVQ to send events to. This is an index originally specified to + * INIT_EVQ. + */ +#define MC_CMD_INIT_TXQ_EXT_IN_TARGET_EVQ_OFST 4 +/* The value to put in the event data. Check hardware spec. for valid range. */ +#define MC_CMD_INIT_TXQ_EXT_IN_LABEL_OFST 8 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_INIT_TXQ_EXT_IN_INSTANCE_OFST 12 +/* There will be more flags here. */ +#define MC_CMD_INIT_TXQ_EXT_IN_FLAGS_OFST 16 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_BUFF_MODE_LBN 0 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_BUFF_MODE_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_IP_CSUM_DIS_LBN 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_IP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_CSUM_DIS_LBN 2 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_CSUM_DIS_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_UDP_ONLY_LBN 3 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TCP_UDP_ONLY_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_CRC_MODE_LBN 4 +#define MC_CMD_INIT_TXQ_EXT_IN_CRC_MODE_WIDTH 4 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TIMESTAMP_LBN 8 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TIMESTAMP_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_PACER_BYPASS_LBN 9 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_PACER_BYPASS_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_IP_CSUM_EN_LBN 10 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_IP_CSUM_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_TCP_CSUM_EN_LBN 11 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_TCP_CSUM_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TSOV2_EN_LBN 12 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TSOV2_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_CTPIO_LBN 13 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_CTPIO_WIDTH 1 +/* Owner ID to use if in buffer mode (zero if physical) */ +#define MC_CMD_INIT_TXQ_EXT_IN_OWNER_ID_OFST 20 +/* The port ID associated with the v-adaptor which should contain this DMAQ. */ +#define MC_CMD_INIT_TXQ_EXT_IN_PORT_ID_OFST 24 +/* 64-bit address of 4k of 4k-aligned host memory buffer */ +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_OFST 28 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_LEN 8 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_LO_OFST 28 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_HI_OFST 32 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_MINNUM 1 +#define MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_MAXNUM 64 +/* Flags related to Qbb flow control mode. */ +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_FLAGS_OFST 540 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_ENABLE_LBN 0 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_ENABLE_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_PRIORITY_LBN 1 +#define MC_CMD_INIT_TXQ_EXT_IN_QBB_PRIORITY_WIDTH 3 + +/* MC_CMD_INIT_TXQ_OUT msgresponse */ +#define MC_CMD_INIT_TXQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FINI_EVQ + * Teardown an EVQ. + * + * All DMAQs or EVQs that point to the EVQ to tear down must be torn down first + * or the operation will fail with EBUSY + */ +#define MC_CMD_FINI_EVQ 0x83 +#undef MC_CMD_0x83_PRIVILEGE_CTG + +#define MC_CMD_0x83_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FINI_EVQ_IN msgrequest */ +#define MC_CMD_FINI_EVQ_IN_LEN 4 +/* Instance of EVQ to destroy. Should be the same instance as that previously + * passed to INIT_EVQ + */ +#define MC_CMD_FINI_EVQ_IN_INSTANCE_OFST 0 + +/* MC_CMD_FINI_EVQ_OUT msgresponse */ +#define MC_CMD_FINI_EVQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FINI_RXQ + * Teardown a RXQ. + */ +#define MC_CMD_FINI_RXQ 0x84 +#undef MC_CMD_0x84_PRIVILEGE_CTG + +#define MC_CMD_0x84_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FINI_RXQ_IN msgrequest */ +#define MC_CMD_FINI_RXQ_IN_LEN 4 +/* Instance of RXQ to destroy */ +#define MC_CMD_FINI_RXQ_IN_INSTANCE_OFST 0 + +/* MC_CMD_FINI_RXQ_OUT msgresponse */ +#define MC_CMD_FINI_RXQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FINI_TXQ + * Teardown a TXQ. + */ +#define MC_CMD_FINI_TXQ 0x85 +#undef MC_CMD_0x85_PRIVILEGE_CTG + +#define MC_CMD_0x85_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FINI_TXQ_IN msgrequest */ +#define MC_CMD_FINI_TXQ_IN_LEN 4 +/* Instance of TXQ to destroy */ +#define MC_CMD_FINI_TXQ_IN_INSTANCE_OFST 0 + +/* MC_CMD_FINI_TXQ_OUT msgresponse */ +#define MC_CMD_FINI_TXQ_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_FILTER_OP + * Multiplexed MCDI call for filter operations + */ +#define MC_CMD_FILTER_OP 0x8a +#undef MC_CMD_0x8a_PRIVILEGE_CTG + +#define MC_CMD_0x8a_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FILTER_OP_IN msgrequest */ +#define MC_CMD_FILTER_OP_IN_LEN 108 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_IN_OP_OFST 0 +/* enum: single-recipient filter insert */ +#define MC_CMD_FILTER_OP_IN_OP_INSERT 0x0 +/* enum: single-recipient filter remove */ +#define MC_CMD_FILTER_OP_IN_OP_REMOVE 0x1 +/* enum: multi-recipient filter subscribe */ +#define MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE 0x2 +/* enum: multi-recipient filter unsubscribe */ +#define MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE 0x3 +/* enum: replace one recipient with another (warning - the filter handle may + * change) + */ +#define MC_CMD_FILTER_OP_IN_OP_REPLACE 0x4 +/* filter handle (for remove / unsubscribe operations) */ +#define MC_CMD_FILTER_OP_IN_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_IN_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_IN_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_IN_HANDLE_HI_OFST 8 +/* The port ID associated with the v-adaptor which should contain this filter. + */ +#define MC_CMD_FILTER_OP_IN_PORT_ID_OFST 12 +/* fields to include in match criteria */ +#define MC_CMD_FILTER_OP_IN_MATCH_FIELDS_OFST 16 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_IP_LBN 0 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_IP_LBN 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_MAC_LBN 2 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_PORT_LBN 3 +#define MC_CMD_FILTER_OP_IN_MATCH_SRC_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_MAC_LBN 4 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_PORT_LBN 5 +#define MC_CMD_FILTER_OP_IN_MATCH_DST_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_ETHER_TYPE_LBN 6 +#define MC_CMD_FILTER_OP_IN_MATCH_ETHER_TYPE_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_INNER_VLAN_LBN 7 +#define MC_CMD_FILTER_OP_IN_MATCH_INNER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_OUTER_VLAN_LBN 8 +#define MC_CMD_FILTER_OP_IN_MATCH_OUTER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_IP_PROTO_LBN 9 +#define MC_CMD_FILTER_OP_IN_MATCH_IP_PROTO_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF0_LBN 10 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF0_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF1_LBN 11 +#define MC_CMD_FILTER_OP_IN_MATCH_FWDEF1_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN 30 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN 31 +#define MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_WIDTH 1 +/* receive destination */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_OFST 20 +/* enum: drop packets */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_DROP 0x0 +/* enum: receive to host */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_HOST 0x1 +/* enum: receive to MC */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_MC 0x2 +/* enum: loop back to TXDP 0 */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_TX0 0x3 +/* enum: loop back to TXDP 1 */ +#define MC_CMD_FILTER_OP_IN_RX_DEST_TX1 0x4 +/* receive queue handle (for multiple queue modes, this is the base queue) */ +#define MC_CMD_FILTER_OP_IN_RX_QUEUE_OFST 24 +/* receive mode */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_OFST 28 +/* enum: receive to just the specified queue */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE 0x0 +/* enum: receive to multiple queues using RSS context */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_RSS 0x1 +/* enum: receive to multiple queues using .1p mapping */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_DOT1P_MAPPING 0x2 +/* enum: install a filter entry that will never match; for test purposes only + */ +#define MC_CMD_FILTER_OP_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000 +/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for + * RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or + * MC_CMD_DOT1P_MAPPING_ALLOC. + */ +#define MC_CMD_FILTER_OP_IN_RX_CONTEXT_OFST 32 +/* transmit domain (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_IN_TX_DOMAIN_OFST 36 +/* transmit destination (either set the MAC and/or PM bits for explicit + * control, or set this field to TX_DEST_DEFAULT for sensible default + * behaviour) + */ +#define MC_CMD_FILTER_OP_IN_TX_DEST_OFST 40 +/* enum: request default behaviour (based on filter type) */ +#define MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT 0xffffffff +#define MC_CMD_FILTER_OP_IN_TX_DEST_MAC_LBN 0 +#define MC_CMD_FILTER_OP_IN_TX_DEST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_IN_TX_DEST_PM_LBN 1 +#define MC_CMD_FILTER_OP_IN_TX_DEST_PM_WIDTH 1 +/* source MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_SRC_MAC_OFST 44 +#define MC_CMD_FILTER_OP_IN_SRC_MAC_LEN 6 +/* source port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_SRC_PORT_OFST 50 +#define MC_CMD_FILTER_OP_IN_SRC_PORT_LEN 2 +/* destination MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_DST_MAC_OFST 52 +#define MC_CMD_FILTER_OP_IN_DST_MAC_LEN 6 +/* destination port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_DST_PORT_OFST 58 +#define MC_CMD_FILTER_OP_IN_DST_PORT_LEN 2 +/* Ethernet type to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_ETHER_TYPE_OFST 60 +#define MC_CMD_FILTER_OP_IN_ETHER_TYPE_LEN 2 +/* Inner VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_INNER_VLAN_OFST 62 +#define MC_CMD_FILTER_OP_IN_INNER_VLAN_LEN 2 +/* Outer VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_IN_OUTER_VLAN_OFST 64 +#define MC_CMD_FILTER_OP_IN_OUTER_VLAN_LEN 2 +/* IP protocol to match (in low byte; set high byte to 0) */ +#define MC_CMD_FILTER_OP_IN_IP_PROTO_OFST 66 +#define MC_CMD_FILTER_OP_IN_IP_PROTO_LEN 2 +/* Firmware defined register 0 to match (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_IN_FWDEF0_OFST 68 +/* Firmware defined register 1 to match (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_IN_FWDEF1_OFST 72 +/* source IP address to match (as bytes in network order; set last 12 bytes to + * 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_IN_SRC_IP_OFST 76 +#define MC_CMD_FILTER_OP_IN_SRC_IP_LEN 16 +/* destination IP address to match (as bytes in network order; set last 12 + * bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_IN_DST_IP_OFST 92 +#define MC_CMD_FILTER_OP_IN_DST_IP_LEN 16 + +/* MC_CMD_FILTER_OP_EXT_IN msgrequest: Extension to MC_CMD_FILTER_OP_IN to + * include handling of VXLAN/NVGRE encapsulated frame filtering (which is + * supported on Medford only). + */ +#define MC_CMD_FILTER_OP_EXT_IN_LEN 172 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_EXT_IN_OP_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_IN/OP */ +/* filter handle (for remove / unsubscribe operations) */ +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_EXT_IN_HANDLE_HI_OFST 8 +/* The port ID associated with the v-adaptor which should contain this filter. + */ +#define MC_CMD_FILTER_OP_EXT_IN_PORT_ID_OFST 12 +/* fields to include in match criteria */ +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_FIELDS_OFST 16 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_IP_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_IP_LBN 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_MAC_LBN 2 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_PORT_LBN 3 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_MAC_LBN 4 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_PORT_LBN 5 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_ETHER_TYPE_LBN 6 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_ETHER_TYPE_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_INNER_VLAN_LBN 7 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_INNER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_OUTER_VLAN_LBN 8 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_OUTER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IP_PROTO_LBN 9 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IP_PROTO_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_FWDEF0_LBN 10 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_FWDEF0_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_VNI_OR_VSID_LBN 11 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_VNI_OR_VSID_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_IP_LBN 12 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_IP_LBN 13 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_IP_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_MAC_LBN 14 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_PORT_LBN 15 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_SRC_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_MAC_LBN 16 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_PORT_LBN 17 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_DST_PORT_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_ETHER_TYPE_LBN 18 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_ETHER_TYPE_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_INNER_VLAN_LBN 19 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_INNER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_OUTER_VLAN_LBN 20 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_OUTER_VLAN_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_IP_PROTO_LBN 21 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_IP_PROTO_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF0_LBN 22 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF0_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF1_LBN 23 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_FWDEF1_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_LBN 24 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_LBN 25 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST_LBN 30 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST_LBN 31 +#define MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST_WIDTH 1 +/* receive destination */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_OFST 20 +/* enum: drop packets */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_DROP 0x0 +/* enum: receive to host */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_HOST 0x1 +/* enum: receive to MC */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_MC 0x2 +/* enum: loop back to TXDP 0 */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX0 0x3 +/* enum: loop back to TXDP 1 */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX1 0x4 +/* receive queue handle (for multiple queue modes, this is the base queue) */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_QUEUE_OFST 24 +/* receive mode */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_OFST 28 +/* enum: receive to just the specified queue */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_SIMPLE 0x0 +/* enum: receive to multiple queues using RSS context */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_RSS 0x1 +/* enum: receive to multiple queues using .1p mapping */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_DOT1P_MAPPING 0x2 +/* enum: install a filter entry that will never match; for test purposes only + */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000 +/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for + * RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or + * MC_CMD_DOT1P_MAPPING_ALLOC. + */ +#define MC_CMD_FILTER_OP_EXT_IN_RX_CONTEXT_OFST 32 +/* transmit domain (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_EXT_IN_TX_DOMAIN_OFST 36 +/* transmit destination (either set the MAC and/or PM bits for explicit + * control, or set this field to TX_DEST_DEFAULT for sensible default + * behaviour) + */ +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_OFST 40 +/* enum: request default behaviour (based on filter type) */ +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_DEFAULT 0xffffffff +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_MAC_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_MAC_WIDTH 1 +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_PM_LBN 1 +#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_PM_WIDTH 1 +/* source MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_SRC_MAC_OFST 44 +#define MC_CMD_FILTER_OP_EXT_IN_SRC_MAC_LEN 6 +/* source port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_SRC_PORT_OFST 50 +#define MC_CMD_FILTER_OP_EXT_IN_SRC_PORT_LEN 2 +/* destination MAC address to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_DST_MAC_OFST 52 +#define MC_CMD_FILTER_OP_EXT_IN_DST_MAC_LEN 6 +/* destination port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_DST_PORT_OFST 58 +#define MC_CMD_FILTER_OP_EXT_IN_DST_PORT_LEN 2 +/* Ethernet type to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_ETHER_TYPE_OFST 60 +#define MC_CMD_FILTER_OP_EXT_IN_ETHER_TYPE_LEN 2 +/* Inner VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_INNER_VLAN_OFST 62 +#define MC_CMD_FILTER_OP_EXT_IN_INNER_VLAN_LEN 2 +/* Outer VLAN tag to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_OUTER_VLAN_OFST 64 +#define MC_CMD_FILTER_OP_EXT_IN_OUTER_VLAN_LEN 2 +/* IP protocol to match (in low byte; set high byte to 0) */ +#define MC_CMD_FILTER_OP_EXT_IN_IP_PROTO_OFST 66 +#define MC_CMD_FILTER_OP_EXT_IN_IP_PROTO_LEN 2 +/* Firmware defined register 0 to match (reserved; set to 0) */ +#define MC_CMD_FILTER_OP_EXT_IN_FWDEF0_OFST 68 +/* VNI (for VXLAN/Geneve, when IP protocol is UDP) or VSID (for NVGRE, when IP + * protocol is GRE) to match (as bytes in network order; set last byte to 0 for + * VXLAN/NVGRE, or 1 for Geneve) + */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_OR_VSID_OFST 72 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_VALUE_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_VALUE_WIDTH 24 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_LBN 24 +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_WIDTH 8 +/* enum: Match VXLAN traffic with this VNI */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_VXLAN 0x0 +/* enum: Match Geneve traffic with this VNI */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_GENEVE 0x1 +/* enum: Reserved for experimental development use */ +#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_EXPERIMENTAL 0xfe +#define MC_CMD_FILTER_OP_EXT_IN_VSID_VALUE_LBN 0 +#define MC_CMD_FILTER_OP_EXT_IN_VSID_VALUE_WIDTH 24 +#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_LBN 24 +#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_WIDTH 8 +/* enum: Match NVGRE traffic with this VSID */ +#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_NVGRE 0x0 +/* source IP address to match (as bytes in network order; set last 12 bytes to + * 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_SRC_IP_OFST 76 +#define MC_CMD_FILTER_OP_EXT_IN_SRC_IP_LEN 16 +/* destination IP address to match (as bytes in network order; set last 12 + * bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_DST_IP_OFST 92 +#define MC_CMD_FILTER_OP_EXT_IN_DST_IP_LEN 16 +/* VXLAN/NVGRE inner frame source MAC address to match (as bytes in network + * order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_MAC_OFST 108 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_MAC_LEN 6 +/* VXLAN/NVGRE inner frame source port to match (as bytes in network order) */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_PORT_OFST 114 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_PORT_LEN 2 +/* VXLAN/NVGRE inner frame destination MAC address to match (as bytes in + * network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_MAC_OFST 116 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_MAC_LEN 6 +/* VXLAN/NVGRE inner frame destination port to match (as bytes in network + * order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_PORT_OFST 122 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_PORT_LEN 2 +/* VXLAN/NVGRE inner frame Ethernet type to match (as bytes in network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_ETHER_TYPE_OFST 124 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_ETHER_TYPE_LEN 2 +/* VXLAN/NVGRE inner frame Inner VLAN tag to match (as bytes in network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_INNER_VLAN_OFST 126 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_INNER_VLAN_LEN 2 +/* VXLAN/NVGRE inner frame Outer VLAN tag to match (as bytes in network order) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_OUTER_VLAN_OFST 128 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_OUTER_VLAN_LEN 2 +/* VXLAN/NVGRE inner frame IP protocol to match (in low byte; set high byte to + * 0) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_IP_PROTO_OFST 130 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_IP_PROTO_LEN 2 +/* VXLAN/NVGRE inner frame Firmware defined register 0 to match (reserved; set + * to 0) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_FWDEF0_OFST 132 +/* VXLAN/NVGRE inner frame Firmware defined register 1 to match (reserved; set + * to 0) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_FWDEF1_OFST 136 +/* VXLAN/NVGRE inner frame source IP address to match (as bytes in network + * order; set last 12 bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_IP_OFST 140 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_SRC_IP_LEN 16 +/* VXLAN/NVGRE inner frame destination IP address to match (as bytes in network + * order; set last 12 bytes to 0 for IPv4 address) + */ +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_IP_OFST 156 +#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_IP_LEN 16 + +/* MC_CMD_FILTER_OP_OUT msgresponse */ +#define MC_CMD_FILTER_OP_OUT_LEN 12 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_OUT_OP_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_IN/OP */ +/* Returned filter handle (for insert / subscribe operations). Note that these + * handles should be considered opaque to the host, although a value of + * 0xFFFFFFFF_FFFFFFFF is guaranteed never to be a valid handle. + */ +#define MC_CMD_FILTER_OP_OUT_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_OUT_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_OFST 8 +/* enum: guaranteed invalid filter handle (low 32 bits) */ +#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_INVALID 0xffffffff +/* enum: guaranteed invalid filter handle (high 32 bits) */ +#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_INVALID 0xffffffff + +/* MC_CMD_FILTER_OP_EXT_OUT msgresponse */ +#define MC_CMD_FILTER_OP_EXT_OUT_LEN 12 +/* identifies the type of operation requested */ +#define MC_CMD_FILTER_OP_EXT_OUT_OP_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_EXT_IN/OP */ +/* Returned filter handle (for insert / subscribe operations). Note that these + * handles should be considered opaque to the host, although a value of + * 0xFFFFFFFF_FFFFFFFF is guaranteed never to be a valid handle. + */ +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_OFST 4 +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_LEN 8 +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_LO_OFST 4 +#define MC_CMD_FILTER_OP_EXT_OUT_HANDLE_HI_OFST 8 +/* Enum values, see field(s): */ +/* MC_CMD_FILTER_OP_OUT/HANDLE */ + + +/***********************************/ +/* MC_CMD_ALLOC_VIS + * Allocate VIs for current PCI function. + */ +#define MC_CMD_ALLOC_VIS 0x8b +#undef MC_CMD_0x8b_PRIVILEGE_CTG + +#define MC_CMD_0x8b_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_ALLOC_VIS_IN msgrequest */ +#define MC_CMD_ALLOC_VIS_IN_LEN 8 +/* The minimum number of VIs that is acceptable */ +#define MC_CMD_ALLOC_VIS_IN_MIN_VI_COUNT_OFST 0 +/* The maximum number of VIs that would be useful */ +#define MC_CMD_ALLOC_VIS_IN_MAX_VI_COUNT_OFST 4 + +/* MC_CMD_ALLOC_VIS_OUT msgresponse: Huntington-compatible VI_ALLOC request. + * Use extended version in new code. + */ +#define MC_CMD_ALLOC_VIS_OUT_LEN 8 +/* The number of VIs allocated on this function */ +#define MC_CMD_ALLOC_VIS_OUT_VI_COUNT_OFST 0 +/* The base absolute VI number allocated to this function. Required to + * correctly interpret wakeup events. + */ +#define MC_CMD_ALLOC_VIS_OUT_VI_BASE_OFST 4 + +/* MC_CMD_ALLOC_VIS_EXT_OUT msgresponse */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_LEN 12 +/* The number of VIs allocated on this function */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_VI_COUNT_OFST 0 +/* The base absolute VI number allocated to this function. Required to + * correctly interpret wakeup events. + */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_VI_BASE_OFST 4 +/* Function's port vi_shift value (always 0 on Huntington) */ +#define MC_CMD_ALLOC_VIS_EXT_OUT_VI_SHIFT_OFST 8 + + +/***********************************/ +/* MC_CMD_FREE_VIS + * Free VIs for current PCI function. Any linked PIO buffers will be unlinked, + * but not freed. + */ +#define MC_CMD_FREE_VIS 0x8c +#undef MC_CMD_0x8c_PRIVILEGE_CTG + +#define MC_CMD_0x8c_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_FREE_VIS_IN msgrequest */ +#define MC_CMD_FREE_VIS_IN_LEN 0 + +/* MC_CMD_FREE_VIS_OUT msgresponse */ +#define MC_CMD_FREE_VIS_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_GET_PORT_ASSIGNMENT + * Get port assignment for current PCI function. + */ +#define MC_CMD_GET_PORT_ASSIGNMENT 0xb8 +#undef MC_CMD_0xb8_PRIVILEGE_CTG + +#define MC_CMD_0xb8_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_GET_PORT_ASSIGNMENT_IN msgrequest */ +#define MC_CMD_GET_PORT_ASSIGNMENT_IN_LEN 0 + +/* MC_CMD_GET_PORT_ASSIGNMENT_OUT msgresponse */ +#define MC_CMD_GET_PORT_ASSIGNMENT_OUT_LEN 4 +/* Identifies the port assignment for this function. */ +#define MC_CMD_GET_PORT_ASSIGNMENT_OUT_PORT_OFST 0 + + +/***********************************/ +/* MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS + * Configure UDP ports for tunnel encapsulation hardware acceleration. The + * parser-dispatcher will attempt to parse traffic on these ports as tunnel + * encapsulation PDUs and filter them using the tunnel encapsulation filter + * chain rather than the standard filter chain. Note that this command can + * cause all functions to see a reset. (Available on Medford only.) + */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS 0x117 +#undef MC_CMD_0x117_PRIVILEGE_CTG + +#define MC_CMD_0x117_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN msgrequest */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMIN 4 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX 68 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LEN(num) (4+4*(num)) +/* Flags */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS_OFST 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS_LEN 2 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING_LBN 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING_WIDTH 1 +/* The number of entries in the ENTRIES array */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES_OFST 2 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES_LEN 2 +/* Entries defining the UDP port to protocol mapping, each laid out as a + * TUNNEL_ENCAP_UDP_PORT_ENTRY + */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_OFST 4 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_LEN 4 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MINNUM 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM 16 + +/* MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT msgresponse */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN 2 +/* Flags */ +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS_OFST 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS_LEN 2 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_LBN 0 +#define MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_WIDTH 1 + + +#endif /* SFC_MCDI_PCOL_H */ diff --git a/src/drivers/net/sfc/mcdi.h b/src/drivers/net/sfc/mcdi.h new file mode 100644 index 000000000..19c62021a --- /dev/null +++ b/src/drivers/net/sfc/mcdi.h @@ -0,0 +1,164 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * + * Written by Martin Habets + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + */ +#ifndef SFC_MCDI_H +#define SFC_MCDI_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +#define MCDI_SEQ_MASK 0xf + +/* We expect that 16- and 32-bit fields in MCDI requests and responses + * are appropriately aligned, but 64-bit fields are only + * 32-bit-aligned. Also, on Siena we must copy to the MC shared + * memory strictly 32 bits at a time, so add any necessary padding. + */ +#define MCDI_DECLARE_BUF(_name, _len) \ + efx_dword_t _name[DIV_ROUND_UP(_len, 4)] +#define MCDI_DECLARE_BUF_OUT_OR_ERR(_name, _len) \ + MCDI_DECLARE_BUF(_name, max_t(size_t, _len, 8)) +#define _MCDI_PTR(_buf, _offset) \ + ((u8 *)(_buf) + (_offset)) +#define MCDI_PTR(_buf, _field) \ + _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST) +#define _MCDI_CHECK_ALIGN(_ofst, _align) \ + ((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1))) +#define _MCDI_DWORD(_buf, _field) \ + ((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2)) + +#define MCDI_WORD(_buf, _field) \ + ((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ + le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) +#define MCDI_SET_DWORD(_buf, _field, _value) \ + EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value) +#define MCDI_DWORD(_buf, _field) \ + EFX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0) +#define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1) \ + EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1) +#define MCDI_POPULATE_DWORD_2(_buf, _field, _name1, _value1, \ + _name2, _value2) \ + EFX_POPULATE_DWORD_2(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2) +#define MCDI_POPULATE_DWORD_3(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3) \ + EFX_POPULATE_DWORD_3(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3) +#define MCDI_POPULATE_DWORD_4(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4) \ + EFX_POPULATE_DWORD_4(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4) +#define MCDI_POPULATE_DWORD_5(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4, _name5, _value5) \ + EFX_POPULATE_DWORD_5(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4, \ + MC_CMD_ ## _name5, _value5) +#define MCDI_POPULATE_DWORD_6(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4, _name5, _value5, \ + _name6, _value6) \ + EFX_POPULATE_DWORD_6(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4, \ + MC_CMD_ ## _name5, _value5, \ + MC_CMD_ ## _name6, _value6) +#define MCDI_POPULATE_DWORD_7(_buf, _field, _name1, _value1, \ + _name2, _value2, _name3, _value3, \ + _name4, _value4, _name5, _value5, \ + _name6, _value6, _name7, _value7) \ + EFX_POPULATE_DWORD_7(*_MCDI_DWORD(_buf, _field), \ + MC_CMD_ ## _name1, _value1, \ + MC_CMD_ ## _name2, _value2, \ + MC_CMD_ ## _name3, _value3, \ + MC_CMD_ ## _name4, _value4, \ + MC_CMD_ ## _name5, _value5, \ + MC_CMD_ ## _name6, _value6, \ + MC_CMD_ ## _name7, _value7) +#define MCDI_SET_QWORD(_buf, _field, _value) \ + do { \ + EFX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[0], \ + EFX_DWORD_0, (u32)(_value)); \ + EFX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[1], \ + EFX_DWORD_0, (u64)(_value) >> 32); \ + } while (0) +#define MCDI_QWORD(_buf, _field) \ + (EFX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[0], EFX_DWORD_0) | \ + (u64)EFX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[1], EFX_DWORD_0) << 32) +#define MCDI_FIELD(_ptr, _type, _field) \ + EFX_EXTRACT_DWORD( \ + *(efx_dword_t *) \ + _MCDI_PTR(_ptr, MC_CMD_ ## _type ## _ ## _field ## _OFST & ~3),\ + MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f, \ + (MC_CMD_ ## _type ## _ ## _field ## _LBN & 0x1f) + \ + MC_CMD_ ## _type ## _ ## _field ## _WIDTH - 1) + +#define _MCDI_ARRAY_PTR(_buf, _field, _index, _align) \ + (_MCDI_PTR(_buf, _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, _align))\ + + (_index) * _MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _LEN, _align)) +#define MCDI_DECLARE_STRUCT_PTR(_name) \ + efx_dword_t *_name +#define MCDI_ARRAY_STRUCT_PTR(_buf, _field, _index) \ + ((efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4)) +#define MCDI_VAR_ARRAY_LEN(_len, _field) \ + min_t(size_t, MC_CMD_ ## _field ## _MAXNUM, \ + ((_len) - MC_CMD_ ## _field ## _OFST) / MC_CMD_ ## _field ## _LEN) +#define MCDI_ARRAY_WORD(_buf, _field, _index) \ + (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ + le16_to_cpu(*(__force const __le16 *) \ + _MCDI_ARRAY_PTR(_buf, _field, _index, 2))) +#define _MCDI_ARRAY_DWORD(_buf, _field, _index) \ + (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 4) + \ + (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4)) +#define MCDI_SET_ARRAY_DWORD(_buf, _field, _index, _value) \ + EFX_SET_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), \ + EFX_DWORD_0, _value) +#define MCDI_ARRAY_DWORD(_buf, _field, _index) \ + EFX_DWORD_FIELD(*_MCDI_ARRAY_DWORD(_buf, _field, _index), EFX_DWORD_0) +#define _MCDI_ARRAY_QWORD(_buf, _field, _index) \ + (BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 8) + \ + (efx_dword_t *)_MCDI_ARRAY_PTR(_buf, _field, _index, 4)) +#define MCDI_SET_ARRAY_QWORD(_buf, _field, _index, _value) \ + do { \ + EFX_SET_DWORD_FIELD(_MCDI_ARRAY_QWORD(_buf, _field, _index)[0],\ + EFX_DWORD_0, (u32)(_value)); \ + EFX_SET_DWORD_FIELD(_MCDI_ARRAY_QWORD(_buf, _field, _index)[1],\ + EFX_DWORD_0, (u64)(_value) >> 32); \ + } while (0) +#define MCDI_ARRAY_FIELD(_buf, _field1, _type, _index, _field2) \ + MCDI_FIELD(MCDI_ARRAY_STRUCT_PTR(_buf, _field1, _index), \ + _type ## _TYPEDEF, _field2) + +#define MCDI_EVENT_FIELD(_ev, _field) \ + EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) + +#endif diff --git a/src/drivers/net/sfc/sfc_hunt.c b/src/drivers/net/sfc/sfc_hunt.c new file mode 100644 index 000000000..25780ffd8 --- /dev/null +++ b/src/drivers/net/sfc/sfc_hunt.c @@ -0,0 +1,1324 @@ +/************************************************************************** + * + * Device driver for Solarflare Communications EF10 devices + * + * Written by Shradha Shah + * + * Copyright 2012-2017 Solarflare Communications Inc. + * + * 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. + * + * 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. + * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efx_hunt.h" +#include "efx_bitfield.h" +#include "ef10_regs.h" +#include "mc_driver_pcol.h" +#include + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define HUNTINGTON_NVRAM_CHUNK 0x80 +#define HUNTINGTON_NVS_MAX_LENGTH 0x1000 + +#define EMCDI_IO(code) EUNIQ(EINFO_EIO, (code)) + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#endif + +struct hunt_nic *primary_nics = NULL; + +struct hunt_nic { + struct efx_nic efx; + + /* PHY information */ + unsigned int phy_cap_mask; + unsigned int phy_cap; + unsigned long link_poll_timer; + + /* resource housekeeping */ + uint64_t uc_filter_id; + uint64_t mc_filter_id; + u8 mac[ETH_ALEN]; + + struct { + /* Common payload for all MCDI requests */ + unsigned int seqno; + + size_t resp_hdr_len; + size_t resp_data_len; + + struct io_buffer *iob; + uint64_t dma_addr; + } mcdi; + + struct hunt_nic *primary; + struct hunt_nic *next_primary; + u32 flags; +}; + +static int hunt_nic_is_primary(struct hunt_nic *hunt) +{ + return (hunt->flags & (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY)); +} + +/******************************************************************************* + * + * + * MCDI transport + * + * This has been based on the implementation of MCDI in the common code driver. + * + * + ******************************************************************************/ + +static int hunt_mcdi_init(struct hunt_nic *hunt) +{ + size_t max_msg_size; + int rc; + + /* The MCDI message has two 32-bit headers (the MCDI header and the + * MCDI v2 extended command) and then up to MCDI_CTL_SDU_LEN_MAX_V2 + * bytes of payload + */ + max_msg_size = 2 * sizeof(efx_dword_t) + MCDI_CTL_SDU_LEN_MAX_V2; + + hunt->mcdi.iob = alloc_iob(max_msg_size); + if (!hunt->mcdi.iob) { + rc = -ENOMEM; + return rc; + } + return 0; +} + +static void hunt_mcdi_copyin(struct hunt_nic *hunt, + unsigned int cmd, + uint8_t *inbuf, + size_t inlen) +{ + efx_dword_t hdr[2]; + uint32_t seqno; + unsigned int xflags; + size_t hdr_len; + u8 *pdu = hunt->mcdi.iob->data; + + seqno = hunt->mcdi.seqno & MCDI_SEQ_MASK; + + xflags = 0; + + EFX_POPULATE_DWORD_7(hdr[0], + MCDI_HEADER_CODE, MC_CMD_V2_EXTN, + MCDI_HEADER_RESYNC, 1, + MCDI_HEADER_DATALEN, 0, + MCDI_HEADER_SEQ, seqno, + MCDI_HEADER_ERROR, 0, + MCDI_HEADER_RESPONSE, 0, + MCDI_HEADER_XFLAGS, xflags); + EFX_POPULATE_DWORD_2(hdr[1], + MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd, + MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen); + + hdr_len = sizeof(hdr); + + memcpy(pdu, &hdr, hdr_len); + memcpy(pdu + hdr_len, inbuf, inlen); + + wmb(); /* Sync the data before ringing the doorbell */ + + /* Ring the doorbell to post the command DMA address to the MC */ + hunt->mcdi.dma_addr = virt_to_bus(hunt->mcdi.iob->data); + + assert((hunt->mcdi.dma_addr & 0xFF) == 0); + + _efx_writel(&hunt->efx, + cpu_to_le32((u64)hunt->mcdi.dma_addr >> 32), + ER_DZ_MC_DB_LWRD); + + _efx_writel(&hunt->efx, + cpu_to_le32((u32)hunt->mcdi.dma_addr), + ER_DZ_MC_DB_HWRD); +} + +static void hunt_mcdi_copyout(struct hunt_nic *hunt, + uint8_t *outbuf, size_t outlen) +{ + size_t offset; + const u8 *pdu = hunt->mcdi.iob->data; + + offset = hunt->mcdi.resp_hdr_len; + + if (outlen > 0) + memcpy(outbuf, pdu+offset, outlen); +} + +static int hunt_mcdi_request_poll(struct hunt_nic *hunt, bool quiet) +{ + unsigned int resplen, respseq, error; + unsigned long finish; + efx_dword_t errdword; + efx_qword_t qword; + const efx_dword_t *pdu = hunt->mcdi.iob->data; + const u8 *pdu1 = hunt->mcdi.iob->data; + int delay, rc; + + /* Spin for up to 5s, polling at intervals of 10us, 20us, ... ~100ms */ + finish = currticks() + (5 * TICKS_PER_SEC); + delay = 10; + while (1) { + udelay(delay); + + /* Check for an MCDI response */ + if (EFX_DWORD_FIELD(*pdu, MCDI_HEADER_RESPONSE)) + break; + + if (currticks() >= finish) + return -ETIMEDOUT; + + if (delay < 100000) + delay *= 2; + } + + memcpy(&qword, pdu1, 8); + + /* qword.dword[0] is the MCDI header; qword.dword[1] is the MCDI v2 + * extended command + */ + respseq = EFX_DWORD_FIELD(qword.dword[0], MCDI_HEADER_SEQ); + error = EFX_DWORD_FIELD(qword.dword[0], MCDI_HEADER_ERROR); + resplen = EFX_DWORD_FIELD(qword.dword[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN); + + if (error && resplen == 0) { + if (!quiet) + DBGC(hunt, "MC rebooted\n"); + return -EIO; + } else if ((respseq ^ hunt->mcdi.seqno) & MCDI_SEQ_MASK) { + if (!quiet) + DBGC(hunt, "MC response mismatch rxseq 0x%x txseq " + "0x%x\n", respseq, hunt->mcdi.seqno); + return -EIO; + } else if (error) { + memcpy(&errdword, pdu1 + 8, 4); + rc = EFX_DWORD_FIELD(errdword, EFX_DWORD_0); + switch (rc) { + case MC_CMD_ERR_ENOENT: + return -ENOENT; + case MC_CMD_ERR_EINTR: + return -EINTR; + case MC_CMD_ERR_EACCES: + return -EACCES; + case MC_CMD_ERR_EBUSY: + return -EBUSY; + case MC_CMD_ERR_EINVAL: + return -EINVAL; + case MC_CMD_ERR_EDEADLK: + return -EDEADLK; + case MC_CMD_ERR_ENOSYS: + return -ENOSYS; + case MC_CMD_ERR_ETIME: + return -ETIME; + case MC_CMD_ERR_EPERM: + return -EPERM; + default: + /* Return the MC error in an I/O error. */ + return EMCDI_IO(rc & 0xff); + } + } + hunt->mcdi.resp_hdr_len = 8; + hunt->mcdi.resp_data_len = resplen; + + return 0; +} + +static void hunt_mcdi_fini(struct hunt_nic *hunt) +{ + free_iob(hunt->mcdi.iob); +} + +int _hunt_mcdi(struct efx_nic *efx, unsigned int cmd, + const efx_dword_t *inbuf, size_t inlen, + efx_dword_t *outbuf, size_t outlen, + size_t *outlen_actual, bool quiet) +{ + int rc; + struct hunt_nic *hunt = (struct hunt_nic *) efx; + size_t local_outlen_actual; + + if (outlen_actual == NULL) + outlen_actual = &local_outlen_actual; + + ++hunt->mcdi.seqno; + hunt_mcdi_copyin(hunt, cmd, (uint8_t *) inbuf, inlen); + + rc = hunt_mcdi_request_poll(hunt, quiet); + if (rc != 0) { + if (!quiet) + DBGC(hunt, "MC response to cmd 0x%x: %s\n", + cmd, strerror(rc)); + return rc; + } + + *outlen_actual = hunt->mcdi.resp_data_len; + + hunt_mcdi_copyout(hunt, (uint8_t *) outbuf, outlen); + + return 0; +} + +static int hunt_mcdi(struct hunt_nic *hunt, struct efx_mcdi_req_s *req) +{ + return _hunt_mcdi(&hunt->efx, req->emr_cmd, + (const efx_dword_t *) req->emr_in_buf, + req->emr_in_length, + (efx_dword_t *) req->emr_out_buf, req->emr_out_length, + &req->emr_out_length_used, false); +} + +static int hunt_mcdi_quiet(struct hunt_nic *hunt, struct efx_mcdi_req_s *req) +{ + return _hunt_mcdi(&hunt->efx, req->emr_cmd, + (const efx_dword_t *) req->emr_in_buf, + req->emr_in_length, + (efx_dword_t *) req->emr_out_buf, req->emr_out_length, + &req->emr_out_length_used, true); +} + +/******************************************************************************* + * + * + * Hardware initialization + * + * + ******************************************************************************/ +static int hunt_get_workarounds(struct hunt_nic *hunt, uint32_t *implemented, + uint32_t *enabled) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_WORKAROUNDS_OUT_LEN); + int rc; + + *implemented = *enabled = 0; + + req.emr_cmd = MC_CMD_GET_WORKAROUNDS; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + + if (rc) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_WORKAROUNDS_OUT_LEN) + return -EMSGSIZE; + + *implemented = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_IMPLEMENTED); + *enabled = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_ENABLED); + return 0; +} + +static int hunt_enable_workaround_35388(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN); + + req.emr_cmd = MC_CMD_WORKAROUND; + req.emr_in_buf = payload; + req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, WORKAROUND_IN_TYPE, + MC_CMD_WORKAROUND_BUG35388); + MCDI_SET_DWORD(req.emr_in_buf, WORKAROUND_IN_ENABLED, 1); + + /* If the firmware doesn't support this workaround, hunt_mcdi() will + * return -EINVAL from hunt_mcdi_request_poll(). + */ + return hunt_mcdi(hunt, &req); +} + +static int hunt_workaround_35388(struct hunt_nic *hunt) +{ + uint32_t implemented, enabled; + int rc = hunt_get_workarounds(hunt, &implemented, &enabled); + + if (rc < 0) + return 0; + if (!(implemented & MC_CMD_GET_WORKAROUNDS_OUT_BUG35388)) + return 0; + if (enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG35388) + return 1; + + rc = hunt_enable_workaround_35388(hunt); + if (rc == 0) + return 1; /* Workaround is enabled */ + else + return 0; +} + +static int hunt_get_port_assignment(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PORT_ASSIGNMENT_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_PORT_ASSIGNMENT; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + hunt->efx.port = MCDI_DWORD(req.emr_out_buf, + GET_PORT_ASSIGNMENT_OUT_PORT); + return 0; +} + +static int hunt_mac_addr(struct hunt_nic *hunt, uint8_t *ll_addr) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_MAC_ADDRESSES; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = MC_CMD_GET_MAC_ADDRESSES_OUT_LEN; + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_MAC_ADDRESSES_OUT_LEN) + return -EMSGSIZE; + + memcpy(ll_addr, + MCDI_PTR(req.emr_out_buf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE), + ETH_ALEN); + + return 0; +} + +static int hunt_get_phy_cfg(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_CFG_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_PHY_CFG; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) + return -EMSGSIZE; + + hunt->phy_cap_mask = hunt->phy_cap = + MCDI_DWORD(req.emr_out_buf, GET_PHY_CFG_OUT_SUPPORTED_CAP); + DBGC2(hunt, "GET_PHY_CFG: flags=%x, caps=%x\n", rc, hunt->phy_cap); + return 0; +} + +static int hunt_driver_attach(struct hunt_nic *hunt, int attach) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_DRV_ATTACH_IN_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_EXT_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_DRV_ATTACH; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + /* Set the PREBOOT flag to indicate later instances of attach should + * force an ENTITY RESET + */ + if (attach) + attach |= 1 << MC_CMD_DRV_PREBOOT_LBN; + + MCDI_SET_DWORD(req.emr_in_buf, DRV_ATTACH_IN_NEW_STATE, attach); + MCDI_SET_DWORD(req.emr_in_buf, DRV_ATTACH_IN_UPDATE, 1); + MCDI_SET_DWORD(req.emr_in_buf, DRV_ATTACH_IN_FIRMWARE_ID, + MC_CMD_FW_DONT_CARE); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) + return -EMSGSIZE; + + hunt->flags = MCDI_DWORD(outbuf, DRV_ATTACH_EXT_OUT_FUNC_FLAGS); + + return 0; +} + +static int hunt_reset(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_ENTITY_RESET_IN_LEN); + + req.emr_cmd = MC_CMD_ENTITY_RESET; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_POPULATE_DWORD_1(req.emr_in_buf, ENTITY_RESET_IN_FLAG, + ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET, 1); + return hunt_mcdi(hunt, &req); +} + +static void hunt_clear_udp_tunnel_ports(struct hunt_nic *hunt) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX); + MCDI_DECLARE_BUF(outbuf, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN); + struct efx_mcdi_req_s req; + int rc; + + memset(inbuf, 0, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX); + MCDI_SET_DWORD(inbuf, SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS, + (1 << MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING_LBN)); + + req.emr_cmd = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi_quiet(hunt, &req); + if (rc) + return; + + if (MCDI_DWORD(outbuf, SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS) & + (1 << MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_LBN)) { + DBGC(hunt, + "Rebooting MC due to clearing UDP tunnel port list\n"); + /* Delay for the MC reboot to complete. */ + mdelay(100); + } +} + +static int hunt_set_mac(struct hunt_nic *hunt) +{ + struct net_device *netdev = hunt->efx.netdev; + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(payload, MC_CMD_SET_MAC_IN_LEN); + unsigned int fcntl; + int rc; + + req.emr_cmd = MC_CMD_SET_MAC; + req.emr_in_buf = payload; + req.emr_in_length = MC_CMD_SET_MAC_IN_LEN; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_MTU, + EFX_MAC_FRAME_LEN(ETH_FRAME_LEN)); + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_DRAIN, 0); + memcpy(MCDI_PTR(req.emr_in_buf, SET_MAC_IN_ADDR), + netdev->ll_addr, ETH_ALEN); + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_REJECT, 0); + + /* If the PHY supports autnegotiation, then configure the MAC to match + * the negotiated settings. Otherwise force the MAC to TX and RX flow + * control. + */ + if (hunt->phy_cap_mask & (1 << MC_CMD_PHY_CAP_AN_LBN)) + fcntl = MC_CMD_FCNTL_AUTO; + else + fcntl = MC_CMD_FCNTL_BIDIR; + MCDI_SET_DWORD(req.emr_in_buf, SET_MAC_IN_FCNTL, fcntl); + + rc = hunt_mcdi(hunt, &req); + /* Ignore failure for permissions reasons */ + if (rc == -EPERM) + rc = 0; + return rc; +} + +static int hunt_alloc_vis(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_ALLOC_VIS_IN_LEN); + + req.emr_cmd = MC_CMD_ALLOC_VIS; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, ALLOC_VIS_IN_MIN_VI_COUNT, 1); + MCDI_SET_DWORD(req.emr_in_buf, ALLOC_VIS_IN_MAX_VI_COUNT, 1); + + return hunt_mcdi(hunt, &req); +} + +static void hunt_free_vis(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + int rc; + + req.emr_cmd = MC_CMD_FREE_VIS; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FREE_VIS Failed\n"); +} + +/******************************************************************************* + * + * + * Link state handling + * + * + ******************************************************************************/ +static int hunt_check_link(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN); + unsigned int flags, speed; + bool up; + int rc; + static bool link_state = false; + + req.emr_cmd = MC_CMD_GET_LINK; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_LEN) + return -EMSGSIZE; + + flags = MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_FLAGS); + up = !!(flags & (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN)); + speed = MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_LINK_SPEED); + + /* Set netdev_link_*() based on the link status from the MC */ + if (up && speed) + netdev_link_up(hunt->efx.netdev); + else + netdev_link_down(hunt->efx.netdev); + + if (up != link_state) { + DBGC(hunt, "Link %s, flags=%x, our caps=%x, lpa=%x, speed=%d, fcntl=%x, mac_fault=%x\n", + (up? "up": "down"), flags, + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_CAP), + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_LP_CAP), + speed, + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_FCNTL), + MCDI_DWORD(req.emr_out_buf, GET_LINK_OUT_MAC_FAULT)); + link_state = up; + } + + return 0; +} + +#define MCDI_PORT_SPEED_CAPS ((1 << MC_CMD_PHY_CAP_10HDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_10FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_100HDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_100FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_1000HDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_1000FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_10000FDX_LBN) | \ + (1 << MC_CMD_PHY_CAP_40000FDX_LBN)) + +/******************************************************************************* + * + * + * TX + * + * + ******************************************************************************/ +static int +hunt_tx_init(struct net_device *netdev, struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + dma_addr_t dma_addr; + efx_qword_t *addr; + MCDI_DECLARE_BUF(inbuf, + MC_CMD_INIT_TXQ_IN_LEN(EFX_TXQ_NBUFS(EFX_TXD_SIZE))); + int rc, npages; + + rc = efx_hunt_tx_init(netdev, &dma_addr); + if (rc != 0) + return rc; + + npages = EFX_TXQ_NBUFS(EFX_TXD_SIZE); + + req.emr_cmd = MC_CMD_INIT_TXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_INIT_TXQ_IN_LEN(npages); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_SIZE, EFX_TXD_SIZE); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_TARGET_EVQ, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_LABEL, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_INSTANCE, 0); + + MCDI_POPULATE_DWORD_6(req.emr_in_buf, INIT_TXQ_IN_FLAGS, + INIT_TXQ_IN_FLAG_BUFF_MODE, 0, + INIT_TXQ_IN_FLAG_IP_CSUM_DIS, 1, + INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, 1, + INIT_TXQ_IN_FLAG_TCP_UDP_ONLY, 0, + INIT_TXQ_IN_CRC_MODE, 0, + INIT_TXQ_IN_FLAG_TIMESTAMP, 0); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_OWNER_ID, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_TXQ_IN_PORT_ID, + EVB_PORT_ID_ASSIGNED); + + addr = (efx_qword_t *) MCDI_PTR(req.emr_in_buf, INIT_TXQ_IN_DMA_ADDR); + + EFX_POPULATE_QWORD_2(*addr, + EFX_DWORD_1, (uint32_t)(dma_addr >> 32), + EFX_DWORD_0, (uint32_t)(dma_addr & 0xffffffff)); + + return hunt_mcdi(hunt, &req); +} + +static void hunt_tx_fini(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_TXQ_IN_LEN); + struct efx_nic *efx = &hunt->efx; + struct efx_tx_queue *txq = &efx->txq; + int rc; + + req.emr_cmd = MC_CMD_FINI_TXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FINI_TXQ_IN_INSTANCE, 0); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FINI_TXQ Failed\n"); + + efx_hunt_free_special_buffer(txq->ring, + sizeof(efx_tx_desc_t) * EFX_TXD_SIZE); + txq->ring = NULL; +} + +/******************************************************************************* + * + * + * RX + * + * + ******************************************************************************/ +static int hunt_rx_filter_insert(struct net_device *netdev, + struct hunt_nic *hunt, + int multicast) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_IN_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_FILTER_OP_OUT_LEN); + int rc; + uint64_t filter_id; + (void) netdev; + + req.emr_cmd = MC_CMD_FILTER_OP; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_OP, + multicast ? MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE + : MC_CMD_FILTER_OP_IN_OP_INSERT); + MCDI_POPULATE_DWORD_1(req.emr_in_buf, FILTER_OP_IN_MATCH_FIELDS, + FILTER_OP_IN_MATCH_DST_MAC, 1); + if (multicast) + memset(MCDI_PTR(req.emr_in_buf, FILTER_OP_IN_DST_MAC), + 0xff, ETH_ALEN); + else + memcpy(MCDI_PTR(req.emr_in_buf, FILTER_OP_IN_DST_MAC), + hunt->mac, ETH_ALEN); + + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_PORT_ID, + EVB_PORT_ID_ASSIGNED); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_RX_DEST, + MC_CMD_FILTER_OP_IN_RX_DEST_HOST); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_RX_QUEUE, 0); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_RX_MODE, 0); + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_TX_DEST, + MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_FILTER_OP_OUT_LEN) + return -EIO; + + filter_id = MCDI_QWORD(req.emr_out_buf, FILTER_OP_OUT_HANDLE); + if (multicast) + hunt->mc_filter_id = filter_id; + else + hunt->uc_filter_id = filter_id; + + return 0; +} + +static int hunt_rx_filter_remove(struct hunt_nic *hunt, + int multicast) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_IN_LEN); + + req.emr_cmd = MC_CMD_FILTER_OP; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FILTER_OP_IN_OP, + multicast ? MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE : + MC_CMD_FILTER_OP_IN_OP_REMOVE); + MCDI_SET_QWORD(req.emr_in_buf, FILTER_OP_IN_HANDLE, + multicast ? hunt->mc_filter_id : + hunt->uc_filter_id); + return hunt_mcdi(hunt, &req); +} + +static int hunt_get_mac(struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN); + int rc; + + req.emr_cmd = MC_CMD_GET_MAC_ADDRESSES; + req.emr_in_buf = NULL; + req.emr_in_length = 0; + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_INIT_EVQ_OUT_LEN) + return -EMSGSIZE; + + memcpy(hunt->mac, MCDI_PTR(outbuf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE), + ETH_ALEN); + return 0; +} + +static int hunt_rx_filter_init(struct net_device *netdev, + struct hunt_nic *hunt) +{ + int rc = hunt_get_mac(hunt); + + if (rc != 0) + return rc; + + rc = hunt_rx_filter_insert(netdev, hunt, 0); + if (rc != 0) + return rc; + + rc = hunt_rx_filter_insert(netdev, hunt, 1); + if (rc != 0) + hunt_rx_filter_remove(hunt, 0); + + return rc; +} + +static int +hunt_rx_init(struct net_device *netdev, + struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + dma_addr_t dma_addr; + efx_qword_t *addr; + MCDI_DECLARE_BUF(inbuf, + MC_CMD_INIT_RXQ_IN_LEN(EFX_RXQ_NBUFS(EFX_RXD_SIZE))); + int rc, npages; + + rc = efx_hunt_rx_init(netdev, &dma_addr); + if (rc != 0) + return rc; + + npages = EFX_RXQ_NBUFS(EFX_RXD_SIZE); + + req.emr_cmd = MC_CMD_INIT_RXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_INIT_RXQ_IN_LEN(npages); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_SIZE, EFX_RXD_SIZE); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_TARGET_EVQ, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_LABEL, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_INSTANCE, 0); + MCDI_POPULATE_DWORD_5(req.emr_in_buf, INIT_RXQ_IN_FLAGS, + INIT_RXQ_IN_FLAG_BUFF_MODE, 0, + INIT_RXQ_IN_FLAG_HDR_SPLIT, 0, + INIT_RXQ_IN_FLAG_TIMESTAMP, 0, + INIT_RXQ_IN_CRC_MODE, 0, + INIT_RXQ_IN_FLAG_PREFIX, 1); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_OWNER_ID, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_RXQ_IN_PORT_ID, + EVB_PORT_ID_ASSIGNED); + + addr = (efx_qword_t *) MCDI_PTR(req.emr_in_buf, INIT_RXQ_IN_DMA_ADDR); + + EFX_POPULATE_QWORD_2(*addr, + EFX_DWORD_1, (uint32_t)(dma_addr >> 32), + EFX_DWORD_0, (uint32_t)(dma_addr & 0xffffffff)); + return hunt_mcdi(hunt, &req); +} + +static void hunt_rx_filter_fini(struct hunt_nic *hunt) +{ + hunt_rx_filter_remove(hunt, 0); + hunt_rx_filter_remove(hunt, 1); +} + +static void hunt_rx_fini(struct hunt_nic *hunt) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_RXQ_IN_LEN); + struct efx_mcdi_req_s req; + struct efx_nic *efx = &hunt->efx; + struct efx_rx_queue *rxq = &efx->rxq; + int rc; + + req.emr_cmd = MC_CMD_FINI_RXQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_FINI_RXQ_IN_LEN; + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FINI_RXQ_IN_INSTANCE, 0); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FINI_RXQ Failed\n"); + + efx_hunt_free_special_buffer(rxq->ring, + sizeof(efx_rx_desc_t) * EFX_RXD_SIZE); + rxq->ring = NULL; +} + +/******************************************************************************* + * + * + * Event queues and interrupts + * + * + ******************************************************************************/ +static int +hunt_ev_init(struct net_device *netdev, + struct hunt_nic *hunt) +{ + struct efx_mcdi_req_s req; + dma_addr_t dma_addr; + efx_qword_t *addr; + MCDI_DECLARE_BUF(inbuf, + MC_CMD_INIT_EVQ_IN_LEN(EFX_EVQ_NBUFS(EFX_EVQ_SIZE))); + MCDI_DECLARE_BUF(outbuf, MC_CMD_INIT_EVQ_OUT_LEN); + int rc, npages; + + rc = efx_hunt_ev_init(netdev, &dma_addr); + if (rc != 0) + return rc; + + npages = EFX_EVQ_NBUFS(EFX_EVQ_SIZE); + + req.emr_cmd = MC_CMD_INIT_EVQ; + req.emr_in_buf = inbuf; + req.emr_in_length = MC_CMD_INIT_EVQ_IN_LEN(npages); + req.emr_out_buf = outbuf; + req.emr_out_length = sizeof(outbuf); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_SIZE, EFX_EVQ_SIZE); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_INSTANCE, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_IRQ_NUM, 0); + + MCDI_POPULATE_DWORD_6(req.emr_in_buf, INIT_EVQ_IN_FLAGS, + INIT_EVQ_IN_FLAG_INTERRUPTING, 1, + INIT_EVQ_IN_FLAG_RPTR_DOS, 0, + INIT_EVQ_IN_FLAG_INT_ARMD, 0, + INIT_EVQ_IN_FLAG_CUT_THRU, 0, + INIT_EVQ_IN_FLAG_RX_MERGE, 0, + INIT_EVQ_IN_FLAG_TX_MERGE, 0); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_TMR_MODE, + MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_TMR_LOAD, 0); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_TMR_RELOAD, 0); + + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_COUNT_MODE, + MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS); + MCDI_SET_DWORD(req.emr_in_buf, INIT_EVQ_IN_COUNT_THRSHLD, 0); + + addr = (efx_qword_t *) MCDI_PTR(req.emr_in_buf, INIT_EVQ_IN_DMA_ADDR); + + EFX_POPULATE_QWORD_2(*addr, + EFX_DWORD_1, (uint32_t)(dma_addr >> 32), + EFX_DWORD_0, (uint32_t)(dma_addr & 0xffffffff)); + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + return rc; + + if (req.emr_out_length_used < MC_CMD_INIT_EVQ_OUT_LEN) + return -EMSGSIZE; + + return 0; +} + +static void hunt_ev_fini(struct hunt_nic *hunt) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_EVQ_IN_LEN); + struct efx_mcdi_req_s req; + struct efx_nic *efx = &hunt->efx; + struct efx_ev_queue *evq = &efx->evq; + int rc; + + req.emr_cmd = MC_CMD_FINI_EVQ; + req.emr_in_buf = inbuf; + req.emr_in_length = sizeof(inbuf); + req.emr_out_buf = NULL; + req.emr_out_length = 0; + + MCDI_SET_DWORD(req.emr_in_buf, FINI_EVQ_IN_INSTANCE, 0); + + rc = hunt_mcdi(hunt, &req); + if (rc != 0) + DBGC(hunt, "MC_CMD_FINI_EVQ Failed\n"); + + efx_hunt_free_special_buffer(evq->ring, + sizeof(efx_event_t) * EFX_EVQ_SIZE); + evq->ring = NULL; +} + +static void +hunt_poll(struct net_device *netdev) +{ + struct hunt_nic *hunt = netdev_priv(netdev); + + /* If called while already polling, return immediately */ + if (hunt->efx.state & EFX_STATE_POLLING) + return; + hunt->efx.state |= EFX_STATE_POLLING; + + /* Poll link state */ + if (hunt->link_poll_timer + TICKS_PER_SEC < currticks()) { + hunt->link_poll_timer = currticks(); + hunt_check_link(hunt); + } + + /* Poll data path */ + efx_hunt_poll(netdev); + + hunt->efx.state &= ~EFX_STATE_POLLING; +} + +/******************************************************************************* + * + * + * Netdevice operations + * + * + ******************************************************************************/ +static int hunt_open(struct net_device *netdev) +{ + struct hunt_nic *hunt = netdev_priv(netdev); + int rc; + + /* Allocate VIs */ + rc = hunt_alloc_vis(hunt); + if (rc != 0) + goto fail2; + + /* Initialize data path */ + rc = hunt_ev_init(netdev, hunt); + if (rc != 0) + goto fail3; + + rc = hunt_rx_init(netdev, hunt); + if (rc != 0) + goto fail4; + + rc = hunt_rx_filter_init(netdev, hunt); + if (rc != 0) + goto fail5; + + rc = hunt_tx_init(netdev, hunt); + if (rc != 0) + goto fail6; + + rc = efx_hunt_open(netdev); + if (rc) + goto fail7; + + rc = hunt_set_mac(hunt); + if (rc) + goto fail8; + + /* Mark the link as down before checking the link state because the + * latter might fail. + */ + netdev_link_down(netdev); + hunt_check_link(hunt); + + DBGC2(hunt, "%s: open ok\n", netdev->name); + return 0; + +fail8: + efx_hunt_close(netdev); +fail7: + hunt_tx_fini(hunt); +fail6: + hunt_rx_filter_fini(hunt); +fail5: + hunt_rx_fini(hunt); +fail4: + hunt_ev_fini(hunt); +fail3: + hunt_free_vis(hunt); +fail2: + DBGC2(hunt, "%s: %s\n", netdev->name, strerror(rc)); + return rc; +} + + +static void hunt_close(struct net_device *netdev) +{ + struct hunt_nic *hunt = netdev_priv(netdev); + + /* Stop datapath */ + efx_hunt_close(netdev); + + hunt_tx_fini(hunt); + hunt_rx_fini(hunt); + hunt_rx_filter_fini(hunt); + hunt_ev_fini(hunt); + + hunt_free_vis(hunt); + + /* Reset hardware and detach */ + hunt_reset(hunt); +} + + +/******************************************************************************* + * + * + * Public operations + * + * + ******************************************************************************/ + +static struct net_device_operations hunt_operations = { + .open = hunt_open, + .close = hunt_close, + .transmit = efx_hunt_transmit, + .poll = hunt_poll, + .irq = efx_hunt_irq, +}; + +static int +hunt_probe(struct pci_device *pci) +{ + struct net_device *netdev; + struct hunt_nic *hunt; + struct efx_nic *efx; + int rc = 0; + + /* Create the network adapter */ + netdev = alloc_etherdev(sizeof(struct hunt_nic)); + if (!netdev) { + rc = -ENOMEM; + goto fail1; + } + + /* Initialise the network adapter, and initialise private storage */ + netdev_init(netdev, &hunt_operations); + pci_set_drvdata(pci, netdev); + netdev->dev = &pci->dev; + netdev->state |= NETDEV_IRQ_UNSUPPORTED; + + hunt = netdev_priv(netdev); + memset(hunt, 0, sizeof(*hunt)); + efx = &hunt->efx; + + efx->type = &hunt_nic_type; + + /* Initialise efx datapath */ + efx_probe(netdev, EFX_HUNTINGTON); + + /* Initialise MCDI. In case we are recovering from a crash, first + * cancel any outstanding request by sending a special message using the + * least significant bits of the 'high' (doorbell) register. + */ + _efx_writel(efx, cpu_to_le32(1), ER_DZ_MC_DB_HWRD); + rc = hunt_mcdi_init(hunt); + if (rc != 0) + goto fail2; + + /* Reset (most) configuration for this function */ + rc = hunt_reset(hunt); + if (rc != 0) + goto fail3; + + /* Medford has a list of UDP tunnel ports that is populated by the + * driver. Avoid dropping any unencapsulated packets. This may cause + * an MC reboot. + */ + hunt_clear_udp_tunnel_ports(hunt); + + /* Enable the workaround for bug35388, if supported */ + efx->workaround_35388 = hunt_workaround_35388(hunt); + + /* Set the RX packet prefix size */ + efx->rx_prefix_size = ES_DZ_RX_PREFIX_SIZE; + + rc = hunt_get_port_assignment(hunt); + if (rc != 0) + goto fail3; + + rc = hunt_mac_addr(hunt, netdev->ll_addr); + if (rc != 0) + goto fail4; + + rc = hunt_get_phy_cfg(hunt); + if (rc != 0) + goto fail5; + + rc = hunt_driver_attach(hunt, 1); + if (rc != 0) + goto fail5; + + /* If not exposing this network device, return successfully here */ + if (hunt->flags & (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT)) + return 0; + + if (hunt_nic_is_primary(hunt)) { + hunt->next_primary = primary_nics; + primary_nics = hunt; + hunt->primary = hunt; + } else { + struct hunt_nic *other_hunt = primary_nics; + + while (other_hunt && !hunt->primary) { + struct pci_device *other_pci = (struct pci_device *) + other_hunt->efx.netdev->dev; + /* Check if the seg:bus:dev parts match. */ + if (PCI_FIRST_FUNC(other_pci->busdevfn) == + PCI_FIRST_FUNC(pci->busdevfn)) + hunt->primary = other_hunt; + + other_hunt = other_hunt->next_primary; + } + if (!hunt->primary) { + rc = -EIO; + goto fail6; + } + } + + rc = register_netdev(netdev); + if (rc != 0) + goto fail8; + + DBG2("%s " PCI_FMT " ok\n", __func__, PCI_ARGS(pci)); + return 0; + +fail8: +fail6: + (void) hunt_driver_attach(hunt, 0); +fail5: +fail4: +fail3: + hunt_mcdi_fini(hunt); +fail2: + efx_remove(netdev); + netdev_put(netdev); +fail1: + DBG2("%s " PCI_FMT " rc=%d\n", __func__, PCI_ARGS(pci), rc); + return rc; +} + +static void hunt_remove(struct pci_device *pci) +{ + struct net_device *netdev = pci_get_drvdata(pci); + struct hunt_nic *hunt = netdev_priv(netdev); + + if (!(hunt->flags & + (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT))) { + /* The netdevice might still be open, so unregister it now + * before ripping stuff out from underneath. + */ + unregister_netdev(netdev); + } + + (void)hunt_driver_attach(hunt, 0); + hunt_mcdi_fini(hunt); + + /* Destroy data path */ + efx_remove(netdev); + + netdev_nullify(netdev); + netdev_put(netdev); +} + +const struct efx_nic_type hunt_nic_type = { + .mcdi_rpc = _hunt_mcdi, +}; + +static struct pci_device_id hunt_nics[] = { + PCI_ROM(0x1924, 0x0a03, "SFC9220", "Solarflare SFN8xxx Adapter", 0), +}; + +struct pci_driver hunt_driver __pci_driver = { + .ids = hunt_nics, + .id_count = ARRAY_SIZE(hunt_nics), + .probe = hunt_probe, + .remove = hunt_remove, +}; diff --git a/src/drivers/net/sis190.c b/src/drivers/net/sis190.c index 991c30f9e..b92e95f2a 100644 --- a/src/drivers/net/sis190.c +++ b/src/drivers/net/sis190.c @@ -72,12 +72,6 @@ struct pci_driver sis190_isa_bridge_driver __pci_driver = { static const u32 sis190_intr_mask = RxQEmpty | RxQInt | TxQ1Int | TxQ0Int | RxHalt | TxHalt | LinkChange; -/* - * Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - * The chips use a 64 element hash table based on the Ethernet CRC. - */ -static const int multicast_filter_limit = 32; - static void __mdio_cmd(void *ioaddr, u32 ctl) { unsigned int i; @@ -971,8 +965,8 @@ static int sis190_get_mac_addr_from_apc(struct pci_device *pdev, list_for_each_entry(d, &(pdev->dev.siblings), siblings) { unsigned int i; - isa_bridge = container_of(d, struct pci_device, dev); for(i = 0; i < sis190_isa_bridge_driver.id_count; i++) { + isa_bridge = container_of(d, struct pci_device, dev); if(isa_bridge->vendor == sis190_isa_bridge_driver.ids[i].vendor && isa_bridge->device == diff --git a/src/drivers/net/sis190.h b/src/drivers/net/sis190.h index 0551333d5..79f94d2d9 100644 --- a/src/drivers/net/sis190.h +++ b/src/drivers/net/sis190.h @@ -297,13 +297,6 @@ static struct mii_chip_info { { NULL, { 0x00, 0x00 }, 0, 0 } }; -static const struct { - const char *name; -} sis_chip_info[] = { - { "SiS 190 PCI Fast Ethernet adapter" }, - { "SiS 191 PCI Gigabit Ethernet adapter" }, -}; - static void sis190_phy_task(struct sis190_private *tp); static void sis190_free(struct net_device *dev); static inline void sis190_init_rxfilter(struct net_device *dev); diff --git a/src/drivers/net/sis900.c b/src/drivers/net/sis900.c index 724515672..8a3ac01bc 100644 --- a/src/drivers/net/sis900.c +++ b/src/drivers/net/sis900.c @@ -249,7 +249,7 @@ static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic * MAC address is read into @net_dev->dev_addr. */ -static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic) +static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic __unused) { #if 0 u8 reg; @@ -279,7 +279,6 @@ static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic #endif /* Does not work with current bus/device model */ - memset ( nic->node_addr, 0, sizeof ( nic->node_addr ) ); return 0; } diff --git a/src/drivers/net/skeleton.c b/src/drivers/net/skeleton.c index 0435b9d0e..0bae3089c 100644 --- a/src/drivers/net/skeleton.c +++ b/src/drivers/net/skeleton.c @@ -34,7 +34,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include "skeleton.h" /** @file @@ -43,54 +42,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/****************************************************************************** - * - * MII interface - * - ****************************************************************************** - */ - -/** - * Read from MII register - * - * @v mii MII interface - * @v reg Register address - * @ret value Data read, or negative error - */ -static int skeleton_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct skeleton_nic *skel = - container_of ( mii, struct skeleton_nic, mii ); - - DBGC ( skel, "SKELETON %p does not yet support MII read\n", skel ); - ( void ) reg; - return -ENOTSUP; -} - -/** - * Write to MII register - * - * @v mii MII interface - * @v reg Register address - * @v data Data to write - * @ret rc Return status code - */ -static int skeleton_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data) { - struct skeleton_nic *skel = - container_of ( mii, struct skeleton_nic, mii ); - - DBGC ( skel, "SKELETON %p does not yet support MII write\n", skel ); - ( void ) reg; - ( void ) data; - return -ENOTSUP; -} - -/** Skeleton MII operations */ -static struct mii_operations skeleton_mii_operations = { - .read = skeleton_mii_read, - .write = skeleton_mii_write, -}; - /****************************************************************************** * * Device reset @@ -254,14 +205,6 @@ static int skeleton_probe ( struct pci_device *pci ) { if ( ( rc = skeleton_reset ( skel ) ) != 0 ) goto err_reset; - /* Initialise and reset MII interface */ - mii_init ( &skel->mii, &skeleton_mii_operations ); - if ( ( rc = mii_reset ( &skel->mii ) ) != 0 ) { - DBGC ( skel, "SKELETON %p could not reset MII: %s\n", - skel, strerror ( rc ) ); - goto err_mii_reset; - } - /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; @@ -273,7 +216,6 @@ static int skeleton_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: - err_mii_reset: skeleton_reset ( skel ); err_reset: iounmap ( skel->regs ); diff --git a/src/drivers/net/skeleton.h b/src/drivers/net/skeleton.h index 2ab01bd56..030922808 100644 --- a/src/drivers/net/skeleton.h +++ b/src/drivers/net/skeleton.h @@ -16,8 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct skeleton_nic { /** Registers */ void *regs; - /** MII interface */ - struct mii_interface mii; }; #endif /* _SKELETON_H */ diff --git a/src/drivers/net/skge.c b/src/drivers/net/skge.c index 6384e7647..c3264225b 100755 --- a/src/drivers/net/skge.c +++ b/src/drivers/net/skge.c @@ -84,9 +84,6 @@ static struct net_device_operations skge_operations = { /* Avoid conditionals by using array */ static const int txqaddr[] = { Q_XA1, Q_XA2 }; static const int rxqaddr[] = { Q_R1, Q_R2 }; -static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F }; -static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F }; -static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F }; static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 }; /* Determine supported/advertised modes based on hardware. @@ -1922,8 +1919,6 @@ static void skge_tx_clean(struct net_device *dev) skge->tx_ring.to_clean = e; } -static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 }; - static inline u16 phy_length(const struct skge_hw *hw, u32 status) { if (hw->chip_id == CHIP_ID_GENESIS) diff --git a/src/drivers/net/skge.h b/src/drivers/net/skge.h index d9e91457a..b13013bba 100755 --- a/src/drivers/net/skge.h +++ b/src/drivers/net/skge.h @@ -65,9 +65,6 @@ FILE_LICENCE ( GPL2_ONLY ); #define SUPPORTED_TP (1 << 7) #define SUPPORTED_FIBRE (1 << 10) -/* from kernel.h */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - /* ----------------------------------- */ #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ diff --git a/src/drivers/net/sky2.c b/src/drivers/net/sky2.c index 35ff66c6d..211f22466 100644 --- a/src/drivers/net/sky2.c +++ b/src/drivers/net/sky2.c @@ -246,7 +246,7 @@ static void sky2_power_aux(struct sky2_hw *hw) Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); /* switch power to VAUX */ - if (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL) + if (sky2_read32(hw, B0_CTST) & Y2_VAUX_AVAIL) sky2_write8(hw, B0_POWER_CTRL, (PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF)); diff --git a/src/drivers/net/sky2.h b/src/drivers/net/sky2.h index 9bb63010e..9c3313128 100644 --- a/src/drivers/net/sky2.h +++ b/src/drivers/net/sky2.h @@ -294,7 +294,7 @@ enum csr_regs { Y2_CFG_AER = 0x1d00, /* PCI Advanced Error Report region */ }; -/* B0_CTST 16 bit Control/Status register */ +/* B0_CTST 24 bit Control/Status register */ enum { Y2_VMAIN_AVAIL = 1<<17,/* VMAIN available (YUKON-2 only) */ Y2_VAUX_AVAIL = 1<<16,/* VAUX available (YUKON-2 only) */ diff --git a/src/drivers/net/smsc75xx.c b/src/drivers/net/smsc75xx.c index 5e4e0e12b..861669edf 100644 --- a/src/drivers/net/smsc75xx.c +++ b/src/drivers/net/smsc75xx.c @@ -39,10 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Interrupt completion profiler */ -static struct profiler smsc75xx_intr_profiler __profiler = - { .name = "smsc75xx.intr" }; - /** Bulk IN completion profiler */ static struct profiler smsc75xx_in_profiler __profiler = { .name = "smsc75xx.in" }; @@ -51,363 +47,6 @@ static struct profiler smsc75xx_in_profiler __profiler = static struct profiler smsc75xx_out_profiler __profiler = { .name = "smsc75xx.out" }; -/****************************************************************************** - * - * Register access - * - ****************************************************************************** - */ - -/** - * Write register (without byte-swapping) - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static int smsc75xx_raw_writel ( struct smsc75xx_device *smsc75xx, - unsigned int address, uint32_t value ) { - int rc; - - /* Write register */ - if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_WRITE, 0, - address, &value, sizeof ( value ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not write %03x: %s\n", - smsc75xx, address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Write register - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @v value Register value - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -smsc75xx_writel ( struct smsc75xx_device *smsc75xx, unsigned int address, - uint32_t value ) { - int rc; - - /* Write register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, address, - cpu_to_le32 ( value ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Read register (without byte-swapping) - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static int smsc75xx_raw_readl ( struct smsc75xx_device *smsc75xx, - unsigned int address, uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_READ, 0, - address, value, sizeof ( *value ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not read %03x: %s\n", - smsc75xx, address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Read register - * - * @v smsc75xx SMSC75xx device - * @v address Register address - * @ret value Register value - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -smsc75xx_readl ( struct smsc75xx_device *smsc75xx, unsigned int address, - uint32_t *value ) { - int rc; - - /* Read register */ - if ( ( rc = smsc75xx_raw_readl ( smsc75xx, address, value ) ) != 0 ) - return rc; - le32_to_cpus ( value ); - - return 0; -} - -/****************************************************************************** - * - * EEPROM access - * - ****************************************************************************** - */ - -/** - * Wait for EEPROM to become idle - * - * @v smsc75xx SMSC75xx device - * @ret rc Return status code - */ -static int smsc75xx_eeprom_wait ( struct smsc75xx_device *smsc75xx ) { - uint32_t e2p_cmd; - unsigned int i; - int rc; - - /* Wait for EPC_BSY to become clear */ - for ( i = 0 ; i < SMSC75XX_EEPROM_MAX_WAIT_MS ; i++ ) { - - /* Read E2P_CMD and check EPC_BSY */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_CMD, - &e2p_cmd ) ) != 0 ) - return rc; - if ( ! ( e2p_cmd & SMSC75XX_E2P_CMD_EPC_BSY ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for EEPROM\n", - smsc75xx ); - return -ETIMEDOUT; -} - -/** - * Read byte from EEPROM - * - * @v smsc75xx SMSC75xx device - * @v address EEPROM address - * @ret byte Byte read, or negative error - */ -static int smsc75xx_eeprom_read_byte ( struct smsc75xx_device *smsc75xx, - unsigned int address ) { - uint32_t e2p_cmd; - uint32_t e2p_data; - int rc; - - /* Wait for EEPROM to become idle */ - if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Initiate read command */ - e2p_cmd = ( SMSC75XX_E2P_CMD_EPC_BSY | SMSC75XX_E2P_CMD_EPC_CMD_READ | - SMSC75XX_E2P_CMD_EPC_ADDR ( address ) ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_E2P_CMD, - e2p_cmd ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Read EEPROM data */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_DATA, - &e2p_data ) ) != 0 ) - return rc; - - return SMSC75XX_E2P_DATA_GET ( e2p_data ); -} - -/** - * Read data from EEPROM - * - * @v smsc75xx SMSC75xx device - * @v address EEPROM address - * @v data Data buffer - * @v len Length of data - * @ret rc Return status code - */ -static int smsc75xx_eeprom_read ( struct smsc75xx_device *smsc75xx, - unsigned int address, void *data, - size_t len ) { - uint8_t *bytes; - int byte; - - /* Read bytes */ - for ( bytes = data ; len-- ; address++, bytes++ ) { - byte = smsc75xx_eeprom_read_byte ( smsc75xx, address ); - if ( byte < 0 ) - return byte; - *bytes = byte; - } - - return 0; -} - -/****************************************************************************** - * - * MII access - * - ****************************************************************************** - */ - -/** - * Wait for MII to become idle - * - * @v smsc75xx SMSC75xx device - * @ret rc Return status code - */ -static int smsc75xx_mii_wait ( struct smsc75xx_device *smsc75xx ) { - uint32_t mii_access; - unsigned int i; - int rc; - - /* Wait for MIIBZY to become clear */ - for ( i = 0 ; i < SMSC75XX_MII_MAX_WAIT_MS ; i++ ) { - - /* Read MII_ACCESS and check MIIBZY */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_ACCESS, - &mii_access ) ) != 0 ) - return rc; - if ( ! ( mii_access & SMSC75XX_MII_ACCESS_MIIBZY ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for MII\n", - smsc75xx ); - return -ETIMEDOUT; -} - -/** - * Read from MII register - * - * @v mii MII interface - * @v reg Register address - * @ret value Data read, or negative error - */ -static int smsc75xx_mii_read ( struct mii_interface *mii, unsigned int reg ) { - struct smsc75xx_device *smsc75xx = - container_of ( mii, struct smsc75xx_device, mii ); - uint32_t mii_access; - uint32_t mii_data; - int rc; - - /* Wait for MII to become idle */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Initiate read command */ - mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | - SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | - SMSC75XX_MII_ACCESS_MIIBZY ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, - mii_access ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Read MII data */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_DATA, - &mii_data ) ) != 0 ) - return rc; - - return SMSC75XX_MII_DATA_GET ( mii_data ); -} - -/** - * Write to MII register - * - * @v mii MII interface - * @v reg Register address - * @v data Data to write - * @ret rc Return status code - */ -static int smsc75xx_mii_write ( struct mii_interface *mii, unsigned int reg, - unsigned int data ) { - struct smsc75xx_device *smsc75xx = - container_of ( mii, struct smsc75xx_device, mii ); - uint32_t mii_access; - uint32_t mii_data; - int rc; - - /* Wait for MII to become idle */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - /* Write MII data */ - mii_data = SMSC75XX_MII_DATA_SET ( data ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_DATA, - mii_data ) ) != 0 ) - return rc; - - /* Initiate write command */ - mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | - SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | - SMSC75XX_MII_ACCESS_MIIWNR | - SMSC75XX_MII_ACCESS_MIIBZY ); - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, - mii_access ) ) != 0 ) - return rc; - - /* Wait for command to complete */ - if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) - return rc; - - return 0; -} - -/** MII operations */ -static struct mii_operations smsc75xx_mii_operations = { - .read = smsc75xx_mii_read, - .write = smsc75xx_mii_write, -}; - -/** - * Check link status - * - * @v smsc75xx SMSC75xx device - * @ret rc Return status code - */ -static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) { - struct net_device *netdev = smsc75xx->netdev; - int intr; - int rc; - - /* Read PHY interrupt source */ - intr = mii_read ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE ); - if ( intr < 0 ) { - rc = intr; - DBGC ( smsc75xx, "SMSC75XX %p could not get PHY interrupt " - "source: %s\n", smsc75xx, strerror ( rc ) ); - return rc; - } - - /* Acknowledge PHY interrupt */ - if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE, - intr ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not acknowledge PHY " - "interrupt: %s\n", smsc75xx, strerror ( rc ) ); - return rc; - } - - /* Check link status */ - if ( ( rc = mii_check_link ( &smsc75xx->mii, netdev ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not check link: %s\n", - smsc75xx, strerror ( rc ) ); - return rc; - } - - DBGC ( smsc75xx, "SMSC75XX %p link %s (intr %#04x)\n", - smsc75xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); - return 0; -} - /****************************************************************************** * * Statistics (for debugging) @@ -415,35 +54,13 @@ static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) { ****************************************************************************** */ -/** - * Get statistics - * - * @v smsc75xx SMSC75xx device - * @v stats Statistics to fill in - * @ret rc Return status code - */ -static int smsc75xx_get_statistics ( struct smsc75xx_device *smsc75xx, - struct smsc75xx_statistics *stats ) { - int rc; - - /* Get statistics */ - if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_GET_STATISTICS, 0, 0, - stats, sizeof ( *stats ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not get statistics: %s\n", - smsc75xx, strerror ( rc ) ); - return rc; - } - - return 0; -} - /** * Dump statistics (for debugging) * - * @v smsc75xx SMSC75xx device + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { +int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ) { struct smsc75xx_statistics stats; int rc; @@ -452,29 +69,33 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { return 0; /* Get statistics */ - if ( ( rc = smsc75xx_get_statistics ( smsc75xx, &stats ) ) != 0 ) + if ( ( rc = smscusb_get_statistics ( smscusb, 0, &stats, + sizeof ( stats ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC75XX %p could not get statistics: " + "%s\n", smscusb, strerror ( rc ) ); return rc; + } /* Dump statistics */ - DBGC ( smsc75xx, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d " - "ovr %d drp %d\n", smsc75xx, le32_to_cpu ( stats.rx.err.fcs ), + DBGC ( smscusb, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d " + "ovr %d drp %d\n", smscusb, le32_to_cpu ( stats.rx.err.fcs ), le32_to_cpu ( stats.rx.err.alignment ), le32_to_cpu ( stats.rx.err.fragment ), le32_to_cpu ( stats.rx.err.jabber ), le32_to_cpu ( stats.rx.err.undersize ), le32_to_cpu ( stats.rx.err.oversize ), le32_to_cpu ( stats.rx.err.dropped ) ); - DBGC ( smsc75xx, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n", - smsc75xx, le32_to_cpu ( stats.rx.byte.unicast ), + DBGC ( smscusb, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n", + smscusb, le32_to_cpu ( stats.rx.byte.unicast ), le32_to_cpu ( stats.rx.byte.broadcast ), le32_to_cpu ( stats.rx.byte.multicast ) ); - DBGC ( smsc75xx, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause " - "%d\n", smsc75xx, le32_to_cpu ( stats.rx.frame.unicast ), + DBGC ( smscusb, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause " + "%d\n", smscusb, le32_to_cpu ( stats.rx.frame.unicast ), le32_to_cpu ( stats.rx.frame.broadcast ), le32_to_cpu ( stats.rx.frame.multicast ), le32_to_cpu ( stats.rx.frame.pause ) ); - DBGC ( smsc75xx, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d " - "mul %d exc %d lat %d\n", smsc75xx, + DBGC ( smscusb, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d " + "mul %d exc %d lat %d\n", smscusb, le32_to_cpu ( stats.tx.err.fcs ), le32_to_cpu ( stats.tx.err.deferral ), le32_to_cpu ( stats.tx.err.carrier ), @@ -483,12 +104,12 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { le32_to_cpu ( stats.tx.err.multiple ), le32_to_cpu ( stats.tx.err.excessive ), le32_to_cpu ( stats.tx.err.late ) ); - DBGC ( smsc75xx, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n", - smsc75xx, le32_to_cpu ( stats.tx.byte.unicast ), + DBGC ( smscusb, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n", + smscusb, le32_to_cpu ( stats.tx.byte.unicast ), le32_to_cpu ( stats.tx.byte.broadcast ), le32_to_cpu ( stats.tx.byte.multicast ) ); - DBGC ( smsc75xx, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause " - "%d\n", smsc75xx, le32_to_cpu ( stats.tx.frame.unicast ), + DBGC ( smscusb, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause " + "%d\n", smscusb, le32_to_cpu ( stats.tx.frame.unicast ), le32_to_cpu ( stats.tx.frame.broadcast ), le32_to_cpu ( stats.tx.frame.multicast ), le32_to_cpu ( stats.tx.frame.pause ) ); @@ -506,31 +127,36 @@ static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { /** * Reset device * - * @v smsc75xx SMSC75xx device + * @v smscusb SMSC USB device * @ret rc Return status code */ -static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { +static int smsc75xx_reset ( struct smscusb_device *smscusb ) { uint32_t hw_cfg; + unsigned int i; int rc; /* Reset device */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, - SMSC75XX_HW_CFG_LRST ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_LRST ) ) != 0 ) return rc; /* Wait for reset to complete */ - udelay ( SMSC75XX_RESET_DELAY_US ); + for ( i = 0 ; i < SMSC75XX_RESET_MAX_WAIT_MS ; i++ ) { - /* Check that reset has completed */ - if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG, - &hw_cfg ) ) != 0 ) - return rc; - if ( hw_cfg & SMSC75XX_HW_CFG_LRST ) { - DBGC ( smsc75xx, "SMSC75XX %p failed to reset\n", smsc75xx ); - return -ETIMEDOUT; + /* Check if reset has completed */ + if ( ( rc = smscusb_readl ( smscusb, SMSC75XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( ! ( hw_cfg & SMSC75XX_HW_CFG_LRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); } - return 0; + DBGC ( smscusb, "SMSC75XX %p timed out waiting for reset\n", + smscusb ); + return -ETIMEDOUT; } /****************************************************************************** @@ -540,60 +166,6 @@ static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { ****************************************************************************** */ -/** - * Complete interrupt transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void smsc75xx_intr_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct smsc75xx_device *smsc75xx = - container_of ( ep, struct smsc75xx_device, usbnet.intr ); - struct net_device *netdev = smsc75xx->netdev; - struct smsc75xx_interrupt *intr; - - /* Profile completions */ - profile_start ( &smsc75xx_intr_profiler ); - - /* Ignore packets cancelled when the endpoint closes */ - if ( ! ep->open ) - goto done; - - /* Record USB errors against the network device */ - if ( rc != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p interrupt failed: %s\n", - smsc75xx, strerror ( rc ) ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); - netdev_rx_err ( netdev, NULL, rc ); - goto done; - } - - /* Extract interrupt data */ - if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { - DBGC ( smsc75xx, "SMSC75XX %p malformed interrupt\n", - smsc75xx ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); - netdev_rx_err ( netdev, NULL, rc ); - goto done; - } - intr = iobuf->data; - - /* Record interrupt status */ - smsc75xx->int_sts = le32_to_cpu ( intr->int_sts ); - profile_stop ( &smsc75xx_intr_profiler ); - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); -} - -/** Interrupt endpoint operations */ -static struct usb_endpoint_driver_operations smsc75xx_intr_operations = { - .complete = smsc75xx_intr_complete, -}; - /** * Complete bulk IN transfer * @@ -603,9 +175,9 @@ static struct usb_endpoint_driver_operations smsc75xx_intr_operations = { */ static void smsc75xx_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { - struct smsc75xx_device *smsc75xx = - container_of ( ep, struct smsc75xx_device, usbnet.in ); - struct net_device *netdev = smsc75xx->netdev; + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.in ); + struct net_device *netdev = smscusb->netdev; struct smsc75xx_rx_header *header; /* Profile completions */ @@ -619,16 +191,16 @@ static void smsc75xx_in_complete ( struct usb_endpoint *ep, /* Record USB errors against the network device */ if ( rc != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p bulk IN failed: %s\n", - smsc75xx, strerror ( rc ) ); + DBGC ( smscusb, "SMSC75XX %p bulk IN failed: %s\n", + smscusb, strerror ( rc ) ); goto err; } /* Sanity check */ if ( iob_len ( iobuf ) < ( sizeof ( *header ) ) ) { - DBGC ( smsc75xx, "SMSC75XX %p underlength bulk IN\n", - smsc75xx ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( smscusb, "SMSC75XX %p underlength bulk IN\n", + smscusb ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EINVAL; goto err; } @@ -639,9 +211,9 @@ static void smsc75xx_in_complete ( struct usb_endpoint *ep, /* Check for errors */ if ( header->command & cpu_to_le32 ( SMSC75XX_RX_RED ) ) { - DBGC ( smsc75xx, "SMSC75XX %p receive error (%08x):\n", - smsc75xx, le32_to_cpu ( header->command ) ); - DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( smscusb, "SMSC75XX %p receive error (%08x):\n", + smscusb, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EIO; goto err; } @@ -658,18 +230,18 @@ static void smsc75xx_in_complete ( struct usb_endpoint *ep, } /** Bulk IN endpoint operations */ -static struct usb_endpoint_driver_operations smsc75xx_in_operations = { +struct usb_endpoint_driver_operations smsc75xx_in_operations = { .complete = smsc75xx_in_complete, }; /** * Transmit packet * - * @v smsc75xx SMSC75xx device + * @v smscusb SMSC USB device * @v iobuf I/O buffer * @ret rc Return status code */ -static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx, +static int smsc75xx_out_transmit ( struct smscusb_device *smscusb, struct io_buffer *iobuf ) { struct smsc75xx_tx_header *header; size_t len = iob_len ( iobuf ); @@ -687,35 +259,13 @@ static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx, header->mss = 0; /* Enqueue I/O buffer */ - if ( ( rc = usb_stream ( &smsc75xx->usbnet.out, iobuf, 0 ) ) != 0 ) + if ( ( rc = usb_stream ( &smscusb->usbnet.out, iobuf, 0 ) ) != 0 ) return rc; profile_stop ( &smsc75xx_out_profiler ); return 0; } -/** - * Complete bulk OUT transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void smsc75xx_out_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct smsc75xx_device *smsc75xx = - container_of ( ep, struct smsc75xx_device, usbnet.out ); - struct net_device *netdev = smsc75xx->netdev; - - /* Report TX completion */ - netdev_tx_complete_err ( netdev, iobuf, rc ); -} - -/** Bulk OUT endpoint operations */ -static struct usb_endpoint_driver_operations smsc75xx_out_operations = { - .complete = smsc75xx_out_complete, -}; - /****************************************************************************** * * Network device interface @@ -730,110 +280,86 @@ static struct usb_endpoint_driver_operations smsc75xx_out_operations = { * @ret rc Return status code */ static int smsc75xx_open ( struct net_device *netdev ) { - struct smsc75xx_device *smsc75xx = netdev->priv; - union smsc75xx_mac mac; + struct smscusb_device *smscusb = netdev->priv; int rc; /* Clear stored interrupt status */ - smsc75xx->int_sts = 0; - - /* Copy MAC address */ - memset ( &mac, 0, sizeof ( mac ) ); - memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + smscusb->int_sts = 0; /* Configure bulk IN empty response */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, - SMSC75XX_HW_CFG_BIR ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_BIR ) ) != 0 ) goto err_hw_cfg; /* Open USB network device */ - if ( ( rc = usbnet_open ( &smsc75xx->usbnet ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not open: %s\n", - smsc75xx, strerror ( rc ) ); + if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) { + DBGC ( smscusb, "SMSC75XX %p could not open: %s\n", + smscusb, strerror ( rc ) ); goto err_open; } /* Configure interrupt endpoint */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_EP_CTL, - ( SMSC75XX_INT_EP_CTL_RDFO_EN | - SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_INT_EP_CTL, + ( SMSC75XX_INT_EP_CTL_RDFO_EN | + SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) goto err_int_ep_ctl; /* Configure bulk IN delay */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_BULK_IN_DLY, - SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_BULK_IN_DLY, + SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) goto err_bulk_in_dly; /* Configure receive filters */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_RFE_CTL, - ( SMSC75XX_RFE_CTL_AB | - SMSC75XX_RFE_CTL_AM | - SMSC75XX_RFE_CTL_AU ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_RFE_CTL, + ( SMSC75XX_RFE_CTL_AB | + SMSC75XX_RFE_CTL_AM | + SMSC75XX_RFE_CTL_AU ) ) ) != 0 ) goto err_rfe_ctl; /* Configure receive FIFO */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_RX_CTL, - ( SMSC75XX_FCT_RX_CTL_EN | - SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_FCT_RX_CTL, + ( SMSC75XX_FCT_RX_CTL_EN | + SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 ) goto err_fct_rx_ctl; /* Configure transmit FIFO */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_TX_CTL, - SMSC75XX_FCT_TX_CTL_EN ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_FCT_TX_CTL, + SMSC75XX_FCT_TX_CTL_EN ) ) != 0 ) goto err_fct_tx_ctl; /* Configure receive datapath */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_RX, - ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT | - SMSC75XX_MAC_RX_FCS | - SMSC75XX_MAC_RX_EN ) ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_MAC_RX, + ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT | + SMSC75XX_MAC_RX_FCS | + SMSC75XX_MAC_RX_EN ) ) ) != 0 ) goto err_mac_rx; /* Configure transmit datapath */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_TX, - SMSC75XX_MAC_TX_EN ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_MAC_TX, + SMSC75XX_MAC_TX_EN ) ) != 0 ) goto err_mac_tx; - /* Write MAC address high register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRH, - mac.addr.h ) ) != 0 ) - goto err_rx_addrh; + /* Set MAC address */ + if ( ( rc = smscusb_set_address ( smscusb, + SMSC75XX_RX_ADDR_BASE ) ) != 0 ) + goto err_set_address; - /* Write MAC address low register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRL, - mac.addr.l ) ) != 0 ) - goto err_rx_addrl; + /* Set MAC address perfect filter */ + if ( ( rc = smscusb_set_filter ( smscusb, + SMSC75XX_ADDR_FILT_BASE ) ) != 0 ) + goto err_set_filter; - /* Write MAC address perfect filter high register */ - mac.addr.h |= cpu_to_le32 ( SMSC75XX_ADDR_FILTH_VALID ); - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTH ( 0 ), - mac.addr.h ) ) != 0 ) - goto err_addr_filth; - - /* Write MAC address perfect filter low register */ - if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTL ( 0 ), - mac.addr.l ) ) != 0 ) - goto err_addr_filtl; - - /* Enable PHY interrupts */ - if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_MASK, - ( SMSC75XX_PHY_INTR_ANEG_DONE | - SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not set PHY interrupt " - "mask: %s\n", smsc75xx, strerror ( rc ) ); - goto err_phy_intr_mask; - } - - /* Update link status */ - smsc75xx_check_link ( smsc75xx ); + /* Enable PHY interrupts and update link status */ + if ( ( rc = smscusb_mii_open ( smscusb, SMSC75XX_MII_PHY_INTR_MASK, + ( SMSC75XX_PHY_INTR_ANEG_DONE | + SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0) + goto err_mii_open; return 0; - err_phy_intr_mask: - err_addr_filtl: - err_addr_filth: - err_rx_addrl: - err_rx_addrh: + err_mii_open: + err_set_filter: + err_set_address: err_mac_tx: err_mac_rx: err_fct_tx_ctl: @@ -841,10 +367,10 @@ static int smsc75xx_open ( struct net_device *netdev ) { err_rfe_ctl: err_bulk_in_dly: err_int_ep_ctl: - usbnet_close ( &smsc75xx->usbnet ); + usbnet_close ( &smscusb->usbnet ); err_open: err_hw_cfg: - smsc75xx_reset ( smsc75xx ); + smsc75xx_reset ( smscusb ); return rc; } @@ -854,16 +380,17 @@ static int smsc75xx_open ( struct net_device *netdev ) { * @v netdev Network device */ static void smsc75xx_close ( struct net_device *netdev ) { - struct smsc75xx_device *smsc75xx = netdev->priv; + struct smscusb_device *smscusb = netdev->priv; /* Close USB network device */ - usbnet_close ( &smsc75xx->usbnet ); + usbnet_close ( &smscusb->usbnet ); /* Dump statistics (for debugging) */ - smsc75xx_dump_statistics ( smsc75xx ); + if ( DBG_LOG ) + smsc75xx_dump_statistics ( smscusb ); /* Reset device */ - smsc75xx_reset ( smsc75xx ); + smsc75xx_reset ( smscusb ); } /** @@ -873,13 +400,12 @@ static void smsc75xx_close ( struct net_device *netdev ) { * @v iobuf I/O buffer * @ret rc Return status code */ -static int smsc75xx_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ) { - struct smsc75xx_device *smsc75xx = netdev->priv; +int smsc75xx_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct smscusb_device *smscusb = netdev->priv; int rc; /* Transmit packet */ - if ( ( rc = smsc75xx_out_transmit ( smsc75xx, iobuf ) ) != 0 ) + if ( ( rc = smsc75xx_out_transmit ( smscusb, iobuf ) ) != 0 ) return rc; return 0; @@ -890,49 +416,48 @@ static int smsc75xx_transmit ( struct net_device *netdev, * * @v netdev Network device */ -static void smsc75xx_poll ( struct net_device *netdev ) { - struct smsc75xx_device *smsc75xx = netdev->priv; +void smsc75xx_poll ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; uint32_t int_sts; int rc; /* Poll USB bus */ - usb_poll ( smsc75xx->bus ); + usb_poll ( smscusb->bus ); /* Refill endpoints */ - if ( ( rc = usbnet_refill ( &smsc75xx->usbnet ) ) != 0 ) + if ( ( rc = usbnet_refill ( &smscusb->usbnet ) ) != 0 ) netdev_rx_err ( netdev, NULL, rc ); /* Do nothing more unless there are interrupts to handle */ - int_sts = smsc75xx->int_sts; + int_sts = smscusb->int_sts; if ( ! int_sts ) return; /* Check link status if applicable */ if ( int_sts & SMSC75XX_INT_STS_PHY_INT ) { - smsc75xx_check_link ( smsc75xx ); + smscusb_mii_check_link ( smscusb ); int_sts &= ~SMSC75XX_INT_STS_PHY_INT; } /* Record RX FIFO overflow if applicable */ if ( int_sts & SMSC75XX_INT_STS_RDFO_INT ) { - DBGC2 ( smsc75xx, "SMSC75XX %p RX FIFO overflowed\n", - smsc75xx ); + DBGC2 ( smscusb, "SMSC75XX %p RX FIFO overflowed\n", smscusb ); netdev_rx_err ( netdev, NULL, -ENOBUFS ); int_sts &= ~SMSC75XX_INT_STS_RDFO_INT; } /* Check for unexpected interrupts */ if ( int_sts ) { - DBGC ( smsc75xx, "SMSC75XX %p unexpected interrupt %#08x\n", - smsc75xx, int_sts ); + DBGC ( smscusb, "SMSC75XX %p unexpected interrupt %#08x\n", + smscusb, int_sts ); netdev_rx_err ( netdev, NULL, -ENOTTY ); } /* Clear interrupts */ - if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_STS, - smsc75xx->int_sts ) ) != 0 ) + if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_INT_STS, + smscusb->int_sts ) ) != 0 ) netdev_rx_err ( netdev, NULL, rc ); - smsc75xx->int_sts = 0; + smscusb->int_sts = 0; } /** SMSC75xx network device operations */ @@ -959,47 +484,42 @@ static struct net_device_operations smsc75xx_operations = { */ static int smsc75xx_probe ( struct usb_function *func, struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; struct net_device *netdev; - struct smsc75xx_device *smsc75xx; + struct smscusb_device *smscusb; int rc; /* Allocate and initialise structure */ - netdev = alloc_etherdev ( sizeof ( *smsc75xx ) ); + netdev = alloc_etherdev ( sizeof ( *smscusb ) ); if ( ! netdev ) { rc = -ENOMEM; goto err_alloc; } netdev_init ( netdev, &smsc75xx_operations ); netdev->dev = &func->dev; - smsc75xx = netdev->priv; - memset ( smsc75xx, 0, sizeof ( *smsc75xx ) ); - smsc75xx->usb = usb; - smsc75xx->bus = usb->port->hub->bus; - smsc75xx->netdev = netdev; - usbnet_init ( &smsc75xx->usbnet, func, &smsc75xx_intr_operations, - &smsc75xx_in_operations, &smsc75xx_out_operations ); - usb_refill_init ( &smsc75xx->usbnet.intr, 0, SMSC75XX_INTR_MAX_FILL ); - usb_refill_init ( &smsc75xx->usbnet.in, SMSC75XX_IN_MTU, + smscusb = netdev->priv; + memset ( smscusb, 0, sizeof ( *smscusb ) ); + smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations ); + smscusb_mii_init ( smscusb, SMSC75XX_MII_BASE, + SMSC75XX_MII_PHY_INTR_SOURCE ); + usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU, SMSC75XX_IN_MAX_FILL ); - mii_init ( &smsc75xx->mii, &smsc75xx_mii_operations ); - DBGC ( smsc75xx, "SMSC75XX %p on %s\n", smsc75xx, func->name ); + DBGC ( smscusb, "SMSC75XX %p on %s\n", smscusb, func->name ); /* Describe USB network device */ - if ( ( rc = usbnet_describe ( &smsc75xx->usbnet, config ) ) != 0 ) { - DBGC ( smsc75xx, "SMSC75XX %p could not describe: %s\n", - smsc75xx, strerror ( rc ) ); + if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) { + DBGC ( smscusb, "SMSC75XX %p could not describe: %s\n", + smscusb, strerror ( rc ) ); goto err_describe; } /* Reset device */ - if ( ( rc = smsc75xx_reset ( smsc75xx ) ) != 0 ) + if ( ( rc = smsc75xx_reset ( smscusb ) ) != 0 ) goto err_reset; /* Read MAC address */ - if ( ( rc = smsc75xx_eeprom_read ( smsc75xx, SMSC75XX_EEPROM_MAC, - netdev->hw_addr, ETH_ALEN ) ) != 0 ) - goto err_eeprom_read; + if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb, + SMSC75XX_E2P_BASE ) ) != 0 ) + goto err_fetch_mac; /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) @@ -1010,7 +530,7 @@ static int smsc75xx_probe ( struct usb_function *func, unregister_netdev ( netdev ); err_register: - err_eeprom_read: + err_fetch_mac: err_reset: err_describe: netdev_nullify ( netdev ); diff --git a/src/drivers/net/smsc75xx.h b/src/drivers/net/smsc75xx.h index 2463b72a1..72339df03 100644 --- a/src/drivers/net/smsc75xx.h +++ b/src/drivers/net/smsc75xx.h @@ -9,25 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include -#include -#include -#include - -/** Register write command */ -#define SMSC75XX_REGISTER_WRITE \ - ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa0 ) ) - -/** Register read command */ -#define SMSC75XX_REGISTER_READ \ - ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa1 ) ) - -/** Get statistics command */ -#define SMSC75XX_GET_STATISTICS \ - ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 0xa2 ) ) +#include "smscusb.h" /** Interrupt status register */ #define SMSC75XX_INT_STS 0x00c @@ -48,19 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC75XX_BULK_IN_DLY 0x03c #define SMSC75XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ -/** EEPROM command register */ -#define SMSC75XX_E2P_CMD 0x040 -#define SMSC75XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ -#define SMSC75XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ -#define SMSC75XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ - -/** EEPROM data register */ -#define SMSC75XX_E2P_DATA 0x044 -#define SMSC75XX_E2P_DATA_GET(e2p_data) \ - ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ - -/** MAC address EEPROM address */ -#define SMSC75XX_EEPROM_MAC 0x01 +/** EEPROM register base */ +#define SMSC75XX_E2P_BASE 0x040 /** Receive filtering engine control register */ #define SMSC75XX_RFE_CTL 0x060 @@ -89,24 +60,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC75XX_MAC_TX 0x108 #define SMSC75XX_MAC_TX_EN 0x00000001UL /**< TX enable */ -/** MAC receive address high register */ -#define SMSC75XX_RX_ADDRH 0x118 +/** MAC receive address register base */ +#define SMSC75XX_RX_ADDR_BASE 0x118 -/** MAC receive address low register */ -#define SMSC75XX_RX_ADDRL 0x11c - -/** MII access register */ -#define SMSC75XX_MII_ACCESS 0x120 -#define SMSC75XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ -#define SMSC75XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ -#define SMSC75XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ -#define SMSC75XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ - -/** MII data register */ -#define SMSC75XX_MII_DATA 0x124 -#define SMSC75XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ -#define SMSC75XX_MII_DATA_GET(mii_data) \ - ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ +/** MII register base */ +#define SMSC75XX_MII_BASE 0x120 /** PHY interrupt source MII register */ #define SMSC75XX_MII_PHY_INTR_SOURCE 29 @@ -115,30 +73,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMSC75XX_MII_PHY_INTR_MASK 30 /** PHY interrupt: auto-negotiation complete */ -#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040 +#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040 /** PHY interrupt: link down */ -#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010 +#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010 -/** MAC address perfect filter N high register */ -#define SMSC75XX_ADDR_FILTH(n) ( 0x300 + ( 8 * (n) ) ) -#define SMSC75XX_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ - -/** MAC address perfect filter N low register */ -#define SMSC75XX_ADDR_FILTL(n) ( 0x304 + ( 8 * (n) ) ) - -/** MAC address */ -union smsc75xx_mac { - /** MAC receive address registers */ - struct { - /** MAC receive address low register */ - uint32_t l; - /** MAC receive address high register */ - uint32_t h; - } __attribute__ (( packed )) addr; - /** Raw MAC address */ - uint8_t raw[ETH_ALEN]; -}; +/** MAC address perfect filter register base */ +#define SMSC75XX_ADDR_FILT_BASE 0x300 /** Receive packet header */ struct smsc75xx_rx_header { @@ -168,12 +109,6 @@ struct smsc75xx_tx_header { /** Insert frame checksum and pad */ #define SMSC75XX_TX_FCS 0x00400000UL -/** Interrupt packet format */ -struct smsc75xx_interrupt { - /** Current value of INT_STS register */ - uint32_t int_sts; -} __attribute__ (( packed )); - /** Byte count statistics */ struct smsc75xx_byte_statistics { /** Unicast byte count */ @@ -264,36 +199,8 @@ struct smsc75xx_statistics { struct smsc75xx_tx_statistics tx; } __attribute__ (( packed )); -/** A SMSC75xx network device */ -struct smsc75xx_device { - /** USB device */ - struct usb_device *usb; - /** USB bus */ - struct usb_bus *bus; - /** Network device */ - struct net_device *netdev; - /** USB network device */ - struct usbnet_device usbnet; - /** MII interface */ - struct mii_interface mii; - /** Interrupt status */ - uint32_t int_sts; -}; - -/** Reset delay (in microseconds) */ -#define SMSC75XX_RESET_DELAY_US 2 - -/** Maximum time to wait for EEPROM (in milliseconds) */ -#define SMSC75XX_EEPROM_MAX_WAIT_MS 100 - -/** Maximum time to wait for MII (in milliseconds) */ -#define SMSC75XX_MII_MAX_WAIT_MS 100 - -/** Interrupt maximum fill level - * - * This is a policy decision. - */ -#define SMSC75XX_INTR_MAX_FILL 2 +/** Maximum time to wait for reset (in milliseconds) */ +#define SMSC75XX_RESET_MAX_WAIT_MS 100 /** Bulk IN maximum fill level * @@ -306,4 +213,11 @@ struct smsc75xx_device { ( sizeof ( struct smsc75xx_rx_header ) + \ ETH_FRAME_LEN + 4 /* possible VLAN header */ ) +extern struct usb_endpoint_driver_operations smsc75xx_in_operations; + +extern int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ); +extern int smsc75xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ); +extern void smsc75xx_poll ( struct net_device *netdev ); + #endif /* _SMSC75XX_H */ diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c new file mode 100644 index 000000000..9b09657db --- /dev/null +++ b/src/drivers/net/smsc95xx.c @@ -0,0 +1,767 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smsc95xx.h" + +/** @file + * + * SMSC LAN95xx USB Ethernet driver + * + */ + +/** Bulk IN completion profiler */ +static struct profiler smsc95xx_in_profiler __profiler = + { .name = "smsc95xx.in" }; + +/** Bulk OUT profiler */ +static struct profiler smsc95xx_out_profiler __profiler = + { .name = "smsc95xx.out" }; + +/****************************************************************************** + * + * MAC address + * + ****************************************************************************** + */ + +/** + * Construct MAC address for Honeywell VM3 + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smsc95xx_vm3_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + struct smbios_structure structure; + struct smbios_system_information system; + struct { + char manufacturer[ 10 /* "Honeywell" + NUL */ ]; + char product[ 4 /* "VM3" + NUL */ ]; + char mac[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + } strings; + int len; + int rc; + + /* Find system information */ + if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_SYSTEM_INFORMATION, 0, + &structure ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not find system " + "information: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Read system information */ + if ( ( rc = read_smbios_structure ( &structure, &system, + sizeof ( system ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not read system " + "information: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* NUL-terminate all strings to be fetched */ + memset ( &strings, 0, sizeof ( strings ) ); + + /* Fetch system manufacturer name */ + len = read_smbios_string ( &structure, system.manufacturer, + strings.manufacturer, + ( sizeof ( strings.manufacturer ) - 1 ) ); + if ( len < 0 ) { + rc = len; + DBGC ( smscusb, "SMSC95XX %p could not read manufacturer " + "name: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Fetch system product name */ + len = read_smbios_string ( &structure, system.product, strings.product, + ( sizeof ( strings.product ) - 1 ) ); + if ( len < 0 ) { + rc = len; + DBGC ( smscusb, "SMSC95XX %p could not read product name: " + "%s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Ignore non-VM3 devices */ + if ( ( strcmp ( strings.manufacturer, "Honeywell" ) != 0 ) || + ( strcmp ( strings.product, "VM3" ) != 0 ) ) + return -ENOTTY; + + /* Find OEM strings */ + if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_OEM_STRINGS, 0, + &structure ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not find OEM strings: %s\n", + smscusb, strerror ( rc ) ); + return rc; + } + + /* Fetch MAC address */ + len = read_smbios_string ( &structure, SMSC95XX_VM3_OEM_STRING_MAC, + strings.mac, ( sizeof ( strings.mac ) - 1 )); + if ( len < 0 ) { + rc = len; + DBGC ( smscusb, "SMSC95XX %p could not read OEM string: %s\n", + smscusb, strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( len != ( ( int ) ( sizeof ( strings.mac ) - 1 ) ) ) { + DBGC ( smscusb, "SMSC95XX %p invalid MAC address \"%s\"\n", + smscusb, strings.mac ); + return -EINVAL; + } + + /* Decode MAC address */ + len = base16_decode ( strings.mac, netdev->hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + DBGC ( smscusb, "SMSC95XX %p invalid MAC address \"%s\"\n", + smscusb, strings.mac ); + return rc; + } + + DBGC ( smscusb, "SMSC95XX %p using VM3 MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/** + * Fetch MAC address + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smsc95xx_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + int rc; + + /* Read MAC address from EEPROM, if present */ + if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb, + SMSC95XX_E2P_BASE ) ) == 0 ) + return 0; + + /* Construct MAC address for Honeywell VM3, if applicable */ + if ( ( rc = smsc95xx_vm3_fetch_mac ( smscusb ) ) == 0 ) + return 0; + + /* Otherwise, generate a random MAC address */ + eth_random_addr ( netdev->hw_addr ); + DBGC ( smscusb, "SMSC95XX %p using random MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/****************************************************************************** + * + * Statistics (for debugging) + * + ****************************************************************************** + */ + +/** + * Dump statistics (for debugging) + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smsc95xx_dump_statistics ( struct smscusb_device *smscusb ) { + struct smsc95xx_rx_statistics rx; + struct smsc95xx_tx_statistics tx; + int rc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return 0; + + /* Get RX statistics */ + if ( ( rc = smscusb_get_statistics ( smscusb, SMSC95XX_RX_STATISTICS, + &rx, sizeof ( rx ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not get RX statistics: " + "%s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Get TX statistics */ + if ( ( rc = smscusb_get_statistics ( smscusb, SMSC95XX_TX_STATISTICS, + &tx, sizeof ( tx ) ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not get TX statistics: " + "%s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Dump statistics */ + DBGC ( smscusb, "SMSC95XX %p RX good %d bad %d crc %d und %d aln %d " + "ovr %d lat %d drp %d\n", smscusb, le32_to_cpu ( rx.good ), + le32_to_cpu ( rx.bad ), le32_to_cpu ( rx.crc ), + le32_to_cpu ( rx.undersize ), le32_to_cpu ( rx.alignment ), + le32_to_cpu ( rx.oversize ), le32_to_cpu ( rx.late ), + le32_to_cpu ( rx.dropped ) ); + DBGC ( smscusb, "SMSC95XX %p TX good %d bad %d pau %d sgl %d mul %d " + "exc %d lat %d und %d def %d car %d\n", smscusb, + le32_to_cpu ( tx.good ), le32_to_cpu ( tx.bad ), + le32_to_cpu ( tx.pause ), le32_to_cpu ( tx.single ), + le32_to_cpu ( tx.multiple ), le32_to_cpu ( tx.excessive ), + le32_to_cpu ( tx.late ), le32_to_cpu ( tx.underrun ), + le32_to_cpu ( tx.deferred ), le32_to_cpu ( tx.carrier ) ); + + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smsc95xx_reset ( struct smscusb_device *smscusb ) { + uint32_t hw_cfg; + uint32_t led_gpio_cfg; + int rc; + + /* Reset device */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_HW_CFG, + SMSC95XX_HW_CFG_LRST ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + udelay ( SMSC95XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + if ( ( rc = smscusb_readl ( smscusb, SMSC95XX_HW_CFG, &hw_cfg ) ) != 0 ) + return rc; + if ( hw_cfg & SMSC95XX_HW_CFG_LRST ) { + DBGC ( smscusb, "SMSC95XX %p failed to reset\n", smscusb ); + return -ETIMEDOUT; + } + + /* Configure LEDs */ + led_gpio_cfg = ( SMSC95XX_LED_GPIO_CFG_GPCTL2_NSPD_LED | + SMSC95XX_LED_GPIO_CFG_GPCTL1_NLNKA_LED | + SMSC95XX_LED_GPIO_CFG_GPCTL0_NFDX_LED ); + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_LED_GPIO_CFG, + led_gpio_cfg ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not configure LEDs: %s\n", + smscusb, strerror ( rc ) ); + /* Ignore error and continue */ + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc95xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.in ); + struct net_device *netdev = smscusb->netdev; + struct smsc95xx_rx_header *header; + + /* Profile completions */ + profile_start ( &smsc95xx_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smscusb, "SMSC95XX %p bulk IN failed: %s\n", + smscusb, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { + DBGC ( smscusb, "SMSC95XX %p underlength bulk IN\n", + smscusb ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header and CRC */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + iob_unput ( iobuf, 4 /* CRC */ ); + + /* Check for errors */ + if ( header->command & cpu_to_le32 ( SMSC95XX_RX_RUNT | + SMSC95XX_RX_LATE | + SMSC95XX_RX_CRC ) ) { + DBGC ( smscusb, "SMSC95XX %p receive error (%08x):\n", + smscusb, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + profile_stop ( &smsc95xx_in_profiler ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations smsc95xx_in_operations = { + .complete = smsc95xx_in_complete, +}; + +/** + * Transmit packet + * + * @v smscusb SMSC USB device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc95xx_out_transmit ( struct smscusb_device *smscusb, + struct io_buffer *iobuf ) { + struct smsc95xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile transmissions */ + profile_start ( &smsc95xx_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->command = cpu_to_le32 ( SMSC95XX_TX_FIRST | SMSC95XX_TX_LAST | + SMSC95XX_TX_LEN ( len ) ); + header->len = cpu_to_le32 ( len ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &smscusb->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &smsc95xx_out_profiler ); + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int smsc95xx_open ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + int rc; + + /* Clear stored interrupt status */ + smscusb->int_sts = 0; + + /* Configure bulk IN empty response */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_HW_CFG, + SMSC95XX_HW_CFG_BIR ) ) != 0 ) + goto err_hw_cfg; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not open: %s\n", + smscusb, strerror ( rc ) ); + goto err_open; + } + + /* Configure interrupt endpoint */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_INT_EP_CTL, + ( SMSC95XX_INT_EP_CTL_RXDF_EN | + SMSC95XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + goto err_int_ep_ctl; + + /* Configure bulk IN delay */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_BULK_IN_DLY, + SMSC95XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + goto err_bulk_in_dly; + + /* Configure MAC */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_MAC_CR, + ( SMSC95XX_MAC_CR_RXALL | + SMSC95XX_MAC_CR_FDPX | + SMSC95XX_MAC_CR_MCPAS | + SMSC95XX_MAC_CR_PRMS | + SMSC95XX_MAC_CR_PASSBAD | + SMSC95XX_MAC_CR_TXEN | + SMSC95XX_MAC_CR_RXEN ) ) ) != 0 ) + goto err_mac_cr; + + /* Configure transmit datapath */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_TX_CFG, + SMSC95XX_TX_CFG_ON ) ) != 0 ) + goto err_tx_cfg; + + /* Set MAC address */ + if ( ( rc = smscusb_set_address ( smscusb, SMSC95XX_ADDR_BASE ) ) != 0 ) + goto err_set_address; + + /* Enable PHY interrupts and update link status */ + if ( ( rc = smscusb_mii_open ( smscusb, SMSC95XX_MII_PHY_INTR_MASK, + ( SMSC95XX_PHY_INTR_ANEG_DONE | + SMSC95XX_PHY_INTR_LINK_DOWN ) ) ) != 0) + goto err_mii_open; + + return 0; + + err_mii_open: + err_set_address: + err_tx_cfg: + err_mac_cr: + err_bulk_in_dly: + err_int_ep_ctl: + usbnet_close ( &smscusb->usbnet ); + err_open: + err_hw_cfg: + smsc95xx_reset ( smscusb ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void smsc95xx_close ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &smscusb->usbnet ); + + /* Dump statistics (for debugging) */ + smsc95xx_dump_statistics ( smscusb ); + + /* Reset device */ + smsc95xx_reset ( smscusb ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc95xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct smscusb_device *smscusb = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = smsc95xx_out_transmit ( smscusb, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void smsc95xx_poll ( struct net_device *netdev ) { + struct smscusb_device *smscusb = netdev->priv; + uint32_t int_sts; + int rc; + + /* Poll USB bus */ + usb_poll ( smscusb->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &smscusb->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + + /* Do nothing more unless there are interrupts to handle */ + int_sts = smscusb->int_sts; + if ( ! int_sts ) + return; + + /* Check link status if applicable */ + if ( int_sts & SMSC95XX_INT_STS_PHY_INT ) { + smscusb_mii_check_link ( smscusb ); + int_sts &= ~SMSC95XX_INT_STS_PHY_INT; + } + + /* Record RX FIFO overflow if applicable */ + if ( int_sts & SMSC95XX_INT_STS_RXDF_INT ) { + DBGC2 ( smscusb, "SMSC95XX %p RX FIFO overflowed\n", + smscusb ); + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + int_sts &= ~SMSC95XX_INT_STS_RXDF_INT; + } + + /* Check for unexpected interrupts */ + if ( int_sts ) { + DBGC ( smscusb, "SMSC95XX %p unexpected interrupt %#08x\n", + smscusb, int_sts ); + netdev_rx_err ( netdev, NULL, -ENOTTY ); + } + + /* Clear interrupts */ + if ( ( rc = smscusb_writel ( smscusb, SMSC95XX_INT_STS, + smscusb->int_sts ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + smscusb->int_sts = 0; +} + +/** SMSC95xx network device operations */ +static struct net_device_operations smsc95xx_operations = { + .open = smsc95xx_open, + .close = smsc95xx_close, + .transmit = smsc95xx_transmit, + .poll = smsc95xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int smsc95xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct net_device *netdev; + struct smscusb_device *smscusb; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *smscusb ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &smsc95xx_operations ); + netdev->dev = &func->dev; + smscusb = netdev->priv; + memset ( smscusb, 0, sizeof ( *smscusb ) ); + smscusb_init ( smscusb, netdev, func, &smsc95xx_in_operations ); + smscusb_mii_init ( smscusb, SMSC95XX_MII_BASE, + SMSC95XX_MII_PHY_INTR_SOURCE ); + usb_refill_init ( &smscusb->usbnet.in, + ( sizeof ( struct smsc95xx_tx_header ) - + sizeof ( struct smsc95xx_rx_header ) ), + SMSC95XX_IN_MTU, SMSC95XX_IN_MAX_FILL ); + DBGC ( smscusb, "SMSC95XX %p on %s\n", smscusb, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) { + DBGC ( smscusb, "SMSC95XX %p could not describe: %s\n", + smscusb, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = smsc95xx_reset ( smscusb ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = smsc95xx_fetch_mac ( smscusb ) ) != 0 ) + goto err_fetch_mac; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_fetch_mac: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void smsc95xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** SMSC95xx device IDs */ +static struct usb_device_id smsc95xx_ids[] = { + { + .name = "smsc9500", + .vendor = 0x0424, + .product = 0x9500, + }, + { + .name = "smsc9505", + .vendor = 0x0424, + .product = 0x9505, + }, + { + .name = "smsc9500a", + .vendor = 0x0424, + .product = 0x9e00, + }, + { + .name = "smsc9505a", + .vendor = 0x0424, + .product = 0x9e01, + }, + { + .name = "smsc9514", + .vendor = 0x0424, + .product = 0xec00, + }, + { + .name = "smsc9500-s", + .vendor = 0x0424, + .product = 0x9900, + }, + { + .name = "smsc9505-s", + .vendor = 0x0424, + .product = 0x9901, + }, + { + .name = "smsc9500a-s", + .vendor = 0x0424, + .product = 0x9902, + }, + { + .name = "smsc9505a-s", + .vendor = 0x0424, + .product = 0x9903, + }, + { + .name = "smsc9514-s", + .vendor = 0x0424, + .product = 0x9904, + }, + { + .name = "smsc9500a-h", + .vendor = 0x0424, + .product = 0x9905, + }, + { + .name = "smsc9505a-h", + .vendor = 0x0424, + .product = 0x9906, + }, + { + .name = "smsc9500-2", + .vendor = 0x0424, + .product = 0x9907, + }, + { + .name = "smsc9500a-2", + .vendor = 0x0424, + .product = 0x9908, + }, + { + .name = "smsc9514-2", + .vendor = 0x0424, + .product = 0x9909, + }, + { + .name = "smsc9530", + .vendor = 0x0424, + .product = 0x9530, + }, + { + .name = "smsc9730", + .vendor = 0x0424, + .product = 0x9730, + }, + { + .name = "smsc89530", + .vendor = 0x0424, + .product = 0x9e08, + }, +}; + +/** SMSC LAN95xx driver */ +struct usb_driver smsc95xx_driver __usb_driver = { + .ids = smsc95xx_ids, + .id_count = ( sizeof ( smsc95xx_ids ) / sizeof ( smsc95xx_ids[0] ) ), + .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ), + .score = USB_SCORE_NORMAL, + .probe = smsc95xx_probe, + .remove = smsc95xx_remove, +}; diff --git a/src/drivers/net/smsc95xx.h b/src/drivers/net/smsc95xx.h new file mode 100644 index 000000000..0cdf38248 --- /dev/null +++ b/src/drivers/net/smsc95xx.h @@ -0,0 +1,180 @@ +#ifndef _SMSC95XX_H +#define _SMSC95XX_H + +/** @file + * + * SMSC LAN95xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "smscusb.h" + +/** Interrupt status register */ +#define SMSC95XX_INT_STS 0x008 +#define SMSC95XX_INT_STS_RXDF_INT 0x00000800UL /**< RX FIFO overflow */ +#define SMSC95XX_INT_STS_PHY_INT 0x00008000UL /**< PHY interrupt */ + +/** Transmit configuration register */ +#define SMSC95XX_TX_CFG 0x010 +#define SMSC95XX_TX_CFG_ON 0x00000004UL /**< TX enable */ + +/** Hardware configuration register */ +#define SMSC95XX_HW_CFG 0x014 +#define SMSC95XX_HW_CFG_BIR 0x00001000UL /**< Bulk IN use NAK */ +#define SMSC95XX_HW_CFG_LRST 0x00000008UL /**< Soft lite reset */ + +/** LED GPIO configuration register */ +#define SMSC95XX_LED_GPIO_CFG 0x024 +#define SMSC95XX_LED_GPIO_CFG_GPCTL2(x) ( (x) << 24 ) /**< GPIO 2 control */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL2_NSPD_LED \ + SMSC95XX_LED_GPIO_CFG_GPCTL2 ( 1 ) /**< Link speed LED */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL1(x) ( (x) << 20 ) /**< GPIO 1 control */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL1_NLNKA_LED \ + SMSC95XX_LED_GPIO_CFG_GPCTL1 ( 1 ) /**< Activity LED */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL0(x) ( (x) << 16 ) /**< GPIO 0 control */ +#define SMSC95XX_LED_GPIO_CFG_GPCTL0_NFDX_LED \ + SMSC95XX_LED_GPIO_CFG_GPCTL0 ( 1 ) /**< Full-duplex LED */ + +/** EEPROM register base */ +#define SMSC95XX_E2P_BASE 0x030 + +/** Interrupt endpoint control register */ +#define SMSC95XX_INT_EP_CTL 0x068 +#define SMSC95XX_INT_EP_CTL_RXDF_EN 0x00000800UL /**< RX FIFO overflow */ +#define SMSC95XX_INT_EP_CTL_PHY_EN 0x00008000UL /**< PHY interrupt */ + +/** Bulk IN delay register */ +#define SMSC95XX_BULK_IN_DLY 0x06c +#define SMSC95XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ + +/** MAC control register */ +#define SMSC95XX_MAC_CR 0x100 +#define SMSC95XX_MAC_CR_RXALL 0x80000000UL /**< Receive all */ +#define SMSC95XX_MAC_CR_FDPX 0x00100000UL /**< Full duplex */ +#define SMSC95XX_MAC_CR_MCPAS 0x00080000UL /**< All multicast */ +#define SMSC95XX_MAC_CR_PRMS 0x00040000UL /**< Promiscuous */ +#define SMSC95XX_MAC_CR_PASSBAD 0x00010000UL /**< Pass bad frames */ +#define SMSC95XX_MAC_CR_TXEN 0x00000008UL /**< TX enabled */ +#define SMSC95XX_MAC_CR_RXEN 0x00000004UL /**< RX enabled */ + +/** MAC address register base */ +#define SMSC95XX_ADDR_BASE 0x104 + +/** MII register base */ +#define SMSC95XX_MII_BASE 0x0114 + +/** PHY interrupt source MII register */ +#define SMSC95XX_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSC95XX_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSC95XX_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSC95XX_PHY_INTR_LINK_DOWN 0x0010 + +/** Receive packet header */ +struct smsc95xx_rx_header { + /** Command word */ + uint32_t command; +} __attribute__ (( packed )); + +/** Runt frame */ +#define SMSC95XX_RX_RUNT 0x00004000UL + +/** Late collision */ +#define SMSC95XX_RX_LATE 0x00000040UL + +/** CRC error */ +#define SMSC95XX_RX_CRC 0x00000002UL + +/** Transmit packet header */ +struct smsc95xx_tx_header { + /** Command word */ + uint32_t command; + /** Frame length */ + uint32_t len; +} __attribute__ (( packed )); + +/** First segment */ +#define SMSC95XX_TX_FIRST 0x00002000UL + +/** Last segment */ +#define SMSC95XX_TX_LAST 0x00001000UL + +/** Buffer size */ +#define SMSC95XX_TX_LEN(len) ( (len) << 0 ) + +/** Receive statistics */ +struct smsc95xx_rx_statistics { + /** Good frames */ + uint32_t good; + /** CRC errors */ + uint32_t crc; + /** Runt frame errors */ + uint32_t undersize; + /** Alignment errors */ + uint32_t alignment; + /** Frame too long errors */ + uint32_t oversize; + /** Later collision errors */ + uint32_t late; + /** Bad frames */ + uint32_t bad; + /** Dropped frames */ + uint32_t dropped; +} __attribute__ (( packed )); + +/** Receive statistics */ +#define SMSC95XX_RX_STATISTICS 0 + +/** Transmit statistics */ +struct smsc95xx_tx_statistics { + /** Good frames */ + uint32_t good; + /** Pause frames */ + uint32_t pause; + /** Single collisions */ + uint32_t single; + /** Multiple collisions */ + uint32_t multiple; + /** Excessive collisions */ + uint32_t excessive; + /** Late collisions */ + uint32_t late; + /** Buffer underruns */ + uint32_t underrun; + /** Excessive deferrals */ + uint32_t deferred; + /** Carrier errors */ + uint32_t carrier; + /** Bad frames */ + uint32_t bad; +} __attribute__ (( packed )); + +/** Transmit statistics */ +#define SMSC95XX_TX_STATISTICS 1 + +/** Reset delay (in microseconds) */ +#define SMSC95XX_RESET_DELAY_US 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define SMSC95XX_IN_MAX_FILL 8 + +/** Bulk IN buffer size */ +#define SMSC95XX_IN_MTU \ + ( sizeof ( struct smsc95xx_rx_header ) + \ + ETH_FRAME_LEN + 4 /* possible VLAN header */ \ + + 4 /* CRC */ ) + +/** Honeywell VM3 MAC address OEM string index */ +#define SMSC95XX_VM3_OEM_STRING_MAC 2 + +#endif /* _SMSC95XX_H */ diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c new file mode 100644 index 000000000..538d338c4 --- /dev/null +++ b/src/drivers/net/smscusb.c @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include "smscusb.h" + +/** @file + * + * SMSC USB Ethernet drivers + * + */ + +/** Interrupt completion profiler */ +static struct profiler smscusb_intr_profiler __profiler = + { .name = "smscusb.intr" }; + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Write register (without byte-swapping) + * + * @v smscusb Smscusb device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +int smscusb_raw_writel ( struct smscusb_device *smscusb, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + DBGCIO ( smscusb, "SMSCUSB %p [%03x] <= %08x\n", + smscusb, address, le32_to_cpu ( value ) ); + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_WRITE, 0, + address, &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not write %03x: %s\n", + smscusb, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Read register (without byte-swapping) + * + * @v smscusb SMSC USB device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +int smscusb_raw_readl ( struct smscusb_device *smscusb, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_REGISTER_READ, 0, + address, value, sizeof ( *value ) ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not read %03x: %s\n", + smscusb, address, strerror ( rc ) ); + return rc; + } + DBGCIO ( smscusb, "SMSCUSB %p [%03x] => %08x\n", + smscusb, address, le32_to_cpu ( *value ) ); + + return 0; +} + +/****************************************************************************** + * + * EEPROM access + * + ****************************************************************************** + */ + +/** + * Wait for EEPROM to become idle + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @ret rc Return status code + */ +static int smscusb_eeprom_wait ( struct smscusb_device *smscusb, + unsigned int e2p_base ) { + uint32_t e2p_cmd; + unsigned int i; + int rc; + + /* Wait for EPC_BSY to become clear */ + for ( i = 0 ; i < SMSCUSB_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* Read E2P_CMD and check EPC_BSY */ + if ( ( rc = smscusb_readl ( smscusb, + ( e2p_base + SMSCUSB_E2P_CMD ), + &e2p_cmd ) ) != 0 ) + return rc; + if ( ! ( e2p_cmd & SMSCUSB_E2P_CMD_EPC_BSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for EEPROM\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Read byte from EEPROM + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @v address EEPROM address + * @ret byte Byte read, or negative error + */ +static int smscusb_eeprom_read_byte ( struct smscusb_device *smscusb, + unsigned int e2p_base, + unsigned int address ) { + uint32_t e2p_cmd; + uint32_t e2p_data; + int rc; + + /* Wait for EEPROM to become idle */ + if ( ( rc = smscusb_eeprom_wait ( smscusb, e2p_base ) ) != 0 ) + return rc; + + /* Initiate read command */ + e2p_cmd = ( SMSCUSB_E2P_CMD_EPC_BSY | SMSCUSB_E2P_CMD_EPC_CMD_READ | + SMSCUSB_E2P_CMD_EPC_ADDR ( address ) ); + if ( ( rc = smscusb_writel ( smscusb, ( e2p_base + SMSCUSB_E2P_CMD ), + e2p_cmd ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_eeprom_wait ( smscusb, e2p_base ) ) != 0 ) + return rc; + + /* Read EEPROM data */ + if ( ( rc = smscusb_readl ( smscusb, ( e2p_base + SMSCUSB_E2P_DATA ), + &e2p_data ) ) != 0 ) + return rc; + + return SMSCUSB_E2P_DATA_GET ( e2p_data ); +} + +/** + * Read data from EEPROM + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @v address EEPROM address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smscusb_eeprom_read ( struct smscusb_device *smscusb, + unsigned int e2p_base, unsigned int address, + void *data, size_t len ) { + uint8_t *bytes; + int byte; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smscusb_eeprom_read_byte ( smscusb, e2p_base, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/** + * Fetch MAC address from EEPROM + * + * @v smscusb SMSC USB device + * @v e2p_base E2P register base + * @ret rc Return status code + */ +int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, + unsigned int e2p_base ) { + struct net_device *netdev = smscusb->netdev; + int rc; + + /* Read MAC address from EEPROM */ + if ( ( rc = smscusb_eeprom_read ( smscusb, e2p_base, SMSCUSB_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + return rc; + + /* Check that EEPROM is physically present */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + DBGC ( smscusb, "SMSCUSB %p has no EEPROM MAC (%s)\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return -ENODEV; + } + + DBGC ( smscusb, "SMSCUSB %p using EEPROM MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/****************************************************************************** + * + * OTP access + * + ****************************************************************************** + */ + +/** + * Power up OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @ret rc Return status code + */ +static int smscusb_otp_power_up ( struct smscusb_device *smscusb, + unsigned int otp_base ) { + uint32_t otp_power; + unsigned int i; + int rc; + + /* Power up OTP */ + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_POWER ), + 0 ) ) != 0 ) + return rc; + + /* Wait for OTP_POWER_DOWN to become clear */ + for ( i = 0 ; i < SMSCUSB_OTP_MAX_WAIT_MS ; i++ ) { + + /* Read OTP_POWER and check OTP_POWER_DOWN */ + if ( ( rc = smscusb_readl ( smscusb, + ( otp_base + SMSCUSB_OTP_POWER ), + &otp_power ) ) != 0 ) + return rc; + if ( ! ( otp_power & SMSCUSB_OTP_POWER_DOWN ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for OTP power up\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Wait for OTP to become idle + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @ret rc Return status code + */ +static int smscusb_otp_wait ( struct smscusb_device *smscusb, + unsigned int otp_base ) { + uint32_t otp_status; + unsigned int i; + int rc; + + /* Wait for OTP_STATUS_BUSY to become clear */ + for ( i = 0 ; i < SMSCUSB_OTP_MAX_WAIT_MS ; i++ ) { + + /* Read OTP_STATUS and check OTP_STATUS_BUSY */ + if ( ( rc = smscusb_readl ( smscusb, + ( otp_base + SMSCUSB_OTP_STATUS ), + &otp_status ) ) != 0 ) + return rc; + if ( ! ( otp_status & SMSCUSB_OTP_STATUS_BUSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for OTP\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Read byte from OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @v address OTP address + * @ret byte Byte read, or negative error + */ +static int smscusb_otp_read_byte ( struct smscusb_device *smscusb, + unsigned int otp_base, + unsigned int address ) { + uint8_t addrh = ( address >> 8 ); + uint8_t addrl = ( address >> 0 ); + uint32_t otp_data; + int rc; + + /* Wait for OTP to become idle */ + if ( ( rc = smscusb_otp_wait ( smscusb, otp_base ) ) != 0 ) + return rc; + + /* Initiate read command */ + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_ADDRH ), + addrh ) ) != 0 ) + return rc; + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_ADDRL ), + addrl ) ) != 0 ) + return rc; + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_CMD ), + SMSCUSB_OTP_CMD_READ ) ) != 0 ) + return rc; + if ( ( rc = smscusb_writel ( smscusb, ( otp_base + SMSCUSB_OTP_GO ), + SMSCUSB_OTP_GO_GO ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_otp_wait ( smscusb, otp_base ) ) != 0 ) + return rc; + + /* Read OTP data */ + if ( ( rc = smscusb_readl ( smscusb, ( otp_base + SMSCUSB_OTP_DATA ), + &otp_data ) ) != 0 ) + return rc; + + return SMSCUSB_OTP_DATA_GET ( otp_data ); +} + +/** + * Read data from OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @v address OTP address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smscusb_otp_read ( struct smscusb_device *smscusb, + unsigned int otp_base, unsigned int address, + void *data, size_t len ) { + uint8_t *bytes; + int byte; + int rc; + + /* Power up OTP */ + if ( ( rc = smscusb_otp_power_up ( smscusb, otp_base ) ) != 0 ) + return rc; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smscusb_otp_read_byte ( smscusb, otp_base, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/** + * Fetch MAC address from OTP + * + * @v smscusb SMSC USB device + * @v otp_base OTP register base + * @ret rc Return status code + */ +int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, + unsigned int otp_base ) { + struct net_device *netdev = smscusb->netdev; + uint8_t signature; + unsigned int address; + int rc; + + /* Read OTP signature byte */ + if ( ( rc = smscusb_otp_read ( smscusb, otp_base, 0, &signature, + sizeof ( signature ) ) ) != 0 ) + return rc; + + /* Determine location of MAC address */ + switch ( signature ) { + case SMSCUSB_OTP_1_SIG: + address = SMSCUSB_OTP_1_MAC; + break; + case SMSCUSB_OTP_2_SIG: + address = SMSCUSB_OTP_2_MAC; + break; + default: + DBGC ( smscusb, "SMSCUSB %p unknown OTP signature %#02x\n", + smscusb, signature ); + return -ENOTSUP; + } + + /* Read MAC address from OTP */ + if ( ( rc = smscusb_otp_read ( smscusb, otp_base, address, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + return rc; + + /* Check that OTP is valid */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + DBGC ( smscusb, "SMSCUSB %p has no layout %#02x OTP MAC (%s)\n", + smscusb, signature, eth_ntoa ( netdev->hw_addr ) ); + return -ENODEV; + } + + DBGC ( smscusb, "SMSCUSB %p using layout %#02x OTP MAC %s\n", + smscusb, signature, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + +/****************************************************************************** + * + * MII access + * + ****************************************************************************** + */ + +/** + * Wait for MII to become idle + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smscusb_mii_wait ( struct smscusb_device *smscusb ) { + unsigned int base = smscusb->mii_base; + uint32_t mii_access; + unsigned int i; + int rc; + + /* Wait for MIIBZY to become clear */ + for ( i = 0 ; i < SMSCUSB_MII_MAX_WAIT_MS ; i++ ) { + + /* Read MII_ACCESS and check MIIBZY */ + if ( ( rc = smscusb_readl ( smscusb, + ( base + SMSCUSB_MII_ACCESS ), + &mii_access ) ) != 0 ) + return rc; + if ( ! ( mii_access & SMSCUSB_MII_ACCESS_MIIBZY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smscusb, "SMSCUSB %p timed out waiting for MII\n", + smscusb ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @ret value Data read, or negative error + */ +static int smscusb_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { + struct smscusb_device *smscusb = + container_of ( mdio, struct smscusb_device, mdio ); + unsigned int base = smscusb->mii_base; + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + /* Initiate read command */ + mii_access = ( SMSCUSB_MII_ACCESS_PHY_ADDRESS | + SMSCUSB_MII_ACCESS_MIIRINDA ( reg ) | + SMSCUSB_MII_ACCESS_MIIBZY ); + if ( ( rc = smscusb_writel ( smscusb, ( base + SMSCUSB_MII_ACCESS ), + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + /* Read MII data */ + if ( ( rc = smscusb_readl ( smscusb, ( base + SMSCUSB_MII_DATA ), + &mii_data ) ) != 0 ) + return rc; + + return SMSCUSB_MII_DATA_GET ( mii_data ); +} + +/** + * Write to MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int smscusb_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, + unsigned int data ) { + struct smscusb_device *smscusb = + container_of ( mdio, struct smscusb_device, mdio ); + unsigned int base = smscusb->mii_base; + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + /* Write MII data */ + mii_data = SMSCUSB_MII_DATA_SET ( data ); + if ( ( rc = smscusb_writel ( smscusb, ( base + SMSCUSB_MII_DATA ), + mii_data ) ) != 0 ) + return rc; + + /* Initiate write command */ + mii_access = ( SMSCUSB_MII_ACCESS_PHY_ADDRESS | + SMSCUSB_MII_ACCESS_MIIRINDA ( reg ) | + SMSCUSB_MII_ACCESS_MIIWNR | + SMSCUSB_MII_ACCESS_MIIBZY ); + if ( ( rc = smscusb_writel ( smscusb, ( base + SMSCUSB_MII_ACCESS ), + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smscusb_mii_wait ( smscusb ) ) != 0 ) + return rc; + + return 0; +} + +/** MII operations */ +struct mii_operations smscusb_mii_operations = { + .read = smscusb_mii_read, + .write = smscusb_mii_write, +}; + +/** + * Check link status + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +int smscusb_mii_check_link ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + int intr; + int rc; + + /* Read PHY interrupt source */ + intr = mii_read ( &smscusb->mii, smscusb->phy_source ); + if ( intr < 0 ) { + rc = intr; + DBGC ( smscusb, "SMSCUSB %p could not get PHY interrupt " + "source: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Acknowledge PHY interrupt */ + if ( ( rc = mii_write ( &smscusb->mii, smscusb->phy_source, + intr ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not acknowledge PHY " + "interrupt: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Check link status */ + if ( ( rc = mii_check_link ( &smscusb->mii, netdev ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not check link: %s\n", + smscusb, strerror ( rc ) ); + return rc; + } + + DBGC ( smscusb, "SMSCUSB %p link %s (intr %#04x)\n", + smscusb, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); + return 0; +} + +/** + * Enable PHY interrupts and update link status + * + * @v smscusb SMSC USB device + * @v phy_mask PHY interrupt mask register + * @v intrs PHY interrupts to enable + * @ret rc Return status code + */ +int smscusb_mii_open ( struct smscusb_device *smscusb, + unsigned int phy_mask, unsigned int intrs ) { + int rc; + + /* Enable PHY interrupts */ + if ( ( rc = mii_write ( &smscusb->mii, phy_mask, intrs ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not set PHY interrupt " + "mask: %s\n", smscusb, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + smscusb_mii_check_link ( smscusb ); + + return 0; +} + +/****************************************************************************** + * + * Receive filtering + * + ****************************************************************************** + */ + +/** + * Set receive address + * + * @v smscusb SMSC USB device + * @v addr_base Receive address register base + * @ret rc Return status code + */ +int smscusb_set_address ( struct smscusb_device *smscusb, + unsigned int addr_base ) { + struct net_device *netdev = smscusb->netdev; + union smscusb_mac mac; + int rc; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + + /* Write MAC address high register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( addr_base + SMSCUSB_RX_ADDRH ), + mac.addr.h ) ) != 0 ) + return rc; + + /* Write MAC address low register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( addr_base + SMSCUSB_RX_ADDRL ), + mac.addr.l ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Set receive filter + * + * @v smscusb SMSC USB device + * @v filt_base Receive filter register base + * @ret rc Return status code + */ +int smscusb_set_filter ( struct smscusb_device *smscusb, + unsigned int filt_base ) { + struct net_device *netdev = smscusb->netdev; + union smscusb_mac mac; + int rc; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + mac.addr.h |= cpu_to_le32 ( SMSCUSB_ADDR_FILTH_VALID ); + + /* Write MAC address perfect filter high register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( filt_base + SMSCUSB_ADDR_FILTH(0) ), + mac.addr.h ) ) != 0 ) + return rc; + + /* Write MAC address perfect filter low register */ + if ( ( rc = smscusb_raw_writel ( smscusb, + ( filt_base + SMSCUSB_ADDR_FILTL(0) ), + mac.addr.l ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smscusb_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.intr ); + struct net_device *netdev = smscusb->netdev; + struct smscusb_interrupt *intr; + + /* Profile completions */ + profile_start ( &smscusb_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smscusb, "SMSCUSB %p interrupt failed: %s\n", + smscusb, strerror ( rc ) ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract interrupt data */ + if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { + DBGC ( smscusb, "SMSCUSB %p malformed interrupt\n", + smscusb ); + DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + intr = iobuf->data; + + /* Record interrupt status */ + smscusb->int_sts = le32_to_cpu ( intr->int_sts ); + profile_stop ( &smscusb_intr_profiler ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +struct usb_endpoint_driver_operations smscusb_intr_operations = { + .complete = smscusb_intr_complete, +}; + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smscusb_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smscusb_device *smscusb = + container_of ( ep, struct smscusb_device, usbnet.out ); + struct net_device *netdev = smscusb->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +struct usb_endpoint_driver_operations smscusb_out_operations = { + .complete = smscusb_out_complete, +}; diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h new file mode 100644 index 000000000..b5d9ad3fc --- /dev/null +++ b/src/drivers/net/smscusb.h @@ -0,0 +1,298 @@ +#ifndef _SMSCUSB_H +#define _SMSCUSB_H + +/** @file + * + * SMSC USB Ethernet drivers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** Register write command */ +#define SMSCUSB_REGISTER_WRITE \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa0 ) ) + +/** Register read command */ +#define SMSCUSB_REGISTER_READ \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa1 ) ) + +/** Get statistics command */ +#define SMSCUSB_GET_STATISTICS \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa2 ) ) + +/** EEPROM command register offset */ +#define SMSCUSB_E2P_CMD 0x000 +#define SMSCUSB_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ +#define SMSCUSB_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ +#define SMSCUSB_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ + +/** EEPROM data register offset */ +#define SMSCUSB_E2P_DATA 0x004 +#define SMSCUSB_E2P_DATA_GET(e2p_data) \ + ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ + +/** MAC address EEPROM address */ +#define SMSCUSB_EEPROM_MAC 0x01 + +/** Maximum time to wait for EEPROM (in milliseconds) */ +#define SMSCUSB_EEPROM_MAX_WAIT_MS 100 + +/** OTP power register offset */ +#define SMSCUSB_OTP_POWER 0x000 +#define SMSCUSB_OTP_POWER_DOWN 0x00000001UL /**< OTP power down */ + +/** OTP address high byte register offset */ +#define SMSCUSB_OTP_ADDRH 0x004 + +/** OTP address low byte register offset */ +#define SMSCUSB_OTP_ADDRL 0x008 + +/** OTP data register offset */ +#define SMSCUSB_OTP_DATA 0x018 +#define SMSCUSB_OTP_DATA_GET(otp_data) \ + ( ( (otp_data) >> 0 ) & 0xff ) /**< OTP data */ + +/** OTP command selection register offset */ +#define SMSCUSB_OTP_CMD 0x020 +#define SMSCUSB_OTP_CMD_READ 0x00000001UL /**< Read command */ + +/** OTP command initiation register offset */ +#define SMSCUSB_OTP_GO 0x028 +#define SMSCUSB_OTP_GO_GO 0x00000001UL /**< Initiate command */ + +/** OTP status register offset */ +#define SMSCUSB_OTP_STATUS 0x030 +#define SMSCUSB_OTP_STATUS_BUSY 0x00000001UL /**< OTP busy */ + +/** Maximum time to wait for OTP (in milliseconds) */ +#define SMSCUSB_OTP_MAX_WAIT_MS 100 + +/** OTP layout 1 signature */ +#define SMSCUSB_OTP_1_SIG 0xf3 + +/** OTP layout 1 MAC address offset */ +#define SMSCUSB_OTP_1_MAC 0x001 + +/** OTP layout 2 signature */ +#define SMSCUSB_OTP_2_SIG 0xf7 + +/** OTP layout 2 MAC address offset */ +#define SMSCUSB_OTP_2_MAC 0x101 + +/** MII access register offset */ +#define SMSCUSB_MII_ACCESS 0x000 +#define SMSCUSB_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ +#define SMSCUSB_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ +#define SMSCUSB_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ +#define SMSCUSB_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ + +/** MII data register offset */ +#define SMSCUSB_MII_DATA 0x004 +#define SMSCUSB_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ +#define SMSCUSB_MII_DATA_GET(mii_data) \ + ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ + +/** Maximum time to wait for MII (in milliseconds) */ +#define SMSCUSB_MII_MAX_WAIT_MS 100 + +/** MAC address */ +union smscusb_mac { + /** MAC receive address registers */ + struct { + /** MAC receive address low register */ + uint32_t l; + /** MAC receive address high register */ + uint32_t h; + } __attribute__ (( packed )) addr; + /** Raw MAC address */ + uint8_t raw[ETH_ALEN]; +}; + +/** MAC receive address high register offset */ +#define SMSCUSB_RX_ADDRH 0x000 + +/** MAC receive address low register offset */ +#define SMSCUSB_RX_ADDRL 0x004 + +/** MAC address perfect filter N high register offset */ +#define SMSCUSB_ADDR_FILTH(n) ( 0x000 + ( 8 * (n) ) ) +#define SMSCUSB_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ + +/** MAC address perfect filter N low register offset */ +#define SMSCUSB_ADDR_FILTL(n) ( 0x004 + ( 8 * (n) ) ) + +/** Interrupt packet format */ +struct smscusb_interrupt { + /** Current value of INT_STS register */ + uint32_t int_sts; +} __attribute__ (( packed )); + +/** An SMSC USB device */ +struct smscusb_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + /** MII interface */ + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; + /** MII register base */ + uint16_t mii_base; + /** PHY interrupt source register */ + uint16_t phy_source; + /** Interrupt status */ + uint32_t int_sts; +}; + +extern int smscusb_raw_writel ( struct smscusb_device *smscusb, + unsigned int address, uint32_t value ); +extern int smscusb_raw_readl ( struct smscusb_device *smscusb, + unsigned int address, uint32_t *value ); + +/** + * Write register + * + * @v smscusb SMSC USB device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smscusb_writel ( struct smscusb_device *smscusb, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = smscusb_raw_writel ( smscusb, address, + cpu_to_le32 ( value ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read register + * + * @v smscusb SMSC USB device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smscusb_readl ( struct smscusb_device *smscusb, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = smscusb_raw_readl ( smscusb, address, value ) ) != 0 ) + return rc; + le32_to_cpus ( value ); + + return 0; +} + +/** + * Get statistics + * + * @v smscusb SMSC USB device + * @v index Statistics set index + * @v data Statistics data to fill in + * @v len Length of statistics data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smscusb_get_statistics ( struct smscusb_device *smscusb, unsigned int index, + void *data, size_t len ) { + int rc; + + /* Read statistics */ + if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_GET_STATISTICS, 0, + index, data, len ) ) != 0 ) { + DBGC ( smscusb, "SMSCUSB %p could not get statistics set %d: " + "%s\n", smscusb, index, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define SMSCUSB_INTR_MAX_FILL 2 + +extern struct usb_endpoint_driver_operations smscusb_intr_operations; +extern struct usb_endpoint_driver_operations smscusb_out_operations; +extern struct mii_operations smscusb_mii_operations; + +/** + * Initialise SMSC USB device + * + * @v smscusb SMSC USB device + * @v netdev Network device + * @v func USB function + * @v in Bulk IN endpoint operations + */ +static inline __attribute__ (( always_inline )) void +smscusb_init ( struct smscusb_device *smscusb, struct net_device *netdev, + struct usb_function *func, + struct usb_endpoint_driver_operations *in ) { + struct usb_device *usb = func->usb; + + smscusb->usb = usb; + smscusb->bus = usb->port->hub->bus; + smscusb->netdev = netdev; + usbnet_init ( &smscusb->usbnet, func, &smscusb_intr_operations, in, + &smscusb_out_operations ); + usb_refill_init ( &smscusb->usbnet.intr, 0, 0, SMSCUSB_INTR_MAX_FILL ); +} + +/** + * Initialise SMSC USB device MII interface + * + * @v smscusb SMSC USB device + * @v mii_base MII register base + * @v phy_source Interrupt source PHY register + */ +static inline __attribute__ (( always_inline )) void +smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base, + unsigned int phy_source ) { + + mdio_init ( &smscusb->mdio, &smscusb_mii_operations ); + mii_init ( &smscusb->mii, &smscusb->mdio, 0 ); + smscusb->mii_base = mii_base; + smscusb->phy_source = phy_source; +} + +extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, + unsigned int e2p_base ); +extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, + unsigned int otp_base ); +extern int smscusb_mii_check_link ( struct smscusb_device *smscusb ); +extern int smscusb_mii_open ( struct smscusb_device *smscusb, + unsigned int phy_mask, unsigned int intrs ); +extern int smscusb_set_address ( struct smscusb_device *smscusb, + unsigned int addr_base ); +extern int smscusb_set_filter ( struct smscusb_device *smscusb, + unsigned int filt_base ); + +#endif /* _SMSCUSB_H */ diff --git a/src/drivers/net/tg3/tg3.c b/src/drivers/net/tg3/tg3.c index 42bfa2d99..1bed06649 100644 --- a/src/drivers/net/tg3/tg3.c +++ b/src/drivers/net/tg3/tg3.c @@ -486,6 +486,8 @@ static void tg3_poll(struct net_device *dev) */ tp->hw_status->status &= ~SD_STATUS_UPDATED; + mb(); + tg3_poll_link(tp); tg3_tx_complete(dev); tg3_rx_complete(dev); @@ -545,7 +547,7 @@ static int tg3_test_dma(struct tg3 *tp) goto out_nofree; } buf_dma = virt_to_bus(buf); - DBGC2(tp->dev, "dma test buffer, virt: %p phys: %#08x\n", buf, buf_dma); + DBGC2(tp->dev, "dma test buffer, virt: %p phys: %#016lx\n", buf, buf_dma); if (tg3_flag(tp, 57765_PLUS)) { tp->dma_rwctrl = DMA_RWCTRL_DIS_CACHE_ALIGNMENT; diff --git a/src/drivers/net/tg3/tg3.h b/src/drivers/net/tg3/tg3.h index 2b85b065b..fa809c041 100644 --- a/src/drivers/net/tg3/tg3.h +++ b/src/drivers/net/tg3/tg3.h @@ -34,6 +34,13 @@ #define ADVERTISED_Autoneg (1 << 6) /* */ +#ifndef ADVERTISED_Pause +#define ADVERTISED_Pause (1 << 13) +#endif +#ifndef ADVERTISED_Asym_Pause +#define ADVERTISED_Asym_Pause (1 << 14) +#endif + /* mdio.h: */ #define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ @@ -52,7 +59,6 @@ #define PCI_X_CMD 2 /* Modes & Features */ #define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ -#define PCI_EXP_DEVCTL 8 /* Device Control */ #define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ #define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ #define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ @@ -140,9 +146,15 @@ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 +#ifndef SPEED_UNKNOWN +#define SPEED_UNKNOWN -1 +#endif #define DUPLEX_HALF 0x00 #define DUPLEX_FULL 0x01 +#ifndef DUPLEX_UNKNOWN +#define DUPLEX_UNKNOWN 0xff +#endif #define TG3_64BIT_REG_HIGH 0x00UL #define TG3_64BIT_REG_LOW 0x04UL @@ -2426,6 +2438,14 @@ #define MII_TG3_FET_SHDW_AUXSTAT2 0x1b #define MII_TG3_FET_SHDW_AUXSTAT2_APD 0x0020 +/* Serdes PHY Register Definitions */ +#define SERDES_TG3_1000X_STATUS 0x14 +#define SERDES_TG3_SGMII_MODE 0x0001 +#define SERDES_TG3_LINK_UP 0x0002 +#define SERDES_TG3_FULL_DUPLEX 0x0004 +#define SERDES_TG3_SPEED_100 0x0008 +#define SERDES_TG3_SPEED_1000 0x0010 + /* APE registers. Accessible through BAR1 */ #define TG3_APE_EVENT 0x000c @@ -2789,7 +2809,7 @@ struct tg3_hw_stats { u8 __reserved4[0xb00-0x9c8]; }; -typedef u32 dma_addr_t; +typedef unsigned long dma_addr_t; /* 'mapping' is superfluous as the chip does not write into * the tx/rx post rings so we could just fetch it from there. @@ -2816,6 +2836,7 @@ struct tg3_link_config { #define DUPLEX_INVALID 0xff #define AUTONEG_INVALID 0xff u16 active_speed; + u32 rmt_adv; /* When we go in and out of low power mode we need * to swap with this state. @@ -3283,10 +3304,9 @@ struct tg3 { u16 subsystem_vendor; u16 subsystem_device; + int link_up; }; -#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) ) - #define TG3_TX_RING_SIZE 512 #define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1) @@ -3321,43 +3341,25 @@ void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val); /* Functions & macros to verify TG3_FLAGS types */ -static inline int variable_test_bit(int nr, volatile const unsigned long *addr) -{ - int oldbit; - - asm volatile("bt %2,%1\n\t" - "sbb %0,%0" - : "=r" (oldbit) - : "m" (*(unsigned long *)addr), "Ir" (nr)); - - return oldbit; -} - static inline int _tg3_flag(enum TG3_FLAGS flag, unsigned long *bits) { - return variable_test_bit(flag, bits); -} - -#define BITOP_ADDR(x) "+m" (*(volatile long *) (x)) - -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - asm volatile("bts %1,%0" : BITOP_ADDR(addr) : "Ir" (nr) : "memory"); + unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); + unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); + return ( !! ( bits[index] & ( 1UL << bit ) ) ); } static inline void _tg3_flag_set(enum TG3_FLAGS flag, unsigned long *bits) { - __set_bit(flag, bits); -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - asm volatile("btr %1,%0" : BITOP_ADDR(addr) : "Ir" (nr)); + unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); + unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); + bits[index] |= ( 1UL << bit ); } static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) { - __clear_bit(flag, bits); + unsigned int index = ( flag / ( 8 * sizeof ( *bits ) ) ); + unsigned int bit = ( flag % ( 8 * sizeof ( *bits ) ) ); + bits[index] &= ~( 1UL << bit ); } #define tg3_flag(tp, flag) \ @@ -3431,6 +3433,39 @@ static inline u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv) return cap; } +static inline u32 mii_adv_to_ethtool_adv_x(u32 adv) +{ + u32 result = 0; + + if (adv & ADVERTISE_1000XHALF) + result |= ADVERTISED_1000baseT_Half; + if (adv & ADVERTISE_1000XFULL) + result |= ADVERTISED_1000baseT_Full; + if (adv & ADVERTISE_1000XPAUSE) + result |= ADVERTISED_Pause; + if (adv & ADVERTISE_1000XPSE_ASYM) + result |= ADVERTISED_Asym_Pause; + + return result; +} + +static inline u32 ethtool_adv_to_mii_adv_x(u32 ethadv) +{ + u32 result = 0; + + if (ethadv & ADVERTISED_1000baseT_Half) + result |= ADVERTISE_1000XHALF; + if (ethadv & ADVERTISED_1000baseT_Full) + result |= ADVERTISE_1000XFULL; + if (ethadv & ADVERTISED_Pause) + result |= ADVERTISE_1000XPAUSE; + if (ethadv & ADVERTISED_Asym_Pause) + result |= ADVERTISE_1000XPSE_ASYM; + + return result; +} + + #define ETH_FCS_LEN 4 #endif /* !(_T3_H) */ diff --git a/src/drivers/net/tg3/tg3_hw.c b/src/drivers/net/tg3/tg3_hw.c index 50353cf36..798f8519f 100644 --- a/src/drivers/net/tg3/tg3_hw.c +++ b/src/drivers/net/tg3/tg3_hw.c @@ -2518,28 +2518,40 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) switch (limit) { case 16: tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0); + /* Fall through */ case 15: tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0); + /* Fall through */ case 14: tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0); + /* Fall through */ case 13: tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0); + /* Fall through */ case 12: tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0); + /* Fall through */ case 11: tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0); + /* Fall through */ case 10: tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0); + /* Fall through */ case 9: tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0); + /* Fall through */ case 8: tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0); + /* Fall through */ case 7: tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0); + /* Fall through */ case 6: tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0); + /* Fall through */ case 5: tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0); + /* Fall through */ case 4: /* tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); */ case 3: diff --git a/src/drivers/net/tg3/tg3_phy.c b/src/drivers/net/tg3/tg3_phy.c index 65dea7e66..e88b0be0f 100644 --- a/src/drivers/net/tg3/tg3_phy.c +++ b/src/drivers/net/tg3/tg3_phy.c @@ -1287,6 +1287,970 @@ static void tg3_link_report(struct tg3 *tp) } #endif +struct tg3_fiber_aneginfo { + int state; +#define ANEG_STATE_UNKNOWN 0 +#define ANEG_STATE_AN_ENABLE 1 +#define ANEG_STATE_RESTART_INIT 2 +#define ANEG_STATE_RESTART 3 +#define ANEG_STATE_DISABLE_LINK_OK 4 +#define ANEG_STATE_ABILITY_DETECT_INIT 5 +#define ANEG_STATE_ABILITY_DETECT 6 +#define ANEG_STATE_ACK_DETECT_INIT 7 +#define ANEG_STATE_ACK_DETECT 8 +#define ANEG_STATE_COMPLETE_ACK_INIT 9 +#define ANEG_STATE_COMPLETE_ACK 10 +#define ANEG_STATE_IDLE_DETECT_INIT 11 +#define ANEG_STATE_IDLE_DETECT 12 +#define ANEG_STATE_LINK_OK 13 +#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14 +#define ANEG_STATE_NEXT_PAGE_WAIT 15 + + u32 flags; +#define MR_AN_ENABLE 0x00000001 +#define MR_RESTART_AN 0x00000002 +#define MR_AN_COMPLETE 0x00000004 +#define MR_PAGE_RX 0x00000008 +#define MR_NP_LOADED 0x00000010 +#define MR_TOGGLE_TX 0x00000020 +#define MR_LP_ADV_FULL_DUPLEX 0x00000040 +#define MR_LP_ADV_HALF_DUPLEX 0x00000080 +#define MR_LP_ADV_SYM_PAUSE 0x00000100 +#define MR_LP_ADV_ASYM_PAUSE 0x00000200 +#define MR_LP_ADV_REMOTE_FAULT1 0x00000400 +#define MR_LP_ADV_REMOTE_FAULT2 0x00000800 +#define MR_LP_ADV_NEXT_PAGE 0x00001000 +#define MR_TOGGLE_RX 0x00002000 +#define MR_NP_RX 0x00004000 + +#define MR_LINK_OK 0x80000000 + + unsigned long link_time, cur_time; + + u32 ability_match_cfg; + int ability_match_count; + + char ability_match, idle_match, ack_match; + + u32 txconfig, rxconfig; +#define ANEG_CFG_NP 0x00000080 +#define ANEG_CFG_ACK 0x00000040 +#define ANEG_CFG_RF2 0x00000020 +#define ANEG_CFG_RF1 0x00000010 +#define ANEG_CFG_PS2 0x00000001 +#define ANEG_CFG_PS1 0x00008000 +#define ANEG_CFG_HD 0x00004000 +#define ANEG_CFG_FD 0x00002000 +#define ANEG_CFG_INVAL 0x00001f06 + +}; +#define ANEG_OK 0 +#define ANEG_DONE 1 +#define ANEG_TIMER_ENAB 2 +#define ANEG_FAILED -1 + +#define ANEG_STATE_SETTLE_TIME 10000 + +static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl) +{ + u16 miireg; + + if ((flow_ctrl & FLOW_CTRL_TX) && (flow_ctrl & FLOW_CTRL_RX)) + miireg = ADVERTISE_1000XPAUSE; + else if (flow_ctrl & FLOW_CTRL_TX) + miireg = ADVERTISE_1000XPSE_ASYM; + else if (flow_ctrl & FLOW_CTRL_RX) + miireg = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; + else + miireg = 0; + + return miireg; +} + +static void tg3_init_bcm8002(struct tg3 *tp) +{ + u32 mac_status = tr32(MAC_STATUS); + int i; + + /* Reset when initting first time or we have a link. */ + if (tg3_flag(tp, INIT_COMPLETE) && + !(mac_status & MAC_STATUS_PCS_SYNCED)) + return; + + /* Set PLL lock range. */ + tg3_writephy(tp, 0x16, 0x8007); + + /* SW reset */ + tg3_writephy(tp, MII_BMCR, BMCR_RESET); + + /* Wait for reset to complete. */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 500; i++) + udelay(10); + + /* Config mode; select PMA/Ch 1 regs. */ + tg3_writephy(tp, 0x10, 0x8411); + + /* Enable auto-lock and comdet, select txclk for tx. */ + tg3_writephy(tp, 0x11, 0x0a10); + + tg3_writephy(tp, 0x18, 0x00a0); + tg3_writephy(tp, 0x16, 0x41ff); + + /* Assert and deassert POR. */ + tg3_writephy(tp, 0x13, 0x0400); + udelay(40); + tg3_writephy(tp, 0x13, 0x0000); + + tg3_writephy(tp, 0x11, 0x0a50); + udelay(40); + tg3_writephy(tp, 0x11, 0x0a10); + + /* Wait for signal to stabilize */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 15000; i++) + udelay(10); + + /* Deselect the channel register so we can read the PHYID + * later. + */ + tg3_writephy(tp, 0x10, 0x8011); +} + +static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) +{ + u16 flowctrl; + int current_link_up; + u32 sg_dig_ctrl, sg_dig_status; + u32 serdes_cfg, expected_sg_dig_ctrl; + int workaround, port_a; + + serdes_cfg = 0; + expected_sg_dig_ctrl = 0; + workaround = 0; + port_a = 1; + current_link_up = 0; + + if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && + tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { + workaround = 1; + if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) + port_a = 0; + + /* preserve bits 0-11,13,14 for signal pre-emphasis */ + /* preserve bits 20-23 for voltage regulator */ + serdes_cfg = tr32(MAC_SERDES_CFG) & 0x00f06fff; + } + + sg_dig_ctrl = tr32(SG_DIG_CTRL); + + if (tp->link_config.autoneg != AUTONEG_ENABLE) { + if (sg_dig_ctrl & SG_DIG_USING_HW_AUTONEG) { + if (workaround) { + u32 val = serdes_cfg; + + if (port_a) + val |= 0xc010000; + else + val |= 0x4010000; + tw32_f(MAC_SERDES_CFG, val); + } + + tw32_f(SG_DIG_CTRL, SG_DIG_COMMON_SETUP); + } + if (mac_status & MAC_STATUS_PCS_SYNCED) { + tg3_setup_flow_control(tp, 0, 0); + current_link_up = 1; + } + goto out; + } + + /* Want auto-negotiation. */ + expected_sg_dig_ctrl = SG_DIG_USING_HW_AUTONEG | SG_DIG_COMMON_SETUP; + + flowctrl = tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); + if (flowctrl & ADVERTISE_1000XPAUSE) + expected_sg_dig_ctrl |= SG_DIG_PAUSE_CAP; + if (flowctrl & ADVERTISE_1000XPSE_ASYM) + expected_sg_dig_ctrl |= SG_DIG_ASYM_PAUSE; + + if (sg_dig_ctrl != expected_sg_dig_ctrl) { + if ((tp->phy_flags & TG3_PHYFLG_PARALLEL_DETECT) && + tp->serdes_counter && + ((mac_status & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_RCVD_CFG)) == + MAC_STATUS_PCS_SYNCED)) { + tp->serdes_counter--; + current_link_up = 1; + goto out; + } +restart_autoneg: + if (workaround) + tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000); + tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | SG_DIG_SOFT_RESET); + udelay(5); + tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); + + tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } else if (mac_status & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET)) { + sg_dig_status = tr32(SG_DIG_STATUS); + mac_status = tr32(MAC_STATUS); + + if ((sg_dig_status & SG_DIG_AUTONEG_COMPLETE) && + (mac_status & MAC_STATUS_PCS_SYNCED)) { + u32 local_adv = 0, remote_adv = 0; + + if (sg_dig_ctrl & SG_DIG_PAUSE_CAP) + local_adv |= ADVERTISE_1000XPAUSE; + if (sg_dig_ctrl & SG_DIG_ASYM_PAUSE) + local_adv |= ADVERTISE_1000XPSE_ASYM; + + if (sg_dig_status & SG_DIG_PARTNER_PAUSE_CAPABLE) + remote_adv |= LPA_1000XPAUSE; + if (sg_dig_status & SG_DIG_PARTNER_ASYM_PAUSE) + remote_adv |= LPA_1000XPAUSE_ASYM; + + tp->link_config.rmt_adv = + mii_adv_to_ethtool_adv_x(remote_adv); + + tg3_setup_flow_control(tp, local_adv, remote_adv); + current_link_up = 1; + tp->serdes_counter = 0; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } else if (!(sg_dig_status & SG_DIG_AUTONEG_COMPLETE)) { + if (tp->serdes_counter) + tp->serdes_counter--; + else { + if (workaround) { + u32 val = serdes_cfg; + + if (port_a) + val |= 0xc010000; + else + val |= 0x4010000; + + tw32_f(MAC_SERDES_CFG, val); + } + + tw32_f(SG_DIG_CTRL, SG_DIG_COMMON_SETUP); + udelay(40); + + /* Link parallel detection - link is up */ + /* only if we have PCS_SYNC and not */ + /* receiving config code words */ + mac_status = tr32(MAC_STATUS); + if ((mac_status & MAC_STATUS_PCS_SYNCED) && + !(mac_status & MAC_STATUS_RCVD_CFG)) { + tg3_setup_flow_control(tp, 0, 0); + current_link_up = 1; + tp->phy_flags |= + TG3_PHYFLG_PARALLEL_DETECT; + tp->serdes_counter = + SERDES_PARALLEL_DET_TIMEOUT; + } else + goto restart_autoneg; + } + } + } else { + tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } + +out: + return current_link_up; +} + +static int tg3_fiber_aneg_smachine(struct tg3 *tp, + struct tg3_fiber_aneginfo *ap) +{ + u16 flowctrl; + unsigned long delta; + u32 rx_cfg_reg; + int ret; + + if (ap->state == ANEG_STATE_UNKNOWN) { + ap->rxconfig = 0; + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + } + ap->cur_time++; + + if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { + rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); + + if (rx_cfg_reg != ap->ability_match_cfg) { + ap->ability_match_cfg = rx_cfg_reg; + ap->ability_match = 0; + ap->ability_match_count = 0; + } else { + if (++ap->ability_match_count > 1) { + ap->ability_match = 1; + ap->ability_match_cfg = rx_cfg_reg; + } + } + if (rx_cfg_reg & ANEG_CFG_ACK) + ap->ack_match = 1; + else + ap->ack_match = 0; + + ap->idle_match = 0; + } else { + ap->idle_match = 1; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->ack_match = 0; + + rx_cfg_reg = 0; + } + + ap->rxconfig = rx_cfg_reg; + ret = ANEG_OK; + + switch (ap->state) { + case ANEG_STATE_UNKNOWN: + if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) + ap->state = ANEG_STATE_AN_ENABLE; + + /* fallthru */ + case ANEG_STATE_AN_ENABLE: + ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); + if (ap->flags & MR_AN_ENABLE) { + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + + ap->state = ANEG_STATE_RESTART_INIT; + } else { + ap->state = ANEG_STATE_DISABLE_LINK_OK; + } + break; + + case ANEG_STATE_RESTART_INIT: + ap->link_time = ap->cur_time; + ap->flags &= ~(MR_NP_LOADED); + ap->txconfig = 0; + tw32(MAC_TX_AUTO_NEG, 0); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ret = ANEG_TIMER_ENAB; + ap->state = ANEG_STATE_RESTART; + + /* fallthru */ + case ANEG_STATE_RESTART: + delta = ap->cur_time - ap->link_time; + if (delta > ANEG_STATE_SETTLE_TIME) + ap->state = ANEG_STATE_ABILITY_DETECT_INIT; + else + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_DISABLE_LINK_OK: + ret = ANEG_DONE; + break; + + case ANEG_STATE_ABILITY_DETECT_INIT: + ap->flags &= ~(MR_TOGGLE_TX); + ap->txconfig = ANEG_CFG_FD; + flowctrl = tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); + if (flowctrl & ADVERTISE_1000XPAUSE) + ap->txconfig |= ANEG_CFG_PS1; + if (flowctrl & ADVERTISE_1000XPSE_ASYM) + ap->txconfig |= ANEG_CFG_PS2; + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ap->state = ANEG_STATE_ABILITY_DETECT; + break; + + case ANEG_STATE_ABILITY_DETECT: + if (ap->ability_match != 0 && ap->rxconfig != 0) + ap->state = ANEG_STATE_ACK_DETECT_INIT; + break; + + case ANEG_STATE_ACK_DETECT_INIT: + ap->txconfig |= ANEG_CFG_ACK; + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ap->state = ANEG_STATE_ACK_DETECT; + + /* fallthru */ + case ANEG_STATE_ACK_DETECT: + if (ap->ack_match != 0) { + if ((ap->rxconfig & ~ANEG_CFG_ACK) == + (ap->ability_match_cfg & ~ANEG_CFG_ACK)) { + ap->state = ANEG_STATE_COMPLETE_ACK_INIT; + } else { + ap->state = ANEG_STATE_AN_ENABLE; + } + } else if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + } + break; + + case ANEG_STATE_COMPLETE_ACK_INIT: + if (ap->rxconfig & ANEG_CFG_INVAL) { + ret = ANEG_FAILED; + break; + } + ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | + MR_LP_ADV_HALF_DUPLEX | + MR_LP_ADV_SYM_PAUSE | + MR_LP_ADV_ASYM_PAUSE | + MR_LP_ADV_REMOTE_FAULT1 | + MR_LP_ADV_REMOTE_FAULT2 | + MR_LP_ADV_NEXT_PAGE | + MR_TOGGLE_RX | + MR_NP_RX); + if (ap->rxconfig & ANEG_CFG_FD) + ap->flags |= MR_LP_ADV_FULL_DUPLEX; + if (ap->rxconfig & ANEG_CFG_HD) + ap->flags |= MR_LP_ADV_HALF_DUPLEX; + if (ap->rxconfig & ANEG_CFG_PS1) + ap->flags |= MR_LP_ADV_SYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_PS2) + ap->flags |= MR_LP_ADV_ASYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_RF1) + ap->flags |= MR_LP_ADV_REMOTE_FAULT1; + if (ap->rxconfig & ANEG_CFG_RF2) + ap->flags |= MR_LP_ADV_REMOTE_FAULT2; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_LP_ADV_NEXT_PAGE; + + ap->link_time = ap->cur_time; + + ap->flags ^= (MR_TOGGLE_TX); + if (ap->rxconfig & 0x0008) + ap->flags |= MR_TOGGLE_RX; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_NP_RX; + ap->flags |= MR_PAGE_RX; + + ap->state = ANEG_STATE_COMPLETE_ACK; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_COMPLETE_ACK: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > ANEG_STATE_SETTLE_TIME) { + if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + } else { + if ((ap->txconfig & ANEG_CFG_NP) == 0 && + !(ap->flags & MR_NP_RX)) { + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + } else { + ret = ANEG_FAILED; + } + } + } + break; + + case ANEG_STATE_IDLE_DETECT_INIT: + ap->link_time = ap->cur_time; + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + ap->state = ANEG_STATE_IDLE_DETECT; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_IDLE_DETECT: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > ANEG_STATE_SETTLE_TIME) { + /* XXX another gem from the Broadcom driver :( */ + ap->state = ANEG_STATE_LINK_OK; + } + break; + + case ANEG_STATE_LINK_OK: + ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); + ret = ANEG_DONE; + break; + + case ANEG_STATE_NEXT_PAGE_WAIT_INIT: + /* ??? unimplemented */ + break; + + case ANEG_STATE_NEXT_PAGE_WAIT: + /* ??? unimplemented */ + break; + + default: + ret = ANEG_FAILED; + break; + } + + return ret; +} + +static int fiber_autoneg(struct tg3 *tp, u32 *txflags, u32 *rxflags) +{ + int res = 0; + struct tg3_fiber_aneginfo aninfo; + int status = ANEG_FAILED; + unsigned int tick; + u32 tmp; + + tw32_f(MAC_TX_AUTO_NEG, 0); + + tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; + tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); + udelay(40); + + tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); + udelay(40); + + memset(&aninfo, 0, sizeof(aninfo)); + aninfo.flags |= MR_AN_ENABLE; + aninfo.state = ANEG_STATE_UNKNOWN; + aninfo.cur_time = 0; + tick = 0; + while (++tick < 195000) { + status = tg3_fiber_aneg_smachine(tp, &aninfo); + if (status == ANEG_DONE || status == ANEG_FAILED) + break; + + udelay(1); + } + + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + *txflags = aninfo.txconfig; + *rxflags = aninfo.flags; + + if (status == ANEG_DONE && + (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | + MR_LP_ADV_FULL_DUPLEX))) + res = 1; + + return res; +} + +static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status) +{ + int current_link_up = 0; + + if (!(mac_status & MAC_STATUS_PCS_SYNCED)) + goto out; + + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + u32 txflags, rxflags; + int i; + + if (fiber_autoneg(tp, &txflags, &rxflags)) { + u32 local_adv = 0, remote_adv = 0; + + if (txflags & ANEG_CFG_PS1) + local_adv |= ADVERTISE_1000XPAUSE; + if (txflags & ANEG_CFG_PS2) + local_adv |= ADVERTISE_1000XPSE_ASYM; + + if (rxflags & MR_LP_ADV_SYM_PAUSE) + remote_adv |= LPA_1000XPAUSE; + if (rxflags & MR_LP_ADV_ASYM_PAUSE) + remote_adv |= LPA_1000XPAUSE_ASYM; + + tp->link_config.rmt_adv = + mii_adv_to_ethtool_adv_x(remote_adv); + + tg3_setup_flow_control(tp, local_adv, remote_adv); + + current_link_up = 1; + } + for (i = 0; i < 30; i++) { + udelay(20); + tw32_f(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(40); + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + + mac_status = tr32(MAC_STATUS); + if (!current_link_up && + (mac_status & MAC_STATUS_PCS_SYNCED) && + !(mac_status & MAC_STATUS_RCVD_CFG)) + current_link_up = 1; + } else { + tg3_setup_flow_control(tp, 0, 0); + + /* Forcing 1000FD link up. */ + current_link_up = 1; + + tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); + udelay(40); + + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + } + +out: + return current_link_up; +} + +static int tg3_test_and_report_link_chg(struct tg3 *tp, int curr_link_up) +{ + if (curr_link_up != tp->link_up) { + if (curr_link_up) { + netdev_link_up(tp->dev); + } else { + netdev_link_down(tp->dev); + if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } + + tg3_link_report(tp); + return 1; + } + + return 0; +} + +static void tg3_clear_mac_status(struct tg3 *tp) +{ + tw32(MAC_EVENT, 0); + + tw32_f(MAC_STATUS, + MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_MI_COMPLETION | + MAC_STATUS_LNKSTATE_CHANGED); + udelay(40); +} + +static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) +{ + u32 orig_pause_cfg; + u16 orig_active_speed; + u8 orig_active_duplex; + u32 mac_status; + int current_link_up = force_reset; + int i; + + orig_pause_cfg = tp->link_config.active_flowctrl; + orig_active_speed = tp->link_config.active_speed; + orig_active_duplex = tp->link_config.active_duplex; + + if (!tg3_flag(tp, HW_AUTONEG) && + tp->link_up && + tg3_flag(tp, INIT_COMPLETE)) { + mac_status = tr32(MAC_STATUS); + mac_status &= (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_RCVD_CFG); + if (mac_status == (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET)) { + tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + return 0; + } + } + + tw32_f(MAC_TX_AUTO_NEG, 0); + + tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); + tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + if (tp->phy_id == TG3_PHY_ID_BCM8002) + tg3_init_bcm8002(tp); + + /* Enable link change event even when serdes polling. */ + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + udelay(40); + + current_link_up = 0; + tp->link_config.rmt_adv = 0; + mac_status = tr32(MAC_STATUS); + + if (tg3_flag(tp, HW_AUTONEG)) + current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); + else + current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); + + tp->hw_status->status = + (SD_STATUS_UPDATED | + (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); + + for (i = 0; i < 100; i++) { + tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(5); + if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_LNKSTATE_CHANGED)) == 0) + break; + } + + mac_status = tr32(MAC_STATUS); + if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { + current_link_up = 0; + if (tp->link_config.autoneg == AUTONEG_ENABLE && + tp->serdes_counter == 0) { + tw32_f(MAC_MODE, (tp->mac_mode | + MAC_MODE_SEND_CONFIGS)); + udelay(1); + tw32_f(MAC_MODE, tp->mac_mode); + } + } + + if (current_link_up) { + tp->link_config.active_speed = SPEED_1000; + tp->link_config.active_duplex = DUPLEX_FULL; + tw32(MAC_LED_CTRL, (tp->led_ctrl | + LED_CTRL_LNKLED_OVERRIDE | + LED_CTRL_1000MBPS_ON)); + } else { + tp->link_config.active_speed = SPEED_UNKNOWN; + tp->link_config.active_duplex = DUPLEX_UNKNOWN; + tw32(MAC_LED_CTRL, (tp->led_ctrl | + LED_CTRL_LNKLED_OVERRIDE | + LED_CTRL_TRAFFIC_OVERRIDE)); + } + + if (!tg3_test_and_report_link_chg(tp, current_link_up)) { + u32 now_pause_cfg = tp->link_config.active_flowctrl; + if (orig_pause_cfg != now_pause_cfg || + orig_active_speed != tp->link_config.active_speed || + orig_active_duplex != tp->link_config.active_duplex) + tg3_link_report(tp); + } + + return 0; +} + +static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) +{ + int err = 0; + u32 bmsr, bmcr; + u16 current_speed = SPEED_UNKNOWN; + u8 current_duplex = DUPLEX_UNKNOWN; + int current_link_up = 0; + u32 local_adv, remote_adv, sgsr; + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) && + !tg3_readphy(tp, SERDES_TG3_1000X_STATUS, &sgsr) && + (sgsr & SERDES_TG3_SGMII_MODE)) { + + if (force_reset) + tg3_phy_reset(tp); + + tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; + + if (!(sgsr & SERDES_TG3_LINK_UP)) { + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + } else { + current_link_up = 1; + if (sgsr & SERDES_TG3_SPEED_1000) { + current_speed = SPEED_1000; + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + } else if (sgsr & SERDES_TG3_SPEED_100) { + current_speed = SPEED_100; + tp->mac_mode |= MAC_MODE_PORT_MODE_MII; + } else { + current_speed = SPEED_10; + tp->mac_mode |= MAC_MODE_PORT_MODE_MII; + } + + if (sgsr & SERDES_TG3_FULL_DUPLEX) + current_duplex = DUPLEX_FULL; + else + current_duplex = DUPLEX_HALF; + } + + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + tg3_clear_mac_status(tp); + + goto fiber_setup_done; + } + + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + tg3_clear_mac_status(tp); + + if (force_reset) + tg3_phy_reset(tp); + + tp->link_config.rmt_adv = 0; + + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { + if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) + bmsr |= BMSR_LSTATUS; + else + bmsr &= ~BMSR_LSTATUS; + } + + err |= tg3_readphy(tp, MII_BMCR, &bmcr); + + if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && + (tp->phy_flags & TG3_PHYFLG_PARALLEL_DETECT)) { + /* do nothing, just check for link up at the end */ + } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { + u32 adv, newadv; + + err |= tg3_readphy(tp, MII_ADVERTISE, &adv); + newadv = adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | + ADVERTISE_1000XPAUSE | + ADVERTISE_1000XPSE_ASYM | + ADVERTISE_SLCT); + + newadv |= tg3_advert_flowctrl_1000X(tp->link_config.flowctrl); + newadv |= ethtool_adv_to_mii_adv_x(tp->link_config.advertising); + + if ((newadv != adv) || !(bmcr & BMCR_ANENABLE)) { + tg3_writephy(tp, MII_ADVERTISE, newadv); + bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; + tg3_writephy(tp, MII_BMCR, bmcr); + + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + tp->serdes_counter = SERDES_AN_TIMEOUT_5714S; + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + + return err; + } + } else { + u32 new_bmcr; + + bmcr &= ~BMCR_SPEED1000; + new_bmcr = bmcr & ~(BMCR_ANENABLE | BMCR_FULLDPLX); + + if (tp->link_config.duplex == DUPLEX_FULL) + new_bmcr |= BMCR_FULLDPLX; + + if (new_bmcr != bmcr) { + /* BMCR_SPEED1000 is a reserved bit that needs + * to be set on write. + */ + new_bmcr |= BMCR_SPEED1000; + + /* Force a linkdown */ + if (tp->link_up) { + u32 adv; + + err |= tg3_readphy(tp, MII_ADVERTISE, &adv); + adv &= ~(ADVERTISE_1000XFULL | + ADVERTISE_1000XHALF | + ADVERTISE_SLCT); + tg3_writephy(tp, MII_ADVERTISE, adv); + tg3_writephy(tp, MII_BMCR, bmcr | + BMCR_ANRESTART | + BMCR_ANENABLE); + udelay(10); + netdev_link_down(tp->dev); + } + tg3_writephy(tp, MII_BMCR, new_bmcr); + bmcr = new_bmcr; + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + err |= tg3_readphy(tp, MII_BMSR, &bmsr); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { + if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) + bmsr |= BMSR_LSTATUS; + else + bmsr &= ~BMSR_LSTATUS; + } + tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT; + } + } + + if (bmsr & BMSR_LSTATUS) { + current_speed = SPEED_1000; + current_link_up = 1; + if (bmcr & BMCR_FULLDPLX) + current_duplex = DUPLEX_FULL; + else + current_duplex = DUPLEX_HALF; + + local_adv = 0; + remote_adv = 0; + + if (bmcr & BMCR_ANENABLE) { + u32 common; + + err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); + err |= tg3_readphy(tp, MII_LPA, &remote_adv); + common = local_adv & remote_adv; + if (common & (ADVERTISE_1000XHALF | + ADVERTISE_1000XFULL)) { + if (common & ADVERTISE_1000XFULL) + current_duplex = DUPLEX_FULL; + else + current_duplex = DUPLEX_HALF; + + tp->link_config.rmt_adv = + mii_adv_to_ethtool_adv_x(remote_adv); + } else if (!tg3_flag(tp, 5780_CLASS)) { + /* Link is up via parallel detect */ + } else { + current_link_up = 0; + } + } + } + +fiber_setup_done: + if (current_link_up && current_duplex == DUPLEX_FULL) + tg3_setup_flow_control(tp, local_adv, remote_adv); + + tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; + if (tp->link_config.active_duplex == DUPLEX_HALF) + tp->mac_mode |= MAC_MODE_HALF_DUPLEX; + + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + + tp->link_config.active_speed = current_speed; + tp->link_config.active_duplex = current_duplex; + + tg3_test_and_report_link_chg(tp, current_link_up); + return err; +} + static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) { DBGP("%s\n", __func__); @@ -1559,15 +2523,12 @@ int tg3_setup_phy(struct tg3 *tp, int force_reset) u32 val; int err; -#if 0 if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) err = tg3_setup_fiber_phy(tp, force_reset); else if (tp->phy_flags & TG3_PHYFLG_MII_SERDES) err = tg3_setup_fiber_mii_phy(tp, force_reset); else -#endif - /* FIXME: add only copper phy variants for now */ - err = tg3_setup_copper_phy(tp, force_reset); + err = tg3_setup_copper_phy(tp, force_reset); val = (2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT); diff --git a/src/drivers/net/thunderx.c b/src/drivers/net/thunderx.c new file mode 100644 index 000000000..9ddb98ab8 --- /dev/null +++ b/src/drivers/net/thunderx.c @@ -0,0 +1,1716 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thunderx.h" +#include "thunderxcfg.h" + +/** @file + * + * Cavium ThunderX Ethernet driver + * + */ + +/** List of BGX Ethernet interfaces */ +static LIST_HEAD ( txnic_bgxs ); + +/** List of physical functions */ +static LIST_HEAD ( txnic_pfs ); + +/** Debug colour for physical function and BGX messages */ +#define TXNICCOL(x) ( &txnic_pfs + (x)->node ) + +/** Board configuration protocol */ +static EFI_THUNDER_CONFIG_PROTOCOL *txcfg; +EFI_REQUEST_PROTOCOL ( EFI_THUNDER_CONFIG_PROTOCOL, &txcfg ); + +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Show virtual NIC diagnostics (for debugging) + * + * @v vnic Virtual NIC + */ +static __attribute__ (( unused )) void txnic_diag ( struct txnic *vnic ) { + + DBGC ( vnic, "TXNIC %s SQ %05zx(%05llx)/%05zx(%05llx) %08llx\n", + vnic->name, + ( ( vnic->sq.prod % TXNIC_SQES ) * TXNIC_SQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_SQ_TAIL(0) ), + ( ( vnic->sq.cons % TXNIC_SQES ) * TXNIC_SQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_SQ_HEAD(0) ), + readq ( vnic->regs + TXNIC_QS_SQ_STATUS(0) ) ); + DBGC ( vnic, "TXNIC %s RQ %05zx(%05llx)/%05zx(%05llx) %016llx\n", + vnic->name, + ( ( vnic->rq.prod % TXNIC_RQES ) * TXNIC_RQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_RBDR_TAIL(0) ), + ( ( vnic->rq.cons % TXNIC_RQES ) * TXNIC_RQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_RBDR_HEAD(0) ), + readq ( vnic->regs + TXNIC_QS_RBDR_STATUS0(0) ) ); + DBGC ( vnic, "TXNIC %s CQ xxxxx(%05llx)/%05x(%05llx) %08llx:%08llx\n", + vnic->name, readq ( vnic->regs + TXNIC_QS_CQ_TAIL(0) ), + ( ( vnic->cq.cons % TXNIC_CQES ) * TXNIC_CQ_STRIDE ), + readq ( vnic->regs + TXNIC_QS_CQ_HEAD(0) ), + readq ( vnic->regs + TXNIC_QS_CQ_STATUS(0) ), + readq ( vnic->regs + TXNIC_QS_CQ_STATUS2(0) ) ); +} + +/****************************************************************************** + * + * Send queue + * + ****************************************************************************** + */ + +/** + * Create send queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_create_sq ( struct txnic *vnic ) { + + /* Reset send queue */ + vnic->sq.prod = 0; + vnic->sq.cons = 0; + writeq ( TXNIC_QS_SQ_CFG_RESET, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); + + /* Configure and enable send queue */ + writeq ( user_to_phys ( vnic->sq.sqe, 0 ), + ( vnic->regs + TXNIC_QS_SQ_BASE(0) ) ); + writeq ( ( TXNIC_QS_SQ_CFG_ENA | TXNIC_QS_SQ_CFG_QSIZE_1K ), + ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); + + DBGC ( vnic, "TXNIC %s SQ at [%08lx,%08lx)\n", + vnic->name, user_to_phys ( vnic->sq.sqe, 0 ), + user_to_phys ( vnic->sq.sqe, TXNIC_SQ_SIZE ) ); + return 0; +} + +/** + * Disable send queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_disable_sq ( struct txnic *vnic ) { + uint64_t status; + unsigned int i; + + /* Disable send queue */ + writeq ( 0, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); + + /* Wait for send queue to be stopped */ + for ( i = 0 ; i < TXNIC_SQ_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if send queue is stopped */ + status = readq ( vnic->regs + TXNIC_QS_SQ_STATUS(0) ); + if ( status & TXNIC_QS_SQ_STATUS_STOPPED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( vnic, "TXNIC %s SQ disable timed out\n", vnic->name ); + return -ETIMEDOUT; +} + +/** + * Destroy send queue + * + * @v vnic Virtual NIC + */ +static void txnic_destroy_sq ( struct txnic *vnic ) { + int rc; + + /* Disable send queue */ + if ( ( rc = txnic_disable_sq ( vnic ) ) != 0 ) { + /* Nothing else we can do */ + return; + } + + /* Reset send queue */ + writeq ( TXNIC_QS_SQ_CFG_RESET, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) ); +} + +/** + * Send packet + * + * @v vnic Virtual NIC + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int txnic_send ( struct txnic *vnic, struct io_buffer *iobuf ) { + struct txnic_sqe sqe; + unsigned int sq_idx; + size_t offset; + size_t len; + + /* Get next send queue entry */ + if ( ( vnic->sq.prod - vnic->sq.cons ) >= TXNIC_SQ_FILL ) { + DBGC ( vnic, "TXNIC %s out of send queue entries\n", + vnic->name ); + return -ENOBUFS; + } + sq_idx = ( vnic->sq.prod++ % TXNIC_SQES ); + offset = ( sq_idx * TXNIC_SQ_STRIDE ); + + /* Populate send descriptor */ + len = iob_len ( iobuf ); + memset ( &sqe, 0, sizeof ( sqe ) ); + sqe.hdr.total = cpu_to_le32 ( ( len >= ETH_ZLEN ) ? len : ETH_ZLEN ); + sqe.hdr.subdcnt = ( TXNIC_SQE_SUBDESCS - 1 ); + sqe.hdr.flags = TXNIC_SEND_HDR_FLAGS; + sqe.gather.size = cpu_to_le16 ( len ); + sqe.gather.flags = TXNIC_SEND_GATHER_FLAGS; + sqe.gather.addr = cpu_to_le64 ( virt_to_bus ( iobuf->data ) ); + DBGC2 ( vnic, "TXNIC %s SQE %#03x is [%08lx,%08lx)\n", + vnic->name, sq_idx, virt_to_bus ( iobuf->data ), + ( virt_to_bus ( iobuf->data ) + len ) ); + + /* Copy send descriptor to ring */ + copy_to_user ( vnic->sq.sqe, offset, &sqe, sizeof ( sqe ) ); + + /* Ring doorbell */ + wmb(); + writeq ( TXNIC_SQE_SUBDESCS, ( vnic->regs + TXNIC_QS_SQ_DOOR(0) ) ); + + return 0; +} + +/** + * Complete send queue entry + * + * @v vnic Virtual NIC + * @v cqe Send completion queue entry + */ +static void txnic_complete_sqe ( struct txnic *vnic, + struct txnic_cqe_send *cqe ) { + struct net_device *netdev = vnic->netdev; + unsigned int sq_idx; + unsigned int status; + + /* Parse completion */ + sq_idx = ( le16_to_cpu ( cqe->sqe_ptr ) / TXNIC_SQE_SUBDESCS ); + status = cqe->send_status; + + /* Sanity check */ + assert ( sq_idx == ( vnic->sq.cons % TXNIC_SQES ) ); + + /* Free send queue entry */ + vnic->sq.cons++; + + /* Complete transmission */ + if ( status ) { + DBGC ( vnic, "TXNIC %s SQE %#03x complete (status %#02x)\n", + vnic->name, sq_idx, status ); + netdev_tx_complete_next_err ( netdev, -EIO ); + } else { + DBGC2 ( vnic, "TXNIC %s SQE %#03x complete\n", + vnic->name, sq_idx ); + netdev_tx_complete_next ( netdev ); + } +} + +/****************************************************************************** + * + * Receive queue + * + ****************************************************************************** + */ + +/** + * Create receive queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_create_rq ( struct txnic *vnic ) { + + /* Reset receive buffer descriptor ring */ + vnic->rq.prod = 0; + vnic->rq.cons = 0; + writeq ( TXNIC_QS_RBDR_CFG_RESET, + ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Configure and enable receive buffer descriptor ring */ + writeq ( user_to_phys ( vnic->rq.rqe, 0 ), + ( vnic->regs + TXNIC_QS_RBDR_BASE(0) ) ); + writeq ( ( TXNIC_QS_RBDR_CFG_ENA | TXNIC_QS_RBDR_CFG_QSIZE_8K | + TXNIC_QS_RBDR_CFG_LINES ( TXNIC_RQE_SIZE / + TXNIC_LINE_SIZE ) ), + ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Enable receive queue */ + writeq ( TXNIC_QS_RQ_CFG_ENA, ( vnic->regs + TXNIC_QS_RQ_CFG(0) ) ); + + DBGC ( vnic, "TXNIC %s RQ at [%08lx,%08lx)\n", + vnic->name, user_to_phys ( vnic->rq.rqe, 0 ), + user_to_phys ( vnic->rq.rqe, TXNIC_RQ_SIZE ) ); + return 0; +} + +/** + * Disable receive queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_disable_rq ( struct txnic *vnic ) { + uint64_t cfg; + unsigned int i; + + /* Disable receive queue */ + writeq ( 0, ( vnic->regs + TXNIC_QS_RQ_CFG(0) ) ); + + /* Wait for receive queue to be disabled */ + for ( i = 0 ; i < TXNIC_RQ_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if receive queue is disabled */ + cfg = readq ( vnic->regs + TXNIC_QS_RQ_CFG(0) ); + if ( ! ( cfg & TXNIC_QS_RQ_CFG_ENA ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( vnic, "TXNIC %s RQ disable timed out\n", vnic->name ); + return -ETIMEDOUT; +} + +/** + * Destroy receive queue + * + * @v vnic Virtual NIC + */ +static void txnic_destroy_rq ( struct txnic *vnic ) { + unsigned int i; + int rc; + + /* Disable receive queue */ + if ( ( rc = txnic_disable_rq ( vnic ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Disable receive buffer descriptor ring */ + writeq ( 0, ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Reset receive buffer descriptor ring */ + writeq ( TXNIC_QS_RBDR_CFG_RESET, + ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) ); + + /* Free any unused I/O buffers */ + for ( i = 0 ; i < TXNIC_RQ_FILL ; i++ ) { + if ( vnic->rq.iobuf[i] ) + free_iob ( vnic->rq.iobuf[i] ); + vnic->rq.iobuf[i] = NULL; + } +} + +/** + * Refill receive queue + * + * @v vnic Virtual NIC + */ +static void txnic_refill_rq ( struct txnic *vnic ) { + struct io_buffer *iobuf; + struct txnic_rqe rqe; + unsigned int rq_idx; + unsigned int rq_iobuf_idx; + unsigned int refilled = 0; + size_t offset; + + /* Refill ring */ + while ( ( vnic->rq.prod - vnic->rq.cons ) < TXNIC_RQ_FILL ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( TXNIC_RQE_SIZE ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rq_idx = ( vnic->rq.prod++ % TXNIC_RQES ); + offset = ( rq_idx * TXNIC_RQ_STRIDE ); + + /* Populate receive descriptor */ + rqe.rbdre.addr = cpu_to_le64 ( virt_to_bus ( iobuf->data ) ); + DBGC2 ( vnic, "TXNIC %s RQE %#03x is [%08lx,%08lx)\n", + vnic->name, rq_idx, virt_to_bus ( iobuf->data ), + ( virt_to_bus ( iobuf->data ) + TXNIC_RQE_SIZE ) ); + + /* Copy receive descriptor to ring */ + copy_to_user ( vnic->rq.rqe, offset, &rqe, sizeof ( rqe ) ); + refilled++; + + /* Record I/O buffer */ + rq_iobuf_idx = ( rq_idx % TXNIC_RQ_FILL ); + assert ( vnic->rq.iobuf[rq_iobuf_idx] == NULL ); + vnic->rq.iobuf[rq_iobuf_idx] = iobuf; + } + + /* Ring doorbell */ + wmb(); + writeq ( refilled, ( vnic->regs + TXNIC_QS_RBDR_DOOR(0) ) ); +} + +/** + * Complete receive queue entry + * + * @v vnic Virtual NIC + * @v cqe Receive completion queue entry + */ +static void txnic_complete_rqe ( struct txnic *vnic, + struct txnic_cqe_rx *cqe ) { + struct net_device *netdev = vnic->netdev; + struct io_buffer *iobuf; + unsigned int errop; + unsigned int rq_idx; + unsigned int rq_iobuf_idx; + size_t apad_len; + size_t len; + + /* Parse completion */ + errop = cqe->errop; + apad_len = TXNIC_CQE_RX_APAD_LEN ( cqe->apad ); + len = le16_to_cpu ( cqe->len ); + + /* Get next receive I/O buffer */ + rq_idx = ( vnic->rq.cons++ % TXNIC_RQES ); + rq_iobuf_idx = ( rq_idx % TXNIC_RQ_FILL ); + iobuf = vnic->rq.iobuf[rq_iobuf_idx]; + vnic->rq.iobuf[rq_iobuf_idx] = NULL; + + /* Populate I/O buffer */ + iob_reserve ( iobuf, apad_len ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( errop ) { + DBGC ( vnic, "TXNIC %s RQE %#03x error (length %zd, errop " + "%#02x)\n", vnic->name, rq_idx, len, errop ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } else { + DBGC2 ( vnic, "TXNIC %s RQE %#03x complete (length %zd)\n", + vnic->name, rq_idx, len ); + netdev_rx ( netdev, iobuf ); + } +} + +/****************************************************************************** + * + * Completion queue + * + ****************************************************************************** + */ + +/** + * Create completion queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_create_cq ( struct txnic *vnic ) { + + /* Reset completion queue */ + vnic->cq.cons = 0; + writeq ( TXNIC_QS_CQ_CFG_RESET, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); + + /* Configure and enable completion queue */ + writeq ( user_to_phys ( vnic->cq.cqe, 0 ), + ( vnic->regs + TXNIC_QS_CQ_BASE(0) ) ); + writeq ( ( TXNIC_QS_CQ_CFG_ENA | TXNIC_QS_CQ_CFG_QSIZE_256 ), + ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); + + DBGC ( vnic, "TXNIC %s CQ at [%08lx,%08lx)\n", + vnic->name, user_to_phys ( vnic->cq.cqe, 0 ), + user_to_phys ( vnic->cq.cqe, TXNIC_CQ_SIZE ) ); + return 0; +} + +/** + * Disable completion queue + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_disable_cq ( struct txnic *vnic ) { + uint64_t cfg; + unsigned int i; + + /* Disable completion queue */ + writeq ( 0, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); + + /* Wait for completion queue to be disabled */ + for ( i = 0 ; i < TXNIC_CQ_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if completion queue is disabled */ + cfg = readq ( vnic->regs + TXNIC_QS_CQ_CFG(0) ); + if ( ! ( cfg & TXNIC_QS_CQ_CFG_ENA ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( vnic, "TXNIC %s CQ disable timed out\n", vnic->name ); + return -ETIMEDOUT; +} + +/** + * Destroy completion queue + * + * @v vnic Virtual NIC + */ +static void txnic_destroy_cq ( struct txnic *vnic ) { + int rc; + + /* Disable completion queue */ + if ( ( rc = txnic_disable_cq ( vnic ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Reset completion queue */ + writeq ( TXNIC_QS_CQ_CFG_RESET, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) ); +} + +/** + * Poll completion queue + * + * @v vnic Virtual NIC + */ +static void txnic_poll_cq ( struct txnic *vnic ) { + union txnic_cqe cqe; + uint64_t status; + size_t offset; + unsigned int qcount; + unsigned int cq_idx; + unsigned int i; + + /* Get number of completions */ + status = readq ( vnic->regs + TXNIC_QS_CQ_STATUS(0) ); + qcount = TXNIC_QS_CQ_STATUS_QCOUNT ( status ); + if ( ! qcount ) + return; + + /* Process completion queue entries */ + for ( i = 0 ; i < qcount ; i++ ) { + + /* Get completion queue entry */ + cq_idx = ( vnic->cq.cons++ % TXNIC_CQES ); + offset = ( cq_idx * TXNIC_CQ_STRIDE ); + copy_from_user ( &cqe, vnic->cq.cqe, offset, sizeof ( cqe ) ); + + /* Process completion queue entry */ + switch ( cqe.common.cqe_type ) { + case TXNIC_CQE_TYPE_SEND: + txnic_complete_sqe ( vnic, &cqe.send ); + break; + case TXNIC_CQE_TYPE_RX: + txnic_complete_rqe ( vnic, &cqe.rx ); + break; + default: + DBGC ( vnic, "TXNIC %s unknown completion type %d\n", + vnic->name, cqe.common.cqe_type ); + DBGC_HDA ( vnic, user_to_phys ( vnic->cq.cqe, offset ), + &cqe, sizeof ( cqe ) ); + break; + } + } + + /* Ring doorbell */ + writeq ( qcount, ( vnic->regs + TXNIC_QS_CQ_DOOR(0) ) ); +} + +/****************************************************************************** + * + * Virtual NIC + * + ****************************************************************************** + */ + +/** + * Open virtual NIC + * + * @v vnic Virtual NIC + * @ret rc Return status code + */ +static int txnic_open ( struct txnic *vnic ) { + int rc; + + /* Create completion queue */ + if ( ( rc = txnic_create_cq ( vnic ) ) != 0 ) + goto err_create_cq; + + /* Create send queue */ + if ( ( rc = txnic_create_sq ( vnic ) ) != 0 ) + goto err_create_sq; + + /* Create receive queue */ + if ( ( rc = txnic_create_rq ( vnic ) ) != 0 ) + goto err_create_rq; + + /* Refill receive queue */ + txnic_refill_rq ( vnic ); + + return 0; + + txnic_destroy_rq ( vnic ); + err_create_rq: + txnic_destroy_sq ( vnic ); + err_create_sq: + txnic_destroy_cq ( vnic ); + err_create_cq: + return rc; +} + +/** + * Close virtual NIC + * + * @v vnic Virtual NIC + */ +static void txnic_close ( struct txnic *vnic ) { + + /* Destroy receive queue */ + txnic_destroy_rq ( vnic ); + + /* Destroy send queue */ + txnic_destroy_sq ( vnic ); + + /* Destroy completion queue */ + txnic_destroy_cq ( vnic ); +} + +/** + * Poll virtual NIC + * + * @v vnic Virtual NIC + */ +static void txnic_poll ( struct txnic *vnic ) { + + /* Poll completion queue */ + txnic_poll_cq ( vnic ); + + /* Refill receive queue */ + txnic_refill_rq ( vnic ); +} + +/** + * Allocate virtual NIC + * + * @v dev Underlying device + * @v membase Register base address + * @ret vnic Virtual NIC, or NULL on failure + */ +static struct txnic * txnic_alloc ( struct device *dev, + unsigned long membase ) { + struct net_device *netdev; + struct txnic *vnic; + + /* Allocate network device */ + netdev = alloc_etherdev ( sizeof ( *vnic ) ); + if ( ! netdev ) + goto err_alloc_netdev; + netdev->dev = dev; + vnic = netdev->priv; + vnic->netdev = netdev; + vnic->name = dev->name; + + /* Allow caller to reuse netdev->priv. (The generic virtual + * NIC code never assumes that netdev->priv==vnic.) + */ + netdev->priv = NULL; + + /* Allocate completion queue */ + vnic->cq.cqe = umalloc ( TXNIC_CQ_SIZE ); + if ( ! vnic->cq.cqe ) + goto err_alloc_cq; + + /* Allocate send queue */ + vnic->sq.sqe = umalloc ( TXNIC_SQ_SIZE ); + if ( ! vnic->sq.sqe ) + goto err_alloc_sq; + + /* Allocate receive queue */ + vnic->rq.rqe = umalloc ( TXNIC_RQ_SIZE ); + if ( ! vnic->rq.rqe ) + goto err_alloc_rq; + + /* Map registers */ + vnic->regs = ioremap ( membase, TXNIC_VF_BAR_SIZE ); + if ( ! vnic->regs ) + goto err_ioremap; + + return vnic; + + iounmap ( vnic->regs ); + err_ioremap: + ufree ( vnic->rq.rqe ); + err_alloc_rq: + ufree ( vnic->sq.sqe ); + err_alloc_sq: + ufree ( vnic->cq.cqe ); + err_alloc_cq: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc_netdev: + return NULL; +} + +/** + * Free virtual NIC + * + * @v vnic Virtual NIC + */ +static void txnic_free ( struct txnic *vnic ) { + struct net_device *netdev = vnic->netdev; + + /* Unmap registers */ + iounmap ( vnic->regs ); + + /* Free receive queue */ + ufree ( vnic->rq.rqe ); + + /* Free send queue */ + ufree ( vnic->sq.sqe ); + + /* Free completion queue */ + ufree ( vnic->cq.cqe ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/****************************************************************************** + * + * Logical MAC virtual NICs + * + ****************************************************************************** + */ + +/** + * Show LMAC diagnostics (for debugging) + * + * @v lmac Logical MAC + */ +static __attribute__ (( unused )) void +txnic_lmac_diag ( struct txnic_lmac *lmac ) { + struct txnic *vnic = lmac->vnic; + uint64_t status1; + uint64_t status2; + uint64_t br_status1; + uint64_t br_status2; + uint64_t br_algn_status; + uint64_t br_pmd_status; + uint64_t an_status; + + /* Read status (clearing latching bits) */ + writeq ( BGX_SPU_STATUS1_RCV_LNK, ( lmac->regs + BGX_SPU_STATUS1 ) ); + writeq ( BGX_SPU_STATUS2_RCVFLT, ( lmac->regs + BGX_SPU_STATUS2 ) ); + status1 = readq ( lmac->regs + BGX_SPU_STATUS1 ); + status2 = readq ( lmac->regs + BGX_SPU_STATUS2 ); + DBGC ( vnic, "TXNIC %s SPU %02llx:%04llx%s%s%s\n", + vnic->name, status1, status2, + ( ( status1 & BGX_SPU_STATUS1_FLT ) ? " FLT" : "" ), + ( ( status1 & BGX_SPU_STATUS1_RCV_LNK ) ? " RCV_LNK" : "" ), + ( ( status2 & BGX_SPU_STATUS2_RCVFLT ) ? " RCVFLT" : "" ) ); + + /* Read BASE-R status (clearing latching bits) */ + writeq ( ( BGX_SPU_BR_STATUS2_LATCHED_LOCK | + BGX_SPU_BR_STATUS2_LATCHED_BER ), + ( lmac->regs + BGX_SPU_BR_STATUS2 ) ); + br_status1 = readq ( lmac->regs + BGX_SPU_BR_STATUS1 ); + br_status2 = readq ( lmac->regs + BGX_SPU_BR_STATUS2 ); + DBGC ( vnic, "TXNIC %s BR %04llx:%04llx%s%s%s%s%s\n", + vnic->name, br_status2, br_status2, + ( ( br_status1 & BGX_SPU_BR_STATUS1_RCV_LNK ) ? " RCV_LNK" : ""), + ( ( br_status1 & BGX_SPU_BR_STATUS1_HI_BER ) ? " HI_BER" : "" ), + ( ( br_status1 & BGX_SPU_BR_STATUS1_BLK_LOCK ) ? + " BLK_LOCK" : "" ), + ( ( br_status2 & BGX_SPU_BR_STATUS2_LATCHED_LOCK ) ? + " LATCHED_LOCK" : "" ), + ( ( br_status2 & BGX_SPU_BR_STATUS2_LATCHED_BER ) ? + " LATCHED_BER" : "" ) ); + + /* Read BASE-R alignment status */ + br_algn_status = readq ( lmac->regs + BGX_SPU_BR_ALGN_STATUS ); + DBGC ( vnic, "TXNIC %s BR ALGN %016llx%s\n", vnic->name, br_algn_status, + ( ( br_algn_status & BGX_SPU_BR_ALGN_STATUS_ALIGND ) ? + " ALIGND" : "" ) ); + + /* Read BASE-R link training status */ + br_pmd_status = readq ( lmac->regs + BGX_SPU_BR_PMD_STATUS ); + DBGC ( vnic, "TXNIC %s BR PMD %04llx\n", vnic->name, br_pmd_status ); + + /* Read autonegotiation status (clearing latching bits) */ + writeq ( ( BGX_SPU_AN_STATUS_PAGE_RX | BGX_SPU_AN_STATUS_LINK_STATUS ), + ( lmac->regs + BGX_SPU_AN_STATUS ) ); + an_status = readq ( lmac->regs + BGX_SPU_AN_STATUS ); + DBGC ( vnic, "TXNIC %s BR AN %04llx%s%s%s%s%s\n", vnic->name, an_status, + ( ( an_status & BGX_SPU_AN_STATUS_XNP_STAT ) ? " XNP_STAT" : ""), + ( ( an_status & BGX_SPU_AN_STATUS_PAGE_RX ) ? " PAGE_RX" : "" ), + ( ( an_status & BGX_SPU_AN_STATUS_AN_COMPLETE ) ? + " AN_COMPLETE" : "" ), + ( ( an_status & BGX_SPU_AN_STATUS_LINK_STATUS ) ? + " LINK_STATUS" : "" ), + ( ( an_status & BGX_SPU_AN_STATUS_LP_AN_ABLE ) ? + " LP_AN_ABLE" : "" ) ); + + /* Read transmit statistics */ + DBGC ( vnic, "TXNIC %s TXF xc %#llx xd %#llx mc %#llx sc %#llx ok " + "%#llx bc %#llx mc %#llx un %#llx pa %#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_TX_STAT0 ), + readq ( lmac->regs + BGX_CMR_TX_STAT1 ), + readq ( lmac->regs + BGX_CMR_TX_STAT2 ), + readq ( lmac->regs + BGX_CMR_TX_STAT3 ), + readq ( lmac->regs + BGX_CMR_TX_STAT5 ), + readq ( lmac->regs + BGX_CMR_TX_STAT14 ), + readq ( lmac->regs + BGX_CMR_TX_STAT15 ), + readq ( lmac->regs + BGX_CMR_TX_STAT16 ), + readq ( lmac->regs + BGX_CMR_TX_STAT17 ) ); + DBGC ( vnic, "TXNIC %s TXB ok %#llx hist %#llx:%#llx:%#llx:%#llx:" + "%#llx:%#llx:%#llx:%#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_TX_STAT4 ), + readq ( lmac->regs + BGX_CMR_TX_STAT6 ), + readq ( lmac->regs + BGX_CMR_TX_STAT7 ), + readq ( lmac->regs + BGX_CMR_TX_STAT8 ), + readq ( lmac->regs + BGX_CMR_TX_STAT9 ), + readq ( lmac->regs + BGX_CMR_TX_STAT10 ), + readq ( lmac->regs + BGX_CMR_TX_STAT11 ), + readq ( lmac->regs + BGX_CMR_TX_STAT12 ), + readq ( lmac->regs + BGX_CMR_TX_STAT13 ) ); + + /* Read receive statistics */ + DBGC ( vnic, "TXNIC %s RXF ok %#llx pa %#llx nm %#llx ov %#llx er " + "%#llx nc %#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_RX_STAT0 ), + readq ( lmac->regs + BGX_CMR_RX_STAT2 ), + readq ( lmac->regs + BGX_CMR_RX_STAT4 ), + readq ( lmac->regs + BGX_CMR_RX_STAT6 ), + readq ( lmac->regs + BGX_CMR_RX_STAT8 ), + readq ( lmac->regs + BGX_CMR_RX_STAT9 ) ); + DBGC ( vnic, "TXNIC %s RXB ok %#llx pa %#llx nm %#llx ov %#llx nc " + "%#llx\n", vnic->name, + readq ( lmac->regs + BGX_CMR_RX_STAT1 ), + readq ( lmac->regs + BGX_CMR_RX_STAT3 ), + readq ( lmac->regs + BGX_CMR_RX_STAT5 ), + readq ( lmac->regs + BGX_CMR_RX_STAT7 ), + readq ( lmac->regs + BGX_CMR_RX_STAT10 ) ); +} + +/** + * Update LMAC link state + * + * @v lmac Logical MAC + */ +static void txnic_lmac_update_link ( struct txnic_lmac *lmac ) { + struct txnic *vnic = lmac->vnic; + struct net_device *netdev = vnic->netdev; + uint64_t status1; + + /* Read status (clearing latching bits) */ + writeq ( BGX_SPU_STATUS1_RCV_LNK, ( lmac->regs + BGX_SPU_STATUS1 ) ); + status1 = readq ( lmac->regs + BGX_SPU_STATUS1 ); + + /* Report link status */ + if ( status1 & BGX_SPU_STATUS1_RCV_LNK ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/** + * Poll LMAC link state + * + * @v lmac Logical MAC + */ +static void txnic_lmac_poll_link ( struct txnic_lmac *lmac ) { + struct txnic *vnic = lmac->vnic; + uint64_t intr; + + /* Get interrupt status */ + intr = readq ( lmac->regs + BGX_SPU_INT ); + if ( ! intr ) + return; + DBGC ( vnic, "TXNIC %s INT %04llx%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + vnic->name, intr, + ( ( intr & BGX_SPU_INT_TRAINING_FAIL ) ? " TRAINING_FAIL" : "" ), + ( ( intr & BGX_SPU_INT_TRAINING_DONE ) ? " TRAINING_DONE" : "" ), + ( ( intr & BGX_SPU_INT_AN_COMPLETE ) ? " AN_COMPLETE" : "" ), + ( ( intr & BGX_SPU_INT_AN_LINK_GOOD ) ? " AN_LINK_GOOD" : "" ), + ( ( intr & BGX_SPU_INT_AN_PAGE_RX ) ? " AN_PAGE_RX" : "" ), + ( ( intr & BGX_SPU_INT_FEC_UNCORR ) ? " FEC_UNCORR" : "" ), + ( ( intr & BGX_SPU_INT_FEC_CORR ) ? " FEC_CORR" : "" ), + ( ( intr & BGX_SPU_INT_BIP_ERR ) ? " BIP_ERR" : "" ), + ( ( intr & BGX_SPU_INT_DBG_SYNC ) ? " DBG_SYNC" : "" ), + ( ( intr & BGX_SPU_INT_ALGNLOS ) ? " ALGNLOS" : "" ), + ( ( intr & BGX_SPU_INT_SYNLOS ) ? " SYNLOS" : "" ), + ( ( intr & BGX_SPU_INT_BITLCKLS ) ? " BITLCKLS" : "" ), + ( ( intr & BGX_SPU_INT_ERR_BLK ) ? " ERR_BLK" : "" ), + ( ( intr & BGX_SPU_INT_RX_LINK_DOWN ) ? " RX_LINK_DOWN" : "" ), + ( ( intr & BGX_SPU_INT_RX_LINK_UP ) ? " RX_LINK_UP" : "" ) ); + + /* Clear interrupt status */ + writeq ( intr, ( lmac->regs + BGX_SPU_INT ) ); + + /* Update link state */ + txnic_lmac_update_link ( lmac ); +} + +/** + * Reset LMAC + * + * @v lmac Logical MAC + */ +static void txnic_lmac_reset ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + void *qsregs = ( pf->regs + TXNIC_PF_QS ( lmac->idx ) ); + + /* There is no reset available for the physical function + * aspects of a virtual NIC; we have to explicitly reload a + * sensible set of default values. + */ + writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_CFG(0) ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_DROP_CFG(0) ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_BP_CFG(0) ) ); + writeq ( 0, ( qsregs + TXNIC_PF_QS_SQ_CFG(0) ) ); +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int txnic_lmac_open ( struct net_device *netdev ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + struct txnic *vnic = lmac->vnic; + unsigned int vnic_idx = lmac->idx; + unsigned int chan_idx = TXNIC_CHAN_IDX ( vnic_idx ); + unsigned int tl4_idx = TXNIC_TL4_IDX ( vnic_idx ); + unsigned int tl3_idx = TXNIC_TL3_IDX ( vnic_idx ); + unsigned int tl2_idx = TXNIC_TL2_IDX ( vnic_idx ); + void *lmregs = ( pf->regs + TXNIC_PF_LMAC ( vnic_idx ) ); + void *chregs = ( pf->regs + TXNIC_PF_CHAN ( chan_idx ) ); + void *qsregs = ( pf->regs + TXNIC_PF_QS ( vnic_idx ) ); + size_t max_pkt_size; + int rc; + + /* Configure channel/match parse indices */ + writeq ( ( TXNIC_PF_MPI_CFG_VNIC ( vnic_idx ) | + TXNIC_PF_MPI_CFG_RSSI_BASE ( vnic_idx ) ), + ( TXNIC_PF_MPI_CFG ( vnic_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_RSSI_RQ_RQ_QS ( vnic_idx ) ), + ( TXNIC_PF_RSSI_RQ ( vnic_idx ) + pf->regs ) ); + + /* Configure LMAC */ + max_pkt_size = ( netdev->max_pkt_len + 4 /* possible VLAN */ ); + writeq ( ( TXNIC_PF_LMAC_CFG_ADJUST_DEFAULT | + TXNIC_PF_LMAC_CFG_MIN_PKT_SIZE ( ETH_ZLEN ) ), + ( TXNIC_PF_LMAC_CFG + lmregs ) ); + writeq ( ( TXNIC_PF_LMAC_CFG2_MAX_PKT_SIZE ( max_pkt_size ) ), + ( TXNIC_PF_LMAC_CFG2 + lmregs ) ); + writeq ( ( TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT_DEFAULT | + TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT_DEFAULT | + TXNIC_PF_LMAC_CREDIT_CC_ENABLE ), + ( TXNIC_PF_LMAC_CREDIT + lmregs ) ); + + /* Configure channels */ + writeq ( ( TXNIC_PF_CHAN_TX_CFG_BP_ENA ), + ( TXNIC_PF_CHAN_TX_CFG + chregs ) ); + writeq ( ( TXNIC_PF_CHAN_RX_CFG_CPI_BASE ( vnic_idx ) ), + ( TXNIC_PF_CHAN_RX_CFG + chregs ) ); + writeq ( ( TXNIC_PF_CHAN_RX_BP_CFG_ENA | + TXNIC_PF_CHAN_RX_BP_CFG_BPID ( vnic_idx ) ), + ( TXNIC_PF_CHAN_RX_BP_CFG + chregs ) ); + + /* Configure traffic limiters */ + writeq ( ( TXNIC_PF_TL2_CFG_RR_QUANTUM_DEFAULT ), + ( TXNIC_PF_TL2_CFG ( tl2_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_TL3_CFG_RR_QUANTUM_DEFAULT ), + ( TXNIC_PF_TL3_CFG ( tl3_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_TL3_CHAN_CHAN ( chan_idx ) ), + ( TXNIC_PF_TL3_CHAN ( tl3_idx ) + pf->regs ) ); + writeq ( ( TXNIC_PF_TL4_CFG_SQ_QS ( vnic_idx ) | + TXNIC_PF_TL4_CFG_RR_QUANTUM_DEFAULT ), + ( TXNIC_PF_TL4_CFG ( tl4_idx ) + pf->regs ) ); + + /* Configure send queue */ + writeq ( ( TXNIC_PF_QS_SQ_CFG_CQ_QS ( vnic_idx ) ), + ( TXNIC_PF_QS_SQ_CFG(0) + qsregs ) ); + writeq ( ( TXNIC_PF_QS_SQ_CFG2_TL4 ( tl4_idx ) ), + ( TXNIC_PF_QS_SQ_CFG2(0) + qsregs ) ); + + /* Configure receive queue */ + writeq ( ( TXNIC_PF_QS_RQ_CFG_CACHING_ALL | + TXNIC_PF_QS_RQ_CFG_CQ_QS ( vnic_idx ) | + TXNIC_PF_QS_RQ_CFG_RBDR_CONT_QS ( vnic_idx ) | + TXNIC_PF_QS_RQ_CFG_RBDR_STRT_QS ( vnic_idx ) ), + ( TXNIC_PF_QS_RQ_CFG(0) + qsregs ) ); + writeq ( ( TXNIC_PF_QS_RQ_BP_CFG_RBDR_BP_ENA | + TXNIC_PF_QS_RQ_BP_CFG_CQ_BP_ENA | + TXNIC_PF_QS_RQ_BP_CFG_BPID ( vnic_idx ) ), + ( TXNIC_PF_QS_RQ_BP_CFG(0) + qsregs ) ); + + /* Enable queue set */ + writeq ( ( TXNIC_PF_QS_CFG_ENA | TXNIC_PF_QS_CFG_VNIC ( vnic_idx ) ), + ( TXNIC_PF_QS_CFG + qsregs ) ); + + /* Open virtual NIC */ + if ( ( rc = txnic_open ( vnic ) ) != 0 ) + goto err_open; + + /* Update link state */ + txnic_lmac_update_link ( lmac ); + + return 0; + + txnic_close ( vnic ); + err_open: + writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void txnic_lmac_close ( struct net_device *netdev ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + struct txnic *vnic = lmac->vnic; + void *qsregs = ( pf->regs + TXNIC_PF_QS ( lmac->idx ) ); + + /* Close virtual NIC */ + txnic_close ( vnic ); + + /* Disable queue set */ + writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int txnic_lmac_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic *vnic = lmac->vnic; + + return txnic_send ( vnic, iobuf ); +} + +/** + * Poll network device + * + * @v netdev Network device + */ +static void txnic_lmac_poll ( struct net_device *netdev ) { + struct txnic_lmac *lmac = netdev->priv; + struct txnic *vnic = lmac->vnic; + + /* Poll virtual NIC */ + txnic_poll ( vnic ); + + /* Poll link state */ + txnic_lmac_poll_link ( lmac ); +} + +/** Network device operations */ +static struct net_device_operations txnic_lmac_operations = { + .open = txnic_lmac_open, + .close = txnic_lmac_close, + .transmit = txnic_lmac_transmit, + .poll = txnic_lmac_poll, +}; + +/** + * Probe logical MAC virtual NIC + * + * @v lmac Logical MAC + * @ret rc Return status code + */ +static int txnic_lmac_probe ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + struct txnic_pf *pf = bgx->pf; + struct txnic *vnic; + struct net_device *netdev; + unsigned long membase; + int rc; + + /* Sanity check */ + assert ( lmac->vnic == NULL ); + + /* Calculate register base address */ + membase = ( pf->vf_membase + ( lmac->idx * pf->vf_stride ) ); + + /* Allocate and initialise network device */ + vnic = txnic_alloc ( &bgx->pci->dev, membase ); + if ( ! vnic ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev = vnic->netdev; + netdev_init ( netdev, &txnic_lmac_operations ); + netdev->priv = lmac; + lmac->vnic = vnic; + + /* Reset device */ + txnic_lmac_reset ( lmac ); + + /* Set MAC address */ + memcpy ( netdev->hw_addr, lmac->mac.raw, ETH_ALEN ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + vnic->name = netdev->name; + DBGC ( TXNICCOL ( pf ), "TXNIC %d/%d/%d is %s (%s)\n", pf->node, + bgx->idx, lmac->idx, vnic->name, eth_ntoa ( lmac->mac.raw ) ); + + /* Update link state */ + txnic_lmac_update_link ( lmac ); + + return 0; + + unregister_netdev ( netdev ); + err_register: + txnic_lmac_reset ( lmac ); + txnic_free ( vnic ); + lmac->vnic = NULL; + err_alloc: + return rc; +} + +/** + * Remove logical MAC virtual NIC + * + * @v lmac Logical MAC + */ +static void txnic_lmac_remove ( struct txnic_lmac *lmac ) { + uint64_t config; + + /* Sanity check */ + assert ( lmac->vnic != NULL ); + + /* Disable packet receive and transmit */ + config = readq ( lmac->regs + BGX_CMR_CONFIG ); + config &= ~( BGX_CMR_CONFIG_DATA_PKT_TX_EN | + BGX_CMR_CONFIG_DATA_PKT_RX_EN ); + writeq ( config, ( lmac->regs + BGX_CMR_CONFIG ) ); + + /* Unregister network device */ + unregister_netdev ( lmac->vnic->netdev ); + + /* Reset device */ + txnic_lmac_reset ( lmac ); + + /* Free virtual NIC */ + txnic_free ( lmac->vnic ); + lmac->vnic = NULL; +} + +/** + * Probe all LMACs on a BGX Ethernet interface + * + * @v pf Physical function + * @v bgx BGX Ethernet interface + * @ret rc Return status code + */ +static int txnic_lmac_probe_all ( struct txnic_pf *pf, struct txnic_bgx *bgx ) { + unsigned int bgx_idx; + int lmac_idx; + int count; + int rc; + + /* Sanity checks */ + bgx_idx = bgx->idx; + assert ( pf->node == bgx->node ); + assert ( pf->bgx[bgx_idx] == NULL ); + assert ( bgx->pf == NULL ); + + /* Associate BGX with physical function */ + pf->bgx[bgx_idx] = bgx; + bgx->pf = pf; + + /* Probe all LMACs */ + count = bgx->count; + for ( lmac_idx = 0 ; lmac_idx < count ; lmac_idx++ ) { + if ( ( rc = txnic_lmac_probe ( &bgx->lmac[lmac_idx] ) ) != 0 ) + goto err_probe; + } + + return 0; + + lmac_idx = count; + err_probe: + for ( lmac_idx-- ; lmac_idx >= 0 ; lmac_idx-- ) + txnic_lmac_remove ( &bgx->lmac[lmac_idx] ); + pf->bgx[bgx_idx] = NULL; + bgx->pf = NULL; + return rc; +} + +/** + * Remove all LMACs on a BGX Ethernet interface + * + * @v pf Physical function + * @v bgx BGX Ethernet interface + */ +static void txnic_lmac_remove_all ( struct txnic_pf *pf, + struct txnic_bgx *bgx ) { + unsigned int lmac_idx; + + /* Sanity checks */ + assert ( pf->bgx[bgx->idx] == bgx ); + assert ( bgx->pf == pf ); + + /* Remove all LMACs */ + for ( lmac_idx = 0 ; lmac_idx < bgx->count ; lmac_idx++ ) + txnic_lmac_remove ( &bgx->lmac[lmac_idx] ); + + /* Disassociate BGX from physical function */ + pf->bgx[bgx->idx] = NULL; + bgx->pf = NULL; +} + +/****************************************************************************** + * + * NIC physical function interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int txnic_pf_probe ( struct pci_device *pci ) { + struct txnic_pf *pf; + struct txnic_bgx *bgx; + unsigned long membase; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + pf = zalloc ( sizeof ( *pf ) ); + if ( ! pf ) { + rc = -ENOMEM; + goto err_alloc; + } + pf->pci = pci; + pci_set_drvdata ( pci, pf ); + + /* Get base addresses */ + membase = pciea_bar_start ( pci, PCIEA_BEI_BAR_0 ); + pf->vf_membase = pciea_bar_start ( pci, PCIEA_BEI_VF_BAR_0 ); + pf->vf_stride = pciea_bar_size ( pci, PCIEA_BEI_VF_BAR_0 ); + + /* Calculate node ID */ + pf->node = txnic_address_node ( membase ); + DBGC ( TXNICCOL ( pf ), "TXNIC %d/*/* PF %s at %#lx (VF %#lx+%#lx)\n", + pf->node, pci->dev.name, membase, pf->vf_membase, pf->vf_stride); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + pf->regs = ioremap ( membase, TXNIC_PF_BAR_SIZE ); + if ( ! pf->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Configure physical function */ + writeq ( TXNIC_PF_CFG_ENA, ( pf->regs + TXNIC_PF_CFG ) ); + writeq ( ( TXNIC_PF_BP_CFG_BP_POLL_ENA | + TXNIC_PF_BP_CFG_BP_POLL_DLY_DEFAULT ), + ( pf->regs + TXNIC_PF_BP_CFG ) ); + for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) { + writeq ( ( TXNIC_PF_INTF_SEND_CFG_BLOCK_BGX | + TXNIC_PF_INTF_SEND_CFG_BLOCK ( i ) ), + ( pf->regs + TXNIC_PF_INTF_SEND_CFG ( i ) ) ); + writeq ( ( TXNIC_PF_INTF_BP_CFG_BP_ENA | + TXNIC_PF_INTF_BP_CFG_BP_ID_BGX | + TXNIC_PF_INTF_BP_CFG_BP_ID ( i ) ), + ( pf->regs + TXNIC_PF_INTF_BP_CFG ( i ) ) ); + } + writeq ( ( TXNIC_PF_PKIND_CFG_LENERR_EN | + TXNIC_PF_PKIND_CFG_MAXLEN_DISABLE | + TXNIC_PF_PKIND_CFG_MINLEN_DISABLE ), + ( pf->regs + TXNIC_PF_PKIND_CFG(0) ) ); + + /* Add to list of physical functions */ + list_add_tail ( &pf->list, &txnic_pfs ); + + /* Probe all LMACs, if applicable */ + list_for_each_entry ( bgx, &txnic_bgxs, list ) { + if ( bgx->node != pf->node ) + continue; + if ( ( rc = txnic_lmac_probe_all ( pf, bgx ) ) != 0 ) + goto err_probe; + } + + return 0; + + err_probe: + for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) { + if ( pf->bgx[i] ) + txnic_lmac_remove_all ( pf, pf->bgx[i] ); + } + list_del ( &pf->list ); + writeq ( 0, ( pf->regs + TXNIC_PF_CFG ) ); + iounmap ( pf->regs ); + err_ioremap: + free ( pf ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void txnic_pf_remove ( struct pci_device *pci ) { + struct txnic_pf *pf = pci_get_drvdata ( pci ); + unsigned int i; + + /* Remove all LMACs, if applicable */ + for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) { + if ( pf->bgx[i] ) + txnic_lmac_remove_all ( pf, pf->bgx[i] ); + } + + /* Remove from list of physical functions */ + list_del ( &pf->list ); + + /* Unmap registers */ + iounmap ( pf->regs ); + + /* Free physical function */ + free ( pf ); +} + +/** NIC physical function PCI device IDs */ +static struct pci_device_id txnic_pf_ids[] = { + PCI_ROM ( 0x177d, 0xa01e, "thunder-pf", "ThunderX NIC PF", 0 ), +}; + +/** NIC physical function PCI driver */ +struct pci_driver txnic_pf_driver __pci_driver = { + .ids = txnic_pf_ids, + .id_count = ( sizeof ( txnic_pf_ids ) / sizeof ( txnic_pf_ids[0] ) ), + .probe = txnic_pf_probe, + .remove = txnic_pf_remove, +}; + +/****************************************************************************** + * + * BGX interface + * + ****************************************************************************** + */ + +/** LMAC types */ +static struct txnic_lmac_type txnic_lmac_types[] = { + [TXNIC_LMAC_XAUI] = { + .name = "XAUI", + .count = 1, + .lane_to_sds = 0xe4, + }, + [TXNIC_LMAC_RXAUI] = { + .name = "RXAUI", + .count = 2, + .lane_to_sds = 0x0e04, + }, + [TXNIC_LMAC_10G_R] = { + .name = "10GBASE-R", + .count = 4, + .lane_to_sds = 0x00000000, + }, + [TXNIC_LMAC_40G_R] = { + .name = "40GBASE-R", + .count = 1, + .lane_to_sds = 0xe4, + }, +}; + +/** + * Detect BGX Ethernet interface LMAC type + * + * @v bgx BGX Ethernet interface + * @ret type LMAC type, or negative error + */ +static int txnic_bgx_detect ( struct txnic_bgx *bgx ) { + uint64_t config; + uint64_t br_pmd_control; + uint64_t rx_lmacs; + unsigned int type; + + /* We assume that the early (pre-UEFI) firmware will have + * configured at least the LMAC 0 type and use of link + * training, and may have overridden the number of LMACs. + */ + + /* Determine type from LMAC 0 */ + config = readq ( bgx->regs + BGX_CMR_CONFIG ); + type = BGX_CMR_CONFIG_LMAC_TYPE_GET ( config ); + if ( ( type >= ( sizeof ( txnic_lmac_types ) / + sizeof ( txnic_lmac_types[0] ) ) ) || + ( txnic_lmac_types[type].count == 0 ) ) { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* BGX unknown type %d\n", + bgx->node, bgx->idx, type ); + return -ENOTTY; + } + bgx->type = &txnic_lmac_types[type]; + + /* Check whether link training is required */ + br_pmd_control = readq ( bgx->regs + BGX_SPU_BR_PMD_CONTROL ); + bgx->training = + ( !! ( br_pmd_control & BGX_SPU_BR_PMD_CONTROL_TRAIN_EN ) ); + + /* Determine number of LMACs */ + rx_lmacs = readq ( bgx->regs + BGX_CMR_RX_LMACS ); + bgx->count = BGX_CMR_RX_LMACS_LMACS_GET ( rx_lmacs ); + if ( ( bgx->count == TXNIC_NUM_LMAC ) && + ( bgx->type->count != TXNIC_NUM_LMAC ) ) { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* assuming %d LMACs\n", + bgx->node, bgx->idx, bgx->type->count ); + bgx->count = bgx->type->count; + } + + return type; +} + +/** + * Initialise BGX Ethernet interface + * + * @v bgx BGX Ethernet interface + * @v type LMAC type + */ +static void txnic_bgx_init ( struct txnic_bgx *bgx, unsigned int type ) { + uint64_t global_config; + uint32_t lane_to_sds; + unsigned int i; + + /* Set number of LMACs */ + writeq ( BGX_CMR_RX_LMACS_LMACS_SET ( bgx->count ), + ( bgx->regs + BGX_CMR_RX_LMACS ) ); + writeq ( BGX_CMR_TX_LMACS_LMACS_SET ( bgx->count ), + ( bgx->regs + BGX_CMR_TX_LMACS ) ); + + /* Set LMAC types and lane mappings, and disable all LMACs */ + lane_to_sds = bgx->type->lane_to_sds; + for ( i = 0 ; i < bgx->count ; i++ ) { + writeq ( ( BGX_CMR_CONFIG_LMAC_TYPE_SET ( type ) | + BGX_CMR_CONFIG_LANE_TO_SDS ( lane_to_sds ) ), + ( bgx->regs + BGX_LMAC ( i ) + BGX_CMR_CONFIG ) ); + lane_to_sds >>= 8; + } + + /* Reset all MAC address filtering */ + for ( i = 0 ; i < TXNIC_NUM_DMAC ; i++ ) + writeq ( 0, ( bgx->regs + BGX_CMR_RX_DMAC_CAM ( i ) ) ); + + /* Reset NCSI steering */ + for ( i = 0 ; i < TXNIC_NUM_STEERING ; i++ ) + writeq ( 0, ( bgx->regs + BGX_CMR_RX_STEERING ( i ) ) ); + + /* Enable backpressure to all channels */ + writeq ( BGX_CMR_CHAN_MSK_AND_ALL ( bgx->count ), + ( bgx->regs + BGX_CMR_CHAN_MSK_AND ) ); + + /* Strip FCS */ + global_config = readq ( bgx->regs + BGX_CMR_GLOBAL_CONFIG ); + global_config |= BGX_CMR_GLOBAL_CONFIG_FCS_STRIP; + writeq ( global_config, ( bgx->regs + BGX_CMR_GLOBAL_CONFIG ) ); +} + +/** + * Get MAC address + * + * @v lmac Logical MAC + */ +static void txnic_bgx_mac ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + unsigned int lmac_idx = TXNIC_LMAC_IDX ( lmac->idx ); + uint64_t mac; + EFI_STATUS efirc; + int rc; + + /* Extract MAC from Board Configuration protocol, if available */ + if ( txcfg ) { + if ( ( efirc = txcfg->GetLmacProp ( txcfg, bgx->node, bgx->idx, + lmac_idx, MAC_ADDRESS, + sizeof ( mac ), + &mac ) ) == 0 ) { + lmac->mac.be64 = cpu_to_be64 ( mac ); + } else { + rc = -EEFI ( efirc ); + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d could not get " + "MAC address: %s\n", bgx->node, bgx->idx, + lmac->idx, strerror ( rc ) ); + } + } else { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no board " + "configuration protocol\n", bgx->node, bgx->idx, + lmac->idx ); + } + + /* Use random MAC address if none available */ + if ( ! lmac->mac.be64 ) { + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no MAC address\n", + bgx->node, bgx->idx, lmac->idx ); + eth_random_addr ( lmac->mac.raw ); + } +} + +/** + * Initialise Super PHY Unit (SPU) + * + * @v lmac Logical MAC + */ +static void txnic_bgx_spu_init ( struct txnic_lmac *lmac ) { + struct txnic_bgx *bgx = lmac->bgx; + + /* Reset PHY */ + writeq ( BGX_SPU_CONTROL1_RESET, ( lmac->regs + BGX_SPU_CONTROL1 ) ); + mdelay ( BGX_SPU_RESET_DELAY_MS ); + + /* Power down PHY */ + writeq ( BGX_SPU_CONTROL1_LO_PWR, ( lmac->regs + BGX_SPU_CONTROL1 ) ); + + /* Configure training, if applicable */ + if ( bgx->training ) { + writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LP_CUP ) ); + writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LD_CUP ) ); + writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LD_REP ) ); + writeq ( BGX_SPU_BR_PMD_CONTROL_TRAIN_EN, + ( lmac->regs + BGX_SPU_BR_PMD_CONTROL ) ); + } + + /* Disable forward error correction */ + writeq ( 0, ( lmac->regs + BGX_SPU_FEC_CONTROL ) ); + + /* Disable autonegotiation */ + writeq ( 0, ( lmac->regs + BGX_SPU_AN_CONTROL ) ); + + /* Power up PHY */ + writeq ( 0, ( lmac->regs + BGX_SPU_CONTROL1 ) ); +} + +/** + * Initialise LMAC + * + * @v bgx BGX Ethernet interface + * @v lmac_idx LMAC index + */ +static void txnic_bgx_lmac_init ( struct txnic_bgx *bgx, + unsigned int lmac_idx ) { + struct txnic_lmac *lmac = &bgx->lmac[lmac_idx]; + uint64_t config; + + /* Record associated BGX */ + lmac->bgx = bgx; + + /* Set register base address (already mapped) */ + lmac->regs = ( bgx->regs + BGX_LMAC ( lmac_idx ) ); + + /* Calculate virtual NIC index */ + lmac->idx = TXNIC_VNIC_IDX ( bgx->idx, lmac_idx ); + + /* Set MAC address */ + txnic_bgx_mac ( lmac ); + + /* Initialise PHY */ + txnic_bgx_spu_init ( lmac ); + + /* Accept all multicasts and broadcasts */ + writeq ( ( BGX_CMR_RX_DMAC_CTL_MCST_MODE_ACCEPT | + BGX_CMR_RX_DMAC_CTL_BCST_ACCEPT ), + ( lmac->regs + BGX_CMR_RX_DMAC_CTL ) ); + + /* Enable LMAC */ + config = readq ( lmac->regs + BGX_CMR_CONFIG ); + config |= ( BGX_CMR_CONFIG_ENABLE | + BGX_CMR_CONFIG_DATA_PKT_RX_EN | + BGX_CMR_CONFIG_DATA_PKT_TX_EN ); + writeq ( config, ( lmac->regs + BGX_CMR_CONFIG ) ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int txnic_bgx_probe ( struct pci_device *pci ) { + struct txnic_bgx *bgx; + struct txnic_pf *pf; + unsigned long membase; + unsigned int i; + int type; + int rc; + + /* Allocate and initialise structure */ + bgx = zalloc ( sizeof ( *bgx ) ); + if ( ! bgx ) { + rc = -ENOMEM; + goto err_alloc; + } + bgx->pci = pci; + pci_set_drvdata ( pci, bgx ); + + /* Get base address */ + membase = pciea_bar_start ( pci, PCIEA_BEI_BAR_0 ); + + /* Calculate node ID and index */ + bgx->node = txnic_address_node ( membase ); + bgx->idx = txnic_address_bgx ( membase ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + bgx->regs = ioremap ( membase, TXNIC_BGX_BAR_SIZE ); + if ( ! bgx->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Detect LMAC type */ + if ( ( type = txnic_bgx_detect ( bgx ) ) < 0 ) { + rc = type; + goto err_detect; + } + DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* BGX %s at %#lx %dx %s%s\n", + bgx->node, bgx->idx, pci->dev.name, membase, bgx->count, + bgx->type->name, ( bgx->training ? "(training)" : "" ) ); + + /* Initialise interface */ + txnic_bgx_init ( bgx, type ); + + /* Initialise all LMACs */ + for ( i = 0 ; i < bgx->count ; i++ ) + txnic_bgx_lmac_init ( bgx, i ); + + /* Add to list of BGX devices */ + list_add_tail ( &bgx->list, &txnic_bgxs ); + + /* Probe all LMACs, if applicable */ + list_for_each_entry ( pf, &txnic_pfs, list ) { + if ( pf->node != bgx->node ) + continue; + if ( ( rc = txnic_lmac_probe_all ( pf, bgx ) ) != 0 ) + goto err_probe; + } + + return 0; + + if ( bgx->pf ) + txnic_lmac_remove_all ( bgx->pf, bgx ); + list_del ( &bgx->list ); + err_probe: + err_detect: + iounmap ( bgx->regs ); + err_ioremap: + free ( bgx ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void txnic_bgx_remove ( struct pci_device *pci ) { + struct txnic_bgx *bgx = pci_get_drvdata ( pci ); + + /* Remove all LMACs, if applicable */ + if ( bgx->pf ) + txnic_lmac_remove_all ( bgx->pf, bgx ); + + /* Remove from list of BGX devices */ + list_del ( &bgx->list ); + + /* Unmap registers */ + iounmap ( bgx->regs ); + + /* Free BGX device */ + free ( bgx ); +} + +/** BGX PCI device IDs */ +static struct pci_device_id txnic_bgx_ids[] = { + PCI_ROM ( 0x177d, 0xa026, "thunder-bgx", "ThunderX BGX", 0 ), +}; + +/** BGX PCI driver */ +struct pci_driver txnic_bgx_driver __pci_driver = { + .ids = txnic_bgx_ids, + .id_count = ( sizeof ( txnic_bgx_ids ) / sizeof ( txnic_bgx_ids[0] ) ), + .probe = txnic_bgx_probe, + .remove = txnic_bgx_remove, +}; diff --git a/src/drivers/net/thunderx.h b/src/drivers/net/thunderx.h new file mode 100644 index 000000000..410daf6e2 --- /dev/null +++ b/src/drivers/net/thunderx.h @@ -0,0 +1,949 @@ +#ifndef _THUNDERX_H +#define _THUNDERX_H + +/** @file + * + * Cavium ThunderX Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/****************************************************************************** + * + * Address space + * + ****************************************************************************** + */ + +/** Size of a cache line */ +#define TXNIC_LINE_SIZE 128 + +/** Virtual function BAR size */ +#define TXNIC_VF_BAR_SIZE 0x200000UL + +/** Physical function BAR size */ +#define TXNIC_PF_BAR_SIZE 0x40000000UL + +/** BGX BAR size */ +#define TXNIC_BGX_BAR_SIZE 0x400000UL + +/** Maximum number of BGX Ethernet interfaces (per node) */ +#define TXNIC_NUM_BGX 2 + +/** Maximum number of Logical MACs (per BGX) */ +#define TXNIC_NUM_LMAC 4 + +/** Maximum number of destination MAC addresses (per BGX) */ +#define TXNIC_NUM_DMAC 32 + +/** Maximum number of steering rules (per BGX) */ +#define TXNIC_NUM_STEERING 8 + +/** + * Calculate node ID + * + * @v addr PCI BAR base address + * @ret node Node ID + */ +static inline unsigned int txnic_address_node ( uint64_t addr ) { + + /* Node ID is in bits [45:44] of the hardcoded BAR address */ + return ( ( addr >> 44 ) & 0x3 ); +} + +/** + * Calculate BGX Ethernet interface index + * + * @v addr PCI BAR base address + * @ret index Index + */ +static inline unsigned int txnic_address_bgx ( uint64_t addr ) { + + /* Index is in bit 24 of the hardcoded BAR address */ + return ( ( addr >> 24 ) & 0x1 ); +} + +/****************************************************************************** + * + * Send queue + * + ****************************************************************************** + */ + +/** Send queue configuration */ +#define TXNIC_QS_SQ_CFG(q) ( ( (q) << 18 ) | 0x010800 ) +#define TXNIC_QS_SQ_CFG_ENA ( 1ULL << 19 ) +#define TXNIC_QS_SQ_CFG_RESET ( 1ULL << 17 ) +#define TXNIC_QS_SQ_CFG_QSIZE(sz) ( ( ( uint64_t ) (sz) ) << 8 ) +#define TXNIC_QS_SQ_CFG_QSIZE_1K \ + TXNIC_QS_SQ_CFG_QSIZE ( 0 ) + +/** Send queue base address */ +#define TXNIC_QS_SQ_BASE(q) ( ( (q) << 18 ) | 0x010820 ) + +/** Send queue head pointer */ +#define TXNIC_QS_SQ_HEAD(q) ( ( (q) << 18 ) | 0x010828 ) + +/** Send queue tail pointer */ +#define TXNIC_QS_SQ_TAIL(q) ( ( (q) << 18 ) | 0x010830 ) + +/** Send queue doorbell */ +#define TXNIC_QS_SQ_DOOR(q) ( ( (q) << 18 ) | 0x010838 ) + +/** Send queue status */ +#define TXNIC_QS_SQ_STATUS(q) ( ( (q) << 18 ) | 0x010840 ) +#define TXNIC_QS_SQ_STATUS_STOPPED ( 1ULL << 21 ) + +/** Maximum time to wait for a send queue to stop + * + * This is a policy decision. + */ +#define TXNIC_SQ_STOP_MAX_WAIT_MS 100 + +/** A send header subdescriptor */ +struct txnic_send_header { + /** Total length */ + uint32_t total; + /** Unused */ + uint8_t unused_a[2]; + /** Subdescriptor count */ + uint8_t subdcnt; + /** Flags */ + uint8_t flags; + /** Unused */ + uint8_t unused_b[8]; +} __attribute__ (( packed )); + +/** Flags for send header subdescriptor + * + * These comprise SUBDC=0x1 and PNC=0x1. + */ +#define TXNIC_SEND_HDR_FLAGS 0x14 + +/** A send gather subdescriptor */ +struct txnic_send_gather { + /** Size */ + uint16_t size; + /** Unused */ + uint8_t unused[5]; + /** Flags */ + uint8_t flags; + /** Address */ + uint64_t addr; +} __attribute__ (( packed )); + +/** Flags for send gather subdescriptor + * + * These comprise SUBDC=0x4 and LD_TYPE=0x0. + */ +#define TXNIC_SEND_GATHER_FLAGS 0x40 + +/** A send queue entry + * + * Each send queue entry comprises a single send header subdescriptor + * and a single send gather subdescriptor. + */ +struct txnic_sqe { + /** Send header descriptor */ + struct txnic_send_header hdr; + /** Send gather descriptor */ + struct txnic_send_gather gather; +} __attribute__ (( packed )); + +/** Number of subdescriptors per send queue entry */ +#define TXNIC_SQE_SUBDESCS ( sizeof ( struct txnic_sqe ) / \ + sizeof ( struct txnic_send_header ) ) + +/** Number of send queue entries + * + * The minimum send queue size is 1024 entries. + */ +#define TXNIC_SQES ( 1024 / TXNIC_SQE_SUBDESCS ) + +/** Send queue maximum fill level + * + * This is a policy decision. + */ +#define TXNIC_SQ_FILL 32 + +/** Send queue alignment */ +#define TXNIC_SQ_ALIGN TXNIC_LINE_SIZE + +/** Send queue stride */ +#define TXNIC_SQ_STRIDE sizeof ( struct txnic_sqe ) + +/** Send queue size */ +#define TXNIC_SQ_SIZE ( TXNIC_SQES * TXNIC_SQ_STRIDE ) + +/** A send queue */ +struct txnic_sq { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Send queue entries */ + userptr_t sqe; +}; + +/****************************************************************************** + * + * Receive queue + * + ****************************************************************************** + */ + +/** Receive queue configuration */ +#define TXNIC_QS_RQ_CFG(q) ( ( (q) << 18 ) | 0x010600 ) +#define TXNIC_QS_RQ_CFG_ENA ( 1ULL << 1 ) + +/** Maximum time to wait for a receive queue to disable + * + * This is a policy decision. + */ +#define TXNIC_RQ_DISABLE_MAX_WAIT_MS 100 + +/** Receive buffer descriptor ring configuration */ +#define TXNIC_QS_RBDR_CFG(q) ( ( (q) << 18 ) | 0x010c00 ) +#define TXNIC_QS_RBDR_CFG_ENA ( 1ULL << 44 ) +#define TXNIC_QS_RBDR_CFG_RESET ( 1ULL << 43 ) +#define TXNIC_QS_RBDR_CFG_QSIZE(sz) ( ( ( uint64_t ) (sz) ) << 32 ) +#define TXNIC_QS_RBDR_CFG_QSIZE_8K \ + TXNIC_QS_RBDR_CFG_QSIZE ( 0 ) +#define TXNIC_QS_RBDR_CFG_LINES(sz) ( ( ( uint64_t ) (sz) ) << 0 ) + +/** Receive buffer descriptor ring base address */ +#define TXNIC_QS_RBDR_BASE(q) ( ( (q) << 18 ) | 0x010c20 ) + +/** Receive buffer descriptor ring head pointer */ +#define TXNIC_QS_RBDR_HEAD(q) ( ( (q) << 18 ) | 0x010c28 ) + +/** Receive buffer descriptor ring tail pointer */ +#define TXNIC_QS_RBDR_TAIL(q) ( ( (q) << 18 ) | 0x010c30 ) + +/** Receive buffer descriptor ring doorbell */ +#define TXNIC_QS_RBDR_DOOR(q) ( ( (q) << 18 ) | 0x010c38 ) + +/** Receive buffer descriptor ring status 0 */ +#define TXNIC_QS_RBDR_STATUS0(q) ( ( (q) << 18 ) | 0x010c40 ) + +/** A receive buffer descriptor ring entry */ +struct txnic_rbdr_entry { + /** Address */ + uint64_t addr; +} __attribute__ (( packed )); + +/** A receive queue entry */ +struct txnic_rqe { + /** Receive buffer descriptor ring entry */ + struct txnic_rbdr_entry rbdre; +} __attribute__ (( packed )); + +/** Number of receive queue entries + * + * The minimum receive queue size is 8192 entries. + */ +#define TXNIC_RQES 8192 + +/** Receive queue maximum fill level + * + * This is a policy decision. Must not exceed TXNIC_RQES. + */ +#define TXNIC_RQ_FILL 32 + +/** Receive queue entry size + * + * This is a policy decision. + */ +#define TXNIC_RQE_SIZE ( ( ETH_DATA_ALIGN + ETH_FRAME_LEN + \ + 4 /* VLAN */ + TXNIC_LINE_SIZE - 1 ) \ + & ~( TXNIC_LINE_SIZE - 1 ) ) + +/** Receive queue alignment */ +#define TXNIC_RQ_ALIGN TXNIC_LINE_SIZE + +/** Receive queue stride */ +#define TXNIC_RQ_STRIDE sizeof ( struct txnic_rqe ) + +/** Receive queue size */ +#define TXNIC_RQ_SIZE ( TXNIC_RQES * TXNIC_RQ_STRIDE ) + +/** A receive queue */ +struct txnic_rq { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Receive queue entries */ + userptr_t rqe; + /** I/O buffers */ + struct io_buffer *iobuf[TXNIC_RQ_FILL]; +}; + +/****************************************************************************** + * + * Completion queue + * + ****************************************************************************** + */ + +/** Completion queue configuration */ +#define TXNIC_QS_CQ_CFG(q) ( ( (q) << 18 ) | 0x010400 ) +#define TXNIC_QS_CQ_CFG_ENA ( 1ULL << 42 ) +#define TXNIC_QS_CQ_CFG_RESET ( 1ULL << 41 ) +#define TXNIC_QS_CQ_CFG_QSIZE(sz) ( ( ( uint64_t ) (sz) ) << 32 ) +#define TXNIC_QS_CQ_CFG_QSIZE_256 \ + TXNIC_QS_CQ_CFG_QSIZE ( 7 ) + +/** Maximum time to wait for a completion queue to disable + * + * This is a policy decision. + */ +#define TXNIC_CQ_DISABLE_MAX_WAIT_MS 100 + +/** Completion queue base address */ +#define TXNIC_QS_CQ_BASE(q) ( ( (q) << 18 ) | 0x010420 ) + +/** Completion queue head pointer */ +#define TXNIC_QS_CQ_HEAD(q) ( ( (q) << 18 ) | 0x010428 ) + +/** Completion queue tail pointer */ +#define TXNIC_QS_CQ_TAIL(q) ( ( (q) << 18 ) | 0x010430 ) + +/** Completion queue doorbell */ +#define TXNIC_QS_CQ_DOOR(q) ( ( (q) << 18 ) | 0x010438 ) + +/** Completion queue status */ +#define TXNIC_QS_CQ_STATUS(q) ( ( (q) << 18 ) | 0x010440 ) +#define TXNIC_QS_CQ_STATUS_QCOUNT(status) \ + ( ( (status) >> 0 ) & 0xffff ) + +/** Completion queue status 2 */ +#define TXNIC_QS_CQ_STATUS2(q) ( ( (q) << 18 ) | 0x010448 ) + +/** A send completion queue entry */ +struct txnic_cqe_send { + /** Status */ + uint8_t send_status; + /** Unused */ + uint8_t unused[4]; + /** Send queue entry pointer */ + uint16_t sqe_ptr; + /** Type */ + uint8_t cqe_type; +} __attribute__ (( packed )); + +/** Send completion queue entry type */ +#define TXNIC_CQE_TYPE_SEND 0x80 + +/** A receive completion queue entry */ +struct txnic_cqe_rx { + /** Error opcode */ + uint8_t errop; + /** Unused */ + uint8_t unused_a[6]; + /** Type */ + uint8_t cqe_type; + /** Unused */ + uint8_t unused_b[1]; + /** Padding */ + uint8_t apad; + /** Unused */ + uint8_t unused_c[4]; + /** Length */ + uint16_t len; +} __attribute__ (( packed )); + +/** Receive completion queue entry type */ +#define TXNIC_CQE_TYPE_RX 0x20 + +/** Applied padding */ +#define TXNIC_CQE_RX_APAD_LEN( apad ) ( (apad) >> 5 ) + +/** Completion queue entry common fields */ +struct txnic_cqe_common { + /** Unused */ + uint8_t unused_a[7]; + /** Type */ + uint8_t cqe_type; +} __attribute__ (( packed )); + +/** A completion queue entry */ +union txnic_cqe { + /** Common fields */ + struct txnic_cqe_common common; + /** Send completion */ + struct txnic_cqe_send send; + /** Receive completion */ + struct txnic_cqe_rx rx; +}; + +/** Number of completion queue entries + * + * The minimum completion queue size is 256 entries. + */ +#define TXNIC_CQES 256 + +/** Completion queue alignment */ +#define TXNIC_CQ_ALIGN 512 + +/** Completion queue stride */ +#define TXNIC_CQ_STRIDE 512 + +/** Completion queue size */ +#define TXNIC_CQ_SIZE ( TXNIC_CQES * TXNIC_CQ_STRIDE ) + +/** A completion queue */ +struct txnic_cq { + /** Consumer counter */ + unsigned int cons; + /** Completion queue entries */ + userptr_t cqe; +}; + +/****************************************************************************** + * + * Virtual NIC + * + ****************************************************************************** + */ + +/** A virtual NIC */ +struct txnic { + /** Registers */ + void *regs; + /** Device name (for debugging) */ + const char *name; + /** Network device */ + struct net_device *netdev; + + /** Send queue */ + struct txnic_sq sq; + /** Receive queue */ + struct txnic_rq rq; + /** Completion queue */ + struct txnic_cq cq; +}; + +/****************************************************************************** + * + * Physical function + * + ****************************************************************************** + */ + +/** Physical function configuration */ +#define TXNIC_PF_CFG 0x000000 +#define TXNIC_PF_CFG_ENA ( 1ULL << 0 ) + +/** Backpressure configuration */ +#define TXNIC_PF_BP_CFG 0x000080 +#define TXNIC_PF_BP_CFG_BP_POLL_ENA ( 1ULL << 6 ) +#define TXNIC_PF_BP_CFG_BP_POLL_DLY(dl) ( ( ( uint64_t ) (dl) ) << 0 ) +#define TXNIC_PF_BP_CFG_BP_POLL_DLY_DEFAULT \ + TXNIC_PF_BP_CFG_BP_POLL_DLY ( 3 ) + +/** Interface send configuration */ +#define TXNIC_PF_INTF_SEND_CFG(in) ( ( (in) << 8 ) | 0x000200 ) +#define TXNIC_PF_INTF_SEND_CFG_BLOCK_BGX ( 1ULL << 3 ) +#define TXNIC_PF_INTF_SEND_CFG_BLOCK(bl) ( ( ( uint64_t ) (bl) ) << 0 ) + +/** Interface backpressure configuration */ +#define TXNIC_PF_INTF_BP_CFG(in) ( ( (in) << 8 ) | 0x000208 ) +#define TXNIC_PF_INTF_BP_CFG_BP_ENA ( 1ULL << 63 ) +#define TXNIC_PF_INTF_BP_CFG_BP_ID_BGX ( 1ULL << 3 ) +#define TXNIC_PF_INTF_BP_CFG_BP_ID(bp) ( ( ( uint64_t ) (bp) ) << 0 ) + +/** Port kind configuration */ +#define TXNIC_PF_PKIND_CFG(pk) ( ( (pk) << 3 ) | 0x000600 ) +#define TXNIC_PF_PKIND_CFG_LENERR_EN ( 1ULL << 33 ) +#define TXNIC_PF_PKIND_CFG_MAXLEN(ct) ( ( ( uint64_t ) (ct) ) << 16 ) +#define TXNIC_PF_PKIND_CFG_MAXLEN_DISABLE \ + TXNIC_PF_PKIND_CFG_MAXLEN ( 0xffff ) +#define TXNIC_PF_PKIND_CFG_MINLEN(ct) ( ( ( uint64_t ) (ct) ) << 0 ) +#define TXNIC_PF_PKIND_CFG_MINLEN_DISABLE \ + TXNIC_PF_PKIND_CFG_MINLEN ( 0x0000 ) + +/** Match parse index configuration */ +#define TXNIC_PF_MPI_CFG(ix) ( ( (ix) << 3 ) | 0x210000 ) +#define TXNIC_PF_MPI_CFG_VNIC(vn) ( ( ( uint64_t ) (vn) ) << 24 ) +#define TXNIC_PF_MPI_CFG_RSSI_BASE(ix) ( ( ( uint64_t ) (ix) ) << 0 ) + +/** RSS indirection receive queue */ +#define TXNIC_PF_RSSI_RQ(ix) ( ( (ix) << 3 ) | 0x220000 ) +#define TXNIC_PF_RSSI_RQ_RQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 3 ) + +/** LMAC registers */ +#define TXNIC_PF_LMAC(lm) ( ( (lm) << 3 ) | 0x240000 ) + +/** LMAC configuration */ +#define TXNIC_PF_LMAC_CFG 0x000000 +#define TXNIC_PF_LMAC_CFG_ADJUST(ad) ( ( ( uint64_t ) (ad) ) << 8 ) +#define TXNIC_PF_LMAC_CFG_ADJUST_DEFAULT \ + TXNIC_PF_LMAC_CFG_ADJUST ( 6 ) +#define TXNIC_PF_LMAC_CFG_MIN_PKT_SIZE(sz) ( ( ( uint64_t ) (sz) ) << 0 ) + +/** LMAC configuration 2 */ +#define TXNIC_PF_LMAC_CFG2 0x000100 +#define TXNIC_PF_LMAC_CFG2_MAX_PKT_SIZE(sz) ( ( ( uint64_t ) (sz) ) << 0 ) + +/** LMAC credit */ +#define TXNIC_PF_LMAC_CREDIT 0x004000 +#define TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT(ct) ( ( ( uint64_t ) (ct) ) << 12 ) +#define TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT_DEFAULT \ + TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT ( 192 ) +#define TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT(ct) ( ( ( uint64_t ) (ct) ) << 2 ) +#define TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT_DEFAULT \ + TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT ( 511 ) +#define TXNIC_PF_LMAC_CREDIT_CC_ENABLE ( 1ULL << 1 ) + +/** Channel registers */ +#define TXNIC_PF_CHAN(ch) ( ( (ch) << 3 ) | 0x400000 ) + +/** Channel transmit configuration */ +#define TXNIC_PF_CHAN_TX_CFG 0x000000 +#define TXNIC_PF_CHAN_TX_CFG_BP_ENA ( 1ULL << 0 ) + +/** Channel receive configuration */ +#define TXNIC_PF_CHAN_RX_CFG 0x020000 +#define TXNIC_PF_CHAN_RX_CFG_CPI_BASE(ix) ( ( ( uint64_t ) (ix) ) << 48 ) + +/** Channel receive backpressure configuration */ +#define TXNIC_PF_CHAN_RX_BP_CFG 0x080000 +#define TXNIC_PF_CHAN_RX_BP_CFG_ENA ( 1ULL << 63 ) +#define TXNIC_PF_CHAN_RX_BP_CFG_BPID(bp) ( ( ( uint64_t ) (bp) ) << 0 ) + +/** Traffic limiter 2 configuration */ +#define TXNIC_PF_TL2_CFG(tl) ( ( (tl) << 3 ) | 0x500000 ) +#define TXNIC_PF_TL2_CFG_RR_QUANTUM(rr) ( ( ( uint64_t ) (rr) ) << 0 ) +#define TXNIC_PF_TL2_CFG_RR_QUANTUM_DEFAULT \ + TXNIC_PF_TL2_CFG_RR_QUANTUM ( 0x905 ) + +/** Traffic limiter 3 configuration */ +#define TXNIC_PF_TL3_CFG(tl) ( ( (tl) << 3 ) | 0x600000 ) +#define TXNIC_PF_TL3_CFG_RR_QUANTUM(rr) ( ( ( uint64_t ) (rr) ) << 0 ) +#define TXNIC_PF_TL3_CFG_RR_QUANTUM_DEFAULT \ + TXNIC_PF_TL3_CFG_RR_QUANTUM ( 0x905 ) + +/** Traffic limiter 3 channel mapping */ +#define TXNIC_PF_TL3_CHAN(tl) ( ( (tl) << 3 ) | 0x620000 ) +#define TXNIC_PF_TL3_CHAN_CHAN(ch) ( ( (ch) & 0x7f ) << 0 ) + +/** Traffic limiter 4 configuration */ +#define TXNIC_PF_TL4_CFG(tl) ( ( (tl) << 3 ) | 0x800000 ) +#define TXNIC_PF_TL4_CFG_SQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 27 ) +#define TXNIC_PF_TL4_CFG_RR_QUANTUM(rr) ( ( ( uint64_t ) (rr) ) << 0 ) +#define TXNIC_PF_TL4_CFG_RR_QUANTUM_DEFAULT \ + TXNIC_PF_TL4_CFG_RR_QUANTUM ( 0x905 ) + +/** Queue set registers */ +#define TXNIC_PF_QS(qs) ( ( (qs) << 21 ) | 0x20000000UL ) + +/** Queue set configuration */ +#define TXNIC_PF_QS_CFG 0x010000 +#define TXNIC_PF_QS_CFG_ENA ( 1ULL << 31 ) +#define TXNIC_PF_QS_CFG_VNIC(vn) ( ( ( uint64_t ) (vn) ) << 0 ) + +/** Receive queue configuration */ +#define TXNIC_PF_QS_RQ_CFG(q) ( ( (q) << 18 ) | 0x010400 ) +#define TXNIC_PF_QS_RQ_CFG_CACHING(cx) ( ( ( uint64_t ) (cx) ) << 26 ) +#define TXNIC_PF_QS_RQ_CFG_CACHING_ALL \ + TXNIC_PF_QS_RQ_CFG_CACHING ( 1 ) +#define TXNIC_PF_QS_RQ_CFG_CQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 19 ) +#define TXNIC_PF_QS_RQ_CFG_RBDR_CONT_QS(qs) ( ( ( uint64_t ) (qs) ) << 9 ) +#define TXNIC_PF_QS_RQ_CFG_RBDR_STRT_QS(qs) ( ( ( uint64_t ) (qs) ) << 1 ) + +/** Receive queue drop configuration */ +#define TXNIC_PF_QS_RQ_DROP_CFG(q) ( ( (q) << 18 ) | 0x010420 ) + +/** Receive queue backpressure configuration */ +#define TXNIC_PF_QS_RQ_BP_CFG(q) ( ( (q) << 18 ) | 0x010500 ) +#define TXNIC_PF_QS_RQ_BP_CFG_RBDR_BP_ENA ( 1ULL << 63 ) +#define TXNIC_PF_QS_RQ_BP_CFG_CQ_BP_ENA ( 1ULL << 62 ) +#define TXNIC_PF_QS_RQ_BP_CFG_BPID(bp) ( ( ( uint64_t ) (bp) ) << 0 ) + +/** Send queue configuration */ +#define TXNIC_PF_QS_SQ_CFG(q) ( ( (q) << 18 ) | 0x010c00 ) +#define TXNIC_PF_QS_SQ_CFG_CQ_QS(qs) ( ( ( uint64_t ) (qs) ) << 3 ) + +/** Send queue configuration 2 */ +#define TXNIC_PF_QS_SQ_CFG2(q) ( ( (q) << 18 ) | 0x010c08 ) +#define TXNIC_PF_QS_SQ_CFG2_TL4(tl) ( ( ( uint64_t ) (tl) ) << 0 ) + +/** A physical function */ +struct txnic_pf { + /** Registers */ + void *regs; + /** PCI device */ + struct pci_device *pci; + /** Node ID */ + unsigned int node; + + /** Virtual function BAR base */ + unsigned long vf_membase; + /** Virtual function BAR stride */ + unsigned long vf_stride; + + /** List of physical functions */ + struct list_head list; + /** BGX Ethernet interfaces (if known) */ + struct txnic_bgx *bgx[TXNIC_NUM_BGX]; +}; + +/** + * Calculate virtual NIC index + * + * @v bgx_idx BGX Ethernet interface index + * @v lmac_idx Logical MAC index + * @ret vnic_idx Virtual NIC index + */ +#define TXNIC_VNIC_IDX( bgx_idx, lmac_idx ) \ + ( ( (bgx_idx) * TXNIC_NUM_LMAC ) + (lmac_idx) ) + +/** + * Calculate BGX Ethernet interface index + * + * @v vnic_idx Virtual NIC index + * @ret bgx_idx BGX Ethernet interface index + */ +#define TXNIC_BGX_IDX( vnic_idx ) ( (vnic_idx) / TXNIC_NUM_LMAC ) + +/** + * Calculate logical MAC index + * + * @v vnic_idx Virtual NIC index + * @ret lmac_idx Logical MAC index + */ +#define TXNIC_LMAC_IDX( vnic_idx ) ( (vnic_idx) % TXNIC_NUM_LMAC ) + +/** + * Calculate traffic limiter 2 index + * + * @v vnic_idx Virtual NIC index + * @v tl2_idx Traffic limiter 2 index + */ +#define TXNIC_TL2_IDX( vnic_idx ) ( (vnic_idx) << 3 ) + +/** + * Calculate traffic limiter 3 index + * + * @v vnic_idx Virtual NIC index + * @v tl3_idx Traffic limiter 3 index + */ +#define TXNIC_TL3_IDX( vnic_idx ) ( (vnic_idx) << 5 ) + +/** + * Calculate traffic limiter 4 index + * + * @v vnic_idx Virtual NIC index + * @v tl4_idx Traffic limiter 4 index + */ +#define TXNIC_TL4_IDX( vnic_idx ) ( (vnic_idx) << 7 ) + +/** + * Calculate channel index + * + * @v vnic_idx Virtual NIC index + * @v chan_idx Channel index + */ +#define TXNIC_CHAN_IDX( vnic_idx ) ( ( TXNIC_BGX_IDX (vnic_idx) << 7 ) | \ + ( TXNIC_LMAC_IDX (vnic_idx) << 4 ) ) + +/****************************************************************************** + * + * BGX Ethernet interface + * + ****************************************************************************** + */ + +/** Per-LMAC registers */ +#define BGX_LMAC(lm) ( ( (lm) << 20 ) | 0x00000000UL ) + +/** CMR configuration */ +#define BGX_CMR_CONFIG 0x000000 +#define BGX_CMR_CONFIG_ENABLE ( 1ULL << 15 ) +#define BGX_CMR_CONFIG_DATA_PKT_RX_EN ( 1ULL << 14 ) +#define BGX_CMR_CONFIG_DATA_PKT_TX_EN ( 1ULL << 13 ) +#define BGX_CMR_CONFIG_LMAC_TYPE_GET(config) \ + ( ( (config) >> 8 ) & 0x7 ) +#define BGX_CMR_CONFIG_LMAC_TYPE_SET(ty) ( ( ( uint64_t ) (ty) ) << 8 ) +#define BGX_CMR_CONFIG_LANE_TO_SDS(ls) ( ( ( uint64_t ) (ls) ) << 0 ) + +/** CMR global configuration */ +#define BGX_CMR_GLOBAL_CONFIG 0x000008 +#define BGX_CMR_GLOBAL_CONFIG_FCS_STRIP ( 1ULL << 6 ) + +/** CMR receive statistics 0 */ +#define BGX_CMR_RX_STAT0 0x000070 + +/** CMR receive statistics 1 */ +#define BGX_CMR_RX_STAT1 0x000078 + +/** CMR receive statistics 2 */ +#define BGX_CMR_RX_STAT2 0x000080 + +/** CMR receive statistics 3 */ +#define BGX_CMR_RX_STAT3 0x000088 + +/** CMR receive statistics 4 */ +#define BGX_CMR_RX_STAT4 0x000090 + +/** CMR receive statistics 5 */ +#define BGX_CMR_RX_STAT5 0x000098 + +/** CMR receive statistics 6 */ +#define BGX_CMR_RX_STAT6 0x0000a0 + +/** CMR receive statistics 7 */ +#define BGX_CMR_RX_STAT7 0x0000a8 + +/** CMR receive statistics 8 */ +#define BGX_CMR_RX_STAT8 0x0000b0 + +/** CMR receive statistics 9 */ +#define BGX_CMR_RX_STAT9 0x0000b8 + +/** CMR receive statistics 10 */ +#define BGX_CMR_RX_STAT10 0x0000c0 + +/** CMR destination MAC control */ +#define BGX_CMR_RX_DMAC_CTL 0x0000e8 +#define BGX_CMR_RX_DMAC_CTL_MCST_MODE(md) ( ( ( uint64_t ) (md) ) << 1 ) +#define BGX_CMR_RX_DMAC_CTL_MCST_MODE_ACCEPT \ + BGX_CMR_RX_DMAC_CTL_MCST_MODE ( 1 ) +#define BGX_CMR_RX_DMAC_CTL_BCST_ACCEPT ( 1ULL << 0 ) + +/** CMR destination MAC CAM */ +#define BGX_CMR_RX_DMAC_CAM(i) ( ( (i) << 3 ) | 0x000200 ) + +/** CMR receive steering */ +#define BGX_CMR_RX_STEERING(i) ( ( (i) << 3 ) | 0x000300 ) + +/** CMR backpressure channel mask AND */ +#define BGX_CMR_CHAN_MSK_AND 0x000450 +#define BGX_CMR_CHAN_MSK_AND_ALL(count) \ + ( 0xffffffffffffffffULL >> ( 16 * ( 4 - (count) ) ) ) + +/** CMR transmit statistics 0 */ +#define BGX_CMR_TX_STAT0 0x000600 + +/** CMR transmit statistics 1 */ +#define BGX_CMR_TX_STAT1 0x000608 + +/** CMR transmit statistics 2 */ +#define BGX_CMR_TX_STAT2 0x000610 + +/** CMR transmit statistics 3 */ +#define BGX_CMR_TX_STAT3 0x000618 + +/** CMR transmit statistics 4 */ +#define BGX_CMR_TX_STAT4 0x000620 + +/** CMR transmit statistics 5 */ +#define BGX_CMR_TX_STAT5 0x000628 + +/** CMR transmit statistics 6 */ +#define BGX_CMR_TX_STAT6 0x000630 + +/** CMR transmit statistics 7 */ +#define BGX_CMR_TX_STAT7 0x000638 + +/** CMR transmit statistics 8 */ +#define BGX_CMR_TX_STAT8 0x000640 + +/** CMR transmit statistics 9 */ +#define BGX_CMR_TX_STAT9 0x000648 + +/** CMR transmit statistics 10 */ +#define BGX_CMR_TX_STAT10 0x000650 + +/** CMR transmit statistics 11 */ +#define BGX_CMR_TX_STAT11 0x000658 + +/** CMR transmit statistics 12 */ +#define BGX_CMR_TX_STAT12 0x000660 + +/** CMR transmit statistics 13 */ +#define BGX_CMR_TX_STAT13 0x000668 + +/** CMR transmit statistics 14 */ +#define BGX_CMR_TX_STAT14 0x000670 + +/** CMR transmit statistics 15 */ +#define BGX_CMR_TX_STAT15 0x000678 + +/** CMR transmit statistics 16 */ +#define BGX_CMR_TX_STAT16 0x000680 + +/** CMR transmit statistics 17 */ +#define BGX_CMR_TX_STAT17 0x000688 + +/** CMR receive logical MACs */ +#define BGX_CMR_RX_LMACS 0x000468 +#define BGX_CMR_RX_LMACS_LMACS_GET(lmacs) \ + ( ( (lmacs) >> 0 ) & 0x7 ) +#define BGX_CMR_RX_LMACS_LMACS_SET(ct) ( ( ( uint64_t ) (ct) ) << 0 ) + +/** CMR transmit logical MACs */ +#define BGX_CMR_TX_LMACS 0x001000 +#define BGX_CMR_TX_LMACS_LMACS_GET(lmacs) \ + ( ( (lmacs) >> 0 ) & 0x7 ) +#define BGX_CMR_TX_LMACS_LMACS_SET(ct) ( ( ( uint64_t ) (ct) ) << 0 ) + +/** SPU control 1 */ +#define BGX_SPU_CONTROL1 0x010000 +#define BGX_SPU_CONTROL1_RESET ( 1ULL << 15 ) +#define BGX_SPU_CONTROL1_LO_PWR ( 1ULL << 11 ) + +/** SPU reset delay */ +#define BGX_SPU_RESET_DELAY_MS 10 + +/** SPU status 1 */ +#define BGX_SPU_STATUS1 0x010008 +#define BGX_SPU_STATUS1_FLT ( 1ULL << 7 ) +#define BGX_SPU_STATUS1_RCV_LNK ( 1ULL << 2 ) + +/** SPU status 2 */ +#define BGX_SPU_STATUS2 0x010020 +#define BGX_SPU_STATUS2_RCVFLT ( 1ULL << 10 ) + +/** SPU BASE-R status 1 */ +#define BGX_SPU_BR_STATUS1 0x010030 +#define BGX_SPU_BR_STATUS1_RCV_LNK ( 1ULL << 12 ) +#define BGX_SPU_BR_STATUS1_HI_BER ( 1ULL << 1 ) +#define BGX_SPU_BR_STATUS1_BLK_LOCK ( 1ULL << 0 ) + +/** SPU BASE-R status 2 */ +#define BGX_SPU_BR_STATUS2 0x010038 +#define BGX_SPU_BR_STATUS2_LATCHED_LOCK ( 1ULL << 15 ) +#define BGX_SPU_BR_STATUS2_LATCHED_BER ( 1ULL << 14 ) + +/** SPU BASE-R alignment status */ +#define BGX_SPU_BR_ALGN_STATUS 0x010050 +#define BGX_SPU_BR_ALGN_STATUS_ALIGND ( 1ULL << 12 ) + +/** SPU BASE-R link training control */ +#define BGX_SPU_BR_PMD_CONTROL 0x010068 +#define BGX_SPU_BR_PMD_CONTROL_TRAIN_EN ( 1ULL << 1 ) + +/** SPU BASE-R link training status */ +#define BGX_SPU_BR_PMD_STATUS 0x010070 + +/** SPU link partner coefficient update */ +#define BGX_SPU_BR_PMD_LP_CUP 0x010078 + +/** SPU local device coefficient update */ +#define BGX_SPU_BR_PMD_LD_CUP 0x010088 + +/** SPU local device status report */ +#define BGX_SPU_BR_PMD_LD_REP 0x010090 + +/** SPU forward error correction control */ +#define BGX_SPU_FEC_CONTROL 0x0100a0 + +/** SPU autonegotation control */ +#define BGX_SPU_AN_CONTROL 0x0100c8 + +/** SPU autonegotiation status */ +#define BGX_SPU_AN_STATUS 0x0100d0 +#define BGX_SPU_AN_STATUS_XNP_STAT ( 1ULL << 7 ) +#define BGX_SPU_AN_STATUS_PAGE_RX ( 1ULL << 6 ) +#define BGX_SPU_AN_STATUS_AN_COMPLETE ( 1ULL << 5 ) +#define BGX_SPU_AN_STATUS_LINK_STATUS ( 1ULL << 2 ) +#define BGX_SPU_AN_STATUS_LP_AN_ABLE ( 1ULL << 0 ) + +/** SPU interrupt */ +#define BGX_SPU_INT 0x010220 +#define BGX_SPU_INT_TRAINING_FAIL ( 1ULL << 14 ) +#define BGX_SPU_INT_TRAINING_DONE ( 1ULL << 13 ) +#define BGX_SPU_INT_AN_COMPLETE ( 1ULL << 12 ) +#define BGX_SPU_INT_AN_LINK_GOOD ( 1ULL << 11 ) +#define BGX_SPU_INT_AN_PAGE_RX ( 1ULL << 10 ) +#define BGX_SPU_INT_FEC_UNCORR ( 1ULL << 9 ) +#define BGX_SPU_INT_FEC_CORR ( 1ULL << 8 ) +#define BGX_SPU_INT_BIP_ERR ( 1ULL << 7 ) +#define BGX_SPU_INT_DBG_SYNC ( 1ULL << 6 ) +#define BGX_SPU_INT_ALGNLOS ( 1ULL << 5 ) +#define BGX_SPU_INT_SYNLOS ( 1ULL << 4 ) +#define BGX_SPU_INT_BITLCKLS ( 1ULL << 3 ) +#define BGX_SPU_INT_ERR_BLK ( 1ULL << 2 ) +#define BGX_SPU_INT_RX_LINK_DOWN ( 1ULL << 1 ) +#define BGX_SPU_INT_RX_LINK_UP ( 1ULL << 0 ) + +/** LMAC types */ +enum txnic_lmac_types { + TXNIC_LMAC_SGMII = 0x0, /**< SGMII/1000BASE-X */ + TXNIC_LMAC_XAUI = 0x1, /**< 10GBASE-X/XAUI or DXAUI */ + TXNIC_LMAC_RXAUI = 0x2, /**< Reduced XAUI */ + TXNIC_LMAC_10G_R = 0x3, /**< 10GBASE-R */ + TXNIC_LMAC_40G_R = 0x4, /**< 40GBASE-R */ +}; + +/** An LMAC type */ +struct txnic_lmac_type { + /** Name */ + const char *name; + /** Number of LMACs */ + uint8_t count; + /** Lane-to-SDS mapping */ + uint32_t lane_to_sds; +}; + +/** An LMAC address */ +union txnic_lmac_address { + struct { + uint8_t pad[2]; + uint8_t raw[ETH_ALEN]; + } __attribute__ (( packed )); + uint64_t be64; +}; + +/** A Logical MAC (LMAC) */ +struct txnic_lmac { + /** Registers */ + void *regs; + /** Containing BGX Ethernet interface */ + struct txnic_bgx *bgx; + /** Virtual NIC index */ + unsigned int idx; + + /** MAC address */ + union txnic_lmac_address mac; + + /** Virtual NIC (if applicable) */ + struct txnic *vnic; +}; + +/** A BGX Ethernet interface */ +struct txnic_bgx { + /** Registers */ + void *regs; + /** PCI device */ + struct pci_device *pci; + /** Node ID */ + unsigned int node; + /** BGX index */ + unsigned int idx; + + /** LMAC type */ + struct txnic_lmac_type *type; + /** Number of LMACs */ + unsigned int count; + /** Link training is in use */ + int training; + + /** List of BGX Ethernet interfaces */ + struct list_head list; + /** Physical function (if known) */ + struct txnic_pf *pf; + + /** Logical MACs */ + struct txnic_lmac lmac[TXNIC_NUM_LMAC]; +}; + +#endif /* _THUNDERX_H */ diff --git a/src/drivers/net/thunderxcfg.h b/src/drivers/net/thunderxcfg.h new file mode 100644 index 000000000..ffb34d36e --- /dev/null +++ b/src/drivers/net/thunderxcfg.h @@ -0,0 +1,155 @@ +#ifndef _THUNDERXCFG_H +#define _THUNDERXCFG_H + +/** @file + * + * Cavium ThunderX Board Configuration + * + * The definitions in this section are extracted from BSD-licensed + * (but non-public) portions of ThunderPkg. + * + */ + +FILE_LICENCE ( BSD2 ); + +#include + +/****************************************************************************** + * + * From ThunderxBoardConfig.h + * + ****************************************************************************** + * + * Header file for Cavium ThunderX Board Configurations + * Copyright (c) 2015, Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define MAX_NODES 2 +#define CLUSTER_COUNT 3 +#define CORE_PER_CLUSTER_COUNT 16 +#define CORE_COUNT (CLUSTER_COUNT*CORE_PER_CLUSTER_COUNT) +#define BGX_PER_NODE_COUNT 2 +#define LMAC_PER_BGX_COUNT 4 +#define PEM_PER_NODE_COUNT 6 +#define LMC_PER_NODE_COUNT 4 +#define DIMM_PER_LMC_COUNT 2 + +#define THUNDERX_CPU_ID(node, cluster, core) (((node) << 16) | ((cluster) << 8) | (core)) + +/****************************************************************************** + * + * From ThunderConfigProtocol.h + * + ****************************************************************************** + * + * Thunder board Configuration Protocol + * + * Copyright (c) 2015, Cavium Inc. All rights reserved.
+ * + * This program and the accompanying materials are licensed and made + * available under the terms and conditions of the BSD License which + * accompanies this distribution. The full text of the license may + * be found at http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" + * BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER + * EXPRESS OR IMPLIED. + * + */ + +#define EFI_THUNDER_CONFIG_PROTOCOL_GUID \ + {0xc12b1873, 0xac17, 0x4176, {0xac, 0x77, 0x7e, 0xcb, 0x4d, 0xef, 0xff, 0xec}} + +/// +/// Forward declaration +/// +typedef struct _EFI_THUNDER_CONFIG_PROTOCOL EFI_THUNDER_CONFIG_PROTOCOL; + +typedef enum { + BGX_ENABLED, + BGX_MODE, + LMAC_COUNT, + BASE_ADDRESS, + LMAC_TYPE_BGX, + QLM_MASK, + QLM_FREQ, + USE_TRAINING +} BGX_PROPERTY; + +typedef enum { + ENABLED, + LANE_TO_SDS, + MAC_ADDRESS +} LMAC_PROPERTY; + +/// +/// Function prototypes +/// +typedef +EFI_STATUS +(EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG)( + IN EFI_THUNDER_CONFIG_PROTOCOL *This, + OUT VOID** cfg + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_BGX_PROP)( + IN EFI_THUNDER_CONFIG_PROTOCOL *This, + IN UINTN NodeId, + IN UINTN BgxId, + IN BGX_PROPERTY BgxProp, + IN UINT64 ValueSize, + OUT UINT64 *Value + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_THUNDER_CONFIG_PROTOCOL_GET_LMAC_PROP)( + IN EFI_THUNDER_CONFIG_PROTOCOL *This, + IN UINTN NodeId, + IN UINTN BgxId, + IN UINTN LmacId, + IN LMAC_PROPERTY LmacProp, + IN UINT64 ValueSize, + OUT UINT64 *Value + ); + +/// +/// Protocol structure +/// +struct _EFI_THUNDER_CONFIG_PROTOCOL { + EFI_THUNDER_CONFIG_PROTOCOL_GET_CONFIG GetConfig; + EFI_THUNDER_CONFIG_PROTOCOL_GET_BGX_PROP GetBgxProp; + EFI_THUNDER_CONFIG_PROTOCOL_GET_LMAC_PROP GetLmacProp; + VOID* BoardConfig; +}; + +#endif /* _THUNDERXCFG_H */ diff --git a/src/drivers/net/tlan.c b/src/drivers/net/tlan.c index 7742f6d80..0e85b35b6 100644 --- a/src/drivers/net/tlan.c +++ b/src/drivers/net/tlan.c @@ -808,6 +808,8 @@ static int tlan_probe ( struct nic *nic, struct pci_device *pci ) { } i++; } + if (chip_idx == -1) + return 0; priv->vendor_id = pci->vendor; priv->dev_id = pci->device; diff --git a/src/drivers/net/velocity.c b/src/drivers/net/velocity.c index 6d5185209..0a2a3ac10 100644 --- a/src/drivers/net/velocity.c +++ b/src/drivers/net/velocity.c @@ -100,13 +100,15 @@ static int velocity_autopoll_start ( struct velocity_nic *vlc ) { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret value Data read, or negative error */ -static int velocity_mii_read ( struct mii_interface *mii, unsigned int reg ) { +static int velocity_mii_read ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg ) { struct velocity_nic *vlc = - container_of ( mii, struct velocity_nic, mii ); + container_of ( mdio, struct velocity_nic, mdio ); int timeout = VELOCITY_TIMEOUT_US; int result; @@ -140,15 +142,17 @@ static int velocity_mii_read ( struct mii_interface *mii, unsigned int reg ) { /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ -static int velocity_mii_write ( struct mii_interface *mii, unsigned int reg, +static int velocity_mii_write ( struct mii_interface *mdio, + unsigned int phy __unused, unsigned int reg, unsigned int data) { struct velocity_nic *vlc = - container_of ( mii, struct velocity_nic, mii ); + container_of ( mdio, struct velocity_nic, mdio ); int timeout = VELOCITY_TIMEOUT_US; DBGC2 ( vlc, "VELOCITY %p MII write reg %d data 0x%04x\n", @@ -194,14 +198,14 @@ static void velocity_set_link ( struct velocity_nic *vlc ) { int tmp; /* Advertise 1000MBit */ - tmp = velocity_mii_read ( &vlc->mii, MII_CTRL1000 ); + tmp = mii_read ( &vlc->mii, MII_CTRL1000 ); tmp |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; - velocity_mii_write ( &vlc->mii, MII_CTRL1000, tmp ); + mii_write ( &vlc->mii, MII_CTRL1000, tmp ); /* Enable GBit operation in MII Control Register */ - tmp = velocity_mii_read ( &vlc->mii, MII_BMCR ); + tmp = mii_read ( &vlc->mii, MII_BMCR ); tmp |= BMCR_SPEED1000; - velocity_mii_write ( &vlc->mii, MII_BMCR, tmp ); + mii_write ( &vlc->mii, MII_BMCR, tmp ); } /****************************************************************************** @@ -747,7 +751,8 @@ static int velocity_probe ( struct pci_device *pci ) { netdev->hw_addr[5] = readb ( vlc->regs + VELOCITY_MAC5 ); /* Initialise and reset MII interface */ - mii_init ( &vlc->mii, &velocity_mii_operations ); + mdio_init ( &vlc->mdio, &velocity_mii_operations ); + mii_init ( &vlc->mii, &vlc->mdio, 0 ); if ( ( rc = mii_reset ( &vlc->mii ) ) != 0 ) { DBGC ( vlc, "VELOCITY %p could not reset MII: %s\n", vlc, strerror ( rc ) ); diff --git a/src/drivers/net/velocity.h b/src/drivers/net/velocity.h index 04e6a1460..84817d1bb 100644 --- a/src/drivers/net/velocity.h +++ b/src/drivers/net/velocity.h @@ -326,7 +326,9 @@ struct velocity_nic { /** Registers */ void *regs; /** MII interface */ - struct mii_interface mii; + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; /** Netdev */ struct net_device *netdev; diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 533ccb0c6..fe79a92c4 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -24,14 +24,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include #include #include #include -#include #include +#include #include "virtio-net.h" /* @@ -76,18 +77,19 @@ enum { QUEUE_NB }; -enum { - /** Max number of pending rx packets */ - NUM_RX_BUF = 8, - - /** Max Ethernet frame length, including FCS and VLAN tag */ - RX_BUF_SIZE = 1522, -}; +/** Max number of pending rx packets */ +#define NUM_RX_BUF 8 struct virtnet_nic { /** Base pio register address */ unsigned long ioaddr; + /** 0 for legacy, 1 for virtio 1.0 */ + int virtio_version; + + /** Virtio 1.0 device data */ + struct virtio_pci_modern_device vdev; + /** RX/TX virtqueues */ struct vring_virtqueue *virtqueue; @@ -97,8 +99,8 @@ struct virtnet_nic { /** Pending rx packet count */ unsigned int rx_num_iobufs; - /** Virtio net packet header, we only need one */ - struct virtio_net_hdr empty_header; + /** Virtio net dummy packet headers */ + struct virtio_net_hdr_modern empty_header[QUEUE_NB]; }; /** Add an iobuf to a virtqueue @@ -113,17 +115,25 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, int vq_idx, struct io_buffer *iobuf ) { struct virtnet_nic *virtnet = netdev->priv; struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx]; + struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx]; unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0; unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2; + size_t header_len = ( virtnet->virtio_version ? + sizeof ( *header ) : sizeof ( header->legacy ) ); struct vring_list list[] = { { /* Share a single zeroed virtio net header between all - * rx and tx packets. This works because this driver + * packets in a ring. This works because this driver * does not use any advanced features so none of the * header fields get used. + * + * Some host implementations (notably Google Compute + * Platform) are known to unconditionally write back + * to header->flags for received packets. Work around + * this by using separate RX and TX headers. */ - .addr = ( char* ) &virtnet->empty_header, - .length = sizeof ( virtnet->empty_header ), + .addr = ( char* ) header, + .length = header_len, }, { .addr = ( char* ) iobuf->data, @@ -135,7 +145,8 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, virtnet, iobuf, vq_idx ); vring_add_buf ( vq, list, out, in, iobuf, 0 ); - vring_kick ( virtnet->ioaddr, vq, 1 ); + vring_kick ( virtnet->virtio_version ? &virtnet->vdev : NULL, + virtnet->ioaddr, vq, 1 ); } /** Try to keep rx virtqueue filled with iobufs @@ -144,12 +155,13 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, */ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { struct virtnet_nic *virtnet = netdev->priv; + size_t len = ( netdev->max_pkt_len + 4 /* VLAN */ ); while ( virtnet->rx_num_iobufs < NUM_RX_BUF ) { struct io_buffer *iobuf; /* Try to allocate a buffer, stop for now if out of memory */ - iobuf = alloc_iob ( RX_BUF_SIZE ); + iobuf = alloc_iob ( len ); if ( ! iobuf ) break; @@ -157,19 +169,36 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { list_add ( &iobuf->list, &virtnet->rx_iobufs ); /* Mark packet length until we know the actual size */ - iob_put ( iobuf, RX_BUF_SIZE ); + iob_put ( iobuf, len ); virtnet_enqueue_iob ( netdev, RX_INDEX, iobuf ); virtnet->rx_num_iobufs++; } } -/** Open network device +/** Helper to free all virtqueue memory + * + * @v netdev Network device + */ +static void virtnet_free_virtqueues ( struct net_device *netdev ) { + struct virtnet_nic *virtnet = netdev->priv; + int i; + + for ( i = 0; i < QUEUE_NB; i++ ) { + virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification ); + vp_free_vq ( &virtnet->virtqueue[i] ); + } + + free ( virtnet->virtqueue ); + virtnet->virtqueue = NULL; +} + +/** Open network device, legacy virtio 0.9.5 * * @v netdev Network device * @ret rc Return status code */ -static int virtnet_open ( struct net_device *netdev ) { +static int virtnet_open_legacy ( struct net_device *netdev ) { struct virtnet_nic *virtnet = netdev->priv; unsigned long ioaddr = virtnet->ioaddr; u32 features; @@ -189,8 +218,7 @@ static int virtnet_open ( struct net_device *netdev ) { if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n", virtnet, i ); - free ( virtnet->virtqueue ); - virtnet->virtqueue = NULL; + virtnet_free_virtqueues ( netdev ); return -ENOENT; } } @@ -205,11 +233,88 @@ static int virtnet_open ( struct net_device *netdev ) { /* Driver is ready */ features = vp_get_features ( ioaddr ); - vp_set_features ( ioaddr, features & ( 1 << VIRTIO_NET_F_MAC ) ); + vp_set_features ( ioaddr, features & ( ( 1 << VIRTIO_NET_F_MAC ) | + ( 1 << VIRTIO_NET_F_MTU ) ) ); vp_set_status ( ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK ); return 0; } +/** Open network device, modern virtio 1.0 + * + * @v netdev Network device + * @ret rc Return status code + */ +static int virtnet_open_modern ( struct net_device *netdev ) { + struct virtnet_nic *virtnet = netdev->priv; + u64 features; + u8 status; + + /* Negotiate features */ + features = vpm_get_features ( &virtnet->vdev ); + if ( ! ( features & VIRTIO_F_VERSION_1 ) ) { + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -EINVAL; + } + vpm_set_features ( &virtnet->vdev, features & ( + ( 1ULL << VIRTIO_NET_F_MAC ) | + ( 1ULL << VIRTIO_NET_F_MTU ) | + ( 1ULL << VIRTIO_F_VERSION_1 ) | + ( 1ULL << VIRTIO_F_ANY_LAYOUT ) | + ( 1ULL << VIRTIO_F_IOMMU_PLATFORM ) ) ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FEATURES_OK ); + + status = vpm_get_status ( &virtnet->vdev ); + if ( ! ( status & VIRTIO_CONFIG_S_FEATURES_OK ) ) { + DBGC ( virtnet, "VIRTIO-NET %p device didn't accept features\n", + virtnet ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -EINVAL; + } + + /* Allocate virtqueues */ + virtnet->virtqueue = zalloc ( QUEUE_NB * + sizeof ( *virtnet->virtqueue ) ); + if ( ! virtnet->virtqueue ) { + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -ENOMEM; + } + + /* Initialize rx/tx virtqueues */ + if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) { + DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n", + virtnet ); + virtnet_free_virtqueues ( netdev ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED ); + return -ENOENT; + } + + /* Disable interrupts before starting */ + netdev_irq ( netdev, 0 ); + + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER_OK ); + + /* Initialize rx packets */ + INIT_LIST_HEAD ( &virtnet->rx_iobufs ); + virtnet->rx_num_iobufs = 0; + virtnet_refill_rx_virtqueue ( netdev ); + return 0; +} + +/** Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int virtnet_open ( struct net_device *netdev ) { + struct virtnet_nic *virtnet = netdev->priv; + + if ( virtnet->virtio_version ) { + return virtnet_open_modern ( netdev ); + } else { + return virtnet_open_legacy ( netdev ); + } +} + /** Close network device * * @v netdev Network device @@ -219,11 +324,14 @@ static void virtnet_close ( struct net_device *netdev ) { struct io_buffer *iobuf; struct io_buffer *next_iobuf; - vp_reset ( virtnet->ioaddr ); + if ( virtnet->virtio_version ) { + vpm_reset ( &virtnet->vdev ); + } else { + vp_reset ( virtnet->ioaddr ); + } /* Virtqueues can be freed now that NIC is reset */ - free ( virtnet->virtqueue ); - virtnet->virtqueue = NULL; + virtnet_free_virtqueues ( netdev ); /* Free rx iobufs */ list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) { @@ -280,7 +388,7 @@ static void virtnet_process_rx_packets ( struct net_device *netdev ) { virtnet->rx_num_iobufs--; /* Update iobuf length */ - iob_unput ( iobuf, RX_BUF_SIZE ); + iob_unput ( iobuf, iob_len ( iobuf ) ); iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) ); DBGC2 ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n", @@ -302,10 +410,14 @@ static void virtnet_poll ( struct net_device *netdev ) { /* Acknowledge interrupt. This is necessary for UNDI operation and * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being - * set (that flag is just a hint and the hypervisor not not have to + * set (that flag is just a hint and the hypervisor does not have to * honor it). */ - vp_get_isr ( virtnet->ioaddr ); + if ( virtnet->virtio_version ) { + vpm_get_isr ( &virtnet->vdev ); + } else { + vp_get_isr ( virtnet->ioaddr ); + } virtnet_process_tx_packets ( netdev ); virtnet_process_rx_packets ( netdev ); @@ -338,17 +450,17 @@ static struct net_device_operations virtnet_operations = { }; /** - * Probe PCI device + * Probe PCI device, legacy virtio 0.9.5 * * @v pci PCI device - * @v id PCI ID * @ret rc Return status code */ -static int virtnet_probe ( struct pci_device *pci ) { +static int virtnet_probe_legacy ( struct pci_device *pci ) { unsigned long ioaddr = pci->ioaddr; struct net_device *netdev; struct virtnet_nic *virtnet; u32 features; + u16 mtu; int rc; /* Allocate and hook up net device */ @@ -368,7 +480,7 @@ static int virtnet_probe ( struct pci_device *pci ) { adjust_pci_device ( pci ); vp_reset ( ioaddr ); - /* Load MAC address */ + /* Load MAC address and MTU */ features = vp_get_features ( ioaddr ); if ( features & ( 1 << VIRTIO_NET_F_MAC ) ) { vp_get ( ioaddr, offsetof ( struct virtio_net_config, mac ), @@ -376,6 +488,12 @@ static int virtnet_probe ( struct pci_device *pci ) { DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet, eth_ntoa ( netdev->hw_addr ) ); } + if ( features & ( 1ULL << VIRTIO_NET_F_MTU ) ) { + vp_get ( ioaddr, offsetof ( struct virtio_net_config, mtu ), + &mtu, sizeof ( mtu ) ); + DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet, mtu ); + netdev->max_pkt_len = ( mtu + ETH_HLEN ); + } /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) @@ -394,6 +512,152 @@ static int virtnet_probe ( struct pci_device *pci ) { return rc; } +/** + * Probe PCI device, modern virtio 1.0 + * + * @v pci PCI device + * @v found_dev Set to non-zero if modern device was found (probe may still fail) + * @ret rc Return status code + */ +static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { + struct net_device *netdev; + struct virtnet_nic *virtnet; + u64 features; + u16 mtu; + int rc, common, isr, notify, config, device; + + common = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_COMMON_CFG ); + if ( ! common ) { + DBG ( "Common virtio capability not found!\n" ); + return -ENODEV; + } + *found_dev = 1; + + isr = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_ISR_CFG ); + notify = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_NOTIFY_CFG ); + config = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_PCI_CFG ); + if ( ! isr || ! notify || ! config ) { + DBG ( "Missing virtio capabilities %i/%i/%i/%i\n", + common, isr, notify, config ); + return -EINVAL; + } + device = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_DEVICE_CFG ); + + /* Allocate and hook up net device */ + netdev = alloc_etherdev ( sizeof ( *virtnet ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &virtnet_operations ); + virtnet = netdev->priv; + + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + + DBGC ( virtnet, "VIRTIO-NET modern %p busaddr=%s irq=%d\n", + virtnet, pci->dev.name, pci->irq ); + + virtnet->vdev.pci = pci; + rc = virtio_pci_map_capability ( pci, common, + sizeof ( struct virtio_pci_common_cfg ), 4, + 0, sizeof ( struct virtio_pci_common_cfg ), + &virtnet->vdev.common ); + if ( rc ) + goto err_map_common; + + rc = virtio_pci_map_capability ( pci, isr, sizeof ( u8 ), 1, + 0, 1, + &virtnet->vdev.isr ); + if ( rc ) + goto err_map_isr; + + virtnet->vdev.notify_cap_pos = notify; + virtnet->vdev.cfg_cap_pos = config; + + /* Map the device capability */ + if ( device ) { + rc = virtio_pci_map_capability ( pci, device, + 0, 4, 0, sizeof ( struct virtio_net_config ), + &virtnet->vdev.device ); + if ( rc ) + goto err_map_device; + } + + /* Enable the PCI device */ + adjust_pci_device ( pci ); + + /* Reset the device and set initial status bits */ + vpm_reset ( &virtnet->vdev ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE ); + vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER ); + + /* Load MAC address and MTU */ + if ( device ) { + features = vpm_get_features ( &virtnet->vdev ); + if ( features & ( 1ULL << VIRTIO_NET_F_MAC ) ) { + vpm_get ( &virtnet->vdev, + offsetof ( struct virtio_net_config, mac ), + netdev->hw_addr, ETH_ALEN ); + DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet, + eth_ntoa ( netdev->hw_addr ) ); + } + if ( features & ( 1ULL << VIRTIO_NET_F_MTU ) ) { + vpm_get ( &virtnet->vdev, + offsetof ( struct virtio_net_config, mtu ), + &mtu, sizeof ( mtu ) ); + DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet, + mtu ); + netdev->max_pkt_len = ( mtu + ETH_HLEN ); + } + } + + /* We need a valid MAC address */ + if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) { + rc = -EADDRNOTAVAIL; + goto err_mac_address; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Mark link as up, control virtqueue is not used */ + netdev_link_up ( netdev ); + + virtnet->virtio_version = 1; + return 0; + + unregister_netdev ( netdev ); +err_register_netdev: +err_mac_address: + vpm_reset ( &virtnet->vdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + + virtio_pci_unmap_capability ( &virtnet->vdev.device ); +err_map_device: + virtio_pci_unmap_capability ( &virtnet->vdev.isr ); +err_map_isr: + virtio_pci_unmap_capability ( &virtnet->vdev.common ); +err_map_common: + return rc; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int virtnet_probe ( struct pci_device *pci ) { + int found_modern = 0; + int rc = virtnet_probe_modern ( pci, &found_modern ); + if ( ! found_modern && pci->device < 0x1040 ) { + /* fall back to the legacy probe */ + rc = virtnet_probe_legacy ( pci ); + } + return rc; +} + /** * Remove device * @@ -401,6 +665,11 @@ static int virtnet_probe ( struct pci_device *pci ) { */ static void virtnet_remove ( struct pci_device *pci ) { struct net_device *netdev = pci_get_drvdata ( pci ); + struct virtnet_nic *virtnet = netdev->priv; + + virtio_pci_unmap_capability ( &virtnet->vdev.device ); + virtio_pci_unmap_capability ( &virtnet->vdev.isr ); + virtio_pci_unmap_capability ( &virtnet->vdev.common ); unregister_netdev ( netdev ); netdev_nullify ( netdev ); @@ -409,6 +678,7 @@ static void virtnet_remove ( struct pci_device *pci ) { static struct pci_device_id virtnet_nics[] = { PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0), +PCI_ROM(0x1af4, 0x1041, "virtio-net", "Virtio Network Interface 1.0", 0), }; struct pci_driver virtnet_driver __pci_driver = { diff --git a/src/drivers/net/virtio-net.h b/src/drivers/net/virtio-net.h index 3abef28ea..ff58d3ef1 100644 --- a/src/drivers/net/virtio-net.h +++ b/src/drivers/net/virtio-net.h @@ -4,6 +4,7 @@ /* The feature bitmap for virtio net */ #define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ #define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MTU 3 /* Initial MTU advice */ #define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ #define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ #define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ @@ -14,11 +15,26 @@ #define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ #define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ #define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Driver can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* Configuration status field is available. */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel is available. */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support. */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering. */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Driver can send gratuitous packets. */ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ u8 mac[6]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + u16 status; + /* Maximum number of each of transmit and receive queues; + * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. + * Legal values are between 1 and 0x8000 + */ + u16 max_virtqueue_pairs; + /* Default maximum transmit unit advice */ + u16 mtu; } __attribute__((packed)); /* This is the first element of the scatter-gather list. If you don't @@ -41,4 +57,14 @@ struct virtio_net_hdr uint16_t csum_start; uint16_t csum_offset; }; + +/* Virtio 1.0 version of the first element of the scatter-gather list. */ +struct virtio_net_hdr_modern +{ + struct virtio_net_hdr legacy; + + /* Used only if VIRTIO_NET_F_MRG_RXBUF: */ + uint16_t num_buffers; +}; + #endif /* _VIRTIO_NET_H_ */ diff --git a/src/drivers/net/vxge/vxge_config.h b/src/drivers/net/vxge/vxge_config.h index bf25134ad..59e8a7966 100644 --- a/src/drivers/net/vxge/vxge_config.h +++ b/src/drivers/net/vxge/vxge_config.h @@ -27,10 +27,6 @@ FILE_LICENCE(GPL2_ONLY); #define WAIT_FACTOR 1 -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif - #define VXGE_HW_MAC_MAX_WIRE_PORTS 2 #define VXGE_HW_MAC_MAX_AGGR_PORTS 2 #define VXGE_HW_MAC_MAX_PORTS 3 diff --git a/src/drivers/net/vxge/vxge_traffic.c b/src/drivers/net/vxge/vxge_traffic.c index 0b1caf106..dbd799015 100644 --- a/src/drivers/net/vxge/vxge_traffic.c +++ b/src/drivers/net/vxge/vxge_traffic.c @@ -667,6 +667,8 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) vxge_debug(VXGE_INFO, "%s: rx frame received at offset %d\n", hldev->ndev->name, ring->rxd_offset); + iobuf = (struct io_buffer *)(intptr_t)rxd->host_control; + if (tcode != VXGE_HW_RING_T_CODE_OK) { netdev_rx_err(hldev->ndev, NULL, -EINVAL); vxge_debug(VXGE_ERR, "%s:%d, rx error tcode %d\n", @@ -675,8 +677,6 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) goto err1; } - iobuf = (struct io_buffer *)(intptr_t)rxd->host_control; - len = VXGE_HW_RING_RXD_1_BUFFER0_SIZE_GET(rxd->control_1); len -= ETH_FCS_LEN; diff --git a/src/drivers/net/w89c840.c b/src/drivers/net/w89c840.c index d8144a8ce..72ccf3a28 100644 --- a/src/drivers/net/w89c840.c +++ b/src/drivers/net/w89c840.c @@ -247,7 +247,7 @@ static struct winbond_private /* MII transceiver section. */ int mii_cnt; /* MII device addresses. */ u16 advertising; /* NWay media advertisement */ - unsigned char phys[2]; /* MII device addresses. */ + unsigned char phys[4]; /* MII device addresses. */ } w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES))); /* NIC specific static variables go here */ diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c index f90d6a91a..35cbc8de9 100644 --- a/src/drivers/usb/ehci.c +++ b/src/drivers/usb/ehci.c @@ -173,6 +173,61 @@ static int ehci_ctrl_reachable ( struct ehci_device *ehci, void *ptr ) { return -ENOTSUP; } +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Dump host controller registers + * + * @v ehci EHCI device + */ +static __unused void ehci_dump ( struct ehci_device *ehci ) { + uint8_t caplength; + uint16_t hciversion; + uint32_t hcsparams; + uint32_t hccparams; + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t configflag; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump capability registers */ + caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH ); + hciversion = readw ( ehci->cap + EHCI_CAP_HCIVERSION ); + hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS ); + hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS ); + DBGC ( ehci, "EHCI %s caplen %02x hciversion %04x hcsparams %08x " + "hccparams %08x\n", ehci->name, caplength, hciversion, + hcsparams, hccparams ); + + /* Dump operational registers */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + usbintr = readl ( ehci->op + EHCI_OP_USBINTR ); + frindex = readl ( ehci->op + EHCI_OP_FRINDEX ); + ctrldssegment = readl ( ehci->op + EHCI_OP_CTRLDSSEGMENT ); + periodiclistbase = readl ( ehci->op + EHCI_OP_PERIODICLISTBASE ); + asynclistaddr = readl ( ehci->op + EHCI_OP_ASYNCLISTADDR ); + configflag = readl ( ehci->op + EHCI_OP_CONFIGFLAG ); + DBGC ( ehci, "EHCI %s usbcmd %08x usbsts %08x usbint %08x frindx " + "%08x\n", ehci->name, usbcmd, usbsts, usbintr, frindex ); + DBGC ( ehci, "EHCI %s ctrlds %08x period %08x asyncl %08x cfgflg " + "%08x\n", ehci->name, ctrldssegment, periodiclistbase, + asynclistaddr, configflag ); +} + /****************************************************************************** * * USB legacy support @@ -233,6 +288,14 @@ static void ehci_legacy_claim ( struct ehci_device *ehci, if ( ! legacy ) return; + /* Dump original SMI usage */ + pci_read_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), + &ctlsts ); + if ( ctlsts ) { + DBGC ( ehci, "EHCI %s BIOS using SMIs: %08x\n", + ehci->name, ctlsts ); + } + /* Claim ownership */ pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), EHCI_USBLEGSUP_OS_OWNED ); @@ -276,9 +339,11 @@ static void ehci_legacy_claim ( struct ehci_device *ehci, */ static void ehci_legacy_release ( struct ehci_device *ehci, struct pci_device *pci ) { + unsigned int legacy = ehci->legacy; + uint32_t ctlsts; /* Do nothing unless legacy support capability is present */ - if ( ! ehci->legacy ) + if ( ! legacy ) return; /* Do nothing if releasing ownership is prevented */ @@ -289,8 +354,14 @@ static void ehci_legacy_release ( struct ehci_device *ehci, } /* Release ownership */ - pci_write_config_byte ( pci, ( ehci->legacy + EHCI_USBLEGSUP_OS ), 0 ); + pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), 0 ); DBGC ( ehci, "EHCI %s released ownership to BIOS\n", ehci->name ); + + /* Dump restored SMI usage */ + pci_read_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), + &ctlsts ); + DBGC ( ehci, "EHCI %s BIOS reclaimed SMIs: %08x\n", + ehci->name, ctlsts ); } /****************************************************************************** @@ -970,10 +1041,10 @@ static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { chr |= EHCI_CHR_TOGGLE; /* Determine endpoint speed */ - if ( usb->port->speed == USB_SPEED_HIGH ) { + if ( usb->speed == USB_SPEED_HIGH ) { chr |= EHCI_CHR_EPS_HIGH; } else { - if ( usb->port->speed == USB_SPEED_FULL ) { + if ( usb->speed == USB_SPEED_FULL ) { chr |= EHCI_CHR_EPS_FULL; } else { chr |= EHCI_CHR_EPS_LOW; diff --git a/src/drivers/usb/uhci.c b/src/drivers/usb/uhci.c index 4220bef72..528c1be1d 100644 --- a/src/drivers/usb/uhci.c +++ b/src/drivers/usb/uhci.c @@ -697,7 +697,7 @@ static int uhci_endpoint_open ( struct usb_endpoint *ep ) { goto err_ring_alloc; endpoint->ring.mtu = ep->mtu; endpoint->ring.flags = UHCI_FL_CERR_MAX; - if ( usb->port->speed < USB_SPEED_FULL ) + if ( usb->speed < USB_SPEED_FULL ) endpoint->ring.flags |= UHCI_FL_LS; endpoint->ring.control = ( UHCI_CONTROL_DEVICE ( usb->address ) | UHCI_CONTROL_ENDPOINT ( ep->address ) ); diff --git a/src/drivers/usb/usbhub.c b/src/drivers/usb/usbhub.c index 5cfc40520..47914bcdb 100644 --- a/src/drivers/usb/usbhub.c +++ b/src/drivers/usb/usbhub.c @@ -155,6 +155,10 @@ static int hub_open ( struct usb_hub *hub ) { /* Refill interrupt ring */ hub_refill ( hubdev ); + /* Delay to allow ports to stabilise on out-of-spec hubs */ + if ( hubdev->flags & USB_HUB_SLOW_START ) + mdelay ( USB_HUB_SLOW_START_DELAY_MS ); + return 0; usb_endpoint_close ( &hubdev->intr ); @@ -410,8 +414,9 @@ static int hub_probe ( struct usb_function *func, hubdev->usb = usb; hubdev->features = ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES ); + hubdev->flags = func->id->driver_data; usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations ); - usb_refill_init ( &hubdev->intr, 0, USB_HUB_INTR_FILL ); + usb_refill_init ( &hubdev->intr, 0, 0, USB_HUB_INTR_FILL ); process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL ); /* Locate hub interface descriptor */ @@ -496,9 +501,10 @@ static void hub_remove ( struct usb_function *func ) { unsigned int i; /* If hub has been unplugged, mark all ports as unplugged */ - if ( usb->port->speed == USB_SPEED_NONE ) { + if ( usb->port->disconnected ) { for ( i = 1 ; i <= hub->ports ; i++ ) { port = usb_port ( hub, i ); + port->disconnected = 1; port->speed = USB_SPEED_NONE; } } @@ -516,6 +522,12 @@ static void hub_remove ( struct usb_function *func ) { /** USB hub device IDs */ static struct usb_device_id hub_ids[] = { + { + .name = "avocent-hub", + .vendor = 0x0624, + .product = 0x0248, + .driver_data = USB_HUB_SLOW_START, + }, { .name = "hub", .vendor = USB_ANY_ID, diff --git a/src/drivers/usb/usbhub.h b/src/drivers/usb/usbhub.h index d7d8f9610..a5f123acc 100644 --- a/src/drivers/usb/usbhub.h +++ b/src/drivers/usb/usbhub.h @@ -257,6 +257,8 @@ struct usb_hub_device { struct usb_hub *hub; /** Features */ unsigned int features; + /** Flags */ + unsigned int flags; /** Interrupt endpoint */ struct usb_endpoint intr; @@ -264,6 +266,12 @@ struct usb_hub_device { struct process refill; }; +/** Hub requires additional settling delay */ +#define USB_HUB_SLOW_START 0x0001 + +/** Additional setting delay for out-of-spec hubs */ +#define USB_HUB_SLOW_START_DELAY_MS 500 + /** Interrupt ring fill level * * This is a policy decision. diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 512adfe2f..a8ab6ab76 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -53,13 +53,14 @@ static LIST_HEAD ( usb_keyboards ); * * @v keycode Keycode * @v modifiers Modifiers + * @v leds LED state * @ret key iPXE key * * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad * page. */ -static unsigned int usbkbd_map ( unsigned int keycode, - unsigned int modifiers ) { +static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, + unsigned int leds ) { unsigned int key; if ( keycode < USBKBD_KEY_A ) { @@ -70,7 +71,8 @@ static unsigned int usbkbd_map ( unsigned int keycode, key = ( keycode - USBKBD_KEY_A + 'a' ); if ( modifiers & USBKBD_CTRL ) { key -= ( 'a' - CTRL_A ); - } else if ( modifiers & USBKBD_SHIFT ) { + } else if ( ( modifiers & USBKBD_SHIFT ) || + ( leds & USBKBD_LED_CAPS_LOCK ) ) { key -= ( 'a' - 'A' ); } } else if ( keycode <= USBKBD_KEY_0 ) { @@ -100,7 +102,22 @@ static unsigned int usbkbd_map ( unsigned int keycode, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP }; - key = special[ keycode - USBKBD_KEY_CAPSLOCK ]; + key = special[ keycode - USBKBD_KEY_CAPS_LOCK ]; + } else if ( keycode <= USBKBD_KEY_PAD_ENTER ) { + /* Keypad (unaffected by Num Lock) */ + key = "\0/*-+\n" [ keycode - USBKBD_KEY_NUM_LOCK ]; + } else if ( keycode <= USBKBD_KEY_PAD_DOT ) { + /* Keypad (affected by Num Lock) */ + if ( leds & USBKBD_LED_NUM_LOCK ) { + key = "1234567890." [ keycode - USBKBD_KEY_PAD_1 ]; + } else { + static const uint16_t keypad[] = { + KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, 0, + KEY_RIGHT, KEY_HOME, KEY_UP, KEY_PPAGE, + KEY_IC, KEY_DC + }; + key = keypad[ keycode - USBKBD_KEY_PAD_1 ]; + }; } else { key = 0; } @@ -124,10 +141,25 @@ static unsigned int usbkbd_map ( unsigned int keycode, */ static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode, unsigned int modifiers ) { + unsigned int leds = 0; unsigned int key; + /* Check for LED-modifying keys */ + if ( keycode == USBKBD_KEY_CAPS_LOCK ) { + leds = USBKBD_LED_CAPS_LOCK; + } else if ( keycode == USBKBD_KEY_NUM_LOCK ) { + leds = USBKBD_LED_NUM_LOCK; + } + + /* Handle LED-modifying keys */ + if ( leds ) { + kbd->leds ^= leds; + kbd->leds_changed = 1; + return; + } + /* Map to iPXE key */ - key = usbkbd_map ( keycode, modifiers ); + key = usbkbd_map ( keycode, modifiers, kbd->leds ); /* Do nothing if this keycode has no corresponding iPXE key */ if ( ! key ) { @@ -333,6 +365,37 @@ static struct usb_endpoint_driver_operations usbkbd_operations = { .complete = usbkbd_complete, }; +/****************************************************************************** + * + * Keyboard LEDs + * + ****************************************************************************** + */ + +/** + * Set keyboard LEDs + * + * @v kbd USB keyboard + * @ret rc Return status code + */ +static int usbkbd_set_leds ( struct usb_keyboard *kbd ) { + struct usb_function *func = kbd->hid.func; + int rc; + + DBGC2 ( kbd, "KBD %s setting LEDs to %#02x\n", kbd->name, kbd->leds ); + + /* Set keyboard LEDs */ + if ( ( rc = usbhid_set_report ( func->usb, func->interface[0], + USBHID_REPORT_OUTPUT, 0, &kbd->leds, + sizeof ( kbd->leds ) ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not set LEDs to %#02x: %s\n", + kbd->name, kbd->leds, strerror ( rc ) ); + return rc; + } + + return 0; +} + /****************************************************************************** * * USB interface @@ -362,7 +425,7 @@ static int usbkbd_probe ( struct usb_function *func, kbd->name = func->name; kbd->bus = usb->port->hub->bus; usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL ); - usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ), + usb_refill_init ( &kbd->hid.in, 0, sizeof ( kbd->report ), USBKBD_INTR_MAX_FILL ); /* Describe USB human interface device */ @@ -400,6 +463,9 @@ static int usbkbd_probe ( struct usb_function *func, /* Add to list of USB keyboards */ list_add_tail ( &kbd->list, &usb_keyboards ); + /* Set initial LED state */ + usbkbd_set_leds ( kbd ); + usb_func_set_drvdata ( func, kbd ); return 0; @@ -484,10 +550,20 @@ static int usbkbd_iskey ( void ) { struct usb_keyboard *kbd; unsigned int fill; - /* Poll all USB keyboards and refill endpoints */ + /* Poll USB keyboards, refill endpoints, and set LEDs if applicable */ list_for_each_entry ( kbd, &usb_keyboards, list ) { + + /* Poll keyboard */ usb_poll ( kbd->bus ); + + /* Refill endpoints */ usb_refill ( &kbd->hid.in ); + + /* Update keyboard LEDs, if applicable */ + if ( kbd->leds_changed ) { + usbkbd_set_leds ( kbd ); + kbd->leds_changed = 0; + } } /* Check for a non-empty keyboard buffer */ diff --git a/src/drivers/usb/usbkbd.h b/src/drivers/usb/usbkbd.h index 7eab24e46..cedebfe71 100644 --- a/src/drivers/usb/usbkbd.h +++ b/src/drivers/usb/usbkbd.h @@ -68,8 +68,20 @@ enum usb_keycode { USBKBD_KEY_SPACE = 0x2c, USBKBD_KEY_MINUS = 0x2d, USBKBD_KEY_SLASH = 0x38, - USBKBD_KEY_CAPSLOCK = 0x39, + USBKBD_KEY_CAPS_LOCK = 0x39, + USBKBD_KEY_F1 = 0x3a, USBKBD_KEY_UP = 0x52, + USBKBD_KEY_NUM_LOCK = 0x53, + USBKBD_KEY_PAD_ENTER = 0x58, + USBKBD_KEY_PAD_1 = 0x59, + USBKBD_KEY_PAD_DOT = 0x63, +}; + +/** USB keyboard LEDs */ +enum usb_keyboard_led { + USBKBD_LED_NUM_LOCK = 0x01, + USBKBD_LED_CAPS_LOCK = 0x02, + USBKBD_LED_SCROLL_LOCK = 0x04, }; /** Keyboard idle duration (in 4ms units) @@ -120,6 +132,11 @@ struct usb_keyboard { /** Autorepeat hold-off time (in number of completions reported) */ unsigned int holdoff; + /** Keyboard LED state */ + uint8_t leds; + /** Keyboard LEDs changed */ + uint8_t leds_changed; + /** Keyboard buffer * * This stores iPXE key values. diff --git a/src/drivers/usb/usbnet.c b/src/drivers/usb/usbnet.c index d18d81772..0fac00b56 100644 --- a/src/drivers/usb/usbnet.c +++ b/src/drivers/usb/usbnet.c @@ -35,11 +35,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * USB network devices use a variety of packet formats and interface * descriptors, but tend to have several features in common: * - * - a single interrupt endpoint using the generic refill mechanism + * - a single bulk OUT endpoint * * - a single bulk IN endpoint using the generic refill mechanism * - * - a single bulk OUT endpoint + * - an optional interrupt endpoint using the generic refill mechanism * * - optional use of an alternate setting to enable the data interface * @@ -55,15 +55,17 @@ int usbnet_open ( struct usbnet_device *usbnet ) { struct usb_device *usb = usbnet->func->usb; int rc; - /* Open interrupt endpoint */ - if ( ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { + /* Open interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { DBGC ( usbnet, "USBNET %s could not open interrupt: %s\n", usbnet->func->name, strerror ( rc ) ); goto err_open_intr; } - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { + /* Refill interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { DBGC ( usbnet, "USBNET %s could not refill interrupt: %s\n", usbnet->func->name, strerror ( rc ) ); goto err_refill_intr; @@ -111,7 +113,8 @@ int usbnet_open ( struct usbnet_device *usbnet ) { usb_set_interface ( usb, usbnet->data, 0 ); err_set_interface: err_refill_intr: - usb_endpoint_close ( &usbnet->intr ); + if ( usbnet_has_intr ( usbnet ) ) + usb_endpoint_close ( &usbnet->intr ); err_open_intr: return rc; } @@ -134,8 +137,9 @@ void usbnet_close ( struct usbnet_device *usbnet ) { if ( usbnet->alternate ) usb_set_interface ( usb, usbnet->data, 0 ); - /* Close interrupt endpoint */ - usb_endpoint_close ( &usbnet->intr ); + /* Close interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) ) + usb_endpoint_close ( &usbnet->intr ); } /** @@ -151,9 +155,11 @@ int usbnet_refill ( struct usbnet_device *usbnet ) { if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) return rc; - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) + /* Refill interrupt endpoint, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { return rc; + } return 0; } @@ -272,9 +278,11 @@ int usbnet_describe ( struct usbnet_device *usbnet, struct usb_configuration_descriptor *config ) { int rc; - /* Describe communications interface */ - if ( ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) + /* Describe communications interface, if applicable */ + if ( usbnet_has_intr ( usbnet ) && + ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) { return rc; + } /* Describe data interface */ if ( ( rc = usbnet_data_describe ( usbnet, config ) ) != 0 ) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 110b43287..ecf8bf4d5 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -801,34 +801,41 @@ static int xhci_port_speed ( struct xhci_device *xhci, unsigned int port, ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); - /* Use the default mappings if applicable */ - if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { - switch ( psiv ) { - case XHCI_SPEED_LOW : return USB_SPEED_LOW; - case XHCI_SPEED_FULL : return USB_SPEED_FULL; - case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; - case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; - default: - DBGC ( xhci, "XHCI %s-%d non-standard PSI value %d\n", + /* Use protocol speed ID table unless device is known to be faulty */ + if ( ! ( xhci->quirks & XHCI_BAD_PSIV ) ) { + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + + XHCI_SUPPORTED_PSI ( i ) ); + if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + speed = USB_SPEED ( mantissa, exponent ); + return speed; + } + } + + /* Record device as faulty if no match is found */ + if ( psic != 0 ) { + DBGC ( xhci, "XHCI %s-%d spurious PSI value %d: " + "assuming PSI table is invalid\n", xhci->name, port, psiv ); - return -ENOTSUP; + xhci->quirks |= XHCI_BAD_PSIV; } } - /* Iterate over PSI dwords looking for a match */ - for ( i = 0 ; i < psic ; i++ ) { - psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); - if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { - mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); - exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); - speed = USB_SPEED ( mantissa, exponent ); - return speed; - } + /* Use the default mappings */ + switch ( psiv ) { + case XHCI_SPEED_LOW : return USB_SPEED_LOW; + case XHCI_SPEED_FULL : return USB_SPEED_FULL; + case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; + case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; + default: + DBGC ( xhci, "XHCI %s-%d unrecognised PSI value %d\n", + xhci->name, port, psiv ); + return -ENOTSUP; } - - DBGC ( xhci, "XHCI %s-%d spurious PSI value %d\n", - xhci->name, port, psiv ); - return -ENOENT; } /** @@ -1558,7 +1565,7 @@ static void xhci_transfer ( struct xhci_device *xhci, } /* Identify endpoint */ - if ( ( trb->endpoint > XHCI_CTX_END ) || + if ( ( trb->endpoint >= XHCI_CTX_END ) || ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) { DBGC ( xhci, "XHCI %s slot %d transfer event invalid epid " "%d:\n", xhci->name, slot->id, trb->endpoint ); @@ -1704,6 +1711,9 @@ static void xhci_event_poll ( struct xhci_device *xhci ) { ( event->cons >> shift ) ) & XHCI_TRB_C ) ) break; + /* Consume this TRB */ + event->cons++; + /* Handle TRB */ type = ( trb->common.type & XHCI_TRB_TYPE_MASK ); switch ( type ) { @@ -1726,14 +1736,11 @@ static void xhci_event_poll ( struct xhci_device *xhci ) { default: DBGC ( xhci, "XHCI %s unrecognised event %#x\n:", - xhci->name, event->cons ); + xhci->name, ( event->cons - 1 ) ); DBGC_HDA ( xhci, virt_to_phys ( trb ), trb, sizeof ( *trb ) ); break; } - - /* Consume this TRB */ - event->cons++; } /* Update dequeue pointer if applicable */ @@ -2606,6 +2613,12 @@ static int xhci_endpoint_stream ( struct usb_endpoint *ep, len -= trb_len; trb++; } + + /* Mark zero-length packet (if present) as a separate transfer */ + if ( zlp && ( count > 1 ) ) + trb[-2].normal.flags = 0; + + /* Generate completion for final TRB */ trb[-1].normal.flags = XHCI_TRB_IOC; /* Enqueue TRBs */ @@ -2753,7 +2766,6 @@ static void xhci_device_close ( struct usb_device *usb ) { static int xhci_device_address ( struct usb_device *usb ) { struct xhci_slot *slot = usb_get_hostdata ( usb ); struct xhci_device *xhci = slot->xhci; - struct usb_port *port = usb->port; struct usb_port *root_port; int psiv; int rc; @@ -2766,7 +2778,7 @@ static int xhci_device_address ( struct usb_device *usb ) { slot->port = root_port->address; /* Calculate protocol speed ID */ - psiv = xhci_port_psiv ( xhci, slot->port, port->speed ); + psiv = xhci_port_psiv ( xhci, slot->port, usb->speed ); if ( psiv < 0 ) { rc = psiv; return rc; diff --git a/src/hci/commands/cert_cmd.c b/src/hci/commands/cert_cmd.c new file mode 100644 index 000000000..24b18bf5c --- /dev/null +++ b/src/hci/commands/cert_cmd.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Certificate management commands + * + */ + +/** "cert" options */ +struct cert_options { + /** Certificate subject name */ + char *name; + /** Keep certificate file after parsing */ + int keep; +}; + +/** "cert" option list */ +static union { + /* "certstore" takes both options */ + struct option_descriptor certstore[2]; + /* "certstat" takes only --subject */ + struct option_descriptor certstat[1]; + /* "certfree" takes only --subject */ + struct option_descriptor certfree[1]; +} opts = { + .certstore = { + OPTION_DESC ( "subject", 's', required_argument, + struct cert_options, name, parse_string ), + OPTION_DESC ( "keep", 'k', no_argument, + struct cert_options, keep, parse_flag ), + }, +}; + +/** A "cert" command descriptor */ +struct cert_command_descriptor { + /** Command descriptor */ + struct command_descriptor cmd; + /** Payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ + int ( * payload ) ( struct x509_certificate *cert ); +}; + +/** + * Construct "cert" command descriptor + * + * @v _struct Options structure type + * @v _options Option descriptor array + * @v _min_args Minimum number of non-option arguments + * @v _max_args Maximum number of non-option arguments + * @v _usage Command usage + * @v _payload Payload method + * @ret _command Command descriptor + */ +#define CERT_COMMAND_DESC( _struct, _options, _min_args, _max_args, \ + _usage, _payload ) \ + { \ + .cmd = COMMAND_DESC ( _struct, _options, _min_args, \ + _max_args, _usage ), \ + .payload = _payload, \ + } + +/** + * Execute "cert" command + * + * @v argc Argument count + * @v argv Argument list + * @v certcmd Command descriptor + * @ret rc Return status code + */ +static int cert_exec ( int argc, char **argv, + struct cert_command_descriptor *certcmd ) { + struct command_descriptor *cmd = &certcmd->cmd; + struct cert_options opts; + struct image *image = NULL; + struct x509_certificate *cert; + struct x509_certificate *tmp; + unsigned int count = 0; + size_t offset = 0; + int next; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, cmd, &opts ) ) != 0 ) + goto err_parse; + + /* Acquire image, if applicable */ + if ( ( optind < argc ) && + ( ( rc = imgacquire ( argv[optind], 0, &image ) ) != 0 ) ) + goto err_acquire; + + /* Get first entry in certificate store */ + tmp = list_first_entry ( &certstore.links, struct x509_certificate, + store.list ); + + /* Iterate over certificates */ + while ( 1 ) { + + /* Get next certificate from image or store as applicable */ + if ( image ) { + + /* Get next certificate from image */ + if ( offset >= image->len ) + break; + next = image_x509 ( image, offset, &cert ); + if ( next < 0 ) { + rc = next; + printf ( "Could not parse certificate: %s\n", + strerror ( rc ) ); + goto err_x509; + } + offset = next; + + } else { + + /* Get next certificate from store */ + cert = tmp; + if ( ! cert ) + break; + tmp = list_next_entry ( tmp, &certstore.links, + store.list ); + x509_get ( cert ); + } + + /* Skip non-matching names, if a name was specified */ + if ( opts.name && ( x509_check_name ( cert, opts.name ) != 0 )){ + x509_put ( cert ); + continue; + } + + /* Execute payload */ + if ( ( rc = certcmd->payload ( cert ) ) != 0 ) { + x509_put ( cert ); + goto err_payload; + } + + /* Count number of certificates processed */ + count++; + + /* Drop reference to certificate */ + x509_put ( cert ); + } + + /* Fail if a name was specified and no matching certificates + * were found. + */ + if ( opts.name && ( count == 0 ) ) { + printf ( "\"%s\" : no such certificate\n", opts.name ); + rc = -ENOENT; + goto err_none; + } + + err_none: + err_payload: + err_x509: + if ( image && ( ! opts.keep ) ) + unregister_image ( image ); + err_acquire: + err_parse: + return rc; +} + +/** + * "certstat" payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ +static int certstat_payload ( struct x509_certificate *cert ) { + + certstat ( cert ); + return 0; +} + +/** "certstat" command descriptor */ +static struct cert_command_descriptor certstat_cmd = + CERT_COMMAND_DESC ( struct cert_options, opts.certstat, 0, 0, NULL, + certstat_payload ); + +/** + * The "certstat" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int certstat_exec ( int argc, char **argv ) { + + return cert_exec ( argc, argv, &certstat_cmd ); +} + +/** + * "certstore" payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ +static int certstore_payload ( struct x509_certificate *cert ) { + + /* Mark certificate as having been added explicitly */ + cert->flags |= X509_FL_EXPLICIT; + + return 0; +} + +/** "certstore" command descriptor */ +static struct cert_command_descriptor certstore_cmd = + CERT_COMMAND_DESC ( struct cert_options, opts.certstore, 0, 1, + "[]", certstore_payload ); + +/** + * The "certstore" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int certstore_exec ( int argc, char **argv ) { + + return cert_exec ( argc, argv, &certstore_cmd ); +} + +/** + * "certfree" payload + * + * @v cert X.509 certificate + * @ret rc Return status code + */ +static int certfree_payload ( struct x509_certificate *cert ) { + + /* Remove from certificate store */ + certstore_del ( cert ); + + return 0; +} + +/** "certfree" command descriptor */ +static struct cert_command_descriptor certfree_cmd = + CERT_COMMAND_DESC ( struct cert_options, opts.certfree, 0, 0, NULL, + certfree_payload ); + +/** + * The "certfree" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int certfree_exec ( int argc, char **argv ) { + + return cert_exec ( argc, argv, &certfree_cmd ); +} + +/** Certificate management commands */ +struct command certmgmt_commands[] __command = { + { + .name = "certstat", + .exec = certstat_exec, + }, + { + .name = "certstore", + .exec = certstore_exec, + }, + { + .name = "certfree", + .exec = certfree_exec, + }, +}; diff --git a/src/hci/commands/ibmgmt_cmd.c b/src/hci/commands/ibmgmt_cmd.c new file mode 100644 index 000000000..1154d749e --- /dev/null +++ b/src/hci/commands/ibmgmt_cmd.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband device management commands + * + */ + +/** "ibstat" options */ +struct ibstat_options {}; + +/** "ibstat" option list */ +static struct option_descriptor ibstat_opts[] = {}; + +/** "ibstat" command descriptor */ +static struct command_descriptor ibstat_cmd = + COMMAND_DESC ( struct ibstat_options, ibstat_opts, 0, 0, "" ); + +/** + * The "ibstat" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int ibstat_exec ( int argc, char **argv ) { + struct ibstat_options opts; + struct ib_device *ibdev; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &ibstat_cmd, &opts ) ) != 0 ) + return rc; + + /* Show all Infiniband devices */ + for_each_ibdev ( ibdev ) + ibstat ( ibdev ); + + return 0; +} + +/** Infiniband commands */ +struct command ibmgmt_commands[] __command = { + { + .name = "ibstat", + .exec = ibstat_exec, + }, +}; diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index f9d6b5b3e..b34378f93 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -172,12 +172,3 @@ struct command image_trust_commands[] __command = { .exec = imgverify_exec, }, }; - -/* Drag in objects via command list */ -REQUIRING_SYMBOL ( image_trust_commands ); - -/* Drag in objects typically required for signature verification */ -REQUIRE_OBJECT ( rsa ); -REQUIRE_OBJECT ( md5 ); -REQUIRE_OBJECT ( sha1 ); -REQUIRE_OBJECT ( sha256 ); diff --git a/src/hci/commands/lotest_cmd.c b/src/hci/commands/lotest_cmd.c index a989932d4..393b3c36e 100644 --- a/src/hci/commands/lotest_cmd.c +++ b/src/hci/commands/lotest_cmd.c @@ -43,12 +43,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct lotest_options { /** MTU */ unsigned int mtu; + /** Broadcast */ + int broadcast; }; /** "lotest" option list */ static struct option_descriptor lotest_opts[] = { OPTION_DESC ( "mtu", 'm', required_argument, struct lotest_options, mtu, parse_integer ), + OPTION_DESC ( "broadcast", 'b', no_argument, + struct lotest_options, broadcast, parse_flag ), }; /** "lotest" command descriptor */ @@ -86,7 +90,8 @@ static int lotest_exec ( int argc, char **argv ) { opts.mtu = ETH_MAX_MTU; /* Perform loopback test */ - if ( ( rc = loopback_test ( sender, receiver, opts.mtu ) ) != 0 ) { + if ( ( rc = loopback_test ( sender, receiver, opts.mtu, + opts.broadcast ) ) != 0 ) { printf ( "Test failed: %s\n", strerror ( rc ) ); return rc; } diff --git a/src/hci/commands/ntp_cmd.c b/src/hci/commands/ntp_cmd.c new file mode 100644 index 000000000..8f741a512 --- /dev/null +++ b/src/hci/commands/ntp_cmd.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * NTP commands + * + */ + +/** "ntp" options */ +struct ntp_options {}; + +/** "ntp" option list */ +static struct option_descriptor ntp_opts[] = {}; + +/** "ntp" command descriptor */ +static struct command_descriptor ntp_cmd = + COMMAND_DESC ( struct ntp_options, ntp_opts, 1, 1, "" ); + +/** + * "ntp" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int ntp_exec ( int argc, char **argv ) { + struct ntp_options opts; + const char *hostname; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &ntp_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse hostname */ + hostname = argv[optind]; + + /* Get time and date via NTP */ + if ( ( rc = ntp ( hostname ) ) != 0 ) { + printf ( "Could not get time and date: %s\n", strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** NTP command */ +struct command ntp_command __command = { + .name = "ntp", + .exec = ntp_exec, +}; diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 24ec8bc4e..3907276a0 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -47,12 +47,14 @@ struct sanboot_options { int no_describe; /** Keep SAN device */ int keep; + /** Filename */ + char *filename; }; /** "sanboot" option list */ static union { - /* "sanboot" takes all three options */ - struct option_descriptor sanboot[3]; + /* "sanboot" takes all four options */ + struct option_descriptor sanboot[4]; /* "sanhook" takes only --drive and --no-describe */ struct option_descriptor sanhook[2]; /* "sanunhook" takes only --drive */ @@ -65,18 +67,20 @@ static union { struct sanboot_options, no_describe, parse_flag ), OPTION_DESC ( "keep", 'k', no_argument, struct sanboot_options, keep, parse_flag ), + OPTION_DESC ( "filename", 'f', required_argument, + struct sanboot_options, filename, parse_string ), }, }; /** "sanhook" command descriptor */ static struct command_descriptor sanhook_cmd = - COMMAND_DESC ( struct sanboot_options, opts.sanhook, 1, 1, + COMMAND_DESC ( struct sanboot_options, opts.sanhook, 1, MAX_ARGUMENTS, "" ); /** "sanboot" command descriptor */ static struct command_descriptor sanboot_cmd = - COMMAND_DESC ( struct sanboot_options, opts.sanboot, 0, 1, + COMMAND_DESC ( struct sanboot_options, opts.sanboot, 0, MAX_ARGUMENTS, "[]" ); /** "sanunhook" command descriptor */ @@ -96,9 +100,10 @@ static int sanboot_core_exec ( int argc, char **argv, struct command_descriptor *cmd, int default_flags, int no_root_path_flags ) { struct sanboot_options opts; - const char *root_path; - struct uri *uri; + struct uri *uris[argc]; + int count; int flags; + int i; int rc; /* Initialise options */ @@ -109,17 +114,14 @@ static int sanboot_core_exec ( int argc, char **argv, if ( ( rc = reparse_options ( argc, argv, cmd, &opts ) ) != 0 ) goto err_parse_options; - /* Parse root path, if present */ - if ( argc > optind ) { - root_path = argv[optind]; - uri = parse_uri ( root_path ); - if ( ! uri ) { + /* Parse root paths, if present */ + count = ( argc - optind ); + for ( i = 0 ; i < count ; i++ ) { + uris[i] = parse_uri ( argv[ optind + i ] ); + if ( ! uris[i] ) { rc = -ENOMEM; goto err_parse_uri; } - } else { - root_path = NULL; - uri = NULL; } /* Construct flags */ @@ -128,16 +130,19 @@ static int sanboot_core_exec ( int argc, char **argv, flags |= URIBOOT_NO_SAN_DESCRIBE; if ( opts.keep ) flags |= URIBOOT_NO_SAN_UNHOOK; - if ( ! root_path ) + if ( ! count ) flags |= no_root_path_flags; /* Boot from root path */ - if ( ( rc = uriboot ( NULL, uri, opts.drive, flags ) ) != 0 ) + if ( ( rc = uriboot ( NULL, uris, count, opts.drive, opts.filename, + flags ) ) != 0 ) goto err_uriboot; err_uriboot: - uri_put ( uri ); + i = count; err_parse_uri: + for ( i-- ; i >= 0 ; i-- ) + uri_put ( uris[i] ); err_parse_options: return rc; } diff --git a/src/hci/commands/time_cmd.c b/src/hci/commands/time_cmd.c index d1dd49caf..08148bf38 100644 --- a/src/hci/commands/time_cmd.c +++ b/src/hci/commands/time_cmd.c @@ -68,7 +68,7 @@ static int time_exec ( int argc, char **argv ) { start = currticks(); rc = execv ( argv[1], argv + 1 ); elapsed = ( currticks() - start ); - decisecs = ( 10 * elapsed / ticks_per_sec() ); + decisecs = ( 10 * elapsed / TICKS_PER_SEC ); printf ( "%s: %d.%ds\n", argv[0], ( decisecs / 10 ), ( decisecs % 10 ) ); diff --git a/src/hci/mucurses/kb.c b/src/hci/mucurses/kb.c index 8face14d8..a4b6ea871 100644 --- a/src/hci/mucurses/kb.c +++ b/src/hci/mucurses/kb.c @@ -103,7 +103,9 @@ int wgetnstr ( WINDOW *win, char *str, int n ) { _wcursback( win ); wdelch( win ); } else { - if ( c >= KEY_MIN ) { + if ( c >= 32 && c <= 126 ) { + *(_str++) = c; n--; + } else { switch(c) { case KEY_LEFT : case KEY_BACKSPACE : @@ -118,9 +120,6 @@ int wgetnstr ( WINDOW *win, char *str, int n ) { break; } } - if ( c >= 32 && c <= 126 ) { - *(_str++) = c; n--; - } } } diff --git a/src/hci/mucurses/slk.c b/src/hci/mucurses/slk.c index 660eb65c0..2a57b1dee 100644 --- a/src/hci/mucurses/slk.c +++ b/src/hci/mucurses/slk.c @@ -81,9 +81,10 @@ static void _print_label ( struct _softlabel sl ) { assert ( slks->max_label_len <= SLK_MAX_LABEL_LEN ); space_ch = ' '; + memset ( str, 0, sizeof ( str ) ); // protect against gaps in the soft label keys array - if ( sl.label == NULL ) { + if ( ! sl.label[0] ) { memset( str, space_ch, (size_t)(slks->max_label_len) ); } else { /* we need to pad the label with varying amounts of leading @@ -268,8 +269,7 @@ int slk_init ( int fmt ) { slks->spaces[0] = 3; slks->spaces[1] = 7; break; default: - nblocks = 0; nmaj = 0; nmin = 0; - break; + return ERR; } // determine maximum label length and major space size @@ -358,7 +358,7 @@ int slk_set ( int labnum, const char *label, int fmt ) { return ERR; strncpy(slks->fkeys[labnum].label, label, - sizeof(slks->fkeys[labnum].label)); + (sizeof(slks->fkeys[labnum].label) - 1)); slks->fkeys[labnum].fmt = fmt; return OK; diff --git a/src/hci/mucurses/windows.c b/src/hci/mucurses/windows.c index 7f39bdea2..090fcfd2e 100644 --- a/src/hci/mucurses/windows.c +++ b/src/hci/mucurses/windows.c @@ -18,9 +18,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret rc return status code */ int delwin ( WINDOW *win ) { - if ( win == NULL ) - return ERR; - /* I think we should blank the region covered by the window - ncurses doesn't do this, but they have a buffer, so they may just be deleting from an offscreen context whereas we @@ -51,13 +48,11 @@ int delwin ( WINDOW *win ) { WINDOW *derwin ( WINDOW *parent, int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *child; - if ( parent == NULL ) - return NULL; - if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) - return NULL; if ( ( (unsigned)ncols > parent->width ) || ( (unsigned)nlines > parent->height ) ) return NULL; + if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) + return NULL; child->ori_y = parent->ori_y + begin_y; child->ori_x = parent->ori_x + begin_x; child->height = nlines; @@ -75,8 +70,6 @@ WINDOW *derwin ( WINDOW *parent, int nlines, int ncols, */ WINDOW *dupwin ( WINDOW *orig ) { WINDOW *copy; - if ( orig == NULL ) - return NULL; if ( ( copy = malloc( sizeof( WINDOW ) ) ) == NULL ) return NULL; copy->scr = orig->scr; @@ -99,8 +92,6 @@ WINDOW *dupwin ( WINDOW *orig ) { * @ret rc return status code */ int mvwin ( WINDOW *win, int y, int x ) { - if ( win == NULL ) - return ERR; if ( ( ( (unsigned)y + win->height ) > LINES ) || ( ( (unsigned)x + win->width ) > COLS ) ) return ERR; @@ -122,11 +113,11 @@ int mvwin ( WINDOW *win, int y, int x ) { */ WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *win; - if ( ( win = malloc( sizeof(WINDOW) ) ) == NULL ) - return NULL; if ( ( (unsigned)( begin_y + nlines ) > stdscr->height ) && ( (unsigned)( begin_x + ncols ) > stdscr->width ) ) return NULL; + if ( ( win = malloc( sizeof(WINDOW) ) ) == NULL ) + return NULL; win->ori_y = begin_y; win->ori_x = begin_x; win->height = nlines; @@ -149,10 +140,6 @@ WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *subwin ( WINDOW *parent, int nlines, int ncols, int begin_y, int begin_x ) { WINDOW *child; - if ( parent == NULL ) - return NULL; - if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) - return NULL; child = newwin( nlines, ncols, begin_y, begin_x ); child->parent = parent; child->scr = parent->scr; diff --git a/src/image/der.c b/src/image/der.c new file mode 100644 index 000000000..fa17e5659 --- /dev/null +++ b/src/image/der.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DER-encoded ASN.1 data + * + */ + +/** + * Extract ASN.1 object from image + * + * @v image DER image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int der_asn1 ( struct image *image, size_t offset __unused, + struct asn1_cursor **cursor ) { + void *data; + + /* Allocate cursor and data buffer */ + *cursor = malloc ( sizeof ( **cursor ) + image->len ); + if ( ! *cursor ) + return -ENOMEM; + data = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); + + /* Populate cursor and data buffer */ + (*cursor)->data = data; + (*cursor)->len = image->len; + copy_from_user ( data, image->data, 0, image->len ); + + return image->len; +} + +/** + * Probe DER image + * + * @v image DER image + * @ret rc Return status code + */ +static int der_probe ( struct image *image ) { + struct asn1_cursor cursor; + uint8_t buf[8]; + size_t extra; + size_t total; + int len; + int rc; + + /* Sanity check: no realistic DER image can be smaller than this */ + if ( image->len < sizeof ( buf ) ) + return -ENOEXEC; + + /* Prepare partial cursor */ + cursor.data = buf; + cursor.len = sizeof ( buf ); + copy_from_user ( buf, image->data, 0, sizeof ( buf ) ); + extra = ( image->len - sizeof ( buf ) ); + + /* Get length of ASN.1 sequence */ + len = asn1_start ( &cursor, ASN1_SEQUENCE, extra ); + if ( len < 0 ) { + rc = len; + DBGC ( image, "DER %s is not valid ASN.1: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + /* Add length of tag and length bytes consumed by asn1_start() */ + total = ( len + ( cursor.data - ( ( void * ) buf ) ) ); + assert ( total <= image->len ); + + /* Check that image comprises a single well-formed ASN.1 object */ + if ( total != image->len ) { + DBGC ( image, "DER %s is not single ASN.1\n", image->name ); + return -ENOEXEC; + } + + return 0; +} + +/** DER image type */ +struct image_type der_image_type __image_type ( PROBE_NORMAL ) = { + .name = "DER", + .probe = der_probe, + .asn1 = der_asn1, +}; diff --git a/src/image/embedded.c b/src/image/embedded.c index 48dd86851..376e5d299 100644 --- a/src/image/embedded.c +++ b/src/image/embedded.c @@ -18,7 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EMBED( _index, _path, _name ) \ extern char embedded_image_ ## _index ## _data[]; \ extern char embedded_image_ ## _index ## _len[]; \ - __asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" \ + __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \ "\nembedded_image_" #_index "_data:\n\t" \ ".incbin \"" _path "\"\n\t" \ "\nembedded_image_" #_index "_end:\n\t" \ diff --git a/src/image/pem.c b/src/image/pem.c new file mode 100644 index 000000000..2dcc36442 --- /dev/null +++ b/src/image/pem.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * PEM-encoded ASN.1 data + * + */ + +/** + * Locate next line + * + * @v data PEM data + * @v len Length of PEM data + * @v offset Starting offset + * @ret next Offset to next line + */ +static size_t pem_next ( userptr_t data, size_t len, size_t offset ) { + off_t eol; + + /* Find and skip next newline character, if any */ + eol = memchr_user ( data, offset, '\n', ( len - offset ) ); + if ( eol < 0 ) + return len; + return ( eol + 1 ); +} + +/** + * Locate boundary marker line + * + * @v data PEM data + * @v len Length of PEM data + * @v offset Starting offset + * @v marker Boundary marker + * @ret offset Offset to boundary marker line, or negative error + */ +static int pem_marker ( userptr_t data, size_t len, size_t offset, + const char *marker ) { + char buf[ strlen ( marker ) ]; + + /* Sanity check */ + assert ( offset <= len ); + + /* Scan for marker at start of line */ + while ( offset < len ) { + + /* Check for marker */ + if ( ( len - offset ) < sizeof ( buf ) ) + break; + copy_from_user ( buf, data, offset, sizeof ( buf ) ); + if ( memcmp ( buf, marker, sizeof ( buf ) ) == 0 ) + return offset; + + /* Move to next line */ + offset = pem_next ( data, len, offset ); + assert ( offset <= len ); + } + + return -ENOENT; +} + +/** + * Extract ASN.1 object from PEM data + * + * @v data PEM data + * @v len Length of PEM data + * @v offset Offset within data + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next object, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +int pem_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ) { + size_t encoded_len; + size_t decoded_max_len; + char *encoded; + void *decoded; + int decoded_len; + int begin; + int end; + int rc; + + /* Locate and skip BEGIN marker */ + begin = pem_marker ( data, len, offset, PEM_BEGIN ); + if ( begin < 0 ) { + rc = begin; + DBGC ( data, "PEM [%#zx,%#zx) missing BEGIN marker: %s\n", + offset, len, strerror ( rc ) ); + goto err_begin; + } + begin = pem_next ( data, len, begin ); + + /* Locate and skip END marker */ + end = pem_marker ( data, len, begin, PEM_END ); + if ( end < 0 ) { + rc = end; + DBGC ( data, "PEM [%#zx,%#zx) missing END marker: %s\n", + offset, len, strerror ( rc ) ); + goto err_end; + } + encoded_len = ( end - begin ); + end = pem_next ( data, len, end ); + + /* Extract Base64-encoded data */ + encoded = malloc ( encoded_len + 1 /* NUL */ ); + if ( ! encoded ) { + rc = -ENOMEM; + goto err_alloc_encoded; + } + copy_from_user ( encoded, data, begin, encoded_len ); + encoded[encoded_len] = '\0'; + + /* Allocate cursor and data buffer */ + decoded_max_len = base64_decoded_max_len ( encoded ); + *cursor = malloc ( sizeof ( **cursor ) + decoded_max_len ); + if ( ! *cursor ) { + rc = -ENOMEM; + goto err_alloc_cursor; + } + decoded = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); + + /* Decode Base64-encoded data */ + decoded_len = base64_decode ( encoded, decoded, decoded_max_len ); + if ( decoded_len < 0 ) { + rc = decoded_len; + DBGC ( data, "PEM could not decode: %s\n", strerror ( rc ) ); + goto err_decode; + } + (*cursor)->data = decoded; + (*cursor)->len = decoded_len; + assert ( (*cursor)->len <= decoded_max_len ); + + /* Free Base64-encoded data */ + free ( encoded ); + + /* Update offset and skip any unencapsulated trailer */ + offset = end; + if ( pem_marker ( data, len, offset, PEM_BEGIN ) < 0 ) + offset = len; + + return offset; + + err_decode: + free ( *cursor ); + *cursor = NULL; + err_alloc_cursor: + free ( encoded ); + err_alloc_encoded: + err_end: + err_begin: + return rc; +} + +/** + * Probe PEM image + * + * @v image PEM image + * @ret rc Return status code + */ +static int pem_image_probe ( struct image *image ) { + int offset; + int rc; + + /* Check that image contains a BEGIN marker */ + if ( ( offset = pem_marker ( image->data, image->len, 0, + PEM_BEGIN ) ) < 0 ) { + rc = offset; + DBGC ( image, "PEM %s has no BEGIN marker: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Extract ASN.1 object from image + * + * @v image PEM image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on the + * allocated ASN.1 cursor. + */ +static int pem_image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ) { + int next; + int rc; + + /* Extract ASN.1 object */ + if ( ( next = pem_asn1 ( image->data, image->len, offset, + cursor ) ) < 0 ) { + rc = next; + DBGC ( image, "PEM %s could not extract ASN.1: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return next; +} + +/** PEM image type */ +struct image_type pem_image_type __image_type ( PROBE_NORMAL ) = { + .name = "PEM", + .probe = pem_image_probe, + .asn1 = pem_image_asn1, +}; diff --git a/src/include/assert.h b/src/include/assert.h index 07f3ecb8c..dd71fa713 100644 --- a/src/include/assert.h +++ b/src/include/assert.h @@ -12,11 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#ifndef ASSERTING #ifdef NDEBUG #define ASSERTING 0 #else #define ASSERTING 1 #endif +#endif extern unsigned int assertion_failures; diff --git a/src/include/compiler.h b/src/include/compiler.h index ca82f9523..a936425de 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -52,6 +52,17 @@ /** Stringify expanded argument */ #define _S2( x ) _S1 ( x ) +/* Assembler section types */ +#ifdef ASSEMBLY +#define PROGBITS _C2 ( ASM_TCHAR, progbits ) +#define NOBITS _C2 ( ASM_TCHAR, nobits ) +#else +#define PROGBITS_OPS _S2 ( ASM_TCHAR_OPS ) "progbits" +#define PROGBITS _S2 ( ASM_TCHAR ) "progbits" +#define NOBITS_OPS _S2 ( ASM_TCHAR_OPS ) "nobits" +#define NOBITS _S2 ( ASM_TCHAR ) "nobits" +#endif + /** * @defgroup symmacros Macros to provide or require explicit symbols * @{ @@ -64,7 +75,7 @@ */ #ifdef ASSEMBLY #define PROVIDE_SYMBOL( symbol ) \ - .section ".provided", "a", @nobits ; \ + .section ".provided", "a", NOBITS ; \ .hidden symbol ; \ .globl symbol ; \ symbol: ; \ @@ -139,14 +150,14 @@ */ #ifdef ASSEMBLY #define PROVIDE_REQUIRING_SYMBOL() \ - .section ".tbl.requiring_symbols", "a", @progbits ; \ + .section ".tbl.requiring_symbols", "a", PROGBITS ; \ __requiring_symbol__: .byte 0 ; \ .size __requiring_symbol__, . - __requiring_symbol__ ; \ .previous #else #define PROVIDE_REQUIRING_SYMBOL() \ __asm__ ( ".section \".tbl.requiring_symbols\", " \ - " \"a\", @progbits\n" \ + " \"a\", " PROGBITS "\n" \ "__requiring_symbol__:\t.byte 0\n" \ ".size __requiring_symbol__, " \ " . - __requiring_symbol__\n" \ @@ -259,6 +270,10 @@ PROVIDE_SYMBOL ( OBJECT_SYMBOL ); #define DBGLVL_MAX 0 #endif +#ifndef DBGLVL_DFLT +#define DBGLVL_DFLT DBGLVL_MAX +#endif + #ifndef ASSEMBLY /** printf() for debugging */ @@ -274,14 +289,23 @@ extern void dbg_pause ( void ); extern void dbg_more ( void ); /* Allow for selective disabling of enabled debug levels */ +#define __debug_disable( object ) _C2 ( __debug_disable_, object ) +char __debug_disable(OBJECT) = ( DBGLVL_MAX & ~DBGLVL_DFLT ); +#define DBG_DISABLE_OBJECT( object, level ) do { \ + extern char __debug_disable(object); \ + __debug_disable(object) |= (level); \ + } while ( 0 ) +#define DBG_ENABLE_OBJECT( object, level ) do { \ + extern char __debug_disable(object); \ + __debug_disable(object) &= ~(level); \ + } while ( 0 ) #if DBGLVL_MAX -int __debug_disable; -#define DBGLVL ( DBGLVL_MAX & ~__debug_disable ) +#define DBGLVL ( DBGLVL_MAX & ~__debug_disable(OBJECT) ) #define DBG_DISABLE( level ) do { \ - __debug_disable |= (level); \ + __debug_disable(OBJECT) |= ( (level) & DBGLVL_MAX ); \ } while ( 0 ) #define DBG_ENABLE( level ) do { \ - __debug_disable &= ~(level); \ + __debug_disable(OBJECT) &= ~( (level) & DBGLVL_MAX ); \ } while ( 0 ) #else #define DBGLVL 0 @@ -379,7 +403,7 @@ int __debug_disable; * * @v level Debug level */ -#define DBG_PAUSE_IF( level ) do { \ +#define DBG_PAUSE_IF( level, ... ) do { \ if ( DBG_ ## level ) { \ dbg_pause(); \ } \ @@ -390,7 +414,7 @@ int __debug_disable; * * @v level Debug level */ -#define DBG_MORE_IF( level ) do { \ +#define DBG_MORE_IF( level, ... ) do { \ if ( DBG_ ## level ) { \ dbg_more(); \ } \ @@ -631,6 +655,13 @@ int __debug_disable; #define barrier() __asm__ __volatile__ ( "" : : : "memory" ) #endif /* ASSEMBLY */ +/** + * Array size + */ +#ifndef ASSEMBLY +#define ARRAY_SIZE(array) ( sizeof (array) / sizeof ( (array)[0] ) ) +#endif /* ASSEMBLY */ + /** * @defgroup licences Licence declarations * diff --git a/src/include/curses.h b/src/include/curses.h index 04060fe27..cf8cc53c9 100644 --- a/src/include/curses.h +++ b/src/include/curses.h @@ -2,6 +2,7 @@ #define CURSES_H #include +#include #include #include @@ -25,7 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef TRUE #define TRUE (1) -typedef int bool; typedef uint32_t chtype; typedef uint32_t attr_t; @@ -443,7 +443,8 @@ extern int wborder ( WINDOW *, chtype, chtype, chtype, chtype, chtype, chtype, extern int wclrtobot ( WINDOW * ) __nonnull; extern int wclrtoeol ( WINDOW * ) __nonnull; extern void wcursyncup ( WINDOW * ); -extern int wcolour_set ( WINDOW *, short, void * ) __nonnull; +extern int wcolour_set ( WINDOW *, short, void * ) + __attribute__ (( nonnull (1))); #define wcolor_set(w,s,v) wcolour_set((w),(s),(v)) extern int wdelch ( WINDOW * ) __nonnull; extern int wdeleteln ( WINDOW * ) __nonnull; diff --git a/src/include/errno.h b/src/include/errno.h index 036479aff..e80bf9ca5 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -258,14 +258,14 @@ static inline void eplatform_discard ( int dummy __unused, ... ) {} * @ret error Error */ #define __einfo_error( einfo ) ( { \ - __asm__ ( ".section \".einfo\", \"\", @progbits\n\t" \ + __asm__ ( ".section \".einfo\", \"\", " PROGBITS_OPS "\n\t" \ ".align 8\n\t" \ "\n1:\n\t" \ ".long ( 4f - 1b )\n\t" \ - ".long %c0\n\t" \ + ".long %a0\n\t" \ ".long ( 2f - 1b )\n\t" \ ".long ( 3f - 1b )\n\t" \ - ".long %c1\n\t" \ + ".long %a1\n\t" \ "\n2:\t.asciz \"" __einfo_desc ( einfo ) "\"\n\t" \ "\n3:\t.asciz \"" __FILE__ "\"\n\t" \ ".align 8\n\t" \ diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index 2ccd691ed..78f402530 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -10,7 +10,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include +#include #include +#include +#include +#include +#include /** * An ACPI description header @@ -18,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * This is the structure common to the start of all ACPI system * description tables. */ -struct acpi_description_header { +struct acpi_header { /** ACPI signature (4 ASCII characters) */ uint32_t signature; /** Length of table, in bytes, including header */ @@ -39,6 +46,22 @@ struct acpi_description_header { uint32_t asl_compiler_revision; } __attribute__ (( packed )); +/** + * Transcribe ACPI table signature (for debugging) + * + * @v signature ACPI table signature + * @ret name ACPI table signature name + */ +static inline const char * acpi_name ( uint32_t signature ) { + static union { + uint32_t signature; + char name[5]; + } u; + + u.signature = cpu_to_le32 ( signature ); + return u.name; +} + /** * Build ACPI signature * @@ -51,13 +74,176 @@ struct acpi_description_header { #define ACPI_SIGNATURE( a, b, c, d ) \ ( ( (a) << 0 ) | ( (b) << 8 ) | ( (c) << 16 ) | ( (d) << 24 ) ) -extern int acpi_describe ( struct interface *interface, - struct acpi_description_header *acpi, size_t len ); -#define acpi_describe_TYPE( object_type ) \ - typeof ( int ( object_type, \ - struct acpi_description_header *acpi, \ - size_t len ) ) +/** Root System Description Pointer signature */ +#define RSDP_SIGNATURE { 'R', 'S', 'D', ' ', 'P', 'T', 'R', ' ' } -extern void acpi_fix_checksum ( struct acpi_description_header *acpi ); +/** Root System Description Pointer */ +struct acpi_rsdp { + /** Signature */ + char signature[8]; + /** To make sum of entire table == 0 */ + uint8_t checksum; + /** OEM identification */ + char oem_id[6]; + /** Revision */ + uint8_t revision; + /** Physical address of RSDT */ + uint32_t rsdt; +} __attribute__ (( packed )); + +/** Root System Description Table (RSDT) signature */ +#define RSDT_SIGNATURE ACPI_SIGNATURE ( 'R', 'S', 'D', 'T' ) + +/** ACPI Root System Description Table (RSDT) */ +struct acpi_rsdt { + /** ACPI header */ + struct acpi_header acpi; + /** ACPI table entries */ + uint32_t entry[0]; +} __attribute__ (( packed )); + +/** Fixed ACPI Description Table (FADT) signature */ +#define FADT_SIGNATURE ACPI_SIGNATURE ( 'F', 'A', 'C', 'P' ) + +/** Fixed ACPI Description Table (FADT) */ +struct acpi_fadt { + /** ACPI header */ + struct acpi_header acpi; + /** Physical address of FACS */ + uint32_t facs; + /** Physical address of DSDT */ + uint32_t dsdt; + /** Unused by iPXE */ + uint8_t unused[20]; + /** PM1a Control Register Block */ + uint32_t pm1a_cnt_blk; + /** PM1b Control Register Block */ + uint32_t pm1b_cnt_blk; + /** PM2 Control Register Block */ + uint32_t pm2_cnt_blk; + /** PM Timer Control Register Block */ + uint32_t pm_tmr_blk; +} __attribute__ (( packed )); + +/** ACPI PM1 Control Register (within PM1a_CNT_BLK or PM1A_CNT_BLK) */ +#define ACPI_PM1_CNT 0 +#define ACPI_PM1_CNT_SLP_TYP(x) ( (x) << 10 ) /**< Sleep type */ +#define ACPI_PM1_CNT_SLP_EN ( 1 << 13 ) /**< Sleep enable */ + +/** ACPI PM Timer Register (within PM_TMR_BLK) */ +#define ACPI_PM_TMR 0 + +/** Differentiated System Description Table (DSDT) signature */ +#define DSDT_SIGNATURE ACPI_SIGNATURE ( 'D', 'S', 'D', 'T' ) + +/** Secondary System Description Table (SSDT) signature */ +#define SSDT_SIGNATURE ACPI_SIGNATURE ( 'S', 'S', 'D', 'T' ) + +/** An ACPI descriptor (used to construct ACPI tables) */ +struct acpi_descriptor { + /** Reference count of containing object */ + struct refcnt *refcnt; + /** Table model */ + struct acpi_model *model; + /** List of ACPI descriptors for this model */ + struct list_head list; +}; + +/** + * Initialise ACPI descriptor + * + * @v desc ACPI descriptor + * @v model Table model + * @v refcnt Reference count + */ +static inline __attribute__ (( always_inline )) void +acpi_init ( struct acpi_descriptor *desc, struct acpi_model *model, + struct refcnt *refcnt ) { + + desc->refcnt = refcnt; + desc->model = model; + INIT_LIST_HEAD ( &desc->list ); +} + +/** An ACPI table model */ +struct acpi_model { + /** List of descriptors */ + struct list_head descs; + /** + * Check if ACPI descriptor is complete + * + * @v desc ACPI descriptor + * @ret rc Return status code + */ + int ( * complete ) ( struct acpi_descriptor *desc ); + /** + * Install ACPI tables + * + * @v install Installation method + * @ret rc Return status code + */ + int ( * install ) ( int ( * install ) ( struct acpi_header *acpi ) ); +}; + +/** ACPI models */ +#define ACPI_MODELS __table ( struct acpi_model, "acpi_models" ) + +/** Declare an ACPI model */ +#define __acpi_model __table_entry ( ACPI_MODELS, 01 ) + +/** + * Calculate static inline ACPI API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define ACPI_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( ACPI_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an ACPI API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_ACPI( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( ACPI_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline ACPI API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_ACPI_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( ACPI_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent ACPI API headers */ +#include +#include + +/* Include all architecture-dependent ACPI API headers */ +#include + +/** + * Locate ACPI root system description table + * + * @ret rsdt ACPI root system description table, or UNULL + */ +userptr_t acpi_find_rsdt ( void ); + +extern struct acpi_descriptor * +acpi_describe ( struct interface *interface ); +#define acpi_describe_TYPE( object_type ) \ + typeof ( struct acpi_descriptor * ( object_type ) ) + +extern void acpi_fix_checksum ( struct acpi_header *acpi ); +extern userptr_t acpi_find ( uint32_t signature, unsigned int index ); +extern int acpi_sx ( uint32_t signature ); +extern void acpi_add ( struct acpi_descriptor *desc ); +extern void acpi_del ( struct acpi_descriptor *desc ); +extern int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ); #endif /* _IPXE_ACPI_H */ diff --git a/src/include/ipxe/aoe.h b/src/include/ipxe/aoe.h index 0c656e7c2..a51044d15 100644 --- a/src/include/ipxe/aoe.h +++ b/src/include/ipxe/aoe.h @@ -117,7 +117,7 @@ struct aoehdr { */ struct abft_table { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** AoE shelf */ uint16_t shelf; /** AoE slot */ diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 5fbd58281..24caecdc5 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -9,7 +9,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include +#include #include #include @@ -159,6 +161,12 @@ struct asn1_builder_header { ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 14 ) +/** ASN.1 OID for id-md4 (1.2.840.113549.2.4) */ +#define ASN1_OID_MD4 \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 2 ), \ + ASN1_OID_SINGLE ( 4 ) + /** ASN.1 OID for id-md5 (1.2.840.113549.2.5) */ #define ASN1_OID_MD5 \ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ @@ -314,18 +322,53 @@ struct asn1_bit_string { unsigned int unused; } __attribute__ (( packed )); +/** + * Invalidate ASN.1 object cursor + * + * @v cursor ASN.1 object cursor + */ +static inline __attribute__ (( always_inline )) void +asn1_invalidate_cursor ( struct asn1_cursor *cursor ) { + cursor->len = 0; +} + /** * Extract ASN.1 type * * @v cursor ASN.1 object cursor - * @ret type Type + * @ret type Type, or ASN1_END if cursor is invalid */ static inline __attribute__ (( always_inline )) unsigned int asn1_type ( const struct asn1_cursor *cursor ) { - return ( *( ( const uint8_t * ) cursor->data ) ); + const uint8_t *type = cursor->data; + + return ( ( cursor->len >= sizeof ( *type ) ) ? *type : ASN1_END ); } -extern void asn1_invalidate_cursor ( struct asn1_cursor *cursor ); +/** + * Get cursor for built object + * + * @v builder ASN.1 object builder + * @ret cursor ASN.1 object cursor + */ +static inline __attribute__ (( always_inline )) struct asn1_cursor * +asn1_built ( struct asn1_builder *builder ) { + union { + struct asn1_builder builder; + struct asn1_cursor cursor; + } *u = container_of ( builder, typeof ( *u ), builder ); + + /* Sanity check */ + linker_assert ( ( ( const void * ) &u->builder.data ) == + &u->cursor.data, asn1_builder_cursor_data_mismatch ); + linker_assert ( &u->builder.len == &u->cursor.len, + asn1_builder_cursor_len_mismatch ); + + return &u->cursor; +} + +extern int asn1_start ( struct asn1_cursor *cursor, unsigned int type, + size_t extra ); extern int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ); extern int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ); @@ -352,6 +395,7 @@ extern int asn1_signature_algorithm ( const struct asn1_cursor *cursor, struct asn1_algorithm **algorithm ); extern int asn1_generalized_time ( const struct asn1_cursor *cursor, time_t *time ); +extern int asn1_grow ( struct asn1_builder *builder, size_t extra ); extern int asn1_prepend_raw ( struct asn1_builder *builder, const void *data, size_t len ); extern int asn1_prepend ( struct asn1_builder *builder, unsigned int type, diff --git a/src/include/ipxe/bitops.h b/src/include/ipxe/bitops.h index 220ab0fe7..7366cd9f1 100644 --- a/src/include/ipxe/bitops.h +++ b/src/include/ipxe/bitops.h @@ -1,235 +1,19 @@ #ifndef _IPXE_BITOPS_H #define _IPXE_BITOPS_H -/* - * Copyright (C) 2008 Michael Brown . - * - * 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 +/** @file * * Bit operations * */ -#include -#include +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* Endianness selection. - * - * This is a property of the NIC, not a property of the host CPU. - */ -#ifdef BITOPS_LITTLE_ENDIAN -#define cpu_to_BIT64 cpu_to_le64 -#define cpu_to_BIT32 cpu_to_le32 -#define BIT64_to_cpu le64_to_cpu -#define BIT32_to_cpu le32_to_cpu -#endif -#ifdef BITOPS_BIG_ENDIAN -#define cpu_to_BIT64 cpu_to_be64 -#define cpu_to_BIT32 cpu_to_be32 -#define BIT64_to_cpu be64_to_cpu -#define BIT32_to_cpu be32_to_cpu -#endif +#include -/** Datatype used to represent a bit in the pseudo-structures */ -typedef unsigned char pseudo_bit_t; - -/** - * Wrapper structure for pseudo_bit_t structures - * - * This structure provides a wrapper around pseudo_bit_t structures. - * It has the correct size, and also encapsulates type information - * about the underlying pseudo_bit_t-based structure, which allows the - * BIT_FILL() etc. macros to work without requiring explicit type - * information. - */ -#define PSEUDO_BIT_STRUCT( _structure ) \ - union { \ - uint8_t bytes[ sizeof ( _structure ) / 8 ]; \ - uint32_t dwords[ sizeof ( _structure ) / 32 ]; \ - uint64_t qwords[ sizeof ( _structure ) / 64 ]; \ - _structure *dummy[0]; \ - } __attribute__ (( packed )) u - -/** Get pseudo_bit_t structure type from wrapper structure pointer */ -#define PSEUDO_BIT_STRUCT_TYPE( _ptr ) \ - typeof ( *((_ptr)->u.dummy[0]) ) - -/** Bit offset of a field within a pseudo_bit_t structure */ -#define BIT_OFFSET( _ptr, _field ) \ - offsetof ( PSEUDO_BIT_STRUCT_TYPE ( _ptr ), _field ) - -/** Bit width of a field within a pseudo_bit_t structure */ -#define BIT_WIDTH( _ptr, _field ) \ - sizeof ( ( ( PSEUDO_BIT_STRUCT_TYPE ( _ptr ) * ) NULL )->_field ) - -/** Qword offset of a field within a pseudo_bit_t structure */ -#define QWORD_OFFSET( _ptr, _field ) \ - ( BIT_OFFSET ( _ptr, _field ) / 64 ) - -/** Qword bit offset of a field within a pseudo_bit_t structure */ -#define QWORD_BIT_OFFSET( _ptr, _index, _field ) \ - ( BIT_OFFSET ( _ptr, _field ) - ( 64 * (_index) ) ) - -/** Bit mask for a field within a pseudo_bit_t structure */ -#define BIT_MASK( _ptr, _field ) \ - ( ( ~( ( uint64_t ) 0 ) ) >> \ - ( 64 - BIT_WIDTH ( _ptr, _field ) ) ) - -/* - * Assemble native-endian qword from named fields and values - * - */ - -#define BIT_ASSEMBLE_1( _ptr, _index, _field, _value ) \ - ( ( ( uint64_t) (_value) ) << \ - QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) - -#define BIT_ASSEMBLE_2( _ptr, _index, _field, _value, ... ) \ - ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ - BIT_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_ASSEMBLE_3( _ptr, _index, _field, _value, ... ) \ - ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ - BIT_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_ASSEMBLE_4( _ptr, _index, _field, _value, ... ) \ - ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ - BIT_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_ASSEMBLE_5( _ptr, _index, _field, _value, ... ) \ - ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ - BIT_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_ASSEMBLE_6( _ptr, _index, _field, _value, ... ) \ - ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ - BIT_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_ASSEMBLE_7( _ptr, _index, _field, _value, ... ) \ - ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ - BIT_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) - -/* - * Build native-endian (positive) qword bitmasks from named fields - * - */ - -#define BIT_MASK_1( _ptr, _index, _field ) \ - ( BIT_MASK ( _ptr, _field ) << \ - QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) - -#define BIT_MASK_2( _ptr, _index, _field, ... ) \ - ( BIT_MASK_1 ( _ptr, _index, _field ) | \ - BIT_MASK_1 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_MASK_3( _ptr, _index, _field, ... ) \ - ( BIT_MASK_1 ( _ptr, _index, _field ) | \ - BIT_MASK_2 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_MASK_4( _ptr, _index, _field, ... ) \ - ( BIT_MASK_1 ( _ptr, _index, _field ) | \ - BIT_MASK_3 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_MASK_5( _ptr, _index, _field, ... ) \ - ( BIT_MASK_1 ( _ptr, _index, _field ) | \ - BIT_MASK_4 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_MASK_6( _ptr, _index, _field, ... ) \ - ( BIT_MASK_1 ( _ptr, _index, _field ) | \ - BIT_MASK_5 ( _ptr, _index, __VA_ARGS__ ) ) - -#define BIT_MASK_7( _ptr, _index, _field, ... ) \ - ( BIT_MASK_1 ( _ptr, _index, _field ) | \ - BIT_MASK_6 ( _ptr, _index, __VA_ARGS__ ) ) - -/* - * Populate little-endian qwords from named fields and values - * - */ - -#define BIT_FILL( _ptr, _index, _assembled ) do { \ - uint64_t *__ptr = &(_ptr)->u.qwords[(_index)]; \ - uint64_t __assembled = (_assembled); \ - *__ptr = cpu_to_BIT64 ( __assembled ); \ - } while ( 0 ) - -#define BIT_FILL_1( _ptr, _field1, ... ) \ - BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - BIT_ASSEMBLE_1 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - _field1, __VA_ARGS__ ) ) - -#define BIT_FILL_2( _ptr, _field1, ... ) \ - BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - BIT_ASSEMBLE_2 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - _field1, __VA_ARGS__ ) ) - -#define BIT_FILL_3( _ptr, _field1, ... ) \ - BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - BIT_ASSEMBLE_3 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - _field1, __VA_ARGS__ ) ) - -#define BIT_FILL_4( _ptr, _field1, ... ) \ - BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - BIT_ASSEMBLE_4 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - _field1, __VA_ARGS__ ) ) - -#define BIT_FILL_5( _ptr, _field1, ... ) \ - BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - BIT_ASSEMBLE_5 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - _field1, __VA_ARGS__ ) ) - -#define BIT_FILL_6( _ptr, _field1, ... ) \ - BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - BIT_ASSEMBLE_6 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ - _field1, __VA_ARGS__ ) ) - -/** Extract value of named field */ -#define BIT_GET64( _ptr, _field ) \ - ( { \ - unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ - uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ - uint64_t __value = BIT64_to_cpu ( *__ptr ); \ - __value >>= \ - QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ - __value &= BIT_MASK ( _ptr, _field ); \ - __value; \ - } ) - -/** Extract value of named field (for fields up to the size of a long) */ -#define BIT_GET( _ptr, _field ) \ - ( ( unsigned long ) BIT_GET64 ( _ptr, _field ) ) - -#define BIT_SET( _ptr, _field, _value ) do { \ - unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ - uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ - unsigned int __shift = \ - QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ - uint64_t __value = (_value); \ - *__ptr &= cpu_to_BIT64 ( ~( BIT_MASK ( _ptr, _field ) << \ - __shift ) ); \ - *__ptr |= cpu_to_BIT64 ( __value << __shift ); \ - } while ( 0 ) +void set_bit ( unsigned int bit, volatile void *bits ); +void clear_bit ( unsigned int bit, volatile void *bits ); +int test_and_set_bit ( unsigned int bit, volatile void *bits ); +int test_and_clear_bit ( unsigned int bit, volatile void *bits ); #endif /* _IPXE_BITOPS_H */ diff --git a/src/include/ipxe/cdc.h b/src/include/ipxe/cdc.h index f1799cd9a..b8b4a59d9 100644 --- a/src/include/ipxe/cdc.h +++ b/src/include/ipxe/cdc.h @@ -14,6 +14,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Class code for communications devices */ #define USB_CLASS_CDC 2 +/** Send encapsulated command */ +#define CDC_SEND_ENCAPSULATED_COMMAND \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x00 ) ) + +/** Get encapsulated response */ +#define CDC_GET_ENCAPSULATED_RESPONSE \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + /** Union functional descriptor */ struct cdc_union_descriptor { /** Descriptor header */ @@ -30,6 +40,11 @@ struct cdc_union_descriptor { /** Ethernet descriptor subtype */ #define CDC_SUBTYPE_ETHERNET 15 +/** Response available */ +#define CDC_RESPONSE_AVAILABLE \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + /** Network connection notification */ #define CDC_NETWORK_CONNECTION \ ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ @@ -52,4 +67,38 @@ extern struct cdc_union_descriptor * cdc_union_descriptor ( struct usb_configuration_descriptor *config, struct usb_interface_descriptor *interface ); +/** + * Send encapsulated command + * + * @v usb USB device + * @v interface Interface number + * @v data Command + * @v len Length of command + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +cdc_send_encapsulated_command ( struct usb_device *usb, unsigned int interface, + void *data, size_t len ) { + + return usb_control ( usb, CDC_SEND_ENCAPSULATED_COMMAND, 0, interface, + data, len ); +} + +/** +* Get encapsulated response +* +* @v usb USB device +* @v interface Interface number +* @v data Response buffer +* @v len Length of response buffer +* @ret rc Return status code +*/ +static inline __attribute__ (( always_inline )) int +cdc_get_encapsulated_response ( struct usb_device *usb, unsigned int interface, + void *data, size_t len ) { + + return usb_control ( usb, CDC_GET_ENCAPSULATED_RESPONSE, 0, interface, + data, len ); +} + #endif /* _IPXE_CDC_H */ diff --git a/src/include/ipxe/certstore.h b/src/include/ipxe/certstore.h index 49b3b512c..e4c789cfd 100644 --- a/src/include/ipxe/certstore.h +++ b/src/include/ipxe/certstore.h @@ -17,5 +17,6 @@ extern struct x509_chain certstore; extern struct x509_certificate * certstore_find ( struct asn1_cursor *raw ); extern struct x509_certificate * certstore_find_key ( struct asn1_cursor *key ); extern void certstore_add ( struct x509_certificate *cert ); +extern void certstore_del ( struct x509_certificate *cert ); #endif /* _IPXE_CERTSTORE_H */ diff --git a/src/include/ipxe/der.h b/src/include/ipxe/der.h new file mode 100644 index 000000000..c63bd9751 --- /dev/null +++ b/src/include/ipxe/der.h @@ -0,0 +1,16 @@ +#ifndef _IPXE_DER_H +#define _IPXE_DER_H + +/** @file + * + * DER image format + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern struct image_type der_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_DER_H */ diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h index d81417e8e..d4ba001b0 100644 --- a/src/include/ipxe/device.h +++ b/src/include/ipxe/device.h @@ -72,7 +72,7 @@ struct device_description { /** A hardware device */ struct device { /** Name */ - char name[32]; + char name[40]; /** Driver name */ const char *driver_name; /** Device description */ diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index a11db3497..b7a5f004b 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -83,6 +83,9 @@ struct dhcp_packet; /** Root path */ #define DHCP_ROOT_PATH 17 +/** Maximum transmission unit */ +#define DHCP_MTU 26 + /** Vendor encapsulated options */ #define DHCP_VENDOR_ENCAP 43 @@ -210,6 +213,29 @@ struct dhcp_pxe_boot_menu_item { /** Vendor class identifier */ #define DHCP_VENDOR_CLASS_ID 60 +/** Vendor class identifier for PXE clients */ +#define DHCP_VENDOR_PXECLIENT( arch, ndi ) \ + 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', DHCP_VENDOR_PXECLIENT_ARCH ( arch ), \ + ':', 'U', 'N', 'D', 'I', ':', DHCP_VENDOR_PXECLIENT_UNDI ( ndi ) + +/** Vendor class identifier architecture for PXE clients */ +#define DHCP_VENDOR_PXECLIENT_ARCH( arch ) \ + ( '0' + ( ( (arch) / 10000 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 1000 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 100 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 10 ) % 10 ) ), \ + ( '0' + ( ( (arch) / 1 ) % 10 ) ) + +/** Vendor class identifier UNDI version for PXE clients */ +#define DHCP_VENDOR_PXECLIENT_UNDI( type, major, minor ) \ + DHCP_VENDOR_PXECLIENT_UNDI_VERSION ( major ), \ + DHCP_VENDOR_PXECLIENT_UNDI_VERSION ( minor ) +#define DHCP_VENDOR_PXECLIENT_UNDI_VERSION( version ) \ + ( '0' + ( ( (version) / 100 ) % 10 ) ), \ + ( '0' + ( ( (version) / 10 ) % 10 ) ), \ + ( '0' + ( ( (version) / 1 ) % 10 ) ) + /** Client identifier */ #define DHCP_CLIENT_ID 61 @@ -266,12 +292,16 @@ enum dhcp_client_architecture_values { DHCP_CLIENT_ARCHITECTURE_LC = 0x0005, /** EFI IA32 */ DHCP_CLIENT_ARCHITECTURE_IA32 = 0x0006, - /** EFI BC */ - DHCP_CLIENT_ARCHITECTURE_EFI = 0x0007, + /** EFI x86-64 */ + DHCP_CLIENT_ARCHITECTURE_X86_64 = 0x0007, /** EFI Xscale */ DHCP_CLIENT_ARCHITECTURE_XSCALE = 0x0008, - /** EFI x86-64 */ - DHCP_CLIENT_ARCHITECTURE_X86_64 = 0x0009, + /** EFI BC */ + DHCP_CLIENT_ARCHITECTURE_EFI = 0x0009, + /** EFI 32-bit ARM */ + DHCP_CLIENT_ARCHITECTURE_ARM32 = 0x000a, + /** EFI 64-bit ARM */ + DHCP_CLIENT_ARCHITECTURE_ARM64 = 0x000b, }; /** Client network device interface */ @@ -403,12 +433,25 @@ struct dhcp_netdev_desc { /** Use cached network settings (obsolete; do not reuse this value) */ #define DHCP_EB_USE_CACHED DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb2 ) -/** BIOS drive number +/** SAN retry count * - * This is the drive number for a drive emulated via INT 13. 0x80 is + * This is the maximum number of times that SAN operations will be + * retried. + */ +#define DHCP_EB_SAN_RETRY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbb ) + +/** SAN filename + * + * This is the path of the bootloader within the SAN device. + */ +#define DHCP_EB_SAN_FILENAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbc ) + +/** SAN drive number + * + * This is the drive number for a SAN-hooked drive. For BIOS, 0x80 is * the first hard disk, 0x81 is the second hard disk, etc. */ -#define DHCP_EB_BIOS_DRIVE DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbd ) +#define DHCP_EB_SAN_DRIVE DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbd ) /** Username * diff --git a/src/include/ipxe/dhcpv6.h b/src/include/ipxe/dhcpv6.h index 9307b6cae..6e70f7e63 100644 --- a/src/include/ipxe/dhcpv6.h +++ b/src/include/ipxe/dhcpv6.h @@ -145,6 +145,21 @@ struct dhcpv6_user_class_option { /** DHCPv6 user class option */ #define DHCPV6_USER_CLASS 15 +/** DHCPv6 vendor class option */ +#define DHCPV6_VENDOR_CLASS 16 + +/** DHCPv6 PXE vendor class + * + * The DHCPv6 vendor class includes a field for an IANA enterprise + * number. The EDK2 codebase uses the value 343, with the comment: + * + * TODO: IANA TBD: temporarily using Intel's + * + * Since this "temporarily" has applied since at least 2010, we assume + * that it has become a de facto standard. + */ +#define DHCPV6_VENDOR_CLASS_PXE 343 + /** DHCPv6 DNS recursive name server option */ #define DHCPV6_DNS_SERVERS 23 @@ -157,6 +172,12 @@ struct dhcpv6_user_class_option { /** DHCPv6 bootfile parameters option */ #define DHCPV6_BOOTFILE_PARAM 60 +/** DHCPv6 client system architecture option */ +#define DHCPV6_CLIENT_ARCHITECTURE 61 + +/** DHCPv6 client network interface identifier option */ +#define DHCPV6_CLIENT_NDI 62 + /** DHCPv6 syslog server option * * This option code has not yet been assigned by IANA. Please update @@ -164,6 +185,39 @@ struct dhcpv6_user_class_option { */ #define DHCPV6_LOG_SERVERS 0xffffffffUL +/** Construct a DHCPv6 byte value */ +#define DHCPV6_BYTE_VALUE( value ) ( (value) & 0xff ) + +/** Construct a DHCPv6 word value */ +#define DHCPV6_WORD_VALUE( value ) \ + DHCPV6_BYTE_VALUE ( (value) >> 8 ), DHCPV6_BYTE_VALUE ( (value) >> 0 ) + +/** Construct a DHCPv6 dword value */ +#define DHCPV6_DWORD_VALUE( value ) \ + DHCPV6_WORD_VALUE ( (value) >> 16 ), DHCPV6_WORD_VALUE ( (value) >> 0 ) + +/** Construct a DHCPv6 option code */ +#define DHCPV6_CODE( code ) DHCPV6_WORD_VALUE ( code ) + +/** Construct a DHCPv6 option length */ +#define DHCPV6_LEN( len ) DHCPV6_WORD_VALUE ( len ) + +/** Construct a DHCPv6 option from a list of bytes */ +#define DHCPV6_OPTION( ... ) \ + DHCPV6_LEN ( VA_ARG_COUNT ( __VA_ARGS__ ) ), __VA_ARGS__ + +/** Construct a DHCPv6 option from a list of characters */ +#define DHCPV6_STRING( ... ) DHCPV6_OPTION ( __VA_ARGS__ ) + +/** Construct a byte-valued DHCPv6 option */ +#define DHCPV6_BYTE( value ) DHCPV6_OPTION ( DHCPV6_BYTE_VALUE ( value ) ) + +/** Construct a word-valued DHCPv6 option */ +#define DHCPV6_WORD( value ) DHCPV6_OPTION ( DHCPV6_WORD_VALUE ( value ) ) + +/** Construct a dword-valued DHCPv6 option */ +#define DHCPV6_DWORD( value ) DHCPV6_OPTION ( DHCPV6_DWORD_VALUE ( value ) ) + /** * Any DHCPv6 option * diff --git a/src/include/ipxe/dummy_sanboot.h b/src/include/ipxe/dummy_sanboot.h new file mode 100644 index 000000000..9c9d942aa --- /dev/null +++ b/src/include/ipxe/dummy_sanboot.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_DUMMY_SANBOOT_H +#define _IPXE_DUMMY_SANBOOT_H + +/** @file + * + * Dummy SAN device + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef SANBOOT_DUMMY +#define SANBOOT_PREFIX_dummy +#else +#define SANBOOT_PREFIX_dummy __dummy_ +#endif + +#endif /* _IPXE_DUMMY_SANBOOT_H */ diff --git a/src/include/ipxe/efi/AArch64/ProcessorBind.h b/src/include/ipxe/efi/AArch64/ProcessorBind.h new file mode 100644 index 000000000..909b5cde4 --- /dev/null +++ b/src/include/ipxe/efi/AArch64/ProcessorBind.h @@ -0,0 +1,156 @@ +/** @file + Processor or Compiler specific defines and types for AArch64. + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PROCESSOR_BIND_H__ +#define __PROCESSOR_BIND_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// Define the processor type so other code can make processor based choices +/// +#define MDE_CPU_AARCH64 + +// +// Make sure we are using the correct packing rules per EFI specification +// +#ifndef __GNUC__ +#pragma pack() +#endif + +#if _MSC_EXTENSIONS + // + // use Microsoft* C compiler dependent integer width types + // + typedef unsigned __int64 UINT64; + typedef __int64 INT64; + typedef unsigned __int32 UINT32; + typedef __int32 INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#else + // + // Assume standard AARCH64 alignment. + // + typedef unsigned long long UINT64; + typedef long long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#endif + +/// +/// Unsigned value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef UINT64 UINTN; + +/// +/// Signed value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef INT64 INTN; + +// +// Processor specific defines +// + +/// +/// A value of native width with the highest bit set. +/// +#define MAX_BIT 0x8000000000000000ULL + +/// +/// A value of native width with the two highest bits set. +/// +#define MAX_2_BITS 0xC000000000000000ULL + +/// +/// Maximum legal AARCH64 address +/// +#define MAX_ADDRESS 0xFFFFFFFFFFFFFFFFULL + +/// +/// Maximum legal AArch64 INTN and UINTN values. +/// +#define MAX_INTN ((INTN)0x7FFFFFFFFFFFFFFFULL) +#define MAX_UINTN ((UINTN)0xFFFFFFFFFFFFFFFFULL) + +/// +/// The stack alignment required for AARCH64 +/// +#define CPU_STACK_ALIGNMENT 16 + +/// +/// Page allocation granularity for AARCH64 +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x10000) + +// +// Modifier to ensure that all protocol member functions and EFI intrinsics +// use the correct C calling convention. All protocol member functions and +// EFI intrinsics are required to modify their member functions with EFIAPI. +// +#define EFIAPI + +// When compiling with Clang, we still use GNU as for the assembler, so we still +// need to define the GCC_ASM* macros. +#if defined(__GNUC__) || defined(__clang__) + /// + /// For GNU assembly code, .global or .globl can declare global symbols. + /// Define this macro to unify the usage. + /// + #define ASM_GLOBAL .globl + + #define GCC_ASM_EXPORT(func__) \ + .global _CONCATENATE (__USER_LABEL_PREFIX__, func__) ;\ + .type ASM_PFX(func__), %function + + #define GCC_ASM_IMPORT(func__) \ + .extern _CONCATENATE (__USER_LABEL_PREFIX__, func__) + +#endif + +/** + Return the pointer to the first instruction of a function given a function pointer. + On ARM CPU architectures, these two pointer values are the same, + so the implementation of this macro is very simple. + + @param FunctionPointer A pointer to a function. + + @return The pointer to the first instruction of a function given a function pointer. + +**/ +#define FUNCTION_ENTRY_POINT(FunctionPointer) (VOID *)(UINTN)(FunctionPointer) + +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ +#endif + +#endif diff --git a/src/include/ipxe/efi/Arm/ProcessorBind.h b/src/include/ipxe/efi/Arm/ProcessorBind.h new file mode 100644 index 000000000..efe3bf174 --- /dev/null +++ b/src/include/ipxe/efi/Arm/ProcessorBind.h @@ -0,0 +1,184 @@ +/** @file + Processor or Compiler specific defines and types for ARM. + + Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+ Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PROCESSOR_BIND_H__ +#define __PROCESSOR_BIND_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// Define the processor type so other code can make processor based choices +/// +#define MDE_CPU_ARM + +// +// Make sure we are using the correct packing rules per EFI specification +// +#ifndef __GNUC__ +#pragma pack() +#endif + +// +// RVCT does not support the __builtin_unreachable() macro +// +#ifdef __ARMCC_VERSION +#define UNREACHABLE() +#endif + +#if _MSC_EXTENSIONS + // + // use Microsoft* C compiler dependent integer width types + // + typedef unsigned __int64 UINT64; + typedef __int64 INT64; + typedef unsigned __int32 UINT32; + typedef __int32 INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#else + // + // Assume standard ARM alignment. + // Need to check portability of long long + // + typedef unsigned long long UINT64; + typedef long long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef signed char INT8; +#endif + +/// +/// Unsigned value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef UINT32 UINTN; + +/// +/// Signed value of native width. (4 bytes on supported 32-bit processor instructions, +/// 8 bytes on supported 64-bit processor instructions) +/// +typedef INT32 INTN; + +// +// Processor specific defines +// + +/// +/// A value of native width with the highest bit set. +/// +#define MAX_BIT 0x80000000 + +/// +/// A value of native width with the two highest bits set. +/// +#define MAX_2_BITS 0xC0000000 + +/// +/// Maximum legal ARM address +/// +#define MAX_ADDRESS 0xFFFFFFFF + +/// +/// Maximum legal ARM INTN and UINTN values. +/// +#define MAX_INTN ((INTN)0x7FFFFFFF) +#define MAX_UINTN ((UINTN)0xFFFFFFFF) + +/// +/// The stack alignment required for ARM +/// +#define CPU_STACK_ALIGNMENT sizeof(UINT64) + +/// +/// Page allocation granularity for ARM +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000) + +// +// Modifier to ensure that all protocol member functions and EFI intrinsics +// use the correct C calling convention. All protocol member functions and +// EFI intrinsics are required to modify their member functions with EFIAPI. +// +#define EFIAPI + +// When compiling with Clang, we still use GNU as for the assembler, so we still +// need to define the GCC_ASM* macros. +#if defined(__GNUC__) || defined(__clang__) + /// + /// For GNU assembly code, .global or .globl can declare global symbols. + /// Define this macro to unify the usage. + /// + #define ASM_GLOBAL .globl + + #if !defined(__APPLE__) + /// + /// ARM EABI defines that the linker should not manipulate call relocations + /// (do bl/blx conversion) unless the target symbol has function type. + /// CodeSourcery 2010.09 started requiring the .type to function properly + /// + #define INTERWORK_FUNC(func__) .type ASM_PFX(func__), %function + + #define GCC_ASM_EXPORT(func__) \ + .global _CONCATENATE (__USER_LABEL_PREFIX__, func__) ;\ + .type ASM_PFX(func__), %function + + #define GCC_ASM_IMPORT(func__) \ + .extern _CONCATENATE (__USER_LABEL_PREFIX__, func__) + + #else + // + // .type not supported by Apple Xcode tools + // + #define INTERWORK_FUNC(func__) + + #define GCC_ASM_EXPORT(func__) \ + .globl _CONCATENATE (__USER_LABEL_PREFIX__, func__) \ + + #define GCC_ASM_IMPORT(name) + + #endif +#endif + +/** + Return the pointer to the first instruction of a function given a function pointer. + On ARM CPU architectures, these two pointer values are the same, + so the implementation of this macro is very simple. + + @param FunctionPointer A pointer to a function. + + @return The pointer to the first instruction of a function given a function pointer. + +**/ +#define FUNCTION_ENTRY_POINT(FunctionPointer) (VOID *)(UINTN)(FunctionPointer) + +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ +#endif + +#endif + + diff --git a/src/include/ipxe/efi/Base.h b/src/include/ipxe/efi/Base.h index ed678a9ce..26c90c154 100644 --- a/src/include/ipxe/efi/Base.h +++ b/src/include/ipxe/efi/Base.h @@ -6,7 +6,7 @@ environment. There are a set of base libraries in the Mde Package that can be used to implement base modules. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -65,6 +65,29 @@ VERIFY_SIZE_OF (UINT64, 8); VERIFY_SIZE_OF (CHAR8, 1); VERIFY_SIZE_OF (CHAR16, 2); +// +// The following three enum types are used to verify that the compiler +// configuration for enum types is compliant with Section 2.3.1 of the +// UEFI 2.3 Specification. These enum types and enum values are not +// intended to be used. A prefix of '__' is used avoid conflicts with +// other types. +// +typedef enum { + __VerifyUint8EnumValue = 0xff +} __VERIFY_UINT8_ENUM_SIZE; + +typedef enum { + __VerifyUint16EnumValue = 0xffff +} __VERIFY_UINT16_ENUM_SIZE; + +typedef enum { + __VerifyUint32EnumValue = 0xffffffff +} __VERIFY_UINT32_ENUM_SIZE; + +VERIFY_SIZE_OF (__VERIFY_UINT8_ENUM_SIZE, 4); +VERIFY_SIZE_OF (__VERIFY_UINT16_ENUM_SIZE, 4); +VERIFY_SIZE_OF (__VERIFY_UINT32_ENUM_SIZE, 4); + // // The Microsoft* C compiler can removed references to unreferenced data items // if the /OPT:REF linker option is used. We defined a macro as this is a @@ -86,6 +109,117 @@ VERIFY_SIZE_OF (CHAR16, 2); #define GLOBAL_REMOVE_IF_UNREFERENCED #endif +// +// Should be used in combination with NORETURN to avoid 'noreturn' returns +// warnings. +// +#ifndef UNREACHABLE + #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4) + /// + /// Signal compilers and analyzers that this call is not reachable. It is + /// up to the compiler to remove any code past that point. + /// Not implemented by GCC 4.4 or earlier. + /// + #define UNREACHABLE() __builtin_unreachable () + #elif defined (__has_feature) + #if __has_builtin (__builtin_unreachable) + /// + /// Signal compilers and analyzers that this call is not reachable. It is + /// up to the compiler to remove any code past that point. + /// + #define UNREACHABLE() __builtin_unreachable () + #endif + #endif + + #ifndef UNREACHABLE + /// + /// Signal compilers and analyzers that this call is not reachable. It is + /// up to the compiler to remove any code past that point. + /// + #define UNREACHABLE() + #endif +#endif + +// +// Signaling compilers and analyzers that a certain function cannot return may +// remove all following code and thus lead to better optimization and less +// false positives. +// +#ifndef NORETURN + #if defined (__GNUC__) || defined (__clang__) + /// + /// Signal compilers and analyzers that the function cannot return. + /// It is up to the compiler to remove any code past a call to functions + /// flagged with this attribute. + /// + #define NORETURN __attribute__((noreturn)) + #elif defined(_MSC_EXTENSIONS) && !defined(MDE_CPU_EBC) + /// + /// Signal compilers and analyzers that the function cannot return. + /// It is up to the compiler to remove any code past a call to functions + /// flagged with this attribute. + /// + #define NORETURN __declspec(noreturn) + #else + /// + /// Signal compilers and analyzers that the function cannot return. + /// It is up to the compiler to remove any code past a call to functions + /// flagged with this attribute. + /// + #define NORETURN + #endif +#endif + +// +// Should be used in combination with ANALYZER_NORETURN to avoid 'noreturn' +// returns warnings. +// +#ifndef ANALYZER_UNREACHABLE + #ifdef __clang_analyzer__ + #if __has_builtin (__builtin_unreachable) + /// + /// Signal the analyzer that this call is not reachable. + /// This excludes compilers. + /// + #define ANALYZER_UNREACHABLE() __builtin_unreachable () + #endif + #endif + + #ifndef ANALYZER_UNREACHABLE + /// + /// Signal the analyzer that this call is not reachable. + /// This excludes compilers. + /// + #define ANALYZER_UNREACHABLE() + #endif +#endif + +// +// Static Analyzers may issue errors about potential NULL-dereferences when +// dereferencing a pointer, that has been checked before, outside of a +// NULL-check. This may lead to false positives, such as when using ASSERT() +// for verification. +// +#ifndef ANALYZER_NORETURN + #ifdef __has_feature + #if __has_feature (attribute_analyzer_noreturn) + /// + /// Signal analyzers that the function cannot return. + /// This excludes compilers. + /// + #define ANALYZER_NORETURN __attribute__((analyzer_noreturn)) + #endif + #endif + + #ifndef ANALYZER_NORETURN + /// + /// Signal the analyzer that the function cannot return. + /// This excludes compilers. + /// + #define ANALYZER_NORETURN + #endif +#endif + // // For symbol name in assembly code, an extra "_" is sometimes necessary // @@ -133,6 +267,20 @@ typedef struct { UINT8 Data4[8]; } GUID; +/// +/// 4-byte buffer. An IPv4 internet protocol address. +/// +typedef struct { + UINT8 Addr[4]; +} IPv4_ADDRESS; + +/// +/// 16-byte buffer. An IPv6 internet protocol address. +/// +typedef struct { + UINT8 Addr[16]; +} IPv6_ADDRESS; + // // 8-bytes unsigned value that represents a physical system address. // @@ -193,7 +341,7 @@ struct _LIST_ENTRY { // // UEFI specification claims 1 and 0. We are concerned about the -// complier portability so we did it this way. +// compiler portability so we did it this way. // /// @@ -213,6 +361,11 @@ struct _LIST_ENTRY { /// #define NULL ((VOID *) 0) +// +// Null character +// +#define CHAR_NULL 0x0000 + /// /// Maximum values for common UEFI Data Types /// @@ -480,7 +633,31 @@ struct _LIST_ENTRY { #define VA_COPY(Dest, Start) __va_copy (Dest, Start) -#elif defined(__GNUC__) && !defined(NO_BUILTIN_VA_FUNCS) +#elif defined(__GNUC__) + +#if defined(MDE_CPU_X64) && !defined(NO_MSABI_VA_FUNCS) +// +// X64 only. Use MS ABI version of GCC built-in macros for variable argument lists. +// +/// +/// Both GCC and LLVM 3.8 for X64 support new variable argument intrinsics for Microsoft ABI +/// + +/// +/// Variable used to traverse the list of arguments. This type can vary by +/// implementation and could be an array or structure. +/// +typedef __builtin_ms_va_list VA_LIST; + +#define VA_START(Marker, Parameter) __builtin_ms_va_start (Marker, Parameter) + +#define VA_ARG(Marker, TYPE) ((sizeof (TYPE) < sizeof (UINTN)) ? (TYPE)(__builtin_va_arg (Marker, UINTN)) : (TYPE)(__builtin_va_arg (Marker, TYPE))) + +#define VA_END(Marker) __builtin_ms_va_end (Marker) + +#define VA_COPY(Dest, Start) __builtin_ms_va_copy (Dest, Start) + +#else // // Use GCC built-in macros for variable argument lists. // @@ -499,6 +676,8 @@ typedef __builtin_va_list VA_LIST; #define VA_COPY(Dest, Start) __builtin_va_copy (Dest, Start) +#endif + #else /// /// Variable used to traverse the list of arguments. This type can vary by @@ -942,6 +1121,11 @@ typedef UINTN RETURN_STATUS; /// #define RETURN_COMPROMISED_DATA ENCODE_ERROR (33) +/// +/// A HTTP error occurred during the network operation. +/// +#define RETURN_HTTP_ERROR ENCODE_ERROR (35) + /// /// The string contained one or more characters that /// the device could not render and were skipped. @@ -971,6 +1155,12 @@ typedef UINTN RETURN_STATUS; /// #define RETURN_WARN_STALE_DATA ENCODE_WARNING (5) +/// +/// The resulting buffer contains UEFI-compliant file system. +/// +#define RETURN_WARN_FILE_SYSTEM ENCODE_WARNING (6) + + /** Returns a 16-bit signature built from 2 ASCII characters. @@ -1024,10 +1214,10 @@ typedef UINTN RETURN_STATUS; #define SIGNATURE_64(A, B, C, D, E, F, G, H) \ (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) -#if defined(_MSC_EXTENSIONS) && !defined (MDE_CPU_EBC) +#if defined(_MSC_EXTENSIONS) && !defined (__INTEL_COMPILER) && !defined (MDE_CPU_EBC) #pragma intrinsic(_ReturnAddress) /** - Get the return address of the calling funcation. + Get the return address of the calling function. Based on intrinsic function _ReturnAddress that provides the address of the instruction in the calling function that will be executed after @@ -1035,27 +1225,27 @@ typedef UINTN RETURN_STATUS; @param L Return Level. - @return The return address of the calling funcation or 0 if L != 0. + @return The return address of the calling function or 0 if L != 0. **/ #define RETURN_ADDRESS(L) ((L == 0) ? _ReturnAddress() : (VOID *) 0) #elif defined(__GNUC__) void * __builtin_return_address (unsigned int level); /** - Get the return address of the calling funcation. + Get the return address of the calling function. Based on built-in Function __builtin_return_address that returns the return address of the current function, or of one of its callers. @param L Return Level. - @return The return address of the calling funcation. + @return The return address of the calling function. **/ #define RETURN_ADDRESS(L) __builtin_return_address (L) #else /** - Get the return address of the calling funcation. + Get the return address of the calling function. @param L Return Level. @@ -1065,5 +1255,18 @@ typedef UINTN RETURN_STATUS; #define RETURN_ADDRESS(L) ((VOID *) 0) #endif +/** + Return the number of elements in an array. + + @param Array An object of array type. Array is only used as an argument to + the sizeof operator, therefore Array is never evaluated. The + caller is responsible for ensuring that Array's type is not + incomplete; that is, Array must have known constant size. + + @return The number of elements in Array. The result has type UINTN. + +**/ +#define ARRAY_SIZE(Array) (sizeof (Array) / sizeof ((Array)[0])) + #endif diff --git a/src/include/ipxe/efi/Guid/Acpi.h b/src/include/ipxe/efi/Guid/Acpi.h new file mode 100644 index 000000000..c4169c5ff --- /dev/null +++ b/src/include/ipxe/efi/Guid/Acpi.h @@ -0,0 +1,48 @@ +/** @file + GUIDs used for ACPI entries in the EFI system table + + These GUIDs point the ACPI tables as defined in the ACPI specifications. + ACPI 2.0 specification defines the ACPI 2.0 GUID. UEFI 2.0 defines the + ACPI 2.0 Table GUID and ACPI Table GUID. + + Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + GUIDs defined in UEFI 2.0 spec. + +**/ + +#ifndef __ACPI_GUID_H__ +#define __ACPI_GUID_H__ + +FILE_LICENCE ( BSD3 ); + +#define ACPI_TABLE_GUID \ + { \ + 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define EFI_ACPI_TABLE_GUID \ + { \ + 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \ + } + +#define ACPI_10_TABLE_GUID ACPI_TABLE_GUID + +// +// ACPI 2.0 or newer tables should use EFI_ACPI_TABLE_GUID. +// +#define EFI_ACPI_20_TABLE_GUID EFI_ACPI_TABLE_GUID + +extern EFI_GUID gEfiAcpiTableGuid; +extern EFI_GUID gEfiAcpi10TableGuid; +extern EFI_GUID gEfiAcpi20TableGuid; + +#endif diff --git a/src/include/ipxe/efi/Ia32/ProcessorBind.h b/src/include/ipxe/efi/Ia32/ProcessorBind.h index 8fb5fbcf2..2d6c4b4bb 100644 --- a/src/include/ipxe/efi/Ia32/ProcessorBind.h +++ b/src/include/ipxe/efi/Ia32/ProcessorBind.h @@ -81,7 +81,7 @@ FILE_LICENCE ( BSD3 ); #pragma warning ( disable : 4057 ) // -// ASSERT(FALSE) or while (TRUE) are legal constructes so supress this warning +// ASSERT(FALSE) or while (TRUE) are legal constructs so suppress this warning // #pragma warning ( disable : 4127 ) @@ -95,7 +95,7 @@ FILE_LICENCE ( BSD3 ); // #pragma warning ( disable : 4206 ) -#if _MSC_VER == 1800 +#if _MSC_VER == 1800 || _MSC_VER == 1900 // // Disable these warnings for VS2013. @@ -103,13 +103,13 @@ FILE_LICENCE ( BSD3 ); // // This warning is for potentially uninitialized local variable, and it may cause false -// positive issues in VS2013 build +// positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4701 ) // // This warning is for potentially uninitialized local pointer variable, and it may cause -// false positive issues in VS2013 build +// false positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4703 ) @@ -121,7 +121,7 @@ FILE_LICENCE ( BSD3 ); #if defined(_MSC_EXTENSIONS) // - // use Microsoft C complier dependent integer width types + // use Microsoft C compiler dependent integer width types // /// @@ -259,6 +259,12 @@ typedef INT32 INTN; /// #define CPU_STACK_ALIGNMENT sizeof(UINTN) +/// +/// Page allocation granularity for IA-32. +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000) + // // Modifier to ensure that all protocol member functions and EFI intrinsics // use the correct C calling convention. All protocol member functions and diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi51.h b/src/include/ipxe/efi/IndustryStandard/Acpi51.h index 3d0e46bc6..1ca114cad 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi51.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi51.h @@ -1,8 +1,9 @@ /** @file - ACPI 5.1 definitions from the ACPI Specification Revision 5.1 July, 2014. + ACPI 5.1 definitions from the ACPI Specification Revision 5.1 Errata B January, 2016. Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
- Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -531,9 +532,18 @@ typedef struct { UINT32 GicId; UINT64 PhysicalBaseAddress; UINT32 SystemVectorBase; - UINT32 Reserved2; + UINT8 GicVersion; + UINT8 Reserved2[3]; } EFI_ACPI_5_1_GIC_DISTRIBUTOR_STRUCTURE; +/// +/// GIC Version +/// +#define EFI_ACPI_5_1_GIC_V1 0x01 +#define EFI_ACPI_5_1_GIC_V2 0x02 +#define EFI_ACPI_5_1_GIC_V3 0x03 +#define EFI_ACPI_5_1_GIC_V4 0x04 + /// /// GIC MSI Frame Structure /// diff --git a/src/include/ipxe/efi/IndustryStandard/Acpi60.h b/src/include/ipxe/efi/IndustryStandard/Acpi60.h index f235b3750..c600735fd 100644 --- a/src/include/ipxe/efi/IndustryStandard/Acpi60.h +++ b/src/include/ipxe/efi/IndustryStandard/Acpi60.h @@ -1,7 +1,8 @@ /** @file - ACPI 6.0 definitions from the ACPI Specification Revision 6.0 April, 2015. + ACPI 6.0 definitions from the ACPI Specification Revision 6.0 Errata A January, 2016. - Copyright (c) 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -291,9 +292,9 @@ typedef struct { } EFI_ACPI_6_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER; /// -/// MADT Revision (as defined in ACPI 6.0 spec.) +/// MADT Revision (as defined in ACPI 6.0 Errata A spec.) /// -#define EFI_ACPI_6_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION 0x03 +#define EFI_ACPI_6_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION 0x04 /// /// Multiple APIC Flags @@ -534,9 +535,18 @@ typedef struct { UINT32 GicId; UINT64 PhysicalBaseAddress; UINT32 SystemVectorBase; - UINT32 Reserved2; + UINT8 GicVersion; + UINT8 Reserved2[3]; } EFI_ACPI_6_0_GIC_DISTRIBUTOR_STRUCTURE; +/// +/// GIC Version +/// +#define EFI_ACPI_6_0_GIC_V1 0x01 +#define EFI_ACPI_6_0_GIC_V2 0x02 +#define EFI_ACPI_6_0_GIC_V3 0x03 +#define EFI_ACPI_6_0_GIC_V4 0x04 + /// /// GIC MSI Frame Structure /// @@ -1385,14 +1395,14 @@ typedef struct { // #define EFI_ACPI_6_0_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_FLAGS_CONTROL_REGION_FOR_MANAGEMENT BIT0 #define EFI_ACPI_6_0_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_FLAGS_PROXIMITY_DOMAIN_VALID BIT1 -#define EFI_ACPI_6_0_NFIT_GUID_VOLATILE_MEMORY_REGION { 0x7305944F, 0xFDDA, 0x44E3, 0xB1, 0x6C, 0x3F, 0x22, 0xD2, 0x52, 0xE5, 0xD0 } -#define EFI_ACPI_6_0_NFIT_GUID_BYTE_ADDRESSABLE_PERSISTENT_MEMORY_REGION { 0x66F0D379, 0xB4F3, 0x4074, 0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB } -#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_CONTROL_REGION { 0x92F701F6, 0x13B4, 0x405D, 0x91, 0x0B, 0x29, 0x93, 0x67, 0xE8, 0x23, 0x4C } -#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_BLOCK_DATA_WINDOW_REGION { 0x91AF0530, 0x5D86, 0x470E, 0xA6, 0xB0, 0x0A, 0x2D, 0xB9, 0x40, 0x82, 0x49 } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_VOLATILE { 0x77AB535A, 0x45FC, 0x624B, 0x55, 0x60, 0xF7, 0xB2, 0x81, 0xD1, 0xF9, 0x6E } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_VOLATILE { 0x3D5ABD30, 0x4175, 0x87CE, 0x6D, 0x64, 0xD2, 0xAD, 0xE5, 0x23, 0xC4, 0xBB } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_PERSISTENT { 0x5CEA02C9, 0x4D07, 0x69D3, 0x26, 0x9F ,0x44, 0x96, 0xFB, 0xE0, 0x96, 0xF9 } -#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_PERSISTENT { 0x08018188, 0x42CD, 0xBB48, 0x10, 0x0F, 0x53, 0x87, 0xD5, 0x3D, 0xED, 0x3D } +#define EFI_ACPI_6_0_NFIT_GUID_VOLATILE_MEMORY_REGION { 0x7305944F, 0xFDDA, 0x44E3, { 0xB1, 0x6C, 0x3F, 0x22, 0xD2, 0x52, 0xE5, 0xD0 }} +#define EFI_ACPI_6_0_NFIT_GUID_BYTE_ADDRESSABLE_PERSISTENT_MEMORY_REGION { 0x66F0D379, 0xB4F3, 0x4074, { 0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB }} +#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_CONTROL_REGION { 0x92F701F6, 0x13B4, 0x405D, { 0x91, 0x0B, 0x29, 0x93, 0x67, 0xE8, 0x23, 0x4C }} +#define EFI_ACPI_6_0_NFIT_GUID_NVDIMM_BLOCK_DATA_WINDOW_REGION { 0x91AF0530, 0x5D86, 0x470E, { 0xA6, 0xB0, 0x0A, 0x2D, 0xB9, 0x40, 0x82, 0x49 }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_VOLATILE { 0x77AB535A, 0x45FC, 0x624B, { 0x55, 0x60, 0xF7, 0xB2, 0x81, 0xD1, 0xF9, 0x6E }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_VOLATILE { 0x3D5ABD30, 0x4175, 0x87CE, { 0x6D, 0x64, 0xD2, 0xAD, 0xE5, 0x23, 0xC4, 0xBB }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_DISK_REGION_PERSISTENT { 0x5CEA02C9, 0x4D07, 0x69D3, { 0x26, 0x9F ,0x44, 0x96, 0xFB, 0xE0, 0x96, 0xF9 }} +#define EFI_ACPI_6_0_NFIT_GUID_RAM_DISK_SUPPORTING_VIRTUAL_CD_REGION_PERSISTENT { 0x08018188, 0x42CD, 0xBB48, { 0x10, 0x0F, 0x53, 0x87, 0xD5, 0x3D, 0xED, 0x3D }} typedef struct { UINT16 Type; UINT16 Length; @@ -1658,6 +1668,9 @@ typedef struct { #define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_LOCAL_INTERRUPT 0x02 #define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_SCI 0x03 #define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_NMI 0x04 +#define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_CMCI 0x05 +#define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_MCE 0x06 +#define EFI_ACPI_6_0_HARDWARE_ERROR_NOTIFICATION_GPIO_SIGNAL 0x07 /// /// Hardware Error Notification Configuration Write Enable Structure Definition @@ -2241,9 +2254,9 @@ typedef struct { #define EFI_ACPI_6_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE SIGNATURE_32('i', 'B', 'F', 'T') /// -/// "IORT" Interrupt Source Override +/// "IORT" I/O Remapping Table /// -#define EFI_ACPI_6_0_INTERRUPT_SOURCE_OVERRIDE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') +#define EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') /// /// "IVRS" I/O Virtualization Reporting Structure diff --git a/src/include/ipxe/efi/IndustryStandard/Pci22.h b/src/include/ipxe/efi/IndustryStandard/Pci22.h index f0f2ae922..c14d4b4bd 100644 --- a/src/include/ipxe/efi/IndustryStandard/Pci22.h +++ b/src/include/ipxe/efi/IndustryStandard/Pci22.h @@ -5,11 +5,10 @@ PCI Local Bus Specification, 2.2 PCI-to-PCI Bridge Architecture Specification, Revision 1.2 PC Card Standard, 8.0 + PCI Power Management Interface Specifiction, Revision 1.2 - - - Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
- Copyright (c) 2014 - 2105, Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2014 - 2015, Hewlett-Packard Development Company, L.P.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -638,6 +637,7 @@ typedef union { #define EFI_PCI_CAPABILITY_ID_SLOTID 0x04 #define EFI_PCI_CAPABILITY_ID_MSI 0x05 #define EFI_PCI_CAPABILITY_ID_HOTPLUG 0x06 +#define EFI_PCI_CAPABILITY_ID_SHPC 0x0C /// /// Capabilities List Header @@ -648,18 +648,6 @@ typedef struct { UINT8 NextItemPtr; } EFI_PCI_CAPABILITY_HDR; -/// -/// Power Management Register Block Definition -/// Section 3.2, PCI Power Management Interface Specifiction, Revision 1.2 -/// -typedef struct { - EFI_PCI_CAPABILITY_HDR Hdr; - UINT16 PMC; - UINT16 PMCSR; - UINT8 BridgeExtention; - UINT8 Data; -} EFI_PCI_CAPABILITY_PMI; - /// /// PMC - Power Management Capabilities /// Section 3.2.3, PCI Power Management Interface Specifiction, Revision 1.2 @@ -668,7 +656,7 @@ typedef union { struct { UINT16 Version : 3; UINT16 PmeClock : 1; - UINT16 : 1; + UINT16 Reserved : 1; UINT16 DeviceSpecificInitialization : 1; UINT16 AuxCurrent : 3; UINT16 D1Support : 1; @@ -687,7 +675,9 @@ typedef union { typedef union { struct { UINT16 PowerState : 2; - UINT16 : 6; + UINT16 ReservedForPciExpress : 1; + UINT16 NoSoftReset : 1; + UINT16 Reserved : 4; UINT16 PmeEnable : 1; UINT16 DataSelect : 4; UINT16 DataScale : 2; @@ -696,6 +686,36 @@ typedef union { UINT16 Data; } EFI_PCI_PMCSR; +#define PCI_POWER_STATE_D0 0 +#define PCI_POWER_STATE_D1 1 +#define PCI_POWER_STATE_D2 2 +#define PCI_POWER_STATE_D3_HOT 3 + +/// +/// PMCSR_BSE - PMCSR PCI-to-PCI Bridge Support Extensions +/// Section 3.2.5, PCI Power Management Interface Specifiction, Revision 1.2 +/// +typedef union { + struct { + UINT8 Reserved : 6; + UINT8 B2B3 : 1; + UINT8 BusPowerClockControl : 1; + } Bits; + UINT8 Uint8; +} EFI_PCI_PMCSR_BSE; + +/// +/// Power Management Register Block Definition +/// Section 3.2, PCI Power Management Interface Specifiction, Revision 1.2 +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + EFI_PCI_PMC PMC; + EFI_PCI_PMCSR PMCSR; + EFI_PCI_PMCSR_BSE BridgeExtention; + UINT8 Data; +} EFI_PCI_CAPABILITY_PMI; + /// /// A.G.P Capability /// Section 6.1.4, Accelerated Graphics Port Interface Specification, Revision 1.0 @@ -762,22 +782,12 @@ typedef struct { /// } EFI_PCI_CAPABILITY_HOTPLUG; -#define DEVICE_ID_NOCARE 0xFFFF - -#define PCI_ACPI_UNUSED 0 -#define PCI_BAR_NOCHANGE 0 -#define PCI_BAR_OLD_ALIGN 0xFFFFFFFFFFFFFFFFULL -#define PCI_BAR_EVEN_ALIGN 0xFFFFFFFFFFFFFFFEULL -#define PCI_BAR_SQUAD_ALIGN 0xFFFFFFFFFFFFFFFDULL -#define PCI_BAR_DQUAD_ALIGN 0xFFFFFFFFFFFFFFFCULL - #define PCI_BAR_IDX0 0x00 #define PCI_BAR_IDX1 0x01 #define PCI_BAR_IDX2 0x02 #define PCI_BAR_IDX3 0x03 #define PCI_BAR_IDX4 0x04 #define PCI_BAR_IDX5 0x05 -#define PCI_BAR_ALL 0xFF /// /// EFI PCI Option ROM definitions diff --git a/src/include/ipxe/efi/IndustryStandard/Tpm20.h b/src/include/ipxe/efi/IndustryStandard/Tpm20.h index 0a5f1c0e1..656bf21eb 100644 --- a/src/include/ipxe/efi/IndustryStandard/Tpm20.h +++ b/src/include/ipxe/efi/IndustryStandard/Tpm20.h @@ -677,7 +677,10 @@ typedef UINT32 TPM_RH; #define TPM_RH_LOCKOUT (TPM_RH)(0x4000000A) #define TPM_RH_ENDORSEMENT (TPM_RH)(0x4000000B) #define TPM_RH_PLATFORM (TPM_RH)(0x4000000C) -#define TPM_RH_LAST (TPM_RH)(0x4000000C) +#define TPM_RH_PLATFORM_NV (TPM_RH)(0x4000000D) +#define TPM_RH_AUTH_00 (TPM_RH)(0x40000010) +#define TPM_RH_AUTH_FF (TPM_RH)(0x4000010F) +#define TPM_RH_LAST (TPM_RH)(0x4000010F) // Table 28 - TPM_HC Constants typedef TPM_HANDLE TPM_HC; diff --git a/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h b/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h index df65be914..3394c7cbe 100644 --- a/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h +++ b/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h @@ -1,7 +1,7 @@ /** @file TCG EFI Platform Definition in TCG_EFI_Platform_1_20_Final - Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -153,6 +153,7 @@ typedef struct tdEFI_HANDOFF_TABLE_POINTERS { /// This structure serves as the header for measuring variables. The name of the /// variable (in Unicode format) should immediately follow, then the variable /// data. +/// This is defined in TCG EFI Platform Spec for TPM1.1 or 1.2 V1.22 /// typedef struct tdEFI_VARIABLE_DATA { EFI_GUID VariableName; @@ -162,6 +163,22 @@ typedef struct tdEFI_VARIABLE_DATA { INT8 VariableData[1]; ///< Driver or platform-specific data } EFI_VARIABLE_DATA; +/// +/// UEFI_VARIABLE_DATA +/// +/// This structure serves as the header for measuring variables. The name of the +/// variable (in Unicode format) should immediately follow, then the variable +/// data. +/// This is defined in TCG PC Client Firmware Profile Spec 00.21 +/// +typedef struct tdUEFI_VARIABLE_DATA { + EFI_GUID VariableName; + UINT64 UnicodeNameLength; + UINT64 VariableDataLength; + CHAR16 UnicodeName[1]; + INT8 VariableData[1]; ///< Driver or platform-specific data +} UEFI_VARIABLE_DATA; + // // For TrEE1.0 compatibility // @@ -190,6 +207,17 @@ typedef struct tdTCG_PCR_EVENT2 { UINT8 Event[1]; } TCG_PCR_EVENT2; +// +// TCG PCR Event2 Header +// Follow TCG EFI Protocol Spec 5.2 Crypto Agile Log Entry Format +// +typedef struct tdTCG_PCR_EVENT2_HDR{ + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; + TPML_DIGEST_VALUES Digests; + UINT32 EventSize; +} TCG_PCR_EVENT2_HDR; + // // Log Header Entry Data // @@ -270,6 +298,33 @@ typedef struct { //UINT8 vendorInfo[vendorInfoSize]; } TCG_EfiSpecIDEventStruct; + + +#define TCG_EfiStartupLocalityEvent_SIGNATURE "StartupLocality" + + +// +// PC Client PTP spec Table 8 Relationship between Locality and Locality Attribute +// +#define LOCALITY_0_INDICATOR 0x01 +#define LOCALITY_1_INDICATOR 0x02 +#define LOCALITY_2_INDICATOR 0x03 +#define LOCALITY_3_INDICATOR 0x04 +#define LOCALITY_4_INDICATOR 0x05 + + +// +// Startup Locality Event +// +typedef struct tdTCG_EfiStartupLocalityEvent{ + UINT8 Signature[16]; + // + // The Locality Indicator which sent the TPM2_Startup command + // + UINT8 StartupLocality; +} TCG_EfiStartupLocalityEvent; + + // // Restore original structure alignment // diff --git a/src/include/ipxe/efi/Library/BaseLib.h b/src/include/ipxe/efi/Library/BaseLib.h index a45a20d70..a03ef30ef 100644 --- a/src/include/ipxe/efi/Library/BaseLib.h +++ b/src/include/ipxe/efi/Library/BaseLib.h @@ -2,7 +2,7 @@ Provides string functions, linked list functions, math functions, synchronization functions, file path functions, and CPU architecture-specific functions. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -189,6 +189,8 @@ typedef struct { /** Returns the length of a Null-terminated Unicode string. + This function is similar as strlen_s defined in C11. + If String is not aligned on a 16-bit boundary, then ASSERT(). @param String A pointer to a Null-terminated Unicode string. @@ -207,14 +209,46 @@ StrnLenS ( IN UINTN MaxSize ); +/** + Returns the size of a Null-terminated Unicode string in bytes, including the + Null terminator. + + This function returns the size of the Null-terminated Unicode string + specified by String in bytes, including the Null terminator. + + If String is not aligned on a 16-bit boundary, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + @param MaxSize The maximum number of Destination Unicode + char, including the Null terminator. + + @retval 0 If String is NULL. + @retval (sizeof (CHAR16) * (MaxSize + 1)) + If there is no Null terminator in the first MaxSize characters of + String. + @return The size of the Null-terminated Unicode string in bytes, including + the Null terminator. + +**/ +UINTN +EFIAPI +StrnSizeS ( + IN CONST CHAR16 *String, + IN UINTN MaxSize + ); + /** Copies the string pointed to by Source (including the terminating null char) to the array pointed to by Destination. + This function is similar as strcpy_s defined in C11. + If Destination is not aligned on a 16-bit boundary, then ASSERT(). If Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -243,10 +277,14 @@ StrCpyS ( Source to the array pointed to by Destination. If no null char is copied from Source, then Destination[Length] is always set to null. + This function is similar as strncpy_s defined in C11. + If Length > 0 and Destination is not aligned on a 16-bit boundary, then ASSERT(). If Length > 0 and Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -277,10 +315,14 @@ StrnCpyS ( Appends a copy of the string pointed to by Source (including the terminating null char) to the end of the string pointed to by Destination. + This function is similar as strcat_s defined in C11. + If Destination is not aligned on a 16-bit boundary, then ASSERT(). If Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -313,10 +355,14 @@ StrCatS ( copied from Source, then Destination[StrLen(Destination) + Length] is always set to null. + This function is similar as strncat_s defined in C11. + If Destination is not aligned on a 16-bit boundary, then ASSERT(). If Source is not aligned on a 16-bit boundary, then ASSERT(). If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Unicode string. @param DestMax The maximum number of Destination Unicode char, including terminating null char. @@ -345,9 +391,245 @@ StrnCatS ( IN UINTN Length ); +/** + Convert a Null-terminated Unicode decimal string to a value of type UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Unicode string specified by String as a decimal number. The format of the + input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +StrDecimalToUintnS ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Unicode decimal string to a value of type UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Unicode string specified by String as a decimal number. The format of the + input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +StrDecimalToUint64S ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type + UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Unicode string specified by String as a hexadecimal number. The format of + the input Unicode string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab + characters, before [zeros], [x] or [hexadecimal digit]. The running zero + before [x] or [hexadecimal digit] will be ignored. Then, the decoding starts + after [x] or the first valid hexadecimal digit. Then, the function stops at + the first character that is a not a valid hexadecimal character or NULL, + whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +StrHexToUintnS ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type + UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Unicode string specified by String as a hexadecimal number. The format of + the input Unicode string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab + characters, before [zeros], [x] or [hexadecimal digit]. The running zero + before [x] or [hexadecimal digit] will be ignored. Then, the decoding starts + after [x] or the first valid hexadecimal digit. Then, the function stops at + the first character that is a not a valid hexadecimal character or NULL, + whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +StrHexToUint64S ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + /** Returns the length of a Null-terminated Ascii string. + This function is similar as strlen_s defined in C11. + @param String A pointer to a Null-terminated Ascii string. @param MaxSize The maximum number of Destination Ascii char, including terminating null char. @@ -364,12 +646,42 @@ AsciiStrnLenS ( IN UINTN MaxSize ); +/** + Returns the size of a Null-terminated Ascii string in bytes, including the + Null terminator. + + This function returns the size of the Null-terminated Ascii string specified + by String in bytes, including the Null terminator. + + @param String A pointer to a Null-terminated Ascii string. + @param MaxSize The maximum number of Destination Ascii + char, including the Null terminator. + + @retval 0 If String is NULL. + @retval (sizeof (CHAR8) * (MaxSize + 1)) + If there is no Null terminator in the first MaxSize characters of + String. + @return The size of the Null-terminated Ascii string in bytes, including the + Null terminator. + +**/ +UINTN +EFIAPI +AsciiStrnSizeS ( + IN CONST CHAR8 *String, + IN UINTN MaxSize + ); + /** Copies the string pointed to by Source (including the terminating null char) to the array pointed to by Destination. + This function is similar as strcpy_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -398,8 +710,12 @@ AsciiStrCpyS ( Source to the array pointed to by Destination. If no null char is copied from Source, then Destination[Length] is always set to null. + This function is similar as strncpy_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -430,8 +746,12 @@ AsciiStrnCpyS ( Appends a copy of the string pointed to by Source (including the terminating null char) to the end of the string pointed to by Destination. + This function is similar as strcat_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -464,8 +784,12 @@ AsciiStrCatS ( copied from Source, then Destination[StrLen(Destination) + Length] is always set to null. + This function is similar as strncat_s defined in C11. + If an error would be returned, then the function will also ASSERT(). + If an error is returned, then the Destination is unmodified. + @param Destination A pointer to a Null-terminated Ascii string. @param DestMax The maximum number of Destination Ascii char, including terminating null char. @@ -494,6 +818,234 @@ AsciiStrnCatS ( IN UINTN Length ); +/** + Convert a Null-terminated Ascii decimal string to a value of type UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Ascii string specified by String as a decimal number. The format of the + input Ascii string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrDecimalToUintnS ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Ascii decimal string to a value of type UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Ascii string specified by String as a decimal number. The format of the + input Ascii string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The function will + ignore the pad space, which includes spaces or tab characters, before + [decimal digits]. The running zero in the beginning of [decimal digits] will + be ignored. Then, the function stops at the first character that is a not a + valid decimal character or a Null-terminator, whichever one comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid decimal digits in the above format, then 0 is stored + at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + decimal digits right after the optional pad spaces, the value of String is + stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrDecimalToUint64S ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + +/** + Convert a Null-terminated Ascii hexadecimal string to a value of type UINTN. + + This function outputs a value of type UINTN by interpreting the contents of + the Ascii string specified by String as a hexadecimal number. The format of + the input Ascii string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. If + "x" appears in the input string, it must be prefixed with at least one 0. The + function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digits]. The running zero before [x] or + [hexadecimal digits] will be ignored. Then, the decoding starts after [x] or + the first valid hexadecimal digit. Then, the function stops at the first + character that is a not a valid hexadecimal character or Null-terminator, + whichever on comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINTN, then + MAX_UINTN is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINTN. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrHexToUintnS ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINTN *Data + ); + +/** + Convert a Null-terminated Ascii hexadecimal string to a value of type UINT64. + + This function outputs a value of type UINT64 by interpreting the contents of + the Ascii string specified by String as a hexadecimal number. The format of + the input Ascii string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. If + "x" appears in the input string, it must be prefixed with at least one 0. The + function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digits]. The running zero before [x] or + [hexadecimal digits] will be ignored. Then, the decoding starts after [x] or + the first valid hexadecimal digit. Then, the function stops at the first + character that is a not a valid hexadecimal character or Null-terminator, + whichever on comes first. + + If String is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and String contains more than + PcdMaximumAsciiStringLength Ascii characters, not including the + Null-terminator, then ASSERT(). + + If String has no valid hexadecimal digits in the above format, then 0 is + stored at the location pointed to by Data. + If the number represented by String exceeds the range defined by UINT64, then + MAX_UINT64 is stored at the location pointed to by Data. + + If EndPointer is not NULL, a pointer to the character that stopped the scan + is stored at the location pointed to by EndPointer. If String has no valid + hexadecimal digits right after the optional pad spaces, the value of String + is stored at the location pointed to by EndPointer. + + @param String Pointer to a Null-terminated Ascii string. + @param EndPointer Pointer to character that stops scan. + @param Data Pointer to the converted value. + + @retval RETURN_SUCCESS Value is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If PcdMaximumAsciiStringLength is not zero, + and String contains more than + PcdMaximumAsciiStringLength Ascii + characters, not including the + Null-terminator. + @retval RETURN_UNSUPPORTED If the number represented by String exceeds + the range defined by UINT64. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrHexToUint64S ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT UINT64 *Data + ); + #ifndef DISABLE_NEW_DEPRECATED_INTERFACES @@ -845,7 +1397,7 @@ StrStr ( If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according - to the range defined by UINTN, then ASSERT(). + to the range defined by UINTN, then MAX_UINTN is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including @@ -885,7 +1437,7 @@ StrDecimalToUintn ( If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according - to the range defined by UINT64, then ASSERT(). + to the range defined by UINT64, then MAX_UINT64 is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including @@ -927,7 +1479,7 @@ StrDecimalToUint64 ( If String has no leading pad spaces, leading zeros or valid hexadecimal digits, then zero is returned. If the number represented by String overflows according to the range defined by - UINTN, then ASSERT(). + UINTN, then MAX_UINTN is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, @@ -969,7 +1521,7 @@ StrHexToUintn ( If String has no leading pad spaces, leading zeros or valid hexadecimal digits, then zero is returned. If the number represented by String overflows according to the range defined by - UINT64, then ASSERT(). + UINT64, then MAX_UINT64 is returned. If PcdMaximumUnicodeStringLength is not zero, and String contains more than PcdMaximumUnicodeStringLength Unicode characters not including the Null-terminator, @@ -987,6 +1539,241 @@ StrHexToUint64 ( ); /** + Convert a Null-terminated Unicode string to IPv6 address and prefix length. + + This function outputs a value of type IPv6_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the Unicode string specified + by String. The format of the input Unicode string String is as follows: + + X:X:X:X:X:X:X:X[/P] + + X contains one to four hexadecimal digit characters in the range [0-9], [a-f] and + [A-F]. X is converted to a value of type UINT16, whose low byte is stored in low + memory address and high byte is stored in high memory address. P contains decimal + digit characters in the range [0-9]. The running zero in the beginning of P will + be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid hexadecimal digit character after eight X's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + "::" can be used to compress one or more groups of X when X contains only 0. + The "::" can only appear once in the String. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If String is not aligned in a 16-bit boundary, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv6 address. + @param PrefixLength Pointer to the converted IPv6 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If X contains more than four hexadecimal + digit characters. + If String contains "::" and number of X + is not less than 8. + If P starts with character that is not a + valid decimal digit character. + If the decimal number converted from P + exceeds 128. + +**/ +RETURN_STATUS +EFIAPI +StrToIpv6Address ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT IPv6_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +/** + Convert a Null-terminated Unicode string to IPv4 address and prefix length. + + This function outputs a value of type IPv4_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the Unicode string specified + by String. The format of the input Unicode string String is as follows: + + D.D.D.D[/P] + + D and P are decimal digit characters in the range [0-9]. The running zero in + the beginning of D and P will be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid decimal digit character after four D's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If String is not aligned in a 16-bit boundary, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero, and String contains more than + PcdMaximumUnicodeStringLength Unicode characters, not including the + Null-terminator, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated Unicode string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv4 address. + @param PrefixLength Pointer to the converted IPv4 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not in the correct format. + If any decimal number converted from D + exceeds 255. + If the decimal number converted from P + exceeds 32. + +**/ +RETURN_STATUS +EFIAPI +StrToIpv4Address ( + IN CONST CHAR16 *String, + OUT CHAR16 **EndPointer, OPTIONAL + OUT IPv4_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +#define GUID_STRING_LENGTH 36 + +/** + Convert a Null-terminated Unicode GUID string to a value of type + EFI_GUID. + + This function outputs a GUID value by interpreting the contents of + the Unicode string specified by String. The format of the input + Unicode string String consists of 36 characters, as follows: + + aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + + The pairs aa - pp are two characters in the range [0-9], [a-f] and + [A-F], with each pair representing a single byte hexadecimal value. + + The mapping between String and the EFI_GUID structure is as follows: + aa Data1[24:31] + bb Data1[16:23] + cc Data1[8:15] + dd Data1[0:7] + ee Data2[8:15] + ff Data2[0:7] + gg Data3[8:15] + hh Data3[0:7] + ii Data4[0:7] + jj Data4[8:15] + kk Data4[16:23] + ll Data4[24:31] + mm Data4[32:39] + nn Data4[40:47] + oo Data4[48:55] + pp Data4[56:63] + + If String is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + If String is not aligned in a 16-bit boundary, then ASSERT(). + + @param String Pointer to a Null-terminated Unicode string. + @param Guid Pointer to the converted GUID. + + @retval RETURN_SUCCESS Guid is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not as the above format. + +**/ +RETURN_STATUS +EFIAPI +StrToGuid ( + IN CONST CHAR16 *String, + OUT GUID *Guid + ); + +/** + Convert a Null-terminated Unicode hexadecimal string to a byte array. + + This function outputs a byte array by interpreting the contents of + the Unicode string specified by String in hexadecimal format. The format of + the input Unicode string String is: + + [XX]* + + X is a hexadecimal digit character in the range [0-9], [a-f] and [A-F]. + The function decodes every two hexadecimal digit characters as one byte. The + decoding stops after Length of characters and outputs Buffer containing + (Length / 2) bytes. + + If String is not aligned in a 16-bit boundary, then ASSERT(). + + If String is NULL, then ASSERT(). + + If Buffer is NULL, then ASSERT(). + + If Length is not multiple of 2, then ASSERT(). + + If PcdMaximumUnicodeStringLength is not zero and Length is greater than + PcdMaximumUnicodeStringLength, then ASSERT(). + + If MaxBufferSize is less than (Length / 2), then ASSERT(). + + @param String Pointer to a Null-terminated Unicode string. + @param Length The number of Unicode characters to decode. + @param Buffer Pointer to the converted bytes array. + @param MaxBufferSize The maximum size of Buffer. + + @retval RETURN_SUCCESS Buffer is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If Length is not multiple of 2. + If PcdMaximumUnicodeStringLength is not zero, + and Length is greater than + PcdMaximumUnicodeStringLength. + @retval RETURN_UNSUPPORTED If Length of characters from String contain + a character that is not valid hexadecimal + digit characters, or a Null-terminator. + @retval RETURN_BUFFER_TOO_SMALL If MaxBufferSize is less than (Length / 2). +**/ +RETURN_STATUS +EFIAPI +StrHexToBytes ( + IN CONST CHAR16 *String, + IN UINTN Length, + OUT UINT8 *Buffer, + IN UINTN MaxBufferSize + ); + +#ifndef DISABLE_NEW_DEPRECATED_INTERFACES + +/** + [ATTENTION] This function is deprecated for security reason. + Convert a Null-terminated Unicode string to a Null-terminated ASCII string and returns the ASCII string. @@ -1026,6 +1813,110 @@ UnicodeStrToAsciiStr ( OUT CHAR8 *Destination ); +#endif + +/** + Convert a Null-terminated Unicode string to a Null-terminated + ASCII string. + + This function is similar to AsciiStrCpyS. + + This function converts the content of the Unicode string Source + to the ASCII string Destination by copying the lower 8 bits of + each Unicode character. The function terminates the ASCII string + Destination by appending a Null-terminator character at the end. + + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((StrLen (Source) + 1) * sizeof (CHAR8)) in bytes. + + If any Unicode characters in Source contain non-zero value in + the upper 8 bits, then ASSERT(). + + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then the Destination is unmodified. + + @param Source The pointer to a Null-terminated Unicode string. + @param Destination The pointer to a Null-terminated ASCII string. + @param DestMax The maximum number of Destination Ascii + char, including terminating null char. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than StrLen(Source). + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If PcdMaximumAsciiStringLength is not zero, + and DestMax is greater than + PcdMaximumAsciiStringLength. + If PcdMaximumUnicodeStringLength is not zero, + and DestMax is greater than + PcdMaximumUnicodeStringLength. + If DestMax is 0. + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +UnicodeStrToAsciiStrS ( + IN CONST CHAR16 *Source, + OUT CHAR8 *Destination, + IN UINTN DestMax + ); + +/** + Convert not more than Length successive characters from a Null-terminated + Unicode string to a Null-terminated Ascii string. If no null char is copied + from Source, then Destination[Length] is always set to null. + + This function converts not more than Length successive characters from the + Unicode string Source to the Ascii string Destination by copying the lower 8 + bits of each Unicode character. The function terminates the Ascii string + Destination by appending a Null-terminator character at the end. + + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((StrLen (Source) + 1) * sizeof (CHAR8)) in bytes. + + If any Unicode characters in Source contain non-zero value in the upper 8 + bits, then ASSERT(). + If Source is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then the Destination is unmodified. + + @param Source The pointer to a Null-terminated Unicode string. + @param Length The maximum number of Unicode characters to + convert. + @param Destination The pointer to a Null-terminated Ascii string. + @param DestMax The maximum number of Destination Ascii + char, including terminating null char. + @param DestinationLength The number of Unicode characters converted. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If DestinationLength is NULL. + If PcdMaximumAsciiStringLength is not zero, + and Length or DestMax is greater than + PcdMaximumAsciiStringLength. + If PcdMaximumUnicodeStringLength is not + zero, and Length or DestMax is greater than + PcdMaximumUnicodeStringLength. + If DestMax is 0. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than + MIN(StrLen(Source), Length). + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +UnicodeStrnToAsciiStrS ( + IN CONST CHAR16 *Source, + IN UINTN Length, + OUT CHAR8 *Destination, + IN UINTN DestMax, + OUT UINTN *DestinationLength + ); #ifndef DISABLE_NEW_DEPRECATED_INTERFACES @@ -1393,7 +2284,7 @@ AsciiStrStr ( If String has only pad spaces, then 0 is returned. If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according to the range defined by - UINTN, then ASSERT(). + UINTN, then MAX_UINTN is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including the Null-terminator, @@ -1430,7 +2321,7 @@ AsciiStrDecimalToUintn ( If String has only pad spaces, then 0 is returned. If String has no pad spaces or valid decimal digits, then 0 is returned. If the number represented by String overflows according to the range defined by - UINT64, then ASSERT(). + UINT64, then MAX_UINT64 is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including the Null-terminator, @@ -1471,7 +2362,7 @@ AsciiStrDecimalToUint64 ( 0 is returned. If the number represented by String overflows according to the range defined by UINTN, - then ASSERT(). + then MAX_UINTN is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including @@ -1512,7 +2403,7 @@ AsciiStrHexToUintn ( 0 is returned. If the number represented by String overflows according to the range defined by UINT64, - then ASSERT(). + then MAX_UINT64 is returned. If String is NULL, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and String contains more than PcdMaximumAsciiStringLength ASCII characters not including @@ -1529,8 +2420,225 @@ AsciiStrHexToUint64 ( IN CONST CHAR8 *String ); +/** + Convert a Null-terminated ASCII string to IPv6 address and prefix length. + + This function outputs a value of type IPv6_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the ASCII string specified + by String. The format of the input ASCII string String is as follows: + + X:X:X:X:X:X:X:X[/P] + + X contains one to four hexadecimal digit characters in the range [0-9], [a-f] and + [A-F]. X is converted to a value of type UINT16, whose low byte is stored in low + memory address and high byte is stored in high memory address. P contains decimal + digit characters in the range [0-9]. The running zero in the beginning of P will + be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid hexadecimal digit character after eight X's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + "::" can be used to compress one or more groups of X when X contains only 0. + The "::" can only appear once in the String. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated ASCII string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv6 address. + @param PrefixLength Pointer to the converted IPv6 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If X contains more than four hexadecimal + digit characters. + If String contains "::" and number of X + is not less than 8. + If P starts with character that is not a + valid decimal digit character. + If the decimal number converted from P + exceeds 128. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToIpv6Address ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT IPv6_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); /** + Convert a Null-terminated ASCII string to IPv4 address and prefix length. + + This function outputs a value of type IPv4_ADDRESS and may output a value + of type UINT8 by interpreting the contents of the ASCII string specified + by String. The format of the input ASCII string String is as follows: + + D.D.D.D[/P] + + D and P are decimal digit characters in the range [0-9]. The running zero in + the beginning of D and P will be ignored. /P is optional. + + When /P is not in the String, the function stops at the first character that is + not a valid decimal digit character after four D's are converted. + + When /P is in the String, the function stops at the first character that is not + a valid decimal digit character after P is converted. + + If String is NULL, then ASSERT(). + + If Address is NULL, then ASSERT(). + + If EndPointer is not NULL and Address is translated from String, a pointer + to the character that stopped the scan is stored at the location pointed to + by EndPointer. + + @param String Pointer to a Null-terminated ASCII string. + @param EndPointer Pointer to character that stops scan. + @param Address Pointer to the converted IPv4 address. + @param PrefixLength Pointer to the converted IPv4 address prefix + length. MAX_UINT8 is returned when /P is + not in the String. + + @retval RETURN_SUCCESS Address is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not in the correct format. + If any decimal number converted from D + exceeds 255. + If the decimal number converted from P + exceeds 32. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToIpv4Address ( + IN CONST CHAR8 *String, + OUT CHAR8 **EndPointer, OPTIONAL + OUT IPv4_ADDRESS *Address, + OUT UINT8 *PrefixLength OPTIONAL + ); + +/** + Convert a Null-terminated ASCII GUID string to a value of type + EFI_GUID. + + This function outputs a GUID value by interpreting the contents of + the ASCII string specified by String. The format of the input + ASCII string String consists of 36 characters, as follows: + + aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + + The pairs aa - pp are two characters in the range [0-9], [a-f] and + [A-F], with each pair representing a single byte hexadecimal value. + + The mapping between String and the EFI_GUID structure is as follows: + aa Data1[24:31] + bb Data1[16:23] + cc Data1[8:15] + dd Data1[0:7] + ee Data2[8:15] + ff Data2[0:7] + gg Data3[8:15] + hh Data3[0:7] + ii Data4[0:7] + jj Data4[8:15] + kk Data4[16:23] + ll Data4[24:31] + mm Data4[32:39] + nn Data4[40:47] + oo Data4[48:55] + pp Data4[56:63] + + If String is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + + @param String Pointer to a Null-terminated ASCII string. + @param Guid Pointer to the converted GUID. + + @retval RETURN_SUCCESS Guid is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + @retval RETURN_UNSUPPORTED If String is not as the above format. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToGuid ( + IN CONST CHAR8 *String, + OUT GUID *Guid + ); + +/** + Convert a Null-terminated ASCII hexadecimal string to a byte array. + + This function outputs a byte array by interpreting the contents of + the ASCII string specified by String in hexadecimal format. The format of + the input ASCII string String is: + + [XX]* + + X is a hexadecimal digit character in the range [0-9], [a-f] and [A-F]. + The function decodes every two hexadecimal digit characters as one byte. The + decoding stops after Length of characters and outputs Buffer containing + (Length / 2) bytes. + + If String is NULL, then ASSERT(). + + If Buffer is NULL, then ASSERT(). + + If Length is not multiple of 2, then ASSERT(). + + If PcdMaximumAsciiStringLength is not zero and Length is greater than + PcdMaximumAsciiStringLength, then ASSERT(). + + If MaxBufferSize is less than (Length / 2), then ASSERT(). + + @param String Pointer to a Null-terminated ASCII string. + @param Length The number of ASCII characters to decode. + @param Buffer Pointer to the converted bytes array. + @param MaxBufferSize The maximum size of Buffer. + + @retval RETURN_SUCCESS Buffer is translated from String. + @retval RETURN_INVALID_PARAMETER If String is NULL. + If Data is NULL. + If Length is not multiple of 2. + If PcdMaximumAsciiStringLength is not zero, + and Length is greater than + PcdMaximumAsciiStringLength. + @retval RETURN_UNSUPPORTED If Length of characters from String contain + a character that is not valid hexadecimal + digit characters, or a Null-terminator. + @retval RETURN_BUFFER_TOO_SMALL If MaxBufferSize is less than (Length / 2). +**/ +RETURN_STATUS +EFIAPI +AsciiStrHexToBytes ( + IN CONST CHAR8 *String, + IN UINTN Length, + OUT UINT8 *Buffer, + IN UINTN MaxBufferSize + ); + +#ifndef DISABLE_NEW_DEPRECATED_INTERFACES + +/** + [ATTENTION] This function is deprecated for security reason. + Convert one Null-terminated ASCII string to a Null-terminated Unicode string and returns the Unicode string. @@ -1564,6 +2672,105 @@ AsciiStrToUnicodeStr ( OUT CHAR16 *Destination ); +#endif + +/** + Convert one Null-terminated ASCII string to a Null-terminated + Unicode string. + + This function is similar to StrCpyS. + + This function converts the contents of the ASCII string Source to the Unicode + string Destination. The function terminates the Unicode string Destination by + appending a Null-terminator character at the end. + + The caller is responsible to make sure Destination points to a buffer with size + equal or greater than ((AsciiStrLen (Source) + 1) * sizeof (CHAR16)) in bytes. + + If Destination is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then the Destination is unmodified. + + @param Source The pointer to a Null-terminated ASCII string. + @param Destination The pointer to a Null-terminated Unicode string. + @param DestMax The maximum number of Destination Unicode + char, including terminating null char. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than StrLen(Source). + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If PcdMaximumUnicodeStringLength is not zero, + and DestMax is greater than + PcdMaximumUnicodeStringLength. + If PcdMaximumAsciiStringLength is not zero, + and DestMax is greater than + PcdMaximumAsciiStringLength. + If DestMax is 0. + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrToUnicodeStrS ( + IN CONST CHAR8 *Source, + OUT CHAR16 *Destination, + IN UINTN DestMax + ); + +/** + Convert not more than Length successive characters from a Null-terminated + Ascii string to a Null-terminated Unicode string. If no null char is copied + from Source, then Destination[Length] is always set to null. + + This function converts not more than Length successive characters from the + Ascii string Source to the Unicode string Destination. The function + terminates the Unicode string Destination by appending a Null-terminator + character at the end. + + The caller is responsible to make sure Destination points to a buffer with + size not smaller than + ((MIN(AsciiStrLen(Source), Length) + 1) * sizeof (CHAR8)) in bytes. + + If Destination is not aligned on a 16-bit boundary, then ASSERT(). + If an error would be returned, then the function will also ASSERT(). + + If an error is returned, then Destination and DestinationLength are + unmodified. + + @param Source The pointer to a Null-terminated Ascii string. + @param Length The maximum number of Ascii characters to convert. + @param Destination The pointer to a Null-terminated Unicode string. + @param DestMax The maximum number of Destination Unicode char, + including terminating null char. + @param DestinationLength The number of Ascii characters converted. + + @retval RETURN_SUCCESS String is converted. + @retval RETURN_INVALID_PARAMETER If Destination is NULL. + If Source is NULL. + If DestinationLength is NULL. + If PcdMaximumUnicodeStringLength is not + zero, and Length or DestMax is greater than + PcdMaximumUnicodeStringLength. + If PcdMaximumAsciiStringLength is not zero, + and Length or DestMax is greater than + PcdMaximumAsciiStringLength. + If DestMax is 0. + @retval RETURN_BUFFER_TOO_SMALL If DestMax is NOT greater than + MIN(AsciiStrLen(Source), Length). + @retval RETURN_ACCESS_DENIED If Source and Destination overlap. + +**/ +RETURN_STATUS +EFIAPI +AsciiStrnToUnicodeStrS ( + IN CONST CHAR8 *Source, + IN UINTN Length, + OUT CHAR16 *Destination, + IN UINTN DestMax, + OUT UINTN *DestinationLength + ); /** Converts an 8-bit value to an 8-bit BCD value. @@ -1610,8 +2817,7 @@ BcdToDecimal8 ( // /** - Removes the last directory or file entry in a path by changing the last - L'\' to a CHAR_NULL. + Removes the last directory or file entry in a path. @param[in, out] Path The pointer to the path to modify. @@ -1635,7 +2841,7 @@ PathRemoveLastItem( @param[in] Path The pointer to the string containing the path. - @return Returns Path, otherwise returns NULL to indicate that an error has occured. + @return Returns Path, otherwise returns NULL to indicate that an error has occurred. **/ CHAR16* EFIAPI diff --git a/src/include/ipxe/efi/Pi/PiHob.h b/src/include/ipxe/efi/Pi/PiHob.h index 121748dec..2663b0525 100644 --- a/src/include/ipxe/efi/Pi/PiHob.h +++ b/src/include/ipxe/efi/Pi/PiHob.h @@ -1,7 +1,7 @@ /** @file HOB related definitions in PI. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -11,7 +11,7 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. @par Revision Reference: - PI Version 1.4 + PI Version 1.4a **/ @@ -295,7 +295,7 @@ typedef UINT32 EFI_RESOURCE_ATTRIBUTE_TYPE; #define EFI_RESOURCE_ATTRIBUTE_PERSISTABLE 0x01000000 #define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED 0x00040000 -#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00800000 +#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00080000 // // Physical memory relative reliability attribute. This diff --git a/src/include/ipxe/efi/ProcessorBind.h b/src/include/ipxe/efi/ProcessorBind.h index 7466814fa..ff1517f33 100644 --- a/src/include/ipxe/efi/ProcessorBind.h +++ b/src/include/ipxe/efi/ProcessorBind.h @@ -18,4 +18,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #endif +#if __arm__ +#include +#endif + +#if __aarch64__ +#include +#endif + #endif /* _IPXE_EFI_PROCESSOR_BIND_H */ diff --git a/src/include/ipxe/efi/Protocol/AcpiTable.h b/src/include/ipxe/efi/Protocol/AcpiTable.h new file mode 100644 index 000000000..798b13dc3 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/AcpiTable.h @@ -0,0 +1,129 @@ +/** @file + The file provides the protocol to install or remove an ACPI + table from a platform. + + Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __ACPI_TABLE_H___ +#define __ACPI_TABLE_H___ + +FILE_LICENCE ( BSD3 ); + +#define EFI_ACPI_TABLE_PROTOCOL_GUID \ + { 0xffe06bdd, 0x6107, 0x46a6, { 0x7b, 0xb2, 0x5a, 0x9c, 0x7e, 0xc5, 0x27, 0x5c }} + + +typedef struct _EFI_ACPI_TABLE_PROTOCOL EFI_ACPI_TABLE_PROTOCOL; + +/** + + The InstallAcpiTable() function allows a caller to install an + ACPI table. When successful, the table will be linked by the + RSDT/XSDT. AcpiTableBuffer specifies the table to be installed. + InstallAcpiTable() will make a copy of the table and insert the + copy into the RSDT/XSDT. InstallAcpiTable() must insert the new + table at the end of the RSDT/XSDT. To prevent namespace + collision, ACPI tables may be created using UEFI ACPI table + format. If this protocol is used to install a table with a + signature already present in the system, the new table will not + replace the existing table. It is a platform implementation + decision to add a new table with a signature matching an + existing table or disallow duplicate table signatures and + return EFI_ACCESS_DENIED. On successful output, TableKey is + initialized with a unique key. Its value may be used in a + subsequent call to UninstallAcpiTable to remove an ACPI table. + If an EFI application is running at the time of this call, the + relevant EFI_CONFIGURATION_TABLE pointer to the RSDT is no + longer considered valid. + + + @param This A pointer to a EFI_ACPI_TABLE_PROTOCOL. + + @param AcpiTableBuffer A pointer to a buffer containing the + ACPI table to be installed. + + @param AcpiTableBufferSize Specifies the size, in bytes, of + the AcpiTableBuffer buffer. + + + @param TableKey Returns a key to refer to the ACPI table. + + @retval EFI_SUCCESS The table was successfully inserted + + @retval EFI_INVALID_PARAMETER Either AcpiTableBuffer is NULL, + TableKey is NULL, or + AcpiTableBufferSize and the size + field embedded in the ACPI table + pointed to by AcpiTableBuffer + are not in sync. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources exist to + complete the request. + @retval EFI_ACCESS_DENIED The table signature matches a table already + present in the system and platform policy + does not allow duplicate tables of this type. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ACPI_TABLE_INSTALL_ACPI_TABLE)( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN VOID *AcpiTableBuffer, + IN UINTN AcpiTableBufferSize, + OUT UINTN *TableKey +); + + +/** + + The UninstallAcpiTable() function allows a caller to remove an + ACPI table. The routine will remove its reference from the + RSDT/XSDT. A table is referenced by the TableKey parameter + returned from a prior call to InstallAcpiTable(). If an EFI + application is running at the time of this call, the relevant + EFI_CONFIGURATION_TABLE pointer to the RSDT is no longer + considered valid. + + @param This A pointer to a EFI_ACPI_TABLE_PROTOCOL. + + @param TableKey Specifies the table to uninstall. The key was + returned from InstallAcpiTable(). + + @retval EFI_SUCCESS The table was successfully inserted + + @retval EFI_NOT_FOUND TableKey does not refer to a valid key + for a table entry. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources exist to + complete the request. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE)( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN UINTN TableKey +); + +/// +/// The EFI_ACPI_TABLE_PROTOCOL provides the ability for a component +/// to install and uninstall ACPI tables from a platform. +/// +struct _EFI_ACPI_TABLE_PROTOCOL { + EFI_ACPI_TABLE_INSTALL_ACPI_TABLE InstallAcpiTable; + EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE UninstallAcpiTable; +}; + +extern EFI_GUID gEfiAcpiTableProtocolGuid; + +#endif + diff --git a/src/include/ipxe/efi/Protocol/AppleNetBoot.h b/src/include/ipxe/efi/Protocol/AppleNetBoot.h new file mode 100644 index 000000000..5946524fd --- /dev/null +++ b/src/include/ipxe/efi/Protocol/AppleNetBoot.h @@ -0,0 +1,46 @@ +#ifndef _IPXE_EFI_APPLE_NET_BOOT_PROTOCOL_H +#define _IPXE_EFI_APPLE_NET_BOOT_PROTOCOL_H + +/** @file + * + * Apple Net Boot Protocol + * + */ + +FILE_LICENCE ( BSD3 ); + +#define EFI_APPLE_NET_BOOT_PROTOCOL_GUID \ + { 0x78ee99fb, 0x6a5e, 0x4186, \ + { 0x97, 0xde, 0xcd, 0x0a, 0xba, 0x34, 0x5a, 0x74 } } + +typedef struct _EFI_APPLE_NET_BOOT_PROTOCOL EFI_APPLE_NET_BOOT_PROTOCOL; + +/** + Get a DHCP packet obtained by the firmware during NetBoot. + + @param This A pointer to the APPLE_NET_BOOT_PROTOCOL instance. + @param BufferSize A pointer to the size of the buffer in bytes. + @param DataBuffer The memory buffer to copy the packet to. If it is + NULL, then the size of the packet is returned + in BufferSize. + @retval EFI_SUCCESS The packet was copied. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the + current packet. BufferSize has been + updated with the size needed to + complete the request. +**/ +typedef +EFI_STATUS +(EFIAPI *GET_DHCP_RESPONSE) ( + IN EFI_APPLE_NET_BOOT_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *DataBuffer + ); + +struct _EFI_APPLE_NET_BOOT_PROTOCOL +{ + GET_DHCP_RESPONSE GetDhcpResponse; + GET_DHCP_RESPONSE GetBsdpResponse; +}; + +#endif /*_IPXE_EFI_APPLE_NET_BOOT_PROTOCOL_H */ diff --git a/src/include/ipxe/efi/Protocol/BlockIo2.h b/src/include/ipxe/efi/Protocol/BlockIo2.h new file mode 100644 index 000000000..0b9cf8eb1 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/BlockIo2.h @@ -0,0 +1,208 @@ +/** @file + Block IO2 protocol as defined in the UEFI 2.3.1 specification. + + The Block IO2 protocol defines an extension to the Block IO protocol which + enables the ability to read and write data at a block level in a non-blocking + manner. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __BLOCK_IO2_H__ +#define __BLOCK_IO2_H__ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_BLOCK_IO2_PROTOCOL_GUID \ + { \ + 0xa77b2472, 0xe282, 0x4e9f, {0xa2, 0x45, 0xc2, 0xc0, 0xe2, 0x7b, 0xbc, 0xc1} \ + } + +typedef struct _EFI_BLOCK_IO2_PROTOCOL EFI_BLOCK_IO2_PROTOCOL; + +/** + The struct of Block IO2 Token. +**/ +typedef struct { + + /// + /// If Event is NULL, then blocking I/O is performed.If Event is not NULL and + /// non-blocking I/O is supported, then non-blocking I/O is performed, and + /// Event will be signaled when the read request is completed. + /// + EFI_EVENT Event; + + /// + /// Defines whether or not the signaled event encountered an error. + /// + EFI_STATUS TransactionStatus; +} EFI_BLOCK_IO2_TOKEN; + + +/** + Reset the block device hardware. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Indicates that the driver may perform a more + exhausive verification operation of the device + during reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_RESET_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + This function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and + non-blocking I/O is being used, the Event associated with this request will + not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is + replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The + caller is responsible for either having implicit or + explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Token->Event is + not NULL.The data was read correctly from the + device if the Token->Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_READ_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA LBA, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + This function writes the requested number of blocks to the device. All blocks + are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA, + EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is + being used, the Event associated with this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The write request was queued if Event is not NULL. + The data was written correctly to the device if + the Event is NULL. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_WRITE_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA LBA, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED + is returned and non-blocking I/O is being used, the Event associated with + this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in,out] Token A pointer to the token associated with the transaction + + @retval EFI_SUCCESS The flush request was queued if Event is not NULL. + All outstanding data was written correctly to the + device if the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while writting back + the data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_FLUSH_EX) ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ); + +/// +/// The Block I/O2 protocol defines an extension to the Block I/O protocol which +/// enables the ability to read and write data at a block level in a non-blocking +// manner. +/// +struct _EFI_BLOCK_IO2_PROTOCOL { + /// + /// A pointer to the EFI_BLOCK_IO_MEDIA data for this device. + /// Type EFI_BLOCK_IO_MEDIA is defined in BlockIo.h. + /// + EFI_BLOCK_IO_MEDIA *Media; + + EFI_BLOCK_RESET_EX Reset; + EFI_BLOCK_READ_EX ReadBlocksEx; + EFI_BLOCK_WRITE_EX WriteBlocksEx; + EFI_BLOCK_FLUSH_EX FlushBlocksEx; +}; + +extern EFI_GUID gEfiBlockIo2ProtocolGuid; + +#endif + diff --git a/src/include/ipxe/efi/Protocol/Cpu.h b/src/include/ipxe/efi/Protocol/Cpu.h deleted file mode 100644 index 665924e88..000000000 --- a/src/include/ipxe/efi/Protocol/Cpu.h +++ /dev/null @@ -1,302 +0,0 @@ -/** @file - CPU Architectural Protocol as defined in PI spec Volume 2 DXE - - This code abstracts the DXE core from processor implementation details. - - Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
- This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#ifndef __ARCH_PROTOCOL_CPU_H__ -#define __ARCH_PROTOCOL_CPU_H__ - -FILE_LICENCE ( BSD3 ); - -#include - -#define EFI_CPU_ARCH_PROTOCOL_GUID \ - { 0x26baccb1, 0x6f42, 0x11d4, {0xbc, 0xe7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } } - -typedef struct _EFI_CPU_ARCH_PROTOCOL EFI_CPU_ARCH_PROTOCOL; - -/// -/// The type of flush operation -/// -typedef enum { - EfiCpuFlushTypeWriteBackInvalidate, - EfiCpuFlushTypeWriteBack, - EfiCpuFlushTypeInvalidate, - EfiCpuMaxFlushType -} EFI_CPU_FLUSH_TYPE; - -/// -/// The type of processor INIT. -/// -typedef enum { - EfiCpuInit, - EfiCpuMaxInitType -} EFI_CPU_INIT_TYPE; - -/** - EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. - - @param InterruptType Defines the type of interrupt or exception that - occurred on the processor.This parameter is processor architecture specific. - @param SystemContext A pointer to the processor context when - the interrupt occurred on the processor. - - @return None - -**/ -typedef -VOID -(EFIAPI *EFI_CPU_INTERRUPT_HANDLER)( - IN CONST EFI_EXCEPTION_TYPE InterruptType, - IN CONST EFI_SYSTEM_CONTEXT SystemContext - ); - -/** - This function flushes the range of addresses from Start to Start+Length - from the processor's data cache. If Start is not aligned to a cache line - boundary, then the bytes before Start to the preceding cache line boundary - are also flushed. If Start+Length is not aligned to a cache line boundary, - then the bytes past Start+Length to the end of the next cache line boundary - are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be - supported. If the data cache is fully coherent with all DMA operations, then - this function can just return EFI_SUCCESS. If the processor does not support - flushing a range of the data cache, then the entire data cache can be flushed. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param Start The beginning physical address to flush from the processor's data - cache. - @param Length The number of bytes to flush from the processor's data cache. This - function may flush more bytes than Length specifies depending upon - the granularity of the flush operation that the processor supports. - @param FlushType Specifies the type of flush operation to perform. - - @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from - the processor's data cache. - @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified - by FlushType. - @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed - from the processor's data cache. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_FLUSH_DATA_CACHE)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_PHYSICAL_ADDRESS Start, - IN UINT64 Length, - IN EFI_CPU_FLUSH_TYPE FlushType - ); - - -/** - This function enables interrupt processing by the processor. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - - @retval EFI_SUCCESS Interrupts are enabled on the processor. - @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_ENABLE_INTERRUPT)( - IN EFI_CPU_ARCH_PROTOCOL *This - ); - - -/** - This function disables interrupt processing by the processor. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - - @retval EFI_SUCCESS Interrupts are disabled on the processor. - @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_DISABLE_INTERRUPT)( - IN EFI_CPU_ARCH_PROTOCOL *This - ); - - -/** - This function retrieves the processor's current interrupt state a returns it in - State. If interrupts are currently enabled, then TRUE is returned. If interrupts - are currently disabled, then FALSE is returned. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param State A pointer to the processor's current interrupt state. Set to TRUE if - interrupts are enabled and FALSE if interrupts are disabled. - - @retval EFI_SUCCESS The processor's current interrupt state was returned in State. - @retval EFI_INVALID_PARAMETER State is NULL. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_GET_INTERRUPT_STATE)( - IN EFI_CPU_ARCH_PROTOCOL *This, - OUT BOOLEAN *State - ); - - -/** - This function generates an INIT on the processor. If this function succeeds, then the - processor will be reset, and control will not be returned to the caller. If InitType is - not supported by this processor, or the processor cannot programmatically generate an - INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error - occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param InitType The type of processor INIT to perform. - - @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen. - @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported - by this processor. - @retval EFI_DEVICE_ERROR The processor INIT failed. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_INIT)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_CPU_INIT_TYPE InitType - ); - - -/** - This function registers and enables the handler specified by InterruptHandler for a processor - interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the - handler for the processor interrupt or exception type specified by InterruptType is uninstalled. - The installed handler is called once for each processor interrupt or exception. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts - are enabled and FALSE if interrupts are disabled. - @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called - when a processor interrupt occurs. If this parameter is NULL, then the handler - will be uninstalled. - - @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. - @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was - previously installed. - @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not - previously installed. - @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_REGISTER_INTERRUPT_HANDLER)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_EXCEPTION_TYPE InterruptType, - IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler - ); - - -/** - This function reads the processor timer specified by TimerIndex and returns it in TimerValue. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param TimerIndex Specifies which processor timer is to be returned in TimerValue. This parameter - must be between 0 and NumberOfTimers-1. - @param TimerValue Pointer to the returned timer value. - @param TimerPeriod A pointer to the amount of time that passes in femtoseconds for each increment - of TimerValue. If TimerValue does not increment at a predictable rate, then 0 is - returned. This parameter is optional and may be NULL. - - @retval EFI_SUCCESS The processor timer value specified by TimerIndex was returned in TimerValue. - @retval EFI_DEVICE_ERROR An error occurred attempting to read one of the processor's timers. - @retval EFI_INVALID_PARAMETER TimerValue is NULL or TimerIndex is not valid. - @retval EFI_UNSUPPORTED The processor does not have any readable timers. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_GET_TIMER_VALUE)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN UINT32 TimerIndex, - OUT UINT64 *TimerValue, - OUT UINT64 *TimerPeriod OPTIONAL - ); - - -/** - This function modifies the attributes for the memory region specified by BaseAddress and - Length from their current attributes to the attributes specified by Attributes. - - @param This The EFI_CPU_ARCH_PROTOCOL instance. - @param BaseAddress The physical address that is the start address of a memory region. - @param Length The size in bytes of the memory region. - @param Attributes The bit mask of attributes to set for the memory region. - - @retval EFI_SUCCESS The attributes were set for the memory region. - @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by - BaseAddress and Length cannot be modified. - @retval EFI_INVALID_PARAMETER Length is zero. - Attributes specified an illegal combination of attributes that - cannot be set together. - @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of - the memory resource range. - @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory - resource range specified by BaseAddress and Length. - The bit mask of attributes is not support for the memory resource - range specified by BaseAddress and Length. - -**/ -typedef -EFI_STATUS -(EFIAPI *EFI_CPU_SET_MEMORY_ATTRIBUTES)( - IN EFI_CPU_ARCH_PROTOCOL *This, - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length, - IN UINT64 Attributes - ); - - -/// -/// The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE -/// Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt -/// vectors and exception vectors, reading internal processor timers, resetting the processor, and -/// determining the processor frequency. -/// -struct _EFI_CPU_ARCH_PROTOCOL { - EFI_CPU_FLUSH_DATA_CACHE FlushDataCache; - EFI_CPU_ENABLE_INTERRUPT EnableInterrupt; - EFI_CPU_DISABLE_INTERRUPT DisableInterrupt; - EFI_CPU_GET_INTERRUPT_STATE GetInterruptState; - EFI_CPU_INIT Init; - EFI_CPU_REGISTER_INTERRUPT_HANDLER RegisterInterruptHandler; - EFI_CPU_GET_TIMER_VALUE GetTimerValue; - EFI_CPU_SET_MEMORY_ATTRIBUTES SetMemoryAttributes; - /// - /// The number of timers that are available in a processor. The value in this - /// field is a constant that must not be modified after the CPU Architectural - /// Protocol is installed. All consumers must treat this as a read-only field. - /// - UINT32 NumberOfTimers; - /// - /// The size, in bytes, of the alignment required for DMA buffer allocations. - /// This is typically the size of the largest data cache line in the platform. - /// The value in this field is a constant that must not be modified after the - /// CPU Architectural Protocol is installed. All consumers must treat this as - /// a read-only field. - /// - UINT32 DmaBufferAlignment; -}; - -extern EFI_GUID gEfiCpuArchProtocolGuid; - -#endif diff --git a/src/include/ipxe/efi/Protocol/DevicePath.h b/src/include/ipxe/efi/Protocol/DevicePath.h index d35b65fa9..d406b2868 100644 --- a/src/include/ipxe/efi/Protocol/DevicePath.h +++ b/src/include/ipxe/efi/Protocol/DevicePath.h @@ -5,7 +5,7 @@ from a software point of view. The path must persist from boot to boot, so it can not contain things like PCI bus numbers that change from boot to boot. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -511,7 +511,7 @@ typedef struct { UINT16 HBAPortNumber; /// /// The Port multiplier port number that facilitates the connection - /// to the device. Bit 15 should be set if the device is directly + /// to the device. Must be set to 0xFFFF if the device is directly /// connected to the HBA. /// UINT16 PortMultiplierPortNumber; @@ -856,6 +856,15 @@ typedef struct { UINT8 SlotNumber; } SD_DEVICE_PATH; +/// +/// EMMC (Embedded MMC) Device Path SubType. +/// +#define MSG_EMMC_DP 0x1D +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 SlotNumber; +} EMMC_DEVICE_PATH; + /// /// iSCSI Device Path SubType /// @@ -1241,6 +1250,7 @@ typedef union { WIFI_DEVICE_PATH WiFi; UFS_DEVICE_PATH Ufs; SD_DEVICE_PATH Sd; + EMMC_DEVICE_PATH Emmc; HARDDRIVE_DEVICE_PATH HardDrive; CDROM_DEVICE_PATH CD; @@ -1297,6 +1307,7 @@ typedef union { WIFI_DEVICE_PATH *WiFi; UFS_DEVICE_PATH *Ufs; SD_DEVICE_PATH *Sd; + EMMC_DEVICE_PATH *Emmc; HARDDRIVE_DEVICE_PATH *HardDrive; CDROM_DEVICE_PATH *CD; diff --git a/src/include/ipxe/efi/Protocol/HiiConfigAccess.h b/src/include/ipxe/efi/Protocol/HiiConfigAccess.h index 17ce3fdcc..df9080591 100644 --- a/src/include/ipxe/efi/Protocol/HiiConfigAccess.h +++ b/src/include/ipxe/efi/Protocol/HiiConfigAccess.h @@ -5,7 +5,7 @@ This protocol is published by drivers providing and requesting configuration data from HII. It may only be invoked by HII. -Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -36,6 +36,7 @@ typedef UINTN EFI_BROWSER_ACTION; #define EFI_BROWSER_ACTION_RETRIEVE 2 #define EFI_BROWSER_ACTION_FORM_OPEN 3 #define EFI_BROWSER_ACTION_FORM_CLOSE 4 +#define EFI_BROWSER_ACTION_SUBMITTED 5 #define EFI_BROWSER_ACTION_DEFAULT_STANDARD 0x1000 #define EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING 0x1001 #define EFI_BROWSER_ACTION_DEFAULT_SAFE 0x1002 diff --git a/src/include/ipxe/efi/Protocol/HiiDatabase.h b/src/include/ipxe/efi/Protocol/HiiDatabase.h index cbc0108b0..e070d29de 100644 --- a/src/include/ipxe/efi/Protocol/HiiDatabase.h +++ b/src/include/ipxe/efi/Protocol/HiiDatabase.h @@ -216,7 +216,7 @@ EFI_STATUS @param Handle An array of EFI_HII_HANDLE instances returned. - @retval EFI_SUCCESS The matching handles are outputed successfully. + @retval EFI_SUCCESS The matching handles are outputted successfully. HandleBufferLength is updated with the actual length. @retval EFI_BUFFER_TOO_SMALL The HandleBufferLength parameter indicates that Handle is too @@ -275,7 +275,7 @@ EFI_STATUS @retval EFI_OUT_OF_RESOURCES BufferSize is too small to hold the package. - @retval EFI_NOT_FOUND The specifiecd Handle could not be found in the + @retval EFI_NOT_FOUND The specified Handle could not be found in the current database. @retval EFI_INVALID_PARAMETER BufferSize was NULL. diff --git a/src/include/ipxe/efi/Protocol/HiiImage.h b/src/include/ipxe/efi/Protocol/HiiImage.h index b18d51a61..ba934a9f9 100644 --- a/src/include/ipxe/efi/Protocol/HiiImage.h +++ b/src/include/ipxe/efi/Protocol/HiiImage.h @@ -1,7 +1,7 @@ /** @file The file provides services to access to images in the images database. - Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -17,6 +17,8 @@ FILE_LICENCE ( BSD3 ); +#include + #define EFI_HII_IMAGE_PROTOCOL_GUID \ { 0x31a6406a, 0x6bdf, 0x4e46, { 0xb2, 0xa2, 0xeb, 0xaa, 0x89, 0xc4, 0x9, 0x20 } } diff --git a/src/include/ipxe/efi/Protocol/LoadFile.h b/src/include/ipxe/efi/Protocol/LoadFile.h index 99387e89f..ba80fdc16 100644 --- a/src/include/ipxe/efi/Protocol/LoadFile.h +++ b/src/include/ipxe/efi/Protocol/LoadFile.h @@ -7,7 +7,7 @@ UEFI 2.0 can boot from any device that produces a LoadFile protocol. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -66,7 +66,7 @@ typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE_INTERFACE; @retval EFI_NO_RESPONSE The remote system did not respond. @retval EFI_NOT_FOUND The file was not found. @retval EFI_ABORTED The file load process was manually cancelled. - + @retval EFI_WARN_FILE_SYSTEM The resulting Buffer contains UEFI-compliant file system. **/ typedef EFI_STATUS diff --git a/src/include/ipxe/efi/Protocol/Mtftp4.h b/src/include/ipxe/efi/Protocol/Mtftp4.h index 0e961cfd4..bc0a8396d 100644 --- a/src/include/ipxe/efi/Protocol/Mtftp4.h +++ b/src/include/ipxe/efi/Protocol/Mtftp4.h @@ -1,5 +1,5 @@ /** @file - EFI Multicast Trivial File Tranfer Protocol Definition + EFI Multicast Trivial File Transfer Protocol Definition Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under @@ -214,7 +214,7 @@ EFI_STATUS ); /** - Timeout callback funtion. + Timeout callback function. @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. @param Token The token that is provided in the diff --git a/src/include/ipxe/efi/Protocol/SerialIo.h b/src/include/ipxe/efi/Protocol/SerialIo.h index a96e5e945..130a6ecdc 100644 --- a/src/include/ipxe/efi/Protocol/SerialIo.h +++ b/src/include/ipxe/efi/Protocol/SerialIo.h @@ -4,7 +4,7 @@ Abstraction of a basic serial device. Targeted at 16550 UART, but could be much more generic. - Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -107,7 +107,7 @@ EFI_STATUS /** Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, - data buts, and stop bits on a serial device. + data bits, and stop bits on a serial device. @param This Protocol instance pointer. @param BaudRate The requested baud rate. A BaudRate value of 0 will use the diff --git a/src/include/ipxe/efi/Protocol/SimpleNetwork.h b/src/include/ipxe/efi/Protocol/SimpleNetwork.h index 2b521a9de..2faa668f3 100644 --- a/src/include/ipxe/efi/Protocol/SimpleNetwork.h +++ b/src/include/ipxe/efi/Protocol/SimpleNetwork.h @@ -9,7 +9,7 @@ MCast - MultiCast ... -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -123,6 +123,25 @@ typedef struct { /// UINT64 UnsupportedProtocol; + /// + /// Number of valid frames received that were duplicated. + /// + UINT64 RxDuplicatedFrames; + + /// + /// Number of encrypted frames received that failed to decrypt. + /// + UINT64 RxDecryptErrorFrames; + + /// + /// Number of frames that failed to transmit after exceeding the retry limit. + /// + UINT64 TxErrorFrames; + + /// + /// Number of frames transmitted successfully after more than one attempt. + /// + UINT64 TxRetryFrames; } EFI_NETWORK_STATISTICS; /// diff --git a/src/include/ipxe/efi/Protocol/SimpleTextIn.h b/src/include/ipxe/efi/Protocol/SimpleTextIn.h index 571ecaf30..e6d0eb24d 100644 --- a/src/include/ipxe/efi/Protocol/SimpleTextIn.h +++ b/src/include/ipxe/efi/Protocol/SimpleTextIn.h @@ -48,7 +48,6 @@ typedef struct { // // Required unicode control chars // -#define CHAR_NULL 0x0000 #define CHAR_BACKSPACE 0x0008 #define CHAR_TAB 0x0009 #define CHAR_LINEFEED 0x000A diff --git a/src/include/ipxe/efi/Protocol/SimpleTextOut.h b/src/include/ipxe/efi/Protocol/SimpleTextOut.h index 8aa36c35a..54d38b393 100644 --- a/src/include/ipxe/efi/Protocol/SimpleTextOut.h +++ b/src/include/ipxe/efi/Protocol/SimpleTextOut.h @@ -162,7 +162,7 @@ typedef EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SIMPLE_TEXT_OUTPUT_INTERFACE; Reset the text output device hardware and optionaly run diagnostics @param This The protocol instance pointer. - @param ExtendedVerification Driver may perform more exhaustive verfication + @param ExtendedVerification Driver may perform more exhaustive verification operation of the device during reset. @retval EFI_SUCCESS The text output device was reset. diff --git a/src/include/ipxe/efi/Uefi/UefiBaseType.h b/src/include/ipxe/efi/Uefi/UefiBaseType.h index 371dae649..5bfcccf34 100644 --- a/src/include/ipxe/efi/Uefi/UefiBaseType.h +++ b/src/include/ipxe/efi/Uefi/UefiBaseType.h @@ -1,8 +1,8 @@ /** @file Defines data types and constants introduced in UEFI. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
-Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+Portions copyright (c) 2011 - 2016, ARM Ltd. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. @@ -89,16 +89,12 @@ typedef struct { /// /// 4-byte buffer. An IPv4 internet protocol address. /// -typedef struct { - UINT8 Addr[4]; -} EFI_IPv4_ADDRESS; +typedef IPv4_ADDRESS EFI_IPv4_ADDRESS; /// /// 16-byte buffer. An IPv6 internet protocol address. /// -typedef struct { - UINT8 Addr[16]; -} EFI_IPv6_ADDRESS; +typedef IPv6_ADDRESS EFI_IPv6_ADDRESS; /// /// 32-byte buffer containing a network Media Access Control address. @@ -153,12 +149,14 @@ typedef union { #define EFI_END_OF_FILE RETURN_END_OF_FILE #define EFI_INVALID_LANGUAGE RETURN_INVALID_LANGUAGE #define EFI_COMPROMISED_DATA RETURN_COMPROMISED_DATA +#define EFI_HTTP_ERROR RETURN_HTTP_ERROR #define EFI_WARN_UNKNOWN_GLYPH RETURN_WARN_UNKNOWN_GLYPH #define EFI_WARN_DELETE_FAILURE RETURN_WARN_DELETE_FAILURE #define EFI_WARN_WRITE_FAILURE RETURN_WARN_WRITE_FAILURE #define EFI_WARN_BUFFER_TOO_SMALL RETURN_WARN_BUFFER_TOO_SMALL #define EFI_WARN_STALE_DATA RETURN_WARN_STALE_DATA +#define EFI_WARN_FILE_SYSTEM RETURN_WARN_FILE_SYSTEM ///@} /// diff --git a/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h b/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h index 49ea24ff9..88c026201 100644 --- a/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h +++ b/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h @@ -3,7 +3,8 @@ IFR is primarily consumed by the EFI presentation engine, and produced by EFI internal application and drivers as well as all add-in card option-ROM drivers -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -211,6 +212,7 @@ typedef struct _EFI_HII_FONT_PACKAGE_HDR { #define EFI_HII_GIBT_GLYPHS 0x11 #define EFI_HII_GIBT_GLYPH_DEFAULT 0x12 #define EFI_HII_GIBT_GLYPHS_DEFAULT 0x13 +#define EFI_HII_GIBT_GLYPH_VARIABILITY 0x14 #define EFI_HII_GIBT_DUPLICATE 0x20 #define EFI_HII_GIBT_SKIP2 0x21 #define EFI_HII_GIBT_SKIP1 0x22 @@ -283,6 +285,13 @@ typedef struct _EFI_HII_GIBT_GLYPHS_DEFAULT_BLOCK { UINT8 BitmapData[1]; } EFI_HII_GIBT_GLYPHS_DEFAULT_BLOCK; +typedef struct _EFI_HII_GIBT_VARIABILITY_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + EFI_HII_GLYPH_INFO Cell; + UINT8 GlyphPackInBits; + UINT8 BitmapData [1]; +} EFI_HII_GIBT_VARIABILITY_BLOCK; + typedef struct _EFI_HII_GIBT_SKIP1_BLOCK { EFI_HII_GLYPH_BLOCK Header; UINT8 SkipCount; @@ -491,6 +500,7 @@ typedef struct _EFI_HII_IMAGE_BLOCK { #define EFI_HII_IIBT_IMAGE_24BIT 0x16 #define EFI_HII_IIBT_IMAGE_24BIT_TRANS 0x17 #define EFI_HII_IIBT_IMAGE_JPEG 0x18 +#define EFI_HII_IIBT_IMAGE_PNG 0x19 #define EFI_HII_IIBT_DUPLICATE 0x20 #define EFI_HII_IIBT_SKIP2 0x21 #define EFI_HII_IIBT_SKIP1 0x22 @@ -611,6 +621,12 @@ typedef struct _EFI_HII_IIBT_JPEG_BLOCK { UINT8 Data[1]; } EFI_HII_IIBT_JPEG_BLOCK; +typedef struct _EFI_HII_IIBT_PNG_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT32 Size; + UINT8 Data[1]; +} EFI_HII_IIBT_PNG_BLOCK; + typedef struct _EFI_HII_IIBT_SKIP1_BLOCK { EFI_HII_IMAGE_BLOCK Header; UINT8 SkipCount; @@ -2112,4 +2128,10 @@ typedef struct _EFI_HII_AIBT_SKIP2_BLOCK { /// #define STRING_TOKEN(t) t +/// +/// IMAGE_TOKEN is not defined in UEFI specification. But it is placed +/// here for the easy access by C files and VFR source files. +/// +#define IMAGE_TOKEN(t) t + #endif diff --git a/src/include/ipxe/efi/Uefi/UefiPxe.h b/src/include/ipxe/efi/Uefi/UefiPxe.h index 5c0b2038f..6ed5c9a22 100644 --- a/src/include/ipxe/efi/Uefi/UefiPxe.h +++ b/src/include/ipxe/efi/Uefi/UefiPxe.h @@ -3,7 +3,7 @@ structure prototypes, global variables and constants that are needed for porting PXE to EFI. -Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -1081,7 +1081,7 @@ typedef struct s_pxe_cpb_start_31 { /// /// protocol driver can provide anything for this Unique_ID, UNDI remembers - /// that as just a 64bit value assocaited to the interface specified by + /// that as just a 64bit value associated to the interface specified by /// the ifnum and gives it back as a parameter to all the call-back routines /// when calling for that interface! /// @@ -1460,6 +1460,26 @@ typedef struct s_pxe_db_statistics { /// #define PXE_STATISTICS_UNSUPPORTED_PROTOCOL 0x15 +/// +/// Number of valid frames received that were duplicated. +/// +#define PXE_STATISTICS_RX_DUPLICATED_FRAMES 0x16 + +/// +/// Number of encrypted frames received that failed to decrypt. +/// +#define PXE_STATISTICS_RX_DECRYPT_ERROR_FRAMES 0x17 + +/// +/// Number of frames that failed to transmit after exceeding the retry limit. +/// +#define PXE_STATISTICS_TX_ERROR_FRAMES 0x18 + +/// +/// Number of frames transmitted successfully after more than one attempt. +/// +#define PXE_STATISTICS_TX_RETRY_FRAMES 0x19 + typedef struct s_pxe_cpb_mcast_ip_to_mac { /// /// Multicast IP address to be converted to multicast MAC address. diff --git a/src/include/ipxe/efi/Uefi/UefiSpec.h b/src/include/ipxe/efi/Uefi/UefiSpec.h index ee276ffed..27edf43ac 100644 --- a/src/include/ipxe/efi/Uefi/UefiSpec.h +++ b/src/include/ipxe/efi/Uefi/UefiSpec.h @@ -1,11 +1,11 @@ /** @file Include file that supports UEFI. - This include file must contain things defined in the UEFI 2.5 specification. - If a code construct is defined in the UEFI 2.5 specification it must be included + This include file must contain things defined in the UEFI 2.6 specification. + If a code construct is defined in the UEFI 2.6 specification it must be included by this include file. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -138,8 +138,7 @@ typedef struct { MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders - that are provided by operating system vendors. The only illegal - memory type values are those in the range EfiMaxMemoryType..0x6FFFFFFF. + that are provided by operating system vendors. @param[in] Pages The number of contiguous 4 KB pages to allocate. @param[in, out] Memory The pointer to a physical address. On input, the way in which the address is used depends on the value of Type. @@ -150,7 +149,7 @@ typedef struct { 2) MemoryType is in the range EfiMaxMemoryType..0x6FFFFFFF. 3) Memory is NULL. - 4) MemoryType was EfiPersistentMemory. + 4) MemoryType is EfiPersistentMemory. @retval EFI_OUT_OF_RESOURCES The pages could not be allocated. @retval EFI_NOT_FOUND The requested pages could not be found. @@ -225,16 +224,16 @@ EFI_STATUS MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders - that are provided by operating system vendors. The only illegal - memory type values are those in the range EfiMaxMemoryType..0x6FFFFFFF. + that are provided by operating system vendors. @param[in] Size The number of bytes to allocate from the pool. @param[out] Buffer A pointer to a pointer to the allocated buffer if the call succeeds; undefined otherwise. @retval EFI_SUCCESS The requested number of bytes was allocated. @retval EFI_OUT_OF_RESOURCES The pool requested could not be allocated. - @retval EFI_INVALID_PARAMETER PoolType was invalid or Buffer is NULL. - PoolType was EfiPersistentMemory. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. + PoolType is EfiPersistentMemory. **/ typedef @@ -630,7 +629,8 @@ VOID attributes bitmask for the variable. @param[in, out] DataSize On input, the size in bytes of the return Data buffer. On output the size of data returned in Data. - @param[out] Data The buffer to return the contents of the variable. + @param[out] Data The buffer to return the contents of the variable. May be NULL + with a zero DataSize in order to determine the size buffer needed. @retval EFI_SUCCESS The function completed successfully. @retval EFI_NOT_FOUND The variable was not found. @@ -650,7 +650,7 @@ EFI_STATUS IN EFI_GUID *VendorGuid, OUT UINT32 *Attributes, OPTIONAL IN OUT UINTN *DataSize, - OUT VOID *Data + OUT VOID *Data OPTIONAL ); /** @@ -1006,11 +1006,15 @@ EFI_STATUS @param[in] ResetType The type of reset to perform. @param[in] ResetStatus The status code for the reset. - @param[in] DataSize The size, in bytes, of WatchdogData. + @param[in] DataSize The size, in bytes, of ResetData. @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or EfiResetShutdown the data buffer starts with a Null-terminated string, optionally followed by additional binary data. - + The string is a description that the caller may use to further + indicate the reason for the system reset. ResetData is only + valid if ResetStatus is something other than EFI_SUCCESS + unless the ResetType is EfiResetPlatformSpecific + where a minimum amount of ResetData is always required. **/ typedef VOID @@ -1752,11 +1756,13 @@ EFI_STATUS #define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004 #define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED 0x0000000000000008 #define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED 0x0000000000000010 +#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY 0x0000000000000040 // // EFI Runtime Services Table // #define EFI_SYSTEM_TABLE_SIGNATURE SIGNATURE_64 ('I','B','I',' ','S','Y','S','T') +#define EFI_2_60_SYSTEM_TABLE_REVISION ((2 << 16) | (60)) #define EFI_2_50_SYSTEM_TABLE_REVISION ((2 << 16) | (50)) #define EFI_2_40_SYSTEM_TABLE_REVISION ((2 << 16) | (40)) #define EFI_2_31_SYSTEM_TABLE_REVISION ((2 << 16) | (31)) @@ -1766,7 +1772,7 @@ EFI_STATUS #define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) #define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) #define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) -#define EFI_SYSTEM_TABLE_REVISION EFI_2_50_SYSTEM_TABLE_REVISION +#define EFI_SYSTEM_TABLE_REVISION EFI_2_60_SYSTEM_TABLE_REVISION #define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION #define EFI_RUNTIME_SERVICES_SIGNATURE SIGNATURE_64 ('R','U','N','T','S','E','R','V') diff --git a/src/include/ipxe/efi/X64/ProcessorBind.h b/src/include/ipxe/efi/X64/ProcessorBind.h index 8ce9d0fb1..9f02e0c5b 100644 --- a/src/include/ipxe/efi/X64/ProcessorBind.h +++ b/src/include/ipxe/efi/X64/ProcessorBind.h @@ -29,6 +29,19 @@ FILE_LICENCE ( BSD3 ); #pragma pack() #endif +#if defined(__GNUC__) && defined(__pic__) && !defined(USING_LTO) +// +// Mark all symbol declarations and references as hidden, meaning they will +// not be subject to symbol preemption. This allows the compiler to refer to +// symbols directly using relative references rather than via the GOT, which +// contains absolute symbol addresses that are subject to runtime relocation. +// +// The LTO linker will not emit GOT based relocations when all symbol +// references can be resolved locally, and so there is no need to set the +// pragma in that case (and doing so will cause other issues). +// +#pragma GCC visibility push (hidden) +#endif #if defined(__INTEL_COMPILER) // @@ -82,7 +95,7 @@ FILE_LICENCE ( BSD3 ); #pragma warning ( disable : 4057 ) // -// ASSERT(FALSE) or while (TRUE) are legal constructes so supress this warning +// ASSERT(FALSE) or while (TRUE) are legal constructs so suppress this warning // #pragma warning ( disable : 4127 ) @@ -96,7 +109,7 @@ FILE_LICENCE ( BSD3 ); // #pragma warning ( disable : 4206 ) -#if _MSC_VER == 1800 +#if _MSC_VER == 1800 || _MSC_VER == 1900 // // Disable these warnings for VS2013. @@ -104,13 +117,13 @@ FILE_LICENCE ( BSD3 ); // // This warning is for potentially uninitialized local variable, and it may cause false -// positive issues in VS2013 build +// positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4701 ) // // This warning is for potentially uninitialized local pointer variable, and it may cause -// false positive issues in VS2013 build +// false positive issues in VS2013 and VS2015 build // #pragma warning ( disable : 4703 ) @@ -121,7 +134,7 @@ FILE_LICENCE ( BSD3 ); #if defined(_MSC_EXTENSIONS) // - // use Microsoft C complier dependent integer width types + // use Microsoft C compiler dependent integer width types // /// @@ -260,6 +273,12 @@ typedef INT64 INTN; /// #define CPU_STACK_ALIGNMENT 16 +/// +/// Page allocation granularity for x64 +/// +#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000) +#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000) + // // Modifier to ensure that all protocol member functions and EFI intrinsics // use the correct C calling convention. All protocol member functions and diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 40d3beb12..669e5364a 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -26,6 +26,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* EFI headers rudely redefine NULL */ #undef NULL +/* EFI headers redefine ARRAY_SIZE */ +#undef ARRAY_SIZE + /* EFI headers expect ICC to define __GNUC__ */ #if defined ( __ICC ) && ! defined ( __GNUC__ ) #define __GNUC__ 1 @@ -154,9 +157,12 @@ struct efi_config_table { #define EEFI( efirc ) EPLATFORM ( EINFO_EPLATFORM, efirc ) extern EFI_GUID efi_absolute_pointer_protocol_guid; +extern EFI_GUID efi_acpi_table_protocol_guid; +extern EFI_GUID efi_apple_net_boot_protocol_guid; extern EFI_GUID efi_arp_protocol_guid; extern EFI_GUID efi_arp_service_binding_protocol_guid; extern EFI_GUID efi_block_io_protocol_guid; +extern EFI_GUID efi_block_io2_protocol_guid; extern EFI_GUID efi_bus_specific_driver_override_protocol_guid; extern EFI_GUID efi_component_name_protocol_guid; extern EFI_GUID efi_component_name2_protocol_guid; @@ -205,10 +211,14 @@ extern EFI_GUID efi_usb2_hc_protocol_guid; extern EFI_GUID efi_usb_io_protocol_guid; extern EFI_GUID efi_vlan_config_protocol_guid; +extern EFI_GUID efi_file_info_id; +extern EFI_GUID efi_file_system_info_id; + extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; extern EFI_SYSTEM_TABLE *efi_systab; +extern int efi_shutdown_in_progress; extern const __attribute__ (( pure )) char * efi_guid_ntoa ( EFI_GUID *guid ); extern const __attribute__ (( pure )) char * diff --git a/src/include/ipxe/efi/efi_acpi.h b/src/include/ipxe/efi/efi_acpi.h new file mode 100644 index 000000000..01456f137 --- /dev/null +++ b/src/include/ipxe/efi/efi_acpi.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFI_ACPI_H +#define _IPXE_EFI_ACPI_H + +/** @file + * + * iPXE ACPI API for EFI + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef ACPI_EFI +#define ACPI_PREFIX_efi +#else +#define ACPI_PREFIX_efi __efi_ +#endif + +#endif /* _IPXE_EFI_ACPI_H */ diff --git a/src/include/ipxe/efi/efi_block.h b/src/include/ipxe/efi/efi_block.h new file mode 100644 index 000000000..f8cf7fc13 --- /dev/null +++ b/src/include/ipxe/efi/efi_block.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFI_BLOCK_H +#define _IPXE_EFI_BLOCK_H + +/** @block + * + * EFI block device protocols + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef SANBOOT_EFI +#define SANBOOT_PREFIX_efi +#else +#define SANBOOT_PREFIX_efi __efi_ +#endif + +#endif /* _IPXE_EFI_BLOCK_H */ diff --git a/src/include/ipxe/efi/efi_entropy.h b/src/include/ipxe/efi/efi_entropy.h index 39a667355..5b16fd7f9 100644 --- a/src/include/ipxe/efi/efi_entropy.h +++ b/src/include/ipxe/efi/efi_entropy.h @@ -22,14 +22,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @ret min_entropy min-entropy of each sample */ -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) { /* We use essentially the same mechanism as for the BIOS * RTC-based entropy source, and so assume the same * min-entropy per sample. */ - return 1.3; + return MIN_ENTROPY ( 1.3 ); } #endif /* _IPXE_EFI_ENTROPY_H */ diff --git a/src/include/ipxe/efi/efi_file.h b/src/include/ipxe/efi/efi_file.h index e4db0305a..79c073cf1 100644 --- a/src/include/ipxe/efi/efi_file.h +++ b/src/include/ipxe/efi/efi_file.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + extern int efi_file_install ( EFI_HANDLE handle ); extern void efi_file_uninstall ( EFI_HANDLE handle ); diff --git a/src/include/ipxe/efi/efi_snp.h b/src/include/ipxe/efi/efi_snp.h index 4c5461ec4..9076f1d56 100644 --- a/src/include/ipxe/efi/efi_snp.h +++ b/src/include/ipxe/efi/efi_snp.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include #include #include @@ -57,6 +59,10 @@ struct efi_snp_device { EFI_HII_CONFIG_ACCESS_PROTOCOL hii; /** HII package list */ EFI_HII_PACKAGE_LIST_HEADER *package_list; + /** EFI child handle for HII association */ + EFI_HANDLE hii_child_handle; + /** Device path of HII child handle */ + EFI_DEVICE_PATH_PROTOCOL *hii_child_path; /** HII handle */ EFI_HII_HANDLE hii_handle; /** Device name */ diff --git a/src/include/ipxe/efi/efi_strings.h b/src/include/ipxe/efi/efi_strings.h index 2f241537e..a8ace45e7 100644 --- a/src/include/ipxe/efi/efi_strings.h +++ b/src/include/ipxe/efi/efi_strings.h @@ -20,4 +20,27 @@ extern int efi_vssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, extern int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ); +/** + * Write a formatted string to a wide-character buffer + * + * @v wbuf Buffer into which to write the string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret wlen Length of formatted string (in wide characters) + */ +static inline int efi_vsprintf ( wchar_t *buf, const char *fmt, va_list args ) { + return efi_vsnprintf ( buf, ~( ( size_t ) 0 ), fmt, args ); +} + +/** + * Write a formatted string to a buffer + * + * @v wbuf Buffer into which to write the string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret wlen Length of formatted string (in wide characters) + */ +#define efi_sprintf( buf, fmt, ... ) \ + efi_snprintf ( (buf), ~( ( size_t ) 0 ), (fmt), ## __VA_ARGS__ ) + #endif /* _IPXE_EFI_STRINGS_H */ diff --git a/src/include/ipxe/efi/efi_timer.h b/src/include/ipxe/efi/efi_timer.h deleted file mode 100644 index c03765393..000000000 --- a/src/include/ipxe/efi/efi_timer.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _IPXE_EFI_TIMER_H -#define _IPXE_EFI_TIMER_H - -/** @file - * - * iPXE timer API for EFI - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_EFI -#define TIMER_PREFIX_efi -#else -#define TIMER_PREFIX_efi __efi_ -#endif - -#endif /* _IPXE_EFI_TIMER_H */ diff --git a/src/include/ipxe/entropy.h b/src/include/ipxe/entropy.h index beeb3abfa..d2e3ce501 100644 --- a/src/include/ipxe/entropy.h +++ b/src/include/ipxe/entropy.h @@ -52,6 +52,25 @@ typedef uint8_t noise_sample_t; /** An entropy sample */ typedef uint8_t entropy_sample_t; +/** An amount of min-entropy + * + * Expressed as a fixed-point quantity in order to avoid floating + * point calculations. + */ +typedef unsigned int min_entropy_t; + +/** Fixed-point scale for min-entropy amounts */ +#define MIN_ENTROPY_SCALE ( 1 << 16 ) + +/** + * Construct a min-entropy fixed-point value + * + * @v bits min-entropy in bits + * @ret min_entropy min-entropy as a fixed-point value + */ +#define MIN_ENTROPY( bits ) \ + ( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) ) + /* Include all architecture-independent entropy API headers */ #include #include @@ -87,7 +106,7 @@ void entropy_disable ( void ); * * This must be a compile-time constant. */ -double min_entropy_per_sample ( void ); +min_entropy_t min_entropy_per_sample ( void ); /** * Get noise sample @@ -142,7 +161,7 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, /* Sanity checks */ linker_assert ( ( min_entropy_per_sample() <= - ( 8 * sizeof ( noise_sample_t ) ) ), + MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ), min_entropy_per_sample_is_impossibly_high ); linker_assert ( ( min_entropy_bits <= ( 8 * max_len ) ), entropy_buffer_too_small ); @@ -151,7 +170,8 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 ); /* Calculate number of samples required to contain sufficient entropy */ - min_samples = ( ( min_entropy_bits * 1.0 ) / min_entropy_per_sample() ); + min_samples = ( MIN_ENTROPY ( min_entropy_bits ) / + min_entropy_per_sample() ); /* Round up to a whole number of samples. We don't have the * ceil() function available, so do the rounding by hand. diff --git a/src/include/ipxe/eoib.h b/src/include/ipxe/eoib.h new file mode 100644 index 000000000..93f496c36 --- /dev/null +++ b/src/include/ipxe/eoib.h @@ -0,0 +1,103 @@ +#ifndef _IPXE_EOIB_H +#define _IPXE_EOIB_H + +/** @file + * + * Ethernet over Infiniband + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** An EoIB header */ +struct eoib_header { + /** Signature */ + uint16_t magic; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** EoIB magic signature */ +#define EOIB_MAGIC 0x8919 + +/** An EoIB device */ +struct eoib_device { + /** Name */ + const char *name; + /** Network device */ + struct net_device *netdev; + /** Underlying Infiniband device */ + struct ib_device *ibdev; + /** List of EoIB devices */ + struct list_head list; + /** Broadcast address */ + struct ib_address_vector broadcast; + + /** Completion queue */ + struct ib_completion_queue *cq; + /** Queue pair */ + struct ib_queue_pair *qp; + /** Broadcast group membership */ + struct ib_mc_membership membership; + + /** Peer cache */ + struct list_head peers; + + /** Send duplicate packet to gateway (or NULL) + * + * @v eoib EoIB device + * @v original Original I/O buffer + */ + void ( * duplicate ) ( struct eoib_device *eoib, + struct io_buffer *original ); + /** Gateway (if any) */ + struct ib_address_vector gateway; + /** Multicast group additional component mask */ + unsigned int mask; +}; + +/** + * Check if EoIB device uses a gateway + * + * @v eoib EoIB device + * @v has_gw EoIB device uses a gateway + */ +static inline int eoib_has_gateway ( struct eoib_device *eoib ) { + + return ( eoib->duplicate != NULL ); +} + +/** + * Force creation of multicast group + * + * @v eoib EoIB device + */ +static inline void eoib_force_group_creation ( struct eoib_device *eoib ) { + + /* Some dubious EoIB implementations require each endpoint to + * force the creation of the multicast group. Yes, this makes + * it impossible for the group parameters (e.g. SL) to ever be + * modified without breaking backwards compatiblity with every + * existing driver. + */ + eoib->mask = ( IB_SA_MCMEMBER_REC_PKEY | IB_SA_MCMEMBER_REC_QKEY | + IB_SA_MCMEMBER_REC_SL | IB_SA_MCMEMBER_REC_FLOW_LABEL | + IB_SA_MCMEMBER_REC_TRAFFIC_CLASS ); +} + +extern int eoib_create ( struct ib_device *ibdev, const uint8_t *hw_addr, + struct ib_address_vector *broadcast, + const char *name ); +extern struct eoib_device * eoib_find ( struct ib_device *ibdev, + const uint8_t *hw_addr ); +extern void eoib_destroy ( struct eoib_device *eoib ); +extern void eoib_set_gateway ( struct eoib_device *eoib, + struct ib_address_vector *av ); + +#endif /* _IPXE_EOIB_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 651adbaee..8e989e42d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -70,6 +70,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_ansicoldef ( ERRFILE_CORE | 0x001e0000 ) #define ERRFILE_fault ( ERRFILE_CORE | 0x001f0000 ) #define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) +#define ERRFILE_pixbuf ( ERRFILE_CORE | 0x00210000 ) +#define ERRFILE_efi_block ( ERRFILE_CORE | 0x00220000 ) +#define ERRFILE_sanboot ( ERRFILE_CORE | 0x00230000 ) +#define ERRFILE_dummy_sanboot ( ERRFILE_CORE | 0x00240000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) @@ -183,6 +187,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_smsc75xx ( ERRFILE_DRIVER | 0x00770000 ) #define ERRFILE_intelvf ( ERRFILE_DRIVER | 0x00780000 ) #define ERRFILE_intelxvf ( ERRFILE_DRIVER | 0x00790000 ) +#define ERRFILE_smsc95xx ( ERRFILE_DRIVER | 0x007a0000 ) +#define ERRFILE_acm ( ERRFILE_DRIVER | 0x007b0000 ) +#define ERRFILE_eoib ( ERRFILE_DRIVER | 0x007c0000 ) +#define ERRFILE_golan ( ERRFILE_DRIVER | 0x007d0000 ) +#define ERRFILE_flexboot_nodnic ( ERRFILE_DRIVER | 0x007e0000 ) +#define ERRFILE_virtio_pci ( ERRFILE_DRIVER | 0x007f0000 ) +#define ERRFILE_pciea ( ERRFILE_DRIVER | 0x00c00000 ) +#define ERRFILE_axge ( ERRFILE_DRIVER | 0x00c10000 ) +#define ERRFILE_thunderx ( ERRFILE_DRIVER | 0x00c20000 ) +#define ERRFILE_af_packet ( ERRFILE_DRIVER | 0x00c30000 ) +#define ERRFILE_sfc_hunt ( ERRFILE_DRIVER | 0x00c40000 ) +#define ERRFILE_efx_hunt ( ERRFILE_DRIVER | 0x00c50000 ) +#define ERRFILE_exanic ( ERRFILE_DRIVER | 0x00c60000 ) +#define ERRFILE_smscusb ( ERRFILE_DRIVER | 0x00c70000 ) +#define ERRFILE_lan78xx ( ERRFILE_DRIVER | 0x00c80000 ) +#define ERRFILE_ena ( ERRFILE_DRIVER | 0x00c90000 ) +#define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) @@ -256,6 +277,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_peerdisc ( ERRFILE_NET | 0x00450000 ) #define ERRFILE_peerblk ( ERRFILE_NET | 0x00460000 ) #define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) +#define ERRFILE_xsigo ( ERRFILE_NET | 0x00480000 ) +#define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 ) +#define ERRFILE_httpntlm ( ERRFILE_NET | 0x004a0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) @@ -265,6 +289,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_embedded ( ERRFILE_IMAGE | 0x00050000 ) #define ERRFILE_pnm ( ERRFILE_IMAGE | 0x00060000 ) #define ERRFILE_png ( ERRFILE_IMAGE | 0x00070000 ) +#define ERRFILE_der ( ERRFILE_IMAGE | 0x00080000 ) +#define ERRFILE_pem ( ERRFILE_IMAGE | 0x00090000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) @@ -343,6 +369,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_pxe ( ERRFILE_OTHER | 0x004a0000 ) #define ERRFILE_efi_usb ( ERRFILE_OTHER | 0x004b0000 ) #define ERRFILE_efi_fbcon ( ERRFILE_OTHER | 0x004c0000 ) +#define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) +#define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) +#define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) +#define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) +#define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 ) /** @} */ diff --git a/src/include/ipxe/eth_slow.h b/src/include/ipxe/eth_slow.h index f6d731b3b..754ea6e1f 100644 --- a/src/include/ipxe/eth_slow.h +++ b/src/include/ipxe/eth_slow.h @@ -190,6 +190,12 @@ struct eth_slow_lacp_entity_tlv { */ #define LACP_STATE_EXPIRED 0x80 +/** LACP fast interval (1 second) */ +#define LACP_INTERVAL_FAST 1 + +/** LACP slow interval (30 seconds) */ +#define LACP_INTERVAL_SLOW 30 + /** LACP collector TLV */ struct eth_slow_lacp_collector_tlv { /** TLV header */ diff --git a/src/include/ipxe/http.h b/src/include/ipxe/http.h index a0dff7d00..0893c9537 100644 --- a/src/include/ipxe/http.h +++ b/src/include/ipxe/http.h @@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include struct http_transaction; @@ -150,14 +151,18 @@ struct http_request_content { size_t len; }; -/** HTTP request authentication descriptor */ -struct http_request_auth { - /** Authentication scheme (if any) */ - struct http_authentication *auth; +/** HTTP request Basic authentication descriptor */ +struct http_request_auth_basic { /** Username */ const char *username; /** Password */ const char *password; +}; + +/** HTTP request Digest authentication descriptor */ +struct http_request_auth_digest { + /** Username */ + const char *username; /** Quality of protection */ const char *qop; /** Algorithm */ @@ -168,6 +173,33 @@ struct http_request_auth { char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; }; +/** HTTP request NTLM authentication descriptor */ +struct http_request_auth_ntlm { + /** Username */ + const char *username; + /** LAN Manager response */ + struct ntlm_lm_response lm; + /** NT response */ + struct ntlm_nt_response nt; + /** Authenticate message length */ + size_t len; +}; + +/** HTTP request authentication descriptor */ +struct http_request_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Per-scheme information */ + union { + /** Basic authentication descriptor */ + struct http_request_auth_basic basic; + /** Digest authentication descriptor */ + struct http_request_auth_digest digest; + /** NTLM authentication descriptor */ + struct http_request_auth_ntlm ntlm; + }; +}; + /** An HTTP request * * This represents a single request to be sent to a server, including @@ -235,10 +267,12 @@ struct http_response_content { struct http_content_encoding *encoding; }; -/** HTTP response authorization descriptor */ -struct http_response_auth { - /** Authentication scheme (if any) */ - struct http_authentication *auth; +/** HTTP response Basic authorization descriptor */ +struct http_response_auth_basic { +}; + +/** HTTP response Digest authorization descriptor */ +struct http_response_auth_digest { /** Realm */ const char *realm; /** Quality of protection */ @@ -251,6 +285,29 @@ struct http_response_auth { const char *opaque; }; +/** HTTP response NTLM authorization descriptor */ +struct http_response_auth_ntlm { + /** Challenge message */ + struct ntlm_challenge *challenge; + /** Challenge information */ + struct ntlm_challenge_info info; +}; + +/** HTTP response authorization descriptor */ +struct http_response_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Per-scheme information */ + union { + /** Basic authorization descriptor */ + struct http_response_auth_basic basic; + /** Digest authorization descriptor */ + struct http_response_auth_digest digest; + /** NTLM authorization descriptor */ + struct http_response_auth_ntlm ntlm; + }; +}; + /** An HTTP response * * This represents a single response received from the server, @@ -461,6 +518,13 @@ struct http_content_encoding { struct http_authentication { /** Name (e.g. "Digest") */ const char *name; + /** Parse remaining "WWW-Authenticate" header line + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ + int ( * parse ) ( struct http_transaction *http, char *line ); /** Perform authentication * * @v http HTTP transaction diff --git a/src/include/ipxe/hyperv.h b/src/include/ipxe/hyperv.h index c61e2a083..9b7e54a51 100644 --- a/src/include/ipxe/hyperv.h +++ b/src/include/ipxe/hyperv.h @@ -37,6 +37,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define HV_GUEST_OS_ID_IPXE ( ( 1ULL << 63 ) | ( 0x18aeULL << 48 ) ) +/** Guest OS identity for Gen 2 UEFI firmware + * + * This does not conform to the documented structure for guest OS + * identities. + */ +#define HV_GUEST_OS_ID_UEFI ( 1ULL << 40 ) + /** Enable hypercall page */ #define HV_HYPERCALL_ENABLE 0x00000001UL @@ -61,6 +68,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Synthetic interrupt vector mask */ #define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff ) +/** Maximum synthetic interrupt number */ +#define HV_SINT_MAX 15 + /** Post message */ #define HV_POST_MESSAGE 0x005c diff --git a/src/include/ipxe/ib_cmrc.h b/src/include/ipxe/ib_cmrc.h index 47ad27fa6..f3276e6ef 100644 --- a/src/include/ipxe/ib_cmrc.h +++ b/src/include/ipxe/ib_cmrc.h @@ -12,9 +12,8 @@ FILE_LICENCE ( BSD2 ); #include #include -extern int ib_cmrc_open ( struct interface *xfer, - struct ib_device *ibdev, - union ib_gid *dgid, - union ib_guid *service_id ); +extern int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, + union ib_gid *dgid, union ib_guid *service_id, + const char *name ); #endif /* _IPXE_IB_CMRC_H */ diff --git a/src/include/ipxe/ib_mad.h b/src/include/ipxe/ib_mad.h index ae1eea7e4..134274026 100644 --- a/src/include/ipxe/ib_mad.h +++ b/src/include/ipxe/ib_mad.h @@ -144,6 +144,9 @@ struct ib_port_info { #define IB_LINK_SPEED_SDR 0x01 #define IB_LINK_SPEED_DDR 0x02 #define IB_LINK_SPEED_QDR 0x04 +#define IB_LINK_SPEED_FDR10 0x08 +#define IB_LINK_SPEED_FDR 0x10 +#define IB_LINK_SPEED_EDR 0x20 #define IB_PORT_STATE_DOWN 0x01 #define IB_PORT_STATE_INIT 0x02 @@ -216,8 +219,25 @@ struct ib_sa_hdr { uint32_t comp_mask[2]; } __attribute__ (( packed )); -#define IB_SA_ATTR_MC_MEMBER_REC 0x38 +#define IB_SA_ATTR_SERVICE_REC 0x31 #define IB_SA_ATTR_PATH_REC 0x35 +#define IB_SA_ATTR_MC_MEMBER_REC 0x38 + +struct ib_service_record { + uint64_t id; + union ib_gid gid; + uint16_t pkey; + uint16_t reserved; + uint32_t lease; + uint8_t key[16]; + char name[64]; + uint8_t data8[16]; + uint16_t data16[8]; + uint32_t data32[4]; + uint64_t data64[2]; +} __attribute__ (( packed )); + +#define IB_SA_SERVICE_REC_NAME (1<<6) struct ib_path_record { uint32_t reserved0[2]; @@ -275,6 +295,7 @@ struct ib_mc_member_record { #define IB_SA_MCMEMBER_REC_PROXY_JOIN (1<<17) union ib_sa_data { + struct ib_service_record service_record; struct ib_path_record path_record; struct ib_mc_member_record mc_member_record; } __attribute__ (( packed )); @@ -504,6 +525,12 @@ union ib_mad_class_specific { struct ib_smp_class_specific smp; } __attribute__ (( packed )); +/** A management datagram transaction identifier */ +struct ib_mad_tid { + uint32_t high; + uint32_t low; +} __attribute__ (( packed )); + /** A management datagram common header * * Defined in section 13.4.2 of the IBA. @@ -515,7 +542,7 @@ struct ib_mad_hdr { uint8_t method; uint16_t status; union ib_mad_class_specific class_specific; - uint32_t tid[2]; + struct ib_mad_tid tid; uint16_t attr_id; uint8_t reserved[2]; uint32_t attr_mod; diff --git a/src/include/ipxe/ib_mcast.h b/src/include/ipxe/ib_mcast.h index 564066975..df348bd9b 100644 --- a/src/include/ipxe/ib_mcast.h +++ b/src/include/ipxe/ib_mcast.h @@ -17,30 +17,25 @@ struct ib_mad_transaction; struct ib_mc_membership { /** Queue pair */ struct ib_queue_pair *qp; - /** Multicast GID */ - union ib_gid gid; + /** Address vector */ + struct ib_address_vector *av; + /** Attached to multicast GID */ + int attached; /** Multicast group join transaction */ struct ib_mad_transaction *madx; /** Handle join success/failure * - * @v ibdev Infiniband device - * @v qp Queue pair * @v membership Multicast group membership * @v rc Status code - * @v mad Response MAD (or NULL on error) */ - void ( * complete ) ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_mc_membership *membership, int rc, - union ib_mad *mad ); + void ( * complete ) ( struct ib_mc_membership *membership, int rc ); }; extern int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership, - union ib_gid *gid, - void ( * joined ) ( struct ib_device *ibdev, - struct ib_queue_pair *qp, - struct ib_mc_membership *memb, - int rc, union ib_mad *mad ) ); + struct ib_address_vector *av, unsigned int mask, + void ( * joined ) ( struct ib_mc_membership *memb, + int rc ) ); extern void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership ); diff --git a/src/include/ipxe/ib_mi.h b/src/include/ipxe/ib_mi.h index c7c8143ba..bd087cd35 100644 --- a/src/include/ipxe/ib_mi.h +++ b/src/include/ipxe/ib_mi.h @@ -127,8 +127,9 @@ ib_create_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi, extern void ib_destroy_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi, struct ib_mad_transaction *madx ); -extern struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, - enum ib_queue_pair_type type ); +extern int ib_create_mi ( struct ib_device *ibdev, + enum ib_queue_pair_type type, + struct ib_mad_interface **new_mi ); extern void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ); diff --git a/src/include/ipxe/ib_packet.h b/src/include/ipxe/ib_packet.h index f275fcb09..747f96399 100644 --- a/src/include/ipxe/ib_packet.h +++ b/src/include/ipxe/ib_packet.h @@ -19,6 +19,7 @@ union ib_guid { uint8_t bytes[8]; uint16_t words[4]; uint32_t dwords[2]; + uint64_t qword; }; /** Infiniband Globally Unique Identifier debug message format */ @@ -33,6 +34,7 @@ union ib_gid { uint8_t bytes[16]; uint16_t words[8]; uint32_t dwords[4]; + uint64_t qwords[2]; struct { union ib_guid prefix; union ib_guid guid; @@ -46,6 +48,9 @@ union ib_gid { #define IB_GID_ARGS( gid ) \ IB_GUID_ARGS ( &(gid)->s.prefix ), IB_GUID_ARGS ( &(gid)->s.guid ) +/** Test for multicast GID */ +#define IB_GID_MULTICAST( gid ) ( (gid)->bytes[0] == 0xff ) + /** An Infiniband Local Route Header */ struct ib_local_route_header { /** Virtual lane and link version */ diff --git a/src/include/ipxe/ib_service.h b/src/include/ipxe/ib_service.h new file mode 100644 index 000000000..88afe4e65 --- /dev/null +++ b/src/include/ipxe/ib_service.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_IB_SERVICE_H +#define _IPXE_IB_SERVICE_H + +/** @file + * + * Infiniband service records + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +extern struct ib_mad_transaction * +ib_create_service_madx ( struct ib_device *ibdev, + struct ib_mad_interface *mi, const char *name, + struct ib_mad_transaction_operations *op ); + +#endif /* _IPXE_IB_SERVICE_H */ diff --git a/src/include/ipxe/ibft.h b/src/include/ipxe/ibft.h index 35f15105c..51ce781a6 100644 --- a/src/include/ipxe/ibft.h +++ b/src/include/ipxe/ibft.h @@ -49,6 +49,9 @@ FILE_LICENCE ( BSD2 ); /** iSCSI Boot Firmware Table signature */ #define IBFT_SIG ACPI_SIGNATURE ( 'i', 'B', 'F', 'T' ) +/** Alignment of structures within iBFT */ +#define IBFT_ALIGN 16 + /** An offset from the start of the iBFT */ typedef uint16_t ibft_off_t; @@ -97,6 +100,20 @@ struct ibft_header { uint8_t flags; } __attribute__ (( packed )); +/** + * iBFT NIC and Target offset pair + * + * There is no implicit relation between the NIC and the Target, but + * using this structure simplifies the table construction code while + * matching the expected table layout. + */ +struct ibft_offset_pair { + /** Offset to NIC structure */ + ibft_off_t nic; + /** Offset to Target structure */ + ibft_off_t target; +} __attribute__ (( packed )); + /** * iBFT Control structure * @@ -108,14 +125,8 @@ struct ibft_control { uint16_t extensions; /** Offset to Initiator structure */ ibft_off_t initiator; - /** Offset to NIC structure for NIC 0 */ - ibft_off_t nic_0; - /** Offset to Target structure for target 0 */ - ibft_off_t target_0; - /** Offset to NIC structure for NIC 1 */ - ibft_off_t nic_1; - /** Offset to Target structure for target 1 */ - ibft_off_t target_1; + /** Offsets to NIC and Target structures */ + struct ibft_offset_pair pair[2]; } __attribute__ (( packed )); /** Structure ID for Control section */ @@ -262,18 +273,13 @@ struct ibft_target { */ struct ibft_table { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** Reserved */ uint8_t reserved[12]; /** Control structure */ struct ibft_control control; } __attribute__ (( packed )); -struct iscsi_session; -struct net_device; - -extern int ibft_describe ( struct iscsi_session *iscsi, - struct acpi_description_header *acpi, - size_t len ); +extern struct acpi_model ibft_model __acpi_model; #endif /* _IPXE_IBFT_H */ diff --git a/src/include/ipxe/if_arp.h b/src/include/ipxe/if_arp.h index 4eb1f80b7..9d7b03fe8 100644 --- a/src/include/ipxe/if_arp.h +++ b/src/include/ipxe/if_arp.h @@ -99,4 +99,14 @@ static inline void * arp_target_pa ( struct arphdr *arphdr ) { return ( arp_target_ha ( arphdr ) + arphdr->ar_hln ); } +/** ARP packet length + * + * @v arphdr ARP header + * @ret len Length (including header) + */ +static inline size_t arp_len ( struct arphdr *arphdr ) { + return ( sizeof ( *arphdr ) + + ( 2 * ( arphdr->ar_hln + arphdr->ar_pln ) ) ); +} + #endif /* _IPXE_IF_ARP_H */ diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index 6abd7a2d2..2e7eb4cee 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -17,6 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct uri; struct pixel_buffer; +struct asn1_cursor; struct image_type; /** An executable image */ @@ -99,6 +100,19 @@ struct image_type { * @ret rc Return status code */ int ( * pixbuf ) ( struct image *image, struct pixel_buffer **pixbuf ); + /** + * Extract ASN.1 object from image + * + * @v image Image + * @v offset Offset within image + * @v cursor ASN.1 cursor to fill in + * @ret next Offset to next image, or negative error + * + * The caller is responsible for eventually calling free() on + * the allocated ASN.1 cursor. + */ + int ( * asn1 ) ( struct image *image, size_t offset, + struct asn1_cursor **cursor ); }; /** @@ -158,6 +172,7 @@ static inline struct image * first_image ( void ) { } extern struct image * alloc_image ( struct uri *uri ); +extern int image_set_uri ( struct image *image, struct uri *uri ); extern int image_set_name ( struct image *image, const char *name ); extern int image_set_cmdline ( struct image *image, const char *cmdline ); extern int register_image ( struct image *image ); @@ -169,6 +184,8 @@ extern int image_select ( struct image *image ); extern struct image * image_find_selected ( void ); extern int image_set_trust ( int require_trusted, int permanent ); extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ); +extern int image_asn1 ( struct image *image, size_t offset, + struct asn1_cursor **cursor ); /** * Increment reference count on an image diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index 0ebf441c2..3044d6316 100644 --- a/src/include/ipxe/in.h +++ b/src/include/ipxe/in.h @@ -69,8 +69,12 @@ struct in6_addr { ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ htons ( 0xfe80 ) ) -#define IN6_IS_ADDR_NONGLOBAL( addr ) \ - ( IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MULTICAST (addr) ) +#define IN6_IS_ADDR_SITELOCAL( addr ) \ + ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ + htons ( 0xfec0 ) ) + +#define IN6_IS_ADDR_ULA( addr ) \ + ( ( *( ( const uint8_t * ) (addr) ) & 0xfe ) == 0xfc ) /** * IPv4 socket address diff --git a/src/include/ipxe/infiniband.h b/src/include/ipxe/infiniband.h index 87cfe5082..6f4951f17 100644 --- a/src/include/ipxe/infiniband.h +++ b/src/include/ipxe/infiniband.h @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** Subnet management interface QPN */ #define IB_QPN_SMI 0 @@ -158,6 +159,8 @@ struct ib_queue_pair { struct ib_device *ibdev; /** List of queue pairs on this Infiniband device */ struct list_head list; + /** Queue pair name */ + const char *name; /** Queue pair number */ unsigned long qpn; /** Externally-visible queue pair number @@ -388,6 +391,9 @@ struct ib_device_operations { union ib_mad *mad ); }; +/** Maximum length of an Infiniband device name */ +#define IBDEV_NAME_LEN 8 + /** An Infiniband device */ struct ib_device { /** Reference counter */ @@ -396,6 +402,10 @@ struct ib_device { struct list_head list; /** List of open Infiniband devices */ struct list_head open_list; + /** Index of this Infiniband device */ + unsigned int index; + /** Name of this Infiniband device */ + char name[IBDEV_NAME_LEN]; /** Underlying device */ struct device *dev; /** List of completion queues */ @@ -448,10 +458,11 @@ struct ib_device { /** General services interface */ struct ib_mad_interface *gsi; + /** IPoIB LEMAC (if non-default) */ + uint8_t lemac[ETH_ALEN]; + /** Driver private data */ void *drv_priv; - /** Owner private data */ - void *owner_priv; }; /** An Infiniband upper-layer driver */ @@ -482,18 +493,20 @@ struct ib_driver { /** Declare an Infiniband driver */ #define __ib_driver __table_entry ( IB_DRIVERS, 01 ) -extern struct ib_completion_queue * -ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, - struct ib_completion_queue_operations *op ); +extern int ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, + struct ib_completion_queue_operations *op, + struct ib_completion_queue **new_cq ); extern void ib_destroy_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ); extern void ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ); -extern struct ib_queue_pair * -ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, - unsigned int num_send_wqes, struct ib_completion_queue *send_cq, - unsigned int num_recv_wqes, struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op ); +extern int ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, + unsigned int num_send_wqes, + struct ib_completion_queue *send_cq, + unsigned int num_recv_wqes, + struct ib_completion_queue *recv_cq, + struct ib_queue_pair_operations *op, + const char *name, struct ib_queue_pair **new_qp ); extern int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ); extern void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ); @@ -695,26 +708,4 @@ ib_get_drvdata ( struct ib_device *ibdev ) { return ibdev->drv_priv; } -/** - * Set Infiniband device owner-private data - * - * @v ibdev Infiniband device - * @v priv Private data - */ -static inline __always_inline void -ib_set_ownerdata ( struct ib_device *ibdev, void *priv ) { - ibdev->owner_priv = priv; -} - -/** - * Get Infiniband device owner-private data - * - * @v ibdev Infiniband device - * @ret priv Private data - */ -static inline __always_inline void * -ib_get_ownerdata ( struct ib_device *ibdev ) { - return ibdev->owner_priv; -} - #endif /* _IPXE_INFINIBAND_H */ diff --git a/src/include/ipxe/interface.h b/src/include/ipxe/interface.h index a8d823775..b65002c80 100644 --- a/src/include/ipxe/interface.h +++ b/src/include/ipxe/interface.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include /** An object interface operation */ @@ -123,6 +124,11 @@ struct interface { struct refcnt *refcnt; /** Interface descriptor */ struct interface_descriptor *desc; + /** Original interface descriptor + * + * Used by intf_reinit(). + */ + struct interface_descriptor *original; }; extern void intf_plug ( struct interface *intf, struct interface *dest ); @@ -143,7 +149,11 @@ extern void intf_close ( struct interface *intf, int rc ); typeof ( void ( object_type, int rc ) ) extern void intf_shutdown ( struct interface *intf, int rc ); +extern void intfs_vshutdown ( va_list intfs, int rc ); +extern void intfs_shutdown ( int rc, ... ) __attribute__ (( sentinel )); extern void intf_restart ( struct interface *intf, int rc ); +extern void intfs_vrestart ( va_list intfs, int rc ); +extern void intfs_restart ( int rc, ... ) __attribute__ (( sentinel )); extern void intf_poke ( struct interface *intf, void ( type ) ( struct interface *intf ) ); @@ -166,6 +176,7 @@ static inline void intf_init ( struct interface *intf, intf->dest = &null_intf; intf->refcnt = refcnt; intf->desc = desc; + intf->original = desc; } /** @@ -177,6 +188,7 @@ static inline void intf_init ( struct interface *intf, .dest = &null_intf, \ .refcnt = NULL, \ .desc = &(descriptor), \ + .original = &(descriptor), \ } /** @@ -236,4 +248,15 @@ static inline void intf_init ( struct interface *intf, */ #define INTF_INTF_DBG( intf, dest ) INTF_DBG ( intf ), INTF_DBG ( dest ) +/** + * Reinitialise an object interface + * + * @v intf Object interface + */ +static inline void intf_reinit ( struct interface *intf ) { + + /* Restore original interface descriptor */ + intf->desc = intf->original; +} + #endif /* _IPXE_INTERFACE_H */ diff --git a/src/include/ipxe/io.h b/src/include/ipxe/io.h index af767915d..fe1388191 100644 --- a/src/include/ipxe/io.h +++ b/src/include/ipxe/io.h @@ -20,8 +20,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include -#include /** Page size */ #define PAGE_SIZE ( 1 << PAGE_SHIFT ) @@ -196,30 +196,6 @@ static inline __always_inline void * bus_to_virt ( unsigned long bus_addr ) { return phys_to_virt ( bus_to_phys ( bus_addr ) ); } -/** - * Map bus address as an I/O address - * - * @v bus_addr Bus address - * @v len Length of region - * @ret io_addr I/O address - */ -void * ioremap ( unsigned long bus_addr, size_t len ); - -/** - * Unmap I/O address - * - * @v io_addr I/O address - */ -void iounmap ( volatile const void *io_addr ); - -/** - * Convert I/O address to bus address (for debug only) - * - * @v io_addr I/O address - * @ret bus_addr Bus address - */ -unsigned long io_to_bus ( volatile const void *io_addr ); - /** * Read byte from memory-mapped device * diff --git a/src/include/ipxe/iobuf.h b/src/include/ipxe/iobuf.h index 27d285d44..b40ade350 100644 --- a/src/include/ipxe/iobuf.h +++ b/src/include/ipxe/iobuf.h @@ -20,7 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * necessary. This is used on behalf of hardware that is not capable * of auto-padding. */ -#define IOB_ZLEN 64 +#define IOB_ZLEN 128 /** * A persistent I/O buffer diff --git a/src/include/ipxe/iomap.h b/src/include/ipxe/iomap.h new file mode 100644 index 000000000..b8ded38ef --- /dev/null +++ b/src/include/ipxe/iomap.h @@ -0,0 +1,78 @@ +#ifndef _IPXE_IOMAP_H +#define _IPXE_IOMAP_H + +/** @file + * + * iPXE I/O mapping API + * + * The I/O mapping API provides methods for mapping and unmapping I/O + * devices. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** + * Calculate static inline I/O mapping API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define IOMAP_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( IOMAP_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an I/O mapping API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_IOMAP( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( IOMAP_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline I/O mapping API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_IOMAP_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( IOMAP_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Map bus address as an I/O address + * + * @v bus_addr Bus address + * @v len Length of region + * @ret io_addr I/O address + */ +void * ioremap ( unsigned long bus_addr, size_t len ); + +/** + * Unmap I/O address + * + * @v io_addr I/O address + */ +void iounmap ( volatile const void *io_addr ); + +/** + * Convert I/O address to bus address (for debug only) + * + * @v io_addr I/O address + * @ret bus_addr Bus address + */ +unsigned long io_to_bus ( volatile const void *io_addr ); + +#endif /* _IPXE_IOMAP_H */ diff --git a/src/include/ipxe/iomap_virt.h b/src/include/ipxe/iomap_virt.h new file mode 100644 index 000000000..4962b7c37 --- /dev/null +++ b/src/include/ipxe/iomap_virt.h @@ -0,0 +1,33 @@ +#ifndef _IPXE_IOMAP_VIRT_H +#define _IPXE_IOMAP_VIRT_H + +/** @file + * + * iPXE I/O mapping API using phys_to_virt() + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOMAP_VIRT +#define IOMAP_PREFIX_virt +#else +#define IOMAP_PREFIX_virt __virt_ +#endif + +static inline __always_inline void * +IOMAP_INLINE ( virt, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { + return ( bus_addr ? phys_to_virt ( bus_addr ) : NULL ); +} + +static inline __always_inline void +IOMAP_INLINE ( virt, iounmap ) ( volatile const void *io_addr __unused ) { + /* Nothing to do */ +} + +static inline __always_inline unsigned long +IOMAP_INLINE ( virt, io_to_bus ) ( volatile const void *io_addr ) { + return virt_to_phys ( io_addr ); +} + +#endif /* _IPXE_IOMAP_VIRT_H */ diff --git a/src/include/ipxe/ipoib.h b/src/include/ipxe/ipoib.h index b34dd32d0..065eeabb7 100644 --- a/src/include/ipxe/ipoib.h +++ b/src/include/ipxe/ipoib.h @@ -62,5 +62,6 @@ struct ipoib_remac { extern const char * ipoib_ntoa ( const void *ll_addr ); extern struct net_device * alloc_ipoibdev ( size_t priv_size ); +extern struct net_device * ipoib_netdev ( struct ib_device *ibdev ); #endif /* _IPXE_IPOIB_H */ diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index b500382c1..4dd43f16d 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -25,6 +25,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** IPv6 maximum hop limit */ #define IPV6_HOP_LIMIT 0xff +/** IPv6 default prefix length */ +#define IPV6_DEFAULT_PREFIX_LEN 64 + +/** IPv6 maximum prefix length */ +#define IPV6_MAX_PREFIX_LEN 128 + /** IPv6 header */ struct ipv6_header { /** Version (4 bits), Traffic class (8 bits), Flow label (20 bits) */ @@ -152,6 +158,24 @@ struct ipv6_pseudo_header { uint8_t next_header; } __attribute__ (( packed )); +/** IPv6 address scopes */ +enum ipv6_address_scope { + /** Interface-local address scope */ + IPV6_SCOPE_INTERFACE_LOCAL = 0x1, + /** Link-local address scope */ + IPV6_SCOPE_LINK_LOCAL = 0x2, + /** Admin-local address scope */ + INV6_SCOPE_ADMIN_LOCAL = 0x4, + /** Site-local address scope */ + IPV6_SCOPE_SITE_LOCAL = 0x5, + /** Organisation-local address scope */ + IPV6_SCOPE_ORGANISATION_LOCAL = 0x8, + /** Global address scope */ + IPV6_SCOPE_GLOBAL = 0xe, + /** Maximum scope */ + IPV6_SCOPE_MAX = 0xf, +}; + /** An IPv6 address/routing table entry */ struct ipv6_miniroute { /** List of miniroutes */ @@ -168,6 +192,8 @@ struct ipv6_miniroute { struct in6_addr prefix_mask; /** Router address */ struct in6_addr router; + /** Scope */ + unsigned int scope; /** Flags */ unsigned int flags; }; @@ -238,15 +264,45 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) { addr->s6_addr[15] = 2; } +/** + * Get multicast address scope + * + * @v addr Multicast address + * @ret scope Address scope + */ +static inline unsigned int +ipv6_multicast_scope ( const struct in6_addr *addr ) { + + return ( addr->s6_addr[1] & 0x0f ); +} + +/** IPv6 settings sibling order */ +enum ipv6_settings_order { + /** No address */ + IPV6_ORDER_PREFIX_ONLY = -4, + /** Link-local address */ + IPV6_ORDER_LINK_LOCAL = -3, + /** Address assigned via SLAAC */ + IPV6_ORDER_SLAAC = -2, + /** Address assigned via DHCPv6 */ + IPV6_ORDER_DHCPV6 = -1, +}; + +/** IPv6 link-local address settings block name */ +#define IPV6_SETTINGS_NAME "link" + extern struct list_head ipv6_miniroutes; extern struct net_protocol ipv6_protocol __net_protocol; extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ); -extern int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ); -extern int ipv6_set_address ( struct net_device *netdev, - struct in6_addr *address ); +extern int ipv6_add_miniroute ( struct net_device *netdev, + struct in6_addr *address, + unsigned int prefix_len, + struct in6_addr *router ); +extern void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ); +extern struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, + struct in6_addr **dest ); extern int parse_ipv6_setting ( const struct setting_type *type, const char *value, void *buf, size_t len ); extern int format_ipv6_setting ( const struct setting_type *type, diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h index c75ff4188..966cf52b6 100644 --- a/src/include/ipxe/iscsi.h +++ b/src/include/ipxe/iscsi.h @@ -16,6 +16,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** Default iSCSI port */ #define ISCSI_PORT 3260 @@ -647,6 +649,8 @@ struct iscsi_session { struct sockaddr target_sockaddr; /** SCSI LUN (for boot firmware table) */ struct scsi_lun lun; + /** ACPI descriptor */ + struct acpi_descriptor desc; }; /** iSCSI session is currently in the security negotiation phase */ @@ -697,4 +701,7 @@ struct iscsi_session { /** Default initiator IQN prefix */ #define ISCSI_DEFAULT_IQN_PREFIX "iqn.2010-04.org.ipxe" +extern const struct setting +initiator_iqn_setting __setting ( SETTING_SANBOOT_EXTRA, initiator-iqn ); + #endif /* _IPXE_ISCSI_H */ diff --git a/src/include/ipxe/job.h b/src/include/ipxe/job.h index 7e1bd8109..c01bd1740 100644 --- a/src/include/ipxe/job.h +++ b/src/include/ipxe/job.h @@ -28,6 +28,8 @@ struct job_progress { * account before calculating @c completed/total. */ unsigned long total; + /** Message (optional) */ + char message[32]; }; extern int job_progress ( struct interface *intf, diff --git a/src/include/ipxe/linux/linux_entropy.h b/src/include/ipxe/linux/linux_entropy.h index afef6fe19..ea8c1f16c 100644 --- a/src/include/ipxe/linux/linux_entropy.h +++ b/src/include/ipxe/linux/linux_entropy.h @@ -20,7 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @ret min_entropy min-entropy of each sample */ -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { /* linux_get_noise() reads a single byte from /dev/random, @@ -28,7 +28,7 @@ ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { * entropy is available. We therefore assume that each sample * contains exactly 8 bits of entropy. */ - return 8.0; + return MIN_ENTROPY ( 8.0 ); } #endif /* _IPXE_LINUX_ENTROPY_H */ diff --git a/src/include/ipxe/linux/linux_timer.h b/src/include/ipxe/linux/linux_timer.h deleted file mode 100644 index 7f46e36b2..000000000 --- a/src/include/ipxe/linux/linux_timer.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _IPXE_LINUX_TIMER_H -#define _IPXE_LINUX_TIMER_H - -/** @file - * - * iPXE timer API for Linux - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef TIMER_LINUX -#define TIMER_PREFIX_linux -#else -#define TIMER_PREFIX_linux __linux_ -#endif - -#endif /* _IPXE_LINUX_TIMER_H */ diff --git a/src/include/ipxe/linux_compat.h b/src/include/ipxe/linux_compat.h deleted file mode 100644 index 4704c4817..000000000 --- a/src/include/ipxe/linux_compat.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _IPXE_LINUX_COMPAT_H -#define _IPXE_LINUX_COMPAT_H - -/** @file - * - * Linux code compatibility - * - * This file exists to ease the building of Linux source code within - * iPXE. This is intended to facilitate quick testing; it is not - * intended to be a substitute for proper porting. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include -#include -#include -#include - -#define __init -#define __exit -#define __initdata -#define __exitdata -#define printk printf - -#endif /* _IPXE_LINUX_COMPAT_H */ diff --git a/src/include/ipxe/list.h b/src/include/ipxe/list.h index 6a9b76f91..8de254984 100644 --- a/src/include/ipxe/list.h +++ b/src/include/ipxe/list.h @@ -348,6 +348,56 @@ extern void extern_list_splice_tail_init ( struct list_head *list, ( type * ) NULL : \ list_entry ( (list)->prev, type, member ) ) +/** + * Get the container of the next entry in a list + * + * @v pos Current list entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret next Next list entry, or NULL at end of list + */ +#define list_next_entry( pos, head, member ) ( { \ + typeof (pos) next = list_entry ( (pos)->member.next, \ + typeof ( *(pos) ), \ + member ); \ + ( ( &next->member == (head) ) ? NULL : next ); } ) + +/** + * Get the container of the previous entry in a list + * + * @v pos Current list entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret next Next list entry, or NULL at end of list + */ +#define list_prev_entry( pos, head, member ) ( { \ + typeof (pos) prev = list_entry ( (pos)->member.prev, \ + typeof ( *(pos) ), \ + member ); \ + ( ( &prev->member == (head) ) ? NULL : prev ); } ) + +/** + * Test if entry is first in a list + * + * @v entry List entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret is_first Entry is first in the list + */ +#define list_is_first_entry( entry, head, member ) \ + ( (head)->next == &(entry)->member ) + +/** + * Test if entry is last in a list + * + * @v entry List entry + * @v head List head + * @v member Name of list field within iterator's type + * @ret is_last Entry is last in the list + */ +#define list_is_last_entry( entry, head, member ) \ + ( (head)->prev == &(entry)->member ) + /** * Iterate over a list * diff --git a/src/include/ipxe/malloc.h b/src/include/ipxe/malloc.h index dd158b8e6..1878978fd 100644 --- a/src/include/ipxe/malloc.h +++ b/src/include/ipxe/malloc.h @@ -22,6 +22,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include extern size_t freemem; +extern size_t usedmem; +extern size_t maxusedmem; extern void * __malloc alloc_memblock ( size_t size, size_t align, size_t offset ); diff --git a/src/include/ipxe/md4.h b/src/include/ipxe/md4.h new file mode 100644 index 000000000..8f172e626 --- /dev/null +++ b/src/include/ipxe/md4.h @@ -0,0 +1,73 @@ +#ifndef _IPXE_MD4_H +#define _IPXE_MD4_H + +/** @file + * + * MD4 algorithm + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** An MD4 digest */ +struct md4_digest { + /** Hash output */ + uint32_t h[4]; +}; + +/** An MD4 data block */ +union md4_block { + /** Raw bytes */ + uint8_t byte[64]; + /** Raw dwords */ + uint32_t dword[16]; + /** Final block structure */ + struct { + /** Padding */ + uint8_t pad[56]; + /** Length in bits */ + uint64_t len; + } final; +}; + +/** MD4 digest and data block + * + * The order of fields within this structure is designed to minimise + * code size. + */ +struct md4_digest_data { + /** Digest of data already processed */ + struct md4_digest digest; + /** Accumulated data */ + union md4_block data; +} __attribute__ (( packed )); + +/** MD4 digest and data block */ +union md4_digest_data_dwords { + /** Digest and data block */ + struct md4_digest_data dd; + /** Raw dwords */ + uint32_t dword[ sizeof ( struct md4_digest_data ) / + sizeof ( uint32_t ) ]; +}; + +/** An MD4 context */ +struct md4_context { + /** Amount of accumulated data */ + size_t len; + /** Digest and accumulated data */ + union md4_digest_data_dwords ddd; +} __attribute__ (( packed )); + +/** MD4 context size */ +#define MD4_CTX_SIZE sizeof ( struct md4_context ) + +/** MD4 digest size */ +#define MD4_DIGEST_SIZE sizeof ( struct md4_digest ) + +extern struct digest_algorithm md4_algorithm; + +#endif /* _IPXE_MD4_H */ diff --git a/src/include/ipxe/mii.h b/src/include/ipxe/mii.h index c2245b49e..89fc92a4a 100644 --- a/src/include/ipxe/mii.h +++ b/src/include/ipxe/mii.h @@ -19,21 +19,24 @@ struct mii_operations { /** * Read from MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @ret data Data read, or negative error */ - int ( * read ) ( struct mii_interface *mii, unsigned int reg ); + int ( * read ) ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg ); /** * Write to MII register * - * @v mii MII interface + * @v mdio MII interface + * @v phy PHY address * @v reg Register address * @v data Data to write * @ret rc Return status code */ - int ( * write ) ( struct mii_interface *mii, unsigned int reg, - unsigned int data ); + int ( * write ) ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg, unsigned int data ); }; /** An MII interface */ @@ -42,49 +45,75 @@ struct mii_interface { struct mii_operations *op; }; +/** An MII device */ +struct mii_device { + /** MII interface */ + struct mii_interface *mdio; + /** PHY address */ + unsigned int address; +}; + /** * Initialise MII interface * - * @v mii MII interface + * @v mdio MII interface * @v op MII interface operations */ static inline __attribute__ (( always_inline )) void -mii_init ( struct mii_interface *mii, struct mii_operations *op ) { - mii->op = op; +mdio_init ( struct mii_interface *mdio, struct mii_operations *op ) { + mdio->op = op; +} + +/** + * Initialise MII device + * + * @v mii MII device + * @v mdio MII interface + * @v address PHY address + */ +static inline __attribute__ (( always_inline )) void +mii_init ( struct mii_device *mii, struct mii_interface *mdio, + unsigned int address ) { + mii->mdio = mdio; + mii->address = address; } /** * Read from MII register * - * @v mii MII interface + * @v mii MII device * @v reg Register address * @ret data Data read, or negative error */ static inline __attribute__ (( always_inline )) int -mii_read ( struct mii_interface *mii, unsigned int reg ) { - return mii->op->read ( mii, reg ); +mii_read ( struct mii_device *mii, unsigned int reg ) { + struct mii_interface *mdio = mii->mdio; + + return mdio->op->read ( mdio, mii->address, reg ); } /** * Write to MII register * - * @v mii MII interface + * @v mii MII device * @v reg Register address * @v data Data to write * @ret rc Return status code */ static inline __attribute__ (( always_inline )) int -mii_write ( struct mii_interface *mii, unsigned int reg, unsigned int data ) { - return mii->op->write ( mii, reg, data ); +mii_write ( struct mii_device *mii, unsigned int reg, unsigned int data ) { + struct mii_interface *mdio = mii->mdio; + + return mdio->op->write ( mdio, mii->address, reg, data ); } /** * Dump MII registers (for debugging) * - * @v mii MII interface + * @v mii MII device */ static inline void -mii_dump ( struct mii_interface *mii ) { +mii_dump ( struct mii_device *mii ) { unsigned int i; int data; @@ -112,9 +141,13 @@ mii_dump ( struct mii_interface *mii ) { /** Maximum time to wait for a reset, in milliseconds */ #define MII_RESET_MAX_WAIT_MS 500 -extern int mii_restart ( struct mii_interface *mii ); -extern int mii_reset ( struct mii_interface *mii ); -extern int mii_check_link ( struct mii_interface *mii, +/** Maximum PHY address */ +#define MII_MAX_PHY_ADDRESS 31 + +extern int mii_restart ( struct mii_device *mii ); +extern int mii_reset ( struct mii_device *mii ); +extern int mii_check_link ( struct mii_device *mii, struct net_device *netdev ); +extern int mii_find ( struct mii_device *mii ); #endif /* _IPXE_MII_H */ diff --git a/src/include/ipxe/mii_bit.h b/src/include/ipxe/mii_bit.h new file mode 100644 index 000000000..0f797e913 --- /dev/null +++ b/src/include/ipxe/mii_bit.h @@ -0,0 +1,55 @@ +#ifndef _IPXE_MII_BIT_H +#define _IPXE_MII_BIT_H + +/** @file + * + * MII bit-bashing interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +#define MII_BIT_START 0xffffffff /**< Start */ +#define MII_BIT_START_MASK 0x80000000 /**< Start mask */ + +#define MII_BIT_CMD_MASK 0x00000008 /**< Command mask */ +#define MII_BIT_CMD_READ 0x00000006 /**< Command read */ +#define MII_BIT_CMD_WRITE 0x00000005 /**< Command write */ +#define MII_BIT_CMD_RW 0x00000001 /**< Command read or write */ + +#define MII_BIT_PHY_MASK 0x00000010 /**< PHY mask */ + +#define MII_BIT_REG_MASK 0x00000010 /**< Register mask */ + +#define MII_BIT_SWITCH 0x00000002 /**< Switch */ +#define MII_BIT_SWITCH_MASK 0x00000002 /**< Switch mask */ + +#define MII_BIT_DATA_MASK 0x00008000 /**< Data mask */ + +/** A bit-bashing MII interface */ +struct mii_bit_basher { + /** MII interface */ + struct mii_interface mdio; + /** Bit-bashing interface */ + struct bit_basher basher; +}; + +/** Bit indices used for MII bit-bashing interface */ +enum { + /** MII clock */ + MII_BIT_MDC = 0, + /** MII data */ + MII_BIT_MDIO, + /** MII data direction */ + MII_BIT_DRIVE, +}; + +/** Delay between MDC transitions */ +#define MII_BIT_UDELAY 1 + +extern void init_mii_bit_basher ( struct mii_bit_basher *miibit ); + +#endif /* _IPXE_MII_BIT_H */ diff --git a/src/include/ipxe/netbios.h b/src/include/ipxe/netbios.h new file mode 100644 index 000000000..c11552556 --- /dev/null +++ b/src/include/ipxe/netbios.h @@ -0,0 +1,30 @@ +#ifndef _IPXE_NETBIOS_H +#define _IPXE_NETBIOS_H + +/** @file + * + * NetBIOS user names + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern const char * netbios_domain ( char **username ); + +/** + * Restore NetBIOS [domain\]username + * + * @v domain NetBIOS domain name + * @v username NetBIOS user name + * + * Restore the separator in a NetBIOS [domain\]username as split by + * netbios_domain(). + */ +static inline void netbios_domain_undo ( const char *domain, char *username ) { + + /* Restore separator, if applicable */ + if ( domain ) + username[-1] = '\\'; +} + +#endif /* _IPXE_NETBIOS_H */ diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index a1d207ffc..d498ab697 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -397,9 +397,16 @@ struct net_device { struct retry_timer link_block; /** Maximum packet length * - * This length includes any link-layer headers. + * This is the maximum packet length (including any link-layer + * headers) supported by the hardware. */ size_t max_pkt_len; + /** Maximum transmission unit length + * + * This is the maximum transmission unit length (excluding any + * link-layer headers) configured for the link. + */ + size_t mtu; /** TX packet queue */ struct list_head tx_queue; /** Deferred TX packet queue */ diff --git a/src/include/ipxe/ntlm.h b/src/include/ipxe/ntlm.h new file mode 100644 index 000000000..b0436c9ac --- /dev/null +++ b/src/include/ipxe/ntlm.h @@ -0,0 +1,199 @@ +#ifndef _IPXE_NTLM_H +#define _IPXE_NTLM_H + +/** @file + * + * NT LAN Manager (NTLM) authentication + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** A message header */ +struct ntlm_header { + /** Magic signature */ + uint8_t magic[8]; + /** Message type */ + uint32_t type; +} __attribute__ (( packed )); + +/** Magic signature */ +#define NTLM_MAGIC { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' } + +/** Message types */ +enum ntlm_type { + /** Negotiate message type */ + NTLM_NEGOTIATE = 0x00000001UL, + /** Challenge message type */ + NTLM_CHALLENGE = 0x00000002UL, + /** Authenticate message */ + NTLM_AUTHENTICATE = 0x00000003UL, +}; + +/** Negotiation flags */ +enum ntlm_flags { + /** Negotiate key exchange */ + NTLM_NEGOTIATE_KEY_EXCH = 0x20000000UL, + /** Negotiate extended security */ + NTLM_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000UL, + /** Negotiate always sign */ + NTLM_NEGOTIATE_ALWAYS_SIGN = 0x00008000UL, + /** Negotiate NTLM key */ + NTLM_NEGOTIATE_NTLM = 0x00000200UL, + /** Request target name and information */ + NTLM_REQUEST_TARGET = 0x00000004UL, + /** Negotiate Unicode character encoding */ + NTLM_NEGOTIATE_UNICODE = 0x00000001UL, +}; + +/** A version descriptor */ +struct ntlm_version { + /** Product major version */ + uint8_t major; + /** Product minor version */ + uint8_t minor; + /** Product build number */ + uint16_t build; + /** Reserved */ + uint8_t reserved[3]; + /** NTLMSSP revision */ + uint8_t revision; +} __attribute__ (( packed )); + +/** A nonce */ +struct ntlm_nonce { + /** Raw bytes */ + uint8_t raw[8]; +} __attribute__ (( packed )); + +/** A variable-length data descriptor */ +struct ntlm_data { + /** Length (in bytes) */ + uint16_t len; + /** Maximum length (in bytes) + * + * Should always be set equal to the length; this field is + * entirely superfluous. + */ + uint16_t max_len; + /** Offset from start of message header */ + uint32_t offset; +} __attribute__ (( packed )); + +/** A Negotiate message */ +struct ntlm_negotiate { + /** Message header */ + struct ntlm_header header; + /** Negotiation flags */ + uint32_t flags; + /** Domain name */ + struct ntlm_data domain; + /** Workstation name */ + struct ntlm_data workstation; +} __attribute__ (( packed )); + +/** A Challenge message */ +struct ntlm_challenge { + /** Message header */ + struct ntlm_header header; + /** Target name */ + struct ntlm_data name; + /** Negotiation flags */ + uint32_t flags; + /** Server nonce */ + struct ntlm_nonce nonce; + /** Reserved */ + uint8_t reserved[8]; + /** Target information */ + struct ntlm_data info; +} __attribute__ (( packed )); + +/** An Authenticate message */ +struct ntlm_authenticate { + /** Message header */ + struct ntlm_header header; + /** LAN Manager response */ + struct ntlm_data lm; + /** NT response */ + struct ntlm_data nt; + /** Domain name */ + struct ntlm_data domain; + /** User name */ + struct ntlm_data user; + /** Workstation name */ + struct ntlm_data workstation; + /** Session key */ + struct ntlm_data session; + /** Negotiation flags */ + uint32_t flags; +} __attribute__ (( packed )); + +/** A LAN Manager response */ +struct ntlm_lm_response { + /** HMAC-MD5 digest */ + uint8_t digest[MD5_DIGEST_SIZE]; + /** Client nonce */ + struct ntlm_nonce nonce; +} __attribute__ (( packed )); + +/** An NT response */ +struct ntlm_nt_response { + /** HMAC-MD5 digest */ + uint8_t digest[MD5_DIGEST_SIZE]; + /** Response version */ + uint8_t version; + /** Highest response version */ + uint8_t high; + /** Reserved */ + uint8_t reserved_a[6]; + /** Current time */ + uint64_t time; + /** Client nonce */ + struct ntlm_nonce nonce; + /** Must be zero */ + uint32_t zero; +} __attribute__ (( packed )); + +/** NTLM version */ +#define NTLM_VERSION_NTLMV2 0x01 + +/** NTLM challenge information */ +struct ntlm_challenge_info { + /** Server nonce */ + struct ntlm_nonce *nonce; + /** Target information */ + void *target; + /** Length of target information */ + size_t len; +}; + +/** An NTLM verification key */ +struct ntlm_key { + /** Raw bytes */ + uint8_t raw[MD5_DIGEST_SIZE]; +}; + +extern const struct ntlm_negotiate ntlm_negotiate; +extern int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len, + struct ntlm_challenge_info *info ); +extern void ntlm_key ( const char *domain, const char *username, + const char *password, struct ntlm_key *key ); +extern 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 ); +extern 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 ); +extern size_t ntlm_authenticate_len ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation ); + +#endif /* _IPXE_NTLM_H */ diff --git a/src/include/ipxe/ntp.h b/src/include/ipxe/ntp.h new file mode 100644 index 000000000..f5b3d2326 --- /dev/null +++ b/src/include/ipxe/ntp.h @@ -0,0 +1,109 @@ +#ifndef _IPXE_NTP_H +#define _IPXE_NTP_H + +/** @file + * + * Network Time Protocol + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** NTP port */ +#define NTP_PORT 123 + +/** An NTP short-format timestamp */ +struct ntp_short { + /** Seconds */ + uint16_t seconds; + /** Fraction of a second */ + uint16_t fraction; +} __attribute__ (( packed )); + +/** An NTP timestamp */ +struct ntp_timestamp { + /** Seconds */ + uint32_t seconds; + /** Fraction of a second */ + uint32_t fraction; +} __attribute__ (( packed )); + +/** An NTP reference identifier */ +union ntp_id { + /** Textual identifier */ + char text[4]; + /** IPv4 address */ + struct in_addr in; + /** Opaque integer */ + uint32_t opaque; +}; + +/** An NTP header */ +struct ntp_header { + /** Flags */ + uint8_t flags; + /** Stratum */ + uint8_t stratum; + /** Polling rate */ + int8_t poll; + /** Precision */ + int8_t precision; + /** Root delay */ + struct ntp_short delay; + /** Root dispersion */ + struct ntp_short dispersion; + /** Reference clock identifier */ + union ntp_id id; + /** Reference timestamp */ + struct ntp_timestamp reference; + /** Originate timestamp */ + struct ntp_timestamp originate; + /** Receive timestamp */ + struct ntp_timestamp receive; + /** Transmit timestamp */ + struct ntp_timestamp transmit; +} __attribute__ (( packed )); + +/** Leap second indicator: unknown */ +#define NTP_FL_LI_UNKNOWN 0xc0 + +/** NTP version: 1 */ +#define NTP_FL_VN_1 0x20 + +/** NTP mode: client */ +#define NTP_FL_MODE_CLIENT 0x03 + +/** NTP mode: server */ +#define NTP_FL_MODE_SERVER 0x04 + +/** NTP mode mask */ +#define NTP_FL_MODE_MASK 0x07 + +/** NTP timestamp for start of Unix epoch */ +#define NTP_EPOCH 2208988800UL + +/** NTP fraction of a second magic value + * + * This is a policy decision. + */ +#define NTP_FRACTION_MAGIC 0x69505845UL + +/** NTP minimum retransmission timeout + * + * This is a policy decision. + */ +#define NTP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC ) + +/** NTP maximum retransmission timeout + * + * This is a policy decision. + */ +#define NTP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) + +extern int start_ntp ( struct interface *job, const char *hostname ); + +#endif /* _IPXE_NTP_H */ diff --git a/src/include/ipxe/null_acpi.h b/src/include/ipxe/null_acpi.h new file mode 100644 index 000000000..1e469e33d --- /dev/null +++ b/src/include/ipxe/null_acpi.h @@ -0,0 +1,23 @@ +#ifndef _IPXE_NULL_ACPI_H +#define _IPXE_NULL_ACPI_H + +/** @file + * + * Standard do-nothing ACPI interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef ACPI_NULL +#define ACPI_PREFIX_null +#else +#define ACPI_PREFIX_null __null_ +#endif + +static inline __always_inline userptr_t +ACPI_INLINE ( null, acpi_find_rsdt ) ( void ) { + return UNULL; +} + +#endif /* _IPXE_NULL_ACPI_H */ diff --git a/src/include/ipxe/null_entropy.h b/src/include/ipxe/null_entropy.h index 91adefa69..5a6bb6218 100644 --- a/src/include/ipxe/null_entropy.h +++ b/src/include/ipxe/null_entropy.h @@ -30,14 +30,14 @@ ENTROPY_INLINE ( null, entropy_disable ) ( void ) { /* Do nothing */ } -static inline __always_inline double +static inline __always_inline min_entropy_t ENTROPY_INLINE ( null, min_entropy_per_sample ) ( void ) { /* Actual amount of min-entropy is zero. To avoid * division-by-zero errors and to allow compilation of * entropy-consuming code, pretend to have 1 bit of entropy in * each sample. */ - return 1.0; + return MIN_ENTROPY ( 1.0 ); } static inline __always_inline int diff --git a/src/include/ipxe/null_sanboot.h b/src/include/ipxe/null_sanboot.h index 58f03339f..b0e36b8b0 100644 --- a/src/include/ipxe/null_sanboot.h +++ b/src/include/ipxe/null_sanboot.h @@ -15,9 +15,4 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SANBOOT_PREFIX_null __null_ #endif -static inline __always_inline unsigned int -SANBOOT_INLINE ( null, san_default_drive ) ( void ) { - return 0; -} - #endif /* _IPXE_NULL_SANBOOT_H */ diff --git a/src/include/ipxe/ocsp.h b/src/include/ipxe/ocsp.h index 71fa41dc9..be0bddc50 100644 --- a/src/include/ipxe/ocsp.h +++ b/src/include/ipxe/ocsp.h @@ -14,6 +14,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include + +/* Allow OCSP to be disabled completely */ +#ifdef OCSP_CHECK +#define OCSP_ENABLED 1 +#else +#define OCSP_ENABLED 0 +#endif /** OCSP algorithm identifier */ #define OCSP_ALGORITHM_IDENTIFIER( ... ) \ @@ -111,6 +119,25 @@ ocsp_put ( struct ocsp_check *ocsp ) { ref_put ( &ocsp->refcnt ); } +/** + * Check if X.509 certificate requires an OCSP check + * + * @v cert X.509 certificate + * @ret ocsp_required An OCSP check is required + */ +static inline int ocsp_required ( struct x509_certificate *cert ) { + + /* An OCSP check is never required if OCSP checks are disabled */ + if ( ! OCSP_ENABLED ) + return 0; + + /* An OCSP check is required if an OCSP URI exists but the + * OCSP status is not (yet) good. + */ + return ( cert->extensions.auth_info.ocsp.uri.len && + ( ! cert->extensions.auth_info.ocsp.good ) ); +} + extern int ocsp_check ( struct x509_certificate *cert, struct x509_certificate *issuer, struct ocsp_check **ocsp ); diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index a841e00ff..ddd8c8d1e 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -94,6 +94,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_CAP_ID_VPD 0x03 /**< Vital product data */ #define PCI_CAP_ID_VNDR 0x09 /**< Vendor-specific */ #define PCI_CAP_ID_EXP 0x10 /**< PCI Express */ +#define PCI_CAP_ID_EA 0x14 /**< Enhanced Allocation */ /** Next capability */ #define PCI_CAP_NEXT 0x01 @@ -104,6 +105,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_PM_CTRL_PME_ENABLE 0x0100 /**< PME pin enable */ #define PCI_PM_CTRL_PME_STATUS 0x8000 /**< PME pin status */ +/** PCI Express */ +#define PCI_EXP_DEVCTL 0x08 +#define PCI_EXP_DEVCTL_FLR 0x8000 /**< Function level reset */ + /** Uncorrectable error status */ #define PCI_ERR_UNCOR_STATUS 0x04 @@ -128,6 +133,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ( ( ( (base) & 0xff ) << 16 ) | ( ( (sub) & 0xff ) << 8 ) | \ ( ( (progif) & 0xff) << 0 ) ) +/** PCI Express function level reset delay (in ms) */ +#define PCI_EXP_FLR_DELAY_MS 100 + /** A PCI device ID list entry */ struct pci_device_id { /** Name */ @@ -187,8 +195,8 @@ struct pci_device { uint32_t class; /** Interrupt number */ uint8_t irq; - /** Bus, device, and function (bus:dev.fn) number */ - uint16_t busdevfn; + /** Segment, bus, device, and function (bus:dev.fn) number */ + uint32_t busdevfn; /** Driver for this device */ struct pci_driver *driver; /** Driver-private data @@ -233,11 +241,13 @@ struct pci_driver { /** Declare a fallback PCI driver */ #define __pci_driver_fallback __table_entry ( PCI_DRIVERS, 02 ) +#define PCI_SEG( busdevfn ) ( ( (busdevfn) >> 16 ) & 0xffff ) #define PCI_BUS( busdevfn ) ( ( (busdevfn) >> 8 ) & 0xff ) #define PCI_SLOT( busdevfn ) ( ( (busdevfn) >> 3 ) & 0x1f ) #define PCI_FUNC( busdevfn ) ( ( (busdevfn) >> 0 ) & 0x07 ) -#define PCI_BUSDEVFN( bus, slot, func ) \ - ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) +#define PCI_BUSDEVFN( segment, bus, slot, func ) \ + ( ( (segment) << 16 ) | ( (bus) << 8 ) | \ + ( (slot) << 3 ) | ( (func) << 0 ) ) #define PCI_FIRST_FUNC( busdevfn ) ( (busdevfn) & ~0x07 ) #define PCI_LAST_FUNC( busdevfn ) ( (busdevfn) | 0x07 ) @@ -263,12 +273,12 @@ struct pci_driver { PCI_ID( _vendor, _device, _name, _description, _data ) /** PCI device debug message format */ -#define PCI_FMT "PCI %02x:%02x.%x" +#define PCI_FMT "%04x:%02x:%02x.%x" /** PCI device debug message arguments */ #define PCI_ARGS( pci ) \ - PCI_BUS ( (pci)->busdevfn ), PCI_SLOT ( (pci)->busdevfn ), \ - PCI_FUNC ( (pci)->busdevfn ) + PCI_SEG ( (pci)->busdevfn ), PCI_BUS ( (pci)->busdevfn ), \ + PCI_SLOT ( (pci)->busdevfn ), PCI_FUNC ( (pci)->busdevfn ) extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, @@ -279,6 +289,8 @@ extern int pci_find_driver ( struct pci_device *pci ); extern int pci_probe ( struct pci_device *pci ); extern void pci_remove ( struct pci_device *pci ); extern int pci_find_capability ( struct pci_device *pci, int capability ); +extern int pci_find_next_capability ( struct pci_device *pci, + int pos, int capability ); extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ); /** diff --git a/src/include/ipxe/pciea.h b/src/include/ipxe/pciea.h new file mode 100644 index 000000000..941c94ed5 --- /dev/null +++ b/src/include/ipxe/pciea.h @@ -0,0 +1,70 @@ +#ifndef _IPXE_PCIEA_H +#define _IPXE_PCIEA_H + +/** @file + * + * PCI Enhanced Allocation + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Number of entries */ +#define PCIEA_ENTRIES 2 +#define PCIEA_ENTRIES_MASK 0x3f + +/** First entry */ +#define PCIEA_FIRST 4 + +/** Entry descriptor */ +#define PCIEA_DESC 0 + +/** Entry size */ +#define PCIEA_DESC_SIZE(desc) ( ( (desc) >> 0 ) & 0x7 ) + +/** BAR equivalent indicator */ +#define PCIEA_DESC_BEI(desc) ( ( (desc) >> 4 ) & 0xf ) + +/** BAR equivalent indicators */ +enum pciea_bei { + PCIEA_BEI_BAR_0 = 0, /**< Standard BAR 0 */ + PCIEA_BEI_BAR_1 = 1, /**< Standard BAR 1 */ + PCIEA_BEI_BAR_2 = 2, /**< Standard BAR 2 */ + PCIEA_BEI_BAR_3 = 3, /**< Standard BAR 3 */ + PCIEA_BEI_BAR_4 = 4, /**< Standard BAR 4 */ + PCIEA_BEI_BAR_5 = 5, /**< Standard BAR 5 */ + PCIEA_BEI_ROM = 8, /**< Expansion ROM BAR */ + PCIEA_BEI_VF_BAR_0 = 9, /**< Virtual function BAR 0 */ + PCIEA_BEI_VF_BAR_1 = 10, /**< Virtual function BAR 1 */ + PCIEA_BEI_VF_BAR_2 = 11, /**< Virtual function BAR 2 */ + PCIEA_BEI_VF_BAR_3 = 12, /**< Virtual function BAR 3 */ + PCIEA_BEI_VF_BAR_4 = 13, /**< Virtual function BAR 4 */ + PCIEA_BEI_VF_BAR_5 = 14, /**< Virtual function BAR 5 */ +}; + +/** Entry is enabled */ +#define PCIEA_DESC_ENABLED 0x80000000UL + +/** Base address low dword */ +#define PCIEA_LOW_BASE 4 + +/** Limit low dword */ +#define PCIEA_LOW_LIMIT 8 + +/** BAR is 64-bit */ +#define PCIEA_LOW_ATTR_64BIT 0x00000002UL + +/** Low dword attribute bit mask */ +#define PCIEA_LOW_ATTR_MASK 0x00000003UL + +/** Offset to high dwords */ +#define PCIEA_LOW_HIGH 8 + +extern unsigned long pciea_bar_start ( struct pci_device *pci, + unsigned int bei ); +extern unsigned long pciea_bar_size ( struct pci_device *pci, + unsigned int bei ); + +#endif /* _IPXE_PCIEA_H */ diff --git a/src/include/ipxe/peerdisc.h b/src/include/ipxe/peerdisc.h index f08ccaae2..45d592e76 100644 --- a/src/include/ipxe/peerdisc.h +++ b/src/include/ipxe/peerdisc.h @@ -109,6 +109,12 @@ peerdisc_init ( struct peerdisc_client *peerdisc, extern unsigned int peerdisc_timeout_secs; +extern void peerdisc_stat ( struct interface *intf, struct peerdisc_peer *peer, + struct list_head *peers ); +#define peerdisc_stat_TYPE( object_type ) \ + typeof ( void ( object_type, struct peerdisc_peer *peer, \ + struct list_head *peers ) ) + extern int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, size_t len ); extern void peerdisc_close ( struct peerdisc_client *peerdisc ); diff --git a/src/include/ipxe/peermux.h b/src/include/ipxe/peermux.h index 44cbdb9d6..54acbfec9 100644 --- a/src/include/ipxe/peermux.h +++ b/src/include/ipxe/peermux.h @@ -41,6 +41,16 @@ struct peerdist_multiplexed_block { struct interface xfer; }; +/** PeerDist statistics */ +struct peerdist_statistics { + /** Maximum observed number of peers */ + unsigned int peers; + /** Number of blocks downloaded in total */ + unsigned int total; + /** Number of blocks downloaded from peers */ + unsigned int local; +}; + /** A PeerDist download multiplexer */ struct peerdist_multiplexer { /** Reference count */ @@ -65,6 +75,9 @@ struct peerdist_multiplexer { struct list_head idle; /** Block downloads */ struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS]; + + /** Statistics */ + struct peerdist_statistics stats; }; extern int peermux_filter ( struct interface *xfer, struct interface *info, diff --git a/src/include/ipxe/pem.h b/src/include/ipxe/pem.h new file mode 100644 index 000000000..d88ec5b6f --- /dev/null +++ b/src/include/ipxe/pem.h @@ -0,0 +1,28 @@ +#ifndef _IPXE_PEM_H +#define _IPXE_PEM_H + +/** @file + * + * PEM-encoded ASN.1 data + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** Pre-encapsulation boundary marker */ +#define PEM_BEGIN "-----BEGIN" + +/** Post-encapsulation boundary marker */ +#define PEM_END "-----END" + +extern int pem_asn1 ( userptr_t data, size_t len, size_t offset, + struct asn1_cursor **cursor ); + +extern struct image_type pem_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_PEM_H */ diff --git a/src/include/ipxe/pool.h b/src/include/ipxe/pool.h index 27066e9b3..81ff57d75 100644 --- a/src/include/ipxe/pool.h +++ b/src/include/ipxe/pool.h @@ -112,7 +112,7 @@ pool_is_reopenable ( struct pooled_connection *pool ) { /* A connection is reopenable if it has been recycled but is * not yet known to be alive. */ - return ( ( pool->flags & POOL_RECYCLED ) & + return ( ( pool->flags & POOL_RECYCLED ) && ( ! ( pool->flags & POOL_ALIVE ) ) ); } diff --git a/src/include/ipxe/process.h b/src/include/ipxe/process.h index d600508e7..0d059f8a1 100644 --- a/src/include/ipxe/process.h +++ b/src/include/ipxe/process.h @@ -29,6 +29,8 @@ struct process { /** A process descriptor */ struct process_descriptor { + /** Process name */ + const char *name; /** Offset of process within containing object */ size_t offset; /** @@ -78,6 +80,7 @@ struct process_descriptor { * @ret desc Object interface descriptor */ #define PROC_DESC( object_type, process, _step ) { \ + .name = #_step, \ .offset = process_offset ( object_type, process ), \ .step = PROC_STEP ( object_type, _step ), \ .reschedule = 1, \ @@ -92,6 +95,7 @@ struct process_descriptor { * @ret desc Object interface descriptor */ #define PROC_DESC_ONCE( object_type, process, _step ) { \ + .name = #_step, \ .offset = process_offset ( object_type, process ), \ .step = PROC_STEP ( object_type, _step ), \ .reschedule = 0, \ @@ -106,6 +110,7 @@ struct process_descriptor { * @ret desc Object interface descriptor */ #define PROC_DESC_PURE( _step ) { \ + .name = #_step, \ .offset = 0, \ .step = PROC_STEP ( struct process, _step ), \ .reschedule = 1, \ @@ -192,7 +197,7 @@ struct process name __permanent_process = { \ #define PROC_COL( process ) process_object ( process ) /** printf() format string for PROC_DBG() */ -#define PROC_FMT "%p+%zx" +#define PROC_FMT "%p %s()" /** * printf() arguments for representing a process @@ -200,6 +205,6 @@ struct process name __permanent_process = { \ * @v process Process * @ret args printf() argument list corresponding to PROC_FMT */ -#define PROC_DBG( process ) process_object ( process ), (process)->desc->offset +#define PROC_DBG( process ) process_object ( process ), (process)->desc->name #endif /* _IPXE_PROCESS_H */ diff --git a/src/include/ipxe/profile.h b/src/include/ipxe/profile.h index b6d2b19e0..2c69e1208 100644 --- a/src/include/ipxe/profile.h +++ b/src/include/ipxe/profile.h @@ -12,11 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#ifndef PROFILING #ifdef NDEBUG #define PROFILING 0 #else #define PROFILING 1 #endif +#endif /** * A data structure for storing profiling information diff --git a/src/include/ipxe/pseudobit.h b/src/include/ipxe/pseudobit.h new file mode 100644 index 000000000..431b106fa --- /dev/null +++ b/src/include/ipxe/pseudobit.h @@ -0,0 +1,249 @@ +#ifndef _IPXE_PSEUDOBIT_H +#define _IPXE_PSEUDOBIT_H + +/* + * Copyright (C) 2008 Michael Brown . + * + * 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 + * + * Pseudo-bit structures + * + */ + +#include +#include + +/* Endianness selection. + * + * This is a property of the device, not a property of the host CPU. + */ +#ifdef PSEUDOBIT_LITTLE_ENDIAN +#define cpu_to_BIT64 cpu_to_le64 +#define cpu_to_BIT32 cpu_to_le32 +#define BIT64_to_cpu le64_to_cpu +#define BIT32_to_cpu le32_to_cpu +#define QWORD_SHIFT( offset, width ) (offset) +#endif +#ifdef PSEUDOBIT_BIG_ENDIAN +#define cpu_to_BIT64 cpu_to_be64 +#define cpu_to_BIT32 cpu_to_be32 +#define BIT64_to_cpu be64_to_cpu +#define BIT32_to_cpu be32_to_cpu +#define QWORD_SHIFT( offset, width ) ( 64 - (offset) - (width) ) +#endif + +/** Datatype used to represent a bit in the pseudo-structures */ +typedef unsigned char pseudo_bit_t; + +/** + * Wrapper structure for pseudo_bit_t structures + * + * This structure provides a wrapper around pseudo_bit_t structures. + * It has the correct size, and also encapsulates type information + * about the underlying pseudo_bit_t-based structure, which allows the + * BIT_FILL() etc. macros to work without requiring explicit type + * information. + */ +#define PSEUDO_BIT_STRUCT( _structure ) \ + union { \ + uint8_t bytes[ sizeof ( _structure ) / 8 ]; \ + uint32_t dwords[ sizeof ( _structure ) / 32 ]; \ + uint64_t qwords[ sizeof ( _structure ) / 64 ]; \ + _structure *dummy[0]; \ + } __attribute__ (( packed )) u + +/** Get pseudo_bit_t structure type from wrapper structure pointer */ +#define PSEUDO_BIT_STRUCT_TYPE( _ptr ) \ + typeof ( *((_ptr)->u.dummy[0]) ) + +/** Bit offset of a field within a pseudo_bit_t structure */ +#define BIT_OFFSET( _ptr, _field ) \ + offsetof ( PSEUDO_BIT_STRUCT_TYPE ( _ptr ), _field ) + +/** Bit width of a field within a pseudo_bit_t structure */ +#define BIT_WIDTH( _ptr, _field ) \ + sizeof ( ( ( PSEUDO_BIT_STRUCT_TYPE ( _ptr ) * ) NULL )->_field ) + +/** Qword offset of a field within a pseudo_bit_t structure */ +#define QWORD_OFFSET( _ptr, _field ) \ + ( BIT_OFFSET ( _ptr, _field ) / 64 ) + +/** Qword bit offset of a field within a pseudo_bit_t structure */ +#define QWORD_BIT_OFFSET( _ptr, _index, _field ) \ + ( BIT_OFFSET ( _ptr, _field ) - ( 64 * (_index) ) ) + +/** Qword bit shift for a field within a pseudo_bit_t structure */ +#define QWORD_BIT_SHIFT( _ptr, _index, _field ) \ + QWORD_SHIFT ( QWORD_BIT_OFFSET ( _ptr, _index, _field ), \ + BIT_WIDTH ( _ptr, _field ) ) + +/** Bit mask for a field within a pseudo_bit_t structure */ +#define BIT_MASK( _ptr, _field ) \ + ( ( ~( ( uint64_t ) 0 ) ) >> \ + ( 64 - BIT_WIDTH ( _ptr, _field ) ) ) + +/* + * Assemble native-endian qword from named fields and values + * + */ + +#define BIT_ASSEMBLE_1( _ptr, _index, _field, _value ) \ + ( ( ( uint64_t) (_value) ) << \ + QWORD_BIT_SHIFT ( _ptr, _index, _field ) ) + +#define BIT_ASSEMBLE_2( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_3( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_4( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_5( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_6( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_7( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Build native-endian (positive) qword bitmasks from named fields + * + */ + +#define BIT_MASK_1( _ptr, _index, _field ) \ + ( BIT_MASK ( _ptr, _field ) << \ + QWORD_BIT_SHIFT ( _ptr, _index, _field ) ) + +#define BIT_MASK_2( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_3( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_4( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_5( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_6( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_7( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Populate device-endian qwords from named fields and values + * + */ + +#define BIT_FILL( _ptr, _index, _assembled ) do { \ + uint64_t *__ptr = &(_ptr)->u.qwords[(_index)]; \ + uint64_t __assembled = (_assembled); \ + *__ptr = cpu_to_BIT64 ( __assembled ); \ + } while ( 0 ) + +#define BIT_FILL_1( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_1 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_2( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_2 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_3( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_3 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_4( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_4 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_5( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_5 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_6( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_6 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_QWORD_PTR( _ptr, _field ) \ + ( { \ + unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + __ptr; \ + } ) + +/** Extract value of named field */ +#define BIT_GET64( _ptr, _field ) \ + ( { \ + unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + uint64_t __value = BIT64_to_cpu ( *__ptr ); \ + __value >>= \ + QWORD_BIT_SHIFT ( _ptr, __index, _field ); \ + __value &= BIT_MASK ( _ptr, _field ); \ + __value; \ + } ) + +/** Extract value of named field (for fields up to the size of a long) */ +#define BIT_GET( _ptr, _field ) \ + ( ( unsigned long ) BIT_GET64 ( _ptr, _field ) ) + +#define BIT_SET( _ptr, _field, _value ) do { \ + unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + unsigned int __shift = \ + QWORD_BIT_SHIFT ( _ptr, __index, _field ); \ + uint64_t __value = (_value); \ + *__ptr &= cpu_to_BIT64 ( ~( BIT_MASK ( _ptr, _field ) << \ + __shift ) ); \ + *__ptr |= cpu_to_BIT64 ( __value << __shift ); \ + } while ( 0 ) + +#endif /* _IPXE_PSEUDOBIT_H */ diff --git a/src/include/ipxe/quiesce.h b/src/include/ipxe/quiesce.h new file mode 100644 index 000000000..00b530b83 --- /dev/null +++ b/src/include/ipxe/quiesce.h @@ -0,0 +1,31 @@ +#ifndef _IPXE_QUIESCE_H +#define _IPXE_QUIESCE_H + +/** @file + * + * Quiesce system + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** A quiescer */ +struct quiescer { + /** Quiesce system */ + void ( * quiesce ) ( void ); + /** Unquiesce system */ + void ( * unquiesce ) ( void ); +}; + +/** Quiescer table */ +#define QUIESCERS __table ( struct quiescer, "quiescers" ) + +/** Declare a quiescer */ +#define __quiescer __table_entry ( QUIESCERS, 01 ) + +extern void quiesce ( void ); +extern void unquiesce ( void ); + +#endif /* _IPXE_QUIESCE_H */ diff --git a/src/include/ipxe/rsa.h b/src/include/ipxe/rsa.h index d947eec73..a1b5e0c03 100644 --- a/src/include/ipxe/rsa.h +++ b/src/include/ipxe/rsa.h @@ -77,6 +77,9 @@ struct rsa_context { void *tmp; }; +/** RSA context size */ +#define RSA_CTX_SIZE sizeof ( struct rsa_context ) + extern struct pubkey_algorithm rsa_algorithm; #endif /* _IPXE_RSA_H */ diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 57025f2c6..b163a94b8 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -7,21 +7,93 @@ * * The sanboot API provides methods for hooking, unhooking, * describing, and booting from SAN devices. - * - * The standard methods (readl()/writel() etc.) do not strictly check - * the type of the address parameter; this is because traditional - * usage does not necessarily provide the correct pointer type. For - * example, code written for ISA devices at fixed I/O addresses (such - * as the keyboard controller) tend to use plain integer constants for - * the address parameter. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include +#include +#include +#include +#include +#include +#include #include -struct uri; +/** A SAN path */ +struct san_path { + /** Containing SAN device */ + struct san_device *sandev; + /** Path index */ + unsigned int index; + /** SAN device URI */ + struct uri *uri; + /** List of open/closed paths */ + struct list_head list; + + /** Underlying block device interface */ + struct interface block; + /** Process */ + struct process process; + /** Path status */ + int path_rc; + + /** ACPI descriptor (if applicable) */ + struct acpi_descriptor *desc; +}; + +/** A SAN device */ +struct san_device { + /** Reference count */ + struct refcnt refcnt; + /** List of SAN devices */ + struct list_head list; + + /** Drive number */ + unsigned int drive; + /** Flags */ + unsigned int flags; + + /** Command interface */ + struct interface command; + /** Command timeout timer */ + struct retry_timer timer; + /** Command status */ + int command_rc; + + /** Raw block device capacity */ + struct block_device_capacity capacity; + /** Block size shift + * + * To allow for emulation of CD-ROM access, this represents + * the left-shift required to translate from exposed logical + * I/O blocks to underlying blocks. + */ + unsigned int blksize_shift; + /** Drive is a CD-ROM */ + int is_cdrom; + + /** Driver private data */ + void *priv; + + /** Number of paths */ + unsigned int paths; + /** Current active path */ + struct san_path *active; + /** List of opened SAN paths */ + struct list_head opened; + /** List of closed SAN paths */ + struct list_head closed; + /** SAN paths */ + struct san_path path[0]; +}; + +/** SAN device flags */ +enum san_device_flags { + /** Device should not be included in description tables */ + SAN_NO_DESCRIBE = 0x0001, +}; /** * Calculate static inline sanboot API function name @@ -54,25 +126,23 @@ struct uri; /* Include all architecture-independent sanboot API headers */ #include +#include +#include /* Include all architecture-dependent sanboot API headers */ #include -/** - * Get default SAN drive number - * - * @ret drive Default drive number - */ -unsigned int san_default_drive ( void ); - /** * Hook SAN device * - * @v uri URI * @v drive Drive number - * @ret rc Return status code + * @v uris List of URIs + * @v count Number of URIs + * @v flags Flags + * @ret drive Drive number, or negative error */ -int san_hook ( struct uri *uri, unsigned int drive ); +int san_hook ( unsigned int drive, struct uri **uris, unsigned int count, + unsigned int flags ); /** * Unhook SAN device @@ -85,16 +155,96 @@ void san_unhook ( unsigned int drive ); * Attempt to boot from a SAN device * * @v drive Drive number + * @v filename Filename (or NULL to use default) * @ret rc Return status code */ -int san_boot ( unsigned int drive ); +int san_boot ( unsigned int drive, const char *filename ); /** - * Describe SAN device for SAN-booted operating system + * Describe SAN devices for SAN-booted operating system * - * @v drive Drive number * @ret rc Return status code */ -int san_describe ( unsigned int drive ); +int san_describe ( void ); + +extern struct list_head san_devices; + +/** Iterate over all SAN devices */ +#define for_each_sandev( sandev ) \ + list_for_each_entry ( (sandev), &san_devices, list ) + +/** There exist some SAN devices + * + * @ret existence Existence of SAN devices + */ +static inline int have_sandevs ( void ) { + return ( ! list_empty ( &san_devices ) ); +} + +/** + * Get reference to SAN device + * + * @v sandev SAN device + * @ret sandev SAN device + */ +static inline __attribute__ (( always_inline )) struct san_device * +sandev_get ( struct san_device *sandev ) { + ref_get ( &sandev->refcnt ); + return sandev; +} + +/** + * Drop reference to SAN device + * + * @v sandev SAN device + */ +static inline __attribute__ (( always_inline )) void +sandev_put ( struct san_device *sandev ) { + ref_put ( &sandev->refcnt ); +} + +/** + * Calculate SAN device block size + * + * @v sandev SAN device + * @ret blksize Sector size + */ +static inline size_t sandev_blksize ( struct san_device *sandev ) { + return ( sandev->capacity.blksize << sandev->blksize_shift ); +} + +/** + * Calculate SAN device capacity + * + * @v sandev SAN device + * @ret blocks Number of blocks + */ +static inline uint64_t sandev_capacity ( struct san_device *sandev ) { + return ( sandev->capacity.blocks >> sandev->blksize_shift ); +} + +/** + * Check if SAN device needs to be reopened + * + * @v sandev SAN device + * @ret needs_reopen SAN device needs to be reopened + */ +static inline int sandev_needs_reopen ( struct san_device *sandev ) { + return ( sandev->active == NULL ); +} + +extern struct san_device * sandev_find ( unsigned int drive ); +extern int sandev_reopen ( struct san_device *sandev ); +extern int sandev_reset ( struct san_device *sandev ); +extern int sandev_read ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer ); +extern int sandev_write ( struct san_device *sandev, uint64_t lba, + unsigned int count, userptr_t buffer ); +extern struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, + size_t priv_size ); +extern int register_sandev ( struct san_device *sandev, unsigned int drive, + unsigned int flags ); +extern void unregister_sandev ( struct san_device *sandev ); +extern unsigned int san_default_drive ( void ); #endif /* _IPXE_SANBOOT_H */ diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 95a553cc8..f463e6674 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -40,7 +40,7 @@ struct setting { * (such as a DHCP option number, or an SMBIOS structure and * field number). */ - unsigned int tag; + uint64_t tag; /** Setting scope (or NULL) * * For historic reasons, a NULL scope with a non-zero tag @@ -62,18 +62,22 @@ struct setting { #define SETTING_NETDEV 01 /**< Network device settings */ #define SETTING_NETDEV_EXTRA 02 /**< Network device additional settings */ -#define SETTING_IP 03 /**< IPv4 settings */ -#define SETTING_IP_EXTRA 04 /**< IPv4 additional settings */ -#define SETTING_BOOT 05 /**< Generic boot settings */ -#define SETTING_BOOT_EXTRA 06 /**< Generic boot additional settings */ -#define SETTING_SANBOOT 07 /**< SAN boot settings */ -#define SETTING_SANBOOT_EXTRA 08 /**< SAN boot additional settings */ -#define SETTING_HOST 09 /**< Host identity settings */ -#define SETTING_HOST_EXTRA 10 /**< Host identity additional settings */ -#define SETTING_AUTH 11 /**< Authentication settings */ -#define SETTING_AUTH_EXTRA 12 /**< Authentication additional settings */ -#define SETTING_CRYPTO 13 /**< Cryptography settings */ -#define SETTING_MISC 14 /**< Miscellaneous settings */ +#define SETTING_IP4 03 /**< IPv4 settings */ +#define SETTING_IP4_EXTRA 04 /**< IPv4 additional settings */ +#define SETTING_IP6 05 /**< IPv6 settings */ +#define SETTING_IP6_EXTRA 06 /**< IPv6 additional settings */ +#define SETTING_IP 07 /**< IPv4 settings */ +#define SETTING_IP_EXTRA 08 /**< IPv4 additional settings */ +#define SETTING_BOOT 09 /**< Generic boot settings */ +#define SETTING_BOOT_EXTRA 10 /**< Generic boot additional settings */ +#define SETTING_SANBOOT 11 /**< SAN boot settings */ +#define SETTING_SANBOOT_EXTRA 12 /**< SAN boot additional settings */ +#define SETTING_HOST 13 /**< Host identity settings */ +#define SETTING_HOST_EXTRA 14 /**< Host identity additional settings */ +#define SETTING_AUTH 15 /**< Authentication settings */ +#define SETTING_AUTH_EXTRA 16 /**< Authentication additional settings */ +#define SETTING_CRYPTO 17 /**< Cryptography settings */ +#define SETTING_MISC 18 /**< Miscellaneous settings */ /** @} */ @@ -140,6 +144,8 @@ struct settings { struct settings_operations *op; /** Default scope for numerical settings constructed for this block */ const struct settings_scope *default_scope; + /** Sibling ordering */ + int order; }; /** @@ -280,7 +286,10 @@ struct builtin_setting { extern const struct settings_scope builtin_scope; /** IPv6 setting scope */ -extern const struct settings_scope ipv6_scope; +extern const struct settings_scope ipv6_settings_scope; + +/** DHCPv6 setting scope */ +extern const struct settings_scope dhcpv6_scope; /** * A generic settings block @@ -421,13 +430,19 @@ extern const struct setting_type setting_type_busdevfn __setting_type; extern const struct setting_type setting_type_dnssl __setting_type; extern const struct setting -ip_setting __setting ( SETTING_IP, ip ); +ip_setting __setting ( SETTING_IP4, ip ); extern const struct setting -netmask_setting __setting ( SETTING_IP, netmask ); +netmask_setting __setting ( SETTING_IP4, netmask ); extern const struct setting -gateway_setting __setting ( SETTING_IP, gateway ); +gateway_setting __setting ( SETTING_IP4, gateway ); extern const struct setting -dns_setting __setting ( SETTING_IP_EXTRA, dns ); +dns_setting __setting ( SETTING_IP4_EXTRA, dns ); +extern const struct setting +ip6_setting __setting ( SETTING_IP6, ip6 ); +extern const struct setting +len6_setting __setting ( SETTING_IP6, len6 ); +extern const struct setting +gateway6_setting __setting ( SETTING_IP6, gateway6 ); extern const struct setting hostname_setting __setting ( SETTING_HOST, hostname ); extern const struct setting @@ -437,6 +452,8 @@ filename_setting __setting ( SETTING_BOOT, filename ); extern const struct setting root_path_setting __setting ( SETTING_SANBOOT, root-path ); extern const struct setting +san_filename_setting __setting ( SETTING_SANBOOT, san-filename ); +extern const struct setting username_setting __setting ( SETTING_AUTH, username ); extern const struct setting password_setting __setting ( SETTING_AUTH, password ); @@ -452,6 +469,20 @@ extern const struct setting busid_setting __setting ( SETTING_NETDEV, busid ); extern const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA, user-class ); +extern const struct setting +vendor_class_setting __setting ( SETTING_HOST_EXTRA, vendor-class ); +extern const struct setting +manufacturer_setting __setting ( SETTING_HOST_EXTRA, manufacturer ); +extern const struct setting +product_setting __setting ( SETTING_HOST_EXTRA, product ); +extern const struct setting +serial_setting __setting ( SETTING_HOST_EXTRA, serial ); +extern const struct setting +asset_setting __setting ( SETTING_HOST_EXTRA, asset ); +extern const struct setting +board_serial_setting __setting ( SETTING_HOST_EXTRA, board-serial ); +extern const struct setting dhcp_server_setting __setting ( SETTING_MISC, + dhcp-server ); /** * Initialise a settings block diff --git a/src/include/ipxe/smbios.h b/src/include/ipxe/smbios.h index 24b05ed62..c1d8fea3e 100644 --- a/src/include/ipxe/smbios.h +++ b/src/include/ipxe/smbios.h @@ -152,6 +152,9 @@ struct smbios_enclosure_information { /** SMBIOS enclosure information structure type */ #define SMBIOS_TYPE_ENCLOSURE_INFORMATION 3 +/** SMBIOS OEM strings structure type */ +#define SMBIOS_TYPE_OEM_STRINGS 11 + /** * SMBIOS entry point descriptor * diff --git a/src/include/ipxe/srp.h b/src/include/ipxe/srp.h index 8d7f799cd..3abb0995f 100644 --- a/src/include/ipxe/srp.h +++ b/src/include/ipxe/srp.h @@ -790,7 +790,7 @@ typedef uint16_t sbft_off_t; */ struct sbft_table { /** ACPI header */ - struct acpi_description_header acpi; + struct acpi_header acpi; /** Offset to SCSI subtable */ sbft_off_t scsi_offset; /** Offset to SRP subtable */ diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h index 063ebaa4b..f5508fe2b 100644 --- a/src/include/ipxe/tcp.h +++ b/src/include/ipxe/tcp.h @@ -26,7 +26,7 @@ struct tcp_header { uint16_t win; /* Advertised window */ uint16_t csum; /* Checksum */ uint16_t urg; /* Urgent pointer */ -}; +} __attribute__ (( packed )); /** @defgroup tcpopts TCP options * @{ @@ -140,8 +140,6 @@ struct tcp_timestamp_padded_option { /** Parsed TCP options */ struct tcp_options { - /** MSS option, if present */ - const struct tcp_mss_option *mssopt; /** Window scale option, if present */ const struct tcp_window_scale_option *wsopt; /** SACK permitted option, if present */ @@ -380,6 +378,14 @@ struct tcp_options { */ #define TCP_MSL ( 2 * 60 * TICKS_PER_SEC ) +/** + * TCP keepalive period + * + * We send keepalive ACKs after this period of inactivity has elapsed + * on an established connection. + */ +#define TCP_KEEPALIVE_DELAY ( 15 * TICKS_PER_SEC ) + /** * TCP maximum header length * diff --git a/src/include/ipxe/tcpip.h b/src/include/ipxe/tcpip.h index be4ac838b..414daad53 100644 --- a/src/include/ipxe/tcpip.h +++ b/src/include/ipxe/tcpip.h @@ -13,6 +13,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include + +extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial, + const void *data, size_t len ); + #include struct io_buffer; @@ -195,17 +199,8 @@ extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip, extern struct tcpip_net_protocol * tcpip_net_protocol ( sa_family_t sa_family ); extern struct net_device * tcpip_netdev ( struct sockaddr_tcpip *st_dest ); extern size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ); -extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ); extern uint16_t tcpip_chksum ( const void *data, size_t len ); extern int tcpip_bind ( struct sockaddr_tcpip *st_local, int ( * available ) ( int port ) ); -/* Use generic_tcpip_continue_chksum() if no architecture-specific - * version is available - */ -#ifndef tcpip_continue_chksum -#define tcpip_continue_chksum generic_tcpip_continue_chksum -#endif - #endif /* _IPXE_TCPIP_H */ diff --git a/src/include/ipxe/time.h b/src/include/ipxe/time.h index 4c5bb2a00..89bf90e03 100644 --- a/src/include/ipxe/time.h +++ b/src/include/ipxe/time.h @@ -50,11 +50,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Include all architecture-dependent time API headers */ #include +extern signed long time_offset; + /** - * Get current time in seconds + * Get current time in seconds (ignoring system clock offset) * * @ret time Time, in seconds */ time_t time_now ( void ); +/** + * Adjust system clock + * + * @v delta Clock adjustment, in seconds + */ +static inline __attribute__ (( always_inline )) void +time_adjust ( signed long delta ) { + + time_offset += delta; +} + #endif /* _IPXE_TIME_H */ diff --git a/src/include/ipxe/timer.h b/src/include/ipxe/timer.h index 82fbb6764..a6dffaf1c 100644 --- a/src/include/ipxe/timer.h +++ b/src/include/ipxe/timer.h @@ -3,75 +3,78 @@ /** @file * - * iPXE timer API + * iPXE timers * - * The timer API provides udelay() for fixed delays, and currticks() - * for a monotonically increasing tick counter. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include -#include - -/** - * Calculate static inline timer API function name - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @ret _subsys_func Subsystem API function - */ -#define TIMER_INLINE( _subsys, _api_func ) \ - SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func ) - -/** - * Provide a timer API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @v _func Implementing function - */ -#define PROVIDE_TIMER( _subsys, _api_func, _func ) \ - PROVIDE_SINGLE_API ( TIMER_PREFIX_ ## _subsys, _api_func, _func ) - -/** - * Provide a static inline timer API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - */ -#define PROVIDE_TIMER_INLINE( _subsys, _api_func ) \ - PROVIDE_SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func ) - -/* Include all architecture-independent I/O API headers */ -#include -#include - -/* Include all architecture-dependent I/O API headers */ -#include - -/** - * Delay for a fixed number of microseconds - * - * @v usecs Number of microseconds for which to delay - */ -void udelay ( unsigned long usecs ); - -/** - * Get current system time in ticks - * - * @ret ticks Current time, in ticks - */ -unsigned long currticks ( void ); - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -unsigned long ticks_per_sec ( void ); +#include /** Number of ticks per second */ -#define TICKS_PER_SEC ( ticks_per_sec() ) +#define TICKS_PER_SEC 1024 + +/** Number of ticks per millisecond + * + * This is (obviously) not 100% consistent with the definition of + * TICKS_PER_SEC, but it allows for multiplications and divisions to + * be elided. In any case, timer ticks are not expected to be a + * precision timing source; for example, the standard BIOS timer is + * based on an 18.2Hz clock. + */ +#define TICKS_PER_MS 1 + +/** A timer */ +struct timer { + /** Name */ + const char *name; + /** + * Probe timer + * + * @ret rc Return status code + */ + int ( * probe ) ( void ); + /** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ + unsigned long ( * currticks ) ( void ); + /** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ + void ( * udelay ) ( unsigned long usecs ); +}; + +/** Timer table */ +#define TIMERS __table ( struct timer, "timers" ) + +/** Declare a timer */ +#define __timer( order ) __table_entry ( TIMERS, order ) + +/** @defgroup timer_order Timer detection order + * + * @{ + */ + +#define TIMER_PREFERRED 01 /**< Preferred timer */ +#define TIMER_NORMAL 02 /**< Normal timer */ + +/** @} */ + +/* + * sleep() prototype is defined by POSIX.1. usleep() prototype is + * defined by 4.3BSD. udelay() and mdelay() prototypes are chosen to + * be reasonably sensible. + * + */ + +extern void udelay ( unsigned long usecs ); +extern void mdelay ( unsigned long msecs ); +extern unsigned long currticks ( void ); +extern unsigned int sleep ( unsigned int seconds ); +extern void sleep_fixed ( unsigned int secs ); #endif /* _IPXE_TIMER_H */ diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 7d982c326..b1e702e18 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -108,6 +108,17 @@ struct tls_header { /* TLS signature algorithms extension */ #define TLS_SIGNATURE_ALGORITHMS 13 +/* TLS renegotiation information extension */ +#define TLS_RENEGOTIATION_INFO 0xff01 + +/** TLS verification data */ +struct tls_verify_data { + /** Client verification data */ + uint8_t client[12]; + /** Server verification data */ + uint8_t server[12]; +} __attribute__ (( packed )); + /** TLS RX state machine state */ enum tls_rx_state { TLS_RX_HEADER = 0, @@ -231,8 +242,8 @@ struct md5_sha1_digest { /** MD5+SHA1 digest size */ #define MD5_SHA1_DIGEST_SIZE sizeof ( struct md5_sha1_digest ) -/** A TLS session */ -struct tls_session { +/** A TLS connection */ +struct tls_connection { /** Reference counter */ struct refcnt refcnt; @@ -271,6 +282,10 @@ struct tls_session { uint8_t *handshake_ctx; /** Client certificate (if used) */ struct x509_certificate *cert; + /** Secure renegotiation flag */ + int secure_renegotiation; + /** Verification data */ + struct tls_verify_data verify; /** Server certificate chain */ struct x509_chain *chain; diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index b3ce7b741..68289d26d 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -223,6 +223,9 @@ struct usb_string_descriptor { /** A USB string descriptor */ #define USB_STRING_DESCRIPTOR 3 +/** Language ID for English */ +#define USB_LANG_ENGLISH 0x0409 + /** A USB interface descriptor */ struct usb_interface_descriptor { /** Descriptor header */ @@ -414,7 +417,9 @@ struct usb_endpoint { /** Recycled I/O buffer list */ struct list_head recycled; - /** Refill buffer length */ + /** Refill buffer reserved header length */ + size_t reserve; + /** Refill buffer payload length */ size_t len; /** Maximum fill level */ unsigned int max; @@ -588,13 +593,16 @@ extern void usb_complete_err ( struct usb_endpoint *ep, * Initialise USB endpoint refill * * @v ep USB endpoint - * @v len Refill buffer length (or zero to use endpoint's MTU) + * @v reserve Refill buffer reserved header length + * @v len Refill buffer payload length (zero for endpoint's MTU) * @v max Maximum fill level */ static inline __attribute__ (( always_inline )) void -usb_refill_init ( struct usb_endpoint *ep, size_t len, unsigned int max ) { +usb_refill_init ( struct usb_endpoint *ep, size_t reserve, size_t len, + unsigned int max ) { INIT_LIST_HEAD ( &ep->recycled ); + ep->reserve = reserve; ep->len = len; ep->max = max; } @@ -662,6 +670,8 @@ struct usb_function { struct usb_driver *driver; /** Driver private data */ void *priv; + /** Driver device ID */ + struct usb_device_id *id; /** List of interface numbers * @@ -698,6 +708,8 @@ struct usb_device { char name[32]; /** USB port */ struct usb_port *port; + /** Device speed */ + unsigned int speed; /** List of devices on this bus */ struct list_head list; /** Device address, if assigned */ @@ -719,6 +731,9 @@ struct usb_device { struct usb_endpoint control; /** Completed control transfers */ struct list_head complete; + + /** Default language ID (if known) */ + unsigned int language; }; /** USB device host controller operations */ @@ -1306,6 +1321,8 @@ struct usb_device_id { uint16_t vendor; /** Product ID */ uint16_t product; + /** Arbitrary driver data */ + unsigned long driver_data; }; /** Match-anything ID */ diff --git a/src/include/ipxe/usbhid.h b/src/include/ipxe/usbhid.h index fe9d84455..233534e0f 100644 --- a/src/include/ipxe/usbhid.h +++ b/src/include/ipxe/usbhid.h @@ -33,6 +33,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ USB_REQUEST_TYPE ( 0x0a ) ) +/** Set report */ +#define USBHID_SET_REPORT \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x09 ) ) + +/** Input report type */ +#define USBHID_REPORT_INPUT 0x01 + +/** Output report type */ +#define USBHID_REPORT_OUTPUT 0x02 + +/** Feature report type */ +#define USBHID_REPORT_FEATURE 0x03 + /** A USB human interface device */ struct usb_hid { /** USB function */ @@ -97,6 +111,26 @@ usbhid_set_idle ( struct usb_device *usb, unsigned int interface, interface, NULL, 0 ); } +/** + * Set report + * + * @v usb USB device + * @v interface Interface number + * @v type Report type + * @v report Report ID + * @v data Report data + * @v len Length of report data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usbhid_set_report ( struct usb_device *usb, unsigned int interface, + unsigned int type, unsigned int report, void *data, + size_t len ) { + + return usb_control ( usb, USBHID_SET_REPORT, ( ( type << 8 ) | report ), + interface, data, len ); +} + extern int usbhid_open ( struct usb_hid *hid ); extern void usbhid_close ( struct usb_hid *hid ); extern int usbhid_refill ( struct usb_hid *hid ); diff --git a/src/include/ipxe/usbnet.h b/src/include/ipxe/usbnet.h index 33a8f3f58..a7276eba5 100644 --- a/src/include/ipxe/usbnet.h +++ b/src/include/ipxe/usbnet.h @@ -36,7 +36,7 @@ struct usbnet_device { * * @v usbnet USB network device * @v func USB function - * @v intr Interrupt endpoint operations + * @v intr Interrupt endpoint operations, or NULL * @v in Bulk IN endpoint operations * @v out Bulk OUT endpoint operations */ @@ -53,6 +53,18 @@ usbnet_init ( struct usbnet_device *usbnet, struct usb_function *func, usb_endpoint_init ( &usbnet->out, usb, out ); } +/** + * Check if USB network device has an interrupt endpoint + * + * @v usbnet USB network device + * @ret has_intr Device has an interrupt endpoint + */ +static inline __attribute__ (( always_inline )) int +usbnet_has_intr ( struct usbnet_device *usbnet ) { + + return ( usbnet->intr.driver != NULL ); +} + extern int usbnet_open ( struct usbnet_device *usbnet ); extern void usbnet_close ( struct usbnet_device *usbnet ); extern int usbnet_refill ( struct usbnet_device *usbnet ); diff --git a/src/include/ipxe/uuid.h b/src/include/ipxe/uuid.h index 6c45eb9aa..24c46acaf 100644 --- a/src/include/ipxe/uuid.h +++ b/src/include/ipxe/uuid.h @@ -47,6 +47,6 @@ static inline void uuid_mangle ( union uuid *uuid ) { __bswap_16s ( &uuid->canonical.c ); } -extern char * uuid_ntoa ( const union uuid *uuid ); +extern const char * uuid_ntoa ( const union uuid *uuid ); #endif /* _IPXE_UUID_H */ diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index a09c46316..f3074f14d 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -37,6 +37,104 @@ /* Virtio ABI version, this must match exactly */ #define VIRTIO_PCI_ABI_VERSION 0 +/* PCI capability types: */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 /* Common configuration */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 /* Notifications */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 /* ISR access */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 /* Device specific configuration */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 /* PCI configuration access */ + +#define __u8 uint8_t +#define __le16 uint16_t +#define __le32 uint32_t +#define __le64 uint64_t + +/* This is the PCI capability header: */ +struct virtio_pci_cap { + __u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + __u8 cap_next; /* Generic PCI field: next ptr. */ + __u8 cap_len; /* Generic PCI field: capability length */ + __u8 cfg_type; /* Identifies the structure. */ + __u8 bar; /* Where to find it. */ + __u8 padding[3]; /* Pad to full dword. */ + __le32 offset; /* Offset within bar. */ + __le32 length; /* Length of the structure, in bytes. */ +}; + +struct virtio_pci_notify_cap { + struct virtio_pci_cap cap; + __le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ +}; + +struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; + __u8 pci_cfg_data[4]; /* Data for BAR access. */ +}; + +/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */ +struct virtio_pci_common_cfg { + /* About the whole device. */ + __le32 device_feature_select; /* read-write */ + __le32 device_feature; /* read-only */ + __le32 guest_feature_select; /* read-write */ + __le32 guest_feature; /* read-write */ + __le16 msix_config; /* read-write */ + __le16 num_queues; /* read-only */ + __u8 device_status; /* read-write */ + __u8 config_generation; /* read-only */ + + /* About a specific virtqueue. */ + __le16 queue_select; /* read-write */ + __le16 queue_size; /* read-write, power of 2. */ + __le16 queue_msix_vector; /* read-write */ + __le16 queue_enable; /* read-write */ + __le16 queue_notify_off; /* read-only */ + __le32 queue_desc_lo; /* read-write */ + __le32 queue_desc_hi; /* read-write */ + __le32 queue_avail_lo; /* read-write */ + __le32 queue_avail_hi; /* read-write */ + __le32 queue_used_lo; /* read-write */ + __le32 queue_used_hi; /* read-write */ +}; + +/* Virtio 1.0 PCI region descriptor. We support memory mapped I/O, port I/O, + * and PCI config space access via the cfg PCI capability as a fallback. */ +struct virtio_pci_region { + void *base; + size_t length; + u8 bar; + +/* How to interpret the base field */ +#define VIRTIO_PCI_REGION_TYPE_MASK 0x00000003 +/* The base field is a memory address */ +#define VIRTIO_PCI_REGION_MEMORY 0x00000001 +/* The base field is a port address */ +#define VIRTIO_PCI_REGION_PORT 0x00000002 +/* The base field is an offset within the PCI bar */ +#define VIRTIO_PCI_REGION_PCI_CONFIG 0x00000003 + unsigned flags; +}; + +/* Virtio 1.0 device state */ +struct virtio_pci_modern_device { + struct pci_device *pci; + + /* VIRTIO_PCI_CAP_PCI_CFG position */ + int cfg_cap_pos; + + /* VIRTIO_PCI_CAP_COMMON_CFG data */ + struct virtio_pci_region common; + + /* VIRTIO_PCI_CAP_DEVICE_CFG data */ + struct virtio_pci_region device; + + /* VIRTIO_PCI_CAP_ISR_CFG data */ + struct virtio_pci_region isr; + + /* VIRTIO_PCI_CAP_NOTIFY_CFG data */ + int notify_cap_pos; +}; + static inline u32 vp_get_features(unsigned int ioaddr) { return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); @@ -96,6 +194,117 @@ static inline void vp_del_vq(unsigned int ioaddr, int queue_index) outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); } +struct vring_virtqueue; + +void vp_free_vq(struct vring_virtqueue *vq); int vp_find_vq(unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq); + + +/* Virtio 1.0 I/O routines abstract away the three possible HW access + * mechanisms - memory, port I/O, and PCI cfg space access. Also built-in + * are endianness conversions - to LE on write and from LE on read. */ + +void vpm_iowrite8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u8 data, size_t offset); + +void vpm_iowrite16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u16 data, size_t offset); + +void vpm_iowrite32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, u32 data, size_t offset); + +static inline void vpm_iowrite64(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, + u64 data, size_t offset_lo, size_t offset_hi) +{ + vpm_iowrite32(vdev, region, (u32)data, offset_lo); + vpm_iowrite32(vdev, region, data >> 32, offset_hi); +} + +u8 vpm_ioread8(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset); + +u16 vpm_ioread16(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset); + +u32 vpm_ioread32(struct virtio_pci_modern_device *vdev, + struct virtio_pci_region *region, size_t offset); + +/* Virtio 1.0 device manipulation routines */ + +#define COMMON_OFFSET(field) offsetof(struct virtio_pci_common_cfg, field) + +static inline void vpm_reset(struct virtio_pci_modern_device *vdev) +{ + vpm_iowrite8(vdev, &vdev->common, 0, COMMON_OFFSET(device_status)); + while (vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status))) + mdelay(1); +} + +static inline u8 vpm_get_status(struct virtio_pci_modern_device *vdev) +{ + return vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status)); +} + +static inline void vpm_add_status(struct virtio_pci_modern_device *vdev, + u8 status) +{ + u8 curr_status = vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status)); + vpm_iowrite8(vdev, &vdev->common, + curr_status | status, COMMON_OFFSET(device_status)); +} + +static inline u64 vpm_get_features(struct virtio_pci_modern_device *vdev) +{ + u32 features_lo, features_hi; + + vpm_iowrite32(vdev, &vdev->common, 0, COMMON_OFFSET(device_feature_select)); + features_lo = vpm_ioread32(vdev, &vdev->common, COMMON_OFFSET(device_feature)); + vpm_iowrite32(vdev, &vdev->common, 1, COMMON_OFFSET(device_feature_select)); + features_hi = vpm_ioread32(vdev, &vdev->common, COMMON_OFFSET(device_feature)); + + return ((u64)features_hi << 32) | features_lo; +} + +static inline void vpm_set_features(struct virtio_pci_modern_device *vdev, + u64 features) +{ + u32 features_lo = (u32)features; + u32 features_hi = features >> 32; + + vpm_iowrite32(vdev, &vdev->common, 0, COMMON_OFFSET(guest_feature_select)); + vpm_iowrite32(vdev, &vdev->common, features_lo, COMMON_OFFSET(guest_feature)); + vpm_iowrite32(vdev, &vdev->common, 1, COMMON_OFFSET(guest_feature_select)); + vpm_iowrite32(vdev, &vdev->common, features_hi, COMMON_OFFSET(guest_feature)); +} + +static inline void vpm_get(struct virtio_pci_modern_device *vdev, + unsigned offset, void *buf, unsigned len) +{ + u8 *ptr = buf; + unsigned i; + + for (i = 0; i < len; i++) + ptr[i] = vpm_ioread8(vdev, &vdev->device, offset + i); +} + +static inline u8 vpm_get_isr(struct virtio_pci_modern_device *vdev) +{ + return vpm_ioread8(vdev, &vdev->isr, 0); +} + +void vpm_notify(struct virtio_pci_modern_device *vdev, + struct vring_virtqueue *vq); + +int vpm_find_vqs(struct virtio_pci_modern_device *vdev, + unsigned nvqs, struct vring_virtqueue *vqs); + +int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type); + +int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, + u32 align, u32 start, u32 size, + struct virtio_pci_region *region); + +void virtio_pci_unmap_capability(struct virtio_pci_region *region); #endif /* _VIRTIO_PCI_H_ */ diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index c687acab7..852769f29 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -1,6 +1,8 @@ #ifndef _VIRTIO_RING_H_ # define _VIRTIO_RING_H_ +#include + /* Status byte for guest to report progress, and synchronize features. */ /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 @@ -8,9 +10,18 @@ #define VIRTIO_CONFIG_S_DRIVER 2 /* Driver has used its parts of the config, and is happy */ #define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* Driver has finished configuring features */ +#define VIRTIO_CONFIG_S_FEATURES_OK 8 /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 +/* Virtio feature flags used to negotiate device and driver features. */ +/* Can the device handle any descriptor layout? */ +#define VIRTIO_F_ANY_LAYOUT 27 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 +#define VIRTIO_F_IOMMU_PLATFORM 33 + #define MAX_QUEUE_NUM (256) #define VRING_DESC_F_NEXT 1 @@ -61,16 +72,15 @@ struct vring { + PAGE_MASK) & ~PAGE_MASK) + \ (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) -typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)]; - struct vring_virtqueue { - virtio_queue_t queue; + unsigned char *queue; struct vring vring; u16 free_head; u16 last_used_idx; - void *vdata[MAX_QUEUE_NUM]; + void **vdata; /* PCI */ int queue_index; + struct virtio_pci_region notification; }; struct vring_list { @@ -84,7 +94,7 @@ static inline void vring_init(struct vring *vr, unsigned int i; unsigned long pa; - vr->num = num; + vr->num = num; /* physical address of desc must be page aligned */ @@ -92,13 +102,13 @@ static inline void vring_init(struct vring *vr, pa = (pa + PAGE_MASK) & ~PAGE_MASK; vr->desc = phys_to_virt(pa); - vr->avail = (struct vring_avail *)&vr->desc[num]; + vr->avail = (struct vring_avail *)&vr->desc[num]; /* physical address of used must be page aligned */ pa = virt_to_phys(&vr->avail->ring[num]); pa = (pa + PAGE_MASK) & ~PAGE_MASK; - vr->used = phys_to_virt(pa); + vr->used = phys_to_virt(pa); for (i = 0; i < num - 1; i++) vr->desc[i].next = i + 1; @@ -134,6 +144,7 @@ void *vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], unsigned int out, unsigned int in, void *index, int num_added); -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); +void vring_kick(struct virtio_pci_modern_device *vdev, unsigned int ioaddr, + struct vring_virtqueue *vq, int num_added); #endif /* _VIRTIO_RING_H_ */ diff --git a/src/include/ipxe/vmbus.h b/src/include/ipxe/vmbus.h index 26fc578c6..682441857 100644 --- a/src/include/ipxe/vmbus.h +++ b/src/include/ipxe/vmbus.h @@ -479,6 +479,8 @@ struct vmbus_device { /** Hyper-V hypervisor */ struct hv_hypervisor *hv; + /** Channel instance */ + union uuid instance; /** Channel ID */ unsigned int channel; /** Monitor ID */ @@ -527,6 +529,12 @@ struct vmbus_driver { * @ret rc Return status code */ int ( * probe ) ( struct vmbus_device *vmdev ); + /** Reset device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ + int ( * reset ) ( struct vmbus_device *vmdev ); /** Remove device * * @v vmdev VMBus device @@ -609,6 +617,23 @@ vmbus_unregister_pages ( struct vmbus_device *vmdev, list_del ( &pages->list ); } +extern unsigned int vmbus_obsolete_gpadl; + +/** + * Check if GPADL is obsolete + * + * @v gpadl GPADL ID + * @v is_obsolete GPADL ID is obsolete + * + * Check if GPADL is obsolete (i.e. was created before the most recent + * Hyper-V reset). + */ +static inline __attribute__ (( always_inline )) int +vmbus_gpadl_is_obsolete ( unsigned int gpadl ) { + + return ( gpadl <= vmbus_obsolete_gpadl ); +} + extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, size_t len ); extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, @@ -629,6 +654,7 @@ extern int vmbus_poll ( struct vmbus_device *vmdev ); extern void vmbus_dump_channel ( struct vmbus_device *vmdev ); extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ); +extern int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ); extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ); #endif /* _IPXE_VMBUS_H */ diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index 0daaf5e59..78eeafbfb 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -16,6 +16,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +struct image; + /** An X.509 serial number */ struct x509_serial { /** Raw serial number */ @@ -187,8 +189,8 @@ struct x509_certificate { /** Link in certificate store */ struct x509_link store; - /** Certificate has been validated */ - int valid; + /** Flags */ + unsigned int flags; /** Maximum number of subsequent certificates in chain */ unsigned int path_remaining; @@ -214,6 +216,16 @@ struct x509_certificate { struct x509_extensions extensions; }; +/** X.509 certificate flags */ +enum x509_flags { + /** Certificate has been validated */ + X509_FL_VALIDATED = 0x0001, + /** Certificate was added at build time */ + X509_FL_PERMANENT = 0x0002, + /** Certificate was added explicitly at run time */ + X509_FL_EXPLICIT = 0x0004, +}; + /** * Get reference to X.509 certificate * @@ -358,6 +370,8 @@ extern int x509_auto_append ( struct x509_chain *chain, extern int x509_validate_chain ( struct x509_chain *chain, time_t time, struct x509_chain *store, struct x509_root *root ); +extern int image_x509 ( struct image *image, size_t offset, + struct x509_certificate **cert ); /* Functions exposed only for unit testing */ extern int x509_check_issuer ( struct x509_certificate *cert, @@ -369,13 +383,22 @@ extern int x509_check_root ( struct x509_certificate *cert, struct x509_root *root ); extern int x509_check_time ( struct x509_certificate *cert, time_t time ); +/** + * Check if X.509 certificate is valid + * + * @v cert X.509 certificate + */ +static inline int x509_is_valid ( struct x509_certificate *cert ) { + return ( cert->flags & X509_FL_VALIDATED ); +} + /** * Invalidate X.509 certificate * * @v cert X.509 certificate */ static inline void x509_invalidate ( struct x509_certificate *cert ) { - cert->valid = 0; + cert->flags &= ~X509_FL_VALIDATED; cert->path_remaining = 0; } diff --git a/src/include/ipxe/xen.h b/src/include/ipxe/xen.h index eac1145ad..0fb8b7625 100644 --- a/src/include/ipxe/xen.h +++ b/src/include/ipxe/xen.h @@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define __XEN_INTERFACE_VERSION__ 0x00040400 #include +#include #include #include #include @@ -58,6 +59,19 @@ struct xen_hypervisor { struct xen_store store; }; +/** + * Test and clear pending event + * + * @v xen Xen hypervisor + * @v port Event channel port + * @ret pending Event was pending + */ +static inline __attribute__ (( always_inline )) int +xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) { + + return test_and_clear_bit ( port, xen->shared->evtchn_pending ); +} + #include /** diff --git a/src/include/ipxe/xsigo.h b/src/include/ipxe/xsigo.h new file mode 100644 index 000000000..f4f14c487 --- /dev/null +++ b/src/include/ipxe/xsigo.h @@ -0,0 +1,406 @@ +#ifndef _IPXE_XSIGO_H +#define _IPXE_XSIGO_H + +/** @file + * + * Xsigo virtual Ethernet devices + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** Xsigo directory service record name */ +#define XDS_SERVICE_NAME "XSIGOXDS" + +/** Xsigo configuration manager service ID */ +#define XCM_SERVICE_ID { 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x97, 0x01 } + +/** Xsigo management class */ +#define XSIGO_MGMT_CLASS 0x0b + +/** Xsigo management class version */ +#define XSIGO_MGMT_CLASS_VERSION 2 + +/** Xsigo configuration manager request MAD */ +#define XSIGO_ATTR_XCM_REQUEST 0xb002 + +/** Generic operating system type */ +#define XSIGO_OS_TYPE_GENERIC 0x40 + +/** Xsigo virtual Ethernet broadcast GID prefix */ +#define XVE_PREFIX 0xff15101cUL + +/** Xsigo resource types */ +enum xsigo_resource_type { + /** Virtual Ethernet resource type */ + XSIGO_RESOURCE_XVE = ( 1 << 6 ), + /** Absence-of-high-availability "resource" type */ + XSIGO_RESOURCE_NO_HA = ( 1 << 4 ), +}; + +/** A Xsigo server identifier */ +struct xsigo_server_id { + /** Virtual machine ID */ + uint32_t vm; + /** Port GUID */ + union ib_guid guid; +} __attribute__ (( packed )); + +/** A Xsigo configuration manager identifier */ +struct xsigo_manager_id { + /** Port GUID */ + union ib_guid guid; + /** LID */ + uint16_t lid; + /** Reserved */ + uint8_t reserved[10]; +} __attribute__ (( packed )); + +/** A Xsigo configuration manager request MAD */ +struct xsigo_managers_request { + /** MAD header */ + struct ib_mad_hdr mad_hdr; + /** Reserved */ + uint8_t reserved0[32]; + /** Server ID */ + struct xsigo_server_id server; + /** Hostname */ + char hostname[ 65 /* Seriously, guys? */ ]; + /** OS version */ + char os_version[32]; + /** CPU architecture */ + char arch[16]; + /** OS type */ + uint8_t os_type; + /** Reserved */ + uint8_t reserved1[3]; + /** Firmware version */ + uint64_t firmware_version; + /** Hardware version */ + uint32_t hardware_version; + /** Driver version */ + uint32_t driver_version; + /** System ID */ + union ib_gid system_id; + /** Resource types */ + uint16_t resources; + /** Reserved */ + uint8_t reserved2[2]; + /** Build version */ + char build[16]; + /** Reserved */ + uint8_t reserved3[19]; +} __attribute__ (( packed )); + +/** Resource types are present */ +#define XSIGO_RESOURCES_PRESENT 0x8000 + +/** A Xsigo configuration manager reply MAD */ +struct xsigo_managers_reply { + /** MAD header */ + struct ib_mad_hdr mad_hdr; + /** Reserved */ + uint8_t reserved0[32]; + /** Server ID */ + struct xsigo_server_id server; + /** Number of XCM records */ + uint8_t count; + /** Version */ + uint8_t version; + /** Reserved */ + uint8_t reserved1[2]; + /** Managers */ + struct xsigo_manager_id manager[8]; + /** Reserved */ + uint8_t reserved2[24]; +} __attribute__ (( packed )); + +/** A Xsigo MAD */ +union xsigo_mad { + /** Generic MAD */ + union ib_mad mad; + /** Configuration manager request */ + struct xsigo_managers_request request; + /** Configuration manager reply */ + struct xsigo_managers_reply reply; +} __attribute__ (( packed )); + +/** An XSMP node identifier */ +struct xsmp_node_id { + /** Auxiliary ID (never used) */ + uint32_t aux; + /** Port GUID */ + union ib_guid guid; +} __attribute__ (( packed )); + +/** An XSMP message header */ +struct xsmp_message_header { + /** Message type */ + uint8_t type; + /** Reason code */ + uint8_t code; + /** Length */ + uint16_t len; + /** Sequence number */ + uint32_t seq; + /** Source node ID */ + struct xsmp_node_id src; + /** Destination node ID */ + struct xsmp_node_id dst; +} __attribute__ (( packed )); + +/** XSMP message types */ +enum xsmp_message_type { + /** Session message type */ + XSMP_TYPE_SESSION = 1, + /** Virtual Ethernet message type */ + XSMP_TYPE_XVE = 6, +}; + +/** An XSMP session message */ +struct xsmp_session_message { + /** Message header */ + struct xsmp_message_header hdr; + /** Message type */ + uint8_t type; + /** Reason code */ + uint8_t code; + /** Length (excluding message header) */ + uint16_t len; + /** Operating system type */ + uint8_t os_type; + /** Reserved */ + uint8_t reserved0; + /** Resource types */ + uint16_t resources; + /** Driver version */ + uint32_t driver_version; + /** Required chassis version */ + uint32_t chassis_version; + /** Boot flags */ + uint32_t boot; + /** Firmware version */ + uint64_t firmware_version; + /** Hardware version */ + uint32_t hardware_version; + /** Vendor part ID */ + uint32_t vendor; + /** Protocol version */ + uint32_t xsmp_version; + /** Chassis name */ + char chassis[32]; + /** Session name */ + char session[32]; + /** Reserved */ + uint8_t reserved1[120]; +} __attribute__ (( packed )); + +/** XSMP session message types */ +enum xsmp_session_type { + /** Keepalive message */ + XSMP_SESSION_TYPE_HELLO = 1, + /** Initial registration message */ + XSMP_SESSION_TYPE_REGISTER = 2, + /** Registration confirmation message */ + XSMP_SESSION_TYPE_CONFIRM = 3, + /** Registration rejection message */ + XSMP_SESSION_TYPE_REJECT = 4, + /** Shutdown message */ + XSMP_SESSION_TYPE_SHUTDOWN = 5, +}; + +/** XSMP boot flags */ +enum xsmp_session_boot { + /** PXE boot */ + XSMP_BOOT_PXE = ( 1 << 0 ), +}; + +/** XSMP virtual Ethernet channel adapter parameters */ +struct xsmp_xve_ca { + /** Subnet prefix (little-endian) */ + union ib_guid prefix_le; + /** Control queue pair number */ + uint32_t ctrl; + /** Data queue pair number */ + uint32_t data; + /** Partition key */ + uint16_t pkey; + /** Queue key */ + uint16_t qkey; +} __attribute__ (( packed )); + +/** XSMP virtual Ethernet MAC address */ +struct xsmp_xve_mac { + /** High 16 bits */ + uint16_t high; + /** Low 32 bits */ + uint32_t low; +} __attribute__ (( packed )); + +/** An XSMP virtual Ethernet message */ +struct xsmp_xve_message { + /** Message header */ + struct xsmp_message_header hdr; + /** Message type */ + uint8_t type; + /** Reason code */ + uint8_t code; + /** Length (excluding message header) */ + uint16_t len; + /** Update bitmask */ + uint32_t update; + /** Resource identifier */ + union ib_guid resource; + /** TCA GUID (little-endian) */ + union ib_guid guid_le; + /** TCA LID */ + uint16_t lid; + /** MAC address (little-endian) */ + struct xsmp_xve_mac mac_le; + /** Rate */ + uint16_t rate; + /** Administrative state (non-zero = "up") */ + uint16_t state; + /** Encapsulation (apparently obsolete and unused) */ + uint16_t encap; + /** MTU */ + uint16_t mtu; + /** Installation flags (apparently obsolete and unused) */ + uint32_t install; + /** Interface name */ + char name[16]; + /** Service level */ + uint16_t sl; + /** Flow control enabled (apparently obsolete and unused) */ + uint16_t flow; + /** Committed rate (in Mbps) */ + uint16_t committed_mbps; + /** Peak rate (in Mbps) */ + uint16_t peak_mbps; + /** Committed burst size (in bytes) */ + uint32_t committed_burst; + /** Peak burst size (in bytes) */ + uint32_t peak_burst; + /** VMware index */ + uint8_t vmware; + /** Reserved */ + uint8_t reserved0; + /** Multipath flags */ + uint16_t multipath; + /** Multipath group name */ + char group[48]; + /** Link aggregation flag */ + uint8_t agg; + /** Link aggregation policy */ + uint8_t policy; + /** Network ID */ + uint32_t network; + /** Mode */ + uint8_t mode; + /** Uplink type */ + uint8_t uplink; + /** Target channel adapter parameters */ + struct xsmp_xve_ca tca; + /** Host channel adapter parameters */ + struct xsmp_xve_ca hca; + /** Reserved */ + uint8_t reserved1[336]; +} __attribute__ (( packed )); + +/** XSMP virtual Ethernet message types */ +enum xsmp_xve_type { + /** Install virtual NIC */ + XSMP_XVE_TYPE_INSTALL = 1, + /** Delete virtual NIC */ + XSMP_XVE_TYPE_DELETE = 2, + /** Update virtual NIC */ + XSMP_XVE_TYPE_UPDATE = 3, + /** Set operational state up */ + XSMP_XVE_TYPE_OPER_UP = 6, + /** Set operational state down */ + XSMP_XVE_TYPE_OPER_DOWN = 7, + /** Get operational state */ + XSMP_XVE_TYPE_OPER_REQ = 15, + /** Virtual NIC is ready */ + XSMP_XVE_TYPE_READY = 20, +}; + +/** XSMP virtual Ethernet message codes */ +enum xsmp_xve_code { + /* Something went wrong */ + XSMP_XVE_CODE_ERROR = 0x84, +}; + +/** XSMP virtual Ethernet update bitmask */ +enum xsmp_xve_update { + /** Update MTU */ + XSMP_XVE_UPDATE_MTU = ( 1 << 2 ), + /** Update administrative state */ + XSMP_XVE_UPDATE_STATE = ( 1 << 6 ), + /** Update gateway to mark as down */ + XSMP_XVE_UPDATE_GW_DOWN = ( 1 << 30 ), + /** Update gateway information */ + XSMP_XVE_UPDATE_GW_CHANGE = ( 1 << 31 ), +}; + +/** XSMP virtual Ethernet modes */ +enum xsmp_xve_mode { + /** Reliable Connected */ + XSMP_XVE_MODE_RC = 1, + /** Unreliable Datagram */ + XSMP_XVE_MODE_UD = 2, +}; + +/** XSMP virtual Ethernet uplink types */ +enum xsmp_xve_uplink { + /** No uplink */ + XSMP_XVE_NO_UPLINK = 1, + /** Has uplink */ + XSMP_XVE_UPLINK = 2, +}; + +/** An XSMP message */ +union xsmp_message { + /** Message header */ + struct xsmp_message_header hdr; + /** Session message */ + struct xsmp_session_message sess; + /** Virtual Ethernet message */ + struct xsmp_xve_message xve; +}; + +/** Delay between attempts to open the Infiniband device + * + * This is a policy decision. + */ +#define XSIGO_OPEN_RETRY_DELAY ( 2 * TICKS_PER_SEC ) + +/** Delay between unsuccessful discovery attempts + * + * This is a policy decision. + */ +#define XSIGO_DISCOVERY_FAILURE_DELAY ( 10 * TICKS_PER_SEC ) + +/** Delay between successful discovery attempts + * + * This is a policy decision. + */ +#define XSIGO_DISCOVERY_SUCCESS_DELAY ( 20 * TICKS_PER_SEC ) + +/** Delay between keepalive requests + * + * This is a policy decision. + */ +#define XSIGO_KEEPALIVE_INTERVAL ( 10 * TICKS_PER_SEC ) + +/** Maximum time to wait for a keepalive response + * + * This is a policy decision. + */ +#define XSIGO_KEEPALIVE_MAX_WAIT ( 2 * TICKS_PER_SEC ) + +#endif /* _IPXE_XSIGO_H */ diff --git a/src/include/linux_api.h b/src/include/linux_api.h index 28a3cda3b..fe9fa910f 100644 --- a/src/include/linux_api.h +++ b/src/include/linux_api.h @@ -46,6 +46,8 @@ typedef __kernel_loff_t loff_t; #include typedef unsigned long nfds_t; typedef uint32_t useconds_t; +typedef uint32_t socklen_t; +struct sockaddr; #define MAP_FAILED ( ( void * ) -1 ) #define SEEK_SET 0 @@ -68,6 +70,11 @@ extern void * linux_mmap ( void *addr, __kernel_size_t length, int prot, extern void * linux_mremap ( void *old_address, __kernel_size_t old_size, __kernel_size_t new_size, int flags ); extern int linux_munmap ( void *addr, __kernel_size_t length ); +extern int linux_socket ( int domain, int type_, int protocol ); +extern int linux_bind ( int fd, const struct sockaddr *addr, + socklen_t addrlen ); +extern ssize_t linux_sendto ( int fd, const void *buf, size_t len, int flags, + const struct sockaddr *daddr, socklen_t addrlen ); extern const char * linux_strerror ( int errnum ); diff --git a/src/include/nic.h b/src/include/nic.h index 4c91f57a6..8b06e88f4 100644 --- a/src/include/nic.h +++ b/src/include/nic.h @@ -209,7 +209,8 @@ static inline void * legacy_isa_get_drvdata ( void *hwdev ) { #undef DRIVER #define DRIVER(_name_text,_unused2,_unused3,_name,_probe,_disable) \ - static const char _name ## _text[] = _name_text; \ + static __attribute__ (( unused )) const char \ + _name ## _text[] = _name_text; \ static inline int \ _name ## _probe ( struct nic *nic, void *hwdev ) { \ return _probe ( nic, hwdev ); \ diff --git a/src/include/stdbool.h b/src/include/stdbool.h new file mode 100644 index 000000000..c49a7f192 --- /dev/null +++ b/src/include/stdbool.h @@ -0,0 +1,10 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define bool _Bool +#define true 1 +#define false 0 + +#endif /* _STDBOOL_H */ diff --git a/src/include/stddef.h b/src/include/stddef.h index 3c056294f..fb01c489d 100644 --- a/src/include/stddef.h +++ b/src/include/stddef.h @@ -34,7 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define container_of( ptr, type, field ) ( { \ type *__container; \ - const typeof ( __container->field ) *__field = (ptr); \ + const volatile typeof ( __container->field ) *__field = (ptr); \ __container = ( ( ( void * ) __field ) - \ offsetof ( type, field ) ); \ __container; } ) diff --git a/src/include/string.h b/src/include/string.h index 0fab6c74b..0f4182001 100644 --- a/src/include/string.h +++ b/src/include/string.h @@ -10,6 +10,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include + +extern void * generic_memset ( void *dest, int character, + size_t len ) __nonnull; +extern void * generic_memcpy ( void *dest, const void *src, + size_t len ) __nonnull; +extern void * generic_memmove ( void *dest, const void *src, + size_t len ) __nonnull; + #include /* Architecture-specific code is expected to provide these functions, @@ -18,12 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); void * memset ( void *dest, int character, size_t len ) __nonnull; void * memcpy ( void *dest, const void *src, size_t len ) __nonnull; void * memmove ( void *dest, const void *src, size_t len ) __nonnull; -extern void * generic_memset ( void *dest, int character, - size_t len ) __nonnull; -extern void * generic_memcpy ( void *dest, const void *src, - size_t len ) __nonnull; -extern void * generic_memmove ( void *dest, const void *src, - size_t len ) __nonnull; extern int __pure memcmp ( const void *first, const void *second, size_t len ) __nonnull; diff --git a/src/include/time.h b/src/include/time.h index 462ac6999..ab93a3dbb 100644 --- a/src/include/time.h +++ b/src/include/time.h @@ -39,10 +39,10 @@ struct tm { * @v t Time to fill in, or NULL * @ret time Current time */ -static inline time_t time ( time_t *t ) { +static inline __attribute__ (( always_inline )) time_t time ( time_t *t ) { time_t now; - now = time_now(); + now = ( time_now() + time_offset ); if ( t ) *t = now; return now; diff --git a/src/include/unistd.h b/src/include/unistd.h index d09e1ae30..6c31c0601 100644 --- a/src/include/unistd.h +++ b/src/include/unistd.h @@ -23,19 +23,9 @@ extern int execv ( const char *command, char * const argv[] ); rc; \ } ) -/* Pick up udelay() */ +/* Pick up udelay() and sleep() */ #include -/* - * sleep() prototype is defined by POSIX.1. usleep() prototype is - * defined by 4.3BSD. udelay() and mdelay() prototypes are chosen to - * be reasonably sensible. - * - */ - -extern unsigned int sleep ( unsigned int seconds ); -extern void mdelay ( unsigned long msecs ); - static inline __always_inline void usleep ( unsigned long usecs ) { udelay ( usecs ); } diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index 4db226b9c..f88b8494f 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -30,8 +30,9 @@ extern void set_autoboot_busloc ( unsigned int bus_type, unsigned int location ); extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len ); -extern int uriboot ( struct uri *filename, struct uri *root_path, int drive, - unsigned int flags ); +extern int uriboot ( struct uri *filename, struct uri **root_paths, + unsigned int root_path_count, int drive, + const char *san_filename, unsigned int flags ); extern struct uri * fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); diff --git a/src/include/usr/certmgmt.h b/src/include/usr/certmgmt.h new file mode 100644 index 000000000..4363b03e1 --- /dev/null +++ b/src/include/usr/certmgmt.h @@ -0,0 +1,16 @@ +#ifndef _USR_CERTMGMT_H +#define _USR_CERTMGMT_H + +/** @file + * + * Certificate management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern void certstat ( struct x509_certificate *cert ); + +#endif /* _USR_CERTMGMT_H */ diff --git a/src/include/usr/ibmgmt.h b/src/include/usr/ibmgmt.h new file mode 100644 index 000000000..16a099134 --- /dev/null +++ b/src/include/usr/ibmgmt.h @@ -0,0 +1,16 @@ +#ifndef _USR_IBMGMT_H +#define _USR_IBMGMT_H + +/** @file + * + * Infiniband device management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +struct ib_device; + +extern void ibstat ( struct ib_device *ibdev ); + +#endif /* _USR_IBMGMT_H */ diff --git a/src/include/usr/lotest.h b/src/include/usr/lotest.h index ce0fe5eda..bd66f4a44 100644 --- a/src/include/usr/lotest.h +++ b/src/include/usr/lotest.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int loopback_test ( struct net_device *sender, - struct net_device *receiver, size_t mtu ); + struct net_device *receiver, + size_t mtu, int broadcast ); #endif /* _USR_LOTEST_H */ diff --git a/src/include/usr/ntpmgmt.h b/src/include/usr/ntpmgmt.h new file mode 100644 index 000000000..284e668e6 --- /dev/null +++ b/src/include/usr/ntpmgmt.h @@ -0,0 +1,14 @@ +#ifndef _USR_NTPMGMT_H +#define _USR_NTPMGMT_H + +/** @file + * + * NTP management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern int ntp ( const char *hostname ); + +#endif /* _USR_NTPMGMT_H */ diff --git a/src/interface/bofm/bofm.c b/src/interface/bofm/bofm.c index 545088dc6..54039193a 100644 --- a/src/interface/bofm/bofm.c +++ b/src/interface/bofm/bofm.c @@ -313,12 +313,12 @@ int bofm ( userptr_t bofmtab, struct pci_device *pci ) { } DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n", en.slot, ( en.port + 1 ), - ( ( en.slot || en.port ) ? "" : "(?)" ), + ( ( en.slot || en.port ) ? "" : "(?)" ), 0, PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), PCI_FUNC ( en.busdevfn ), en.mport ); bofm = bofm_find_busdevfn ( en.busdevfn ); if ( ! bofm ) { - DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", + DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0, PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), PCI_FUNC ( en.busdevfn ), en.mport ); continue; diff --git a/src/interface/efi/efi_acpi.c b/src/interface/efi/efi_acpi.c new file mode 100644 index 000000000..a347eaf3a --- /dev/null +++ b/src/interface/efi/efi_acpi.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 + * + * iPXE ACPI API for EFI + * + */ + +#include +#include +#include +#include + +/** ACPI configuration table */ +static EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *rsdp; +EFI_USE_TABLE ( ACPI_10_TABLE, &rsdp, 0 ); + +/** + * Locate ACPI root system description table + * + * @ret rsdt ACPI root system description table, or UNULL + */ +static userptr_t efi_find_rsdt ( void ) { + + /* Locate RSDT via ACPI configuration table, if available */ + if ( rsdp ) + return phys_to_user ( rsdp->RsdtAddress ); + + return UNULL; +} + +PROVIDE_ACPI ( efi, acpi_find_rsdt, efi_find_rsdt ); diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c new file mode 100644 index 000000000..91f830a11 --- /dev/null +++ b/src/interface/efi/efi_block.c @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 + * + * EFI block device protocols + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** ACPI table protocol protocol */ +static EFI_ACPI_TABLE_PROTOCOL *acpi; +EFI_REQUEST_PROTOCOL ( EFI_ACPI_TABLE_PROTOCOL, &acpi ); + +/** Boot filename */ +static wchar_t efi_block_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; + +/** iPXE EFI block device vendor device path GUID */ +#define IPXE_BLOCK_DEVICE_PATH_GUID \ + { 0x8998b594, 0xf531, 0x4e87, \ + { 0x8b, 0xdf, 0x8f, 0x88, 0x54, 0x3e, 0x99, 0xd4 } } + +/** iPXE EFI block device vendor device path GUID */ +static EFI_GUID ipxe_block_device_path_guid + = IPXE_BLOCK_DEVICE_PATH_GUID; + +/** An iPXE EFI block device vendor device path */ +struct efi_block_vendor_path { + /** Generic vendor device path */ + VENDOR_DEVICE_PATH vendor; + /** Block device URI */ + CHAR16 uri[0]; +} __attribute__ (( packed )); + +/** EFI SAN device private data */ +struct efi_block_data { + /** SAN device */ + struct san_device *sandev; + /** EFI handle */ + EFI_HANDLE handle; + /** Media descriptor */ + EFI_BLOCK_IO_MEDIA media; + /** Block I/O protocol */ + EFI_BLOCK_IO_PROTOCOL block_io; + /** Device path protocol */ + EFI_DEVICE_PATH_PROTOCOL *path; +}; + +/** + * Read from or write to EFI block device + * + * @v sandev SAN device + * @v lba Starting LBA + * @v data Data buffer + * @v len Size of buffer + * @v sandev_rw SAN device read/write method + * @ret rc Return status code + */ +static int efi_block_rw ( struct san_device *sandev, uint64_t lba, + void *data, size_t len, + int ( * sandev_rw ) ( struct san_device *sandev, + uint64_t lba, unsigned int count, + userptr_t buffer ) ) { + struct efi_block_data *block = sandev->priv; + unsigned int count; + int rc; + + /* Sanity check */ + count = ( len / block->media.BlockSize ); + if ( ( count * block->media.BlockSize ) != len ) { + DBGC ( sandev, "EFIBLK %#02x impossible length %#zx\n", + sandev->drive, len ); + return -EINVAL; + } + + /* Read from / write to block device */ + if ( ( rc = sandev_rw ( sandev, lba, count, + virt_to_user ( data ) ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Reset EFI block device + * + * @v block_io Block I/O protocol + * @v verify Perform extended verification + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *block_io, + BOOLEAN verify __unused ) { + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; + int rc; + + DBGC2 ( sandev, "EFIBLK %#02x reset\n", sandev->drive ); + efi_snp_claim(); + rc = sandev_reset ( sandev ); + efi_snp_release(); + return EFIRC ( rc ); +} + +/** + * Read from EFI block device + * + * @v block_io Block I/O protocol + * @v media Media identifier + * @v lba Starting LBA + * @v len Size of buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, + EFI_LBA lba, UINTN len, VOID *data ) { + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; + int rc; + + DBGC2 ( sandev, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", + sandev->drive, lba, data, ( ( size_t ) len ) ); + efi_snp_claim(); + rc = efi_block_rw ( sandev, lba, data, len, sandev_read ); + efi_snp_release(); + return EFIRC ( rc ); +} + +/** + * Write to EFI block device + * + * @v block_io Block I/O protocol + * @v media Media identifier + * @v lba Starting LBA + * @v len Size of buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, + EFI_LBA lba, UINTN len, VOID *data ) { + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; + int rc; + + DBGC2 ( sandev, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", + sandev->drive, lba, data, ( ( size_t ) len ) ); + efi_snp_claim(); + rc = efi_block_rw ( sandev, lba, data, len, sandev_write ); + efi_snp_release(); + return EFIRC ( rc ); +} + +/** + * Flush data to EFI block device + * + * @v block_io Block I/O protocol + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_block_io_flush ( EFI_BLOCK_IO_PROTOCOL *block_io ) { + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; + + DBGC2 ( sandev, "EFIBLK %#02x flush\n", sandev->drive ); + + /* Nothing to do */ + return 0; +} + +/** + * Connect all possible drivers to EFI block device + * + * @v sandev SAN device + */ +static void efi_block_connect ( struct san_device *sandev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block_data *block = sandev->priv; + EFI_STATUS efirc; + int rc; + + /* Try to connect all possible drivers to this block device */ + if ( ( efirc = bs->ConnectController ( block->handle, NULL, + NULL, 1 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not connect drivers: %s\n", + sandev->drive, strerror ( rc ) ); + /* May not be an error; may already be connected */ + } + DBGC2 ( sandev, "EFIBLK %#02x supports protocols:\n", sandev->drive ); + DBGC2_EFI_PROTOCOLS ( sandev, block->handle ); +} + +/** + * Hook EFI block 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 efi_block_hook ( unsigned int drive, struct uri **uris, + unsigned int count, unsigned int flags ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DEVICE_PATH_PROTOCOL *end; + struct efi_block_vendor_path *vendor; + struct efi_snp_device *snpdev; + struct san_device *sandev; + struct efi_block_data *block; + size_t prefix_len; + size_t uri_len; + size_t vendor_len; + size_t len; + char *uri_buf; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + if ( ! count ) { + DBG ( "EFIBLK has no URIs\n" ); + rc = -ENOTTY; + goto err_no_uris; + } + + /* Find an appropriate parent device handle */ + snpdev = last_opened_snpdev(); + if ( ! snpdev ) { + DBG ( "EFIBLK could not identify SNP device\n" ); + rc = -ENODEV; + goto err_no_snpdev; + } + + /* Calculate length of private data */ + prefix_len = efi_devpath_len ( snpdev->path ); + uri_len = format_uri ( uris[0], NULL, 0 ); + vendor_len = ( sizeof ( *vendor ) + + ( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) ); + len = ( sizeof ( *block ) + uri_len + 1 /* NUL */ + prefix_len + + vendor_len + sizeof ( *end ) ); + + /* Allocate and initialise structure */ + sandev = alloc_sandev ( uris, count, len ); + if ( ! sandev ) { + rc = -ENOMEM; + goto err_alloc; + } + block = sandev->priv; + block->sandev = sandev; + block->media.MediaPresent = 1; + block->media.LogicalBlocksPerPhysicalBlock = 1; + block->block_io.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + block->block_io.Media = &block->media; + block->block_io.Reset = efi_block_io_reset; + block->block_io.ReadBlocks = efi_block_io_read; + block->block_io.WriteBlocks = efi_block_io_write; + block->block_io.FlushBlocks = efi_block_io_flush; + uri_buf = ( ( ( void * ) block ) + sizeof ( *block ) ); + block->path = ( ( ( void * ) uri_buf ) + uri_len + 1 /* NUL */ ); + + /* Construct device path */ + memcpy ( block->path, snpdev->path, prefix_len ); + vendor = ( ( ( void * ) block->path ) + prefix_len ); + vendor->vendor.Header.Type = HARDWARE_DEVICE_PATH; + vendor->vendor.Header.SubType = HW_VENDOR_DP; + vendor->vendor.Header.Length[0] = ( vendor_len & 0xff ); + vendor->vendor.Header.Length[1] = ( vendor_len >> 8 ); + memcpy ( &vendor->vendor.Guid, &ipxe_block_device_path_guid, + sizeof ( vendor->vendor.Guid ) ); + format_uri ( uris[0], uri_buf, ( uri_len + 1 /* NUL */ ) ); + efi_snprintf ( vendor->uri, ( uri_len + 1 /* NUL */ ), "%s", uri_buf ); + end = ( ( ( void * ) vendor ) + vendor_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + DBGC ( sandev, "EFIBLK %#02x has device path %s\n", + drive, efi_devpath_text ( block->path ) ); + + /* Register SAN device */ + if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x could not register: %s\n", + drive, strerror ( rc ) ); + goto err_register; + } + + /* Update media descriptor */ + block->media.BlockSize = + ( sandev->capacity.blksize << sandev->blksize_shift ); + block->media.LastBlock = + ( ( sandev->capacity.blocks >> sandev->blksize_shift ) - 1 ); + + /* Install protocols */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &block->handle, + &efi_block_io_protocol_guid, &block->block_io, + &efi_device_path_protocol_guid, block->path, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not install protocols: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_install; + } + + /* Connect all possible protocols */ + efi_block_connect ( sandev ); + + return drive; + + bs->UninstallMultipleProtocolInterfaces ( + block->handle, + &efi_block_io_protocol_guid, &block->block_io, + &efi_device_path_protocol_guid, block->path, NULL ); + err_install: + unregister_sandev ( sandev ); + err_register: + sandev_put ( sandev ); + err_alloc: + err_no_snpdev: + err_no_uris: + return rc; +} + +/** + * Unhook EFI block device + * + * @v drive Drive number + */ +static void efi_block_unhook ( unsigned int drive ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct san_device *sandev; + struct efi_block_data *block; + + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { + DBG ( "EFIBLK cannot find drive %#02x\n", drive ); + return; + } + block = sandev->priv; + + /* Uninstall protocols */ + bs->UninstallMultipleProtocolInterfaces ( + block->handle, + &efi_block_io_protocol_guid, &block->block_io, + &efi_device_path_protocol_guid, block->path, NULL ); + + /* Unregister SAN device */ + unregister_sandev ( sandev ); + + /* Drop reference to drive */ + sandev_put ( sandev ); +} + +/** An installed ACPI table */ +struct efi_acpi_table { + /** List of installed tables */ + struct list_head list; + /** Table key */ + UINTN key; +}; + +/** List of installed ACPI tables */ +static LIST_HEAD ( efi_acpi_tables ); + +/** + * Install ACPI table + * + * @v hdr ACPI description header + * @ret rc Return status code + */ +static int efi_block_install ( struct acpi_header *hdr ) { + size_t len = le32_to_cpu ( hdr->length ); + struct efi_acpi_table *installed; + EFI_STATUS efirc; + int rc; + + /* Allocate installed table record */ + installed = zalloc ( sizeof ( *installed ) ); + if ( ! installed ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Fill in common parameters */ + strncpy ( hdr->oem_id, "FENSYS", sizeof ( hdr->oem_id ) ); + strncpy ( hdr->oem_table_id, "iPXE", sizeof ( hdr->oem_table_id ) ); + + /* Fix up ACPI checksum */ + acpi_fix_checksum ( hdr ); + + /* Install table */ + if ( ( efirc = acpi->InstallAcpiTable ( acpi, hdr, len, + &installed->key ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( acpi, "EFIBLK could not install %s: %s\n", + acpi_name ( hdr->signature ), strerror ( rc ) ); + DBGC_HDA ( acpi, 0, hdr, len ); + goto err_install; + } + + /* Add to list of installed tables */ + list_add_tail ( &installed->list, &efi_acpi_tables ); + + DBGC ( acpi, "EFIBLK installed %s as ACPI table %#lx:\n", + acpi_name ( hdr->signature ), + ( ( unsigned long ) installed->key ) ); + DBGC_HDA ( acpi, 0, hdr, len ); + return 0; + + list_del ( &installed->list ); + err_install: + free ( installed ); + err_alloc: + return rc; +} + +/** + * Describe EFI block devices + * + * @ret rc Return status code + */ +static int efi_block_describe ( void ) { + struct efi_acpi_table *installed; + struct efi_acpi_table *tmp; + UINTN key; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + if ( ! acpi ) { + DBG ( "EFIBLK has no ACPI table protocol\n" ); + return -ENOTSUP; + } + + /* Uninstall any existing ACPI tables */ + list_for_each_entry_safe ( installed, tmp, &efi_acpi_tables, list ) { + key = installed->key; + if ( ( efirc = acpi->UninstallAcpiTable ( acpi, key ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( acpi, "EFIBLK could not uninstall ACPI table " + "%#lx: %s\n", ( ( unsigned long ) key ), + strerror ( rc ) ); + /* Continue anyway */ + } + list_del ( &installed->list ); + free ( installed ); + } + + /* Install ACPI tables */ + if ( ( rc = acpi_install ( efi_block_install ) ) != 0 ) { + DBGC ( acpi, "EFIBLK could not install ACPI tables: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Try booting from child device of EFI block device + * + * @v sandev SAN device + * @v handle EFI handle + * @v filename Filename (or NULL to use default) + * @v image Image handle to fill in + * @ret rc Return status code + */ +static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, + const char *filename, EFI_HANDLE *image ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block_data *block = sandev->priv; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; + EFI_DEVICE_PATH_PROTOCOL *boot_path; + FILEPATH_DEVICE_PATH *filepath; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t prefix_len; + size_t filepath_len; + size_t boot_path_len; + EFI_STATUS efirc; + int rc; + + /* Identify device path */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_device_path_protocol_guid, + &path.interface, efi_image_handle, + handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + DBGC ( sandev, "EFIBLK %#02x found filesystem with no device " + "path??", sandev->drive ); + rc = -EEFI ( efirc ); + goto err_open_device_path; + } + + /* Check if this device is a child of our block device */ + prefix_len = efi_devpath_len ( block->path ); + if ( memcmp ( path.path, block->path, prefix_len ) != 0 ) { + /* Not a child device */ + rc = -ENOTTY; + goto err_not_child; + } + DBGC ( sandev, "EFIBLK %#02x found child device %s\n", + sandev->drive, efi_devpath_text ( path.path ) ); + + /* Construct device path for boot image */ + end = efi_devpath_end ( path.path ); + prefix_len = ( ( ( void * ) end ) - ( ( void * ) path.path ) ); + filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH + + ( filename ? + ( ( strlen ( filename ) + 1 /* NUL */ ) * + sizeof ( filepath->PathName[0] ) ) : + sizeof ( efi_block_boot_filename ) ) ); + boot_path_len = ( prefix_len + filepath_len + sizeof ( *end ) ); + boot_path = zalloc ( boot_path_len ); + if ( ! boot_path ) { + rc = -ENOMEM; + goto err_alloc_path; + } + memcpy ( boot_path, path.path, prefix_len ); + filepath = ( ( ( void * ) boot_path ) + prefix_len ); + filepath->Header.Type = MEDIA_DEVICE_PATH; + filepath->Header.SubType = MEDIA_FILEPATH_DP; + filepath->Header.Length[0] = ( filepath_len & 0xff ); + filepath->Header.Length[1] = ( filepath_len >> 8 ); + if ( filename ) { + efi_sprintf ( filepath->PathName, "%s", filename ); + } else { + memcpy ( filepath->PathName, efi_block_boot_filename, + sizeof ( efi_block_boot_filename ) ); + } + end = ( ( ( void * ) filepath ) + filepath_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + DBGC ( sandev, "EFIBLK %#02x trying to load %s\n", + sandev->drive, efi_devpath_text ( boot_path ) ); + + /* Try loading boot image from this device */ + if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path, + NULL, 0, image ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not load image: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_load_image; + } + + /* Success */ + rc = 0; + + err_load_image: + free ( boot_path ); + err_alloc_path: + err_not_child: + err_open_device_path: + return rc; +} + +/** + * Boot from EFI block device + * + * @v drive Drive number + * @v filename Filename (or NULL to use default) + * @ret rc Return status code + */ +static int efi_block_boot ( unsigned int drive, const char *filename ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct san_device *sandev; + EFI_HANDLE *handles; + EFI_HANDLE image = NULL; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { + DBG ( "EFIBLK cannot find drive %#02x\n", drive ); + rc = -ENODEV; + goto err_sandev_find; + } + + /* Release SNP devices */ + efi_snp_release(); + + /* Connect all possible protocols */ + efi_block_connect ( sandev ); + + /* Locate all handles supporting the Simple File System protocol */ + if ( ( efirc = bs->LocateHandleBuffer ( + ByProtocol, &efi_simple_file_system_protocol_guid, + NULL, &count, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x cannot locate file systems: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_locate_file_systems; + } + + /* Try booting from any available child device containing a + * suitable boot image. This is something of a wild stab in + * the dark, but should end up conforming to user expectations + * most of the time. + */ + rc = -ENOENT; + for ( i = 0 ; i < count ; i++ ) { + if ( ( rc = efi_block_boot_image ( sandev, handles[i], filename, + &image ) ) != 0 ) + continue; + DBGC ( sandev, "EFIBLK %#02x found boot image\n", + sandev->drive ); + efirc = bs->StartImage ( image, NULL, NULL ); + rc = ( efirc ? -EEFI ( efirc ) : 0 ); + bs->UnloadImage ( image ); + DBGC ( sandev, "EFIBLK %#02x boot image returned: %s\n", + sandev->drive, strerror ( rc ) ); + break; + } + + bs->FreePool ( handles ); + err_locate_file_systems: + efi_snp_claim(); + err_sandev_find: + return rc; +} + +PROVIDE_SANBOOT ( efi, san_hook, efi_block_hook ); +PROVIDE_SANBOOT ( efi, san_unhook, efi_block_unhook ); +PROVIDE_SANBOOT ( efi, san_describe, efi_block_describe ); +PROVIDE_SANBOOT ( efi, san_boot, efi_block_boot ); diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 3b30f3097..047baed47 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -239,6 +239,14 @@ static const char *ansi_sequences[] = { [SCAN_DELETE] = "[3~", [SCAN_PAGE_UP] = "[5~", [SCAN_PAGE_DOWN] = "[6~", + [SCAN_F5] = "[15~", + [SCAN_F6] = "[17~", + [SCAN_F7] = "[18~", + [SCAN_F8] = "[19~", + [SCAN_F9] = "[20~", + [SCAN_F10] = "[21~", + [SCAN_F11] = "[23~", + [SCAN_F12] = "[24~", /* EFI translates some (but not all) incoming escape sequences * via the serial console into equivalent scancodes. When it * doesn't recognise a sequence, it helpfully(!) translates diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 84160d643..8ea0a822d 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -71,12 +71,18 @@ struct efi_well_known_guid { static struct efi_well_known_guid efi_well_known_guids[] = { { &efi_absolute_pointer_protocol_guid, "AbsolutePointer" }, + { &efi_acpi_table_protocol_guid, + "AcpiTable" }, + { &efi_apple_net_boot_protocol_guid, + "AppleNetBoot" }, { &efi_arp_protocol_guid, "Arp" }, { &efi_arp_service_binding_protocol_guid, "ArpSb" }, { &efi_block_io_protocol_guid, "BlockIo" }, + { &efi_block_io2_protocol_guid, + "BlockIo2" }, { &efi_bus_specific_driver_override_protocol_guid, "BusSpecificDriverOverride" }, { &efi_component_name_protocol_guid, diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 22aa3ee72..04796414a 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -44,6 +44,9 @@ static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding; /** List of controlled EFI devices */ static LIST_HEAD ( efi_devices ); +/** We are currently disconnecting drivers */ +static int efi_driver_disconnecting; + /** * Find EFI device * @@ -142,6 +145,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } path; EFI_DEVICE_PATH_PROTOCOL *path_end; size_t path_len; + EFI_TPL saved_tpl; EFI_STATUS efirc; int rc; @@ -159,6 +163,17 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, goto err_already_started; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Do nothing if we are currently disconnecting drivers */ + if ( efi_driver_disconnecting ) { + DBGC ( device, "EFIDRV %s refusing to start during " + "disconnection\n", efi_handle_name ( device ) ); + efirc = EFI_NOT_READY; + goto err_disconnecting; + } + /* Open device path */ if ( ( efirc = bs->OpenProtocol ( device, &efi_device_path_protocol_guid, @@ -204,6 +219,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, DBGC ( device, "EFIDRV %s using driver \"%s\"\n", efi_handle_name ( device ), efidev->driver->name ); + bs->RestoreTPL ( saved_tpl ); return 0; } DBGC ( device, "EFIDRV %s could not start driver \"%s\": %s\n", @@ -220,6 +236,8 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, efi_image_handle, device ); } err_open_path: + err_disconnecting: + bs->RestoreTPL ( saved_tpl ); err_already_started: return efirc; } @@ -238,8 +256,10 @@ static EFI_STATUS EFIAPI efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_HANDLE device, UINTN num_children, EFI_HANDLE *children ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_driver *efidrv; struct efi_device *efidev; + EFI_TPL saved_tpl; UINTN i; DBGC ( device, "EFIDRV %s DRIVER_STOP", efi_handle_name ( device ) ); @@ -257,6 +277,9 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, return EFI_DEVICE_ERROR; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Stop this device */ efidrv = efidev->driver; assert ( efidrv != NULL ); @@ -264,6 +287,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, list_del ( &efidev->dev.siblings ); free ( efidev ); + bs->RestoreTPL ( saved_tpl ); return 0; } @@ -411,6 +435,7 @@ static int efi_driver_connect ( EFI_HANDLE device ) { DBGC2_EFI_PROTOCOLS ( device, device ); DBGC ( device, "EFIDRV %s disconnecting existing drivers\n", efi_handle_name ( device ) ); + efi_driver_disconnecting = 1; if ( ( efirc = bs->DisconnectController ( device, NULL, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); @@ -419,6 +444,7 @@ static int efi_driver_connect ( EFI_HANDLE device ) { strerror ( rc ) ); /* Ignore the error and attempt to connect our drivers */ } + efi_driver_disconnecting = 0; DBGC2 ( device, "EFIDRV %s after disconnecting:\n", efi_handle_name ( device ) ); DBGC2_EFI_PROTOCOLS ( device, device ); @@ -450,9 +476,11 @@ static int efi_driver_disconnect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; /* Disconnect our driver */ + efi_driver_disconnecting = 1; bs->DisconnectController ( device, efi_driver_binding.DriverBindingHandle, NULL ); + efi_driver_disconnecting = 0; return 0; } @@ -497,14 +525,16 @@ static int efi_driver_handles ( int ( * method ) ( EFI_HANDLE handle ) ) { /* Connect/disconnect driver from all handles */ for ( i = 0 ; i < num_handles ; i++ ) { - if ( ( rc = method ( handles[i] ) ) != 0 ) - goto err_method; + if ( ( rc = method ( handles[i] ) ) != 0 ) { + /* Ignore errors and continue to process + * remaining handles. + */ + } } /* Success */ rc = 0; - err_method: bs->FreePool ( handles ); err_locate: return rc; diff --git a/src/arch/x86/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c similarity index 94% rename from src/arch/x86/interface/efi/efi_entropy.c rename to src/interface/efi/efi_entropy.c index a54bd12e6..2a2fc9054 100644 --- a/src/arch/x86/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -78,6 +79,9 @@ static int efi_entropy_enable ( void ) { DBGC ( &tick, "ENTROPY %s RNG protocol\n", ( efirng ? "has" : "has no" ) ); + /* Drop to TPL_APPLICATION to allow timer tick event to take place */ + bs->RestoreTPL ( TPL_APPLICATION ); + /* Create timer tick event */ if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL, &tick ) ) != 0 ) { @@ -99,18 +103,20 @@ static void efi_entropy_disable ( void ) { /* Close timer tick event */ bs->CloseEvent ( tick ); + + /* Return to TPL_CALLBACK */ + bs->RaiseTPL ( TPL_CALLBACK ); } /** * Wait for a timer tick * - * @ret low TSC low-order bits, or negative error + * @ret low CPU profiling low-order bits, or negative error */ static int efi_entropy_tick ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; UINTN index; uint16_t low; - uint32_t discard_d; EFI_STATUS efirc; int rc; @@ -129,8 +135,8 @@ static int efi_entropy_tick ( void ) { return rc; } - /* Get current TSC low-order bits */ - __asm__ __volatile__ ( "rdtsc" : "=a" ( low ), "=d" ( discard_d ) ); + /* Get current CPU profiling timestamp low-order bits */ + low = profile_timestamp(); return low; } diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index 01b691b65..abc5a9390 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -110,7 +110,6 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { */ static int efifb_glyphs ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_FONT_DISPLAY_INFO *info; EFI_IMAGE_OUTPUT *blt; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel; size_t offset; @@ -122,16 +121,42 @@ static int efifb_glyphs ( void ) { EFI_STATUS efirc; int rc; - /* Get font height */ - if ( ( efirc = efifb.hiifont->GetFontInfo ( efifb.hiifont, NULL, NULL, - &info, NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not get font information: %s\n", - strerror ( rc ) ); - goto err_info; + /* Get font height. The GetFontInfo() call nominally returns + * this information in an EFI_FONT_DISPLAY_INFO structure, but + * is known to fail on many UEFI implementations. Instead, we + * iterate over all printable characters to find the maximum + * height. + */ + efifb.font.height = 0; + for ( character = 0 ; character < 256 ; character++ ) { + + /* Skip non-printable characters */ + if ( ! isprint ( character ) ) + continue; + + /* Get glyph */ + blt = NULL; + if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, + character, NULL, &blt, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n", + character, strerror ( rc ) ); + continue; + } + assert ( blt != NULL ); + + /* Calculate maximum height */ + if ( efifb.font.height < blt->Height ) + efifb.font.height = blt->Height; + + /* Free glyph */ + bs->FreePool ( blt ); + } + if ( ! efifb.font.height ) { + DBGC ( &efifb, "EFIFB could not get font height\n" ); + return -ENOENT; } - assert ( info != NULL ); - efifb.font.height = info->FontInfo.FontSize; /* Allocate glyph data */ len = ( 256 * efifb.font.height * sizeof ( bitmask ) ); @@ -152,7 +177,7 @@ static int efifb_glyphs ( void ) { /* Get glyph */ blt = NULL; if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, - character, info, &blt, + character, NULL, &blt, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n", @@ -187,19 +212,16 @@ static int efifb_glyphs ( void ) { copy_to_user ( efifb.glyphs, offset++, &bitmask, sizeof ( bitmask ) ); } + + /* Free glyph */ bs->FreePool ( blt ); } - /* Free font information */ - bs->FreePool ( info ); - efifb.font.glyph = efifb_glyph; return 0; ufree ( efifb.glyphs ); err_alloc: - bs->FreePool ( info ); - err_info: return rc; } diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 05eadc971..52de0987c 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -47,12 +47,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** EFI file information GUID */ -static EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; - -/** EFI file system information GUID */ -static EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; - /** EFI media ID */ #define EFI_MEDIA_ID_MAGIC 0x69505845 diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 8ec5e541d..663585dc2 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -25,8 +25,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include +#include #include #include +#include #include #include #include @@ -66,6 +69,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include /** @file * @@ -82,6 +87,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); EFI_GUID efi_absolute_pointer_protocol_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; +/** ACPI table protocol GUID */ +EFI_GUID efi_acpi_table_protocol_guid + = EFI_ACPI_TABLE_PROTOCOL_GUID; + +/** Apple NetBoot protocol GUID */ +EFI_GUID efi_apple_net_boot_protocol_guid + = EFI_APPLE_NET_BOOT_PROTOCOL_GUID; + /** ARP protocol GUID */ EFI_GUID efi_arp_protocol_guid = EFI_ARP_PROTOCOL_GUID; @@ -94,6 +107,10 @@ EFI_GUID efi_arp_service_binding_protocol_guid EFI_GUID efi_block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID; +/** Block I/O version 2 protocol GUID */ +EFI_GUID efi_block_io2_protocol_guid + = EFI_BLOCK_IO2_PROTOCOL_GUID; + /** Bus specific driver override protocol GUID */ EFI_GUID efi_bus_specific_driver_override_protocol_guid = EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID; @@ -281,3 +298,9 @@ EFI_GUID efi_usb_io_protocol_guid /** VLAN configuration protocol GUID */ EFI_GUID efi_vlan_config_protocol_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID; + +/** File information GUID */ +EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; + +/** File system information GUID */ +EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; diff --git a/src/interface/efi/efi_hii.c b/src/interface/efi/efi_hii.c index 0ea970e67..506fc8869 100644 --- a/src/interface/efi/efi_hii.c +++ b/src/interface/efi/efi_hii.c @@ -117,6 +117,7 @@ static void * efi_ifr_op ( struct efi_ifr_builder *ifr, unsigned int opcode, ifr->ops_len = new_ops_len; /* Fill in opcode header */ + memset ( op, 0, len ); op->OpCode = opcode; op->Length = len; diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 93ada21d4..ed9707f2c 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -32,8 +32,17 @@ EFI_HANDLE efi_image_handle; /** Loaded image protocol for this image */ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; -/** System table passed to entry point */ -EFI_SYSTEM_TABLE *efi_systab; +/** System table passed to entry point + * + * We construct the symbol name efi_systab via the PLATFORM macro. + * This ensures that the symbol is defined only in EFI builds, and so + * prevents EFI code from being incorrectly linked in to a non-EFI + * build. + */ +EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab ); + +/** EFI shutdown is in progress */ +int efi_shutdown_in_progress; /** Event used to signal shutdown */ static EFI_EVENT efi_shutdown_event; @@ -50,6 +59,13 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); */ static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, void *context __unused ) { + + /* Mark shutdown as being in progress, to indicate that large + * parts of the system (e.g. timers) are no longer functional. + */ + efi_shutdown_in_progress = 1; + + /* Shut down iPXE */ shutdown_boot(); } diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c new file mode 100644 index 000000000..bd010ad2e --- /dev/null +++ b/src/interface/efi/efi_local.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI local file access + * + */ + +/** Download blocksize */ +#define EFI_LOCAL_BLKSIZE 4096 + +/** An EFI local file */ +struct efi_local { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Download process */ + struct process process; + + /** EFI root directory */ + EFI_FILE_PROTOCOL *root; + /** EFI file */ + EFI_FILE_PROTOCOL *file; + /** Length of file */ + size_t len; +}; + +/** + * Close local file + * + * @v local Local file + * @v rc Reason for close + */ +static void efi_local_close ( struct efi_local *local, int rc ) { + + /* Stop process */ + process_del ( &local->process ); + + /* Shut down data transfer interface */ + intf_shutdown ( &local->xfer, rc ); + + /* Close EFI file */ + if ( local->file ) { + local->file->Close ( local->file ); + local->file = NULL; + } + + /* Close EFI root directory */ + if ( local->root ) { + local->root->Close ( local->root ); + local->root = NULL; + } +} + +/** + * Local file process + * + * @v local Local file + */ +static void efi_local_step ( struct efi_local *local ) { + EFI_FILE_PROTOCOL *file = local->file; + struct io_buffer *iobuf = NULL; + size_t remaining; + size_t frag_len; + UINTN size; + EFI_STATUS efirc; + int rc; + + /* Wait until data transfer interface is ready */ + if ( ! xfer_window ( &local->xfer ) ) + return; + + /* Presize receive buffer */ + remaining = local->len; + xfer_seek ( &local->xfer, remaining ); + xfer_seek ( &local->xfer, 0 ); + + /* Get file contents */ + while ( remaining ) { + + /* Calculate length for this fragment */ + frag_len = remaining; + if ( frag_len > EFI_LOCAL_BLKSIZE ) + frag_len = EFI_LOCAL_BLKSIZE; + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &local->xfer, frag_len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err; + } + + /* Read block */ + size = frag_len; + if ( ( efirc = file->Read ( file, &size, iobuf->data ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not read from file: %s\n", + local, strerror ( rc ) ); + goto err; + } + assert ( size <= frag_len ); + iob_put ( iobuf, size ); + + /* Deliver data */ + if ( ( rc = xfer_deliver_iob ( &local->xfer, + iob_disown ( iobuf ) ) ) != 0 ) { + DBGC ( local, "LOCAL %p could not deliver data: %s\n", + local, strerror ( rc ) ); + goto err; + } + + /* Move to next block */ + remaining -= frag_len; + } + + /* Close download */ + efi_local_close ( local, 0 ); + + return; + + err: + free_iob ( iobuf ); + efi_local_close ( local, rc ); +} + +/** Data transfer interface operations */ +static struct interface_operation efi_local_operations[] = { + INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ), + INTF_OP ( intf_close, struct efi_local *, efi_local_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor efi_local_xfer_desc = + INTF_DESC ( struct efi_local, xfer, efi_local_operations ); + +/** Process descriptor */ +static struct process_descriptor efi_local_process_desc = + PROC_DESC_ONCE ( struct efi_local, process, efi_local_step ); + +/** + * Check for matching volume name + * + * @v local Local file + * @v device Device handle + * @v root Root filesystem handle + * @v volume Volume name + * @ret rc Return status code + */ +static int efi_local_check_volume_name ( struct efi_local *local, + EFI_HANDLE device, + EFI_FILE_PROTOCOL *root, + const char *volume ) { + EFI_FILE_SYSTEM_INFO *info; + UINTN size; + char *label; + EFI_STATUS efirc; + int rc; + + /* Get length of file system information */ + size = 0; + root->GetInfo ( root, &efi_file_system_info_id, &size, NULL ); + + /* Allocate file system information */ + info = malloc ( size ); + if ( ! info ) { + rc = -ENOMEM; + goto err_alloc_info; + } + + /* Get file system information */ + if ( ( efirc = root->GetInfo ( root, &efi_file_system_info_id, &size, + info ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not get file system info on %s: " + "%s\n", local, efi_handle_name ( device ), + strerror ( rc ) ); + goto err_get_info; + } + DBGC2 ( local, "LOCAL %p found %s with label \"%ls\"\n", + local, efi_handle_name ( device ), info->VolumeLabel ); + + /* Construct volume label for comparison */ + if ( asprintf ( &label, "%ls", info->VolumeLabel ) < 0 ) { + rc = -ENOMEM; + goto err_alloc_label; + } + + /* Compare volume label */ + if ( strcasecmp ( volume, label ) != 0 ) { + rc = -ENOENT; + goto err_compare; + } + + /* Success */ + rc = 0; + + err_compare: + free ( label ); + err_alloc_label: + err_get_info: + free ( info ); + err_alloc_info: + return rc; +} + +/** + * Open root filesystem + * + * @v local Local file + * @v device Device handle + * @v root Root filesystem handle to fill in + * @ret rc Return status code + */ +static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device, + EFI_FILE_PROTOCOL **root ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + void *interface; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; + } u; + EFI_STATUS efirc; + int rc; + + /* Open file system protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_file_system_protocol_guid, + &u.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not open filesystem on %s: %s\n", + local, efi_handle_name ( device ), strerror ( rc ) ); + goto err_filesystem; + } + + /* Open root directory */ + if ( ( efirc = u.fs->OpenVolume ( u.fs, root ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not open volume on %s: %s\n", + local, efi_handle_name ( device ), strerror ( rc ) ); + goto err_volume; + } + + /* Success */ + rc = 0; + + err_volume: + bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid, + efi_image_handle, device ); + err_filesystem: + return rc; +} + +/** + * Open root filesystem of specified volume + * + * @v local Local file + * @v volume Volume name, or NULL to use loaded image's device + * @ret rc Return status code + */ +static int efi_local_open_volume ( struct efi_local *local, + const char *volume ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; + int ( * check ) ( struct efi_local *local, EFI_HANDLE device, + EFI_FILE_PROTOCOL *root, const char *volume ); + EFI_FILE_PROTOCOL *root; + EFI_HANDLE *handles; + EFI_HANDLE device; + UINTN num_handles; + UINTN i; + EFI_STATUS efirc; + int rc; + + /* Identify candidate handles */ + if ( volume ) { + /* Locate all filesystem handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol, + NULL, &num_handles, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not enumerate handles: " + "%s\n", local, strerror ( rc ) ); + return rc; + } + check = efi_local_check_volume_name; + } else { + /* Use our loaded image's device handle */ + handles = &efi_loaded_image->DeviceHandle; + num_handles = 1; + check = NULL; + } + + /* Find matching handle */ + for ( i = 0 ; i < num_handles ; i++ ) { + + /* Get this device handle */ + device = handles[i]; + + /* Open root directory */ + if ( ( rc = efi_local_open_root ( local, device, &root ) ) != 0) + continue; + + /* Check volume name, if applicable */ + if ( ( check == NULL ) || + ( ( rc = check ( local, device, root, volume ) ) == 0 ) ) { + DBGC ( local, "LOCAL %p using %s", + local, efi_handle_name ( device ) ); + if ( volume ) + DBGC ( local, " with label \"%s\"", volume ); + DBGC ( local, "\n" ); + local->root = root; + break; + } + + /* Close root directory */ + root->Close ( root ); + } + + /* Free handles, if applicable */ + if ( volume ) + bs->FreePool ( handles ); + + /* Fail if we found no matching handle */ + if ( ! local->root ) { + DBGC ( local, "LOCAL %p found no matching handle\n", local ); + return -ENOENT; + } + + return 0; +} + +/** + * Open fully-resolved path + * + * @v local Local file + * @v resolved Resolved path + * @ret rc Return status code + */ +static int efi_local_open_resolved ( struct efi_local *local, + const char *resolved ) { + size_t name_len = strlen ( resolved ); + CHAR16 name[ name_len + 1 /* wNUL */ ]; + EFI_FILE_PROTOCOL *file; + EFI_STATUS efirc; + int rc; + + /* Construct filename */ + efi_snprintf ( name, ( name_len + 1 /* wNUL */ ), "%s", resolved ); + + /* Open file */ + if ( ( efirc = local->root->Open ( local->root, &file, name, + EFI_FILE_MODE_READ, 0 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not open \"%s\": %s\n", + local, resolved, strerror ( rc ) ); + return rc; + } + local->file = file; + + return 0; +} + +/** + * Open specified path + * + * @v local Local file + * @v path Path to file + * @ret rc Return status code + */ +static int efi_local_open_path ( struct efi_local *local, const char *path ) { + FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath, + FILEPATH_DEVICE_PATH, Header); + size_t fp_len = ( fp ? efi_devpath_len ( &fp->Header ) : 0 ); + char base[ fp_len / 2 /* Cannot exceed this length */ ]; + size_t remaining = sizeof ( base ); + size_t len; + char *resolved; + char *tmp; + int rc; + + /* Construct base path to our own image, if possible */ + memset ( base, 0, sizeof ( base ) ); + tmp = base; + while ( fp && ( fp->Header.Type != END_DEVICE_PATH_TYPE ) ) { + len = snprintf ( tmp, remaining, "%ls", fp->PathName ); + assert ( len < remaining ); + tmp += len; + remaining -= len; + fp = ( ( ( void * ) fp ) + ( ( fp->Header.Length[1] << 8 ) | + fp->Header.Length[0] ) ); + } + DBGC2 ( local, "LOCAL %p base path \"%s\"\n", + local, base ); + + /* Convert to sane path separators */ + for ( tmp = base ; *tmp ; tmp++ ) { + if ( *tmp == '\\' ) + *tmp = '/'; + } + + /* Resolve path */ + resolved = resolve_path ( base, path ); + if ( ! resolved ) { + rc = -ENOMEM; + goto err_resolve; + } + + /* Convert to insane path separators */ + for ( tmp = resolved ; *tmp ; tmp++ ) { + if ( *tmp == '/' ) + *tmp = '\\'; + } + DBGC ( local, "LOCAL %p using \"%s\"\n", + local, resolved ); + + /* Open resolved path */ + if ( ( rc = efi_local_open_resolved ( local, resolved ) ) != 0 ) + goto err_open; + + err_open: + free ( resolved ); + err_resolve: + return rc; +} + +/** + * Get file length + * + * @v local Local file + * @ret rc Return status code + */ +static int efi_local_len ( struct efi_local *local ) { + EFI_FILE_PROTOCOL *file = local->file; + EFI_FILE_INFO *info; + EFI_STATUS efirc; + UINTN size; + int rc; + + /* Get size of file information */ + size = 0; + file->GetInfo ( file, &efi_file_info_id, &size, NULL ); + + /* Allocate file information */ + info = malloc ( size ); + if ( ! info ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get file information */ + if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size, + info ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not get file info: %s\n", + local, strerror ( rc ) ); + goto err_info; + } + + /* Record file length */ + local->len = info->FileSize; + + /* Success */ + rc = 0; + + err_info: + free ( info ); + err_alloc: + return rc; +} + +/** + * Open local file + * + * @v xfer Data transfer interface + * @v uri Request URI + * @ret rc Return status code + */ +static int efi_local_open ( struct interface *xfer, struct uri *uri ) { + struct efi_local *local; + const char *volume; + const char *path; + int rc; + + /* Parse URI */ + volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL ); + path = ( uri->opaque ? uri->opaque : uri->path ); + + /* Allocate and initialise structure */ + local = zalloc ( sizeof ( *local ) ); + if ( ! local ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &local->refcnt, NULL ); + intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); + process_init ( &local->process, &efi_local_process_desc, + &local->refcnt ); + + /* Open specified volume */ + if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 ) + goto err_open_root; + + /* Open specified path */ + if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) + goto err_open_file; + + /* Get length of file */ + if ( ( rc = efi_local_len ( local ) ) != 0 ) + goto err_len; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &local->xfer, xfer ); + ref_put ( &local->refcnt ); + return 0; + + err_len: + err_open_file: + err_open_root: + efi_local_close ( local, 0 ); + ref_put ( &local->refcnt ); + err_alloc: + return rc; +} + +/** EFI local file URI opener */ +struct uri_opener efi_local_uri_opener __uri_opener = { + .scheme = "file", + .open = efi_local_open, +}; diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index be305ba6c..c1f451c99 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -61,58 +61,158 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ -/** PCI root bridge I/O protocol */ -static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci; -EFI_REQUEST_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); +/** + * Locate EFI PCI root bridge I/O protocol + * + * @v pci PCI device + * @ret handle EFI PCI root bridge handle + * @ret root EFI PCI root bridge I/O protocol, or NULL if not found + * @ret rc Return status code + */ +static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *handles; + UINTN num_handles; + union { + void *interface; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + } u; + EFI_STATUS efirc; + UINTN i; + int rc; + /* Enumerate all handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, + &efi_pci_root_bridge_io_protocol_guid, + NULL, &num_handles, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot locate root bridges: " + "%s\n", PCI_ARGS ( pci ), strerror ( rc ) ); + goto err_locate; + } + + /* Look for matching root bridge I/O protocol */ + for ( i = 0 ; i < num_handles ; i++ ) { + *handle = handles[i]; + if ( ( efirc = bs->OpenProtocol ( *handle, + &efi_pci_root_bridge_io_protocol_guid, + &u.interface, efi_image_handle, *handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n", + PCI_ARGS ( pci ), efi_handle_name ( *handle ), + strerror ( rc ) ); + continue; + } + if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) { + *root = u.root; + bs->FreePool ( handles ); + return 0; + } + bs->CloseProtocol ( *handle, + &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, *handle ); + } + DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n", + PCI_ARGS ( pci ) ); + rc = -ENOENT; + + bs->FreePool ( handles ); + err_locate: + return rc; +} + +/** + * Calculate EFI PCI configuration space address + * + * @v pci PCI device + * @v location Encoded offset and width + * @ret address EFI PCI address + */ static unsigned long efipci_address ( struct pci_device *pci, unsigned long location ) { + return EFI_PCI_ADDRESS ( PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ), EFIPCI_OFFSET ( location ) ); } +/** + * Read from PCI configuration space + * + * @v pci PCI device + * @v location Encoded offset and width + * @ret value Value + * @ret rc Return status code + */ int efipci_read ( struct pci_device *pci, unsigned long location, void *value ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; EFI_STATUS efirc; int rc; - if ( ! efipci ) - return -ENOTSUP; + /* Identify root bridge */ + if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + goto err_root; - if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ), - efipci_address ( pci, location ), 1, - value ) ) != 0 ) { + /* Read from configuration space */ + if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + value ) ) != 0 ) { rc = -EEFI ( efirc ); - DBG ( "EFIPCI config read from " PCI_FMT " offset %02lx " - "failed: %s\n", PCI_ARGS ( pci ), - EFIPCI_OFFSET ( location ), strerror ( rc ) ); - return -EIO; + DBGC ( pci, "EFIPCI " PCI_FMT " config read from offset %02lx " + "failed: %s\n", PCI_ARGS ( pci ), + EFIPCI_OFFSET ( location ), strerror ( rc ) ); + goto err_read; } - return 0; + err_read: + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); + err_root: + return rc; } +/** + * Write to PCI configuration space + * + * @v pci PCI device + * @v location Encoded offset and width + * @v value Value + * @ret rc Return status code + */ int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; EFI_STATUS efirc; int rc; - if ( ! efipci ) - return -ENOTSUP; + /* Identify root bridge */ + if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + goto err_root; - if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), - efipci_address ( pci, location ), 1, - &value ) ) != 0 ) { + /* Read from configuration space */ + if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + &value ) ) != 0 ) { rc = -EEFI ( efirc ); - DBG ( "EFIPCI config write to " PCI_FMT " offset %02lx " - "failed: %s\n", PCI_ARGS ( pci ), - EFIPCI_OFFSET ( location ), strerror ( rc ) ); - return -EIO; + DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx " + "failed: %s\n", PCI_ARGS ( pci ), + EFIPCI_OFFSET ( location ), strerror ( rc ) ); + goto err_write; } - return 0; + err_write: + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); + err_root: + return rc; } PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus ); @@ -146,6 +246,7 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, void *interface; } pci_io; UINTN pci_segment, pci_bus, pci_dev, pci_fn; + unsigned int busdevfn; EFI_STATUS efirc; int rc; @@ -168,10 +269,10 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, efi_handle_name ( device ), strerror ( rc ) ); goto err_get_location; } - DBGC2 ( device, "EFIPCI %s is PCI %04lx:%02lx:%02lx.%lx\n", - efi_handle_name ( device ), ( ( unsigned long ) pci_segment ), - ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), - ( ( unsigned long ) pci_fn ) ); + busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); + pci_init ( pci, busdevfn ); + DBGCP ( device, "EFIPCI " PCI_FMT " is %s\n", + PCI_ARGS ( pci ), efi_handle_name ( device ) ); /* Try to enable I/O cycles, memory cycles, and bus mastering. * Some platforms will 'helpfully' report errors if these bits @@ -190,10 +291,10 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ - pci_init ( pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); if ( ( rc = pci_read_config ( pci ) ) != 0 ) { - DBGC ( device, "EFIPCI %s cannot read PCI configuration: %s\n", - efi_handle_name ( device ), strerror ( rc ) ); + DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI " + "configuration: %s\n", + PCI_ARGS ( pci ), strerror ( rc ) ); goto err_pci_read_config; } @@ -263,12 +364,14 @@ static int efipci_supported ( EFI_HANDLE device ) { /* Look for a driver */ if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { - DBGCP ( device, "EFIPCI %s has no driver\n", - efi_handle_name ( device ) ); + DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " + "has no driver\n", PCI_ARGS ( &pci ), pci.vendor, + pci.device, pci.class ); return rc; } - DBGC ( device, "EFIPCI %s has driver \"%s\"\n", - efi_handle_name ( device ), pci.id->name ); + DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) has driver " + "\"%s\"\n", PCI_ARGS ( &pci ), pci.vendor, pci.device, + pci.class, pci.id->name ); return 0; } @@ -303,8 +406,8 @@ static int efipci_start ( struct efi_device *efidev ) { /* Find driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { - DBGC ( device, "EFIPCI %s has no driver\n", - efi_handle_name ( device ) ); + DBGC ( device, "EFIPCI " PCI_FMT " has no driver\n", + PCI_ARGS ( pci ) ); goto err_find_driver; } @@ -314,13 +417,13 @@ static int efipci_start ( struct efi_device *efidev ) { /* Probe driver */ if ( ( rc = pci_probe ( pci ) ) != 0 ) { - DBGC ( device, "EFIPCI %s could not probe driver \"%s\": %s\n", - efi_handle_name ( device ), pci->id->name, + DBGC ( device, "EFIPCI " PCI_FMT " could not probe driver " + "\"%s\": %s\n", PCI_ARGS ( pci ), pci->id->name, strerror ( rc ) ); goto err_probe; } - DBGC ( device, "EFIPCI %s using driver \"%s\"\n", - efi_handle_name ( device ), pci->id->name ); + DBGC ( device, "EFIPCI " PCI_FMT " using driver \"%s\"\n", + PCI_ARGS ( pci ), pci->id->name ); efidev_set_drvdata ( efidev, pci ); return 0; diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index 1847e3fd1..a1f81df59 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -80,6 +81,8 @@ struct efi_pxe { EFI_PXE_BASE_CODE_PROTOCOL base; /** PXE base code mode */ EFI_PXE_BASE_CODE_MODE mode; + /** Apple NetBoot protocol */ + EFI_APPLE_NET_BOOT_PROTOCOL apple; /** TCP/IP network-layer protocol */ struct tcpip_net_protocol *tcpip; @@ -1498,6 +1501,83 @@ static EFI_PXE_BASE_CODE_PROTOCOL efi_pxe_base_code_protocol = { .SetPackets = efi_pxe_set_packets, }; +/****************************************************************************** + * + * Apple NetBoot protocol + * + ****************************************************************************** + */ + +/** + * Get DHCP/BSDP response + * + * @v packet Packet + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_apple_get_response ( EFI_PXE_BASE_CODE_PACKET *packet, UINTN *len, + VOID *data ) { + + /* Check length */ + if ( *len < sizeof ( *packet ) ) { + *len = sizeof ( *packet ); + return EFI_BUFFER_TOO_SMALL; + } + + /* Copy packet */ + memcpy ( data, packet, sizeof ( *packet ) ); + *len = sizeof ( *packet ); + + return EFI_SUCCESS; +} + +/** + * Get DHCP response + * + * @v apple Apple NetBoot protocol + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_apple_get_dhcp_response ( EFI_APPLE_NET_BOOT_PROTOCOL *apple, + UINTN *len, VOID *data ) { + struct efi_pxe *pxe = container_of ( apple, struct efi_pxe, apple ); + + return efi_apple_get_response ( &pxe->mode.DhcpAck, len, data ); +} + +/** + * Get BSDP response + * + * @v apple Apple NetBoot protocol + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_apple_get_bsdp_response ( EFI_APPLE_NET_BOOT_PROTOCOL *apple, + UINTN *len, VOID *data ) { + struct efi_pxe *pxe = container_of ( apple, struct efi_pxe, apple ); + + return efi_apple_get_response ( &pxe->mode.PxeReply, len, data ); +} + +/** Apple NetBoot protocol */ +static EFI_APPLE_NET_BOOT_PROTOCOL efi_apple_net_boot_protocol = { + .GetDhcpResponse = efi_apple_get_dhcp_response, + .GetBsdpResponse = efi_apple_get_bsdp_response, +}; + +/****************************************************************************** + * + * Installer + * + ****************************************************************************** + */ + /** * Install PXE base code protocol * @@ -1526,6 +1606,8 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { pxe->handle = handle; memcpy ( &pxe->base, &efi_pxe_base_code_protocol, sizeof ( pxe->base )); pxe->base.Mode = &pxe->mode; + memcpy ( &pxe->apple, &efi_apple_net_boot_protocol, + sizeof ( pxe->apple ) ); pxe->buf.op = &efi_pxe_buf_operations; intf_init ( &pxe->tftp, &efi_pxe_tftp_desc, &pxe->refcnt ); intf_init ( &pxe->udp, &efi_pxe_udp_desc, &pxe->refcnt ); @@ -1545,7 +1627,9 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { /* Install PXE base code protocol */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &handle, &efi_pxe_base_code_protocol_guid, &pxe->base, + &handle, + &efi_pxe_base_code_protocol_guid, &pxe->base, + &efi_apple_net_boot_protocol_guid, &pxe->apple, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pxe, "PXE %s could not install base code protocol: %s\n", @@ -1560,7 +1644,9 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { return 0; bs->UninstallMultipleProtocolInterfaces ( - handle, &efi_pxe_base_code_protocol_guid, &pxe->base, + handle, + &efi_pxe_base_code_protocol_guid, &pxe->base, + &efi_apple_net_boot_protocol_guid, &pxe->apple, NULL ); err_install_protocol: ref_put ( &pxe->refcnt ); @@ -1590,7 +1676,9 @@ void efi_pxe_uninstall ( EFI_HANDLE handle ) { /* Uninstall PXE base code protocol */ bs->UninstallMultipleProtocolInterfaces ( - handle, &efi_pxe_base_code_protocol_guid, &pxe->base, + handle, + &efi_pxe_base_code_protocol_guid, &pxe->base, + &efi_apple_net_boot_protocol_guid, &pxe->apple, NULL ); /* Remove from list and drop list's reference */ diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 4508967e6..3f95a8961 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -44,6 +45,9 @@ static LIST_HEAD ( efi_snp_devices ); /** Network devices are currently claimed for use by iPXE */ static int efi_snp_claimed; +/** TPL prior to network devices being claimed */ +static EFI_TPL efi_snp_old_tpl; + /* Downgrade user experience if configured to do so * * The default UEFI user experience for network boot is somewhat @@ -229,8 +233,10 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { static EFI_STATUS EFIAPI efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; int rc; DBGC ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n", @@ -238,17 +244,26 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, ( ( unsigned long ) extra_tx_bufsize ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Open network device */ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n", snpdev, snpdev->netdev->name, strerror ( rc ) ); - return EFIRC ( rc ); + goto err_open; } efi_snp_set_state ( snpdev ); - return 0; + err_open: + bs->RestoreTPL ( saved_tpl ); + err_claimed: + return EFIRC ( rc ); } /** @@ -260,29 +275,41 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, */ static EFI_STATUS EFIAPI efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; int rc; DBGC ( snpdev, "SNPDEV %p RESET (%s extended verification)\n", snpdev, ( ext_verify ? "with" : "without" ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Close network device */ netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); efi_snp_flush ( snpdev ); + /* Reopen network device */ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", snpdev, snpdev->netdev->name, strerror ( rc ) ); - return EFIRC ( rc ); + goto err_open; } efi_snp_set_state ( snpdev ); - return 0; + err_open: + bs->RestoreTPL ( saved_tpl ); + err_claimed: + return EFIRC ( rc ); } /** @@ -293,8 +320,10 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { */ static EFI_STATUS EFIAPI efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; DBGC ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); @@ -302,10 +331,17 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { if ( efi_snp_claimed ) return EFI_NOT_READY; + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + + /* Close network device */ netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); efi_snp_flush ( snpdev ); + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + return 0; } @@ -508,8 +544,10 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, static EFI_STATUS EFIAPI efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 *interrupts, VOID **txbuf ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); + EFI_TPL saved_tpl; DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); @@ -519,6 +557,9 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, return EFI_NOT_READY; } + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -541,6 +582,9 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, " TX:%p", *txbuf ); } + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + DBGC2 ( snpdev, "\n" ); return 0; } @@ -562,12 +606,14 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN ll_header_len, UINTN len, VOID *data, EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, UINT16 *net_proto ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; struct io_buffer *iobuf; size_t payload_len; unsigned int tx_fill; + EFI_TPL saved_tpl; int rc; DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, @@ -588,8 +634,13 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, "\n" ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); /* Sanity checks */ if ( ll_header_len ) { @@ -673,6 +724,9 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, snpdev->tx[ snpdev->tx_prod++ % EFI_SNP_NUM_TX ] = data; snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + return 0; err_ring_full: @@ -681,6 +735,8 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, free_iob ( iobuf ); err_alloc_iob: err_sanity: + bs->RestoreTPL ( saved_tpl ); + err_claimed: return EFIRC ( rc ); } @@ -701,6 +757,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN *ll_header_len, UINTN *len, VOID *data, EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, UINT16 *net_proto ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; @@ -709,30 +766,42 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, const void *iob_ll_src; uint16_t iob_net_proto; unsigned int iob_flags; + size_t copy_len; + EFI_TPL saved_tpl; int rc; DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, ( ( unsigned long ) *len ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + if ( efi_snp_claimed ) { + rc = -EAGAIN; + goto err_claimed; + } + + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); /* Poll the network device */ efi_snp_poll ( snpdev ); - /* Dequeue a packet, if one is available */ + /* Check for an available packet */ iobuf = list_first_entry ( &snpdev->rx, struct io_buffer, list ); if ( ! iobuf ) { DBGC2 ( snpdev, "\n" ); rc = -EAGAIN; goto out_no_packet; } - list_del ( &iobuf->list ); DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); - /* Return packet to caller */ - memcpy ( data, iobuf->data, iob_len ( iobuf ) ); + /* Dequeue packet */ + list_del ( &iobuf->list ); + + /* Return packet to caller, truncating to buffer length */ + copy_len = iob_len ( iobuf ); + if ( copy_len > *len ) + copy_len = *len; + memcpy ( data, iobuf->data, copy_len ); *len = iob_len ( iobuf ); /* Attempt to decode link-layer header */ @@ -754,11 +823,14 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, if ( net_proto ) *net_proto = ntohs ( iob_net_proto ); - rc = 0; + /* Check buffer length */ + rc = ( ( copy_len == *len ) ? 0 : -ERANGE ); out_bad_ll_header: free_iob ( iobuf ); out_no_packet: + bs->RestoreTPL ( saved_tpl ); + err_claimed: return EFIRC ( rc ); } @@ -770,7 +842,9 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, */ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, VOID *context ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = context; + EFI_TPL saved_tpl; DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); @@ -782,8 +856,14 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, if ( efi_snp_claimed ) return; + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Poll the network device */ efi_snp_poll ( snpdev ); + + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); } /** SNP interface */ @@ -1545,8 +1625,10 @@ static int efi_snp_probe ( struct net_device *netdev ) { struct efi_snp_device *snpdev; EFI_DEVICE_PATH_PROTOCOL *path_end; MAC_ADDR_DEVICE_PATH *macpath; + VLAN_DEVICE_PATH *vlanpath; size_t path_prefix_len = 0; unsigned int ifcnt; + unsigned int tag; void *interface; EFI_STATUS efirc; int rc; @@ -1634,7 +1716,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { /* Allocate the new device path */ path_prefix_len = efi_devpath_len ( efidev->path ); snpdev->path = zalloc ( path_prefix_len + sizeof ( *macpath ) + - sizeof ( *path_end ) ); + sizeof ( *vlanpath ) + sizeof ( *path_end ) ); if ( ! snpdev->path ) { rc = -ENOMEM; goto err_alloc_device_path; @@ -1643,14 +1725,24 @@ static int efi_snp_probe ( struct net_device *netdev ) { /* Populate the device path */ memcpy ( snpdev->path, efidev->path, path_prefix_len ); macpath = ( ( ( void * ) snpdev->path ) + path_prefix_len ); - path_end = ( ( void * ) ( macpath + 1 ) ); memset ( macpath, 0, sizeof ( *macpath ) ); macpath->Header.Type = MESSAGING_DEVICE_PATH; macpath->Header.SubType = MSG_MAC_ADDR_DP; macpath->Header.Length[0] = sizeof ( *macpath ); memcpy ( &macpath->MacAddress, netdev->ll_addr, - sizeof ( macpath->MacAddress ) ); + netdev->ll_protocol->ll_addr_len ); macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); + if ( ( tag = vlan_tag ( netdev ) ) ) { + vlanpath = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); + memset ( vlanpath, 0, sizeof ( *vlanpath ) ); + vlanpath->Header.Type = MESSAGING_DEVICE_PATH; + vlanpath->Header.SubType = MSG_VLAN_DP; + vlanpath->Header.Length[0] = sizeof ( *vlanpath ); + vlanpath->VlanId = tag; + path_end = ( ( ( void * ) vlanpath ) + sizeof ( *vlanpath ) ); + } else { + path_end = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); + } memset ( path_end, 0, sizeof ( *path_end ) ); path_end->Type = END_DEVICE_PATH_TYPE; path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -1875,8 +1967,13 @@ struct efi_snp_device * last_opened_snpdev ( void ) { * @v delta Claim count change */ void efi_snp_add_claim ( int delta ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; + /* Raise TPL if we are about to claim devices */ + if ( ! efi_snp_claimed ) + efi_snp_old_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Claim SNP devices */ efi_snp_claimed += delta; assert ( efi_snp_claimed >= 0 ); @@ -1884,4 +1981,8 @@ void efi_snp_add_claim ( int delta ) { /* Update SNP mode state for each interface */ list_for_each_entry ( snpdev, &efi_snp_devices, list ) efi_snp_set_state ( snpdev ); + + /* Restore TPL if we have released devices */ + if ( ! efi_snp_claimed ) + bs->RestoreTPL ( efi_snp_old_tpl ); } diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 720402bdb..651bef040 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -63,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** EFI platform setup formset GUID */ @@ -546,6 +547,13 @@ efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii, /* Initialise results */ *results = NULL; + /* Work around apparently broken UEFI specification */ + if ( ! ( request && request[0] ) ) { + DBGC ( snpdev, "SNPDEV %p ExtractConfig ignoring malformed " + "request\n", snpdev ); + return EFI_INVALID_PARAMETER; + } + /* Process all request fragments */ for ( pos = *progress = request ; *progress && **progress ; pos = *progress + 1 ) { @@ -646,6 +654,9 @@ static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = { */ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + VENDOR_DEVICE_PATH *vendor_path; + EFI_DEVICE_PATH_PROTOCOL *path_end; + size_t path_prefix_len; int efirc; int rc; @@ -667,9 +678,46 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { goto err_build_package_list; } + /* Allocate the new device path */ + path_prefix_len = efi_devpath_len ( snpdev->path ); + snpdev->hii_child_path = zalloc ( path_prefix_len + + sizeof ( *vendor_path ) + + sizeof ( *path_end ) ); + if ( ! snpdev->hii_child_path ) { + DBGC ( snpdev, + "SNPDEV %p could not allocate HII child device path\n", + snpdev ); + rc = -ENOMEM; + goto err_alloc_child_path; + } + + /* Populate the device path */ + memcpy ( snpdev->hii_child_path, snpdev->path, path_prefix_len ); + vendor_path = ( ( ( void * ) snpdev->hii_child_path ) + + path_prefix_len ); + vendor_path->Header.Type = HARDWARE_DEVICE_PATH; + vendor_path->Header.SubType = HW_VENDOR_DP; + vendor_path->Header.Length[0] = sizeof ( *vendor_path ); + efi_snp_hii_random_guid ( &vendor_path->Guid ); + path_end = ( ( void * ) ( vendor_path + 1 ) ); + path_end->Type = END_DEVICE_PATH_TYPE; + path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + path_end->Length[0] = sizeof ( *path_end ); + + /* Create device path and child handle for HII association */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &snpdev->hii_child_handle, + &efi_device_path_protocol_guid, snpdev->hii_child_path, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( snpdev, "SNPDEV %p could not create HII child handle: " + "%s\n", snpdev, strerror ( rc ) ); + goto err_hii_child_handle; + } + /* Add HII packages */ if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list, - snpdev->handle, + snpdev->hii_child_handle, &snpdev->hii_handle ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n", @@ -679,7 +727,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { /* Install HII protocol */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &snpdev->handle, + &snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); @@ -688,15 +736,34 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { goto err_install_protocol; } + /* Add as child of handle with SNP instance */ + if ( ( rc = efi_child_add ( snpdev->handle, + snpdev->hii_child_handle ) ) != 0 ) { + DBGC ( snpdev, + "SNPDEV %p could not adopt HII child handle: %s\n", + snpdev, strerror ( rc ) ); + goto err_efi_child_add; + } + return 0; + efi_child_del ( snpdev->handle, snpdev->hii_child_handle ); + err_efi_child_add: bs->UninstallMultipleProtocolInterfaces ( - snpdev->handle, + snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, NULL ); err_install_protocol: efihii->RemovePackageList ( efihii, snpdev->hii_handle ); err_new_package_list: + bs->UninstallMultipleProtocolInterfaces ( + snpdev->hii_child_handle, + &efi_device_path_protocol_guid, snpdev->hii_child_path, + NULL ); + err_hii_child_handle: + free ( snpdev->hii_child_path ); + snpdev->hii_child_path = NULL; + err_alloc_child_path: free ( snpdev->package_list ); snpdev->package_list = NULL; err_build_package_list: @@ -717,11 +784,18 @@ void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) { return; /* Uninstall protocols and remove package list */ + efi_child_del ( snpdev->handle, snpdev->hii_child_handle ); bs->UninstallMultipleProtocolInterfaces ( - snpdev->handle, + snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, NULL ); efihii->RemovePackageList ( efihii, snpdev->hii_handle ); + bs->UninstallMultipleProtocolInterfaces ( + snpdev->hii_child_handle, + &efi_device_path_protocol_guid, snpdev->hii_child_path, + NULL ); + free ( snpdev->hii_child_path ); + snpdev->hii_child_path = NULL; free ( snpdev->package_list ); snpdev->package_list = NULL; } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 81620c92c..8fe307ceb 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -25,12 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -#include -#include #include #include +#include #include -#include /** @file * @@ -38,19 +36,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Scale factor to apply to CPU timer 0 +/** + * Number of jiffies per second * - * The timer is scaled down in order to ensure that reasonable values - * for "number of ticks" don't exceed the size of an unsigned long. + * This is a policy decision. */ -#define EFI_TIMER0_SHIFT 12 +#define EFI_JIFFIES_PER_SEC 32 -/** Calibration time */ -#define EFI_CALIBRATE_DELAY_MS 1 +/** Current tick count */ +static unsigned long efi_jiffies; -/** CPU protocol */ -static EFI_CPU_ARCH_PROTOCOL *cpu_arch; -EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch ); +/** Timer tick event */ +static EFI_EVENT efi_tick_event; + +/** Colour for debug messages */ +#define colour &efi_jiffies /** * Delay for a fixed number of microseconds @@ -64,8 +64,8 @@ static void efi_udelay ( unsigned long usecs ) { if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) { rc = -EEFI ( efirc ); - DBG ( "EFI could not delay for %ldus: %s\n", - usecs, strerror ( rc ) ); + DBGC ( colour, "EFI could not delay for %ldus: %s\n", + usecs, strerror ( rc ) ); /* Probably screwed */ } } @@ -76,53 +76,149 @@ static void efi_udelay ( unsigned long usecs ) { * @ret ticks Current time, in ticks */ static unsigned long efi_currticks ( void ) { - UINT64 time; - EFI_STATUS efirc; - int rc; + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - /* Read CPU timer 0 (TSC) */ - if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBG ( "EFI could not read CPU timer: %s\n", strerror ( rc ) ); - /* Probably screwed */ - return -1UL; + /* UEFI manages to ingeniously combine the worst aspects of + * both polling and interrupt-driven designs. There is no way + * to support proper interrupt-driven operation, since there + * is no way to hook in an interrupt service routine. A + * mockery of interrupts is provided by UEFI timers, which + * trigger at a preset rate and can fire at any time. + * + * We therefore have all of the downsides of a polling design + * (inefficiency and inability to sleep until something + * interesting happens) combined with all of the downsides of + * an interrupt-driven design (the complexity of code that + * could be preempted at any time). + * + * The UEFI specification expects us to litter the entire + * codebase with calls to RaiseTPL() as needed for sections of + * code that are not reentrant. Since this doesn't actually + * gain us any substantive benefits (since even with such + * calls we would still be suffering from the limitations of a + * polling design), we instead choose to run at TPL_CALLBACK + * almost all of the time, dropping to TPL_APPLICATION to + * allow timer ticks to occur. + * + * + * For added excitement, UEFI provides no clean way for device + * drivers to shut down in preparation for handover to a + * booted operating system. The platform firmware simply + * doesn't bother to call the drivers' Stop() methods. + * Instead, all non-trivial drivers must register an + * EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when + * ExitBootServices() is called, and clean up without any + * reference to the EFI driver model. + * + * Unfortunately, all timers silently stop working when + * ExitBootServices() is called. Even more unfortunately, and + * for no discernible reason, this happens before any + * EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled. The + * net effect of this entertaining design choice is that any + * timeout loops on the shutdown path (e.g. for gracefully + * closing outstanding TCP connections) may wait indefinitely. + * + * There is no way to report failure from currticks(), since + * the API lazily assumes that the host system continues to + * travel through time in the usual direction. Work around + * EFI's violation of this assumption by falling back to a + * simple free-running monotonic counter during shutdown. + */ + if ( efi_shutdown_in_progress ) { + efi_jiffies++; + } else { + bs->RestoreTPL ( TPL_APPLICATION ); + bs->RaiseTPL ( TPL_CALLBACK ); } - return ( time >> EFI_TIMER0_SHIFT ); + return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) ); } /** - * Get number of ticks per second + * Timer tick * - * @ret ticks_per_sec Number of ticks per second + * @v event Timer tick event + * @v context Event context */ -static unsigned long efi_ticks_per_sec ( void ) { - static unsigned long ticks_per_sec = 0; +static EFIAPI void efi_tick ( EFI_EVENT event __unused, + void *context __unused ) { - /* Calibrate timer, if necessary. EFI does nominally provide - * the timer speed via the (optional) TimerPeriod parameter to - * the GetTimerValue() call, but it gets the speed slightly - * wrong. By up to three orders of magnitude. Not helpful. - */ - if ( ! ticks_per_sec ) { - unsigned long start; - unsigned long elapsed; - - DBG ( "Calibrating EFI timer with a %d ms delay\n", - EFI_CALIBRATE_DELAY_MS ); - start = currticks(); - mdelay ( EFI_CALIBRATE_DELAY_MS ); - elapsed = ( currticks() - start ); - ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS )); - DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld " - "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS, - ticks_per_sec ); - } - - return ticks_per_sec; + /* Increment tick count */ + efi_jiffies++; } -PROVIDE_TIMER ( efi, udelay, efi_udelay ); -PROVIDE_TIMER ( efi, currticks, efi_currticks ); -PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec ); +/** + * Start timer tick + * + */ +static void efi_tick_startup ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Create timer tick event */ + if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ), + TPL_CALLBACK, efi_tick, NULL, + &efi_tick_event ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not create timer tick: %s\n", + strerror ( rc ) ); + /* Nothing we can do about it */ + return; + } + + /* Start timer tick */ + if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic, + ( 10000000 / EFI_JIFFIES_PER_SEC ) ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not start timer tick: %s\n", + strerror ( rc ) ); + /* Nothing we can do about it */ + return; + } + DBGC ( colour, "EFI timer started at %d ticks per second\n", + EFI_JIFFIES_PER_SEC ); +} + +/** + * Stop timer tick + * + * @v booting System is shutting down in order to boot + */ +static void efi_tick_shutdown ( int booting __unused ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Stop timer tick */ + if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerCancel, 0 ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not stop timer tick: %s\n", + strerror ( rc ) ); + /* Self-destruct initiated */ + return; + } + DBGC ( colour, "EFI timer stopped\n" ); + + /* Destroy timer tick event */ + if ( ( efirc = bs->CloseEvent ( efi_tick_event ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( colour, "EFI could not destroy timer tick: %s\n", + strerror ( rc ) ); + /* Probably non-fatal */ + return; + } +} + +/** Timer tick startup function */ +struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .startup = efi_tick_startup, + .shutdown = efi_tick_shutdown, +}; + +/** EFI timer */ +struct timer efi_timer __timer ( TIMER_NORMAL ) = { + .name = "efi", + .currticks = efi_currticks, + .udelay = efi_udelay, +}; diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index 94f216104..48274f1d6 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -64,50 +64,6 @@ static const char * efi_usb_direction_name ( EFI_USB_DATA_DIRECTION direction ){ ****************************************************************************** */ -/** - * Poll USB bus - * - * @v usbdev EFI USB device - */ -static void efi_usb_poll ( struct efi_usb_device *usbdev ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct usb_bus *bus = usbdev->usb->port->hub->bus; - EFI_TPL tpl; - - /* UEFI manages to ingeniously combine the worst aspects of - * both polling and interrupt-driven designs. There is no way - * to support proper interrupt-driven operation, since there - * is no way to hook in an interrupt service routine. A - * mockery of interrupts is provided by UEFI timers, which - * trigger at a preset rate and can fire at any time. - * - * We therefore have all of the downsides of a polling design - * (inefficiency and inability to sleep until something - * interesting happens) combined with all of the downsides of - * an interrupt-driven design (the complexity of code that - * could be preempted at any time). - * - * The UEFI specification expects us to litter the entire - * codebase with calls to RaiseTPL() as needed for sections of - * code that are not reentrant. Since this doesn't actually - * gain us any substantive benefits (since even with such - * calls we would still be suffering from the limitations of a - * polling design), we instead choose to wrap only calls to - * usb_poll(). This should be sufficient for most practical - * purposes. - * - * A "proper" solution would involve rearchitecting the whole - * codebase to support interrupt-driven operation. - */ - tpl = bs->RaiseTPL ( TPL_NOTIFY ); - - /* Poll bus */ - usb_poll ( bus ); - - /* Restore task priority level */ - bs->RestoreTPL ( tpl ); -} - /** * Poll USB bus (from endpoint event timer) * @@ -216,7 +172,7 @@ static int efi_usb_open ( struct efi_usb_interface *usbintf, /* Create event */ if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ), - TPL_NOTIFY, efi_usb_timer, usbep, + TPL_CALLBACK, efi_usb_timer, usbep, &usbep->event ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( usbdev, "USBDEV %s %s could not create event: %s\n", @@ -363,7 +319,7 @@ static int efi_usb_sync_transfer ( struct efi_usb_interface *usbintf, for ( i = 0 ; ( ( timeout == 0 ) || ( i < timeout ) ) ; i++ ) { /* Poll bus */ - efi_usb_poll ( usbdev ); + usb_poll ( usbdev->usb->port->hub->bus ); /* Check for completion */ if ( usbep->rc != -EINPROGRESS ) { @@ -472,7 +428,7 @@ static int efi_usb_async_start ( struct efi_usb_interface *usbintf, usbep->context = context; /* Prefill endpoint */ - usb_refill_init ( &usbep->ep, len, EFI_USB_ASYNC_FILL ); + usb_refill_init ( &usbep->ep, 0, len, EFI_USB_ASYNC_FILL ); if ( ( rc = usb_prefill ( &usbep->ep ) ) != 0 ) { DBGC ( usbdev, "USBDEV %s %s could not prefill: %s\n", usbintf->name, usb_endpoint_name ( &usbep->ep ), @@ -549,6 +505,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, EFI_USB_DATA_DIRECTION direction, UINT32 timeout, VOID *data, UINTN len, UINT32 *status ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; @@ -556,6 +513,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, USB_REQUEST_TYPE ( packet->Request ) ); unsigned int value = le16_to_cpu ( packet->Value ); unsigned int index = le16_to_cpu ( packet->Index ); + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s control %04x:%04x:%04x:%04x %s %dms " @@ -564,6 +522,9 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, efi_usb_direction_name ( direction ), timeout, data, ( ( size_t ) len ) ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Clear status */ *status = 0; @@ -607,6 +568,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, err_control: err_change_config: + bs->RestoreTPL ( saved_tpl ); return EFIRC ( rc ); } @@ -624,16 +586,21 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, static EFI_STATUS EFIAPI efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, UINTN *len, UINTN timeout, UINT32 *status ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; size_t actual = *len; + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s bulk %s %p+%zx %dms\n", usbintf->name, ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data, ( ( size_t ) *len ), ( ( unsigned int ) timeout ) ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Clear status */ *status = 0; @@ -643,10 +610,12 @@ efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, data, &actual ) ) != 0 ) { /* Assume that any error represents a timeout */ *status = EFI_USB_ERR_TIMEOUT; - return rc; + goto err_transfer; } - return 0; + err_transfer: + bs->RestoreTPL ( saved_tpl ); + return EFIRC ( rc ); } /** @@ -664,16 +633,21 @@ static EFI_STATUS EFIAPI efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, UINTN *len, UINTN timeout, UINT32 *status ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; size_t actual = *len; + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s sync intr %s %p+%zx %dms\n", usbintf->name, ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data, ( ( size_t ) *len ), ( ( unsigned int ) timeout ) ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Clear status */ *status = 0; @@ -683,10 +657,12 @@ efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, timeout, data, &actual ) ) != 0 ) { /* Assume that any error represents a timeout */ *status = EFI_USB_ERR_TIMEOUT; - return rc; + goto err_transfer; } - return 0; + err_transfer: + bs->RestoreTPL ( saved_tpl ); + return EFIRC ( rc ); } /** @@ -706,9 +682,11 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, BOOLEAN start, UINTN interval, UINTN len, EFI_ASYNC_USB_TRANSFER_CALLBACK callback, VOID *context ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; + EFI_TPL saved_tpl; int rc; DBGC2 ( usbdev, "USBDEV %s async intr %s len %#zx int %d %p/%p\n", @@ -717,6 +695,9 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, ( ( size_t ) len ), ( ( unsigned int ) interval ), callback, context ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Start/stop transfer as applicable */ if ( start ) { @@ -731,11 +712,13 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, /* Stop transfer */ efi_usb_async_stop ( usbintf, endpoint ); + /* Success */ + rc = 0; + } - return 0; - err_start: + bs->RestoreTPL ( saved_tpl ); return EFIRC ( rc ); } @@ -933,12 +916,16 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, struct usb_descriptor_header header; VOID *buffer; size_t len; + EFI_TPL saved_tpl; EFI_STATUS efirc; int rc; DBGC2 ( usbdev, "USBDEV %s get string %d:%d descriptor\n", usbintf->name, language, index ); + /* Raise TPL */ + saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + /* Read descriptor header */ if ( ( rc = usb_get_descriptor ( usbdev->usb, 0, USB_STRING_DESCRIPTOR, index, language, &header, @@ -972,6 +959,9 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, ( len - sizeof ( header ) ) ); memset ( ( buffer + len - sizeof ( header ) ), 0, sizeof ( **string ) ); + /* Restore TPL */ + bs->RestoreTPL ( saved_tpl ); + /* Return allocated string */ *string = buffer; return 0; @@ -980,6 +970,7 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, bs->FreePool ( buffer ); err_alloc: err_get_header: + bs->RestoreTPL ( saved_tpl ); return EFIRC ( rc ); } diff --git a/src/arch/x86/prefix/efidrvprefix.c b/src/interface/efi/efidrvprefix.c similarity index 100% rename from src/arch/x86/prefix/efidrvprefix.c rename to src/interface/efi/efidrvprefix.c diff --git a/src/arch/x86/prefix/efiprefix.c b/src/interface/efi/efiprefix.c similarity index 100% rename from src/arch/x86/prefix/efiprefix.c rename to src/interface/efi/efiprefix.c diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index 795929eae..45a7caec4 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -49,6 +50,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define VMBUS_GPADL_MAGIC 0x18ae0000 +/** Current (i.e. most recently issued) GPADL ID */ +static unsigned int vmbus_gpadl = VMBUS_GPADL_MAGIC; + +/** Obsolete GPADL ID threshold + * + * When the Hyper-V connection is reset, any previous GPADLs are + * automatically rendered obsolete. + */ +unsigned int vmbus_obsolete_gpadl; + /** * Post message * @@ -89,12 +100,12 @@ static int vmbus_post_empty_message ( struct hv_hypervisor *hv, } /** - * Wait for received message + * Wait for received message of any type * * @v hv Hyper-V hypervisor * @ret rc Return status code */ -static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) { +static int vmbus_wait_for_any_message ( struct hv_hypervisor *hv ) { struct vmbus *vmbus = hv->vmbus; int rc; @@ -115,6 +126,38 @@ static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) { return 0; } +/** + * Wait for received message of a specified type, ignoring any others + * + * @v hv Hyper-V hypervisor + * @v type Message type + * @ret rc Return status code + */ +static int vmbus_wait_for_message ( struct hv_hypervisor *hv, + unsigned int type ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + int rc; + + /* Loop until specified message arrives, or until an error occurs */ + while ( 1 ) { + + /* Wait for message */ + if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 ) + return rc; + + /* Check for requested message type */ + if ( header->type == cpu_to_le32 ( type ) ) + return 0; + + /* Ignore any other messages (e.g. due to additional + * channels being offered at runtime). + */ + DBGC ( vmbus, "VMBUS %p ignoring message type %d (expecting " + "%d)\n", vmbus, le32_to_cpu ( header->type ), type ); + } +} + /** * Initiate contact * @@ -143,15 +186,10 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv, return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_VERSION_RESPONSE ) ) !=0) return rc; /* Check response */ - if ( version->header.type != cpu_to_le32 ( VMBUS_VERSION_RESPONSE ) ) { - DBGC ( vmbus, "VMBUS %p unexpected version response type %d\n", - vmbus, le32_to_cpu ( version->header.type ) ); - return -EPROTO; - } if ( ! version->supported ) { DBGC ( vmbus, "VMBUS %p requested version not supported\n", vmbus ); @@ -177,8 +215,6 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv, * @ret rc Return status code */ static int vmbus_unload ( struct hv_hypervisor *hv ) { - struct vmbus *vmbus = hv->vmbus; - const struct vmbus_message_header *header = &vmbus->message->header; int rc; /* Post message */ @@ -186,16 +222,9 @@ static int vmbus_unload ( struct hv_hypervisor *hv ) { return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_UNLOAD_RESPONSE ) ) != 0) return rc; - /* Check response */ - if ( header->type != cpu_to_le32 ( VMBUS_UNLOAD_RESPONSE ) ) { - DBGC ( vmbus, "VMBUS %p unexpected unload response type %d\n", - vmbus, le32_to_cpu ( header->type ) ); - return -EPROTO; - } - return 0; } @@ -262,12 +291,12 @@ int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, uint64_t pfn[pfn_count]; } __attribute__ (( packed )) gpadlhdr; const struct vmbus_gpadl_created *created = &vmbus->message->created; - static unsigned int gpadl = VMBUS_GPADL_MAGIC; + unsigned int gpadl; unsigned int i; int rc; /* Allocate GPADL ID */ - gpadl++; + gpadl = ++vmbus_gpadl; /* Construct message */ memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) ); @@ -289,15 +318,10 @@ int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_GPADL_CREATED ) ) != 0 ) return rc; /* Check response */ - if ( created->header.type != cpu_to_le32 ( VMBUS_GPADL_CREATED ) ) { - DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", - vmdev->dev.name, le32_to_cpu ( created->header.type ) ); - return -EPROTO; - } if ( created->channel != cpu_to_le32 ( vmdev->channel ) ) { DBGC ( vmdev, "VMBUS %s unexpected GPADL channel %d\n", vmdev->dev.name, le32_to_cpu ( created->channel ) ); @@ -333,6 +357,15 @@ int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) { const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown; int rc; + /* If GPADL is obsolete (i.e. was created before the most + * recent Hyper-V reset), then we will never receive a + * response to the teardown message. Since the GPADL is + * already destroyed as far as the hypervisor is concerned, no + * further action is required. + */ + if ( vmbus_gpadl_is_obsolete ( gpadl ) ) + return 0; + /* Construct message */ memset ( &teardown, 0, sizeof ( teardown ) ); teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN ); @@ -345,15 +378,10 @@ int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) { return rc; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + if ( ( rc = vmbus_wait_for_message ( hv, VMBUS_GPADL_TORNDOWN ) ) != 0 ) return rc; /* Check response */ - if ( torndown->header.type != cpu_to_le32 ( VMBUS_GPADL_TORNDOWN ) ) { - DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", - vmdev->dev.name, le32_to_cpu ( torndown->header.type ) ); - return -EPROTO; - } if ( torndown->gpadl != cpu_to_le32 ( gpadl ) ) { DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n", vmdev->dev.name, le32_to_cpu ( torndown->gpadl ) ); @@ -439,32 +467,31 @@ int vmbus_open ( struct vmbus_device *vmdev, /* Post message */ if ( ( rc = vmbus_post_message ( hv, &open.header, sizeof ( open ) ) ) != 0 ) - return rc; + goto err_post_message; /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) - return rc; + if ( ( rc = vmbus_wait_for_message ( hv, + VMBUS_OPEN_CHANNEL_RESULT ) ) != 0) + goto err_wait_for_message; /* Check response */ - if ( opened->header.type != cpu_to_le32 ( VMBUS_OPEN_CHANNEL_RESULT ) ){ - DBGC ( vmdev, "VMBUS %s unexpected open response type %d\n", - vmdev->dev.name, le32_to_cpu ( opened->header.type ) ); - return -EPROTO; - } if ( opened->channel != cpu_to_le32 ( vmdev->channel ) ) { DBGC ( vmdev, "VMBUS %s unexpected opened channel %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->channel ) ); - return -EPROTO; + rc = -EPROTO; + goto err_check_response; } if ( opened->id != open_id /* Non-endian */ ) { DBGC ( vmdev, "VMBUS %s unexpected open ID %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->id ) ); - return -EPROTO; + rc = -EPROTO; + goto err_check_response; } if ( opened->status != 0 ) { DBGC ( vmdev, "VMBUS %s open failed: %#08x\n", vmdev->dev.name, le32_to_cpu ( opened->status ) ); - return -EPROTO; + rc = -EPROTO; + goto err_check_response; } /* Store channel parameters */ @@ -483,6 +510,9 @@ int vmbus_open ( struct vmbus_device *vmdev, ( virt_to_phys ( vmdev->out ) + len ) ); return 0; + err_check_response: + err_wait_for_message: + err_post_message: vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ); err_establish: free_dma ( ring, len ); @@ -519,8 +549,7 @@ void vmbus_close ( struct vmbus_device *vmdev ) { } /* Tear down GPADL */ - if ( ( rc = vmbus_gpadl_teardown ( vmdev, - vmdev->gpadl ) ) != 0 ) { + if ( ( rc = vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ) ) != 0 ) { DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: " "%s\n", vmdev->dev.name, strerror ( rc ) ); /* We can't prevent the remote VM from continuing to @@ -559,7 +588,7 @@ static void vmbus_signal_monitor ( struct vmbus_device *vmdev ) { group = ( vmdev->monitor / ( 8 * sizeof ( trigger->pending ) )); bit = ( vmdev->monitor % ( 8 * sizeof ( trigger->pending ) ) ); trigger = &vmbus->monitor_out->trigger[group]; - hv_set_bit ( trigger, bit ); + set_bit ( bit, trigger ); } /** @@ -720,7 +749,7 @@ static int vmbus_send ( struct vmbus_device *vmdev, return 0; /* Set channel bit in interrupt page */ - hv_set_bit ( vmbus->intr->out, vmdev->channel ); + set_bit ( vmdev->channel, vmbus->intr->out ); /* Signal the host */ vmdev->signal ( vmdev ); @@ -1120,6 +1149,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, const struct vmbus_message_header *header = &vmbus->message->header; const struct vmbus_offer_channel *offer = &vmbus->message->offer; const union uuid *type; + union uuid instance; struct vmbus_driver *driver; struct vmbus_device *vmdev; struct vmbus_device *tmp; @@ -1134,8 +1164,8 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, while ( 1 ) { /* Wait for response */ - if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) - goto err_wait_for_message; + if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 ) + goto err_wait_for_any_message; /* Handle response */ if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) { @@ -1164,14 +1194,19 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, rc = -ENOMEM; goto err_alloc_vmdev; } + memcpy ( &instance, &offer->instance, + sizeof ( instance ) ); + uuid_mangle ( &instance ); snprintf ( vmdev->dev.name, sizeof ( vmdev->dev.name ), - "vmbus:%02x", channel ); + "{%s}", uuid_ntoa ( &instance ) ); vmdev->dev.desc.bus_type = BUS_TYPE_HV; INIT_LIST_HEAD ( &vmdev->dev.children ); list_add_tail ( &vmdev->dev.siblings, &parent->children ); vmdev->dev.parent = parent; vmdev->hv = hv; + memcpy ( &vmdev->instance, &offer->instance, + sizeof ( vmdev->instance ) ); vmdev->channel = channel; vmdev->monitor = offer->monitor; vmdev->signal = ( offer->monitored ? @@ -1186,6 +1221,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, } else if ( header->type == cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) { + /* End of offer list */ break; } else { @@ -1218,7 +1254,7 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, } err_unexpected_offer: err_alloc_vmdev: - err_wait_for_message: + err_wait_for_any_message: /* Free any devices allocated (but potentially not yet probed) */ list_for_each_entry_safe ( vmdev, tmp, &parent->children, dev.siblings ) { @@ -1229,6 +1265,77 @@ static int vmbus_probe_channels ( struct hv_hypervisor *hv, return rc; } + +/** + * Reset channels + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +static int vmbus_reset_channels ( struct hv_hypervisor *hv, + struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + const struct vmbus_offer_channel *offer = &vmbus->message->offer; + const union uuid *type; + struct vmbus_device *vmdev; + unsigned int channel; + int rc; + + /* Post message */ + if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0) + return rc; + + /* Collect responses */ + while ( 1 ) { + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 ) + return rc; + + /* Handle response */ + if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) { + + /* Parse offer */ + type = &offer->type; + channel = le32_to_cpu ( offer->channel ); + DBGC2 ( vmbus, "VMBUS %p offer %d type %s", + vmbus, channel, uuid_ntoa ( type ) ); + if ( offer->monitored ) + DBGC2 ( vmbus, " monitor %d", offer->monitor ); + DBGC2 ( vmbus, "\n" ); + + /* Do nothing with the offer; we already have all + * of the relevant state from the initial probe. + */ + + } else if ( header->type == + cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) { + + /* End of offer list */ + break; + + } else { + DBGC ( vmbus, "VMBUS %p unexpected offer response type " + "%d\n", vmbus, le32_to_cpu ( header->type ) ); + return -EPROTO; + } + } + + /* Reset all devices */ + list_for_each_entry ( vmdev, &parent->children, dev.siblings ) { + if ( ( rc = vmdev->driver->reset ( vmdev ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not reset: %s\n", + vmdev->dev.name, strerror ( rc ) ); + /* Continue attempting to reset other devices */ + continue; + } + } + + return 0; +} + /** * Remove channels * @@ -1315,6 +1422,39 @@ int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) { return rc; } +/** + * Reset Hyper-V virtual machine bus + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + int rc; + + /* Mark all existent GPADLs as obsolete */ + vmbus_obsolete_gpadl = vmbus_gpadl; + + /* Clear interrupt and monitor pages */ + memset ( vmbus->intr, 0, PAGE_SIZE ); + memset ( vmbus->monitor_in, 0, PAGE_SIZE ); + memset ( vmbus->monitor_out, 0, PAGE_SIZE ); + + /* Enable message interrupt */ + hv_enable_sint ( hv, VMBUS_MESSAGE_SINT ); + + /* Renegotiate protocol version */ + if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 ) + return rc; + + /* Reenumerate channels */ + if ( ( rc = vmbus_reset_channels ( hv, parent ) ) != 0 ) + return rc; + + return 0; +} + /** * Remove Hyper-V virtual machine bus * diff --git a/src/interface/linux/linux_timer.c b/src/interface/linux/linux_timer.c index 7a994517b..9c5e96f2b 100644 --- a/src/interface/linux/linux_timer.c +++ b/src/interface/linux/linux_timer.c @@ -39,16 +39,6 @@ static void linux_udelay(unsigned long usecs) linux_usleep(usecs); } -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -static unsigned long linux_ticks_per_sec(void) -{ - return 1000; -} - /** * Get current system time in ticks * @@ -67,21 +57,25 @@ static unsigned long linux_currticks(void) { static struct timeval start; static int initialized = 0; + struct timeval now; + unsigned long ticks; if (! initialized) { linux_gettimeofday(&start, NULL); initialized = 1; } - struct timeval now; linux_gettimeofday(&now, NULL); - unsigned long ticks = (now.tv_sec - start.tv_sec) * linux_ticks_per_sec(); - ticks += now.tv_usec / (long)(1000000 / linux_ticks_per_sec()); + ticks = ( ( now.tv_sec - start.tv_sec ) * TICKS_PER_SEC ); + ticks += ( now.tv_usec / ( 1000000 / TICKS_PER_SEC ) ); return ticks; } -PROVIDE_TIMER(linux, udelay, linux_udelay); -PROVIDE_TIMER(linux, currticks, linux_currticks); -PROVIDE_TIMER(linux, ticks_per_sec, linux_ticks_per_sec); +/** Linux timer */ +struct timer linux_timer __timer ( TIMER_NORMAL ) = { + .name = "linux", + .currticks = linux_currticks, + .udelay = linux_udelay, +}; diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index 5eadfa081..2d571f2e4 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -248,7 +248,7 @@ const struct setting asset_setting __setting ( SETTING_HOST_EXTRA, asset ) = { /** Board serial number setting (may differ from chassis serial number) */ const struct setting board_serial_setting __setting ( SETTING_HOST_EXTRA, - board_serial ) = { + board-serial ) = { .name = "board-serial", .description = "Base board serial", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_BASE_BOARD_INFORMATION, diff --git a/src/interface/xen/xenbus.c b/src/interface/xen/xenbus.c index c328af443..5dd01dfa3 100644 --- a/src/interface/xen/xenbus.c +++ b/src/interface/xen/xenbus.c @@ -206,13 +206,14 @@ static struct xen_driver * xenbus_find_driver ( const char *type ) { * * @v xen Xen hypervisor * @v parent Parent device - * @v type Device type * @v instance Device instance + * @v driver Device driver * @ret rc Return status code */ static int xenbus_probe_device ( struct xen_hypervisor *xen, - struct device *parent, const char *type, - const char *instance ) { + struct device *parent, const char *instance, + struct xen_driver *driver ) { + const char *type = driver->type; struct xen_device *xendev; size_t key_len; int rc; @@ -234,6 +235,10 @@ static int xenbus_probe_device ( struct xen_hypervisor *xen, xendev->xen = xen; xendev->key = ( ( void * ) ( xendev + 1 ) ); snprintf ( xendev->key, key_len, "device/%s/%s", type, instance ); + xendev->driver = driver; + xendev->dev.driver_name = driver->name; + DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key, + xendev->driver->name ); /* Read backend key */ if ( ( rc = xenstore_read ( xen, &xendev->backend, xendev->key, @@ -253,18 +258,6 @@ static int xenbus_probe_device ( struct xen_hypervisor *xen, DBGC ( xendev, "XENBUS %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); - /* Look for a driver */ - xendev->driver = xenbus_find_driver ( type ); - if ( ! xendev->driver ) { - DBGC ( xendev, "XENBUS %s has no driver\n", xendev->key ); - /* Not a fatal error */ - rc = 0; - goto err_no_driver; - } - xendev->dev.driver_name = xendev->driver->name; - DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key, - xendev->driver->name ); - /* Probe driver */ if ( ( rc = xendev->driver->probe ( xendev ) ) != 0 ) { DBGC ( xendev, "XENBUS could not probe %s: %s\n", @@ -276,7 +269,6 @@ static int xenbus_probe_device ( struct xen_hypervisor *xen, xendev->driver->remove ( xendev ); err_probe: - err_no_driver: err_read_backend_id: free ( xendev->backend ); err_read_backend: @@ -310,11 +302,21 @@ static void xenbus_remove_device ( struct xen_device *xendev ) { */ static int xenbus_probe_type ( struct xen_hypervisor *xen, struct device *parent, const char *type ) { + struct xen_driver *driver; char *children; char *child; size_t len; int rc; + /* Look for a driver */ + driver = xenbus_find_driver ( type ); + if ( ! driver ) { + DBGC ( xen, "XENBUS has no driver for \"%s\" devices\n", type ); + /* Not a fatal error */ + rc = 0; + goto err_no_driver; + } + /* Get children of this key */ if ( ( rc = xenstore_directory ( xen, &children, &len, "device", type, NULL ) ) != 0 ) { @@ -326,8 +328,8 @@ static int xenbus_probe_type ( struct xen_hypervisor *xen, /* Probe each child */ for ( child = children ; child < ( children + len ) ; child += ( strlen ( child ) + 1 /* NUL */ ) ) { - if ( ( rc = xenbus_probe_device ( xen, parent, type, - child ) ) != 0 ) + if ( ( rc = xenbus_probe_device ( xen, parent, child, + driver ) ) != 0 ) goto err_probe_device; } @@ -337,6 +339,7 @@ static int xenbus_probe_type ( struct xen_hypervisor *xen, err_probe_device: free ( children ); err_directory: + err_no_driver: return rc; } diff --git a/src/interface/xen/xenstore.c b/src/interface/xen/xenstore.c index 23424a926..a14881fcd 100644 --- a/src/interface/xen/xenstore.c +++ b/src/interface/xen/xenstore.c @@ -538,8 +538,7 @@ void xenstore_dump ( struct xen_hypervisor *xen, const char *key ) { child += ( strlen ( child ) + 1 /* NUL */ ) ) { /* Construct child key */ - asprintf ( &child_key, "%s/%s", key, child ); - if ( ! child_key ) { + if ( asprintf ( &child_key, "%s/%s", key, child ) < 0 ){ DBGC ( xen, "XENSTORE could not allocate child " "key \"%s/%s\"\n", key, child ); rc = -ENOMEM; diff --git a/src/libgcc/__divdi3.c b/src/libgcc/__divdi3.c index 7097b11e1..224bb69c7 100644 --- a/src/libgcc/__divdi3.c +++ b/src/libgcc/__divdi3.c @@ -6,21 +6,5 @@ __libgcc int64_t __divdi3(int64_t num, int64_t den) { - int minus = 0; - int64_t v; - - if ( num < 0 ) { - num = -num; - minus = 1; - } - if ( den < 0 ) { - den = -den; - minus ^= 1; - } - - v = __udivmoddi4(num, den, NULL); - if ( minus ) - v = -v; - - return v; + return __divmoddi4(num, den, NULL); } diff --git a/src/libgcc/__divmoddi4.c b/src/libgcc/__divmoddi4.c new file mode 100644 index 000000000..95e328d06 --- /dev/null +++ b/src/libgcc/__divmoddi4.c @@ -0,0 +1,25 @@ +#include "libgcc.h" + +__libgcc int64_t __divmoddi4(int64_t num, int64_t den, int64 *rem_p) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, (uint64_t *)rem_p); + if ( minus ) { + v = -v; + if ( rem_p ) + *rem_p = -(*rem_p); + } + + return v; +} diff --git a/src/libgcc/__moddi3.c b/src/libgcc/__moddi3.c index d671bbc4d..ea6fd6f7b 100644 --- a/src/libgcc/__moddi3.c +++ b/src/libgcc/__moddi3.c @@ -6,21 +6,8 @@ __libgcc int64_t __moddi3(int64_t num, int64_t den) { - int minus = 0; int64_t v; - if ( num < 0 ) { - num = -num; - minus = 1; - } - if ( den < 0 ) { - den = -den; - minus ^= 1; - } - - (void) __udivmoddi4(num, den, (uint64_t *)&v); - if ( minus ) - v = -v; - + (void) __divmoddi4(num, den, &v); return v; } diff --git a/src/libgcc/implicit.c b/src/libgcc/implicit.c new file mode 100644 index 000000000..645ae6d22 --- /dev/null +++ b/src/libgcc/implicit.c @@ -0,0 +1,26 @@ +/** @file + * + * gcc sometimes likes to insert implicit calls to memcpy() and + * memset(). Unfortunately, there doesn't seem to be any way to + * prevent it from doing this, or to force it to use the optimised + * versions as seen by C code; it insists on inserting symbol + * references to "memcpy" and "memset". We therefore include wrapper + * functions just to keep gcc happy. + * + */ + +#include + +void * gcc_implicit_memcpy ( void *dest, const void *src, + size_t len ) asm ( "memcpy" ); + +void * gcc_implicit_memcpy ( void *dest, const void *src, size_t len ) { + return memcpy ( dest, src, len ); +} + +void * gcc_implicit_memset ( void *dest, int character, + size_t len ) asm ( "memset" ); + +void * gcc_implicit_memset ( void *dest, int character, size_t len ) { + return memset ( dest, character, len ); +} diff --git a/src/libgcc/libgcc.h b/src/libgcc/libgcc.h index d3e9bdd73..eb7c68ec5 100644 --- a/src/libgcc/libgcc.h +++ b/src/libgcc/libgcc.h @@ -8,6 +8,7 @@ extern __libgcc uint64_t __udivmoddi4 ( uint64_t num, uint64_t den, uint64_t *rem ); extern __libgcc uint64_t __udivdi3 (uint64_t num, uint64_t den ); extern __libgcc uint64_t __umoddi3 ( uint64_t num, uint64_t den ); +extern __libgcc int64_t __divmoddi4 ( int64_t num, int64_t den, int64_t *rem ); extern __libgcc int64_t __divdi3 ( int64_t num, int64_t den ); extern __libgcc int64_t __moddi3 ( int64_t num, int64_t den ); diff --git a/src/libgcc/memcpy.c b/src/libgcc/memcpy.c deleted file mode 100644 index e98b78384..000000000 --- a/src/libgcc/memcpy.c +++ /dev/null @@ -1,18 +0,0 @@ -/** @file - * - * gcc sometimes likes to insert implicit calls to memcpy(). - * Unfortunately, there doesn't seem to be any way to prevent it from - * doing this, or to force it to use the optimised memcpy() as seen by - * C code; it insists on inserting a symbol reference to "memcpy". We - * therefore include wrapper functions just to keep gcc happy. - * - */ - -#include - -void * gcc_implicit_memcpy ( void *dest, const void *src, - size_t len ) asm ( "memcpy" ); - -void * gcc_implicit_memcpy ( void *dest, const void *src, size_t len ) { - return memcpy ( dest, src, len ); -} diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c index d4970ad5c..482000102 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -1321,7 +1321,7 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev, ctx->ticks_start = currticks(); ctx->ticks_beacon = 0; ctx->ticks_channel = currticks(); - ctx->hop_time = ticks_per_sec() / ( active ? 2 : 6 ); + ctx->hop_time = TICKS_PER_SEC / ( active ? 2 : 6 ); /* * Channels on 2.4GHz overlap, and the most commonly used @@ -1363,8 +1363,8 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev, int net80211_probe_step ( struct net80211_probe_ctx *ctx ) { struct net80211_device *dev = ctx->dev; - u32 start_timeout = NET80211_PROBE_TIMEOUT * ticks_per_sec(); - u32 gather_timeout = ticks_per_sec(); + u32 start_timeout = NET80211_PROBE_TIMEOUT * TICKS_PER_SEC; + u32 gather_timeout = TICKS_PER_SEC; u32 now = currticks(); struct io_buffer *iob; int signal; @@ -1586,9 +1586,6 @@ struct list_head *net80211_probe_finish_all ( struct net80211_probe_ctx *ctx ) { struct list_head *beacons = ctx->beacons; - if ( ! ctx ) - return NULL; - net80211_keep_mgmt ( ctx->dev, ctx->old_keep_mgmt ); if ( ctx->probe ) @@ -2606,7 +2603,7 @@ static void net80211_rx_frag ( struct net80211_device *dev, /* start a frag cache entry */ int i, newest = -1; u32 curr_ticks = currticks(), newest_ticks = 0; - u32 timeout = ticks_per_sec() * NET80211_FRAG_TIMEOUT; + u32 timeout = TICKS_PER_SEC * NET80211_FRAG_TIMEOUT; for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) { if ( dev->frags[i].in_use == 0 ) diff --git a/src/net/aoe.c b/src/net/aoe.c index 2da8655b4..3a6611d04 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -53,6 +53,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 ); struct net_protocol aoe_protocol __net_protocol; +struct acpi_model abft_model __acpi_model; /****************************************************************************** * @@ -91,6 +92,9 @@ struct aoe_device { struct interface config; /** Device is configued */ int configured; + + /** ACPI descriptor */ + struct acpi_descriptor desc; }; /** An AoE command */ @@ -790,32 +794,13 @@ static struct device * aoedev_identify_device ( struct aoe_device *aoedev ) { } /** - * Describe AoE device in an ACPI table + * Get AoE ACPI descriptor * * @v aoedev AoE device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code + * @ret desc ACPI descriptor */ -static int aoedev_describe ( struct aoe_device *aoedev, - struct acpi_description_header *acpi, - size_t len ) { - struct abft_table *abft = - container_of ( acpi, struct abft_table, acpi ); - - /* Sanity check */ - if ( len < sizeof ( *abft ) ) - return -ENOBUFS; - - /* Populate table */ - abft->acpi.signature = cpu_to_le32 ( ABFT_SIG ); - abft->acpi.length = cpu_to_le32 ( sizeof ( *abft ) ); - abft->acpi.revision = 1; - abft->shelf = cpu_to_le16 ( aoedev->major ); - abft->slot = aoedev->minor; - memcpy ( abft->mac, aoedev->netdev->ll_addr, sizeof ( abft->mac ) ); - - return 0; +static struct acpi_descriptor * aoedev_describe ( struct aoe_device *aoedev ) { + return &aoedev->desc; } /** AoE device ATA interface operations */ @@ -869,6 +854,7 @@ static int aoedev_open ( struct interface *parent, struct net_device *netdev, aoedev->minor = minor; memcpy ( aoedev->target, netdev->ll_broadcast, netdev->ll_protocol->ll_addr_len ); + acpi_init ( &aoedev->desc, &abft_model, &aoedev->refcnt ); /* Initiate configuration */ if ( ( rc = aoedev_cfg_command ( aoedev, &aoedev->config ) ) < 0 ) { @@ -1059,3 +1045,61 @@ struct uri_opener aoe_uri_opener __uri_opener = { .scheme = "aoe", .open = aoe_open, }; + +/****************************************************************************** + * + * AoE boot firmware table (aBFT) + * + ****************************************************************************** + */ + +/** + * Check if AoE boot firmware table descriptor is complete + * + * @v desc ACPI descriptor + * @ret rc Return status code + */ +static int abft_complete ( struct acpi_descriptor *desc __unused ) { + return 0; +} + +/** + * Install AoE boot firmware table(s) + * + * @v install Installation method + * @ret rc Return status code + */ +static int abft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { + struct aoe_device *aoedev; + struct abft_table abft; + int rc; + + list_for_each_entry ( aoedev, &abft_model.descs, desc.list ) { + + /* Populate table */ + memset ( &abft, 0, sizeof ( abft ) ); + abft.acpi.signature = cpu_to_le32 ( ABFT_SIG ); + abft.acpi.length = cpu_to_le32 ( sizeof ( abft ) ); + abft.acpi.revision = 1; + abft.shelf = cpu_to_le16 ( aoedev->major ); + abft.slot = aoedev->minor; + memcpy ( abft.mac, aoedev->netdev->ll_addr, + sizeof ( abft.mac ) ); + + /* Install table */ + if ( ( rc = install ( &abft.acpi ) ) != 0 ) { + DBGC ( aoedev, "AoE %s could not install aBFT: %s\n", + aoedev_name ( aoedev ), strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** aBFT model */ +struct acpi_model abft_model __acpi_model = { + .descs = LIST_HEAD_INIT ( abft_model.descs ), + .complete = abft_complete, + .install = abft_install, +}; diff --git a/src/net/arp.c b/src/net/arp.c index 1e27c44e7..c9b4109a9 100644 --- a/src/net/arp.c +++ b/src/net/arp.c @@ -139,8 +139,15 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, struct arp_net_protocol *arp_net_protocol; struct net_protocol *net_protocol; struct ll_protocol *ll_protocol; + size_t len = iob_len ( iobuf ); int rc; + /* Sanity check */ + if ( ( len < sizeof ( *arphdr ) ) || ( len < arp_len ( arphdr ) ) ) { + rc = -EINVAL; + goto done; + } + /* Identify network-layer and link-layer protocols */ arp_net_protocol = arp_find_protocol ( arphdr->ar_pro ); if ( ! arp_net_protocol ) { diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c index 049c26cb3..baa51dbc1 100644 --- a/src/net/eth_slow.c +++ b/src/net/eth_slow.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -91,7 +92,7 @@ eth_slow_marker_tlv_name ( uint8_t type ) { * @ret name LACP state name */ static const char * eth_slow_lacp_state_name ( uint8_t state ) { - static char state_chars[] = "AFGSRTLX"; + static char state_chars[] = "AFGSCDLX"; unsigned int i; for ( i = 0 ; i < 8 ; i++ ) { @@ -148,9 +149,30 @@ static int eth_slow_lacp_rx ( struct io_buffer *iobuf, struct net_device *netdev ) { union eth_slow_packet *eth_slow = iobuf->data; struct eth_slow_lacp *lacp = ð_slow->lacp; + unsigned int interval; eth_slow_lacp_dump ( iobuf, netdev, "RX" ); + /* If partner is not in sync, collecting, and distributing, + * then block the link until after the next expected LACP + * packet. + */ + if ( ~lacp->actor.state & ( LACP_STATE_IN_SYNC | + LACP_STATE_COLLECTING | + LACP_STATE_DISTRIBUTING ) ) { + DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name ); + interval = ( ( lacp->actor.state & LACP_STATE_FAST ) ? + ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) : + ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) ); + netdev_link_block ( netdev, interval ); + } else { + if ( netdev_link_blocked ( netdev ) ) { + DBGC ( netdev, "SLOW %s LACP partner is up\n", + netdev->name ); + } + netdev_link_unblock ( netdev ); + } + /* Build response */ memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) ); memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) ); diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 6ddf05344..26fdedea8 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -278,6 +278,3 @@ REQUIRING_SYMBOL ( ethernet_protocol ); /* Drag in Ethernet configuration */ REQUIRE_OBJECT ( config_ethernet ); - -/* Drag in Ethernet slow protocols */ -REQUIRE_OBJECT ( eth_slow ); diff --git a/src/net/fcoe.c b/src/net/fcoe.c index c3258f15e..f910eeead 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -1094,7 +1094,7 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { /* Send keepalive */ start_timer_fixed ( &fcoe->timer, - ( ( fcoe->keepalive * TICKS_PER_SEC ) / 1000 ) ); + ( fcoe->keepalive * TICKS_PER_MS ) ); fcoe_fip_tx_keepalive ( fcoe ); /* Abandon FCF if we have not seen its advertisements */ diff --git a/src/net/fcp.c b/src/net/fcp.c index 930bf7dd4..d92cfdcf3 100644 --- a/src/net/fcp.c +++ b/src/net/fcp.c @@ -843,25 +843,6 @@ static size_t fcpdev_window ( struct fcp_device *fcpdev ) { ~( ( size_t ) 0 ) : 0 ); } -/** - * Describe FCP device in an ACPI table - * - * @v fcpdev FCP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int fcpdev_acpi_describe ( struct fcp_device *fcpdev, - struct acpi_description_header *acpi, - size_t len ) { - - DBGC ( fcpdev, "FCP %p cannot yet describe device in an ACPI table\n", - fcpdev ); - ( void ) acpi; - ( void ) len; - return 0; -} - /** * Describe FCP device using EDD * @@ -917,7 +898,6 @@ static struct interface_operation fcpdev_scsi_op[] = { INTF_OP ( scsi_command, struct fcp_device *, fcpdev_scsi_command ), INTF_OP ( xfer_window, struct fcp_device *, fcpdev_window ), INTF_OP ( intf_close, struct fcp_device *, fcpdev_close ), - INTF_OP ( acpi_describe, struct fcp_device *, fcpdev_acpi_describe ), INTF_OP ( edd_describe, struct fcp_device *, fcpdev_edd_describe ), INTF_OP ( identify_device, struct fcp_device *, fcpdev_identify_device ), diff --git a/src/net/infiniband.c b/src/net/infiniband.c index 2e3d76d54..3b79a660c 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -53,6 +54,17 @@ struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices ); /** List of open Infiniband devices, in reverse order of opening */ static struct list_head open_ib_devices = LIST_HEAD_INIT ( open_ib_devices ); +/** Infiniband device index */ +static unsigned int ibdev_index = 0; + +/** Post send work queue entry profiler */ +static struct profiler ib_post_send_profiler __profiler = + { .name = "ib.post_send" }; + +/** Post receive work queue entry profiler */ +static struct profiler ib_post_recv_profiler __profiler = + { .name = "ib.post_recv" }; + /* Disambiguate the various possible EINPROGRESSes */ #define EINPROGRESS_INIT __einfo_error ( EINFO_EINPROGRESS_INIT ) #define EINFO_EINPROGRESS_INIT __einfo_uniqify \ @@ -80,44 +92,48 @@ struct errortab infiniband_errors[] __errortab = { * @v ibdev Infiniband device * @v num_cqes Number of completion queue entries * @v op Completion queue operations - * @ret cq New completion queue + * @v new_cq New completion queue to fill in + * @ret rc Return status code */ -struct ib_completion_queue * -ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, - struct ib_completion_queue_operations *op ) { +int ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, + struct ib_completion_queue_operations *op, + struct ib_completion_queue **new_cq ) { struct ib_completion_queue *cq; int rc; - DBGC ( ibdev, "IBDEV %p creating completion queue\n", ibdev ); + DBGC ( ibdev, "IBDEV %s creating completion queue\n", ibdev->name ); /* Allocate and initialise data structure */ cq = zalloc ( sizeof ( *cq ) ); - if ( ! cq ) + if ( ! cq ) { + rc = -ENOMEM; goto err_alloc_cq; + } cq->ibdev = ibdev; - list_add ( &cq->list, &ibdev->cqs ); + list_add_tail ( &cq->list, &ibdev->cqs ); cq->num_cqes = num_cqes; INIT_LIST_HEAD ( &cq->work_queues ); cq->op = op; /* Perform device-specific initialisation and get CQN */ if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not initialise completion " - "queue: %s\n", ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not initialise completion " + "queue: %s\n", ibdev->name, strerror ( rc ) ); goto err_dev_create_cq; } - DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) " - "with CQN %#lx\n", ibdev, num_cqes, cq, + DBGC ( ibdev, "IBDEV %s created %d-entry completion queue %p (%p) " + "with CQN %#lx\n", ibdev->name, num_cqes, cq, ib_cq_get_drvdata ( cq ), cq->cqn ); - return cq; + *new_cq = cq; + return 0; ibdev->op->destroy_cq ( ibdev, cq ); err_dev_create_cq: list_del ( &cq->list ); free ( cq ); err_alloc_cq: - return NULL; + return rc; } /** @@ -128,8 +144,8 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, */ void ib_destroy_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ) { - DBGC ( ibdev, "IBDEV %p destroying completion queue %#lx\n", - ibdev, cq->cqn ); + DBGC ( ibdev, "IBDEV %s destroying completion queue %#lx\n", + ibdev->name, cq->cqn ); assert ( list_empty ( &cq->work_queues ) ); ibdev->op->destroy_cq ( ibdev, cq ); list_del ( &cq->list ); @@ -173,64 +189,69 @@ void ib_poll_cq ( struct ib_device *ibdev, * @v num_recv_wqes Number of receive work queue entries * @v recv_cq Receive completion queue * @v op Queue pair operations - * @ret qp Queue pair + * @v name Queue pair name + * @v new_qp New queue pair to fill in + * @ret rc Return status code * * The queue pair will be left in the INIT state; you must call * ib_modify_qp() before it is ready to use for sending and receiving. */ -struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, - enum ib_queue_pair_type type, - unsigned int num_send_wqes, - struct ib_completion_queue *send_cq, - unsigned int num_recv_wqes, - struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op ) { +int ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type, + unsigned int num_send_wqes, + struct ib_completion_queue *send_cq, + unsigned int num_recv_wqes, + struct ib_completion_queue *recv_cq, + struct ib_queue_pair_operations *op, const char *name, + struct ib_queue_pair **new_qp ) { struct ib_queue_pair *qp; size_t total_size; int rc; - DBGC ( ibdev, "IBDEV %p creating queue pair\n", ibdev ); + DBGC ( ibdev, "IBDEV %s creating queue pair\n", ibdev->name ); /* Allocate and initialise data structure */ total_size = ( sizeof ( *qp ) + ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ) + ( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) ); qp = zalloc ( total_size ); - if ( ! qp ) + if ( ! qp ) { + rc = -ENOMEM; goto err_alloc_qp; + } qp->ibdev = ibdev; - list_add ( &qp->list, &ibdev->qps ); + list_add_tail ( &qp->list, &ibdev->qps ); qp->type = type; qp->send.qp = qp; qp->send.is_send = 1; qp->send.cq = send_cq; - list_add ( &qp->send.list, &send_cq->work_queues ); + list_add_tail ( &qp->send.list, &send_cq->work_queues ); qp->send.psn = ( random() & 0xffffffUL ); qp->send.num_wqes = num_send_wqes; qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) ); qp->recv.qp = qp; qp->recv.cq = recv_cq; - list_add ( &qp->recv.list, &recv_cq->work_queues ); + list_add_tail ( &qp->recv.list, &recv_cq->work_queues ); qp->recv.psn = ( random() & 0xffffffUL ); qp->recv.num_wqes = num_recv_wqes; qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) + ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) )); INIT_LIST_HEAD ( &qp->mgids ); qp->op = op; + qp->name = name; /* Perform device-specific initialisation and get QPN */ if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not initialise queue pair: " - "%s\n", ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not initialise queue pair: " + "%s\n", ibdev->name, strerror ( rc ) ); goto err_dev_create_qp; } - DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n", - ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn ); - DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n", - ibdev, qp->qpn, num_send_wqes, qp->send.iobufs, + DBGC ( ibdev, "IBDEV %s created queue pair %p (%p) with QPN %#lx\n", + ibdev->name, qp, ib_qp_get_drvdata ( qp ), qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx has %d send entries at [%p,%p)\n", + ibdev->name, qp->qpn, num_send_wqes, qp->send.iobufs, qp->recv.iobufs ); - DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n", - ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs, + DBGC ( ibdev, "IBDEV %s QPN %#lx has %d receive entries at [%p,%p)\n", + ibdev->name, qp->qpn, num_recv_wqes, qp->recv.iobufs, ( ( ( void * ) qp ) + total_size ) ); /* Calculate externally-visible QPN */ @@ -246,11 +267,12 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, break; } if ( qp->ext_qpn != qp->qpn ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx has external QPN %#lx\n", - ibdev, qp->qpn, qp->ext_qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx has external QPN %#lx\n", + ibdev->name, qp->qpn, qp->ext_qpn ); } - return qp; + *new_qp = qp; + return 0; ibdev->op->destroy_qp ( ibdev, qp ); err_dev_create_qp: @@ -259,7 +281,7 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, list_del ( &qp->list ); free ( qp ); err_alloc_qp: - return NULL; + return rc; } /** @@ -272,11 +294,11 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { int rc; - DBGC ( ibdev, "IBDEV %p modifying QPN %#lx\n", ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s modifying QPN %#lx\n", ibdev->name, qp->qpn ); if ( ( rc = ibdev->op->modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not modify QPN %#lx: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not modify QPN %#lx: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); return rc; } @@ -293,8 +315,8 @@ void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { struct io_buffer *iobuf; unsigned int i; - DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n", - ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s destroying QPN %#lx\n", + ibdev->name, qp->qpn ); assert ( list_empty ( &qp->mgids ) ); @@ -397,10 +419,13 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_address_vector dest_copy; int rc; + /* Start profiling */ + profile_start ( &ib_post_send_profiler ); + /* Check queue fill level */ if ( qp->send.fill >= qp->send.num_wqes ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx send queue full\n", - ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx send queue full\n", + ibdev->name, qp->qpn ); return -ENOBUFS; } @@ -420,12 +445,17 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Post to hardware */ if ( ( rc = ibdev->op->post_send ( ibdev, qp, dest, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx could not post send WQE: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not post send WQE: " + "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); return rc; } + /* Increase fill level */ qp->send.fill++; + + /* Stop profiling */ + profile_stop ( &ib_post_send_profiler ); + return 0; } @@ -441,28 +471,36 @@ int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct io_buffer *iobuf ) { int rc; + /* Start profiling */ + profile_start ( &ib_post_recv_profiler ); + /* Check packet length */ if ( iob_tailroom ( iobuf ) < IB_MAX_PAYLOAD_SIZE ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx wrong RX buffer size (%zd)\n", - ibdev, qp->qpn, iob_tailroom ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx wrong RX buffer size (%zd)\n", + ibdev->name, qp->qpn, iob_tailroom ( iobuf ) ); return -EINVAL; } /* Check queue fill level */ if ( qp->recv.fill >= qp->recv.num_wqes ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx receive queue full\n", - ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx receive queue full\n", + ibdev->name, qp->qpn ); return -ENOBUFS; } /* Post to hardware */ if ( ( rc = ibdev->op->post_recv ( ibdev, qp, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx could not post receive WQE: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not post receive WQE: " + "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); return rc; } + /* Increase fill level */ qp->recv.fill++; + + /* Stop profiling */ + profile_stop ( &ib_post_recv_profiler ); + return 0; } @@ -531,8 +569,8 @@ void ib_refill_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { /* Post I/O buffer */ if ( ( rc = ib_post_recv ( ibdev, qp, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not refill: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not refill: %s\n", + ibdev->name, strerror ( rc ) ); free_iob ( iobuf ); /* Give up */ return; @@ -598,8 +636,8 @@ static void ib_notify ( struct ib_device *ibdev ) { */ void ib_link_state_changed ( struct ib_device *ibdev ) { - DBGC ( ibdev, "IBDEV %p link state is %s\n", - ibdev, ib_link_state_text ( ibdev ) ); + DBGC ( ibdev, "IBDEV %s link state is %s\n", + ibdev->name, ib_link_state_text ( ibdev ) ); /* Notify drivers of link state change */ ib_notify ( ibdev ); @@ -622,31 +660,29 @@ int ib_open ( struct ib_device *ibdev ) { /* Open device */ if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not open: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not open: %s\n", + ibdev->name, strerror ( rc ) ); goto err_open; } /* Create subnet management interface */ - ibdev->smi = ib_create_mi ( ibdev, IB_QPT_SMI ); - if ( ! ibdev->smi ) { - DBGC ( ibdev, "IBDEV %p could not create SMI\n", ibdev ); - rc = -ENOMEM; + if ( ( rc = ib_create_mi ( ibdev, IB_QPT_SMI, &ibdev->smi ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %s could not create SMI: %s\n", + ibdev->name, strerror ( rc ) ); goto err_create_smi; } /* Create subnet management agent */ if ( ( rc = ib_create_sma ( ibdev, ibdev->smi ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not create SMA: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not create SMA: %s\n", + ibdev->name, strerror ( rc ) ); goto err_create_sma; } /* Create general services interface */ - ibdev->gsi = ib_create_mi ( ibdev, IB_QPT_GSI ); - if ( ! ibdev->gsi ) { - DBGC ( ibdev, "IBDEV %p could not create GSI\n", ibdev ); - rc = -ENOMEM; + if ( ( rc = ib_create_mi ( ibdev, IB_QPT_GSI, &ibdev->gsi ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %s could not create GSI: %s\n", + ibdev->name, strerror ( rc ) ); goto err_create_gsi; } @@ -728,7 +764,7 @@ int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, goto err_alloc_mgid; } memcpy ( &mgid->gid, gid, sizeof ( mgid->gid ) ); - list_add ( &mgid->list, &qp->mgids ); + list_add_tail ( &mgid->list, &qp->mgids ); /* Add to hardware multicast GID list */ if ( ( rc = ibdev->op->mcast_attach ( ibdev, qp, gid ) ) != 0 ) @@ -808,14 +844,14 @@ int ib_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) { /* Adapters with embedded SMAs do not need to support this method */ if ( ! ibdev->op->set_port_info ) { - DBGC ( ibdev, "IBDEV %p does not support setting port " - "information\n", ibdev ); + DBGC ( ibdev, "IBDEV %s does not support setting port " + "information\n", ibdev->name ); return -ENOTSUP; } if ( ( rc = ibdev->op->set_port_info ( ibdev, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not set port information: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not set port information: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } @@ -833,14 +869,14 @@ int ib_set_pkey_table ( struct ib_device *ibdev, union ib_mad *mad ) { /* Adapters with embedded SMAs do not need to support this method */ if ( ! ibdev->op->set_pkey_table ) { - DBGC ( ibdev, "IBDEV %p does not support setting partition " - "key table\n", ibdev ); + DBGC ( ibdev, "IBDEV %s does not support setting partition " + "key table\n", ibdev->name ); return -ENOTSUP; } if ( ( rc = ibdev->op->set_pkey_table ( ibdev, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not set partition key table: " - "%s\n", ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not set partition key table: " + "%s\n", ibdev->name, strerror ( rc ) ); return rc; } @@ -929,17 +965,24 @@ int register_ibdev ( struct ib_device *ibdev ) { struct ib_driver *driver; int rc; + /* Record device index and create device name */ + if ( ibdev->name[0] == '\0' ) { + snprintf ( ibdev->name, sizeof ( ibdev->name ), "inf%d", + ibdev_index ); + } + ibdev->index = ++ibdev_index; + /* Add to device list */ ibdev_get ( ibdev ); list_add_tail ( &ibdev->list, &ib_devices ); - DBGC ( ibdev, "IBDEV %p registered (phys %s)\n", ibdev, + DBGC ( ibdev, "IBDEV %s registered (phys %s)\n", ibdev->name, ibdev->dev->name ); /* Probe device */ for_each_table_entry ( driver, IB_DRIVERS ) { if ( ( rc = driver->probe ( ibdev ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not add %s device: %s\n", - ibdev, driver->name, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not add %s device: %s\n", + ibdev->name, driver->name, strerror ( rc ) ); goto err_probe; } } @@ -969,7 +1012,11 @@ void unregister_ibdev ( struct ib_device *ibdev ) { /* Remove from device list */ list_del ( &ibdev->list ); ibdev_put ( ibdev ); - DBGC ( ibdev, "IBDEV %p unregistered\n", ibdev ); + DBGC ( ibdev, "IBDEV %s unregistered\n", ibdev->name ); + + /* Reset device index if no devices remain */ + if ( list_empty ( &ib_devices ) ) + ibdev_index = 0; } /** @@ -1010,6 +1057,3 @@ REQUIRING_SYMBOL ( register_ibdev ); /* Drag in Infiniband configuration */ REQUIRE_OBJECT ( config_infiniband ); - -/* Drag in IPoIB */ -REQUIRE_OBJECT ( ipoib ); diff --git a/src/net/infiniband/ib_cm.c b/src/net/infiniband/ib_cm.c index 85982f09d..247b8e7a0 100644 --- a/src/net/infiniband/ib_cm.c +++ b/src/net/infiniband/ib_cm.c @@ -65,6 +65,7 @@ static struct ib_connection * ib_cm_find ( uint32_t local_id ) { * * @v ibdev Infiniband device * @v mi Management interface + * @v tid Transaction identifier * @v av Address vector * @v local_id Local communication ID * @v remote_id Remote communication ID @@ -72,6 +73,7 @@ static struct ib_connection * ib_cm_find ( uint32_t local_id ) { */ static int ib_cm_send_rtu ( struct ib_device *ibdev, struct ib_mad_interface *mi, + struct ib_mad_tid *tid, struct ib_address_vector *av, uint32_t local_id, uint32_t remote_id ) { union ib_mad mad; @@ -83,11 +85,13 @@ static int ib_cm_send_rtu ( struct ib_device *ibdev, mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; mad.hdr.class_version = IB_CM_CLASS_VERSION; mad.hdr.method = IB_MGMT_METHOD_SEND; + memcpy ( &mad.hdr.tid, tid, sizeof ( mad.hdr.tid ) ); mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE ); rtu->local_id = htonl ( local_id ); rtu->remote_id = htonl ( remote_id ); - if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){ - DBG ( "CM could not send RTU: %s\n", strerror ( rc ) ); + if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ) { + DBGC ( local_id, "CM %08x could not send RTU: %s\n", + local_id, strerror ( rc ) ); return rc; } @@ -120,12 +124,13 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, conn = ib_cm_find ( local_id ); if ( conn ) { /* Try to send "ready to use" reply */ - if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id, + if ( ( rc = ib_cm_send_rtu ( ibdev, mi, &mad->hdr.tid, av, + conn->local_id, conn->remote_id ) ) != 0 ) { /* Ignore errors; the remote end will retry */ } } else { - DBG ( "CM unidentified connection %08x\n", local_id ); + DBGC ( local_id, "CM %08x unexpected REP\n", local_id ); } } @@ -134,6 +139,7 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, * * @v ibdev Infiniband device * @v mi Management interface + * @v tid Transaction identifier * @v av Address vector * @v local_id Local communication ID * @v remote_id Remote communication ID @@ -141,6 +147,7 @@ static void ib_cm_recv_rep ( struct ib_device *ibdev, */ static int ib_cm_send_drep ( struct ib_device *ibdev, struct ib_mad_interface *mi, + struct ib_mad_tid *tid, struct ib_address_vector *av, uint32_t local_id, uint32_t remote_id ) { union ib_mad mad; @@ -152,11 +159,13 @@ static int ib_cm_send_drep ( struct ib_device *ibdev, mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; mad.hdr.class_version = IB_CM_CLASS_VERSION; mad.hdr.method = IB_MGMT_METHOD_SEND; + memcpy ( &mad.hdr.tid, tid, sizeof ( mad.hdr.tid ) ); mad.hdr.attr_id = htons ( IB_CM_ATTR_DISCONNECT_REPLY ); drep->local_id = htonl ( local_id ); drep->remote_id = htonl ( remote_id ); - if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){ - DBG ( "CM could not send DREP: %s\n", strerror ( rc ) ); + if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ) { + DBGC ( local_id, "CM %08x could not send DREP: %s\n", + local_id, strerror ( rc ) ); return rc; } @@ -191,11 +200,11 @@ static void ib_cm_recv_dreq ( struct ib_device *ibdev, &dreq->private_data, sizeof ( dreq->private_data ) ); } else { - DBG ( "CM unidentified connection %08x\n", local_id ); + DBGC ( local_id, "CM %08x unexpected DREQ\n", local_id ); } /* Send reply */ - if ( ( rc = ib_cm_send_drep ( ibdev, mi, av, local_id, + if ( ( rc = ib_cm_send_drep ( ibdev, mi, &mad->hdr.tid, av, local_id, remote_id ) ) != 0 ) { /* Ignore errors; the remote end will retry */ } @@ -256,6 +265,7 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, struct ib_cm_common *common = &mad->cm.cm_data.common; struct ib_cm_connect_reply *rep = &mad->cm.cm_data.connect_reply; struct ib_cm_connect_reject *rej = &mad->cm.cm_data.connect_reject; + uint32_t local_id = conn->local_id; void *private_data = NULL; size_t private_data_len = 0; @@ -263,8 +273,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -EIO; if ( rc != 0 ) { - DBGC ( conn, "CM %p connection request failed: %s\n", - conn, strerror ( rc ) ); + DBGC ( local_id, "CM %08x connection request failed: %s\n", + local_id, strerror ( rc ) ); goto out; } @@ -280,18 +290,19 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, qp->send.psn = ( ntohl ( rep->starting_psn ) >> 8 ); private_data = &rep->private_data; private_data_len = sizeof ( rep->private_data ); - DBGC ( conn, "CM %p connected to QPN %lx PSN %x\n", - conn, qp->av.qpn, qp->send.psn ); + DBGC ( local_id, "CM %08x connected to QPN %#lx PSN %#x\n", + local_id, qp->av.qpn, qp->send.psn ); /* Modify queue pair */ if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( conn, "CM %p could not modify queue pair: %s\n", - conn, strerror ( rc ) ); + DBGC ( local_id, "CM %08x could not modify queue " + "pair: %s\n", local_id, strerror ( rc ) ); goto out; } /* Send "ready to use" reply */ - if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id, + if ( ( rc = ib_cm_send_rtu ( ibdev, mi, &mad->hdr.tid, av, + conn->local_id, conn->remote_id ) ) != 0 ) { /* Treat as non-fatal */ rc = 0; @@ -300,8 +311,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, case htons ( IB_CM_ATTR_CONNECT_REJECT ) : /* Extract fields */ - DBGC ( conn, "CM %p connection rejected (reason %d)\n", - conn, ntohs ( rej->reason ) ); + DBGC ( local_id, "CM %08x connection rejected (reason %d)\n", + local_id, ntohs ( rej->reason ) ); /* Private data is valid only for a Consumer Reject */ if ( rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) { private_data = &rej->private_data; @@ -311,8 +322,8 @@ static void ib_cm_req_complete ( struct ib_device *ibdev, break; default: - DBGC ( conn, "CM %p unexpected response (attribute %04x)\n", - conn, ntohs ( mad->hdr.attr_id ) ); + DBGC ( local_id, "CM %08x unexpected response (attribute " + "%04x)\n", local_id, ntohs ( mad->hdr.attr_id ) ); rc = -ENOTSUP; break; } @@ -347,12 +358,13 @@ static void ib_cm_path_complete ( struct ib_device *ibdev, struct ib_queue_pair *qp = conn->qp; union ib_mad mad; struct ib_cm_connect_request *req = &mad.cm.cm_data.connect_request; + uint32_t local_id = conn->local_id; size_t private_data_len; /* Report failures */ if ( rc != 0 ) { - DBGC ( conn, "CM %p path lookup failed: %s\n", - conn, strerror ( rc ) ); + DBGC ( local_id, "CM %08x path lookup failed: %s\n", + local_id, strerror ( rc ) ); conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 ); goto out; } @@ -405,8 +417,8 @@ static void ib_cm_path_complete ( struct ib_device *ibdev, conn->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, av, &ib_cm_req_op ); if ( ! conn->madx ) { - DBGC ( conn, "CM %p could not create connection request\n", - conn ); + DBGC ( local_id, "CM %08x could not create connection " + "request\n", local_id ); conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 ); goto out; } @@ -441,6 +453,7 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, void *private_data, size_t private_data_len, struct ib_connection_operations *op ) { struct ib_connection *conn; + uint32_t local_id; /* Allocate and initialise request */ conn = zalloc ( sizeof ( *conn ) + private_data_len ); @@ -451,7 +464,7 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, memset ( &qp->av, 0, sizeof ( qp->av ) ); qp->av.gid_present = 1; memcpy ( &qp->av.gid, dgid, sizeof ( qp->av.gid ) ); - conn->local_id = random(); + conn->local_id = local_id = random(); memcpy ( &conn->service_id, service_id, sizeof ( conn->service_id ) ); conn->op = op; conn->private_data_len = private_data_len; @@ -466,10 +479,11 @@ ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, /* Add to list of connections */ list_add ( &conn->list, &ib_cm_conns ); - DBGC ( conn, "CM %p created for IBDEV %p QPN %lx\n", - conn, ibdev, qp->qpn ); - DBGC ( conn, "CM %p connecting to " IB_GID_FMT " " IB_GUID_FMT "\n", - conn, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); + DBGC ( local_id, "CM %08x created for IBDEV %s QPN %#lx\n", + local_id, ibdev->name, qp->qpn ); + DBGC ( local_id, "CM %08x connecting to " IB_GID_FMT " " + IB_GUID_FMT "\n", local_id, IB_GID_ARGS ( dgid ), + IB_GUID_ARGS ( service_id ) ); return conn; diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 1cc0fcfef..b8f4bf36b 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -69,6 +69,8 @@ FILE_LICENCE ( BSD2 ); struct ib_cmrc_connection { /** Reference count */ struct refcnt refcnt; + /** Name */ + const char *name; /** Data transfer interface */ struct interface xfer; /** Infiniband device */ @@ -108,14 +110,19 @@ struct ib_cmrc_connection { * shutdown process has run. */ static void ib_cmrc_shutdown ( struct ib_cmrc_connection *cmrc ) { + struct ib_device *ibdev = cmrc->ibdev; - DBGC ( cmrc, "CMRC %p shutting down\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s shutting down\n", + ibdev->name, cmrc->name ); /* Shut down Infiniband interface */ - ib_destroy_conn ( cmrc->ibdev, cmrc->qp, cmrc->conn ); - ib_destroy_qp ( cmrc->ibdev, cmrc->qp ); - ib_destroy_cq ( cmrc->ibdev, cmrc->cq ); - ib_close ( cmrc->ibdev ); + ib_destroy_conn ( ibdev, cmrc->qp, cmrc->conn ); + ib_destroy_qp ( ibdev, cmrc->qp ); + ib_destroy_cq ( ibdev, cmrc->cq ); + ib_close ( ibdev ); + + /* Cancel any pending shutdown */ + process_del ( &cmrc->shutdown ); /* Drop the remaining reference */ ref_put ( &cmrc->refcnt ); @@ -146,7 +153,7 @@ static void ib_cmrc_close ( struct ib_cmrc_connection *cmrc, int rc ) { * @v private_data Private data, if available * @v private_data_len Length of private data */ -static void ib_cmrc_changed ( struct ib_device *ibdev __unused, +static void ib_cmrc_changed ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_connection *conn __unused, int rc_cm, void *private_data, size_t private_data_len ) { @@ -155,22 +162,24 @@ static void ib_cmrc_changed ( struct ib_device *ibdev __unused, /* Record connection status */ if ( rc_cm == 0 ) { - DBGC ( cmrc, "CMRC %p connected\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s connected\n", + ibdev->name, cmrc->name ); cmrc->connected = 1; } else { - DBGC ( cmrc, "CMRC %p disconnected: %s\n", - cmrc, strerror ( rc_cm ) ); + DBGC ( cmrc, "CMRC %s %s disconnected: %s\n", + ibdev->name, cmrc->name, strerror ( rc_cm ) ); cmrc->connected = 0; } /* Pass up any private data */ - DBGC2 ( cmrc, "CMRC %p received private data:\n", cmrc ); + DBGC2 ( cmrc, "CMRC %s %s received private data:\n", + ibdev->name, cmrc->name ); DBGC2_HDA ( cmrc, 0, private_data, private_data_len ); if ( private_data && ( rc_xfer = xfer_deliver_raw ( &cmrc->xfer, private_data, private_data_len ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not deliver private data: %s\n", - cmrc, strerror ( rc_xfer ) ); + DBGC ( cmrc, "CMRC %s %s could not deliver private data: %s\n", + ibdev->name, cmrc->name, strerror ( rc_xfer ) ); ib_cmrc_close ( cmrc, rc_xfer ); return; } @@ -198,7 +207,7 @@ static struct ib_connection_operations ib_cmrc_conn_op = { * @v iobuf I/O buffer * @v rc Completion status code */ -static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, +static void ib_cmrc_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct io_buffer *iobuf, int rc ) { struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp ); @@ -208,8 +217,8 @@ static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, /* Close the connection on any send errors */ if ( rc != 0 ) { - DBGC ( cmrc, "CMRC %p send error: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s send error: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); ib_cmrc_close ( cmrc, rc ); return; } @@ -225,7 +234,7 @@ static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, * @v iobuf I/O buffer * @v rc Completion status code */ -static void ib_cmrc_complete_recv ( struct ib_device *ibdev __unused, +static void ib_cmrc_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_address_vector *dest __unused, struct ib_address_vector *source __unused, @@ -234,20 +243,20 @@ static void ib_cmrc_complete_recv ( struct ib_device *ibdev __unused, /* Close the connection on any receive errors */ if ( rc != 0 ) { - DBGC ( cmrc, "CMRC %p receive error: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s receive error: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); free_iob ( iobuf ); ib_cmrc_close ( cmrc, rc ); return; } - DBGC2 ( cmrc, "CMRC %p received:\n", cmrc ); + DBGC2 ( cmrc, "CMRC %s %s received:\n", ibdev->name, cmrc->name ); DBGC2_HDA ( cmrc, 0, iobuf->data, iob_len ( iobuf ) ); /* Pass up data */ if ( ( rc = xfer_deliver_iob ( &cmrc->xfer, iobuf ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not deliver data: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s could not deliver data: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); ib_cmrc_close ( cmrc, rc ); return; } @@ -275,6 +284,7 @@ static struct ib_queue_pair_operations ib_cmrc_queue_pair_ops = { static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, struct io_buffer *iobuf, struct xfer_metadata *meta __unused ) { + struct ib_device *ibdev = cmrc->ibdev; int rc; /* If no connection has yet been attempted, send this datagram @@ -284,8 +294,9 @@ static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, /* Abort if we have already sent a CM connection request */ if ( cmrc->conn ) { - DBGC ( cmrc, "CMRC %p attempt to send before " - "connection is complete\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s attempt to send before " + "connection is complete\n", + ibdev->name, cmrc->name ); rc = -EIO; goto out; } @@ -296,18 +307,21 @@ static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, iobuf->data, iob_len ( iobuf ), &ib_cmrc_conn_op ); if ( ! cmrc->conn ) { - DBGC ( cmrc, "CMRC %p could not connect\n", cmrc ); + DBGC ( cmrc, "CMRC %s %s could not connect\n", + ibdev->name, cmrc->name ); rc = -ENOMEM; goto out; } + DBGC ( cmrc, "CMRC %s %s using CM %08x\n", + ibdev->name, cmrc->name, cmrc->conn->local_id ); } else { /* Send via QP */ if ( ( rc = ib_post_send ( cmrc->ibdev, cmrc->qp, NULL, iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not send: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s could not send: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); goto out; } @@ -377,10 +391,12 @@ static struct process_descriptor ib_cmrc_shutdown_desc = * @v ibdev Infiniband device * @v dgid Destination GID * @v service_id Service ID + * @v name Connection name * @ret rc Returns status code */ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, - union ib_gid *dgid, union ib_guid *service_id ) { + union ib_gid *dgid, union ib_guid *service_id, + const char *name ) { struct ib_cmrc_connection *cmrc; int rc; @@ -391,6 +407,7 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, goto err_alloc; } ref_init ( &cmrc->refcnt, NULL ); + cmrc->name = name; intf_init ( &cmrc->xfer, &ib_cmrc_xfer_desc, &cmrc->refcnt ); cmrc->ibdev = ibdev; memcpy ( &cmrc->dgid, dgid, sizeof ( cmrc->dgid ) ); @@ -400,32 +417,31 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, /* Open Infiniband device */ if ( ( rc = ib_open ( ibdev ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not open device: %s\n", - cmrc, strerror ( rc ) ); + DBGC ( cmrc, "CMRC %s %s could not open device: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); goto err_open; } /* Create completion queue */ - cmrc->cq = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES, - &ib_cmrc_completion_ops ); - if ( ! cmrc->cq ) { - DBGC ( cmrc, "CMRC %p could not create completion queue\n", - cmrc ); - rc = -ENOMEM; + if ( ( rc = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES, + &ib_cmrc_completion_ops, &cmrc->cq ) ) != 0){ + DBGC ( cmrc, "CMRC %s %s could not create completion queue: " + "%s\n", ibdev->name, cmrc->name, strerror ( rc ) ); goto err_create_cq; } /* Create queue pair */ - cmrc->qp = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES, - cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, - &ib_cmrc_queue_pair_ops ); - if ( ! cmrc->qp ) { - DBGC ( cmrc, "CMRC %p could not create queue pair\n", cmrc ); - rc = -ENOMEM; + if ( ( rc = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES, + cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, + &ib_cmrc_queue_pair_ops, name, + &cmrc->qp ) ) != 0 ) { + DBGC ( cmrc, "CMRC %s %s could not create queue pair: %s\n", + ibdev->name, cmrc->name, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( cmrc->qp, cmrc ); - DBGC ( cmrc, "CMRC %p using QPN %lx\n", cmrc, cmrc->qp->qpn ); + DBGC ( cmrc, "CMRC %s %s using QPN %#lx\n", + ibdev->name, cmrc->name, cmrc->qp->qpn ); /* Attach to parent interface, transfer reference (implicitly) * to our shutdown process, and return. diff --git a/src/net/infiniband/ib_mcast.c b/src/net/infiniband/ib_mcast.c index fc4ff7f0a..f7264287c 100644 --- a/src/net/infiniband/ib_mcast.c +++ b/src/net/infiniband/ib_mcast.c @@ -42,26 +42,34 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Generate multicast membership MAD * * @v ibdev Infiniband device - * @v gid Multicast GID - * @v join Join (rather than leave) group + * @v av Address vector + * @v method Method (IB_MGMT_METHOD_SET or IB_MGMT_METHOD_DELETE) + * @v mask Additional component mask * @v mad MAD to fill in */ -static void ib_mcast_mad ( struct ib_device *ibdev, union ib_gid *gid, - int join, union ib_mad *mad ) { +static void ib_mcast_mad ( struct ib_device *ibdev, + struct ib_address_vector *av, + unsigned int method, unsigned int mask, + union ib_mad *mad ) { struct ib_mad_sa *sa = &mad->sa; /* Construct multicast membership record request */ memset ( sa, 0, sizeof ( *sa ) ); sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; sa->mad_hdr.class_version = IB_SA_CLASS_VERSION; - sa->mad_hdr.method = - ( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE ); + sa->mad_hdr.method = method; sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC ); sa->sa_hdr.comp_mask[1] = htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | - IB_SA_MCMEMBER_REC_JOIN_STATE ); - sa->sa_data.mc_member_record.scope__join_state = 1; - memcpy ( &sa->sa_data.mc_member_record.mgid, gid, + IB_SA_MCMEMBER_REC_JOIN_STATE | mask ); + sa->sa_data.mc_member_record.qkey = htonl ( av->qkey ); + sa->sa_data.mc_member_record.pkey = + htons ( ibdev->pkey | IB_PKEY_FULL ); + sa->sa_data.mc_member_record.rate_selector__rate = av->rate; + sa->sa_data.mc_member_record.sl__flow_label__hop_limit = + htonl ( av->sl << 28 ); + sa->sa_data.mc_member_record.scope__join_state = 0x01; + memcpy ( &sa->sa_data.mc_member_record.mgid, &av->gid, sizeof ( sa->sa_data.mc_member_record.mgid ) ); memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid, sizeof ( sa->sa_data.mc_member_record.port_gid ) ); @@ -75,42 +83,45 @@ static void ib_mcast_mad ( struct ib_device *ibdev, union ib_gid *gid, * @v madx Management transaction * @v rc Status code * @v mad Received MAD (or NULL on error) - * @v av Source address vector (or NULL on error) + * @v src Source address vector (or NULL on error) */ static void ib_mcast_complete ( struct ib_device *ibdev, struct ib_mad_interface *mi __unused, struct ib_mad_transaction *madx, int rc, union ib_mad *mad, - struct ib_address_vector *av __unused ) { + struct ib_address_vector *src __unused ) { struct ib_mc_membership *membership = ib_madx_get_ownerdata ( madx ); struct ib_queue_pair *qp = membership->qp; - union ib_gid *gid = &membership->gid; + struct ib_address_vector *av = membership->av; struct ib_mc_member_record *mc_member_record = &mad->sa.sa_data.mc_member_record; int joined; - unsigned long qkey; /* Report failures */ if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -ENOTCONN; if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx join failed: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx join failed: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); goto out; } /* Extract values from MAD */ joined = ( mad->hdr.method == IB_MGMT_METHOD_GET_RESP ); - qkey = ntohl ( mc_member_record->qkey ); - DBGC ( ibdev, "IBDEV %p QPN %lx %s " IB_GID_FMT " qkey %lx\n", - ibdev, qp->qpn, ( joined ? "joined" : "left" ), - IB_GID_ARGS ( gid ), qkey ); + av->qkey = ntohl ( mc_member_record->qkey ); + av->lid = ntohs ( mc_member_record->mlid ); + av->rate = ( mc_member_record->rate_selector__rate & 0x3f ); + av->sl = ( ( ntohl ( mc_member_record->sl__flow_label__hop_limit ) + >> 28 ) & 0x0f ); + DBGC ( ibdev, "IBDEV %s QPN %#lx %s " IB_GID_FMT " qkey %#lx\n", + ibdev->name, qp->qpn, ( joined ? "joined" : "left" ), + IB_GID_ARGS ( &av->gid ), av->qkey ); /* Set queue key */ - qp->qkey = qkey; + qp->qkey = av->qkey; if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not modify qkey: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not modify qkey: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); goto out; } @@ -120,7 +131,7 @@ static void ib_mcast_complete ( struct ib_device *ibdev, membership->madx = NULL; /* Hand off to upper completion handler */ - membership->complete ( ibdev, qp, membership, rc, mad ); + membership->complete ( membership, rc ); } /** Multicast membership management transaction completion operations */ @@ -134,44 +145,45 @@ static struct ib_mad_transaction_operations ib_mcast_op = { * @v ibdev Infiniband device * @v qp Queue pair * @v membership Multicast group membership - * @v gid Multicast GID to join + * @v av Address vector to fill in * @v joined Join completion handler * @ret rc Return status code */ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_mc_membership *membership, union ib_gid *gid, - void ( * complete ) ( struct ib_device *ibdev, - struct ib_queue_pair *qp, - struct ib_mc_membership *membership, - int rc, union ib_mad *mad ) ) { + struct ib_mc_membership *membership, + struct ib_address_vector *av, unsigned int mask, + void ( * complete ) ( struct ib_mc_membership *membership, + int rc ) ) { union ib_mad mad; int rc; - DBGC ( ibdev, "IBDEV %p QPN %lx joining " IB_GID_FMT "\n", - ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx joining " IB_GID_FMT "\n", + ibdev->name, qp->qpn, IB_GID_ARGS ( &av->gid ) ); - /* Sanity check */ + /* Sanity checks */ assert ( qp != NULL ); + assert ( ! membership->attached ); /* Initialise structure */ membership->qp = qp; - memcpy ( &membership->gid, gid, sizeof ( membership->gid ) ); + membership->av = av; membership->complete = complete; /* Attach queue pair to multicast GID */ - if ( ( rc = ib_mcast_attach ( ibdev, qp, gid ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not attach: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); + if ( ( rc = ib_mcast_attach ( ibdev, qp, &av->gid ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %s QPN %#lx could not attach: %s\n", + ibdev->name, qp->qpn, strerror ( rc ) ); goto err_mcast_attach; } + membership->attached = 1; /* Initiate multicast membership join */ - ib_mcast_mad ( ibdev, gid, 1, &mad ); + ib_mcast_mad ( ibdev, av, IB_MGMT_METHOD_SET, mask, &mad ); membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, &ib_mcast_op ); if ( ! membership->madx ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not create join " - "transaction\n", ibdev, qp->qpn ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not create join " + "transaction\n", ibdev->name, qp->qpn ); rc = -ENOMEM; goto err_create_madx; } @@ -181,7 +193,8 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx ); err_create_madx: - ib_mcast_detach ( ibdev, qp, gid ); + ib_mcast_detach ( ibdev, qp, &av->gid ); + membership->attached = 0; err_mcast_attach: return rc; } @@ -195,18 +208,23 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, */ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_mc_membership *membership ) { - union ib_gid *gid = &membership->gid; + struct ib_address_vector *av = membership->av; union ib_mad mad; int rc; - DBGC ( ibdev, "IBDEV %p QPN %lx leaving " IB_GID_FMT "\n", - ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + /* Do nothing if we are already detached from the multicast GID */ + if ( ! membership->attached ) + return; + + DBGC ( ibdev, "IBDEV %s QPN %#lx leaving " IB_GID_FMT "\n", + ibdev->name, qp->qpn, IB_GID_ARGS ( &av->gid ) ); /* Sanity check */ assert ( qp != NULL ); /* Detach from multicast GID */ - ib_mcast_detach ( ibdev, qp, &membership->gid ); + ib_mcast_detach ( ibdev, qp, &av->gid ); + membership->attached = 0; /* Cancel multicast membership join, if applicable */ if ( membership->madx ) { @@ -215,9 +233,9 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, } /* Send a single group leave MAD */ - ib_mcast_mad ( ibdev, &membership->gid, 0, &mad ); + ib_mcast_mad ( ibdev, av, IB_MGMT_METHOD_DELETE, 0, &mad ); if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not send leave request: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s QPN %#lx could not send leave request: " + "%s\n", ibdev->name, qp->qpn, strerror ( rc ) ); } } diff --git a/src/net/infiniband/ib_mi.c b/src/net/infiniband/ib_mi.c index b43212974..781a3e2ea 100644 --- a/src/net/infiniband/ib_mi.c +++ b/src/net/infiniband/ib_mi.c @@ -106,7 +106,7 @@ static int ib_mi_handle ( struct ib_device *ibdev, /* Otherwise, ignore it */ DBGC ( mi, "MI %p RX TID %08x%08x ignored\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ) ); return -ENOTSUP; } @@ -152,7 +152,7 @@ static void ib_mi_complete_recv ( struct ib_device *ibdev, goto out; } DBGC ( mi, "MI %p RX TID %08x%08x (%02x,%02x,%02x,%04x) status " - "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), + "%04x\n", mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ), hdr->mgmt_class, hdr->class_version, hdr->method, ntohs ( hdr->attr_id ), ntohs ( hdr->status ) ); DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) ); @@ -192,12 +192,12 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, /* Set common fields */ hdr->base_version = IB_MGMT_BASE_VERSION; - if ( ( hdr->tid[0] == 0 ) && ( hdr->tid[1] == 0 ) ) { - hdr->tid[0] = htonl ( IB_MI_TID_MAGIC ); - hdr->tid[1] = htonl ( ++next_tid ); + if ( ( hdr->tid.high == 0 ) && ( hdr->tid.low == 0 ) ) { + hdr->tid.high = htonl ( IB_MI_TID_MAGIC ); + hdr->tid.low = htonl ( ++next_tid ); } DBGC ( mi, "MI %p TX TID %08x%08x (%02x,%02x,%02x,%04x) status " - "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), + "%04x\n", mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ), hdr->mgmt_class, hdr->class_version, hdr->method, ntohs ( hdr->attr_id ), ntohs ( hdr->status ) ); DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) ); @@ -217,8 +217,8 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, smp->return_path.hops[hop_pointer] = ibdev->port; } else { DBGC ( mi, "MI %p TX TID %08x%08x invalid hop pointer " - "%d\n", mi, ntohl ( hdr->tid[0] ), - ntohl ( hdr->tid[1] ), hop_pointer ); + "%d\n", mi, ntohl ( hdr->tid.high ), + ntohl ( hdr->tid.low ), hop_pointer ); return -EINVAL; } } @@ -228,7 +228,7 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, if ( ! iobuf ) { DBGC ( mi, "MI %p could not allocate buffer for TID " "%08x%08x\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ) ); return -ENOMEM; } memcpy ( iob_put ( iobuf, sizeof ( *mad ) ), mad, sizeof ( *mad ) ); @@ -236,7 +236,7 @@ int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, /* Send I/O buffer */ if ( ( rc = ib_post_send ( ibdev, mi->qp, av, iobuf ) ) != 0 ) { DBGC ( mi, "MI %p TX TID %08x%08x failed: %s\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ), strerror ( rc ) ); free_iob ( iobuf ); return rc; @@ -261,7 +261,7 @@ static void ib_mi_timer_expired ( struct retry_timer *timer, int expired ) { /* Abandon transaction if we have tried too many times */ if ( expired ) { DBGC ( mi, "MI %p abandoning TID %08x%08x\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); + mi, ntohl ( hdr->tid.high ), ntohl ( hdr->tid.low ) ); madx->op->complete ( ibdev, mi, madx, -ETIMEDOUT, NULL, NULL ); return; } @@ -341,38 +341,44 @@ void ib_destroy_madx ( struct ib_device *ibdev __unused, * * @v ibdev Infiniband device * @v type Queue pair type - * @ret mi Management agent, or NULL + * @v new_mi New management interface to fill in + * @ret rc Return status code */ -struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, - enum ib_queue_pair_type type ) { +int ib_create_mi ( struct ib_device *ibdev, enum ib_queue_pair_type type, + struct ib_mad_interface **new_mi ) { struct ib_mad_interface *mi; + const char *name; int rc; /* Allocate and initialise fields */ mi = zalloc ( sizeof ( *mi ) ); - if ( ! mi ) + if ( ! mi ) { + rc = -ENOMEM; goto err_alloc; + } mi->ibdev = ibdev; INIT_LIST_HEAD ( &mi->madx ); /* Create completion queue */ - mi->cq = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops ); - if ( ! mi->cq ) { - DBGC ( mi, "MI %p could not allocate completion queue\n", mi ); + if ( ( rc = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops, + &mi->cq ) ) != 0 ) { + DBGC ( mi, "MI %p could not create completion queue: %s\n", + mi, strerror ( rc ) ); goto err_create_cq; } /* Create queue pair */ - mi->qp = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq, - IB_MI_NUM_RECV_WQES, mi->cq, - &ib_mi_queue_pair_ops ); - if ( ! mi->qp ) { - DBGC ( mi, "MI %p could not allocate queue pair\n", mi ); + name = ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ); + if ( ( rc = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq, + IB_MI_NUM_RECV_WQES, mi->cq, + &ib_mi_queue_pair_ops, name, &mi->qp ) )!=0){ + DBGC ( mi, "MI %p could not create queue pair: %s\n", + mi, strerror ( rc ) ); goto err_create_qp; } ib_qp_set_ownerdata ( mi->qp, mi ); DBGC ( mi, "MI %p (%s) running on QPN %#lx\n", - mi, ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ), mi->qp->qpn ); + mi, mi->qp->name, mi->qp->qpn ); /* Set queue key */ mi->qp->qkey = ( ( type == IB_QPT_SMI ) ? IB_QKEY_SMI : IB_QKEY_GSI ); @@ -384,7 +390,8 @@ struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, /* Fill receive ring */ ib_refill_recv ( ibdev, mi->qp ); - return mi; + *new_mi = mi; + return 0; err_modify_qp: ib_destroy_qp ( ibdev, mi->qp ); @@ -393,7 +400,7 @@ struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, err_create_cq: free ( mi ); err_alloc: - return NULL; + return rc; } /** @@ -408,8 +415,8 @@ void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ) { /* Flush any outstanding requests */ list_for_each_entry_safe ( madx, tmp, &mi->madx, list ) { DBGC ( mi, "MI %p destroyed while TID %08x%08x in progress\n", - mi, ntohl ( madx->mad.hdr.tid[0] ), - ntohl ( madx->mad.hdr.tid[1] ) ); + mi, ntohl ( madx->mad.hdr.tid.high ), + ntohl ( madx->mad.hdr.tid.low ) ); madx->op->complete ( ibdev, mi, madx, -ECANCELED, NULL, NULL ); } diff --git a/src/net/infiniband/ib_packet.c b/src/net/infiniband/ib_packet.c index d3a22d309..8169925f1 100644 --- a/src/net/infiniband/ib_packet.c +++ b/src/net/infiniband/ib_packet.c @@ -63,8 +63,8 @@ int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf, unsigned int vl; unsigned int lnh; - DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n", - ibdev, ibdev->lid, qp->ext_qpn, dest->lid, dest->qpn, + DBGC2 ( ibdev, "IBDEV %s TX %04x:%08lx => %04x:%08lx (key %08lx)\n", + ibdev->name, ibdev->lid, qp->ext_qpn, dest->lid, dest->qpn, dest->qkey ); /* Calculate packet length */ @@ -152,8 +152,8 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, /* Extract LRH */ if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n", - ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for LRH\n", + ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } lrh = iobuf->data; @@ -166,16 +166,16 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, /* Reject unsupported packets */ if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) { - DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n", - ibdev, lnh ); + DBGC ( ibdev, "IBDEV %s RX unsupported LNH %x\n", + ibdev->name, lnh ); return -ENOTSUP; } /* Extract GRH, if present */ if ( lnh == IB_LNH_GRH ) { if ( iob_len ( iobuf ) < sizeof ( *grh ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) " - "for GRH\n", ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) " + "for GRH\n", ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } grh = iobuf->data; @@ -190,23 +190,23 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, /* Extract BTH */ if ( iob_len ( iobuf ) < sizeof ( *bth ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n", - ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for BTH\n", + ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } bth = iobuf->data; iob_pull ( iobuf, sizeof ( *bth ) ); if ( bth->opcode != BTH_OPCODE_UD_SEND ) { - DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n", - ibdev, bth->opcode ); + DBGC ( ibdev, "IBDEV %s unsupported BTH opcode %x\n", + ibdev->name, bth->opcode ); return -ENOTSUP; } dest->qpn = ntohl ( bth->dest_qp ); /* Extract DETH */ if ( iob_len ( iobuf ) < sizeof ( *deth ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n", - ibdev, iob_len ( iobuf ) ); + DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for DETH\n", + ibdev->name, iob_len ( iobuf ) ); return -EINVAL; } deth = iobuf->data; @@ -226,24 +226,25 @@ int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, if ( qp ) { if ( IB_LID_MULTICAST ( dest->lid ) && grh ) { if ( ! ( *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ))){ - DBGC ( ibdev, "IBDEV %p RX for unknown MGID " - IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( &grh->dgid ) ); + DBGC ( ibdev, "IBDEV %s RX for unknown MGID " + IB_GID_FMT "\n", ibdev->name, + IB_GID_ARGS ( &grh->dgid ) ); return -ENODEV; } } else { if ( ! ( *qp = ib_find_qp_qpn ( ibdev, dest->qpn ) ) ) { - DBGC ( ibdev, "IBDEV %p RX for nonexistent " - "QPN %lx\n", ibdev, dest->qpn ); + DBGC ( ibdev, "IBDEV %s RX for nonexistent " + "QPN %#lx\n", ibdev->name, dest->qpn ); return -ENODEV; } } assert ( *qp ); } - DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n", - ibdev, dest->lid, ( IB_LID_MULTICAST ( dest->lid ) ? - ( qp ? (*qp)->ext_qpn : -1UL ) : dest->qpn ), + DBGC2 ( ibdev, "IBDEV %s RX %04x:%08lx <= %04x:%08lx (key %08x)\n", + ibdev->name, dest->lid, + ( IB_LID_MULTICAST ( dest->lid ) ? + ( qp ? (*qp)->ext_qpn : -1UL ) : dest->qpn ), source->lid, source->qpn, ntohl ( deth->qkey ) ); DBGCP_HDA ( ibdev, 0, ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ), diff --git a/src/net/infiniband/ib_pathrec.c b/src/net/infiniband/ib_pathrec.c index f9cbab87f..f846710f2 100644 --- a/src/net/infiniband/ib_pathrec.c +++ b/src/net/infiniband/ib_pathrec.c @@ -61,9 +61,9 @@ static void ib_path_complete ( struct ib_device *ibdev, if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) rc = -ENETUNREACH; if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %p path lookup for " IB_GID_FMT + DBGC ( ibdev, "IBDEV %s path lookup for " IB_GID_FMT " failed: %s\n", - ibdev, IB_GID_ARGS ( dgid ), strerror ( rc ) ); + ibdev->name, IB_GID_ARGS ( dgid ), strerror ( rc ) ); goto out; } @@ -71,9 +71,15 @@ static void ib_path_complete ( struct ib_device *ibdev, path->av.lid = ntohs ( pathrec->dlid ); path->av.sl = ( pathrec->reserved__sl & 0x0f ); path->av.rate = ( pathrec->rate_selector__rate & 0x3f ); - DBGC ( ibdev, "IBDEV %p path to " IB_GID_FMT " is %04x sl %d rate " - "%d\n", ibdev, IB_GID_ARGS ( dgid ), path->av.lid, path->av.sl, - path->av.rate ); + DBGC ( ibdev, "IBDEV %s path to " IB_GID_FMT " lid %d sl %d rate " + "%d\n", ibdev->name, IB_GID_ARGS ( dgid ), path->av.lid, + path->av.sl, path->av.rate ); + + /* Use only the LID if no GRH is needed for this path */ + if ( memcmp ( &path->av.gid.s.prefix, &ibdev->gid.s.prefix, + sizeof ( path->av.gid.s.prefix ) ) == 0 ) { + path->av.gid_present = 0; + } out: /* Destroy the completed transaction */ @@ -245,13 +251,6 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { struct ib_cached_path *cached; unsigned int cache_idx; - /* Sanity check */ - if ( ! av->gid_present ) { - DBGC ( ibdev, "IBDEV %p attempt to look up path without GID\n", - ibdev ); - return -EINVAL; - } - /* Look in cache for a matching entry */ cached = ib_find_path_cache_entry ( ibdev, gid ); if ( cached && cached->path->av.lid ) { @@ -259,11 +258,12 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { av->lid = cached->path->av.lid; av->rate = cached->path->av.rate; av->sl = cached->path->av.sl; - DBGC2 ( ibdev, "IBDEV %p cache hit for " IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( gid ) ); + av->gid_present = cached->path->av.gid_present; + DBGC2 ( ibdev, "IBDEV %s cache hit for " IB_GID_FMT "\n", + ibdev->name, IB_GID_ARGS ( gid ) ); return 0; } - DBGC ( ibdev, "IBDEV %p cache miss for " IB_GID_FMT "%s\n", ibdev, + DBGC ( ibdev, "IBDEV %s cache miss for " IB_GID_FMT "%s\n", ibdev->name, IB_GID_ARGS ( gid ), ( cached ? " (in progress)" : "" ) ); /* If lookup is already in progress, do nothing */ @@ -282,8 +282,8 @@ int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { /* Create new path */ cached->path = ib_create_path ( ibdev, av, &ib_cached_path_op ); if ( ! cached->path ) { - DBGC ( ibdev, "IBDEV %p could not create path\n", - ibdev ); + DBGC ( ibdev, "IBDEV %s could not create path\n", + ibdev->name ); return -ENOMEM; } ib_path_set_ownerdata ( cached->path, cached ); diff --git a/src/net/infiniband/ib_service.c b/src/net/infiniband/ib_service.c new file mode 100644 index 000000000..f035382ee --- /dev/null +++ b/src/net/infiniband/ib_service.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband service records + * + */ + +/** + * Create service record management transaction + * + * @v ibdev Infiniband device + * @v mi Management interface + * @v name Service name + * @v op Management transaction operations + * @ret madx Management transaction, or NULL on error + */ +struct ib_mad_transaction * +ib_create_service_madx ( struct ib_device *ibdev, + struct ib_mad_interface *mi, const char *name, + struct ib_mad_transaction_operations *op ) { + union ib_mad mad; + struct ib_mad_sa *sa = &mad.sa; + struct ib_service_record *svc = &sa->sa_data.service_record; + + /* Construct service record request */ + memset ( sa, 0, sizeof ( *sa ) ); + sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; + sa->mad_hdr.class_version = IB_SA_CLASS_VERSION; + sa->mad_hdr.method = IB_MGMT_METHOD_GET; + sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_SERVICE_REC ); + sa->sa_hdr.comp_mask[1] = htonl ( IB_SA_SERVICE_REC_NAME ); + snprintf ( svc->name, sizeof ( svc->name ), "%s", name ); + + /* Create management transaction */ + return ib_create_madx ( ibdev, mi, &mad, NULL, op ); +} diff --git a/src/net/infiniband/ib_sma.c b/src/net/infiniband/ib_sma.c index a05d7c924..24ec9f4e0 100644 --- a/src/net/infiniband/ib_sma.c +++ b/src/net/infiniband/ib_sma.c @@ -176,9 +176,9 @@ static int ib_sma_set_port_info ( struct ib_device *ibdev, ( port_info->link_speed_active__link_speed_enabled & 0xf ) ) ) ibdev->link_speed_enabled = link_speed_enabled; ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf ); - DBGC ( mi, "SMA %p set LID %04x SMLID %04x link width %02x speed " - "%02x\n", mi, ibdev->lid, ibdev->sm_lid, - ibdev->link_width_enabled, ibdev->link_speed_enabled ); + DBGC ( mi, "SMA %p set LID %d SMLID %d link width %d speed %d\n", + mi, ibdev->lid, ibdev->sm_lid, ibdev->link_width_enabled, + ibdev->link_speed_enabled ); /* Update parameters on device */ if ( ( rc = ib_set_port_info ( ibdev, mad ) ) != 0 ) { @@ -358,7 +358,7 @@ struct ib_mad_agent ib_sma_agent[] __ib_mad_agent = { int ib_create_sma ( struct ib_device *ibdev, struct ib_mad_interface *mi ) { /* Nothing to do */ - DBGC ( ibdev, "IBDEV %p SMA using SMI %p\n", ibdev, mi ); + DBGC ( ibdev, "IBDEV %s SMA using SMI %p\n", ibdev->name, mi ); return 0; } diff --git a/src/net/infiniband/ib_smc.c b/src/net/infiniband/ib_smc.c index c1741b26c..5de7deba2 100644 --- a/src/net/infiniband/ib_smc.c +++ b/src/net/infiniband/ib_smc.c @@ -86,8 +86,8 @@ static int ib_smc_get_node_info ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_NODE_INFO ), 0, local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get node info: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get node info: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -109,8 +109,8 @@ static int ib_smc_get_port_info ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_PORT_INFO ), htonl ( ibdev->port ), local_mad, mad )) !=0){ - DBGC ( ibdev, "IBDEV %p could not get port info: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get port info: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -132,8 +132,8 @@ static int ib_smc_get_guid_info ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_GUID_INFO ), 0, local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get GUID info: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -155,8 +155,8 @@ static int ib_smc_get_pkey_table ( struct ib_device *ibdev, /* Issue MAD */ if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_PKEY_TABLE ), 0, local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n", - ibdev, strerror ( rc ) ); + DBGC ( ibdev, "IBDEV %s could not get pkey table: %s\n", + ibdev->name, strerror ( rc ) ); return rc; } return 0; @@ -216,8 +216,8 @@ static int ib_smc_get ( struct ib_device *ibdev, ib_local_mad_t local_mad ) { return rc; ibdev->pkey = ntohs ( pkey_table->pkey[0] ); - DBGC ( ibdev, "IBDEV %p port GID is " IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( &ibdev->gid ) ); + DBGC ( ibdev, "IBDEV %s port GID is " IB_GID_FMT "\n", + ibdev->name, IB_GID_ARGS ( &ibdev->gid ) ); return 0; } diff --git a/src/net/infiniband/ib_srp.c b/src/net/infiniband/ib_srp.c index 3700184c0..cf1ef3bfd 100644 --- a/src/net/infiniband/ib_srp.c +++ b/src/net/infiniband/ib_srp.c @@ -60,6 +60,8 @@ FILE_LICENCE ( BSD2 ); #define EINFO_EINVAL_RP_TOO_SHORT __einfo_uniqify \ ( EINFO_EINVAL, 0x04, "Root path too short" ) +struct acpi_model ib_sbft_model __acpi_model; + /****************************************************************************** * * IB SRP devices @@ -67,6 +69,20 @@ FILE_LICENCE ( BSD2 ); ****************************************************************************** */ +/** + * An IB SRP sBFT created by iPXE + */ +struct ipxe_ib_sbft { + /** The table header */ + struct sbft_table table; + /** The SCSI subtable */ + struct sbft_scsi_subtable scsi; + /** The SRP subtable */ + struct sbft_srp_subtable srp; + /** The Infiniband subtable */ + struct sbft_ib_subtable ib; +}; + /** An Infiniband SRP device */ struct ib_srp_device { /** Reference count */ @@ -80,10 +96,10 @@ struct ib_srp_device { /** Infiniband device */ struct ib_device *ibdev; - /** Destination GID (for boot firmware table) */ - union ib_gid dgid; - /** Service ID (for boot firmware table) */ - union ib_guid service_id; + /** ACPI descriptor */ + struct acpi_descriptor desc; + /** Boot firmware table parameters */ + struct ipxe_ib_sbft sbft; }; /** @@ -113,43 +129,15 @@ static void ib_srp_close ( struct ib_srp_device *ib_srp, int rc ) { } /** - * Describe IB SRP device in an ACPI table + * Get IB SRP ACPI descriptor * - * @v srpdev SRP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code + * @v ib_srp IB SRP device + * @ret desc ACPI descriptor */ -static int ib_srp_describe ( struct ib_srp_device *ib_srp, - struct acpi_description_header *acpi, - size_t len ) { - struct ib_device *ibdev = ib_srp->ibdev; - struct sbft_table *sbft = - container_of ( acpi, struct sbft_table, acpi ); - struct sbft_ib_subtable *ib_sbft; - size_t used; +static struct acpi_descriptor * +ib_srp_describe ( struct ib_srp_device *ib_srp ) { - /* Sanity check */ - if ( acpi->signature != SBFT_SIG ) - return -EINVAL; - - /* Append IB subtable to existing table */ - used = le32_to_cpu ( sbft->acpi.length ); - sbft->ib_offset = cpu_to_le16 ( used ); - ib_sbft = ( ( ( void * ) sbft ) + used ); - used += sizeof ( *ib_sbft ); - if ( used > len ) - return -ENOBUFS; - sbft->acpi.length = cpu_to_le32 ( used ); - - /* Populate subtable */ - memcpy ( &ib_sbft->sgid, &ibdev->gid, sizeof ( ib_sbft->sgid ) ); - memcpy ( &ib_sbft->dgid, &ib_srp->dgid, sizeof ( ib_sbft->dgid ) ); - memcpy ( &ib_sbft->service_id, &ib_srp->service_id, - sizeof ( ib_sbft->service_id ) ); - ib_sbft->pkey = cpu_to_le16 ( ibdev->pkey ); - - return 0; + return &ib_srp->desc; } /** IB SRP CMRC interface operations */ @@ -188,6 +176,7 @@ static int ib_srp_open ( struct interface *block, struct ib_device *ibdev, union srp_port_id *initiator, union srp_port_id *target, struct scsi_lun *lun ) { struct ib_srp_device *ib_srp; + struct ipxe_ib_sbft *sbft; int rc; /* Allocate and initialise structure */ @@ -200,17 +189,23 @@ static int ib_srp_open ( struct interface *block, struct ib_device *ibdev, intf_init ( &ib_srp->srp, &ib_srp_srp_desc, &ib_srp->refcnt ); intf_init ( &ib_srp->cmrc, &ib_srp_cmrc_desc, &ib_srp->refcnt ); ib_srp->ibdev = ibdev_get ( ibdev ); + acpi_init ( &ib_srp->desc, &ib_sbft_model, &ib_srp->refcnt ); DBGC ( ib_srp, "IBSRP %p for " IB_GID_FMT " " IB_GUID_FMT "\n", ib_srp, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); /* Preserve parameters required for boot firmware table */ - memcpy ( &ib_srp->dgid, dgid, sizeof ( ib_srp->dgid ) ); - memcpy ( &ib_srp->service_id, service_id, - sizeof ( ib_srp->service_id ) ); + sbft = &ib_srp->sbft; + memcpy ( &sbft->scsi.lun, lun, sizeof ( sbft->scsi.lun ) ); + memcpy ( &sbft->srp.initiator, initiator, + sizeof ( sbft->srp.initiator ) ); + memcpy ( &sbft->srp.target, target, sizeof ( sbft->srp.target ) ); + memcpy ( &sbft->ib.dgid, dgid, sizeof ( sbft->ib.dgid ) ); + memcpy ( &sbft->ib.service_id, service_id, + sizeof ( sbft->ib.service_id ) ); /* Open CMRC socket */ if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid, - service_id ) ) != 0 ) { + service_id, "SRP" ) ) != 0 ) { DBGC ( ib_srp, "IBSRP %p could not open CMRC socket: %s\n", ib_srp, strerror ( rc ) ); goto err_cmrc_open; @@ -579,3 +574,67 @@ struct uri_opener ib_srp_uri_opener __uri_opener = { .scheme = "ib_srp", .open = ib_srp_open_uri, }; + +/****************************************************************************** + * + * IB SRP boot firmware table (sBFT) + * + ****************************************************************************** + */ + +/** + * Check if IB SRP boot firmware table descriptor is complete + * + * @v desc ACPI descriptor + * @ret rc Return status code + */ +static int ib_sbft_complete ( struct acpi_descriptor *desc __unused ) { + return 0; +} + +/** + * Install IB SRP boot firmware table(s) + * + * @v install Installation method + * @ret rc Return status code + */ +static int ib_sbft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { + struct ib_srp_device *ib_srp; + struct ipxe_ib_sbft *sbft; + struct ib_device *ibdev; + int rc; + + list_for_each_entry ( ib_srp, &ib_sbft_model.descs, desc.list ) { + + /* Complete table */ + sbft = &ib_srp->sbft; + ibdev = ib_srp->ibdev; + 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 ) ); + sbft->table.srp_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) ); + sbft->table.ib_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), ib ) ); + memcpy ( &sbft->ib.sgid, &ibdev->gid, sizeof ( sbft->ib.sgid )); + sbft->ib.pkey = cpu_to_le16 ( ibdev->pkey ); + + /* Install table */ + if ( ( rc = install ( &sbft->table.acpi ) ) != 0 ) { + DBGC ( ib_srp, "IBSRP %p could not install sBFT: %s\n", + ib_srp, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** IB sBFT model */ +struct acpi_model ib_sbft_model __acpi_model = { + .descs = LIST_HEAD_INIT ( ib_sbft_model.descs ), + .complete = ib_sbft_complete, + .install = ib_sbft_install, +}; diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c new file mode 100644 index 000000000..0ee753c38 --- /dev/null +++ b/src/net/infiniband/xsigo.c @@ -0,0 +1,1859 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Xsigo virtual Ethernet devices + * + */ + +/** A Xsigo device */ +struct xsigo_device { + /** Reference count */ + struct refcnt refcnt; + /** Underlying Infiniband device */ + struct ib_device *ibdev; + /** List of Xsigo devices */ + struct list_head list; + /** Device name */ + const char *name; + + /** Link opener timer */ + struct retry_timer opener; + + /** Discovery timer */ + struct retry_timer discovery; + /** Discovery management transaction (if any) */ + struct ib_mad_transaction *madx; + + /** List of configuration managers */ + struct list_head managers; +}; + +/** A Xsigo configuration manager */ +struct xsigo_manager { + /** Reference count */ + struct refcnt refcnt; + /** Xsigo device */ + struct xsigo_device *xdev; + /** List of managers */ + struct list_head list; + /** Device name */ + char name[16]; + /** Manager ID */ + struct xsigo_manager_id id; + + /** Data transfer interface */ + struct interface xfer; + /** Connection timer */ + struct retry_timer reopen; + /** Keepalive timer */ + struct retry_timer keepalive; + /** Transmission process */ + struct process process; + /** Pending transmissions */ + unsigned int pending; + /** Transmit sequence number */ + uint32_t seq; + + /** List of virtual Ethernet devices */ + struct list_head nics; +}; + +/** Configuration manager pending transmissions */ +enum xsigo_manager_pending { + /** Send connection request */ + XCM_TX_CONNECT = 0x0001, + /** Send registration message */ + XCM_TX_REGISTER = 0x0002, +}; + +/** A Xsigo virtual Ethernet device */ +struct xsigo_nic { + /** Configuration manager */ + struct xsigo_manager *xcm; + /** List of virtual Ethernet devices */ + struct list_head list; + /** Device name */ + char name[16]; + + /** Resource identifier */ + union ib_guid resource; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Network ID */ + unsigned long network; +}; + +/** Configuration manager service ID */ +static union ib_guid xcm_service_id = { + .bytes = XCM_SERVICE_ID, +}; + +/** List of all Xsigo devices */ +static LIST_HEAD ( xsigo_devices ); + +/** + * Free Xsigo device + * + * @v refcnt Reference count + */ +static void xsigo_free ( struct refcnt *refcnt ) { + struct xsigo_device *xdev = + container_of ( refcnt, struct xsigo_device, refcnt ); + + /* Sanity checks */ + assert ( ! timer_running ( &xdev->opener ) ); + assert ( ! timer_running ( &xdev->discovery ) ); + assert ( xdev->madx == NULL ); + assert ( list_empty ( &xdev->managers ) ); + + /* Drop reference to Infiniband device */ + ibdev_put ( xdev->ibdev ); + + /* Free device */ + free ( xdev ); +} + +/** + * Free configuration manager + * + * @v refcnt Reference count + */ +static void xcm_free ( struct refcnt *refcnt ) { + struct xsigo_manager *xcm = + container_of ( refcnt, struct xsigo_manager, refcnt ); + + /* Sanity checks */ + assert ( ! timer_running ( &xcm->reopen ) ); + assert ( ! timer_running ( &xcm->keepalive ) ); + assert ( ! process_running ( &xcm->process ) ); + assert ( list_empty ( &xcm->nics ) ); + + /* Drop reference to Xsigo device */ + ref_put ( &xcm->xdev->refcnt ); + + /* Free manager */ + free ( xcm ); +} + +/**************************************************************************** + * + * Virtual Ethernet (XVE) devices + * + **************************************************************************** + */ + +/** + * Create virtual Ethernet device + * + * @v xcm Configuration manager + * @v resource Resource identifier + * @v mac Ethernet MAC + * @v network Network identifier + * @v name Device name + * @ret rc Return status code + */ +static int xve_create ( struct xsigo_manager *xcm, union ib_guid *resource, + const uint8_t *mac, unsigned long network, + unsigned long qkey, const char *name ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct xsigo_nic *xve; + struct ib_address_vector broadcast; + int rc; + + /* Allocate and initialise structure */ + xve = zalloc ( sizeof ( *xve ) ); + if ( ! xve ) { + rc = -ENOMEM; + goto err_alloc; + } + xve->xcm = xcm; + snprintf ( xve->name, sizeof ( xve->name ), "%s", name ); + memcpy ( &xve->resource, resource, sizeof ( xve->resource ) ); + memcpy ( xve->mac, mac, ETH_ALEN ); + xve->network = network; + DBGC ( xve, "XVE %s created for %s " IB_GUID_FMT "\n", + xve->name, xcm->name, IB_GUID_ARGS ( resource ) ); + DBGC ( xve, "XVE %s is MAC %s on network %ld\n", + xve->name, eth_ntoa ( mac ), network ); + + /* Construct broadcast address vector */ + memset ( &broadcast, 0, sizeof ( broadcast ) ); + broadcast.qpn = IB_QPN_BROADCAST; + broadcast.qkey = qkey; + broadcast.gid_present = 1; + broadcast.gid.dwords[0] = htonl ( XVE_PREFIX ); + broadcast.gid.words[2] = htons ( ibdev->pkey ); + broadcast.gid.dwords[3] = htonl ( network ); + + /* Create EoIB device */ + if ( ( rc = eoib_create ( ibdev, xve->mac, &broadcast, + xve->name ) ) != 0 ) { + DBGC ( xve, "XVE %s could not create EoIB device: %s\n", + xve->name, strerror ( rc ) ); + goto err_create; + } + + /* Add to list of virtual Ethernet devices. Do this only + * after creating the EoIB device, so that our net device + * notifier won't attempt to send an operational state update + * before we have acknowledged the installation. + */ + list_add ( &xve->list, &xcm->nics ); + + return 0; + + list_del ( &xve->list ); + err_create: + free ( xve ); + err_alloc: + return rc; +} + +/** + * Find virtual Ethernet device + * + * @v xcm Configuration manager + * @v resource Resource identifier + * @ret xve Virtual Ethernet device, or NULL + */ +static struct xsigo_nic * xve_find ( struct xsigo_manager *xcm, + union ib_guid *resource ) { + struct xsigo_nic *xve; + + list_for_each_entry ( xve, &xcm->nics, list ) { + if ( memcmp ( resource, &xve->resource, + sizeof ( *resource ) ) == 0 ) + return xve; + } + return NULL; +} + +/** + * Destroy virtual Ethernet device + * + * @v xve Virtual Ethernet device + */ +static void xve_destroy ( struct xsigo_nic *xve ) { + struct xsigo_manager *xcm = xve->xcm; + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct eoib_device *eoib; + + /* Destroy corresponding EoIB device, if any */ + if ( ( eoib = eoib_find ( ibdev, xve->mac ) ) ) + eoib_destroy ( eoib ); + + /* Remove from list of virtual Ethernet devices */ + list_del ( &xve->list ); + + /* Free virtual Ethernet device */ + DBGC ( xve, "XVE %s destroyed\n", xve->name ); + free ( xve ); +} + +/** + * Update virtual Ethernet device MTU + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v mtu New MTU (excluding Ethernet and EoIB headers) + * @ret rc Return status code + */ +static int xve_update_mtu ( struct xsigo_nic *xve, struct eoib_device *eoib, + size_t mtu ) { + struct net_device *netdev = eoib->netdev; + size_t max; + + /* Check that we can support this MTU */ + max = ( IB_MAX_PAYLOAD_SIZE - ( sizeof ( struct ethhdr ) + + sizeof ( struct eoib_header ) ) ); + if ( mtu > max ) { + DBGC ( xve, "XVE %s cannot support MTU %zd (max %zd)\n", + xve->name, mtu, max ); + return -ERANGE; + } + + /* Update MTU. No need to close/reopen the network device, + * since our Infiniband stack uses a fixed MTU anyway. Note + * that the network device sees the Ethernet frame header but + * not the EoIB header. + */ + netdev->max_pkt_len = ( mtu + sizeof ( struct ethhdr ) ); + netdev->mtu = mtu; + DBGC ( xve, "XVE %s has MTU %zd\n", xve->name, mtu ); + + return 0; +} + +/** + * Open virtual Ethernet device + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v open New administrative state + * @ret rc Return status code + */ +static int xve_open ( struct xsigo_nic *xve, struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + int rc; + + /* Do nothing if network device is already open */ + if ( netdev_is_open ( netdev ) ) + return 0; + DBGC ( xve, "XVE %s opening network device\n", xve->name ); + + /* Open network device */ + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + DBGC ( xve, "XVE %s could not open: %s\n", + xve->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Close virtual Ethernet device + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + */ +static void xve_close ( struct xsigo_nic *xve, struct eoib_device *eoib ) { + struct net_device *netdev = eoib->netdev; + + /* Do nothing if network device is already closed */ + if ( ! netdev_is_open ( netdev ) ) + return; + + /* Close network device */ + netdev_close ( netdev ); + DBGC ( xve, "XVE %s closed network device\n", xve->name ); +} + +/** + * Update virtual Ethernet device administrative state + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v open New administrative state + * @ret rc Return status code + */ +static int xve_update_state ( struct xsigo_nic *xve, struct eoib_device *eoib, + int open ) { + + /* Open or close device, as applicable */ + if ( open ) { + return xve_open ( xve, eoib ); + } else { + xve_close ( xve, eoib ); + return 0; + } +} + +/** + * Update gateway (TCA) + * + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @v av Address vector, or NULL if no gateway + * @ret rc Return status code + */ +static int xve_update_tca ( struct xsigo_nic *xve, struct eoib_device *eoib, + struct ib_address_vector *av ) { + + /* Update gateway address */ + eoib_set_gateway ( eoib, av ); + if ( av ) { + DBGC ( xve, "XVE %s has TCA " IB_GID_FMT " data %#lx qkey " + "%#lx\n", xve->name, IB_GID_ARGS ( &av->gid ), av->qpn, + av->qkey ); + } else { + DBGC ( xve, "XVE %s has no TCA\n", xve->name ); + } + + /* The Linux driver will modify the local device's link state + * to reflect the EoIB-to-Ethernet gateway's link state, but + * this seems philosophically incorrect since communication + * within the EoIB broadcast domain still works regardless of + * the state of the gateway. + */ + + return 0; +} + +/**************************************************************************** + * + * Server management protocol (XSMP) session messages + * + **************************************************************************** + */ + +/** + * Get session message name (for debugging) + * + * @v type Message type + * @ret name Message name + */ +static const char * xsmp_session_type ( unsigned int type ) { + static char buf[16]; + + switch ( type ) { + case XSMP_SESSION_TYPE_HELLO: return "HELLO"; + case XSMP_SESSION_TYPE_REGISTER: return "REGISTER"; + case XSMP_SESSION_TYPE_CONFIRM: return "CONFIRM"; + case XSMP_SESSION_TYPE_REJECT: return "REJECT"; + case XSMP_SESSION_TYPE_SHUTDOWN: return "SHUTDOWN"; + default: + snprintf ( buf, sizeof ( buf ), "UNKNOWN<%d>", type ); + return buf; + } +} + +/** + * Extract chassis name (for debugging) + * + * @v msg Session message + * @ret chassis Chassis name + */ +static const char * xsmp_chassis_name ( struct xsmp_session_message *msg ) { + static char chassis[ sizeof ( msg->chassis ) + 1 /* NUL */ ]; + + memcpy ( chassis, msg->chassis, sizeof ( msg->chassis ) ); + return chassis; +} + +/** + * Extract session name (for debugging) + * + * @v msg Session message + * @ret session Session name + */ +static const char * xsmp_session_name ( struct xsmp_session_message *msg ) { + static char session[ sizeof ( msg->session ) + 1 /* NUL */ ]; + + memcpy ( session, msg->session, sizeof ( msg->session ) ); + return session; +} + +/** + * Send session message + * + * @v xcm Configuration manager + * @v type Message type + * @ret rc Return status code + */ +static int xsmp_tx_session ( struct xsigo_manager *xcm, unsigned int type ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct xsmp_session_message msg; + int rc; + + /* Construct session message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr.type = XSMP_TYPE_SESSION; + msg.hdr.len = htons ( sizeof ( msg ) ); + msg.hdr.seq = htonl ( ++xcm->seq ); + memcpy ( &msg.hdr.src.guid, &ibdev->gid.s.guid, + sizeof ( msg.hdr.src.guid ) ); + memcpy ( &msg.hdr.dst.guid, &xcm->id.guid, + sizeof ( msg.hdr.dst.guid ) ); + msg.type = type; + msg.len = htons ( sizeof ( msg ) - sizeof ( msg.hdr ) ); + msg.os_type = XSIGO_OS_TYPE_GENERIC; + msg.resources = htons ( XSIGO_RESOURCE_XVE | + XSIGO_RESOURCE_NO_HA ); + msg.boot = htonl ( XSMP_BOOT_PXE ); + DBGCP ( xcm, "XCM %s TX[%d] session %s\n", xcm->name, + ntohl ( msg.hdr.seq ), xsmp_session_type ( msg.type ) ); + DBGCP_HDA ( xcm, 0, &msg, sizeof ( msg ) ); + + /* Send session message */ + if ( ( rc = xfer_deliver_raw ( &xcm->xfer, &msg, + sizeof ( msg ) ) ) != 0 ) { + DBGC ( xcm, "XCM %s TX session %s failed: %s\n", xcm->name, + xsmp_session_type ( msg.type ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Send registration message + * + * @v xcm Configuration manager + * @ret rc Return status code + */ +static inline int xsmp_tx_session_register ( struct xsigo_manager *xcm ) { + + DBGC ( xcm, "XCM %s registering with " IB_GUID_FMT "\n", + xcm->name, IB_GUID_ARGS ( &xcm->id.guid ) ); + + /* Send registration message */ + return xsmp_tx_session ( xcm, XSMP_SESSION_TYPE_REGISTER ); +} + +/** + * Send keepalive message + * + * @v xcm Configuration manager + * @ret rc Return status code + */ +static int xsmp_tx_session_hello ( struct xsigo_manager *xcm ) { + + /* Send keepalive message */ + return xsmp_tx_session ( xcm, XSMP_SESSION_TYPE_HELLO ); +} + +/** + * Handle received keepalive message + * + * @v xcm Configuration manager + * @v msg Keepalive message + * @ret rc Return status code + */ +static int xsmp_rx_session_hello ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg __unused ) { + + /* Respond to keepalive message. Note that the XCM doesn't + * seem to actually ever send these. + */ + return xsmp_tx_session_hello ( xcm ); +} + +/** + * Handle received registration confirmation message + * + * @v xcm Configuration manager + * @v msg Registration confirmation message + * @ret rc Return status code + */ +static int xsmp_rx_session_confirm ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGC ( xcm, "XCM %s registered with \"%s\" as \"%s\"\n", xcm->name, + xsmp_chassis_name ( msg ), xsmp_session_name ( msg ) ); + + return 0; +} + +/** + * Handle received registration rejection message + * + * @v xcm Configuration manager + * @v msg Registration confirmation message + * @ret rc Return status code + */ +static int xsmp_rx_session_reject ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGC ( xcm, "XCM %s rejected by \"%s\":\n", + xcm->name, xsmp_chassis_name ( msg ) ); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + return -EPERM; +} + +/** + * Handle received shutdown message + * + * @v xcm Configuration manager + * @v msg Registration confirmation message + * @ret rc Return status code + */ +static int xsmp_rx_session_shutdown ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGC ( xcm, "XCM %s shut down by \"%s\":\n", + xcm->name, xsmp_chassis_name ( msg ) ); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + return -ENOTCONN; +} + +/** + * Handle received session message + * + * @v xcm Configuration manager + * @v msg Session message + * @ret rc Return status code + */ +static int xsmp_rx_session ( struct xsigo_manager *xcm, + struct xsmp_session_message *msg ) { + + DBGCP ( xcm, "XCM %s RX[%d] session %s\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_session_type ( msg->type ) ); + DBGCP_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + /* Handle message according to type */ + switch ( msg->type ) { + case XSMP_SESSION_TYPE_HELLO: + return xsmp_rx_session_hello ( xcm, msg ); + case XSMP_SESSION_TYPE_CONFIRM: + return xsmp_rx_session_confirm ( xcm, msg ); + case XSMP_SESSION_TYPE_REJECT: + return xsmp_rx_session_reject ( xcm, msg ); + case XSMP_SESSION_TYPE_SHUTDOWN: + return xsmp_rx_session_shutdown ( xcm, msg ); + default: + DBGC ( xcm, "XCM %s RX[%d] session unexpected %s:\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_session_type ( msg->type )); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + return -EPROTO; + } +} + +/**************************************************************************** + * + * Server management protocol (XSMP) virtual Ethernet (XVE) messages + * + **************************************************************************** + */ + +/** + * Get virtual Ethernet message name (for debugging) + * + * @v type Message type + * @ret name Message name + */ +static const char * xsmp_xve_type ( unsigned int type ) { + static char buf[16]; + + switch ( type ) { + case XSMP_XVE_TYPE_INSTALL: return "INSTALL"; + case XSMP_XVE_TYPE_DELETE: return "DELETE"; + case XSMP_XVE_TYPE_UPDATE: return "UPDATE"; + case XSMP_XVE_TYPE_OPER_UP: return "OPER_UP"; + case XSMP_XVE_TYPE_OPER_DOWN: return "OPER_DOWN"; + case XSMP_XVE_TYPE_OPER_REQ: return "OPER_REQ"; + case XSMP_XVE_TYPE_READY: return "READY"; + default: + snprintf ( buf, sizeof ( buf ), "UNKNOWN<%d>", type ); + return buf; + } +} + +/** + * Send virtual Ethernet message + * + * @v xcm Configuration manager + * @v msg Partial message + * @ret rc Return status code + */ +static int xsmp_tx_xve ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + int rc; + + /* Fill in common header fields */ + msg->hdr.type = XSMP_TYPE_XVE; + msg->hdr.len = htons ( sizeof ( *msg ) ); + msg->hdr.seq = htonl ( ++xcm->seq ); + memcpy ( &msg->hdr.src.guid, &ibdev->gid.s.guid, + sizeof ( msg->hdr.src.guid ) ); + memcpy ( &msg->hdr.dst.guid, &xcm->id.guid, + sizeof ( msg->hdr.dst.guid ) ); + msg->len = htons ( sizeof ( *msg ) - sizeof ( msg->hdr ) ); + DBGCP ( xcm, "XCM %s TX[%d] xve %s code %#02x\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_xve_type ( msg->type ), + msg->code ); + DBGCP_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + /* Send virtual Ethernet message */ + if ( ( rc = xfer_deliver_raw ( &xcm->xfer, msg, + sizeof ( *msg ) ) ) != 0 ) { + DBGC ( xcm, "XCM %s TX xve %s failed: %s\n", xcm->name, + xsmp_xve_type ( msg->type ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Send virtual Ethernet message including current device parameters + * + * @v xcm Configuration manager + * @v msg Partial virtual Ethernet message + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @ret rc Return status code + */ +static int xsmp_tx_xve_params ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg, + struct xsigo_nic *xve, + struct eoib_device *eoib ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct net_device *netdev = eoib->netdev; + + /* Set successful response code */ + msg->code = 0; + + /* Include network identifier, MTU, and current HCA parameters */ + msg->network = htonl ( xve->network ); + msg->mtu = htons ( netdev->max_pkt_len - sizeof ( struct ethhdr ) ); + msg->hca.prefix_le.qword = bswap_64 ( ibdev->gid.s.prefix.qword ); + msg->hca.pkey = htons ( ibdev->pkey ); + msg->hca.qkey = msg->tca.qkey; + if ( eoib->qp ) { + msg->hca.data = htonl ( eoib->qp->ext_qpn ); + msg->hca.qkey = htons ( eoib->qp->qkey ); + } + + /* The message type field is (ab)used to return the current + * operational status. + */ + if ( msg->type == XSMP_XVE_TYPE_OPER_REQ ) { + msg->type = ( netdev_is_open ( netdev ) ? + XSMP_XVE_TYPE_OPER_UP : XSMP_XVE_TYPE_OPER_DOWN ); + } + + /* Send message */ + DBGC ( xve, "XVE %s network %d MTU %d ctrl %#x data %#x qkey %#04x " + "%s\n", xve->name, ntohl ( msg->network ), ntohs ( msg->mtu ), + ntohl ( msg->hca.ctrl ), ntohl ( msg->hca.data ), + ntohs ( msg->hca.qkey ), xsmp_xve_type ( msg->type ) ); + + return xsmp_tx_xve ( xcm, msg ); +} + +/** + * Send virtual Ethernet error response + * + * @v xcm Configuration manager + * @v msg Partial virtual Ethernet message + * @ret rc Return status code + */ +static inline int xsmp_tx_xve_nack ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + + /* Set error response code. (There aren't any meaningful + * detailed response codes defined by the wire protocol.) + */ + msg->code = XSMP_XVE_CODE_ERROR; + + /* Send message */ + return xsmp_tx_xve ( xcm, msg ); +} + +/** + * Send virtual Ethernet notification + * + * @v xcm Configuration manager + * @v type Message type + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @ret rc Return status code + */ +static int xsmp_tx_xve_notify ( struct xsigo_manager *xcm, + unsigned int type, + struct xsigo_nic *xve, + struct eoib_device *eoib ) { + struct xsmp_xve_message msg; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.type = type; + memcpy ( &msg.resource, &xve->resource, sizeof ( msg.resource ) ); + + /* Send message */ + return xsmp_tx_xve_params ( xcm, &msg, xve, eoib ); +} + +/** + * Send virtual Ethernet current operational state + * + * @v xcm Configuration manager + * @v xve Virtual Ethernet device + * @v eoib EoIB device + * @ret rc Return status code + */ +static inline int xsmp_tx_xve_oper ( struct xsigo_manager *xcm, + struct xsigo_nic *xve, + struct eoib_device *eoib ) { + + /* Send notification */ + return xsmp_tx_xve_notify ( xcm, XSMP_XVE_TYPE_OPER_REQ, xve, eoib ); +} + +/** + * Handle received virtual Ethernet modification message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @v update Update bitmask + * @ret rc Return status code + */ +static int xsmp_rx_xve_modify ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg, + unsigned int update ) { + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + struct xsigo_nic *xve; + struct eoib_device *eoib; + struct ib_address_vector tca; + size_t mtu; + int rc; + + /* Avoid returning uninitialised HCA parameters in response */ + memset ( &msg->hca, 0, sizeof ( msg->hca ) ); + + /* Find virtual Ethernet device */ + xve = xve_find ( xcm, &msg->resource ); + if ( ! xve ) { + DBGC ( xcm, "XCM %s unrecognised resource " IB_GUID_FMT "\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + rc = -ENOENT; + goto err_no_xve; + } + + /* Find corresponding EoIB device */ + eoib = eoib_find ( ibdev, xve->mac ); + if ( ! eoib ) { + DBGC ( xve, "XVE %s has no EoIB device\n", xve->name ); + rc = -EPIPE; + goto err_no_eoib; + } + + /* The Xsigo management software fails to create the EoIB + * multicast group. This is a fundamental design flaw. + */ + eoib_force_group_creation ( eoib ); + + /* Extract modifiable parameters. Note that the TCA GID is + * erroneously transmitted as little-endian. + */ + mtu = ntohs ( msg->mtu ); + tca.qpn = ntohl ( msg->tca.data ); + tca.qkey = ntohs ( msg->tca.qkey ); + tca.gid_present = 1; + tca.gid.s.prefix.qword = bswap_64 ( msg->tca.prefix_le.qword ); + tca.gid.s.guid.qword = bswap_64 ( msg->guid_le.qword ); + + /* Update MTU, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_MTU ) && + ( ( rc = xve_update_mtu ( xve, eoib, mtu ) ) != 0 ) ) + goto err_mtu; + update &= ~XSMP_XVE_UPDATE_MTU; + + /* Update admin state, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_STATE ) && + ( ( rc = xve_update_state ( xve, eoib, msg->state ) ) != 0 ) ) + goto err_state; + update &= ~XSMP_XVE_UPDATE_STATE; + + /* Remove gateway, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_GW_DOWN ) && + ( ( rc = xve_update_tca ( xve, eoib, NULL ) ) != 0 ) ) + goto err_gw_down; + update &= ~XSMP_XVE_UPDATE_GW_DOWN; + + /* Update gateway, if applicable */ + if ( ( update & XSMP_XVE_UPDATE_GW_CHANGE ) && + ( ( rc = xve_update_tca ( xve, eoib, &tca ) ) != 0 ) ) + goto err_gw_change; + update &= ~XSMP_XVE_UPDATE_GW_CHANGE; + + /* Warn about unexpected updates */ + if ( update ) { + DBGC ( xve, "XVE %s unrecognised update(s) %#08x\n", + xve->name, update ); + } + + xsmp_tx_xve_params ( xcm, msg, xve, eoib ); + return 0; + + err_gw_change: + err_gw_down: + err_state: + err_mtu: + err_no_eoib: + err_no_xve: + /* Send NACK */ + xsmp_tx_xve_nack ( xcm, msg ); + return rc; +} + +/** + * Handle received virtual Ethernet installation message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_install ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + union { + struct xsmp_xve_mac msg; + uint8_t raw[ETH_ALEN]; + } mac; + char name[ sizeof ( msg->name ) + 1 /* NUL */ ]; + unsigned long network; + unsigned long qkey; + unsigned int update; + int rc; + + /* Demangle MAC address (which is erroneously transmitted as + * little-endian). + */ + mac.msg.high = bswap_16 ( msg->mac_le.high ); + mac.msg.low = bswap_32 ( msg->mac_le.low ); + + /* Extract interface name (which may not be NUL-terminated) */ + memcpy ( name, msg->name, ( sizeof ( name ) - 1 /* NUL */ ) ); + name[ sizeof ( name ) - 1 /* NUL */ ] = '\0'; + + /* Extract remaining message parameters */ + network = ntohl ( msg->network ); + qkey = ntohs ( msg->tca.qkey ); + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " install \"%s\" %s net %ld qkey " + "%#lx\n", xcm->name, IB_GUID_ARGS ( &msg->resource ), name, + eth_ntoa ( mac.raw ), network, qkey ); + + /* Create virtual Ethernet device, if applicable */ + if ( ( xve_find ( xcm, &msg->resource ) == NULL ) && + ( ( rc = xve_create ( xcm, &msg->resource, mac.raw, network, + qkey, name ) ) != 0 ) ) + goto err_create; + + /* Handle remaining parameters as for a modification message */ + update = XSMP_XVE_UPDATE_MTU; + if ( msg->uplink == XSMP_XVE_UPLINK ) + update |= XSMP_XVE_UPDATE_GW_CHANGE; + return xsmp_rx_xve_modify ( xcm, msg, update ); + + err_create: + /* Send NACK */ + xsmp_tx_xve_nack ( xcm, msg ); + return rc; +} + +/** + * Handle received virtual Ethernet deletion message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_delete ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + struct xsigo_nic *xve; + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " delete\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + + /* Destroy virtual Ethernet device (if any) */ + if ( ( xve = xve_find ( xcm, &msg->resource ) ) ) + xve_destroy ( xve ); + + /* Send ACK */ + msg->code = 0; + xsmp_tx_xve ( xcm, msg ); + + return 0; +} + +/** + * Handle received virtual Ethernet update message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_update ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + unsigned int update = ntohl ( msg->update ); + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " update (%08x)\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ), update ); + + /* Handle as a modification message */ + return xsmp_rx_xve_modify ( xcm, msg, update ); +} + +/** + * Handle received virtual Ethernet operational request message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_oper_req ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " operational request\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + + /* Handle as a nullipotent modification message */ + return xsmp_rx_xve_modify ( xcm, msg, 0 ); +} + +/** + * Handle received virtual Ethernet readiness message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve_ready ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + int rc; + + DBGC2 ( xcm, "XCM %s " IB_GUID_FMT " ready\n", + xcm->name, IB_GUID_ARGS ( &msg->resource ) ); + + /* Handle as a nullipotent modification message */ + if ( ( rc = xsmp_rx_xve_modify ( xcm, msg, 0 ) ) != 0 ) + return rc; + + /* Send an unsolicited operational state update, since there + * is no other way to convey the current operational state. + */ + msg->type = XSMP_XVE_TYPE_OPER_REQ; + if ( ( rc = xsmp_rx_xve_modify ( xcm, msg, 0 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Handle received virtual Ethernet message + * + * @v xcm Configuration manager + * @v msg Virtual Ethernet message + * @ret rc Return status code + */ +static int xsmp_rx_xve ( struct xsigo_manager *xcm, + struct xsmp_xve_message *msg ) { + + DBGCP ( xcm, "XCM %s RX[%d] xve %s\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_xve_type ( msg->type ) ); + DBGCP_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + + /* Handle message according to type */ + switch ( msg->type ) { + case XSMP_XVE_TYPE_INSTALL: + return xsmp_rx_xve_install ( xcm, msg ); + case XSMP_XVE_TYPE_DELETE: + return xsmp_rx_xve_delete ( xcm, msg ); + case XSMP_XVE_TYPE_UPDATE: + return xsmp_rx_xve_update ( xcm, msg ); + case XSMP_XVE_TYPE_OPER_REQ: + return xsmp_rx_xve_oper_req ( xcm, msg ); + case XSMP_XVE_TYPE_READY: + return xsmp_rx_xve_ready ( xcm, msg ); + default: + DBGC ( xcm, "XCM %s RX[%d] xve unexpected %s:\n", xcm->name, + ntohl ( msg->hdr.seq ), xsmp_xve_type ( msg->type ) ); + DBGC_HDA ( xcm, 0, msg, sizeof ( *msg ) ); + return -EPROTO; + } +} + +/**************************************************************************** + * + * Configuration managers (XCM) + * + **************************************************************************** + */ + +/** + * Close configuration manager connection + * + * @v xcm Configuration manager + * @v rc Reason for close + */ +static void xcm_close ( struct xsigo_manager *xcm, int rc ) { + + DBGC ( xcm, "XCM %s closed: %s\n", xcm->name, strerror ( rc ) ); + + /* Stop transmission process */ + process_del ( &xcm->process ); + + /* Stop keepalive timer */ + stop_timer ( &xcm->keepalive ); + + /* Restart data transfer interface */ + intf_restart ( &xcm->xfer, rc ); + + /* Schedule reconnection attempt */ + start_timer ( &xcm->reopen ); +} + +/** + * Send data to configuration manager + * + * @v xcm Configuration manager + */ +static void xcm_step ( struct xsigo_manager *xcm ) { + int rc; + + /* Do nothing unless we have something to send */ + if ( ! xcm->pending ) + return; + + /* Send (empty) connection request, if applicable */ + if ( xcm->pending & XCM_TX_CONNECT ) { + if ( ( rc = xfer_deliver_raw ( &xcm->xfer, NULL, 0 ) ) != 0 ) { + DBGC ( xcm, "XCM %s could not send connection request: " + "%s\n", xcm->name, strerror ( rc ) ); + goto err; + } + xcm->pending &= ~XCM_TX_CONNECT; + return; + } + + /* Wait until data transfer interface is connected */ + if ( ! xfer_window ( &xcm->xfer ) ) + return; + + /* Send registration message, if applicable */ + if ( xcm->pending & XCM_TX_REGISTER ) { + if ( ( rc = xsmp_tx_session_register ( xcm ) ) != 0 ) + goto err; + xcm->pending &= ~XCM_TX_REGISTER; + return; + } + + return; + + err: + xcm_close ( xcm, rc ); +} + +/** + * Receive data from configuration manager + * + * @v xcm Configuration manager + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int xcm_deliver ( struct xsigo_manager *xcm, struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + union xsmp_message *msg; + size_t len = iob_len ( iobuf ); + int rc; + + /* Sanity check */ + if ( len < sizeof ( msg->hdr ) ) { + DBGC ( xcm, "XCM %s underlength message:\n", xcm->name ); + DBGC_HDA ( xcm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + goto out; + } + msg = iobuf->data; + + /* Handle message according to type */ + if ( ! msg->hdr.type ) { + + /* Ignore unused communication manager private data blocks */ + rc = 0; + + } else if ( ( msg->hdr.type == XSMP_TYPE_SESSION ) && + ( len >= sizeof ( msg->sess ) ) ) { + + /* Session message */ + rc = xsmp_rx_session ( xcm, &msg->sess ); + + } else if ( ( msg->hdr.type == XSMP_TYPE_XVE ) && + ( len >= sizeof ( msg->xve ) ) ) { + + /* Virtual Ethernet message */ + xsmp_rx_xve ( xcm, &msg->xve ); + + /* Virtual Ethernet message errors are non-fatal */ + rc = 0; + + } else { + + /* Unknown message */ + DBGC ( xcm, "XCM %s unexpected message type %d:\n", + xcm->name, msg->hdr.type ); + DBGC_HDA ( xcm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + } + + out: + free_iob ( iobuf ); + if ( rc != 0 ) + xcm_close ( xcm, rc ); + return rc; +} + +/** Configuration manager data transfer interface operations */ +static struct interface_operation xcm_xfer_op[] = { + INTF_OP ( xfer_deliver, struct xsigo_manager *, xcm_deliver ), + INTF_OP ( xfer_window_changed, struct xsigo_manager *, xcm_step ), + INTF_OP ( intf_close, struct xsigo_manager *, xcm_close ), +}; + +/** Configuration manager data transfer interface descriptor */ +static struct interface_descriptor xcm_xfer_desc = + INTF_DESC ( struct xsigo_manager, xfer, xcm_xfer_op ); + +/** Configuration manager process descriptor */ +static struct process_descriptor xcm_process_desc = + PROC_DESC_ONCE ( struct xsigo_manager, process, xcm_step ); + +/** + * Handle configuration manager connection timer expiry + * + * @v timer Connection timer + * @v fail Failure indicator + */ +static void xcm_reopen ( struct retry_timer *timer, int fail __unused ) { + struct xsigo_manager *xcm = + container_of ( timer, struct xsigo_manager, reopen ); + struct xsigo_device *xdev = xcm->xdev; + struct ib_device *ibdev = xdev->ibdev; + union ib_gid gid; + int rc; + + /* Stop transmission process */ + process_del ( &xcm->process ); + + /* Stop keepalive timer */ + stop_timer ( &xcm->keepalive ); + + /* Restart data transfer interface */ + intf_restart ( &xcm->xfer, -ECANCELED ); + + /* Reset sequence number */ + xcm->seq = 0; + + /* Construct GID */ + memcpy ( &gid.s.prefix, &ibdev->gid.s.prefix, sizeof ( gid.s.prefix ) ); + memcpy ( &gid.s.guid, &xcm->id.guid, sizeof ( gid.s.guid ) ); + DBGC ( xcm, "XCM %s connecting to " IB_GID_FMT "\n", + xcm->name, IB_GID_ARGS ( &gid ) ); + + /* Open CMRC connection */ + if ( ( rc = ib_cmrc_open ( &xcm->xfer, ibdev, &gid, + &xcm_service_id, xcm->name ) ) != 0 ) { + DBGC ( xcm, "XCM %s could not open CMRC connection: %s\n", + xcm->name, strerror ( rc ) ); + start_timer ( &xcm->reopen ); + return; + } + + /* Schedule transmissions */ + xcm->pending |= ( XCM_TX_CONNECT | XCM_TX_REGISTER ); + process_add ( &xcm->process ); + + /* Start keepalive timer */ + start_timer_fixed ( &xcm->keepalive, XSIGO_KEEPALIVE_INTERVAL ); + + return; +} + +/** + * Handle configuration manager keepalive timer expiry + * + * @v timer Connection timer + * @v fail Failure indicator + */ +static void xcm_keepalive ( struct retry_timer *timer, int fail __unused ) { + struct xsigo_manager *xcm = + container_of ( timer, struct xsigo_manager, keepalive ); + int rc; + + /* Send keepalive message. The server won't actually respond + * to these, but it gives the RC queue pair a chance to + * complain if it doesn't ever at least get an ACK. + */ + if ( ( rc = xsmp_tx_session_hello ( xcm ) ) != 0 ) { + xcm_close ( xcm, rc ); + return; + } + + /* Restart keepalive timer */ + start_timer_fixed ( &xcm->keepalive, XSIGO_KEEPALIVE_INTERVAL ); +} + +/** + * Create configuration manager + * + * @v xsigo Xsigo device + * @v id Configuration manager ID + * @ret rc Return status code + */ +static int xcm_create ( struct xsigo_device *xdev, + struct xsigo_manager_id *id ) { + struct xsigo_manager *xcm; + + /* Allocate and initialise structure */ + xcm = zalloc ( sizeof ( *xcm ) ); + if ( ! xcm ) + return -ENOMEM; + ref_init ( &xcm->refcnt, xcm_free ); + xcm->xdev = xdev; + ref_get ( &xcm->xdev->refcnt ); + snprintf ( xcm->name, sizeof ( xcm->name ), "%s:xcm-%d", + xdev->name, ntohs ( id->lid ) ); + memcpy ( &xcm->id, id, sizeof ( xcm->id ) ); + intf_init ( &xcm->xfer, &xcm_xfer_desc, &xcm->refcnt ); + timer_init ( &xcm->keepalive, xcm_keepalive, &xcm->refcnt ); + timer_init ( &xcm->reopen, xcm_reopen, &xcm->refcnt ); + process_init_stopped ( &xcm->process, &xcm_process_desc, &xcm->refcnt ); + INIT_LIST_HEAD ( &xcm->nics ); + + /* Start timer to open connection */ + start_timer_nodelay ( &xcm->reopen ); + + /* Add to list of managers and transfer reference to list */ + list_add ( &xcm->list, &xdev->managers ); + DBGC ( xcm, "XCM %s created for " IB_GUID_FMT " (LID %d)\n", xcm->name, + IB_GUID_ARGS ( &xcm->id.guid ), ntohs ( id->lid ) ); + return 0; +} + +/** + * Find configuration manager + * + * @v xsigo Xsigo device + * @v id Configuration manager ID + * @ret xcm Configuration manager, or NULL + */ +static struct xsigo_manager * xcm_find ( struct xsigo_device *xdev, + struct xsigo_manager_id *id ) { + struct xsigo_manager *xcm; + union ib_guid *guid = &id->guid; + + /* Find configuration manager */ + list_for_each_entry ( xcm, &xdev->managers, list ) { + if ( memcmp ( guid, &xcm->id.guid, sizeof ( *guid ) ) == 0 ) + return xcm; + } + return NULL; +} + +/** + * Destroy configuration manager + * + * @v xcm Configuration manager + */ +static void xcm_destroy ( struct xsigo_manager *xcm ) { + struct xsigo_nic *xve; + + /* Remove all EoIB NICs */ + while ( ( xve = list_first_entry ( &xcm->nics, struct xsigo_nic, + list ) ) ) { + xve_destroy ( xve ); + } + + /* Stop transmission process */ + process_del ( &xcm->process ); + + /* Stop timers */ + stop_timer ( &xcm->keepalive ); + stop_timer ( &xcm->reopen ); + + /* Shut down data transfer interface */ + intf_shutdown ( &xcm->xfer, 0 ); + + /* Remove from list of managers and drop list's reference */ + DBGC ( xcm, "XCM %s destroyed\n", xcm->name ); + list_del ( &xcm->list ); + ref_put ( &xcm->refcnt ); +} + +/** + * Synchronise list of configuration managers + * + * @v xdev Xsigo device + * @v ids List of manager IDs + * @v count Number of manager IDs + * @ret rc Return status code + */ +static int xcm_list ( struct xsigo_device *xdev, struct xsigo_manager_id *ids, + unsigned int count ) { + struct xsigo_manager_id *id; + struct xsigo_manager *xcm; + struct xsigo_manager *tmp; + struct list_head list; + unsigned int i; + int rc; + + /* Create list of managers to be retained */ + INIT_LIST_HEAD ( &list ); + for ( i = 0, id = ids ; i < count ; i++, id++ ) { + if ( ( xcm = xcm_find ( xdev, id ) ) ) { + list_del ( &xcm->list ); + list_add_tail ( &xcm->list, &list ); + } + } + + /* Destroy any managers not in the list */ + list_for_each_entry_safe ( xcm, tmp, &xdev->managers, list ) + xcm_destroy ( xcm ); + list_splice ( &list, &xdev->managers ); + + /* Create any new managers in the list, and force reconnection + * for any changed LIDs. + */ + for ( i = 0, id = ids ; i < count ; i++, id++ ) { + if ( ( xcm = xcm_find ( xdev, id ) ) ) { + if ( xcm->id.lid != id->lid ) + start_timer_nodelay ( &xcm->reopen ); + continue; + } + if ( ( rc = xcm_create ( xdev, id ) ) != 0 ) { + DBGC ( xdev, "XDEV %s could not create manager: %s\n", + xdev->name, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/**************************************************************************** + * + * Configuration manager discovery + * + **************************************************************************** + */ + +/** A stage of discovery */ +struct xsigo_discovery { + /** Name */ + const char *name; + /** Management transaction operations */ + struct ib_mad_transaction_operations op; +}; + +/** + * Handle configuration manager lookup completion + * + * @v ibdev Infiniband device + * @v mi Management interface + * @v madx Management transaction + * @v rc Status code + * @v mad Received MAD (or NULL on error) + * @v av Source address vector (or NULL on error) + */ +static void xsigo_xcm_complete ( struct ib_device *ibdev, + struct ib_mad_interface *mi __unused, + struct ib_mad_transaction *madx, + int rc, union ib_mad *mad, + struct ib_address_vector *av __unused ) { + struct xsigo_device *xdev = ib_madx_get_ownerdata ( madx ); + union xsigo_mad *xsmad = container_of ( mad, union xsigo_mad, mad ); + struct xsigo_managers_reply *reply = &xsmad->reply; + + /* Check for failures */ + if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ) ) + rc = -ENODEV; + if ( rc != 0 ) { + DBGC ( xdev, "XDEV %s manager lookup failed: %s\n", + xdev->name, strerror ( rc ) ); + goto out; + } + + /* Sanity checks */ + if ( reply->count > ( sizeof ( reply->manager ) / + sizeof ( reply->manager[0] ) ) ) { + DBGC ( xdev, "XDEV %s has too many managers (%d)\n", + xdev->name, reply->count ); + goto out; + } + + /* Synchronise list of managers */ + if ( ( rc = xcm_list ( xdev, reply->manager, reply->count ) ) != 0 ) + goto out; + + /* Report an empty list of managers */ + if ( reply->count == 0 ) + DBGC ( xdev, "XDEV %s has no managers\n", xdev->name ); + + /* Delay next discovery attempt */ + start_timer_fixed ( &xdev->discovery, XSIGO_DISCOVERY_SUCCESS_DELAY ); + +out: + /* Destroy the completed transaction */ + ib_destroy_madx ( ibdev, ibdev->gsi, madx ); + xdev->madx = NULL; +} + +/** Configuration manager lookup discovery stage */ +static struct xsigo_discovery xsigo_xcm_discovery = { + .name = "manager", + .op = { + .complete = xsigo_xcm_complete, + }, +}; + +/** + * Handle directory service lookup completion + * + * @v ibdev Infiniband device + * @v mi Management interface + * @v madx Management transaction + * @v rc Status code + * @v mad Received MAD (or NULL on error) + * @v av Source address vector (or NULL on error) + */ +static void xsigo_xds_complete ( struct ib_device *ibdev, + struct ib_mad_interface *mi __unused, + struct ib_mad_transaction *madx, + int rc, union ib_mad *mad, + struct ib_address_vector *av __unused ) { + struct xsigo_device *xdev = ib_madx_get_ownerdata ( madx ); + union xsigo_mad *xsmad = container_of ( mad, union xsigo_mad, mad ); + struct xsigo_managers_request *request = &xsmad->request; + struct ib_service_record *svc; + struct ib_address_vector dest; + union ib_guid *guid; + + /* Allow for reuse of transaction pointer */ + xdev->madx = NULL; + + /* Check for failures */ + if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ) ) + rc = -ENODEV; + if ( rc != 0 ) { + DBGC ( xdev, "XDEV %s directory lookup failed: %s\n", + xdev->name, strerror ( rc ) ); + goto out; + } + + /* Construct address vector */ + memset ( &dest, 0, sizeof ( dest ) ); + svc = &mad->sa.sa_data.service_record; + dest.lid = ntohs ( svc->data16[0] ); + dest.sl = ibdev->sm_sl; + dest.qpn = IB_QPN_GSI; + dest.qkey = IB_QKEY_GSI; + guid = ( ( union ib_guid * ) &svc->data64[0] ); + DBGC2 ( xdev, "XDEV %s found directory at LID %d GUID " IB_GUID_FMT + "\n", xdev->name, dest.lid, IB_GUID_ARGS ( guid ) ); + + /* Construct request (reusing MAD buffer) */ + memset ( request, 0, sizeof ( *request ) ); + request->mad_hdr.mgmt_class = XSIGO_MGMT_CLASS; + request->mad_hdr.class_version = XSIGO_MGMT_CLASS_VERSION; + request->mad_hdr.method = IB_MGMT_METHOD_GET; + request->mad_hdr.attr_id = htons ( XSIGO_ATTR_XCM_REQUEST ); + memcpy ( &request->server.guid, &ibdev->gid.s.guid, + sizeof ( request->server.guid ) ); + snprintf ( request->os_version, sizeof ( request->os_version ), + "%s %s", product_short_name, product_version ); + snprintf ( request->arch, sizeof ( request->arch ), _S2 ( ARCH ) ); + request->os_type = XSIGO_OS_TYPE_GENERIC; + request->resources = htons ( XSIGO_RESOURCES_PRESENT | + XSIGO_RESOURCE_XVE | + XSIGO_RESOURCE_NO_HA ); + + /* The handling of this request on the server side is a + * textbook example of how not to design a wire protocol. The + * server uses the _driver_ version number to determine which + * fields are present. + */ + request->driver_version = htonl ( 0x2a2a2a ); + + /* The build version field is ignored unless it happens to + * contain the substring "xg-". + */ + snprintf ( request->build, sizeof ( request->build ), + "not-xg-%08lx", build_id ); + + /* The server side user interface occasionally has no way to + * refer to an entry with an empty hostname. + */ + fetch_string_setting ( NULL, &hostname_setting, request->hostname, + sizeof ( request->hostname ) ); + if ( ! request->hostname[0] ) { + snprintf ( request->hostname, sizeof ( request->hostname ), + "%s-" IB_GUID_FMT, product_short_name, + IB_GUID_ARGS ( &ibdev->gid.s.guid ) ); + } + + /* Start configuration manager lookup */ + xdev->madx = ib_create_madx ( ibdev, ibdev->gsi, mad, &dest, + &xsigo_xcm_discovery.op ); + if ( ! xdev->madx ) { + DBGC ( xdev, "XDEV %s could not start manager lookup\n", + xdev->name ); + goto out; + } + ib_madx_set_ownerdata ( xdev->madx, xdev ); + +out: + /* Destroy the completed transaction */ + ib_destroy_madx ( ibdev, ibdev->gsi, madx ); +} + +/** Directory service lookup discovery stage */ +static struct xsigo_discovery xsigo_xds_discovery = { + .name = "directory", + .op = { + .complete = xsigo_xds_complete, + }, +}; + +/** + * Discover configuration managers + * + * @v timer Retry timer + * @v over Failure indicator + */ +static void xsigo_discover ( struct retry_timer *timer, int over __unused ) { + struct xsigo_device *xdev = + container_of ( timer, struct xsigo_device, discovery ); + struct ib_device *ibdev = xdev->ibdev; + struct xsigo_discovery *discovery; + + /* Restart timer */ + start_timer_fixed ( &xdev->discovery, XSIGO_DISCOVERY_FAILURE_DELAY ); + + /* Cancel any pending discovery transaction */ + if ( xdev->madx ) { + discovery = container_of ( xdev->madx->op, + struct xsigo_discovery, op ); + DBGC ( xdev, "XDEV %s timed out waiting for %s lookup\n", + xdev->name, discovery->name ); + ib_destroy_madx ( ibdev, ibdev->gsi, xdev->madx ); + xdev->madx = NULL; + } + + /* Start directory service lookup */ + xdev->madx = ib_create_service_madx ( ibdev, ibdev->gsi, + XDS_SERVICE_NAME, + &xsigo_xds_discovery.op ); + if ( ! xdev->madx ) { + DBGC ( xdev, "XDEV %s could not start directory lookup\n", + xdev->name ); + return; + } + ib_madx_set_ownerdata ( xdev->madx, xdev ); +} + +/**************************************************************************** + * + * Infiniband device driver + * + **************************************************************************** + */ + +/** + * Open link and start discovery + * + * @v opener Link opener + * @v over Failure indicator + */ +static void xsigo_ib_open ( struct retry_timer *opener, int over __unused ) { + struct xsigo_device *xdev = + container_of ( opener, struct xsigo_device, opener ); + struct ib_device *ibdev = xdev->ibdev; + int rc; + + /* Open Infiniband device */ + if ( ( rc = ib_open ( ibdev ) ) != 0 ) { + DBGC ( xdev, "XDEV %s could not open: %s\n", + xdev->name, strerror ( rc ) ); + /* Delay and try again */ + start_timer_fixed ( &xdev->opener, XSIGO_OPEN_RETRY_DELAY ); + return; + } + + /* If link is already up, then start discovery */ + if ( ib_link_ok ( ibdev ) ) + start_timer_nodelay ( &xdev->discovery ); +} + +/** + * Probe Xsigo device + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int xsigo_ib_probe ( struct ib_device *ibdev ) { + struct xsigo_device *xdev; + + /* Allocate and initialise structure */ + xdev = zalloc ( sizeof ( *xdev ) ); + if ( ! xdev ) + return -ENOMEM; + ref_init ( &xdev->refcnt, xsigo_free ); + xdev->ibdev = ibdev_get ( ibdev ); + xdev->name = ibdev->name; + timer_init ( &xdev->opener, xsigo_ib_open, &xdev->refcnt ); + timer_init ( &xdev->discovery, xsigo_discover, &xdev->refcnt ); + INIT_LIST_HEAD ( &xdev->managers ); + + /* Start timer to open Infiniband device. (We are currently + * within the Infiniband device probe callback list; opening + * the device here would have interesting side-effects.) + */ + start_timer_nodelay ( &xdev->opener ); + + /* Add to list of devices and transfer reference to list */ + list_add_tail ( &xdev->list, &xsigo_devices ); + DBGC ( xdev, "XDEV %s created for " IB_GUID_FMT "\n", + xdev->name, IB_GUID_ARGS ( &ibdev->gid.s.guid ) ); + return 0; +} + +/** + * Handle device or link status change + * + * @v ibdev Infiniband device + */ +static void xsigo_ib_notify ( struct ib_device *ibdev ) { + struct xsigo_device *xdev; + + /* Stop/restart discovery on any attached devices */ + list_for_each_entry ( xdev, &xsigo_devices, list ) { + + /* Skip non-attached devices */ + if ( xdev->ibdev != ibdev ) + continue; + + /* Stop any ongoing discovery */ + if ( xdev->madx ) { + ib_destroy_madx ( ibdev, ibdev->gsi, xdev->madx ); + xdev->madx = NULL; + } + stop_timer ( &xdev->discovery ); + + /* If link is up, then start discovery */ + if ( ib_link_ok ( ibdev ) ) + start_timer_nodelay ( &xdev->discovery ); + } +} + +/** + * Remove Xsigo device + * + * @v ibdev Infiniband device + */ +static void xsigo_ib_remove ( struct ib_device *ibdev ) { + struct xsigo_device *xdev; + struct xsigo_device *tmp; + + /* Remove any attached Xsigo devices */ + list_for_each_entry_safe ( xdev, tmp, &xsigo_devices, list ) { + + /* Skip non-attached devices */ + if ( xdev->ibdev != ibdev ) + continue; + + /* Stop any ongoing discovery */ + if ( xdev->madx ) { + ib_destroy_madx ( ibdev, ibdev->gsi, xdev->madx ); + xdev->madx = NULL; + } + stop_timer ( &xdev->discovery ); + + /* Destroy all configuration managers */ + xcm_list ( xdev, NULL, 0 ); + + /* Close Infiniband device, if applicable */ + if ( ! timer_running ( &xdev->opener ) ) + ib_close ( xdev->ibdev ); + + /* Stop link opener */ + stop_timer ( &xdev->opener ); + + /* Remove from list of devices and drop list's reference */ + DBGC ( xdev, "XDEV %s destroyed\n", xdev->name ); + list_del ( &xdev->list ); + ref_put ( &xdev->refcnt ); + } +} + +/** Xsigo Infiniband driver */ +struct ib_driver xsigo_ib_driver __ib_driver = { + .name = "Xsigo", + .probe = xsigo_ib_probe, + .notify = xsigo_ib_notify, + .remove = xsigo_ib_remove, +}; + +/**************************************************************************** + * + * Network device driver + * + **************************************************************************** + */ + +/** + * Handle device or link status change + * + * @v netdev Network device + */ +static void xsigo_net_notify ( struct net_device *netdev ) { + struct xsigo_device *xdev; + struct ib_device *ibdev; + struct xsigo_manager *xcm; + struct xsigo_nic *xve; + struct eoib_device *eoib; + + /* Send current operational state to XCM, if applicable */ + list_for_each_entry ( xdev, &xsigo_devices, list ) { + ibdev = xdev->ibdev; + list_for_each_entry ( xcm, &xdev->managers, list ) { + list_for_each_entry ( xve, &xcm->nics, list ) { + eoib = eoib_find ( ibdev, xve->mac ); + if ( ! eoib ) + continue; + if ( eoib->netdev != netdev ) + continue; + xsmp_tx_xve_oper ( xcm, xve, eoib ); + } + } + } +} + +/** Xsigo network driver */ +struct net_driver xsigo_net_driver __net_driver = { + .name = "Xsigo", + .notify = xsigo_net_notify, +}; diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 8eb04a65f..b9ce5e7f7 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -79,11 +79,11 @@ static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" }; * @v address IPv4 address * @v netmask Subnet mask * @v gateway Gateway address (if any) - * @ret miniroute Routing table entry, or NULL + * @ret rc Return status code */ -static struct ipv4_miniroute * __malloc -add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, - struct in_addr netmask, struct in_addr gateway ) { +static int add_ipv4_miniroute ( struct net_device *netdev, + struct in_addr address, struct in_addr netmask, + struct in_addr gateway ) { struct ipv4_miniroute *miniroute; DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) ); @@ -96,7 +96,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, miniroute = malloc ( sizeof ( *miniroute ) ); if ( ! miniroute ) { DBGC ( netdev, "IPv4 could not add miniroute\n" ); - return NULL; + return -ENOMEM; } /* Record routing information */ @@ -114,7 +114,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, list_add ( &miniroute->list, &ipv4_miniroutes ); } - return miniroute; + return 0; } /** @@ -552,6 +552,7 @@ static int ipv4_rx ( struct io_buffer *iobuf, /* Discard unicast packets not destined for us */ if ( ( ! ( flags & LL_MULTICAST ) ) && + ( iphdr->dest.s_addr != INADDR_BROADCAST ) && ipv4_has_any_addr ( netdev ) && ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) { DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet " @@ -788,7 +789,7 @@ int format_ipv4_setting ( const struct setting_type *type __unused, } /** IPv4 address setting */ -const struct setting ip_setting __setting ( SETTING_IP, ip ) = { +const struct setting ip_setting __setting ( SETTING_IP4, ip ) = { .name = "ip", .description = "IP address", .tag = DHCP_EB_YIADDR, @@ -796,7 +797,7 @@ const struct setting ip_setting __setting ( SETTING_IP, ip ) = { }; /** IPv4 subnet mask setting */ -const struct setting netmask_setting __setting ( SETTING_IP, netmask ) = { +const struct setting netmask_setting __setting ( SETTING_IP4, netmask ) = { .name = "netmask", .description = "Subnet mask", .tag = DHCP_SUBNET_MASK, @@ -804,7 +805,7 @@ const struct setting netmask_setting __setting ( SETTING_IP, netmask ) = { }; /** Default gateway setting */ -const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = { +const struct setting gateway_setting __setting ( SETTING_IP4, gateway ) = { .name = "gateway", .description = "Default gateway", .tag = DHCP_ROUTERS, @@ -812,33 +813,69 @@ const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = { }; /** - * Create IPv4 routing table based on configured settings + * Send gratuitous ARP, if applicable * + * @v netdev Network device + * @v address IPv4 address + * @v netmask Subnet mask + * @v gateway Gateway address (if any) * @ret rc Return status code */ -static int ipv4_create_routes ( void ) { - struct ipv4_miniroute *miniroute; - struct ipv4_miniroute *tmp; +static int ipv4_gratuitous_arp ( struct net_device *netdev, + struct in_addr address, + struct in_addr netmask __unused, + struct in_addr gateway __unused ) { + int rc; + + /* Do nothing if network device already has this IPv4 address */ + if ( ipv4_has_addr ( netdev, address ) ) + return 0; + + /* Transmit gratuitous ARP */ + DBGC ( netdev, "IPv4 sending gratuitous ARP for %s via %s\n", + inet_ntoa ( address ), netdev->name ); + if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, &address, + &address ) ) != 0 ) { + DBGC ( netdev, "IPv4 could not transmit gratuitous ARP: %s\n", + strerror ( rc ) ); + /* Treat failures as non-fatal */ + } + + return 0; +} + +/** + * Process IPv4 network device settings + * + * @v apply Application method + * @ret rc Return status code + */ +static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev, + struct in_addr address, + struct in_addr netmask, + struct in_addr gateway ) ) { struct net_device *netdev; struct settings *settings; struct in_addr address = { 0 }; struct in_addr netmask = { 0 }; struct in_addr gateway = { 0 }; + int rc; - /* Delete all existing routes */ - list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) - del_ipv4_miniroute ( miniroute ); - - /* Create a route for each configured network device */ + /* Process settings for each network device */ for_each_netdev ( netdev ) { + + /* Get network device settings */ settings = netdev_settings ( netdev ); + /* Get IPv4 address */ address.s_addr = 0; fetch_ipv4_setting ( settings, &ip_setting, &address ); if ( ! address.s_addr ) continue; + /* Get subnet mask */ fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); + /* Calculate default netmask, if necessary */ if ( ! netmask.s_addr ) { if ( IN_IS_CLASSA ( address.s_addr ) ) { @@ -849,18 +886,42 @@ static int ipv4_create_routes ( void ) { netmask.s_addr = INADDR_NET_CLASSC; } } + /* Get default gateway, if present */ fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); - /* Configure route */ - miniroute = add_ipv4_miniroute ( netdev, address, - netmask, gateway ); - if ( ! miniroute ) - return -ENOMEM; + + /* Apply settings */ + if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 ) + return rc; } return 0; } +/** + * Create IPv4 routing table based on configured settings + * + * @ret rc Return status code + */ +static int ipv4_create_routes ( void ) { + struct ipv4_miniroute *miniroute; + struct ipv4_miniroute *tmp; + int rc; + + /* Send gratuitous ARPs for any new IPv4 addresses */ + ipv4_settings ( ipv4_gratuitous_arp ); + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) + del_ipv4_miniroute ( miniroute ); + + /* Create a route for each configured network device */ + if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 ) + return rc; + + return 0; +} + /** IPv4 settings applicator */ struct settings_applicator ipv4_settings_applicator __settings_applicator = { .apply = ipv4_create_routes, diff --git a/src/net/ipv6.c b/src/net/ipv6.c index bbc00d33e..4b2c33eb4 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -78,6 +79,40 @@ static uint32_t ipv6col ( struct in6_addr *in ) { return crc32_le ( 0, in, sizeof ( *in ) ); } +/** + * Determine IPv6 address scope + * + * @v addr IPv6 address + * @ret scope Address scope + */ +static unsigned int ipv6_scope ( const struct in6_addr *addr ) { + + /* Multicast addresses directly include a scope field */ + if ( IN6_IS_ADDR_MULTICAST ( addr ) ) + return ipv6_multicast_scope ( addr ); + + /* Link-local addresses have link-local scope */ + if ( IN6_IS_ADDR_LINKLOCAL ( addr ) ) + return IPV6_SCOPE_LINK_LOCAL; + + /* Site-local addresses have site-local scope */ + if ( IN6_IS_ADDR_SITELOCAL ( addr ) ) + return IPV6_SCOPE_SITE_LOCAL; + + /* Unique local addresses do not directly map to a defined + * scope. They effectively have a scope which is wider than + * link-local but narrower than global. Since the only + * multicast packets that we transmit are link-local, we can + * simply choose an arbitrary scope between link-local and + * global. + */ + if ( IN6_IS_ADDR_ULA ( addr ) ) + return IPV6_SCOPE_ORGANISATION_LOCAL; + + /* All other addresses are assumed to be global */ + return IPV6_SCOPE_GLOBAL; +} + /** * Dump IPv6 routing table entry * @@ -119,23 +154,32 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) { } /** - * Check if IPv6 address is within a routing table entry's local network + * Count matching bits of an IPv6 routing table entry prefix * * @v miniroute Routing table entry * @v address IPv6 address - * @ret is_on_link Address is within this entry's local network + * @ret match_len Number of matching prefix bits */ -static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute, - struct in6_addr *address ) { +static unsigned int ipv6_match_len ( struct ipv6_miniroute *miniroute, + struct in6_addr *address ) { + unsigned int match_len = 0; unsigned int i; + uint32_t diff; for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / sizeof ( address->s6_addr32[0] ) ) ; i++ ) { - if ( (( address->s6_addr32[i] ^ miniroute->address.s6_addr32[i]) - & miniroute->prefix_mask.s6_addr32[i] ) != 0 ) - return 0; + + diff = ntohl ( ~( ( ~( address->s6_addr32[i] ^ + miniroute->address.s6_addr32[i] ) ) + & miniroute->prefix_mask.s6_addr32[i] ) ); + match_len += 32; + if ( diff ) { + match_len -= flsl ( diff ); + break; + } } - return 1; + + return match_len; } /** @@ -148,12 +192,15 @@ static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute, static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, struct in6_addr *address ) { struct ipv6_miniroute *miniroute; + unsigned int match_len; list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { - if ( ( miniroute->netdev == netdev ) && - ipv6_is_on_link ( miniroute, address ) ) { - return miniroute; - } + if ( miniroute->netdev != netdev ) + continue; + match_len = ipv6_match_len ( miniroute, address ); + if ( match_len < miniroute->prefix_len ) + continue; + return miniroute; } return NULL; } @@ -164,107 +211,89 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, * @v netdev Network device * @v address IPv6 address (or prefix) * @v prefix_len Prefix length - * @v flags Flags - * @ret miniroute Routing table entry, or NULL on failure + * @v router Router address (if any) + * @ret rc Return status code */ -static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev, - struct in6_addr *address, - unsigned int prefix_len, - unsigned int flags ) { +int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address, + unsigned int prefix_len, struct in6_addr *router ) { struct ipv6_miniroute *miniroute; uint8_t *prefix_mask; - - /* Create routing table entry */ - miniroute = zalloc ( sizeof ( *miniroute ) ); - if ( ! miniroute ) - return NULL; - miniroute->netdev = netdev_get ( netdev ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->prefix_len = prefix_len; - assert ( prefix_len <= ( 8 * sizeof ( miniroute->prefix_mask ) ) ); - for ( prefix_mask = miniroute->prefix_mask.s6_addr ; prefix_len >= 8 ; - prefix_mask++, prefix_len -= 8 ) { - *prefix_mask = 0xff; - } - if ( prefix_len ) - *prefix_mask <<= ( 8 - prefix_len ); - miniroute->flags = flags; - list_add ( &miniroute->list, &ipv6_miniroutes ); - ipv6_dump_miniroute ( miniroute ); - - return miniroute; -} - -/** - * Define IPv6 on-link prefix - * - * @v netdev Network device - * @v prefix IPv6 address prefix - * @v prefix_len Prefix length - * @v router Router address (or NULL) - * @ret rc Return status code - */ -int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ) { - struct ipv6_miniroute *miniroute; - int changed; + unsigned int remaining; + unsigned int i; /* Find or create routing table entry */ - miniroute = ipv6_miniroute ( netdev, prefix ); - if ( ! miniroute ) - miniroute = ipv6_add_miniroute ( netdev, prefix, prefix_len, 0); - if ( ! miniroute ) - return -ENOMEM; + miniroute = ipv6_miniroute ( netdev, address ); + if ( miniroute ) { - /* Record router and add to start or end of list as appropriate */ - list_del ( &miniroute->list ); + /* Remove from existing position in routing table */ + list_del ( &miniroute->list ); + + } else { + + /* Create new routing table entry */ + miniroute = zalloc ( sizeof ( *miniroute ) ); + if ( ! miniroute ) + return -ENOMEM; + miniroute->netdev = netdev_get ( netdev ); + memcpy ( &miniroute->address, address, + sizeof ( miniroute->address ) ); + + /* Default to prefix length of 64 if none specified */ + if ( ! prefix_len ) + prefix_len = IPV6_DEFAULT_PREFIX_LEN; + miniroute->prefix_len = prefix_len; + assert ( prefix_len <= IPV6_MAX_PREFIX_LEN ); + + /* Construct prefix mask */ + remaining = prefix_len; + for ( prefix_mask = miniroute->prefix_mask.s6_addr ; + remaining >= 8 ; prefix_mask++, remaining -= 8 ) { + *prefix_mask = 0xff; + } + if ( remaining ) + *prefix_mask <<= ( 8 - remaining ); + } + + /* Add to start of routing table */ + list_add ( &miniroute->list, &ipv6_miniroutes ); + + /* Set or update address, if applicable */ + for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / + sizeof ( address->s6_addr32[0] ) ) ; i++ ) { + if ( ( address->s6_addr32[i] & + ~miniroute->prefix_mask.s6_addr32[i] ) != 0 ) { + memcpy ( &miniroute->address, address, + sizeof ( miniroute->address ) ); + miniroute->flags |= IPV6_HAS_ADDRESS; + } + } + if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN ) + miniroute->flags |= IPV6_HAS_ADDRESS; + + /* Update scope */ + miniroute->scope = ipv6_scope ( &miniroute->address ); + + /* Set or update router, if applicable */ if ( router ) { - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ROUTER ) ) || - ( memcmp ( &miniroute->router, router, - sizeof ( miniroute->router ) ) != 0 ) ); - miniroute->flags |= IPV6_HAS_ROUTER; memcpy ( &miniroute->router, router, sizeof ( miniroute->router ) ); - list_add_tail ( &miniroute->list, &ipv6_miniroutes ); - } else { - changed = ( miniroute->flags & IPV6_HAS_ROUTER ); - miniroute->flags &= ~IPV6_HAS_ROUTER; - list_add ( &miniroute->list, &ipv6_miniroutes ); + miniroute->flags |= IPV6_HAS_ROUTER; } - if ( changed ) - ipv6_dump_miniroute ( miniroute ); + ipv6_dump_miniroute ( miniroute ); return 0; } /** - * Add IPv6 on-link address + * Delete IPv6 minirouting table entry * - * @v netdev Network device - * @v address IPv6 address - * @ret rc Return status code - * - * An on-link prefix for the address must already exist. + * @v miniroute Routing table entry */ -int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) { - struct ipv6_miniroute *miniroute; - int changed; +void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) { - /* Find routing table entry */ - miniroute = ipv6_miniroute ( netdev, address ); - if ( ! miniroute ) - return -EADDRNOTAVAIL; - - /* Record address */ - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) || - ( memcmp ( &miniroute->address, address, - sizeof ( miniroute->address ) ) != 0 ) ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->flags |= IPV6_HAS_ADDRESS; - if ( changed ) - ipv6_dump_miniroute ( miniroute ); - - return 0; + netdev_put ( miniroute->netdev ); + list_del ( &miniroute->list ); + free ( miniroute ); } /** @@ -275,9 +304,17 @@ int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) { * @ret dest Next hop destination address * @ret miniroute Routing table entry to use, or NULL if no route */ -static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, - struct in6_addr **dest ) { +struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, + struct in6_addr **dest ) { struct ipv6_miniroute *miniroute; + struct ipv6_miniroute *chosen = NULL; + unsigned int best = 0; + unsigned int match_len; + unsigned int score; + unsigned int scope; + + /* Calculate destination address scope */ + scope = ipv6_scope ( *dest ); /* Find first usable route in routing table */ list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { @@ -286,37 +323,54 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, if ( ! netdev_is_open ( miniroute->netdev ) ) continue; - /* Skip routing table entries with no usable source address */ + /* Skip entries with no usable source address */ if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) continue; - if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) { + /* Skip entries with a non-matching scope ID, if + * destination specifies a scope ID. + */ + if ( scope_id && ( miniroute->netdev->index != scope_id ) ) + continue; - /* If destination is non-global, and the scope ID - * matches this network device, then use this route. - */ - if ( miniroute->netdev->index == scope_id ) - return miniroute; + /* Skip entries that are out of scope */ + if ( miniroute->scope < scope ) + continue; - } else { + /* Calculate match length */ + match_len = ipv6_match_len ( miniroute, *dest ); - /* If destination is an on-link global - * address, then use this route. - */ - if ( ipv6_is_on_link ( miniroute, *dest ) ) - return miniroute; + /* If destination is on-link, then use this route */ + if ( match_len >= miniroute->prefix_len ) + return miniroute; - /* If destination is an off-link global - * address, and we have a default gateway, - * then use this route. - */ - if ( miniroute->flags & IPV6_HAS_ROUTER ) { - *dest = &miniroute->router; - return miniroute; - } + /* If destination is unicast, then skip off-link + * entries with no router. + */ + if ( ! ( IN6_IS_ADDR_MULTICAST ( *dest ) || + ( miniroute->flags & IPV6_HAS_ROUTER ) ) ) + continue; + + /* Choose best route, defined as being the route with + * the smallest viable scope. If two routes both have + * the same scope, then prefer the route with the + * longest match length. + */ + score = ( ( ( IPV6_SCOPE_MAX + 1 - miniroute->scope ) << 8 ) + + match_len ); + if ( score > best ) { + chosen = miniroute; + best = score; } } + /* Return chosen route, if any */ + if ( chosen ) { + if ( ! IN6_IS_ADDR_MULTICAST ( *dest ) ) + *dest = &chosen->router; + return chosen; + } + return NULL; } @@ -902,7 +956,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { const char *netdev_name; /* Identify network device, if applicable */ - if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) { + if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) { netdev = find_netdev_by_index ( sin6->sin6_scope_id ); netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); } else { @@ -968,7 +1022,8 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { } sin6->sin6_scope_id = netdev->index; - } else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) { + } else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) || + IN6_IS_ADDR_MULTICAST ( &in ) ) { /* If no network device is explicitly specified for a * link-local or multicast address, default to using @@ -1061,61 +1116,235 @@ int format_ipv6_setting ( const struct setting_type *type __unused, return snprintf ( buf, len, "%s", inet6_ntoa ( ipv6 ) ); } +/** IPv6 settings scope */ +const struct settings_scope ipv6_settings_scope; + +/** IPv6 address setting */ +const struct setting ip6_setting __setting ( SETTING_IP6, ip6 ) = { + .name = "ip6", + .description = "IPv6 address", + .type = &setting_type_ipv6, + .scope = &ipv6_settings_scope, +}; + +/** IPv6 prefix length setting */ +const struct setting len6_setting __setting ( SETTING_IP6, len6 ) = { + .name = "len6", + .description = "IPv6 prefix length", + .type = &setting_type_int8, + .scope = &ipv6_settings_scope, +}; + +/** Default gateway setting */ +const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = { + .name = "gateway6", + .description = "IPv6 gateway", + .type = &setting_type_ipv6, + .scope = &ipv6_settings_scope, +}; + /** - * Create IPv6 network device + * Check applicability of IPv6 link-local address setting * - * @v netdev Network device - * @ret rc Return status code + * @v settings Settings block + * @v setting Setting to fetch + * @ret applies Setting applies within this settings block */ -static int ipv6_probe ( struct net_device *netdev ) { - struct ipv6_miniroute *miniroute; - struct in6_addr address; +static int ipv6_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &ipv6_settings_scope ); +} + +/** + * Fetch IPv6 link-local address 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 ipv6_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct net_device *netdev = + container_of ( settings->parent, struct net_device, + settings.settings ); + struct in6_addr ip6; + uint8_t *len6; int prefix_len; int rc; /* Construct link-local address from EUI-64 as per RFC 2464 */ - memset ( &address, 0, sizeof ( address ) ); - prefix_len = ipv6_link_local ( &address, netdev ); + memset ( &ip6, 0, sizeof ( ip6 ) ); + prefix_len = ipv6_link_local ( &ip6, netdev ); if ( prefix_len < 0 ) { rc = prefix_len; - DBGC ( netdev, "IPv6 %s could not construct link-local " - "address: %s\n", netdev->name, strerror ( rc ) ); return rc; } - /* Create link-local address for this network device */ - miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len, - IPV6_HAS_ADDRESS ); - if ( ! miniroute ) - return -ENOMEM; + /* Handle setting */ + if ( setting_cmp ( setting, &ip6_setting ) == 0 ) { - return 0; + /* Return link-local ip6 */ + if ( len > sizeof ( ip6 ) ) + len = sizeof ( ip6 ); + memcpy ( data, &ip6, len ); + return sizeof ( ip6 ); + + } else if ( setting_cmp ( setting, &len6_setting ) == 0 ) { + + /* Return prefix length */ + if ( len ) { + len6 = data; + *len6 = prefix_len; + } + return sizeof ( *len6 ); + + } + + return -ENOENT; } +/** IPv6 link-local address settings operations */ +static struct settings_operations ipv6_settings_operations = { + .applies = ipv6_applies, + .fetch = ipv6_fetch, +}; + +/** IPv6 link-local address settings */ +struct ipv6_settings { + /** Reference counter */ + struct refcnt refcnt; + /** Settings interface */ + struct settings settings; +}; + /** - * Destroy IPv6 network device + * Register IPv6 link-local address settings * * @v netdev Network device + * @ret rc Return status code */ -static void ipv6_remove ( struct net_device *netdev ) { - struct ipv6_miniroute *miniroute; - struct ipv6_miniroute *tmp; +static int ipv6_register_settings ( struct net_device *netdev ) { + struct settings *parent = netdev_settings ( netdev ); + struct ipv6_settings *ipv6set; + int rc; - /* Delete all miniroutes for this network device */ - list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) { - if ( miniroute->netdev == netdev ) { - netdev_put ( miniroute->netdev ); - list_del ( &miniroute->list ); - free ( miniroute ); - } + /* Allocate and initialise structure */ + ipv6set = zalloc ( sizeof ( *ipv6set ) ); + if ( ! ipv6set ) { + rc = -ENOMEM; + goto err_alloc; } + ref_init ( &ipv6set->refcnt, NULL ); + settings_init ( &ipv6set->settings, &ipv6_settings_operations, + &ipv6set->refcnt, &ipv6_settings_scope ); + ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL; + + /* Register settings */ + if ( ( rc = register_settings ( &ipv6set->settings, parent, + IPV6_SETTINGS_NAME ) ) != 0 ) + goto err_register; + + err_register: + ref_put ( &ipv6set->refcnt ); + err_alloc: + return rc; } /** IPv6 network device driver */ struct net_driver ipv6_driver __net_driver = { .name = "IPv6", - .probe = ipv6_probe, - .remove = ipv6_remove, + .probe = ipv6_register_settings, +}; + +/** + * Create IPv6 routing table based on configured settings + * + * @v netdev Network device + * @v settings Settings block + * @ret rc Return status code + */ +static int ipv6_create_routes ( struct net_device *netdev, + struct settings *settings ) { + struct settings *child; + struct settings *origin; + struct in6_addr ip6_buf; + struct in6_addr gateway6_buf; + struct in6_addr *ip6 = &ip6_buf; + struct in6_addr *gateway6 = &gateway6_buf; + uint8_t len6; + size_t len; + int rc; + + /* First, create routing table for any child settings. We do + * this depth-first and in reverse order so that the end + * result reflects the relative priorities of the settings + * blocks. + */ + list_for_each_entry_reverse ( child, &settings->children, siblings ) + ipv6_create_routes ( netdev, child ); + + /* Fetch IPv6 address, if any */ + len = fetch_setting ( settings, &ip6_setting, &origin, NULL, + ip6, sizeof ( *ip6 ) ); + if ( ( len != sizeof ( *ip6 ) ) || ( origin != settings ) ) + return 0; + + /* Fetch prefix length, if defined */ + len = fetch_setting ( settings, &len6_setting, &origin, NULL, + &len6, sizeof ( len6 ) ); + if ( ( len != sizeof ( len6 ) ) || ( origin != settings ) ) + len6 = 0; + if ( len6 > IPV6_MAX_PREFIX_LEN ) + len6 = IPV6_MAX_PREFIX_LEN; + + /* Fetch gateway, if defined */ + len = fetch_setting ( settings, &gateway6_setting, &origin, NULL, + gateway6, sizeof ( *gateway6 ) ); + if ( ( len != sizeof ( *gateway6 ) ) || ( origin != settings ) ) + gateway6 = NULL; + + /* Create or update route */ + if ( ( rc = ipv6_add_miniroute ( netdev, ip6, len6, gateway6 ) ) != 0){ + DBGC ( netdev, "IPv6 %s could not add route: %s\n", + netdev->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Create IPv6 routing table based on configured settings + * + * @ret rc Return status code + */ +static int ipv6_create_all_routes ( void ) { + struct ipv6_miniroute *miniroute; + struct ipv6_miniroute *tmp; + struct net_device *netdev; + struct settings *settings; + int rc; + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) + ipv6_del_miniroute ( miniroute ); + + /* Create routes for each configured network device */ + for_each_netdev ( netdev ) { + settings = netdev_settings ( netdev ); + if ( ( rc = ipv6_create_routes ( netdev, settings ) ) != 0 ) + return rc; + } + + return 0; +} + +/** IPv6 settings applicator */ +struct settings_applicator ipv6_settings_applicator __settings_applicator = { + .apply = ipv6_create_all_routes, }; /* Drag in objects via ipv6_protocol */ diff --git a/src/net/ndp.c b/src/net/ndp.c index e62f7d5cb..f28e71cbd 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include @@ -38,8 +39,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ); static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, + struct in6_addr *router, struct ndp_router_advertisement_header *radv, size_t len ); @@ -339,10 +342,6 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, union ndp_option *option, size_t len ) { struct ndp_router_advertisement_header *radv = &ndp->radv; struct ndp_prefix_information_option *prefix_opt = &option->prefix; - struct in6_addr *router = &sin6_src->sin6_addr; - struct in6_addr address; - int prefix_len; - int rc; /* Sanity check */ if ( sizeof ( *prefix_opt ) > len ) { @@ -350,53 +349,14 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, "short at %zd bytes\n", netdev->name, len ); return -EINVAL; } + DBGC ( netdev, "NDP %s found %sdefault router %s ", netdev->name, ( radv->lifetime ? "" : "non-" ), inet6_ntoa ( &sin6_src->sin6_addr ) ); DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n", ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ), ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ), - inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len ); - - /* Ignore off-link prefixes */ - if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ) - return 0; - - /* Define prefix */ - if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix, - prefix_opt->prefix_len, - ( radv->lifetime ? - router : NULL ) ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n", - netdev->name, inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len, strerror ( rc ) ); - return rc; - } - - /* Perform stateless address autoconfiguration, if applicable */ - if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) { - memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) ); - prefix_len = ipv6_eui64 ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "NDP %s could not construct SLAAC " - "address: %s\n", netdev->name, strerror ( rc ) ); - return rc; - } - if ( prefix_len != prefix_opt->prefix_len ) { - DBGC ( netdev, "NDP %s incorrect SLAAC prefix length " - "%d (expected %d)\n", netdev->name, - prefix_opt->prefix_len, prefix_len ); - return -EINVAL; - } - if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not set address %s: %s\n", - netdev->name, inet6_ntoa ( &address ), - strerror ( rc ) ); - return rc; - } - } + inet6_ntoa ( &prefix_opt->prefix ), prefix_opt->prefix_len ); return 0; } @@ -576,6 +536,7 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, struct sockaddr_in6 *sin6_dest __unused ) { union ndp_header *ndp = iobuf->data; struct ndp_router_advertisement_header *radv = &ndp->radv; + struct in6_addr *router = &sin6_src->sin6_addr; size_t len = iob_len ( iobuf ); int rc; @@ -586,8 +547,8 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, goto err_options; /* Pass to IPv6 autoconfiguration */ - if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv, - len ) ) != 0 ) + if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, router, + radv, len ) ) != 0 ) goto err_ipv6conf; err_ipv6conf: @@ -618,16 +579,30 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = { * */ +/** An NDP prefix settings block */ +struct ndp_prefix_settings { + /** Settings interface */ + struct settings settings; + /** Name */ + char name[4]; + /** Prefix information option */ + struct ndp_prefix_information_option *prefix; +}; + /** An NDP settings block */ struct ndp_settings { /** Reference counter */ struct refcnt refcnt; /** Settings interface */ struct settings settings; + /** Router address */ + struct in6_addr router; + /** Router lifetime */ + unsigned int lifetime; /** Length of NDP options */ size_t len; /** NDP options */ - union ndp_option option[0]; + union ndp_option options[0]; }; /** NDP settings scope */ @@ -638,9 +613,11 @@ static const struct settings_scope ndp_settings_scope; * * @v type NDP option type * @v offset Starting offset of data + * @v len Length of data (or 0 to use all remaining data) * @ret tag NDP tag */ -#define NDP_TAG( type, offset ) ( ( (offset) << 8 ) | (type) ) +#define NDP_TAG( type, offset, len ) \ + ( ( (len) << 16 ) | ( (offset) << 8 ) | (type) ) /** * Extract NDP tag type @@ -648,7 +625,7 @@ static const struct settings_scope ndp_settings_scope; * @v tag NDP tag * @ret type NDP option type */ -#define NDP_TAG_TYPE( tag ) ( (tag) & 0xff ) +#define NDP_TAG_TYPE( tag ) ( ( (tag) >> 0 ) & 0xff ) /** * Extract NDP tag offset @@ -656,7 +633,23 @@ static const struct settings_scope ndp_settings_scope; * @v tag NDP tag * @ret offset Starting offset of data */ -#define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 ) +#define NDP_TAG_OFFSET( tag ) ( ( (tag) >> 8 ) & 0xff ) + +/** + * Extract NDP tag length + * + * @v tag NDP tag + * @ret len Length of data (or 0 to use all remaining data) + */ +#define NDP_TAG_LEN( tag ) ( ( (tag) >> 16 ) & 0xff ) + +/** + * Extract NDP tag instance + * + * @v tag NDP tag + * @ret instance Instance + */ +#define NDP_TAG_INSTANCE( tag ) ( ( (tag) >> 24 ) & 0xff ) /** * Check applicability of NDP setting @@ -689,44 +682,58 @@ static int ndp_fetch ( struct settings *settings, container_of ( settings->parent, struct net_device, settings.settings ); union ndp_option *option; - unsigned int type = NDP_TAG_TYPE ( setting->tag ); - unsigned int offset = NDP_TAG_OFFSET ( setting->tag ); - size_t remaining; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + unsigned int tag_instance; + size_t offset; size_t option_len; - size_t payload_len; + void *option_data; + + /* Parse setting tag */ + tag_type = NDP_TAG_TYPE ( setting->tag ); + tag_offset = NDP_TAG_OFFSET ( setting->tag ); + tag_len = NDP_TAG_LEN ( setting->tag ); + tag_instance = NDP_TAG_INSTANCE ( setting->tag ); /* Scan through NDP options for requested type. We can assume * that the options are well-formed, otherwise they would have * been rejected prior to being stored. */ - option = ndpset->option; - remaining = ndpset->len; - while ( remaining ) { + for ( offset = 0 ; offset < ndpset->len ; offset += option_len ) { /* Calculate option length */ + option = ( ( ( void * ) ndpset->options ) + offset ); option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - /* If this is the requested option, return it */ - if ( option->header.type == type ) { + /* Skip options that do not match this tag */ + if ( option->header.type != tag_type ) + continue; - /* Sanity check */ - if ( offset > option_len ) { - DBGC ( netdev, "NDP %s option %d too short\n", - netdev->name, type ); - return -EINVAL; - } - payload_len = ( option_len - offset ); + /* Skip previous instances of this option */ + if ( tag_instance-- != 0 ) + continue; - /* Copy data to output buffer */ - if ( len > payload_len ) - len = payload_len; - memcpy ( data, ( ( ( void * ) option ) + offset ), len); - return payload_len; + /* Sanity check */ + if ( ( tag_offset + tag_len ) > option_len ) { + DBGC ( netdev, "NDP %s option %d too short\n", + netdev->name, tag_type ); + return -EINVAL; } + if ( ! tag_len ) + tag_len = ( option_len - tag_offset ); + option_data = ( ( ( void * ) option ) + tag_offset ); - /* Move to next option */ - option = ( ( ( void * ) option ) + option_len ); - remaining -= option_len; + /* Copy data to output buffer */ + if ( len > tag_len ) + len = tag_len; + memcpy ( data, option_data, len ); + + /* Default to hex if no type is specified */ + if ( ! setting->type ) + setting->type = &setting_type_hex; + + return tag_len; } return -ENOENT; @@ -738,22 +745,218 @@ static struct settings_operations ndp_settings_operations = { .fetch = ndp_fetch, }; +/** + * Check applicability of NDP per-prefix setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @ret applies Setting applies within this settings block + */ +static int ndp_prefix_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &ipv6_settings_scope ); +} + +/** + * Fetch value of NDP IPv6 address setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data, + size_t len ) { + struct ndp_prefix_settings *prefset = + container_of ( settings, struct ndp_prefix_settings, settings ); + struct ndp_settings *ndpset = + container_of ( settings->parent, struct ndp_settings, settings); + struct net_device *netdev = + container_of ( ndpset->settings.parent, struct net_device, + settings.settings ); + struct ndp_prefix_information_option *prefix = prefset->prefix; + struct in6_addr ip6; + int prefix_len; + + /* Skip dead prefixes */ + if ( ! prefix->valid ) + return -ENOENT; + + /* Construct IPv6 address via SLAAC, if applicable */ + memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) ); + if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) { + prefix_len = ipv6_eui64 ( &ip6, netdev ); + if ( prefix_len < 0 ) + return prefix_len; + if ( prefix_len != prefix->prefix_len ) + return -EINVAL; + } + + /* Fill in IPv6 address */ + if ( len > sizeof ( ip6 ) ) + len = sizeof ( ip6 ); + memcpy ( data, &ip6, len ); + + return sizeof ( ip6 ); +} + +/** + * Fetch value of NDP prefix length setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_len6 ( struct settings *settings, void *data, + size_t len ) { + struct ndp_prefix_settings *prefset = + container_of ( settings, struct ndp_prefix_settings, settings ); + struct ndp_prefix_information_option *prefix = prefset->prefix; + uint8_t *len6; + + /* Fill in prefix length */ + if ( len >= sizeof ( *len6 ) ) { + /* We treat an off-link prefix as having a prefix + * length covering the entire IPv6 address. + */ + len6 = data; + *len6 = ( ( prefix->flags & NDP_PREFIX_ON_LINK ) ? + prefix->prefix_len : -1UL ); + } + + return sizeof ( *len6 ); +} + +/** + * Fetch value of NDP router address setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_gateway6 ( struct settings *settings, + void *data, size_t len ) { + struct ndp_settings *ndpset = + container_of ( settings->parent, struct ndp_settings, settings); + + /* Treat non-routing router as non-existent */ + if ( ! ndpset->lifetime ) + return -ENOENT; + + /* Fill in router address */ + if ( len > sizeof ( ndpset->router ) ) + len = sizeof ( ndpset->router ); + memcpy ( data, &ndpset->router, len ); + + return sizeof ( ndpset->router ); +} + +/** An NDP per-prefix setting operation */ +struct ndp_prefix_operation { + /** Generic setting */ + const struct setting *setting; + /** + * Fetch value of setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ + int ( * fetch ) ( struct settings *settings, void *data, size_t len ); +}; + +/** NDP per-prefix settings operations */ +static struct ndp_prefix_operation ndp_prefix_operations[] = { + { &ip6_setting, ndp_prefix_fetch_ip6 }, + { &len6_setting, ndp_prefix_fetch_len6 }, + { &gateway6_setting, ndp_prefix_fetch_gateway6 }, +}; + +/** + * Fetch value of NDP pre-prefix 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 ndp_prefix_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct ndp_prefix_operation *op; + unsigned int i; + + /* Handle per-prefix settings */ + for ( i = 0 ; i < ( sizeof ( ndp_prefix_operations ) / + sizeof ( ndp_prefix_operations[0] ) ) ; i++ ) { + op = &ndp_prefix_operations[i]; + if ( setting_cmp ( setting, op->setting ) == 0 ) + return op->fetch ( settings, data, len ); + } + + return -ENOENT; +} + +/** NDP per-prefix settings operations */ +static struct settings_operations ndp_prefix_settings_operations = { + .applies = ndp_prefix_applies, + .fetch = ndp_prefix_fetch, +}; + /** * Register NDP settings * * @v netdev Network device - * @v option NDP options + * @v router Router address + * @v lifetime Router lifetime + * @v options NDP options * @v len Length of options * @ret rc Return status code */ static int ndp_register_settings ( struct net_device *netdev, - union ndp_option *option, size_t len ) { + struct in6_addr *router, + unsigned int lifetime, + union ndp_option *options, size_t len ) { struct settings *parent = netdev_settings ( netdev ); + union ndp_option *option; struct ndp_settings *ndpset; + struct ndp_prefix_settings *prefset; + size_t offset; + size_t option_len; + unsigned int prefixes; + unsigned int instance; + int order; int rc; + /* Count number of prefix options. We can assume that the + * options are well-formed, otherwise they would have been + * rejected prior to being stored. + */ + order = IPV6_ORDER_PREFIX_ONLY; + for ( prefixes = 0, offset = 0 ; offset < len ; offset += option_len ) { + + /* Skip non-prefix options */ + option = ( ( ( void * ) options ) + offset ); + option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); + if ( option->header.type != NDP_OPT_PREFIX ) + continue; + + /* Count number of prefixes */ + prefixes++; + + /* Increase overall order if we have SLAAC addresses */ + if ( option->prefix.flags & NDP_PREFIX_AUTONOMOUS ) + order = IPV6_ORDER_SLAAC; + } + /* Allocate and initialise structure */ - ndpset = zalloc ( sizeof ( *ndpset ) + len ); + ndpset = zalloc ( sizeof ( *ndpset ) + len + + ( prefixes * sizeof ( *prefset ) ) ); if ( ! ndpset ) { rc = -ENOMEM; goto err_alloc; @@ -761,14 +964,54 @@ static int ndp_register_settings ( struct net_device *netdev, ref_init ( &ndpset->refcnt, NULL ); settings_init ( &ndpset->settings, &ndp_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); + ndpset->settings.order = order; + memcpy ( &ndpset->router, router, sizeof ( ndpset->router ) ); + ndpset->lifetime = lifetime; ndpset->len = len; - memcpy ( ndpset->option, option, len ); + memcpy ( ndpset->options, options, len ); + prefset = ( ( ( void * ) ndpset->options ) + len ); /* Register settings */ if ( ( rc = register_settings ( &ndpset->settings, parent, NDP_SETTINGS_NAME ) ) != 0 ) goto err_register; + /* Construct and register per-prefix settings */ + for ( instance = 0, offset = 0 ; offset < len ; offset += option_len ) { + + /* Skip non-prefix options */ + option = ( ( ( void * ) ndpset->options ) + offset ); + option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); + if ( option->header.type != NDP_OPT_PREFIX ) + continue; + + /* Initialise structure */ + settings_init ( &prefset->settings, + &ndp_prefix_settings_operations, + &ndpset->refcnt, &ndp_settings_scope ); + prefset->settings.order = + ( ( option->prefix.flags & NDP_PREFIX_AUTONOMOUS ) ? + IPV6_ORDER_SLAAC : IPV6_ORDER_PREFIX_ONLY ); + prefset->prefix = &option->prefix; + snprintf ( prefset->name, sizeof ( prefset->name ), "%d", + instance++ ); + + /* Register settings */ + if ( ( rc = register_settings ( &prefset->settings, + &ndpset->settings, + prefset->name ) ) != 0 ) + goto err_register_prefix; + + /* Move to next per-prefix settings */ + prefset++; + } + assert ( instance == prefixes ); + + ref_put ( &ndpset->refcnt ); + return 0; + + err_register_prefix: + unregister_settings ( &ndpset->settings ); err_register: ref_put ( &ndpset->refcnt ); err_alloc: @@ -776,11 +1019,11 @@ static int ndp_register_settings ( struct net_device *netdev, } /** DNS server setting */ -const struct setting ndp_dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { +const struct setting ndp_dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .name = "dns6", .description = "DNS server", .tag = NDP_TAG ( NDP_OPT_RDNSS, - offsetof ( struct ndp_rdnss_option, addresses ) ), + offsetof ( struct ndp_rdnss_option, addresses ), 0 ), .type = &setting_type_ipv6, .scope = &ndp_settings_scope, }; @@ -790,7 +1033,7 @@ const struct setting ndp_dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { .name = "dnssl", .description = "DNS search list", .tag = NDP_TAG ( NDP_OPT_DNSSL, - offsetof ( struct ndp_dnssl_option, names ) ), + offsetof ( struct ndp_dnssl_option, names ), 0 ), .type = &setting_type_dnssl, .scope = &ndp_settings_scope, }; @@ -897,6 +1140,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { * Handle router advertisement during IPv6 autoconfiguration * * @v netdev Network device + * @v router Router address * @v radv Router advertisement * @v len Length of router advertisement * @ret rc Return status code @@ -906,6 +1150,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { */ static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, + struct in6_addr *router, struct ndp_router_advertisement_header *radv, size_t len ) { struct ipv6conf *ipv6conf; @@ -915,13 +1160,10 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev, /* Identify IPv6 configurator, if any */ ipv6conf = ipv6conf_demux ( netdev ); - if ( ! ipv6conf ) { - /* Not an error; router advertisements are processed - * as a background activity even when no explicit - * autoconfiguration is taking place. - */ + + /* Do nothing unless IPv6 autoconfiguration is in progress */ + if ( ! ipv6conf ) return 0; - } /* If this is not the first solicited router advertisement, ignore it */ if ( ! timer_running ( &ipv6conf->timer ) ) @@ -932,8 +1174,9 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev, /* Register NDP settings */ option_len = ( len - offsetof ( typeof ( *radv ), option ) ); - if ( ( rc = ndp_register_settings ( netdev, radv->option, - option_len ) ) != 0 ) + if ( ( rc = ndp_register_settings ( netdev, router, + ntohl ( radv->lifetime ), + radv->option, option_len ) ) != 0 ) return rc; /* Start DHCPv6 if required */ diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index edd4c4b9f..cc2e10354 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -45,6 +45,11 @@ const struct setting mac_setting __setting ( SETTING_NETDEV, mac ) = { .description = "MAC address", .type = &setting_type_hex, }; +const struct setting hwaddr_setting __setting ( SETTING_NETDEV, hwaddr ) = { + .name = "hwaddr", + .description = "Hardware address", + .type = &setting_type_hex, +}; const struct setting bustype_setting __setting ( SETTING_NETDEV, bustype ) = { .name = "bustype", .description = "Bus type", @@ -65,9 +70,20 @@ const struct setting chip_setting __setting ( SETTING_NETDEV, chip ) = { .description = "Chip", .type = &setting_type_string, }; +const struct setting ifname_setting __setting ( SETTING_NETDEV, ifname ) = { + .name = "ifname", + .description = "Interface name", + .type = &setting_type_string, +}; +const struct setting mtu_setting __setting ( SETTING_NETDEV, mtu ) = { + .name = "mtu", + .description = "MTU", + .type = &setting_type_int16, + .tag = DHCP_MTU, +}; /** - * Store MAC address setting + * Store link-layer address setting * * @v netdev Network device * @v data Setting data, or NULL to clear setting @@ -92,7 +108,7 @@ static int netdev_store_mac ( struct net_device *netdev, } /** - * Fetch MAC address setting + * Fetch link-layer address setting * * @v netdev Network device * @v data Buffer to fill with setting data @@ -101,11 +117,30 @@ static int netdev_store_mac ( struct net_device *netdev, */ static int netdev_fetch_mac ( struct net_device *netdev, void *data, size_t len ) { + size_t max_len = netdev->ll_protocol->ll_addr_len; - if ( len > netdev->ll_protocol->ll_addr_len ) - len = netdev->ll_protocol->ll_addr_len; + if ( len > max_len ) + len = max_len; memcpy ( data, netdev->ll_addr, len ); - return netdev->ll_protocol->ll_addr_len; + return max_len; +} + +/** + * Fetch hardware address setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_hwaddr ( struct net_device *netdev, void *data, + size_t len ) { + size_t max_len = netdev->ll_protocol->hw_addr_len; + + if ( len > max_len ) + len = max_len; + memcpy ( data, netdev->hw_addr, len ); + return max_len; } /** @@ -136,7 +171,8 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data, assert ( desc->bus_type < ( sizeof ( bustypes ) / sizeof ( bustypes[0] ) ) ); bustype = bustypes[desc->bus_type]; - assert ( bustype != NULL ); + if ( ! bustype ) + return -ENOENT; strncpy ( data, bustype, len ); return strlen ( bustype ); } @@ -199,6 +235,22 @@ static int netdev_fetch_chip ( struct net_device *netdev, void *data, return strlen ( chip ); } +/** + * Fetch ifname setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_ifname ( struct net_device *netdev, void *data, + size_t len ) { + const char *ifname = netdev->name; + + strncpy ( data, ifname, len ); + return strlen ( ifname ); +} + /** A network device setting operation */ struct netdev_setting_operation { /** Setting */ @@ -225,10 +277,12 @@ struct netdev_setting_operation { /** Network device settings */ static struct netdev_setting_operation netdev_setting_operations[] = { { &mac_setting, netdev_store_mac, netdev_fetch_mac }, + { &hwaddr_setting, NULL, netdev_fetch_hwaddr }, { &bustype_setting, NULL, netdev_fetch_bustype }, { &busloc_setting, NULL, netdev_fetch_busloc }, { &busid_setting, NULL, netdev_fetch_busid }, { &chip_setting, NULL, netdev_fetch_chip }, + { &ifname_setting, NULL, netdev_fetch_ifname }, }; /** @@ -354,3 +408,66 @@ static void netdev_redirect_settings_init ( void ) { struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = { .initialise = netdev_redirect_settings_init, }; + +/** + * Apply network device settings + * + * @ret rc Return status code + */ +static int apply_netdev_settings ( void ) { + struct net_device *netdev; + struct settings *settings; + struct ll_protocol *ll_protocol; + size_t max_mtu; + size_t old_mtu; + size_t mtu; + int rc; + + /* Process settings for each network device */ + for_each_netdev ( netdev ) { + + /* Get network device settings */ + settings = netdev_settings ( netdev ); + + /* Get MTU */ + mtu = fetch_uintz_setting ( settings, &mtu_setting ); + + /* Do nothing unless MTU is specified */ + if ( ! mtu ) + continue; + + /* Limit MTU to maximum supported by hardware */ + ll_protocol = netdev->ll_protocol; + max_mtu = ( netdev->max_pkt_len - ll_protocol->ll_header_len ); + if ( mtu > max_mtu ) { + DBGC ( netdev, "NETDEV %s cannot support MTU %zd (max " + "%zd)\n", netdev->name, mtu, max_mtu ); + mtu = max_mtu; + } + + /* Update maximum packet length */ + old_mtu = netdev->mtu; + netdev->mtu = mtu; + if ( mtu != old_mtu ) { + DBGC ( netdev, "NETDEV %s MTU is %zd\n", + netdev->name, mtu ); + } + + /* Close and reopen network device if MTU has increased */ + if ( netdev_is_open ( netdev ) && ( mtu > old_mtu ) ) { + netdev_close ( netdev ); + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + DBGC ( netdev, "NETDEV %s could not reopen: " + "%s\n", netdev->name, strerror ( rc ) ); + return rc; + } + } + } + + return 0; +} + +/** Network device settings applicator */ +struct settings_applicator netdev_applicator __settings_applicator = { + .apply = apply_netdev_settings, +}; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 7c40a2ac8..71a37eccc 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -402,11 +402,24 @@ void netdev_tx_complete_err ( struct net_device *netdev, list_del ( &iobuf->list ); netdev_tx_err ( netdev, iobuf, rc ); - /* Transmit first pending packet, if any */ - if ( ( iobuf = list_first_entry ( &netdev->tx_deferred, - struct io_buffer, list ) ) != NULL ) { + /* Handle pending transmit queue */ + while ( ( iobuf = list_first_entry ( &netdev->tx_deferred, + struct io_buffer, list ) ) ) { + + /* Remove from pending transmit queue */ list_del ( &iobuf->list ); + + /* When any transmit completion fails, cancel all + * pending transmissions. + */ + if ( rc != 0 ) { + netdev_tx_err ( netdev, iobuf, -ECANCELED ); + continue; + } + + /* Otherwise, attempt to transmit the first pending packet */ netdev_tx ( netdev, iobuf ); + break; } } @@ -663,6 +676,12 @@ int register_netdev ( struct net_device *netdev ) { ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr ); } + /* Set MTU, if not already set */ + if ( ! netdev->mtu ) { + netdev->mtu = ( netdev->max_pkt_len - + ll_protocol->ll_header_len ); + } + /* Reject network devices that are already available via a * different hardware device. */ @@ -675,6 +694,14 @@ int register_netdev ( struct net_device *netdev ) { goto err_duplicate; } + /* Reject named network devices that already exist */ + if ( netdev->name[0] && ( duplicate = find_netdev ( netdev->name ) ) ) { + DBGC ( netdev, "NETDEV rejecting duplicate name %s\n", + duplicate->name ); + rc = -EEXIST; + goto err_duplicate; + } + /* Record device index and create device name */ if ( netdev->name[0] == '\0' ) { snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", @@ -725,6 +752,8 @@ int register_netdev ( struct net_device *netdev ) { clear_settings ( netdev_settings ( netdev ) ); unregister_settings ( netdev_settings ( netdev ) ); err_register_settings: + list_del ( &netdev->list ); + netdev_put ( netdev ); err_duplicate: return rc; } @@ -845,12 +874,9 @@ void unregister_netdev ( struct net_device *netdev ) { */ void netdev_irq ( struct net_device *netdev, int enable ) { - /* Do nothing if device does not support interrupts */ - if ( ! netdev_irq_supported ( netdev ) ) - return; - - /* Enable or disable device interrupts */ - netdev->op->irq ( netdev, enable ); + /* Enable or disable device interrupts, if applicable */ + if ( netdev_irq_supported ( netdev ) ) + netdev->op->irq ( netdev, enable ); /* Record interrupt enabled state */ netdev->state &= ~NETDEV_IRQ_ENABLED; diff --git a/src/net/peerblk.c b/src/net/peerblk.c index 9fd52b736..78888d2db 100644 --- a/src/net/peerblk.c +++ b/src/net/peerblk.c @@ -270,6 +270,9 @@ static int peerblk_deliver ( struct peerdist_block *peerblk, */ static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { struct digest_algorithm *digest = peerblk->digest; + struct peerdisc_segment *segment = peerblk->discovery.segment; + struct peerdisc_peer *head; + struct peerdisc_peer *peer; uint8_t hash[digest->digestsize]; unsigned long now = peerblk_timestamp(); @@ -296,6 +299,11 @@ static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { profile_custom ( &peerblk_attempt_success_profiler, ( now - peerblk->attempted ) ); + /* Report peer statistics */ + head = list_entry ( &segment->peers, struct peerdisc_peer, list ); + peer = ( ( peerblk->peer == head ) ? NULL : peerblk->peer ); + peerdisc_stat ( &peerblk->xfer, peer, &segment->peers ); + /* Close download */ peerblk_close ( peerblk, 0 ); return; diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index 5b0e98911..20ac2427b 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -76,6 +76,36 @@ static struct peerdisc_segment * peerdisc_find ( const char *id ); static int peerdisc_discovered ( struct peerdisc_segment *segment, const char *location ); +/****************************************************************************** + * + * Statistics reporting + * + ****************************************************************************** + */ + +/** + * Report peer discovery statistics + * + * @v intf Interface + * @v peer Selected peer (or NULL) + * @v peers List of available peers + */ +void peerdisc_stat ( struct interface *intf, struct peerdisc_peer *peer, + struct list_head *peers ) { + struct interface *dest; + peerdisc_stat_TYPE ( void * ) *op = + intf_get_dest_op ( intf, peerdisc_stat, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object, peer, peers ); + } else { + /* Default is to do nothing */ + } + + intf_put ( dest ); +} + /****************************************************************************** * * Discovery sockets @@ -408,7 +438,7 @@ static struct peerdisc_segment * peerdisc_create ( const char *id ) { } random_uuid; size_t uuid_len; size_t id_len; - char *uuid; + const char *uuid; char *uuid_copy; char *id_copy; unsigned int i; diff --git a/src/net/peermux.c b/src/net/peermux.c index 634c69992..a391ed373 100644 --- a/src/net/peermux.c +++ b/src/net/peermux.c @@ -24,9 +24,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include #include +#include #include #include @@ -74,6 +76,28 @@ static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) { intf_shutdown ( &peermux->info, rc ); } +/** + * Report progress of PeerDist download + * + * @v peermux PeerDist download multiplexer + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int peermux_progress ( struct peerdist_multiplexer *peermux, + struct job_progress *progress ) { + struct peerdist_statistics *stats = &peermux->stats; + unsigned int percentage; + + /* Construct PeerDist status message */ + if ( stats->total ) { + percentage = ( ( 100 * stats->local ) / stats->total ); + snprintf ( progress->message, sizeof ( progress->message ), + "%3d%% from %d peers", percentage, stats->peers ); + } + + return 0; +} + /** * Receive content information * @@ -274,6 +298,35 @@ peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) { return xfer_buffer ( &peermux->xfer ); } +/** + * Record peer discovery statistics + * + * @v peermblk PeerDist multiplexed block download + * @v peer Selected peer (or NULL) + * @v peers List of available peers + */ +static void peermux_block_stat ( struct peerdist_multiplexed_block *peermblk, + struct peerdisc_peer *peer, + struct list_head *peers ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + struct peerdist_statistics *stats = &peermux->stats; + struct peerdisc_peer *tmp; + unsigned int count = 0; + + /* Record maximum number of available peers */ + list_for_each_entry ( tmp, peers, list ) + count++; + if ( count > stats->peers ) + stats->peers = count; + + /* Update block counts */ + if ( peer ) + stats->local++; + stats->total++; + DBGC2 ( peermux, "PEERMUX %p downloaded %d/%d from %d peers\n", + peermux, stats->local, stats->total, stats->peers ); +} + /** * Close multiplexed block download * @@ -303,6 +356,8 @@ static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk, /** Data transfer interface operations */ static struct interface_operation peermux_xfer_operations[] = { + INTF_OP ( job_progress, struct peerdist_multiplexer *, + peermux_progress ), INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ), }; @@ -330,6 +385,8 @@ static struct interface_operation peermux_block_operations[] = { peermux_block_deliver ), INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *, peermux_block_buffer ), + INTF_OP ( peerdisc_stat, struct peerdist_multiplexed_block *, + peermux_block_stat ), INTF_OP ( intf_close, struct peerdist_multiplexed_block *, peermux_block_close ), }; diff --git a/src/net/stp.c b/src/net/stp.c index d4e65a1a2..3d78400af 100644 --- a/src/net/stp.c +++ b/src/net/stp.c @@ -40,15 +40,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Disambiguate the various error causes */ #define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL ) #define EINFO_ENOTSUP_PROTOCOL \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x02, \ "Non-STP packet received" ) #define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION ) #define EINFO_ENOTSUP_VERSION \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x03, \ "Legacy STP packet received" ) #define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE ) #define EINFO_ENOTSUP_TYPE \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x04, \ "Non-RSTP packet received" ) /** @@ -110,7 +110,7 @@ static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev, "forwarding\n", netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ), stp->flags ); - hello = ( ( ntohs ( stp->hello ) * TICKS_PER_SEC ) / 256 ); + hello = ( ntohs ( stp->hello ) * ( TICKS_PER_SEC / 256 ) ); netdev_link_block ( netdev, ( hello * 2 ) ); rc = -ENETUNREACH; goto done; diff --git a/src/net/tcp.c b/src/net/tcp.c index c69c83b85..cb3b84edd 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -113,6 +113,8 @@ struct tcp_connection { struct process process; /** Retransmission timer */ struct retry_timer timer; + /** Keepalive timer */ + struct retry_timer keepalive; /** Shutdown (TIME_WAIT) timer */ struct retry_timer wait; @@ -177,6 +179,7 @@ static struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.xfer" }; static struct process_descriptor tcp_process_desc; static struct interface_descriptor tcp_xfer_desc; static void tcp_expired ( struct retry_timer *timer, int over ); +static void tcp_keepalive_expired ( struct retry_timer *timer, int over ); static void tcp_wait_expired ( struct retry_timer *timer, int over ); static struct tcp_connection * tcp_demux ( unsigned int local_port ); static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, @@ -284,6 +287,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt ); process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt ); timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt ); + timer_init ( &tcp->keepalive, tcp_keepalive_expired, &tcp->refcnt ); timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt ); tcp->prev_tcp_state = TCP_CLOSED; tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); @@ -380,6 +384,7 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { /* Remove from list and drop reference */ process_del ( &tcp->process ); stop_timer ( &tcp->timer ); + stop_timer ( &tcp->keepalive ); stop_timer ( &tcp->wait ); list_del ( &tcp->list ); ref_put ( &tcp->refcnt ); @@ -394,6 +399,9 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 ); + /* Stop keepalive timer */ + stop_timer ( &tcp->keepalive ); + /* If we have no data remaining to send, start sending FIN */ if ( list_empty ( &tcp->tx_queue ) && ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) { @@ -689,7 +697,7 @@ static void tcp_xmit_sack ( struct tcp_connection *tcp, uint32_t sack_seq ) { wsopt->wsopt.length = sizeof ( wsopt->wsopt ); wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE; spopt = iob_push ( iobuf, sizeof ( *spopt ) ); - memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt ) ); + memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt->nop ) ); spopt->spopt.kind = TCP_OPTION_SACK_PERMITTED; spopt->spopt.length = sizeof ( spopt->spopt ); } @@ -801,6 +809,32 @@ static void tcp_expired ( struct retry_timer *timer, int over ) { } } +/** + * Keepalive timer expired + * + * @v timer Keepalive timer + * @v over Failure indicator + */ +static void tcp_keepalive_expired ( struct retry_timer *timer, + int over __unused ) { + struct tcp_connection *tcp = + container_of ( timer, struct tcp_connection, keepalive ); + + DBGC ( tcp, "TCP %p sending keepalive\n", tcp ); + + /* Reset keepalive timer */ + start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY ); + + /* Send keepalive. We do this only to preserve or restore + * state in intermediate devices (e.g. firewall NAT tables); + * we don't actually care about eliciting a response to verify + * that the peer is still alive. We therefore send just a + * pure ACK, to keep our transmit path simple. + */ + tcp->flags |= TCP_ACK_PENDING; + tcp_xmit ( tcp ); +} + /** * Shutdown timer expired * @@ -904,50 +938,86 @@ static struct tcp_connection * tcp_demux ( unsigned int local_port ) { /** * Parse TCP received options * - * @v tcp TCP connection - * @v data Raw options data - * @v len Raw options length + * @v tcp TCP connection (may be NULL) + * @v tcphdr TCP header + * @v hlen TCP header length * @v options Options structure to fill in + * @ret rc Return status code */ -static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, - size_t len, struct tcp_options *options ) { - const void *end = ( data + len ); +static int tcp_rx_opts ( struct tcp_connection *tcp, + const struct tcp_header *tcphdr, size_t hlen, + struct tcp_options *options ) { + const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ); + const void *end = ( ( ( void * ) tcphdr ) + hlen ); const struct tcp_option *option; unsigned int kind; + size_t remaining; + size_t min; + /* Sanity check */ + assert ( hlen >= sizeof ( *tcphdr ) ); + + /* Parse options */ memset ( options, 0, sizeof ( *options ) ); - while ( data < end ) { + while ( ( remaining = ( end - data ) ) ) { + + /* Extract option code */ option = data; kind = option->kind; + + /* Handle single-byte options */ if ( kind == TCP_OPTION_END ) - return; + break; if ( kind == TCP_OPTION_NOP ) { data++; continue; } + + /* Handle multi-byte options */ + min = sizeof ( *option ); switch ( kind ) { case TCP_OPTION_MSS: - options->mssopt = data; + /* Ignore received MSS */ break; case TCP_OPTION_WS: options->wsopt = data; + min = sizeof ( *options->wsopt ); break; case TCP_OPTION_SACK_PERMITTED: options->spopt = data; + min = sizeof ( *options->spopt ); break; case TCP_OPTION_SACK: /* Ignore received SACKs */ break; case TCP_OPTION_TS: options->tsopt = data; + min = sizeof ( *options->tsopt ); break; default: DBGC ( tcp, "TCP %p received unknown option %d\n", tcp, kind ); break; } + if ( remaining < min ) { + DBGC ( tcp, "TCP %p received truncated option %d\n", + tcp, kind ); + return -EINVAL; + } + if ( option->length < min ) { + DBGC ( tcp, "TCP %p received underlength option %d\n", + tcp, kind ); + return -EINVAL; + } + if ( option->length > remaining ) { + DBGC ( tcp, "TCP %p received overlength option %d\n", + tcp, kind ); + return -EINVAL; + } data += option->length; } + + return 0; } /** @@ -1011,6 +1081,12 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, tcp->snd_win_scale = options->wsopt->scale; tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE; } + DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window " + "x%d, RX window x%d\n", tcp, + ( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ), + ( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ), + ( 1 << tcp->snd_win_scale ), + ( 1 << tcp->rcv_win_scale ) ); } /* Ignore duplicate SYN */ @@ -1063,6 +1139,10 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, /* Update window size */ tcp->snd_win = win; + /* Hold off (or start) the keepalive timer, if applicable */ + if ( ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) + start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY ); + /* Ignore ACKs that don't actually acknowledge any new data. * (In particular, do not stop the retransmission timer; this * avoids creating a sorceror's apprentice syndrome when a @@ -1369,8 +1449,8 @@ static int tcp_rx ( struct io_buffer *iobuf, ack = ntohl ( tcphdr->ack ); raw_win = ntohs ( tcphdr->win ); flags = tcphdr->flags; - tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ), - ( hlen - sizeof ( *tcphdr ) ), &options ); + if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 ) + goto discard; if ( tcp && options.tsopt ) tcp->ts_val = ntohl ( options.tsopt->tsval ); iob_pull ( iobuf, hlen ); diff --git a/src/net/tcp/httpauth.c b/src/net/tcp/httpauth.c index fb6dcd035..2c57e3d48 100644 --- a/src/net/tcp/httpauth.c +++ b/src/net/tcp/httpauth.c @@ -54,46 +54,6 @@ static struct http_authentication * http_authentication ( const char *name ) { return NULL; } -/** An HTTP "WWW-Authenticate" response field */ -struct http_www_authenticate_field { - /** Name */ - const char *name; - /** Offset */ - size_t offset; -}; - -/** Define an HTTP "WWW-Authenticate" response field */ -#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) { \ - .name = #_name, \ - .offset = offsetof ( struct http_transaction, \ - response.auth._name ), \ - } - -/** - * Set HTTP "WWW-Authenticate" response field value - * - * @v http HTTP transaction - * @v field Response field - * @v value Field value - */ -static inline void -http_www_auth_field ( struct http_transaction *http, - struct http_www_authenticate_field *field, char *value ) { - char **ptr; - - ptr = ( ( ( void * ) http ) + field->offset ); - *ptr = value; -} - -/** HTTP "WWW-Authenticate" fields */ -static struct http_www_authenticate_field http_www_auth_fields[] = { - HTTP_WWW_AUTHENTICATE_FIELD ( realm ), - HTTP_WWW_AUTHENTICATE_FIELD ( qop ), - HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ), - HTTP_WWW_AUTHENTICATE_FIELD ( nonce ), - HTTP_WWW_AUTHENTICATE_FIELD ( opaque ), -}; - /** * Parse HTTP "WWW-Authenticate" header * @@ -103,43 +63,38 @@ static struct http_www_authenticate_field http_www_auth_fields[] = { */ static int http_parse_www_authenticate ( struct http_transaction *http, char *line ) { - struct http_www_authenticate_field *field; + struct http_authentication *auth; char *name; - char *key; - char *value; - unsigned int i; + int rc; /* Get scheme name */ name = http_token ( &line, NULL ); if ( ! name ) { DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n", - http, value ); + http, line ); return -EPROTO; } /* Identify scheme */ - http->response.auth.auth = http_authentication ( name ); - if ( ! http->response.auth.auth ) { + auth = http_authentication ( name ); + if ( ! auth ) { DBGC ( http, "HTTP %p unrecognised authentication scheme " "\"%s\"\n", http, name ); - return -ENOTSUP; + /* Ignore; the server may offer other schemes */ + return 0; } - /* Process fields */ - while ( ( key = http_token ( &line, &value ) ) ) { - for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / - sizeof ( http_www_auth_fields[0] ) ) ; i++){ - field = &http_www_auth_fields[i]; - if ( strcasecmp ( key, field->name ) == 0 ) - http_www_auth_field ( http, field, value ); - } - } + /* Use first supported scheme */ + if ( http->response.auth.auth ) + return 0; + http->response.auth.auth = auth; - /* Allow HTTP request to be retried if the request had not - * already tried authentication. - */ - if ( ! http->request.auth.auth ) - http->response.flags |= HTTP_RESPONSE_RETRY; + /* Parse remaining header line */ + if ( ( rc = auth->parse ( http, line ) ) != 0 ) { + DBGC ( http, "HTTP %p could not parse %s WWW-Authenticate " + "\"%s\": %s\n", http, name, line, strerror ( rc ) ); + return rc; + } return 0; } diff --git a/src/net/tcp/httpbasic.c b/src/net/tcp/httpbasic.c index 7ed7de9e7..52a67063d 100644 --- a/src/net/tcp/httpbasic.c +++ b/src/net/tcp/httpbasic.c @@ -42,6 +42,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x01, \ "No username available for Basic authentication" ) +/** + * Parse HTTP "WWW-Authenticate" header for Basic authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_basic_auth ( struct http_transaction *http, + char *line __unused ) { + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + /** * Perform HTTP Basic authentication * @@ -49,7 +68,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret rc Return status code */ static int http_basic_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; + struct http_request_auth_basic *req = &http->request.auth.basic; /* Record username and password */ if ( ! http->uri->user ) { @@ -73,7 +92,7 @@ static int http_basic_authenticate ( struct http_transaction *http ) { */ static int http_format_basic_auth ( struct http_transaction *http, char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; + struct http_request_auth_basic *req = &http->request.auth.basic; size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ + strlen ( req->password ) ); char user_pw[ user_pw_len + 1 /* NUL */ ]; @@ -93,6 +112,7 @@ static int http_format_basic_auth ( struct http_transaction *http, /** HTTP Basic authentication scheme */ struct http_authentication http_basic_auth __http_authentication = { .name = "Basic", + .parse = http_parse_basic_auth, .authenticate = http_basic_authenticate, .format = http_format_basic_auth, }; diff --git a/src/net/tcp/httpblock.c b/src/net/tcp/httpblock.c index e124ad2d6..1abd6b34d 100644 --- a/src/net/tcp/httpblock.c +++ b/src/net/tcp/httpblock.c @@ -114,21 +114,3 @@ int http_block_read_capacity ( struct http_transaction *http, err_open: return rc; } - -/** - * Describe device in ACPI table - * - * @v http HTTP transaction - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -int http_acpi_describe ( struct http_transaction *http, - struct acpi_description_header *acpi, size_t len ) { - - DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", - http ); - ( void ) acpi; - ( void ) len; - return 0; -} diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c index a2c01a418..2cfca9c94 100644 --- a/src/net/tcp/httpconn.c +++ b/src/net/tcp/httpconn.c @@ -277,6 +277,10 @@ int http_connect ( struct interface *xfer, struct uri *uri ) { /* Allocate and initialise structure */ conn = zalloc ( sizeof ( *conn ) ); + if ( ! conn ) { + rc = -ENOMEM; + goto err_alloc; + } ref_init ( &conn->refcnt, http_conn_free ); conn->uri = uri_get ( uri ); conn->scheme = scheme; @@ -310,5 +314,6 @@ int http_connect ( struct interface *xfer, struct uri *uri ) { conn->scheme->name, conn->uri->host, port, strerror ( rc ) ); http_conn_close ( conn, rc ); ref_put ( &conn->refcnt ); + err_alloc: return rc; } diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index f3685de04..b3c9a00e6 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -55,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /* Disambiguate the various error causes */ @@ -110,6 +111,12 @@ static struct profiler http_rx_profiler __profiler = { .name = "http.rx" }; /** Data transfer profiler */ static struct profiler http_xfer_profiler __profiler = { .name = "http.xfer" }; +/** Human-readable error messages */ +struct errortab http_errors[] __errortab = { + __einfo_errortab ( EINFO_EIO_4XX ), + __einfo_errortab ( EINFO_EIO_5XX ), +}; + static struct http_state http_request; static struct http_state http_headers; static struct http_state http_trailers; @@ -189,8 +196,8 @@ char * http_token ( char **line, char **value ) { if ( value ) *value = NULL; - /* Skip any initial whitespace */ - while ( isspace ( **line ) ) + /* Skip any initial whitespace or commas */ + while ( ( isspace ( **line ) ) || ( **line == ',' ) ) (*line)++; /* Check for end of line and record token position */ @@ -201,8 +208,8 @@ char * http_token ( char **line, char **value ) { /* Scan for end of token */ while ( ( c = **line ) ) { - /* Terminate if we hit an unquoted whitespace */ - if ( isspace ( c ) && ! quote ) + /* Terminate if we hit an unquoted whitespace or comma */ + if ( ( isspace ( c ) || ( c == ',' ) ) && ! quote ) break; /* Terminate if we hit a closing quote */ @@ -275,15 +282,9 @@ static void http_close ( struct http_transaction *http, int rc ) { /* Stop timer */ stop_timer ( &http->timer ); - /* Close all interfaces, allowing for the fact that the - * content-decoded and transfer-decoded interfaces may be - * connected to the same object. - */ - intf_shutdown ( &http->conn, rc ); - intf_nullify ( &http->transfer ); - intf_shutdown ( &http->content, rc ); - intf_shutdown ( &http->transfer, rc ); - intf_shutdown ( &http->xfer, rc ); + /* Close all interfaces */ + intfs_shutdown ( rc, &http->conn, &http->transfer, &http->content, + &http->xfer, NULL ); } /** @@ -358,6 +359,9 @@ static void http_step ( struct http_transaction *http ) { if ( ! xfer_window ( &http->conn ) ) return; + /* Notify data transfer interface that window may have changed */ + xfer_window_changed ( &http->xfer ); + /* Do nothing until data transfer interface is ready */ if ( ! xfer_window ( &http->xfer ) ) return; @@ -515,28 +519,11 @@ __weak int http_block_read_capacity ( struct http_transaction *http __unused, return -ENOTSUP; } -/** - * Describe device in ACPI table (when HTTP block device support is not present) - * - * @v http HTTP transaction - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -__weak int http_acpi_describe ( struct http_transaction *http __unused, - struct acpi_description_header *acpi __unused, - size_t len __unused ) { - - return -ENOTSUP; -} - /** HTTP data transfer interface operations */ static struct interface_operation http_xfer_operations[] = { INTF_OP ( block_read, struct http_transaction *, http_block_read ), INTF_OP ( block_read_capacity, struct http_transaction *, http_block_read_capacity ), - INTF_OP ( acpi_describe, struct http_transaction *, - http_acpi_describe ), INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), INTF_OP ( intf_close, struct http_transaction *, http_close ), }; @@ -684,6 +671,51 @@ int http_open ( struct interface *xfer, struct http_method *method, return rc; } +/** + * Redirect HTTP transaction + * + * @v http HTTP transaction + * @v location New location + * @ret rc Return status code + */ +static int http_redirect ( struct http_transaction *http, + const char *location ) { + struct uri *location_uri; + struct uri *resolved_uri; + int rc; + + DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", http, location ); + + /* Parse location URI */ + location_uri = parse_uri ( location ); + if ( ! location_uri ) { + rc = -ENOMEM; + goto err_parse_uri; + } + + /* Resolve as relative to original URI */ + resolved_uri = resolve_uri ( http->uri, location_uri ); + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto err_resolve_uri; + } + + /* Redirect to new URI */ + if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI, + resolved_uri ) ) != 0 ) { + DBGC ( http, "HTTP %p could not redirect: %s\n", + http, strerror ( rc ) ); + goto err_redirect; + } + + err_redirect: + uri_put ( resolved_uri ); + err_resolve_uri: + uri_put ( location_uri ); + err_parse_uri: + return rc; +} + /** * Handle successful transfer completion * @@ -717,14 +749,8 @@ static int http_transfer_complete ( struct http_transaction *http ) { /* Perform redirection, if applicable */ if ( ( location = http->response.location ) ) { - DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", - http, location ); - if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, - location ) ) != 0 ) { - DBGC ( http, "HTTP %p could not redirect: %s\n", - http, strerror ( rc ) ); + if ( ( rc = http_redirect ( http, location ) ) != 0 ) return rc; - } http_close ( http, 0 ); return 0; } @@ -745,15 +771,9 @@ static int http_transfer_complete ( struct http_transaction *http ) { } } - /* Restart content decoding interfaces (which may be attached - * to the same object). - */ - intf_nullify ( &http->content ); - intf_nullify ( &http->transfer ); - intf_restart ( &http->content, http->response.rc ); - intf_restart ( &http->transfer, http->response.rc ); - http->content.desc = &http_content_desc; - http->transfer.desc = &http_transfer_desc; + /* Restart content decoding interfaces */ + intfs_restart ( http->response.rc, &http->content, &http->transfer, + NULL ); intf_plug_plug ( &http->transfer, &http->content ); http->len = 0; assert ( http->remaining == 0 ); @@ -1143,6 +1163,8 @@ static int http_parse_status ( struct http_transaction *http, char *line ) { response_rc = -EIO_OTHER; } http->response.rc = response_rc; + if ( response_rc ) + DBGC ( http, "HTTP %p status %s\n", http, status ); return 0; } @@ -1162,13 +1184,17 @@ static int http_parse_header ( struct http_transaction *http, char *line ) { DBGC2 ( http, "HTTP %p RX %s\n", http, line ); /* Extract header name */ - sep = strstr ( line, ": " ); + sep = strchr ( line, ':' ); if ( ! sep ) { DBGC ( http, "HTTP %p malformed header \"%s\"\n", http, line ); return -EINVAL_HEADER; } *sep = '\0'; - line = ( sep + 2 /* ": " */ ); + + /* Extract remainder of line */ + line = ( sep + 1 ); + while ( isspace ( *line ) ) + line++; /* Process header, if recognised */ for_each_table_entry ( header, HTTP_RESPONSE_HEADERS ) { @@ -1276,19 +1302,17 @@ http_response_transfer_encoding __http_response_header = { * @ret rc Return status code */ static int http_parse_connection ( struct http_transaction *http, char *line ) { + char *token; /* Check for known connection intentions */ - if ( strcasecmp ( line, "keep-alive" ) == 0 ) { - http->response.flags |= HTTP_RESPONSE_KEEPALIVE; - return 0; - } - if ( strcasecmp ( line, "close" ) == 0 ) { - http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; - return 0; + while ( ( token = http_token ( &line, NULL ) ) ) { + if ( strcasecmp ( token, "keep-alive" ) == 0 ) + http->response.flags |= HTTP_RESPONSE_KEEPALIVE; + if ( strcasecmp ( token, "close" ) == 0 ) + http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; } - DBGC ( http, "HTTP %p unrecognised Connection \"%s\"\n", http, line ); - return -ENOTSUP_CONNECTION; + return 0; } /** HTTP "Connection" header */ diff --git a/src/net/tcp/httpdigest.c b/src/net/tcp/httpdigest.c index 626dd7e9d..4074078c7 100644 --- a/src/net/tcp/httpdigest.c +++ b/src/net/tcp/httpdigest.c @@ -45,6 +45,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EACCES, 0x01, \ "No username available for Digest authentication" ) +/** An HTTP Digest "WWW-Authenticate" response field */ +struct http_digest_field { + /** Name */ + const char *name; + /** Offset */ + size_t offset; +}; + +/** Define an HTTP Digest "WWW-Authenticate" response field */ +#define HTTP_DIGEST_FIELD( _name ) { \ + .name = #_name, \ + .offset = offsetof ( struct http_transaction, \ + response.auth.digest._name ), \ + } + +/** + * Set HTTP Digest "WWW-Authenticate" response field value + * + * @v http HTTP transaction + * @v field Response field + * @v value Field value + */ +static inline void +http_digest_field ( struct http_transaction *http, + struct http_digest_field *field, char *value ) { + char **ptr; + + ptr = ( ( ( void * ) http ) + field->offset ); + *ptr = value; +} + +/** HTTP Digest "WWW-Authenticate" fields */ +static struct http_digest_field http_digest_fields[] = { + HTTP_DIGEST_FIELD ( realm ), + HTTP_DIGEST_FIELD ( qop ), + HTTP_DIGEST_FIELD ( algorithm ), + HTTP_DIGEST_FIELD ( nonce ), + HTTP_DIGEST_FIELD ( opaque ), +}; + +/** + * Parse HTTP "WWW-Authenticate" header for Digest authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_digest_auth ( struct http_transaction *http, + char *line ) { + struct http_digest_field *field; + char *key; + char *value; + unsigned int i; + + /* Process fields */ + while ( ( key = http_token ( &line, &value ) ) ) { + for ( i = 0 ; i < ( sizeof ( http_digest_fields ) / + sizeof ( http_digest_fields[0] ) ) ; i++){ + field = &http_digest_fields[i]; + if ( strcasecmp ( key, field->name ) == 0 ) + http_digest_field ( http, field, value ); + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + /** * Initialise HTTP Digest * @@ -95,13 +168,14 @@ static void http_digest_final ( struct md5_context *ctx, char *out, * @ret rc Return status code */ static int http_digest_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; + struct http_request_auth_digest *req = &http->request.auth.digest; + struct http_response_auth_digest *rsp = &http->response.auth.digest; char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; static const char md5sess[] = "MD5-sess"; static const char md5[] = "MD5"; struct md5_context ctx; + const char *password; /* Check for required response parameters */ if ( ! rsp->realm ) { @@ -122,7 +196,7 @@ static int http_digest_authenticate ( struct http_transaction *http ) { return -EACCES_USERNAME; } req->username = http->uri->user; - req->password = ( http->uri->password ? http->uri->password : "" ); + password = ( http->uri->password ? http->uri->password : "" ); /* Handle quality of protection */ if ( rsp->qop ) { @@ -146,7 +220,7 @@ static int http_digest_authenticate ( struct http_transaction *http ) { http_digest_init ( &ctx ); http_digest_update ( &ctx, req->username ); http_digest_update ( &ctx, rsp->realm ); - http_digest_update ( &ctx, req->password ); + http_digest_update ( &ctx, password ); http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); if ( req->algorithm == md5sess ) { http_digest_init ( &ctx ); @@ -187,8 +261,8 @@ static int http_digest_authenticate ( struct http_transaction *http ) { */ static int http_format_digest_auth ( struct http_transaction *http, char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; + struct http_request_auth_digest *req = &http->request.auth.digest; + struct http_response_auth_digest *rsp = &http->response.auth.digest; size_t used = 0; /* Sanity checks */ @@ -225,6 +299,7 @@ static int http_format_digest_auth ( struct http_transaction *http, /** HTTP Digest authentication scheme */ struct http_authentication http_digest_auth __http_authentication = { .name = "Digest", + .parse = http_parse_digest_auth, .authenticate = http_digest_authenticate, .format = http_format_digest_auth, }; diff --git a/src/net/tcp/httpgce.c b/src/net/tcp/httpgce.c new file mode 100644 index 000000000..c5d879028 --- /dev/null +++ b/src/net/tcp/httpgce.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 + * + * Google Compute Engine (GCE) metadata retrieval + * + * For some unspecified "security" reason, the Google Compute Engine + * metadata server will refuse any requests that do not include the + * non-standard HTTP header "Metadata-Flavor: Google". + */ + +#include +#include +#include + +/** Metadata host name + * + * This is used to identify metadata requests, in the absence of any + * more robust mechanism. + */ +#define GCE_METADATA_HOST_NAME "metadata.google.internal" + +/** + * Construct HTTP "Metadata-Flavor" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_metadata_flavor ( struct http_transaction *http, + char *buf, size_t len ) { + + /* Do nothing unless this appears to be a Google Compute + * Engine metadata request. + */ + if ( strcasecmp ( http->request.host, GCE_METADATA_HOST_NAME ) != 0 ) + return 0; + + /* Construct host URI */ + return snprintf ( buf, len, "Google" ); +} + +/** HTTP "Metadata-Flavor" header */ +struct http_request_header http_request_metadata_flavor __http_request_header ={ + .name = "Metadata-Flavor", + .format = http_format_metadata_flavor, +}; diff --git a/src/net/tcp/httpntlm.c b/src/net/tcp/httpntlm.c new file mode 100644 index 000000000..25187bd19 --- /dev/null +++ b/src/net/tcp/httpntlm.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 + * + * Hyper Text Transfer Protocol (HTTP) NTLM authentication + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct http_authentication http_ntlm_auth __http_authentication; + +/** Workstation name used for NTLM authentication */ +static const char http_ntlm_workstation[] = "iPXE"; + +/** + * Parse HTTP "WWW-Authenticate" header for NTLM authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_ntlm_auth ( struct http_transaction *http, char *line ) { + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + char *copy; + int len; + int rc; + + /* Create temporary copy of Base64-encoded challenge message */ + copy = strdup ( line ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Decode challenge message, overwriting the original */ + len = base64_decode ( copy, line, strlen ( line ) ); + if ( len < 0 ) { + rc = len; + DBGC ( http, "HTTP %p could not decode NTLM challenge " + "\"%s\": %s\n", http, copy, strerror ( rc ) ); + goto err_decode; + } + + /* Parse challenge, if present */ + if ( len ) { + rsp->challenge = ( ( void * ) line ); + if ( ( rc = ntlm_challenge ( rsp->challenge, len, + &rsp->info ) ) != 0 ) { + DBGC ( http, "HTTP %p could not parse NTLM challenge: " + "%s\n", http, strerror ( rc ) ); + goto err_challenge; + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. Note that NTLM requires an + * additional round trip to obtain the challenge message, + * which is not present in the initial WWW-Authenticate. + */ + if ( ( http->request.auth.auth == NULL ) || + ( ( http->request.auth.auth == &http_ntlm_auth ) && + ( http->request.auth.ntlm.len == 0 ) && len ) ) { + http->response.flags |= HTTP_RESPONSE_RETRY; + } + + /* Success */ + rc = 0; + + err_challenge: + err_decode: + free ( copy ); + err_alloc: + return rc; +} + +/** + * Perform HTTP NTLM authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_ntlm_authenticate ( struct http_transaction *http ) { + struct http_request_auth_ntlm *req = &http->request.auth.ntlm; + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + struct ntlm_key key; + const char *domain; + char *username; + const char *password; + + /* If we have no challenge yet, then just send a Negotiate message */ + if ( ! rsp->challenge ) { + DBGC ( http, "HTTP %p sending NTLM Negotiate\n", http ); + return 0; + } + + /* Record username */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for NTLM " + "authentication\n", http ); + return -EACCES; + } + req->username = http->uri->user; + password = ( http->uri->password ? http->uri->password : "" ); + + /* Split NetBIOS [domain\]username */ + username = ( ( char * ) req->username ); + domain = netbios_domain ( &username ); + + /* Generate key */ + ntlm_key ( domain, username, password, &key ); + + /* Generate responses */ + ntlm_response ( &rsp->info, &key, NULL, &req->lm, &req->nt ); + + /* Calculate Authenticate message length */ + req->len = ntlm_authenticate_len ( &rsp->info, domain, username, + http_ntlm_workstation ); + + /* Restore NetBIOS [domain\]username */ + netbios_domain_undo ( domain, username ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for NTLM authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_ntlm_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth_ntlm *req = &http->request.auth.ntlm; + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + struct ntlm_authenticate *auth; + const char *domain; + char *username; + size_t check; + + /* If we have no challenge yet, then just send a Negotiate message */ + if ( ! rsp->challenge ) { + return base64_encode ( &ntlm_negotiate, + sizeof ( ntlm_negotiate ), buf, len ); + } + + /* Skip allocation if just calculating length */ + if ( ! len ) + return base64_encoded_len ( req->len ); + + /* Allocate temporary buffer for Authenticate message */ + auth = malloc ( req->len ); + if ( ! auth ) + return -ENOMEM; + + /* Split NetBIOS [domain\]username */ + username = ( ( char * ) req->username ); + domain = netbios_domain ( &username ); + + /* Construct raw Authenticate message */ + check = ntlm_authenticate ( &rsp->info, domain, username, + http_ntlm_workstation, &req->lm, + &req->nt, auth ); + assert ( check == req->len ); + + /* Restore NetBIOS [domain\]username */ + netbios_domain_undo ( domain, username ); + + /* Base64-encode Authenticate message */ + len = base64_encode ( auth, req->len, buf, len ); + + /* Free raw Authenticate message */ + free ( auth ); + + return len; +} + +/** HTTP NTLM authentication scheme */ +struct http_authentication http_ntlm_auth __http_authentication = { + .name = "NTLM", + .parse = http_parse_ntlm_auth, + .authenticate = http_ntlm_authenticate, + .format = http_format_ntlm_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_ntlm_auth ); +REQUIRE_OBJECT ( httpauth ); diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 019a4c14e..f8379b285 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -109,10 +109,6 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); __einfo_error ( EINFO_ENOTSUP_TARGET_STATUS ) #define EINFO_ENOTSUP_TARGET_STATUS \ __einfo_uniqify ( EINFO_ENOTSUP, 0x04, "Unsupported target status" ) -#define ENOTSUP_NOP_IN \ - __einfo_error ( EINFO_ENOTSUP_NOP_IN ) -#define EINFO_ENOTSUP_NOP_IN \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x05, "Unsupported NOP-In received" ) #define EPERM_INITIATOR_AUTHENTICATION \ __einfo_error ( EINFO_EPERM_INITIATOR_AUTHENTICATION ) #define EINFO_EPERM_INITIATOR_AUTHENTICATION \ @@ -231,9 +227,8 @@ static void iscsi_close ( struct iscsi_session *iscsi, int rc ) { process_del ( &iscsi->process ); /* Shut down interfaces */ - intf_shutdown ( &iscsi->socket, rc ); - intf_shutdown ( &iscsi->control, rc ); - intf_shutdown ( &iscsi->data, rc ); + intfs_shutdown ( rc, &iscsi->socket, &iscsi->control, &iscsi->data, + NULL ); } /** @@ -621,12 +616,15 @@ static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, * sent as ping requests, but we can happily accept NOP-Ins * sent merely to update CmdSN. */ - if ( nop_in->ttt != htonl ( ISCSI_TAG_RESERVED ) ) { - DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT " - "%08x\n", iscsi, ntohl ( nop_in->ttt ) ); - return -ENOTSUP_NOP_IN; - } + if ( nop_in->ttt == htonl ( ISCSI_TAG_RESERVED ) ) + return 0; + /* Ignore any other NOP-Ins. The target may eventually + * disconnect us for failing to respond, but this minimises + * unnecessary connection closures. + */ + DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT %08x\n", + iscsi, ntohl ( nop_in->ttt ) ); return 0; } @@ -646,12 +644,12 @@ static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, * * HeaderDigest=None * DataDigest=None - * MaxConnections is irrelevant; we make only one connection anyway [4] + * MaxConnections=1 (irrelevant; we make only one connection anyway) [4] * InitialR2T=Yes [1] - * ImmediateData is irrelevant; we never send immediate data [4] + * ImmediateData=No (irrelevant; we never send immediate data) [4] * MaxRecvDataSegmentLength=8192 (default; we don't care) [3] * MaxBurstLength=262144 (default; we don't care) [3] - * FirstBurstLength=262144 (default; we don't care) + * FirstBurstLength=65536 (irrelevant due to other settings) [5] * DefaultTime2Wait=0 [2] * DefaultTime2Retain=0 [2] * MaxOutstandingR2T=1 @@ -676,6 +674,11 @@ static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, * these parameters, but some targets (notably a QNAP TS-639Pro) fail * unless they are supplied, so we explicitly specify the default * values. + * + * [5] FirstBurstLength is defined to be irrelevant since we already + * force InitialR2T=Yes and ImmediateData=No, but some targets + * (notably LIO as of kernel 4.11) fail unless it is specified, so we + * explicitly specify the default value. */ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, void *data, size_t len ) { @@ -734,13 +737,14 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, "ImmediateData=No%c" "MaxRecvDataSegmentLength=8192%c" "MaxBurstLength=262144%c" + "FirstBurstLength=65536%c" "DefaultTime2Wait=0%c" "DefaultTime2Retain=0%c" "MaxOutstandingR2T=1%c" "DataPDUInOrder=Yes%c" "DataSequenceInOrder=Yes%c" "ErrorRecoveryLevel=0%c", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); } return used; @@ -1710,6 +1714,7 @@ static int iscsi_vredirect ( struct iscsi_session *iscsi, int type, va_list args ) { va_list tmp; struct sockaddr *peer; + int rc; /* Intercept redirects to a LOCATION_SOCKET and record the IP * address for the iBFT. This is a bit of a hack, but avoids @@ -1725,7 +1730,15 @@ static int iscsi_vredirect ( struct iscsi_session *iscsi, int type, va_end ( tmp ); } - return xfer_vreopen ( &iscsi->socket, type, args ); + /* Redirect to new location */ + if ( ( rc = xfer_vreopen ( &iscsi->socket, type, args ) ) != 0 ) + goto err; + + return 0; + + err: + iscsi_close ( iscsi, rc ); + return rc; } /** iSCSI socket interface operations */ @@ -1803,12 +1816,23 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi, return iscsi->itt; } +/** + * Get iSCSI ACPI descriptor + * + * @v iscsi iSCSI session + * @ret desc ACPI descriptor + */ +static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) { + + return &iscsi->desc; +} + /** iSCSI SCSI command-issuing interface operations */ static struct interface_operation iscsi_control_op[] = { INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ), INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ), INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), - INTF_OP ( acpi_describe, struct iscsi_session *, ibft_describe ), + INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ), }; /** iSCSI SCSI command-issuing interface descriptor */ @@ -1897,6 +1921,7 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi, char rp_copy[ strlen ( root_path ) + 1 ]; char *rp_comp[NUM_RP_COMPONENTS]; char *rp = rp_copy; + int skip = 0; int i = 0; int rc; @@ -1906,11 +1931,15 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi, rp_comp[i++] = rp; if ( i == NUM_RP_COMPONENTS ) break; - for ( ; *rp != ':' ; rp++ ) { + for ( ; ( ( *rp != ':' ) || skip ) ; rp++ ) { if ( ! *rp ) { DBGC ( iscsi, "iSCSI %p root path \"%s\" " "too short\n", iscsi, root_path ); return -EINVAL_ROOT_PATH_TOO_SHORT; + } else if ( *rp == '[' ) { + skip = 1; + } else if ( *rp == ']' ) { + skip = 0; } } *(rp++) = '\0'; @@ -2057,6 +2086,7 @@ static int iscsi_open ( struct interface *parent, struct uri *uri ) { intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt ); process_init_stopped ( &iscsi->process, &iscsi_process_desc, &iscsi->refcnt ); + acpi_init ( &iscsi->desc, &ibft_model, &iscsi->refcnt ); /* Parse root path */ if ( ( rc = iscsi_parse_root_path ( iscsi, uri->opaque ) ) != 0 ) diff --git a/src/net/tcp/oncrpc.c b/src/net/tcp/oncrpc.c index 6469867e9..cb66aeb85 100644 --- a/src/net/tcp/oncrpc.c +++ b/src/net/tcp/oncrpc.c @@ -128,7 +128,6 @@ void oncrpc_init_session ( struct oncrpc_session *session, int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, uint32_t proc_name, const struct oncrpc_field fields[] ) { - int rc; size_t frame_size; struct io_buffer *io_buf; @@ -161,11 +160,7 @@ int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, oncrpc_iob_add_fields ( io_buf, header ); oncrpc_iob_add_fields ( io_buf, fields ); - rc = xfer_deliver_iob ( intf, io_buf ); - if ( rc != 0 ) - free_iob ( io_buf ); - - return rc; + return xfer_deliver_iob ( intf, iob_disown ( io_buf ) ); } size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) { diff --git a/src/net/tcpip.c b/src/net/tcpip.c index c9e4ee789..cc7d02005 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -144,8 +144,7 @@ size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ) { return 0; /* Calculate MTU */ - mtu = ( netdev->max_pkt_len - netdev->ll_protocol->ll_header_len - - tcpip_net->header_len ); + mtu = ( netdev->mtu - tcpip_net->header_len ); return mtu; } diff --git a/src/net/tls.c b/src/net/tls.c index db01fb291..9d994cd77 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -162,14 +162,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPERM_CLIENT_CERT \ __einfo_uniqify ( EINFO_EPERM, 0x03, \ "No suitable client certificate available" ) +#define EPERM_RENEG_INSECURE __einfo_error ( EINFO_EPERM_RENEG_INSECURE ) +#define EINFO_EPERM_RENEG_INSECURE \ + __einfo_uniqify ( EINFO_EPERM, 0x04, \ + "Secure renegotiation not supported" ) +#define EPERM_RENEG_VERIFY __einfo_error ( EINFO_EPERM_RENEG_VERIFY ) +#define EINFO_EPERM_RENEG_VERIFY \ + __einfo_uniqify ( EINFO_EPERM, 0x05, \ + "Secure renegotiation verification failed" ) #define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION ) #define EINFO_EPROTO_VERSION \ __einfo_uniqify ( EINFO_EPROTO, 0x01, \ "Illegal protocol version upgrade" ) -static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, +static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ); -static void tls_clear_cipher ( struct tls_session *tls, +static void tls_clear_cipher ( struct tls_connection *tls, struct tls_cipherspec *cipherspec ); /****************************************************************************** @@ -217,12 +225,12 @@ static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) { } /** - * Determine if TLS session is ready for application data + * Determine if TLS connection is ready for application data * - * @v tls TLS session - * @ret is_ready TLS session is ready + * @v tls TLS connection + * @ret is_ready TLS connection is ready */ -static int tls_ready ( struct tls_session *tls ) { +static int tls_ready ( struct tls_connection *tls ) { return ( ( ! is_pending ( &tls->client_negotiation ) ) && ( ! is_pending ( &tls->server_negotiation ) ) ); } @@ -300,13 +308,13 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { */ /** - * Free TLS session + * Free TLS connection * * @v refcnt Reference counter */ static void free_tls ( struct refcnt *refcnt ) { - struct tls_session *tls = - container_of ( refcnt, struct tls_session, refcnt ); + struct tls_connection *tls = + container_of ( refcnt, struct tls_connection, refcnt ); struct io_buffer *iobuf; struct io_buffer *tmp; @@ -327,12 +335,12 @@ static void free_tls ( struct refcnt *refcnt ) { } /** - * Finish with TLS session + * Finish with TLS connection * - * @v tls TLS session + * @v tls TLS connection * @v rc Status code */ -static void tls_close ( struct tls_session *tls, int rc ) { +static void tls_close ( struct tls_connection *tls, int rc ) { /* Remove pending operations, if applicable */ pending_put ( &tls->client_negotiation ); @@ -357,12 +365,12 @@ static void tls_close ( struct tls_session *tls, int rc ) { /** * Generate random data * - * @v tls TLS session + * @v tls TLS connection * @v data Buffer to fill * @v len Length of buffer * @ret rc Return status code */ -static int tls_generate_random ( struct tls_session *tls, +static int tls_generate_random ( struct tls_connection *tls, void *data, size_t len ) { int rc; @@ -399,7 +407,7 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest, /** * Generate secure pseudo-random data using a single hash function * - * @v tls TLS session + * @v tls TLS connection * @v digest Hash function to use * @v secret Secret * @v secret_len Length of secret @@ -407,7 +415,7 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest, * @v out_len Length of output buffer * @v seeds ( data, len ) pairs of seed data, terminated by NULL */ -static void tls_p_hash_va ( struct tls_session *tls, +static void tls_p_hash_va ( struct tls_connection *tls, struct digest_algorithm *digest, void *secret, size_t secret_len, void *out, size_t out_len, @@ -468,15 +476,15 @@ static void tls_p_hash_va ( struct tls_session *tls, /** * Generate secure pseudo-random data * - * @v tls TLS session + * @v tls TLS connection * @v secret Secret * @v secret_len Length of secret * @v out Output buffer * @v out_len Length of output buffer * @v ... ( data, len ) pairs of seed data, terminated by NULL */ -static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len, - void *out, size_t out_len, ... ) { +static void tls_prf ( struct tls_connection *tls, void *secret, + size_t secret_len, void *out, size_t out_len, ... ) { va_list seeds; va_list tmp; size_t subsecret_len; @@ -545,12 +553,12 @@ static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len, /** * Generate master secret * - * @v tls TLS session + * @v tls TLS connection * * The pre-master secret and the client and server random values must * already be known. */ -static void tls_generate_master_secret ( struct tls_session *tls ) { +static void tls_generate_master_secret ( struct tls_connection *tls ) { DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); DBGC_HD ( tls, &tls->pre_master_secret, sizeof ( tls->pre_master_secret ) ); @@ -573,11 +581,11 @@ static void tls_generate_master_secret ( struct tls_session *tls ) { /** * Generate key material * - * @v tls TLS session + * @v tls TLS connection * * The master secret must already be known. */ -static int tls_generate_keys ( struct tls_session *tls ) { +static int tls_generate_keys ( struct tls_connection *tls ) { struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending; struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; size_t hash_size = tx_cipherspec->suite->digest->digestsize; @@ -693,7 +701,7 @@ tls_find_cipher_suite ( unsigned int cipher_suite ) { * * @v cipherspec TLS cipher specification */ -static void tls_clear_cipher ( struct tls_session *tls __unused, +static void tls_clear_cipher ( struct tls_connection *tls __unused, struct tls_cipherspec *cipherspec ) { if ( cipherspec->suite ) { @@ -708,12 +716,12 @@ static void tls_clear_cipher ( struct tls_session *tls __unused, /** * Set cipher suite * - * @v tls TLS session + * @v tls TLS connection * @v cipherspec TLS cipher specification * @v suite Cipher suite * @ret rc Return status code */ -static int tls_set_cipher ( struct tls_session *tls, +static int tls_set_cipher ( struct tls_connection *tls, struct tls_cipherspec *cipherspec, struct tls_cipher_suite *suite ) { struct pubkey_algorithm *pubkey = suite->pubkey; @@ -751,11 +759,11 @@ static int tls_set_cipher ( struct tls_session *tls, /** * Select next cipher suite * - * @v tls TLS session + * @v tls TLS connection * @v cipher_suite Cipher suite specification * @ret rc Return status code */ -static int tls_select_cipher ( struct tls_session *tls, +static int tls_select_cipher ( struct tls_connection *tls, unsigned int cipher_suite ) { struct tls_cipher_suite *suite; int rc; @@ -786,12 +794,12 @@ static int tls_select_cipher ( struct tls_session *tls, /** * Activate next cipher suite * - * @v tls TLS session + * @v tls TLS connection * @v pending Pending cipher specification * @v active Active cipher specification to replace * @ret rc Return status code */ -static int tls_change_cipher ( struct tls_session *tls, +static int tls_change_cipher ( struct tls_connection *tls, struct tls_cipherspec *pending, struct tls_cipherspec *active ) { @@ -850,11 +858,11 @@ tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, /** * Add handshake record to verification hash * - * @v tls TLS session + * @v tls TLS connection * @v data Handshake record * @v len Length of handshake record */ -static void tls_add_handshake ( struct tls_session *tls, +static void tls_add_handshake ( struct tls_connection *tls, const void *data, size_t len ) { digest_update ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx, @@ -866,13 +874,13 @@ static void tls_add_handshake ( struct tls_session *tls, /** * Calculate handshake verification hash * - * @v tls TLS session + * @v tls TLS connection * @v out Output buffer * * Calculates the MD5+SHA1 or SHA256 digest over all handshake * messages seen so far. */ -static void tls_verify_handshake ( struct tls_session *tls, void *out ) { +static void tls_verify_handshake ( struct tls_connection *tls, void *out ) { struct digest_algorithm *digest = tls->handshake_digest; uint8_t ctx[ digest->ctxsize ]; @@ -887,24 +895,48 @@ static void tls_verify_handshake ( struct tls_session *tls, void *out ) { ****************************************************************************** */ +/** + * Restart negotiation + * + * @v tls TLS connection + */ +static void tls_restart ( struct tls_connection *tls ) { + + /* Sanity check */ + assert ( ! tls->tx_pending ); + assert ( ! is_pending ( &tls->client_negotiation ) ); + assert ( ! is_pending ( &tls->server_negotiation ) ); + + /* (Re)initialise handshake context */ + digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); + digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx ); + tls->handshake_digest = &sha256_algorithm; + tls->handshake_ctx = tls->handshake_sha256_ctx; + + /* (Re)start negotiation */ + tls->tx_pending = TLS_TX_CLIENT_HELLO; + pending_get ( &tls->client_negotiation ); + pending_get ( &tls->server_negotiation ); +} + /** * Resume TX state machine * - * @v tls TLS session + * @v tls TLS connection */ -static void tls_tx_resume ( struct tls_session *tls ) { +static void tls_tx_resume ( struct tls_connection *tls ) { process_add ( &tls->process ); } /** * Transmit Handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_send_handshake ( struct tls_session *tls, +static int tls_send_handshake ( struct tls_connection *tls, void *data, size_t len ) { /* Add to handshake digest */ @@ -917,10 +949,10 @@ static int tls_send_handshake ( struct tls_session *tls, /** * Transmit Client Hello record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_client_hello ( struct tls_session *tls ) { +static int tls_send_client_hello ( struct tls_connection *tls ) { struct { uint32_t type_length; uint16_t version; @@ -954,6 +986,13 @@ static int tls_send_client_hello ( struct tls_session *tls ) { struct tls_signature_hash_id code[TLS_NUM_SIG_HASH_ALGORITHMS]; } __attribute__ (( packed )) signature_algorithms; + uint16_t renegotiation_info_type; + uint16_t renegotiation_info_len; + struct { + uint8_t len; + uint8_t data[ tls->secure_renegotiation ? + sizeof ( tls->verify.client ) :0]; + } __attribute__ (( packed )) renegotiation_info; } __attribute__ (( packed )) extensions; } __attribute__ (( packed )) hello; struct tls_cipher_suite *suite; @@ -995,6 +1034,14 @@ static int tls_send_client_hello ( struct tls_session *tls ) { = htons ( sizeof ( hello.extensions.signature_algorithms.code)); i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS ) hello.extensions.signature_algorithms.code[i++] = sighash->code; + hello.extensions.renegotiation_info_type + = htons ( TLS_RENEGOTIATION_INFO ); + hello.extensions.renegotiation_info_len + = htons ( sizeof ( hello.extensions.renegotiation_info ) ); + hello.extensions.renegotiation_info.len + = sizeof ( hello.extensions.renegotiation_info.data ); + memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client, + sizeof ( hello.extensions.renegotiation_info.data ) ); return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); } @@ -1002,10 +1049,10 @@ static int tls_send_client_hello ( struct tls_session *tls ) { /** * Transmit Certificate record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_certificate ( struct tls_session *tls ) { +static int tls_send_certificate ( struct tls_connection *tls ) { struct { uint32_t type_length; tls24_t length; @@ -1048,10 +1095,10 @@ static int tls_send_certificate ( struct tls_session *tls ) { /** * Transmit Client Key Exchange record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_client_key_exchange ( struct tls_session *tls ) { +static int tls_send_client_key_exchange ( struct tls_connection *tls ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx ); @@ -1092,10 +1139,10 @@ static int tls_send_client_key_exchange ( struct tls_session *tls ) { /** * Transmit Certificate Verify record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_certificate_verify ( struct tls_session *tls ) { +static int tls_send_certificate_verify ( struct tls_connection *tls ) { struct digest_algorithm *digest = tls->handshake_digest; struct x509_certificate *cert = tls->cert; struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey; @@ -1182,10 +1229,10 @@ static int tls_send_certificate_verify ( struct tls_session *tls ) { /** * Transmit Change Cipher record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_change_cipher ( struct tls_session *tls ) { +static int tls_send_change_cipher ( struct tls_connection *tls ) { static const uint8_t change_cipher[1] = { 1 }; return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER, change_cipher, sizeof ( change_cipher ) ); @@ -1194,27 +1241,31 @@ static int tls_send_change_cipher ( struct tls_session *tls ) { /** * Transmit Finished record * - * @v tls TLS session + * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_finished ( struct tls_session *tls ) { +static int tls_send_finished ( struct tls_connection *tls ) { struct digest_algorithm *digest = tls->handshake_digest; struct { uint32_t type_length; - uint8_t verify_data[12]; + uint8_t verify_data[ sizeof ( tls->verify.client ) ]; } __attribute__ (( packed )) finished; uint8_t digest_out[ digest->digestsize ]; int rc; + /* Construct client verification data */ + tls_verify_handshake ( tls, digest_out ); + tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), + tls->verify.client, sizeof ( tls->verify.client ), + "client finished", digest_out, sizeof ( digest_out ) ); + /* Construct record */ memset ( &finished, 0, sizeof ( finished ) ); finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) | htonl ( sizeof ( finished ) - sizeof ( finished.type_length ) ) ); - tls_verify_handshake ( tls, digest_out ); - tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - finished.verify_data, sizeof ( finished.verify_data ), - "client finished", digest_out, sizeof ( digest_out ) ); + memcpy ( finished.verify_data, tls->verify.client, + sizeof ( finished.verify_data ) ); /* Transmit record */ if ( ( rc = tls_send_handshake ( tls, &finished, @@ -1230,12 +1281,12 @@ static int tls_send_finished ( struct tls_session *tls ) { /** * Receive new Change Cipher record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_new_change_cipher ( struct tls_session *tls, +static int tls_new_change_cipher ( struct tls_connection *tls, const void *data, size_t len ) { int rc; @@ -1259,22 +1310,21 @@ static int tls_new_change_cipher ( struct tls_session *tls, /** * Receive new Alert record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_new_alert ( struct tls_session *tls, const void *data, +static int tls_new_alert ( struct tls_connection *tls, const void *data, size_t len ) { const struct { uint8_t level; uint8_t description; char next[0]; } __attribute__ (( packed )) *alert = data; - const void *end = alert->next; /* Sanity check */ - if ( end != ( data + len ) ) { + if ( sizeof ( *alert ) != len ) { DBGC ( tls, "TLS %p received overlength Alert\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_ALERT; @@ -1297,37 +1347,138 @@ static int tls_new_alert ( struct tls_session *tls, const void *data, } /** - * Receive new Server Hello handshake record + * Receive new Hello Request handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext handshake record * @v len Length of plaintext handshake record * @ret rc Return status code */ -static int tls_new_server_hello ( struct tls_session *tls, +static int tls_new_hello_request ( struct tls_connection *tls, + const void *data __unused, + size_t len __unused ) { + + /* Ignore if a handshake is in progress */ + if ( ! tls_ready ( tls ) ) { + DBGC ( tls, "TLS %p ignoring Hello Request\n", tls ); + return 0; + } + + /* Fail unless server supports secure renegotiation */ + if ( ! tls->secure_renegotiation ) { + DBGC ( tls, "TLS %p refusing to renegotiate insecurely\n", + tls ); + return -EPERM_RENEG_INSECURE; + } + + /* Restart negotiation */ + tls_restart ( tls ); + + return 0; +} + +/** + * Receive new Server Hello handshake record + * + * @v tls TLS connection + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_server_hello ( struct tls_connection *tls, const void *data, size_t len ) { const struct { uint16_t version; uint8_t random[32]; uint8_t session_id_len; - char next[0]; + uint8_t session_id[0]; } __attribute__ (( packed )) *hello_a = data; + const uint8_t *session_id; const struct { - uint8_t session_id[hello_a->session_id_len]; uint16_t cipher_suite; uint8_t compression_method; char next[0]; - } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next; - const void *end = hello_b->next; + } __attribute__ (( packed )) *hello_b; + const struct { + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *exts; + const struct { + uint16_t type; + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *ext; + const struct { + uint8_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *reneg = NULL; uint16_t version; + size_t exts_len; + size_t ext_len; + size_t remaining; int rc; - /* Sanity check */ - if ( end > ( data + len ) ) { + /* Parse header */ + if ( ( sizeof ( *hello_a ) > len ) || + ( hello_a->session_id_len > ( len - sizeof ( *hello_a ) ) ) || + ( sizeof ( *hello_b ) > ( len - sizeof ( *hello_a ) - + hello_a->session_id_len ) ) ) { DBGC ( tls, "TLS %p received underlength Server Hello\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_HELLO; } + session_id = hello_a->session_id; + hello_b = ( ( void * ) ( session_id + hello_a->session_id_len ) ); + + /* Parse extensions, if present */ + remaining = ( len - sizeof ( *hello_a ) - hello_a->session_id_len - + sizeof ( *hello_b ) ); + if ( remaining ) { + + /* Parse extensions length */ + exts = ( ( void * ) hello_b->next ); + if ( ( sizeof ( *exts ) > remaining ) || + ( ( exts_len = ntohs ( exts->len ) ) > + ( remaining - sizeof ( *exts ) ) ) ) { + DBGC ( tls, "TLS %p received underlength extensions\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_HELLO; + } + + /* Parse extensions */ + for ( ext = ( ( void * ) exts->data ), remaining = exts_len ; + remaining ; + ext = ( ( ( void * ) ext ) + sizeof ( *ext ) + ext_len ), + remaining -= ( sizeof ( *ext ) + ext_len ) ) { + + /* Parse extension length */ + if ( ( sizeof ( *ext ) > remaining ) || + ( ( ext_len = ntohs ( ext->len ) ) > + ( remaining - sizeof ( *ext ) ) ) ) { + DBGC ( tls, "TLS %p received underlength " + "extension\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_HELLO; + } + + /* Record known extensions */ + switch ( ext->type ) { + case htons ( TLS_RENEGOTIATION_INFO ) : + reneg = ( ( void * ) ext->data ); + if ( ( sizeof ( *reneg ) > ext_len ) || + ( reneg->len > + ( ext_len - sizeof ( *reneg ) ) ) ) { + DBGC ( tls, "TLS %p received " + "underlength renegotiation " + "info\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_HELLO; + } + break; + } + } + } /* Check and store protocol version */ version = ntohs ( hello_a->version ); @@ -1367,27 +1518,44 @@ static int tls_new_server_hello ( struct tls_session *tls, if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) return rc; + /* Handle secure renegotiation */ + if ( tls->secure_renegotiation ) { + + /* Secure renegotiation is expected; verify data */ + if ( ( reneg == NULL ) || + ( reneg->len != sizeof ( tls->verify ) ) || + ( memcmp ( reneg->data, &tls->verify, + sizeof ( tls->verify ) ) != 0 ) ) { + DBGC ( tls, "TLS %p server failed secure " + "renegotiation\n", tls ); + return -EPERM_RENEG_VERIFY; + } + + } else if ( reneg != NULL ) { + + /* Secure renegotiation is being enabled */ + if ( reneg->len != 0 ) { + DBGC ( tls, "TLS %p server provided non-empty initial " + "renegotiation\n", tls ); + return -EPERM_RENEG_VERIFY; + } + tls->secure_renegotiation = 1; + } + return 0; } /** * Parse certificate chain * - * @v tls TLS session + * @v tls TLS connection * @v data Certificate chain * @v len Length of certificate chain * @ret rc Return status code */ -static int tls_parse_chain ( struct tls_session *tls, +static int tls_parse_chain ( struct tls_connection *tls, const void *data, size_t len ) { - const void *end = ( data + len ); - const struct { - tls24_t length; - uint8_t data[0]; - } __attribute__ (( packed )) *certificate; - size_t certificate_len; - struct x509_certificate *cert; - const void *next; + size_t remaining = len; int rc; /* Free any existing certificate chain */ @@ -1402,25 +1570,37 @@ static int tls_parse_chain ( struct tls_session *tls, } /* Add certificates to chain */ - while ( data < end ) { + while ( remaining ) { + const struct { + tls24_t length; + uint8_t data[0]; + } __attribute__ (( packed )) *certificate = data; + size_t certificate_len; + size_t record_len; + struct x509_certificate *cert; - /* Extract raw certificate data */ - certificate = data; + /* Parse header */ + if ( sizeof ( *certificate ) > remaining ) { + DBGC ( tls, "TLS %p underlength certificate:\n", tls ); + DBGC_HDA ( tls, 0, data, remaining ); + rc = -EINVAL_CERTIFICATE; + goto err_underlength; + } certificate_len = tls_uint24 ( &certificate->length ); - next = ( certificate->data + certificate_len ); - if ( next > end ) { + if ( certificate_len > ( remaining - sizeof ( *certificate ) )){ DBGC ( tls, "TLS %p overlength certificate:\n", tls ); - DBGC_HDA ( tls, 0, data, ( end - data ) ); + DBGC_HDA ( tls, 0, data, remaining ); rc = -EINVAL_CERTIFICATE; goto err_overlength; } + record_len = ( sizeof ( *certificate ) + certificate_len ); /* Add certificate to chain */ if ( ( rc = x509_append_raw ( tls->chain, certificate->data, certificate_len ) ) != 0 ) { DBGC ( tls, "TLS %p could not append certificate: %s\n", tls, strerror ( rc ) ); - DBGC_HDA ( tls, 0, data, ( end - data ) ); + DBGC_HDA ( tls, 0, data, remaining ); goto err_parse; } cert = x509_last ( tls->chain ); @@ -1428,13 +1608,15 @@ static int tls_parse_chain ( struct tls_session *tls, tls, x509_name ( cert ) ); /* Move to next certificate in list */ - data = next; + data += record_len; + remaining -= record_len; } return 0; err_parse: err_overlength: + err_underlength: x509_chain_put ( tls->chain ); tls->chain = NULL; err_alloc_chain: @@ -1444,23 +1626,29 @@ static int tls_parse_chain ( struct tls_session *tls, /** * Receive new Certificate handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext handshake record * @v len Length of plaintext handshake record * @ret rc Return status code */ -static int tls_new_certificate ( struct tls_session *tls, +static int tls_new_certificate ( struct tls_connection *tls, const void *data, size_t len ) { const struct { tls24_t length; uint8_t certificates[0]; } __attribute__ (( packed )) *certificate = data; - size_t certificates_len = tls_uint24 ( &certificate->length ); - const void *end = ( certificate->certificates + certificates_len ); + size_t certificates_len; int rc; - /* Sanity check */ - if ( end != ( data + len ) ) { + /* Parse header */ + if ( sizeof ( *certificate ) > len ) { + DBGC ( tls, "TLS %p received underlength Server Certificate\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_CERTIFICATES; + } + certificates_len = tls_uint24 ( &certificate->length ); + if ( certificates_len > ( len - sizeof ( *certificate ) ) ) { DBGC ( tls, "TLS %p received overlength Server Certificate\n", tls ); DBGC_HD ( tls, data, len ); @@ -1478,12 +1666,12 @@ static int tls_new_certificate ( struct tls_session *tls, /** * Receive new Certificate Request handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext handshake record * @v len Length of plaintext handshake record * @ret rc Return status code */ -static int tls_new_certificate_request ( struct tls_session *tls, +static int tls_new_certificate_request ( struct tls_connection *tls, const void *data __unused, size_t len __unused ) { @@ -1511,21 +1699,20 @@ static int tls_new_certificate_request ( struct tls_session *tls, /** * Receive new Server Hello Done handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext handshake record * @v len Length of plaintext handshake record * @ret rc Return status code */ -static int tls_new_server_hello_done ( struct tls_session *tls, +static int tls_new_server_hello_done ( struct tls_connection *tls, const void *data, size_t len ) { const struct { char next[0]; } __attribute__ (( packed )) *hello_done = data; - const void *end = hello_done->next; int rc; /* Sanity check */ - if ( end != ( data + len ) ) { + if ( sizeof ( *hello_done ) != len ) { DBGC ( tls, "TLS %p received overlength Server Hello Done\n", tls ); DBGC_HD ( tls, data, len ); @@ -1545,24 +1732,22 @@ static int tls_new_server_hello_done ( struct tls_session *tls, /** * Receive new Finished handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext handshake record * @v len Length of plaintext handshake record * @ret rc Return status code */ -static int tls_new_finished ( struct tls_session *tls, +static int tls_new_finished ( struct tls_connection *tls, const void *data, size_t len ) { struct digest_algorithm *digest = tls->handshake_digest; const struct { - uint8_t verify_data[12]; + uint8_t verify_data[ sizeof ( tls->verify.server ) ]; char next[0]; } __attribute__ (( packed )) *finished = data; - const void *end = finished->next; uint8_t digest_out[ digest->digestsize ]; - uint8_t verify_data[ sizeof ( finished->verify_data ) ]; /* Sanity check */ - if ( end != ( data + len ) ) { + if ( sizeof ( *finished ) != len ) { DBGC ( tls, "TLS %p received overlength Finished\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_FINISHED; @@ -1571,10 +1756,10 @@ static int tls_new_finished ( struct tls_session *tls, /* Verify data */ tls_verify_handshake ( tls, digest_out ); tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - verify_data, sizeof ( verify_data ), "server finished", - digest_out, sizeof ( digest_out ) ); - if ( memcmp ( verify_data, finished->verify_data, - sizeof ( verify_data ) ) != 0 ) { + tls->verify.server, sizeof ( tls->verify.server ), + "server finished", digest_out, sizeof ( digest_out ) ); + if ( memcmp ( tls->verify.server, finished->verify_data, + sizeof ( tls->verify.server ) ) != 0 ) { DBGC ( tls, "TLS %p verification failed\n", tls ); return -EPERM_VERIFY; } @@ -1591,35 +1776,49 @@ static int tls_new_finished ( struct tls_session *tls, /** * Receive new Handshake record * - * @v tls TLS session + * @v tls TLS connection * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_new_handshake ( struct tls_session *tls, +static int tls_new_handshake ( struct tls_connection *tls, const void *data, size_t len ) { - const void *end = ( data + len ); + size_t remaining = len; int rc; - while ( data != end ) { + while ( remaining ) { const struct { uint8_t type; tls24_t length; uint8_t payload[0]; } __attribute__ (( packed )) *handshake = data; - const void *payload = &handshake->payload; - size_t payload_len = tls_uint24 ( &handshake->length ); - const void *next = ( payload + payload_len ); + const void *payload; + size_t payload_len; + size_t record_len; - /* Sanity check */ - if ( next > end ) { + /* Parse header */ + if ( sizeof ( *handshake ) > remaining ) { + DBGC ( tls, "TLS %p received underlength Handshake\n", + tls ); + DBGC_HD ( tls, data, remaining ); + return -EINVAL_HANDSHAKE; + } + payload_len = tls_uint24 ( &handshake->length ); + if ( payload_len > ( remaining - sizeof ( *handshake ) ) ) { DBGC ( tls, "TLS %p received overlength Handshake\n", tls ); DBGC_HD ( tls, data, len ); return -EINVAL_HANDSHAKE; } + payload = &handshake->payload; + record_len = ( sizeof ( *handshake ) + payload_len ); + /* Handle payload */ switch ( handshake->type ) { + case TLS_HELLO_REQUEST: + rc = tls_new_hello_request ( tls, payload, + payload_len ); + break; case TLS_SERVER_HELLO: rc = tls_new_server_hello ( tls, payload, payload_len ); break; @@ -1648,16 +1847,15 @@ static int tls_new_handshake ( struct tls_session *tls, * which are explicitly excluded). */ if ( handshake->type != TLS_HELLO_REQUEST ) - tls_add_handshake ( tls, data, - sizeof ( *handshake ) + - payload_len ); + tls_add_handshake ( tls, data, record_len ); /* Abort on failure */ if ( rc != 0 ) return rc; /* Move to next handshake record */ - data = next; + data += record_len; + remaining -= record_len; } return 0; @@ -1666,15 +1864,15 @@ static int tls_new_handshake ( struct tls_session *tls, /** * Receive new record * - * @v tls TLS session + * @v tls TLS connection * @v type Record type * @v rx_data List of received data buffers * @ret rc Return status code */ -static int tls_new_record ( struct tls_session *tls, unsigned int type, +static int tls_new_record ( struct tls_connection *tls, unsigned int type, struct list_head *rx_data ) { struct io_buffer *iobuf; - int ( * handler ) ( struct tls_session *tls, const void *data, + int ( * handler ) ( struct tls_connection *tls, const void *data, size_t len ); int rc; @@ -1812,16 +2010,16 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, /** * Allocate and assemble stream-ciphered record from data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @ret data Data * @ret len Length of data * @ret digest MAC digest * @ret plaintext_len Length of plaintext record * @ret plaintext Allocated plaintext record */ -static void * __malloc tls_assemble_stream ( struct tls_session *tls, - const void *data, size_t len, - void *digest, size_t *plaintext_len ) { +static void * __malloc +tls_assemble_stream ( struct tls_connection *tls, const void *data, size_t len, + void *digest, size_t *plaintext_len ) { size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize; void *plaintext; void *content; @@ -1847,14 +2045,14 @@ static void * __malloc tls_assemble_stream ( struct tls_session *tls, /** * Allocate and assemble block-ciphered record from data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @ret data Data * @ret len Length of data * @ret digest MAC digest * @ret plaintext_len Length of plaintext record * @ret plaintext Allocated plaintext record */ -static void * tls_assemble_block ( struct tls_session *tls, +static void * tls_assemble_block ( struct tls_connection *tls, const void *data, size_t len, void *digest, size_t *plaintext_len ) { size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize; @@ -1895,13 +2093,13 @@ static void * tls_assemble_block ( struct tls_session *tls, /** * Send plaintext record * - * @v tls TLS session + * @v tls TLS connection * @v type Record type * @v data Plaintext record * @v len Length of plaintext record * @ret rc Return status code */ -static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, +static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ) { struct tls_header plaintext_tlshdr; struct tls_header *tlshdr; @@ -1987,12 +2185,12 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, /** * Split stream-ciphered record into data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @v rx_data List of received data buffers * @v mac MAC to fill in * @ret rc Return status code */ -static int tls_split_stream ( struct tls_session *tls, +static int tls_split_stream ( struct tls_connection *tls, struct list_head *rx_data, void **mac ) { size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; struct io_buffer *iobuf; @@ -2014,12 +2212,12 @@ static int tls_split_stream ( struct tls_session *tls, /** * Split block-ciphered record into data and MAC portions * - * @v tls TLS session + * @v tls TLS connection * @v rx_data List of received data buffers * @v mac MAC to fill in * @ret rc Return status code */ -static int tls_split_block ( struct tls_session *tls, +static int tls_split_block ( struct tls_connection *tls, struct list_head *rx_data, void **mac ) { size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; struct io_buffer *iobuf; @@ -2072,12 +2270,12 @@ static int tls_split_block ( struct tls_session *tls, /** * Receive new ciphertext record * - * @v tls TLS session + * @v tls TLS connection * @v tlshdr Record header * @v rx_data List of received data buffers * @ret rc Return status code */ -static int tls_new_ciphertext ( struct tls_session *tls, +static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_header *tlshdr, struct list_head *rx_data ) { struct tls_header plaintext_tlshdr; @@ -2145,10 +2343,10 @@ static int tls_new_ciphertext ( struct tls_session *tls, /** * Check flow control window * - * @v tls TLS session + * @v tls TLS connection * @ret len Length of window */ -static size_t tls_plainstream_window ( struct tls_session *tls ) { +static size_t tls_plainstream_window ( struct tls_connection *tls ) { /* Block window unless we are ready to accept data */ if ( ! tls_ready ( tls ) ) @@ -2160,12 +2358,12 @@ static size_t tls_plainstream_window ( struct tls_session *tls ) { /** * Deliver datagram as raw data * - * @v tls TLS session + * @v tls TLS connection * @v iobuf I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ -static int tls_plainstream_deliver ( struct tls_session *tls, +static int tls_plainstream_deliver ( struct tls_connection *tls, struct io_buffer *iobuf, struct xfer_metadata *meta __unused ) { int rc; @@ -2187,14 +2385,16 @@ static int tls_plainstream_deliver ( struct tls_session *tls, /** TLS plaintext stream interface operations */ static struct interface_operation tls_plainstream_ops[] = { - INTF_OP ( xfer_deliver, struct tls_session *, tls_plainstream_deliver ), - INTF_OP ( xfer_window, struct tls_session *, tls_plainstream_window ), - INTF_OP ( intf_close, struct tls_session *, tls_close ), + INTF_OP ( xfer_deliver, struct tls_connection *, + tls_plainstream_deliver ), + INTF_OP ( xfer_window, struct tls_connection *, + tls_plainstream_window ), + INTF_OP ( intf_close, struct tls_connection *, tls_close ), }; /** TLS plaintext stream interface descriptor */ static struct interface_descriptor tls_plainstream_desc = - INTF_DESC_PASSTHRU ( struct tls_session, plainstream, + INTF_DESC_PASSTHRU ( struct tls_connection, plainstream, tls_plainstream_ops, cipherstream ); /****************************************************************************** @@ -2207,10 +2407,10 @@ static struct interface_descriptor tls_plainstream_desc = /** * Handle received TLS header * - * @v tls TLS session + * @v tls TLS connection * @ret rc Returned status code */ -static int tls_newdata_process_header ( struct tls_session *tls ) { +static int tls_newdata_process_header ( struct tls_connection *tls ) { size_t data_len = ntohs ( tls->rx_header.length ); size_t remaining = data_len; size_t frag_len; @@ -2272,10 +2472,10 @@ static int tls_newdata_process_header ( struct tls_session *tls ) { /** * Handle received TLS data payload * - * @v tls TLS session + * @v tls TLS connection * @ret rc Returned status code */ -static int tls_newdata_process_data ( struct tls_session *tls ) { +static int tls_newdata_process_data ( struct tls_connection *tls ) { struct io_buffer *iobuf; int rc; @@ -2305,19 +2505,34 @@ static int tls_newdata_process_data ( struct tls_session *tls ) { return 0; } +/** + * Check flow control window + * + * @v tls TLS connection + * @ret len Length of window + */ +static size_t tls_cipherstream_window ( struct tls_connection *tls ) { + + /* Open window until we are ready to accept data */ + if ( ! tls_ready ( tls ) ) + return -1UL; + + return xfer_window ( &tls->plainstream ); +} + /** * Receive new ciphertext * - * @v tls TLS session + * @v tls TLS connection * @v iobuf I/O buffer * @v meta Data transfer metadat * @ret rc Return status code */ -static int tls_cipherstream_deliver ( struct tls_session *tls, +static int tls_cipherstream_deliver ( struct tls_connection *tls, struct io_buffer *iobuf, struct xfer_metadata *xfer __unused ) { size_t frag_len; - int ( * process ) ( struct tls_session *tls ); + int ( * process ) ( struct tls_connection *tls ); struct io_buffer *dest; int rc; @@ -2365,15 +2580,18 @@ static int tls_cipherstream_deliver ( struct tls_session *tls, /** TLS ciphertext stream interface operations */ static struct interface_operation tls_cipherstream_ops[] = { - INTF_OP ( xfer_deliver, struct tls_session *, + INTF_OP ( xfer_deliver, struct tls_connection *, tls_cipherstream_deliver ), - INTF_OP ( xfer_window_changed, struct tls_session *, tls_tx_resume ), - INTF_OP ( intf_close, struct tls_session *, tls_close ), + INTF_OP ( xfer_window, struct tls_connection *, + tls_cipherstream_window ), + INTF_OP ( xfer_window_changed, struct tls_connection *, + tls_tx_resume ), + INTF_OP ( intf_close, struct tls_connection *, tls_close ), }; /** TLS ciphertext stream interface descriptor */ static struct interface_descriptor tls_cipherstream_desc = - INTF_DESC_PASSTHRU ( struct tls_session, cipherstream, + INTF_DESC_PASSTHRU ( struct tls_connection, cipherstream, tls_cipherstream_ops, plainstream ); /****************************************************************************** @@ -2386,10 +2604,10 @@ static struct interface_descriptor tls_cipherstream_desc = /** * Handle certificate validation completion * - * @v tls TLS session + * @v tls TLS connection * @v rc Reason for completion */ -static void tls_validator_done ( struct tls_session *tls, int rc ) { +static void tls_validator_done ( struct tls_connection *tls, int rc ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; @@ -2444,12 +2662,12 @@ static void tls_validator_done ( struct tls_session *tls, int rc ) { /** TLS certificate validator interface operations */ static struct interface_operation tls_validator_ops[] = { - INTF_OP ( intf_close, struct tls_session *, tls_validator_done ), + INTF_OP ( intf_close, struct tls_connection *, tls_validator_done ), }; /** TLS certificate validator interface descriptor */ static struct interface_descriptor tls_validator_desc = - INTF_DESC ( struct tls_session, validator, tls_validator_ops ); + INTF_DESC ( struct tls_connection, validator, tls_validator_ops ); /****************************************************************************** * @@ -2461,9 +2679,9 @@ static struct interface_descriptor tls_validator_desc = /** * TLS TX state machine * - * @v tls TLS session + * @v tls TLS connection */ -static void tls_tx_step ( struct tls_session *tls ) { +static void tls_tx_step ( struct tls_connection *tls ) { int rc; /* Wait for cipherstream to become ready */ @@ -2529,9 +2747,14 @@ static void tls_tx_step ( struct tls_session *tls ) { tls->tx_pending &= ~TLS_TX_FINISHED; } - /* Reschedule process if pending transmissions remain */ - if ( tls->tx_pending ) + /* Reschedule process if pending transmissions remain, + * otherwise send notification of a window change. + */ + if ( tls->tx_pending ) { tls_tx_resume ( tls ); + } else { + xfer_window_changed ( &tls->plainstream ); + } return; @@ -2541,7 +2764,7 @@ static void tls_tx_step ( struct tls_session *tls ) { /** TLS TX process descriptor */ static struct process_descriptor tls_process_desc = - PROC_DESC_ONCE ( struct tls_session, process, tls_tx_step ); + PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step ); /****************************************************************************** * @@ -2552,7 +2775,7 @@ static struct process_descriptor tls_process_desc = int add_tls ( struct interface *xfer, const char *name, struct interface **next ) { - struct tls_session *tls; + struct tls_connection *tls; int rc; /* Allocate and initialise TLS structure */ @@ -2574,6 +2797,9 @@ int add_tls ( struct interface *xfer, const char *name, tls_clear_cipher ( tls, &tls->rx_cipherspec ); tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); tls->client_random.gmt_unix_time = time ( NULL ); + iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, + sizeof ( tls->rx_header ) ); + INIT_LIST_HEAD ( &tls->rx_data ); if ( ( rc = tls_generate_random ( tls, &tls->client_random.random, ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) { goto err_random; @@ -2583,18 +2809,9 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { goto err_random; } - digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); - digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx ); - tls->handshake_digest = &sha256_algorithm; - tls->handshake_ctx = tls->handshake_sha256_ctx; - tls->tx_pending = TLS_TX_CLIENT_HELLO; - iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, - sizeof ( tls->rx_header ) ); - INIT_LIST_HEAD ( &tls->rx_data ); - /* Add pending operations for server and client Finished messages */ - pending_get ( &tls->client_negotiation ); - pending_get ( &tls->server_negotiation ); + /* Start negotiation */ + tls_restart ( tls ); /* Attach to parent interface, mortalise self, and return */ intf_plug_plug ( &tls->plainstream, xfer ); diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 9342ad21e..3a3666c9a 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -82,21 +82,29 @@ static uint8_t dhcp_request_options_data[] = { DHCP_MESSAGE_TYPE, DHCP_BYTE ( 0 ), DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ), - DHCP_CLIENT_ARCHITECTURE, DHCP_ARCH_CLIENT_ARCHITECTURE, - DHCP_CLIENT_NDI, DHCP_ARCH_CLIENT_NDI, - DHCP_VENDOR_CLASS_ID, DHCP_ARCH_VENDOR_CLASS_ID, + DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), + DHCP_CLIENT_NDI, DHCP_OPTION ( DHCP_ARCH_CLIENT_NDI ), + DHCP_VENDOR_CLASS_ID, + DHCP_STRING ( DHCP_VENDOR_PXECLIENT ( DHCP_ARCH_CLIENT_ARCHITECTURE, + DHCP_ARCH_CLIENT_NDI ) ), DHCP_USER_CLASS_ID, DHCP_STRING ( 'i', 'P', 'X', 'E' ), DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, - DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, - DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, - DHCP_DOMAIN_SEARCH, + DHCP_ROOT_PATH, DHCP_MTU, DHCP_VENDOR_ENCAP, + DHCP_VENDOR_CLASS_ID, DHCP_TFTP_SERVER_NAME, + DHCP_BOOTFILE_NAME, DHCP_DOMAIN_SEARCH, 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */ DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), DHCP_END }; +/** Settings copied in to all DHCP requests */ +static const struct setting * dhcp_request_settings[] = { + &user_class_setting, + &vendor_class_setting, +}; + /** DHCP server address setting */ const struct setting dhcp_server_setting __setting ( SETTING_MISC, dhcp-server ) = { @@ -973,11 +981,13 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, struct dhcp_netdev_desc dhcp_desc; struct dhcp_client_id client_id; struct dhcp_client_uuid client_uuid; + const struct setting *setting; uint8_t *dhcp_features; size_t dhcp_features_len; size_t ll_addr_len; - void *user_class; + void *raw; ssize_t len; + unsigned int i; int rc; /* Create DHCP packet */ @@ -1045,19 +1055,23 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt, } } - /* Add user class, if we have one. */ - if ( ( len = fetch_raw_setting_copy ( NULL, &user_class_setting, - &user_class ) ) >= 0 ) { - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID, - user_class, len ) ) != 0 ) { - DBG ( "DHCP could not set user class: %s\n", - strerror ( rc ) ); - goto err_store_user_class; + /* Add request settings, if applicable */ + for ( i = 0 ; i < ( sizeof ( dhcp_request_settings ) / + sizeof ( dhcp_request_settings[0] ) ) ; i++ ) { + setting = dhcp_request_settings[i]; + if ( ( len = fetch_raw_setting_copy ( NULL, setting, + &raw ) ) >= 0 ) { + rc = dhcppkt_store ( dhcppkt, setting->tag, raw, len ); + free ( raw ); + if ( rc != 0 ) { + DBG ( "DHCP could not set %s: %s\n", + setting->name, strerror ( rc ) ); + goto err_store_raw; + } } } - err_store_user_class: - free ( user_class ); + err_store_raw: err_store_client_uuid: err_store_client_id: err_store_busid: diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index a63543775..253032e4e 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -265,6 +266,8 @@ struct dhcpv6_settings { struct refcnt refcnt; /** Settings block */ struct settings settings; + /** Leased address */ + struct in6_addr lease; /** Option list */ struct dhcpv6_option_list options; }; @@ -279,7 +282,32 @@ struct dhcpv6_settings { static int dhcpv6_applies ( struct settings *settings __unused, const struct setting *setting ) { - return ( setting->scope == &ipv6_scope ); + return ( ( setting->scope == &dhcpv6_scope ) || + ( setting_cmp ( setting, &ip6_setting ) == 0 ) ); +} + +/** + * Fetch value of DHCPv6 leased address + * + * @v dhcpset DHCPv6 settings + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int dhcpv6_fetch_lease ( struct dhcpv6_settings *dhcpv6set, + void *data, size_t len ) { + struct in6_addr *lease = &dhcpv6set->lease; + + /* Do nothing unless a leased address exists */ + if ( IN6_IS_ADDR_UNSPECIFIED ( lease ) ) + return -ENOENT; + + /* Copy leased address */ + if ( len > sizeof ( *lease ) ) + len = sizeof ( *lease ); + memcpy ( data, lease, len ); + + return sizeof ( *lease ); } /** @@ -299,6 +327,10 @@ static int dhcpv6_fetch ( struct settings *settings, const union dhcpv6_any_option *option; size_t option_len; + /* Handle leased address */ + if ( setting_cmp ( setting, &ip6_setting ) == 0 ) + return dhcpv6_fetch_lease ( dhcpv6set, data, len ); + /* Find option */ option = dhcpv6_option ( &dhcpv6set->options, setting->tag ); if ( ! option ) @@ -321,11 +353,13 @@ static struct settings_operations dhcpv6_settings_operations = { /** * Register DHCPv6 options as network device settings * + * @v lease DHCPv6 leased address * @v options DHCPv6 option list * @v parent Parent settings block * @ret rc Return status code */ -static int dhcpv6_register ( struct dhcpv6_option_list *options, +static int dhcpv6_register ( struct in6_addr *lease, + struct dhcpv6_option_list *options, struct settings *parent ) { struct dhcpv6_settings *dhcpv6set; void *data; @@ -340,12 +374,14 @@ static int dhcpv6_register ( struct dhcpv6_option_list *options, } ref_init ( &dhcpv6set->refcnt, NULL ); settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations, - &dhcpv6set->refcnt, &ipv6_scope ); + &dhcpv6set->refcnt, &dhcpv6_scope ); + dhcpv6set->settings.order = IPV6_ORDER_DHCPV6; data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) ); len = options->len; memcpy ( data, options->data, len ); dhcpv6set->options.data = data; dhcpv6set->options.len = len; + memcpy ( &dhcpv6set->lease, lease, sizeof ( dhcpv6set->lease ) ); /* Register settings */ if ( ( rc = register_settings ( &dhcpv6set->settings, parent, @@ -364,10 +400,22 @@ static int dhcpv6_register ( struct dhcpv6_option_list *options, * */ -/** Options to be requested */ -static uint16_t dhcpv6_requested_options[] = { - htons ( DHCPV6_DNS_SERVERS ), htons ( DHCPV6_DOMAIN_LIST ), - htons ( DHCPV6_BOOTFILE_URL ), htons ( DHCPV6_BOOTFILE_PARAM ), +/** Raw option data for options common to all DHCPv6 requests */ +static uint8_t dhcpv6_request_options_data[] = { + DHCPV6_CODE ( DHCPV6_OPTION_REQUEST ), + DHCPV6_OPTION ( DHCPV6_CODE ( DHCPV6_DNS_SERVERS ), + DHCPV6_CODE ( DHCPV6_DOMAIN_LIST ), + DHCPV6_CODE ( DHCPV6_BOOTFILE_URL ), + DHCPV6_CODE ( DHCPV6_BOOTFILE_PARAM ) ), + DHCPV6_CODE ( DHCPV6_VENDOR_CLASS ), + DHCPV6_OPTION ( DHCPV6_DWORD_VALUE ( DHCPV6_VENDOR_CLASS_PXE ), + DHCPV6_STRING ( + DHCP_VENDOR_PXECLIENT ( DHCP_ARCH_CLIENT_ARCHITECTURE, + DHCP_ARCH_CLIENT_NDI ) ) ), + DHCPV6_CODE ( DHCPV6_CLIENT_ARCHITECTURE ), + DHCPV6_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ), + DHCPV6_CODE ( DHCPV6_CLIENT_NDI ), + DHCPV6_OPTION ( DHCP_ARCH_CLIENT_NDI ) }; /** @@ -414,8 +462,6 @@ enum dhcpv6_session_state_flags { DHCPV6_RX_RECORD_SERVER_ID = 0x04, /** Record received IPv6 address */ DHCPV6_RX_RECORD_IAADDR = 0x08, - /** Apply received IPv6 address */ - DHCPV6_RX_APPLY_IAADDR = 0x10, }; /** DHCPv6 request state */ @@ -423,7 +469,7 @@ static struct dhcpv6_session_state dhcpv6_request = { .tx_type = DHCPV6_REQUEST, .rx_type = DHCPV6_REPLY, .flags = ( DHCPV6_TX_IA_NA | DHCPV6_TX_IAADDR | - DHCPV6_RX_RECORD_IAADDR | DHCPV6_RX_APPLY_IAADDR ), + DHCPV6_RX_RECORD_IAADDR ), .next = NULL, }; @@ -565,15 +611,14 @@ static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { struct dhcpv6_duid_option *server_id; struct dhcpv6_ia_na_option *ia_na; struct dhcpv6_iaaddr_option *iaaddr; - struct dhcpv6_option_request_option *option_request; struct dhcpv6_user_class_option *user_class; struct dhcpv6_elapsed_time_option *elapsed; struct dhcpv6_header *dhcphdr; struct io_buffer *iobuf; + void *options; size_t client_id_len; size_t server_id_len; size_t ia_na_len; - size_t option_request_len; size_t user_class_string_len; size_t user_class_len; size_t elapsed_len; @@ -592,16 +637,14 @@ static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { } else { ia_na_len = 0; } - option_request_len = ( sizeof ( *option_request ) + - sizeof ( dhcpv6_requested_options ) ); user_class_string_len = dhcpv6_user_class ( NULL, 0 ); user_class_len = ( sizeof ( *user_class ) + sizeof ( user_class->user_class[0] ) + user_class_string_len ); elapsed_len = sizeof ( *elapsed ); total_len = ( sizeof ( *dhcphdr ) + client_id_len + server_id_len + - ia_na_len + option_request_len + user_class_len + - elapsed_len ); + ia_na_len + sizeof ( dhcpv6_request_options_data ) + + user_class_len + elapsed_len ); /* Allocate packet */ iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len ); @@ -652,13 +695,10 @@ static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { } } - /* Construct option request */ - option_request = iob_put ( iobuf, option_request_len ); - option_request->header.code = htons ( DHCPV6_OPTION_REQUEST ); - option_request->header.len = htons ( option_request_len - - sizeof ( option_request->header )); - memcpy ( option_request->requested, dhcpv6_requested_options, - sizeof ( dhcpv6_requested_options ) ); + /* Construct fixed request options */ + options = iob_put ( iobuf, sizeof ( dhcpv6_request_options_data ) ); + memcpy ( options, dhcpv6_request_options_data, + sizeof ( dhcpv6_request_options_data ) ); /* Construct user class */ user_class = iob_put ( iobuf, user_class_len ); @@ -828,41 +868,25 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6, dhcpv6->server_duid_len ); } - /* Apply identity association address, if applicable */ - if ( dhcpv6->state->flags & DHCPV6_RX_APPLY_IAADDR ) { - if ( ( rc = ipv6_set_address ( dhcpv6->netdev, - &dhcpv6->lease ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not apply %s: %s\n", - dhcpv6->netdev->name, - inet6_ntoa ( &dhcpv6->lease ), strerror ( rc ) ); - /* This is plausibly the error we want to return */ - dhcpv6->rc = rc; - goto done; - } - } - - /* Transition to next state or complete DHCPv6, as applicable */ + /* Transition to next state, if applicable */ if ( dhcpv6->state->next ) { - - /* Transition to next state */ dhcpv6_set_state ( dhcpv6, dhcpv6->state->next ); rc = 0; - - } else { - - /* Register settings */ - if ( ( rc = dhcpv6_register ( &options, parent ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not register " - "settings: %s\n", dhcpv6->netdev->name, - strerror ( rc ) ); - goto done; - } - - /* Mark as complete */ - dhcpv6_finished ( dhcpv6, 0 ); - DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name ); + goto done; } + /* Register settings */ + if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &options, + parent ) ) != 0 ) { + DBGC ( dhcpv6, "DHCPv6 %s could not register settings: %s\n", + dhcpv6->netdev->name, strerror ( rc ) ); + goto done; + } + + /* Mark as complete */ + dhcpv6_finished ( dhcpv6, 0 ); + DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name ); + done: free_iob ( iobuf ); return rc; @@ -980,7 +1004,7 @@ const struct setting filename6_setting __setting ( SETTING_BOOT, filename ) = { .description = "Boot filename", .tag = DHCPV6_BOOTFILE_URL, .type = &setting_type_string, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; /** DNS search list setting */ @@ -989,5 +1013,5 @@ const struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { .description = "DNS search list", .tag = DHCPV6_DOMAIN_LIST, .type = &setting_type_dnssl, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index 2d77477f6..f412f7109 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -42,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -416,7 +417,7 @@ static const char * dns_name ( struct dns_name *name ) { static char buf[256]; int len; - len = dns_decode ( name, buf, sizeof ( buf ) ); + len = dns_decode ( name, buf, ( sizeof ( buf ) - 1 /* NUL */ ) ); return ( ( len < 0 ) ? "" : buf ); } @@ -867,6 +868,28 @@ static void dns_xfer_close ( struct dns_request *dns, int rc ) { dns_done ( dns, rc ); } +/** + * Report job progress + * + * @v dns DNS request + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int dns_progress ( struct dns_request *dns, + struct job_progress *progress ) { + int len; + + /* Show current question as progress message */ + len = dns_decode ( &dns->name, progress->message, + ( sizeof ( progress->message ) - 1 /* NUL */ ) ); + if ( len < 0 ) { + /* Ignore undecodable names */ + progress->message[0] = '\0'; + } + + return 0; +} + /** DNS socket interface operations */ static struct interface_operation dns_socket_operations[] = { INTF_OP ( xfer_deliver, struct dns_request *, dns_xfer_deliver ), @@ -879,6 +902,7 @@ static struct interface_descriptor dns_socket_desc = /** DNS resolver interface operations */ static struct interface_operation dns_resolv_op[] = { + INTF_OP ( job_progress, struct dns_request *, dns_progress ), INTF_OP ( intf_close, struct dns_request *, dns_done ), }; @@ -1048,7 +1072,7 @@ const struct setting_type setting_type_dnssl __setting_type = { }; /** IPv4 DNS server setting */ -const struct setting dns_setting __setting ( SETTING_IP_EXTRA, dns ) = { +const struct setting dns_setting __setting ( SETTING_IP4_EXTRA, dns ) = { .name = "dns", .description = "DNS server", .tag = DHCP_DNS_SERVERS, @@ -1056,12 +1080,12 @@ const struct setting dns_setting __setting ( SETTING_IP_EXTRA, dns ) = { }; /** IPv6 DNS server setting */ -const struct setting dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { +const struct setting dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .name = "dns6", .description = "DNS server", .tag = DHCPV6_DNS_SERVERS, .type = &setting_type_ipv6, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; /** DNS search list */ diff --git a/src/net/udp/ntp.c b/src/net/udp/ntp.c new file mode 100644 index 000000000..11f8ccc00 --- /dev/null +++ b/src/net/udp/ntp.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network Time Protocol + * + */ + +/** An NTP client */ +struct ntp_client { + /** Reference count */ + struct refcnt refcnt; + /** Job control interface */ + struct interface job; + /** Data transfer interface */ + struct interface xfer; + /** Retransmission timer */ + struct retry_timer timer; +}; + +/** + * Close NTP client + * + * @v ntp NTP client + * @v rc Reason for close + */ +static void ntp_close ( struct ntp_client *ntp, int rc ) { + + /* Stop timer */ + stop_timer ( &ntp->timer ); + + /* Shut down interfaces */ + intf_shutdown ( &ntp->xfer, rc ); + intf_shutdown ( &ntp->job, rc ); +} + +/** + * Send NTP request + * + * @v ntp NTP client + * @ret rc Return status code + */ +static int ntp_request ( struct ntp_client *ntp ) { + struct ntp_header hdr; + int rc; + + DBGC ( ntp, "NTP %p sending request\n", ntp ); + + /* Construct header */ + memset ( &hdr, 0, sizeof ( hdr ) ); + hdr.flags = ( NTP_FL_LI_UNKNOWN | NTP_FL_VN_1 | NTP_FL_MODE_CLIENT ); + hdr.transmit.seconds = htonl ( time ( NULL ) + NTP_EPOCH ); + hdr.transmit.fraction = htonl ( NTP_FRACTION_MAGIC ); + + /* Send request */ + if ( ( rc = xfer_deliver_raw ( &ntp->xfer, &hdr, + sizeof ( hdr ) ) ) != 0 ) { + DBGC ( ntp, "NTP %p could not send request: %s\n", + ntp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle NTP response + * + * @v ntp NTP client + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int ntp_deliver ( struct ntp_client *ntp, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct ntp_header *hdr; + struct sockaddr_tcpip *st_src; + int32_t delta; + int rc; + + /* Check source port */ + st_src = ( ( struct sockaddr_tcpip * ) meta->src ); + if ( st_src->st_port != htons ( NTP_PORT ) ) { + DBGC ( ntp, "NTP %p received non-NTP packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + + /* Check packet length */ + if ( iob_len ( iobuf ) < sizeof ( *hdr ) ) { + DBGC ( ntp, "NTP %p received malformed packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + hdr = iobuf->data; + + /* Check mode */ + if ( ( hdr->flags & NTP_FL_MODE_MASK ) != NTP_FL_MODE_SERVER ) { + DBGC ( ntp, "NTP %p received non-server packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + + /* Check magic value */ + if ( hdr->originate.fraction != htonl ( NTP_FRACTION_MAGIC ) ) { + DBGC ( ntp, "NTP %p received unrecognised packet:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + goto ignore; + } + + /* Check for Kiss-o'-Death packets */ + if ( ! hdr->stratum ) { + DBGC ( ntp, "NTP %p received kiss-o'-death:\n", ntp ); + DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + goto close; + } + + /* Calculate clock delta */ + delta = ( ntohl ( hdr->receive.seconds ) - + ntohl ( hdr->originate.seconds ) ); + DBGC ( ntp, "NTP %p delta %d seconds\n", ntp, delta ); + + /* Adjust system clock */ + time_adjust ( delta ); + + /* Success */ + rc = 0; + + close: + ntp_close ( ntp, rc ); + ignore: + free_iob ( iobuf ); + return 0; +} + +/** + * Handle data transfer window change + * + * @v ntp NTP client + */ +static void ntp_window_changed ( struct ntp_client *ntp ) { + + /* Start timer to send initial request */ + start_timer_nodelay ( &ntp->timer ); +} + +/** Data transfer interface operations */ +static struct interface_operation ntp_xfer_op[] = { + INTF_OP ( xfer_deliver, struct ntp_client *, ntp_deliver ), + INTF_OP ( xfer_window_changed, struct ntp_client *, + ntp_window_changed ), + INTF_OP ( intf_close, struct ntp_client *, ntp_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor ntp_xfer_desc = + INTF_DESC_PASSTHRU ( struct ntp_client, xfer, ntp_xfer_op, job ); + +/** Job control interface operations */ +static struct interface_operation ntp_job_op[] = { + INTF_OP ( intf_close, struct ntp_client *, ntp_close ), +}; + +/** Job control interface descriptor */ +static struct interface_descriptor ntp_job_desc = + INTF_DESC_PASSTHRU ( struct ntp_client, job, ntp_job_op, xfer ); + +/** + * Handle NTP timer expiry + * + * @v timer Retransmission timer + * @v fail Failure indicator + */ +static void ntp_expired ( struct retry_timer *timer, int fail ) { + struct ntp_client *ntp = + container_of ( timer, struct ntp_client, timer ); + + /* Shut down client if we have failed */ + if ( fail ) { + ntp_close ( ntp, -ETIMEDOUT ); + return; + } + + /* Otherwise, restart timer and (re)transmit request */ + start_timer ( &ntp->timer ); + ntp_request ( ntp ); +} + +/** + * Start NTP client + * + * @v job Job control interface + * @v hostname NTP server + * @ret rc Return status code + */ +int start_ntp ( struct interface *job, const char *hostname ) { + struct ntp_client *ntp; + union { + struct sockaddr_tcpip st; + struct sockaddr sa; + } server; + int rc; + + /* Allocate and initialise structure*/ + ntp = zalloc ( sizeof ( *ntp ) ); + if ( ! ntp ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &ntp->refcnt, NULL ); + intf_init ( &ntp->job, &ntp_job_desc, &ntp->refcnt ); + intf_init ( &ntp->xfer, &ntp_xfer_desc, &ntp->refcnt ); + timer_init ( &ntp->timer, ntp_expired, &ntp->refcnt ); + set_timer_limits ( &ntp->timer, NTP_MIN_TIMEOUT, NTP_MAX_TIMEOUT ); + + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st.st_port = htons ( NTP_PORT ); + if ( ( rc = xfer_open_named_socket ( &ntp->xfer, SOCK_DGRAM, &server.sa, + hostname, NULL ) ) != 0 ) { + DBGC ( ntp, "NTP %p could not open socket: %s\n", + ntp, strerror ( rc ) ); + goto err_open; + } + + /* Attach parent interface, mortalise self, and return */ + intf_plug_plug ( &ntp->job, job ); + ref_put ( &ntp->refcnt ); + return 0; + + err_open: + ntp_close ( ntp, rc ); + ref_put ( &ntp->refcnt ); + err_alloc: + return rc; +} diff --git a/src/net/udp/slam.c b/src/net/udp/slam.c index 8b26bfb3c..c165b4fb9 100644 --- a/src/net/udp/slam.c +++ b/src/net/udp/slam.c @@ -266,7 +266,8 @@ static int slam_tx_nack ( struct slam_request *slam ) { if ( ! iobuf ) { DBGC ( slam, "SLAM %p could not allocate I/O buffer\n", slam ); - return -ENOMEM; + rc = -ENOMEM; + goto err_alloc; } /* Construct NACK. We always request only a single packet; @@ -294,14 +295,19 @@ static int slam_tx_nack ( struct slam_request *slam ) { "0-%ld\n", slam, ( num_blocks - 1 ) ); } if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 ) - return rc; + goto err_put_value; if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 ) - return rc; + goto err_put_value; nul = iob_put ( iobuf, 1 ); *nul = 0; /* Transmit packet */ - return xfer_deliver_iob ( &slam->socket, iobuf ); + return xfer_deliver_iob ( &slam->socket, iob_disown ( iobuf ) ); + + err_put_value: + free_iob ( iobuf ); + err_alloc: + return rc; } /** @@ -394,12 +400,16 @@ static int slam_pull_value ( struct slam_request *slam, return -EINVAL; } - /* Read value */ + /* Strip value */ iob_pull ( iobuf, len ); - *value = ( *data & 0x1f ); - while ( --len ) { - *value <<= 8; - *value |= *(++data); + + /* Read value, if applicable */ + if ( value ) { + *value = ( *data & 0x1f ); + while ( --len ) { + *value <<= 8; + *value |= *(++data); + } } return 0; @@ -415,6 +425,8 @@ static int slam_pull_value ( struct slam_request *slam, static int slam_pull_header ( struct slam_request *slam, struct io_buffer *iobuf ) { void *header = iobuf->data; + unsigned long total_bytes; + unsigned long block_size; int rc; /* If header matches cached header, just pull it and return */ @@ -431,22 +443,26 @@ static int slam_pull_header ( struct slam_request *slam, */ if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 ) return rc; - if ( ( rc = slam_pull_value ( slam, iobuf, - &slam->total_bytes ) ) != 0 ) + if ( ( rc = slam_pull_value ( slam, iobuf, &total_bytes ) ) != 0 ) return rc; - if ( ( rc = slam_pull_value ( slam, iobuf, - &slam->block_size ) ) != 0 ) + if ( ( rc = slam_pull_value ( slam, iobuf, &block_size ) ) != 0 ) return rc; + /* Sanity check */ + if ( block_size == 0 ) { + DBGC ( slam, "SLAM %p ignoring zero block size\n", slam ); + return -EINVAL; + } + /* Update the cached header */ slam->header_len = ( iobuf->data - header ); assert ( slam->header_len <= sizeof ( slam->header ) ); memcpy ( slam->header, header, slam->header_len ); /* Calculate number of blocks */ - slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) / - slam->block_size ); - + slam->total_bytes = total_bytes; + slam->block_size = block_size; + slam->num_blocks = ( ( total_bytes + block_size - 1 ) / block_size ); DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num " "blocks %ld\n", slam, slam->total_bytes, slam->block_size, slam->num_blocks ); diff --git a/src/net/udp/syslog.c b/src/net/udp/syslog.c index b6eee6036..a45fc459d 100644 --- a/src/net/udp/syslog.c +++ b/src/net/udp/syslog.c @@ -213,7 +213,7 @@ const struct setting syslog6_setting __setting ( SETTING_MISC, syslog6 ) = { .description = "Syslog server", .tag = DHCPV6_LOG_SERVERS, .type = &setting_type_ipv6, - .scope = &ipv6_scope, + .scope = &dhcpv6_scope, }; /** diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index 953bcb46a..6ce27497c 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -279,6 +279,8 @@ static int tftp_presize ( struct tftp_request *tftp, size_t filesize ) { * length is an exact multiple of the blocksize will have a * trailing zero-length block, which must be included. */ + if ( tftp->blksize == 0 ) + return -EINVAL; num_blocks = ( ( filesize / tftp->blksize ) + 1 ); if ( ( rc = bitmap_resize ( &tftp->bitmap, num_blocks ) ) != 0 ) { DBGC ( tftp, "TFTP %p could not resize bitmap to %d blocks: " @@ -325,7 +327,7 @@ void tftp_set_mtftp_port ( unsigned int port ) { * @ret rc Return status code */ static int tftp_send_rrq ( struct tftp_request *tftp ) { - const char *path = tftp->uri->path; + const char *path = ( tftp->uri->path + 1 /* skip '/' */ ); struct tftp_rrq *rrq; size_t len; struct io_buffer *iobuf; @@ -1067,6 +1069,8 @@ static int tftp_core_open ( struct interface *xfer, struct uri *uri, return -EINVAL; if ( ! uri->path ) return -EINVAL; + if ( uri->path[0] != '/' ) + return -EINVAL; /* Allocate and populate TFTP structure */ tftp = zalloc ( sizeof ( *tftp ) ); @@ -1180,13 +1184,12 @@ struct uri_opener mtftp_uri_opener __uri_opener = { */ static int tftp_apply_settings ( void ) { static struct in_addr tftp_server = { 0 }; - struct in_addr last_tftp_server; + struct in_addr new_tftp_server; char uri_string[32]; struct uri *uri; /* Retrieve TFTP server setting */ - last_tftp_server = tftp_server; - fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server ); + fetch_ipv4_setting ( NULL, &next_server_setting, &new_tftp_server ); /* If TFTP server setting has changed, set the current working * URI to match. Do it only when the TFTP server has changed @@ -1195,18 +1198,19 @@ static int tftp_apply_settings ( void ) { * an unrelated setting and triggered all the settings * applicators. */ - if ( tftp_server.s_addr != last_tftp_server.s_addr ) { - if ( tftp_server.s_addr ) { - snprintf ( uri_string, sizeof ( uri_string ), - "tftp://%s/", inet_ntoa ( tftp_server ) ); - uri = parse_uri ( uri_string ); - if ( ! uri ) - return -ENOMEM; - } else { - uri = NULL; - } + if ( new_tftp_server.s_addr && + ( new_tftp_server.s_addr != tftp_server.s_addr ) ) { + DBGC ( &tftp_server, "TFTP server changed %s => ", + inet_ntoa ( tftp_server ) ); + DBGC ( &tftp_server, "%s\n", inet_ntoa ( new_tftp_server ) ); + snprintf ( uri_string, sizeof ( uri_string ), + "tftp://%s/", inet_ntoa ( new_tftp_server ) ); + uri = parse_uri ( uri_string ); + if ( ! uri ) + return -ENOMEM; churi ( uri ); uri_put ( uri ); + tftp_server = new_tftp_server; } return 0; diff --git a/src/net/validator.c b/src/net/validator.c index db968398a..40f778c7d 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -41,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** @file * @@ -133,7 +134,7 @@ const struct setting crosscert_setting __setting ( SETTING_CRYPTO, crosscert )={ }; /** Default cross-signed certificate source */ -static const char crosscert_default[] = "http://ca.ipxe.org/auto"; +static const char crosscert_default[] = CROSSCERT; /** * Append cross-signing certificates to certificate chain @@ -238,6 +239,10 @@ static int validator_start_download ( struct validator *validator, /* Determine cross-signed certificate source */ fetch_string_setting_copy ( NULL, &crosscert_setting, &crosscert_copy ); crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default ); + if ( ! crosscert[0] ) { + rc = -EINVAL; + goto err_check_uri_string; + } /* Allocate URI string */ uri_string_len = ( strlen ( crosscert ) + 22 /* "/%08x.der?subject=" */ @@ -276,6 +281,7 @@ static int validator_start_download ( struct validator *validator, err_open_uri_string: free ( uri_string ); err_alloc_uri_string: + err_check_uri_string: free ( crosscert_copy ); return rc; } @@ -477,13 +483,12 @@ static void validator_step ( struct validator *validator ) { issuer = link->cert; if ( ! cert ) continue; - if ( ! issuer->valid ) + if ( ! x509_is_valid ( issuer ) ) continue; /* The issuer is valid, but this certificate is not * yet valid. If OCSP is applicable, start it. */ - if ( cert->extensions.auth_info.ocsp.uri.len && - ( ! cert->extensions.auth_info.ocsp.good ) ) { + if ( ocsp_required ( cert ) ) { /* Start OCSP */ if ( ( rc = validator_start_ocsp ( validator, cert, issuer ) ) != 0 ) { diff --git a/src/arch/x86/scripts/efi.lds b/src/scripts/efi.lds similarity index 100% rename from src/arch/x86/scripts/efi.lds rename to src/scripts/efi.lds diff --git a/src/tests/asn1_test.c b/src/tests/asn1_test.c new file mode 100644 index 000000000..df3f01b63 --- /dev/null +++ b/src/tests/asn1_test.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 + * + * ASN.1 self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include "asn1_test.h" + +/** + * Report ASN.1 test result + * + * @v test ASN.1 test + * @v file Test code file + * @v line Test code line + */ +void asn1_okx ( struct asn1_test *test, const char *file, unsigned int line ) { + struct digest_algorithm *digest = &asn1_test_digest_algorithm; + struct asn1_cursor *cursor; + uint8_t ctx[digest->ctxsize]; + uint8_t out[ASN1_TEST_DIGEST_SIZE]; + unsigned int i; + size_t offset; + int next; + + /* Sanity check */ + assert ( sizeof ( out ) == digest->digestsize ); + + /* Correct image data pointer */ + test->image->data = virt_to_user ( ( void * ) test->image->data ); + + /* Check that image is detected as correct type */ + okx ( register_image ( test->image ) == 0, file, line ); + okx ( test->image->type == test->type, file, line ); + + /* Check that all ASN.1 objects can be extracted */ + for ( offset = 0, i = 0 ; i < test->count ; offset = next, i++ ) { + + /* Extract ASN.1 object */ + next = image_asn1 ( test->image, offset, &cursor ); + okx ( next >= 0, file, line ); + okx ( ( ( size_t ) next ) > offset, file, line ); + if ( next > 0 ) { + + /* Calculate digest of ASN.1 object */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, cursor->data, + cursor->len ); + digest_final ( digest, ctx, out ); + + /* Compare against expected digest */ + okx ( memcmp ( out, test->expected[i].digest, + sizeof ( out ) ) == 0, file, line ); + + /* Free ASN.1 object */ + free ( cursor ); + } + } + + /* Check that we have reached the end of the image */ + okx ( offset == test->image->len, file, line ); + + /* Unregister image */ + unregister_image ( test->image ); +} diff --git a/src/tests/asn1_test.h b/src/tests/asn1_test.h new file mode 100644 index 000000000..c8167ed36 --- /dev/null +++ b/src/tests/asn1_test.h @@ -0,0 +1,73 @@ +#ifndef _ASN1_TEST_H +#define _ASN1_TEST_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** Digest algorithm used for ASN.1 tests */ +#define asn1_test_digest_algorithm sha1_algorithm + +/** Digest size used for ASN.1 tests */ +#define ASN1_TEST_DIGEST_SIZE SHA1_DIGEST_SIZE + +/** An ASN.1 test digest */ +struct asn1_test_digest { + /** Digest value */ + uint8_t digest[ASN1_TEST_DIGEST_SIZE]; +}; + +/** An ASN.1 test */ +struct asn1_test { + /** Image type */ + struct image_type *type; + /** Source image */ + struct image *image; + /** Expected digests of ASN.1 objects */ + struct asn1_test_digest *expected; + /** Number of ASN.1 objects */ + unsigned int count; +}; + +/** + * Define an ASN.1 test + * + * @v _name Test name + * @v _type Test image file type + * @v _file Test image file data + * @v ... Expected ASN.1 object digests + * @ret test ASN.1 test + */ +#define ASN1( _name, _type, _file, ... ) \ + static const char _name ## __file[] = _file; \ + static struct image _name ## __image = { \ + .refcnt = REF_INIT ( ref_no_free ), \ + .name = #_name, \ + .data = ( userptr_t ) ( _name ## __file ), \ + .len = sizeof ( _name ## __file ), \ + }; \ + static struct asn1_test_digest _name ## _expected[] = { \ + __VA_ARGS__ \ + }; \ + static struct asn1_test _name = { \ + .type = _type, \ + .image = & _name ## __image, \ + .expected = _name ## _expected, \ + .count = ( sizeof ( _name ## _expected ) / \ + sizeof ( _name ## _expected[0] ) ), \ + }; + +extern void asn1_okx ( struct asn1_test *test, const char *file, + unsigned int line ); + +/** + * Report ASN.1 test result + * + * @v test ASN.1 test + */ +#define asn1_ok( test ) asn1_okx ( test, __FILE__, __LINE__ ) + +#endif /* _ASN1_TEST_H */ diff --git a/src/tests/bitops_test.c b/src/tests/bitops_test.c new file mode 100644 index 000000000..f29fc6801 --- /dev/null +++ b/src/tests/bitops_test.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +/** @file + * + * Bit operations self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include + +/** + * Perform bit operations self-tests + * + */ +static void bitops_test_exec ( void ) { + uint8_t bits[32]; + + /* Initialise bits */ + memset ( bits, 0, sizeof ( bits ) ); + + /* Test set_bit() */ + set_bit ( 0, bits ); + ok ( bits[0] == 0x01 ); + set_bit ( 17, bits ); + ok ( bits[2] == 0x02 ); + set_bit ( 22, bits ); + ok ( bits[2] == 0x42 ); + set_bit ( 22, bits ); + ok ( bits[2] == 0x42 ); + + /* Test clear_bit() */ + clear_bit ( 0, bits ); + ok ( bits[0] == 0x00 ); + bits[5] = 0xff; + clear_bit ( 42, bits ); + ok ( bits[5] == 0xfb ); + clear_bit ( 42, bits ); + ok ( bits[5] == 0xfb ); + clear_bit ( 44, bits ); + ok ( bits[5] == 0xeb ); + + /* Test test_and_set_bit() */ + ok ( test_and_set_bit ( 0, bits ) == 0 ); + ok ( bits[0] == 0x01 ); + ok ( test_and_set_bit ( 0, bits ) != 0 ); + ok ( bits[0] == 0x01 ); + ok ( test_and_set_bit ( 69, bits ) == 0 ); + ok ( bits[8] == 0x20 ); + ok ( test_and_set_bit ( 69, bits ) != 0 ); + ok ( bits[8] == 0x20 ); + ok ( test_and_set_bit ( 69, bits ) != 0 ); + ok ( bits[8] == 0x20 ); + + /* Test test_and_clear_bit() */ + ok ( test_and_clear_bit ( 0, bits ) != 0 ); + ok ( bits[0] == 0x00 ); + ok ( test_and_clear_bit ( 0, bits ) == 0 ); + ok ( bits[0] == 0x00 ); + bits[31] = 0xeb; + ok ( test_and_clear_bit ( 255, bits ) != 0 ); + ok ( bits[31] == 0x6b ); + ok ( test_and_clear_bit ( 255, bits ) == 0 ); + ok ( bits[31] == 0x6b ); + ok ( test_and_clear_bit ( 255, bits ) == 0 ); + ok ( bits[31] == 0x6b ); +} + +/** Bit operations self-test */ +struct self_test bitops_test __self_test = { + .name = "bitops", + .exec = bitops_test_exec, +}; diff --git a/src/tests/der_test.c b/src/tests/der_test.c new file mode 100644 index 000000000..00cc644f4 --- /dev/null +++ b/src/tests/der_test.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 + * + * DER self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "asn1_test.h" + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define inline expected digest */ +#define DIGEST(...) { { __VA_ARGS__ } } + +/** 32-bit RSA private key */ +ASN1 ( rsa32, &der_image_type, + DATA ( 0x30, 0x2c, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0xb7, 0x56, + 0x5c, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x04, 0x66, + 0xa4, 0xc4, 0x35, 0x02, 0x03, 0x00, 0xda, 0x9f, 0x02, 0x03, + 0x00, 0xd6, 0xaf, 0x02, 0x02, 0x01, 0x59, 0x02, 0x02, 0x4e, + 0xe1, 0x02, 0x03, 0x00, 0xa6, 0x5a ), + DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed, + 0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ) ); + +/** 64-bit RSA private key */ +ASN1 ( rsa64, &der_image_type, + DATA ( 0x30, 0x3e, 0x02, 0x01, 0x00, 0x02, 0x09, 0x00, 0xa1, 0xba, + 0xb5, 0x70, 0x00, 0x89, 0xc0, 0x43, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x08, 0x43, 0x98, 0xc6, 0x3c, 0x5f, 0xdc, 0x98, + 0x01, 0x02, 0x05, 0x00, 0xcf, 0x91, 0x1c, 0x5d, 0x02, 0x05, + 0x00, 0xc7, 0x77, 0x85, 0x1f, 0x02, 0x05, 0x00, 0xbc, 0xb3, + 0x33, 0x91, 0x02, 0x04, 0x1b, 0xf9, 0x38, 0x13, 0x02, 0x04, + 0x19, 0xf2, 0x58, 0x86 ), + DIGEST ( 0xee, 0x17, 0x32, 0x31, 0xf0, 0x3d, 0xfd, 0xaa, 0x9b, 0x47, + 0xaf, 0x7b, 0x4b, 0x52, 0x0b, 0xb1, 0xab, 0x25, 0x3f, 0x11 ) ); + +/** + * Perform DER self-test + * + */ +static void der_test_exec ( void ) { + + /* Perform tests */ + asn1_ok ( &rsa32 ); + asn1_ok ( &rsa64 ); +} + +/** DER self-test */ +struct self_test der_test __self_test = { + .name = "der", + .exec = der_test_exec, +}; diff --git a/src/tests/iobuf_test.c b/src/tests/iobuf_test.c new file mode 100644 index 000000000..a417c2e8c --- /dev/null +++ b/src/tests/iobuf_test.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +/** @file + * + * I/O buffer tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include + +/* Forward declaration */ +struct self_test iobuf_test __self_test; + +/** + * Report I/O buffer allocation test result + * + * @v len Required length of buffer + * @v align Physical alignment + * @v offset Offset from physical alignment + * @v file Test code file + * @v line Test code line + */ +static inline void alloc_iob_okx ( size_t len, size_t align, size_t offset, + const char *file, unsigned int line ) { + struct io_buffer *iobuf; + + /* Allocate I/O buffer */ + iobuf = alloc_iob_raw ( len, align, offset ); + okx ( iobuf != NULL, file, line ); + DBGC ( &iobuf_test, "IOBUF %p (%#08lx+%#zx) for %#zx align %#zx " + "offset %#zx\n", iobuf, virt_to_phys ( iobuf->data ), + iob_tailroom ( iobuf ), len, align, offset ); + + /* Validate requested length and alignment */ + okx ( ( ( ( intptr_t ) iobuf ) & ( __alignof__ ( *iobuf ) - 1 ) ) == 0, + file, line ); + okx ( iob_tailroom ( iobuf ) >= len, file, line ); + okx ( ( ( align == 0 ) || + ( ( virt_to_phys ( iobuf->data ) & ( align - 1 ) ) == + ( offset & ( align - 1 ) ) ) ), file, line ); + + /* Overwrite entire content of I/O buffer (for Valgrind) */ + memset ( iob_put ( iobuf, len ), 0x55, len ); + + /* Free I/O buffer */ + free_iob ( iobuf ); +} +#define alloc_iob_ok( len, align, offset ) \ + alloc_iob_okx ( len, align, offset, __FILE__, __LINE__ ) + +/** + * Report I/O buffer allocation failure test result + * + * @v len Required length of buffer + * @v align Physical alignment + * @v offset Offset from physical alignment + * @v file Test code file + * @v line Test code line + */ +static inline void alloc_iob_fail_okx ( size_t len, size_t align, size_t offset, + const char *file, unsigned int line ) { + struct io_buffer *iobuf; + + /* Allocate I/O buffer */ + iobuf = alloc_iob_raw ( len, align, offset ); + okx ( iobuf == NULL, file, line ); +} +#define alloc_iob_fail_ok( len, align, offset ) \ + alloc_iob_fail_okx ( len, align, offset, __FILE__, __LINE__ ) + +/** + * Perform I/O buffer self-tests + * + */ +static void iobuf_test_exec ( void ) { + + /* Check zero-length allocations */ + alloc_iob_ok ( 0, 0, 0 ); + alloc_iob_ok ( 0, 0, 1 ); + alloc_iob_ok ( 0, 1, 0 ); + alloc_iob_ok ( 0, 1024, 0 ); + alloc_iob_ok ( 0, 139, -17 ); + + /* Check various sensible allocations */ + alloc_iob_ok ( 1, 0, 0 ); + alloc_iob_ok ( 16, 16, 0 ); + alloc_iob_ok ( 64, 0, 0 ); + alloc_iob_ok ( 65, 0, 0 ); + alloc_iob_ok ( 65, 1024, 19 ); + alloc_iob_ok ( 1536, 1536, 0 ); + alloc_iob_ok ( 2048, 2048, 0 ); + alloc_iob_ok ( 2048, 2048, -10 ); + + /* Excessively large or excessively aligned allocations should fail */ + alloc_iob_fail_ok ( -1UL, 0, 0 ); + alloc_iob_fail_ok ( -1UL, 1024, 0 ); + alloc_iob_fail_ok ( 0, -1UL, 0 ); + alloc_iob_fail_ok ( 1024, -1UL, 0 ); +} + +/** I/O buffer self-test */ +struct self_test iobuf_test __self_test = { + .name = "iobuf", + .exec = iobuf_test_exec, +}; diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c index 772eb1b82..9b3a744df 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -41,6 +41,38 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Define inline IPv6 address */ #define IPV6(...) { __VA_ARGS__ } +/** An IPv6 test routing table entry */ +struct ipv6_test_route { + /** Local address */ + const char *address; + /** Prefix length */ + unsigned int prefix_len; + /** Router address (if any) */ + const char *router; +}; + +/** An IPv6 test routing table */ +struct ipv6_test_table { + /** Test routing table entries */ + const struct ipv6_test_route *routes; + /** Number of table entries */ + unsigned int count; + /** Constructed routing table */ + struct list_head list; +}; + +/** Define a test routing table */ +#define TABLE( name, ... ) \ + static const struct ipv6_test_route name ## _routes[] = { \ + __VA_ARGS__ \ + }; \ + static struct ipv6_test_table name = { \ + .routes = name ## _routes, \ + .count = ( sizeof ( name ## _routes ) / \ + sizeof ( name ## _routes[0] ) ), \ + .list = LIST_HEAD_INIT ( name.list ), \ + }; + /** The unspecified IPv6 address */ static const struct in6_addr sample_unspecified = { .s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -53,6 +85,18 @@ static const struct in6_addr sample_link_local = { 0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ), }; +/** A sample site-local IPv6 address */ +static const struct in6_addr sample_site_local = { + .s6_addr = IPV6 ( 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ), +}; + +/** A sample ULA IPv6 address */ +static const struct in6_addr sample_ula = { + .s6_addr = IPV6 ( 0xfd, 0x44, 0x91, 0x12, 0x64, 0x42, 0x00, 0x00, + 0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ), +}; + /** A sample global IPv6 address */ static const struct in6_addr sample_global = { .s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4, @@ -65,27 +109,57 @@ static const struct in6_addr sample_multicast = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), }; +/** Dummy network device used for routing tests */ +static struct net_device ipv6_test_netdev = { + .refcnt = REF_INIT ( ref_no_free ), + .index = 42, + .state = NETDEV_OPEN, +}; + +/** Routing table with only a link-local address */ +TABLE ( table_link_local, + { "fe80::69ff:fe50:5845", 64, NULL } ); + +/** Routing table with a global address */ +TABLE ( table_normal, + { "fe80::69ff:fe50:5845", 64, NULL }, + { "2001:db8:3::1", 64, "fe80::1" } ); + +/** Routing table with multiple addresses and routers */ +TABLE ( table_multi, + { "fe80::69ff:fe50:5845", 64, NULL }, + { "2001:db8:3::1", 64, "fe80::1" }, + { "2001:db8:5::1", 64, NULL }, + { "2001:db8:42::1", 64, "fe80::2" }, + { "fd44:9112:6442::69ff:fe50:5845", 64, "fe80::1" }, + { "fd70:6ba9:50ae::69ff:fe50:5845", 64, "fe80::3" } ); + /** * Report an inet6_ntoa() test result * * @v addr IPv6 address * @v text Expected textual representation + * @v file Test code file + * @v line Test code line */ +static void inet6_ntoa_okx ( const struct in6_addr *addr, const char *text, + const char *file, unsigned int line ) { + char *actual; + + actual = inet6_ntoa ( addr ); + DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) " + "= %s\n", ntohs ( addr->s6_addr16[0] ), + ntohs ( addr->s6_addr16[1] ), ntohs ( addr->s6_addr16[2] ), + ntohs ( addr->s6_addr16[3] ), ntohs ( addr->s6_addr16[4] ), + ntohs ( addr->s6_addr16[5] ), ntohs ( addr->s6_addr16[6] ), + ntohs ( addr->s6_addr16[7] ), actual ); + okx ( strcmp ( actual, text ) == 0, file, line ); +} #define inet6_ntoa_ok( addr, text ) do { \ static const struct in6_addr in = { \ .s6_addr = addr, \ }; \ - static const char expected[] = text; \ - char *actual; \ - \ - actual = inet6_ntoa ( &in ); \ - DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) " \ - "= %s\n", ntohs ( in.s6_addr16[0] ), \ - ntohs ( in.s6_addr16[1] ), ntohs ( in.s6_addr16[2] ), \ - ntohs ( in.s6_addr16[3] ), ntohs ( in.s6_addr16[4] ), \ - ntohs ( in.s6_addr16[5] ), ntohs ( in.s6_addr16[6] ), \ - ntohs ( in.s6_addr16[7] ), actual ); \ - ok ( strcmp ( actual, expected ) == 0 ); \ + inet6_ntoa_okx ( &in, text, __FILE__, __LINE__ ); \ } while ( 0 ) /** @@ -93,31 +167,182 @@ static const struct in6_addr sample_multicast = { * * @v text Textual representation * @v addr Expected IPv6 address + * @v file Test code file + * @v line Test code line */ +static void inet6_aton_okx ( const char *text, const struct in6_addr *addr, + const char *file, unsigned int line ) { + struct in6_addr actual; + + okx ( inet6_aton ( text, &actual ) == 0, file, line ); + DBG ( "inet6_aton ( \"%s\" ) = %s\n", text, inet6_ntoa ( &actual ) ); + okx ( memcmp ( &actual, addr, sizeof ( actual ) ) == 0, + file, line ); +} #define inet6_aton_ok( text, addr ) do { \ - static const char string[] = text; \ - static const struct in6_addr expected = { \ + static const struct in6_addr in = { \ .s6_addr = addr, \ }; \ - struct in6_addr actual; \ - \ - ok ( inet6_aton ( string, &actual ) == 0 ); \ - DBG ( "inet6_aton ( \"%s\" ) = %s\n", string, \ - inet6_ntoa ( &actual ) ); \ - ok ( memcmp ( &actual, &expected, sizeof ( actual ) ) == 0 ); \ + inet6_aton_okx ( text, &in, __FILE__, __LINE__ ); \ } while ( 0 ) /** * Report an inet6_aton() failure test result * * @v text Textual representation + * @v file Test code file + * @v line Test code line */ -#define inet6_aton_fail_ok( text ) do { \ - static const char string[] = text; \ - struct in6_addr dummy; \ - \ - ok ( inet6_aton ( string, &dummy ) != 0 ); \ - } while ( 0 ) +static void inet6_aton_fail_okx ( const char *text, const char *file, + unsigned int line ) { + struct in6_addr dummy; + + okx ( inet6_aton ( text, &dummy ) != 0, file, line ); +} +#define inet6_aton_fail_ok( text ) \ + inet6_aton_fail_okx ( text, __FILE__, __LINE__ ) + +/** + * Create test routing table + * + * @v table Test routing table + * @v file Test code file + * @v line Test code line + */ +static void ipv6_table_okx ( struct ipv6_test_table *table, const char *file, + unsigned int line ) { + const struct ipv6_test_route *route; + struct in6_addr address; + struct in6_addr router; + struct list_head saved; + unsigned int i; + + /* Sanity check */ + okx ( list_empty ( &table->list ), file, line ); + + /* Save existing routing table */ + INIT_LIST_HEAD ( &saved ); + list_splice_init ( &ipv6_miniroutes, &saved ); + + /* Construct routing table */ + for ( i = 0 ; i < table->count ; i++ ) { + + /* Parse address and router (if applicable) */ + route = &table->routes[i]; + okx ( inet6_aton ( route->address, &address ) == 0, + file, line ); + if ( route->router ) { + okx ( inet6_aton ( route->router, &router ) == 0, + file, line ); + } + + /* Add routing table entry */ + okx ( ipv6_add_miniroute ( &ipv6_test_netdev, &address, + route->prefix_len, + ( route->router ? + &router : NULL ) ) == 0, + file, line ); + } + + /* Save constructed routing table */ + list_splice_init ( &ipv6_miniroutes, &table->list ); + + /* Restore original routing table */ + list_splice ( &saved, &ipv6_miniroutes ); +} +#define ipv6_table_ok( table ) \ + ipv6_table_okx ( table, __FILE__, __LINE__ ) + +/** + * Report an ipv6_route() test result + * + * @v table Test routing table + * @v dest Destination address + * @v src Expected source address, or NULL to expect failure + * @v next Expected next hop address, or NULL to expect destination + * @v file Test code file + * @v line Test code line + */ +static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest, + const char *src, const char *next, + const char *file, unsigned int line ) { + struct in6_addr in_dest; + struct in6_addr in_src; + struct in6_addr in_next; + struct in6_addr *actual; + struct ipv6_miniroute *miniroute; + struct list_head saved; + + /* Switch to test routing table */ + INIT_LIST_HEAD ( &saved ); + list_splice_init ( &ipv6_miniroutes, &saved ); + list_splice_init ( &table->list, &ipv6_miniroutes ); + + /* Parse addresses */ + okx ( inet6_aton ( dest, &in_dest ) == 0, file, line ); + if ( src ) + okx ( inet6_aton ( src, &in_src ) == 0, file, line ); + if ( next ) { + okx ( inet6_aton ( next, &in_next ) == 0, file, line ); + } else { + memcpy ( &in_next, &in_dest, sizeof ( in_next ) ); + } + + /* Perform routing */ + actual = &in_dest; + miniroute = ipv6_route ( ipv6_test_netdev.index, &actual ); + + /* Validate result */ + if ( src ) { + + /* Check that a route was found */ + okx ( miniroute != NULL, file, line ); + DBG ( "ipv6_route ( %s ) = %s", dest, inet6_ntoa ( actual ) ); + DBG ( " from %s\n", inet6_ntoa ( &miniroute->address ) ); + + /* Check that expected source address was used */ + okx ( memcmp ( &miniroute->address, &in_src, + sizeof ( in_src ) ) == 0, file, line ); + + /* Check that expected next hop address was used */ + okx ( memcmp ( actual, &in_next, sizeof ( *actual ) ) == 0, + file, line ); + + } else { + + /* Routing is expected to fail */ + okx ( miniroute == NULL, file, line ); + } + + /* Restore original routing table */ + list_splice_init ( &ipv6_miniroutes, &table->list ); + list_splice ( &saved, &ipv6_miniroutes ); +} +#define ipv6_route_ok( table, dest, src, next ) \ + ipv6_route_okx ( table, dest, src, next, __FILE__, __LINE__ ) + +/** + * Destroy test routing table + * + * @v table Test routing table + */ +static void ipv6_table_del ( struct ipv6_test_table *table ) { + struct ipv6_miniroute *miniroute; + struct ipv6_miniroute *tmp; + struct list_head saved; + + /* Switch to test routing table */ + INIT_LIST_HEAD ( &saved ); + list_splice_init ( &ipv6_miniroutes, &saved ); + list_splice_init ( &table->list, &ipv6_miniroutes ); + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) + ipv6_del_miniroute ( miniroute ); + + /* Restore original routing table */ + list_splice ( &saved, &ipv6_miniroutes ); +} /** * Perform IPv6 self-tests @@ -128,16 +353,34 @@ static void ipv6_test_exec ( void ) { /* Address testing macros */ ok ( IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) ); ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_ula ) ); ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) ); ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) ); ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) ); ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_ula ) ); ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) ); ok ( IN6_IS_ADDR_MULTICAST ( &sample_multicast ) ); ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) ); ok ( IN6_IS_ADDR_LINKLOCAL ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_ula ) ); ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) ); ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_multicast ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_unspecified ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_link_local ) ); + ok ( IN6_IS_ADDR_SITELOCAL ( &sample_site_local ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_ula ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_global ) ); + ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_multicast ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_unspecified ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_site_local ) ); + ok ( IN6_IS_ADDR_ULA ( &sample_ula ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_global ) ); + ok ( ! IN6_IS_ADDR_ULA ( &sample_multicast ) ); /* inet6_ntoa() tests */ inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4, @@ -214,6 +457,58 @@ static void ipv6_test_exec ( void ) { inet6_aton_fail_ok ( "2001:db8::1::2" ); inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" ); inet6_aton_fail_ok ( ":::" ); + + /* Create test routing tables */ + ipv6_table_ok ( &table_link_local ); + ipv6_table_ok ( &table_normal ); + ipv6_table_ok ( &table_multi ); + + /* Routing table with only a link-local address */ + ipv6_route_ok ( &table_link_local, "fe80::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_link_local, "2001:db8:1::1", + NULL, NULL ); + ipv6_route_ok ( &table_link_local, "ff02::1", + "fe80::69ff:fe50:5845", NULL ); + + /** Routing table with a global address */ + ipv6_route_ok ( &table_normal, "fe80::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_normal, "2001:db8:3::42", + "2001:db8:3::1", NULL ); + ipv6_route_ok ( &table_normal, "2001:ba8:0:1d4::6950:5845", + "2001:db8:3::1", "fe80::1" ); + ipv6_route_ok ( &table_normal, "ff02::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_normal, "ff0e::1", + "2001:db8:3::1", NULL ); + + /** Routing table with multiple addresses and routers */ + ipv6_route_ok ( &table_multi, "fe80::1", + "fe80::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:3::17", + "2001:db8:3::1", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:5::92", + "2001:db8:5::1", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:42::17", + "2001:db8:42::1", NULL ); + ipv6_route_ok ( &table_multi, "2001:db8:5:1::17", + "2001:db8:3::1", "fe80::1" ); + ipv6_route_ok ( &table_multi, "fd44:9112:6442::1", + "fd44:9112:6442::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_multi, "fd70:6ba9:50ae::1", + "fd70:6ba9:50ae::69ff:fe50:5845", NULL ); + ipv6_route_ok ( &table_multi, "fd40::3", + "fd44:9112:6442::69ff:fe50:5845", "fe80::1" ); + ipv6_route_ok ( &table_multi, "fd70::2", + "fd70:6ba9:50ae::69ff:fe50:5845", "fe80::3" ); + ipv6_route_ok ( &table_multi, "ff02::1", + "fe80::69ff:fe50:5845", NULL ); + + /* Destroy test routing tables */ + ipv6_table_del ( &table_link_local ); + ipv6_table_del ( &table_normal ); + ipv6_table_del ( &table_multi ); } /** IPv6 self-test */ diff --git a/src/tests/list_test.c b/src/tests/list_test.c index 352c87da0..d5b5c65db 100644 --- a/src/tests/list_test.c +++ b/src/tests/list_test.c @@ -396,6 +396,50 @@ static void list_test_exec ( void ) { ok ( list_first_entry ( list, struct list_test, list ) == NULL ); ok ( list_last_entry ( list, struct list_test, list ) == NULL ); + /* Test list_next_entry() and list_prev_entry() */ + INIT_LIST_HEAD ( list ); + list_add_tail ( &list_tests[5].list, list ); + list_add_tail ( &list_tests[3].list, list ); + list_add_tail ( &list_tests[1].list, list ); + list_add_tail ( &list_tests[7].list, list ); + ok ( list_prev_entry ( &list_tests[5], list, list ) == NULL ); + ok ( list_next_entry ( &list_tests[5], list, list ) == &list_tests[3] ); + ok ( list_prev_entry ( &list_tests[3], list, list ) == &list_tests[5] ); + ok ( list_next_entry ( &list_tests[3], list, list ) == &list_tests[1] ); + ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[3] ); + ok ( list_next_entry ( &list_tests[1], list, list ) == &list_tests[7] ); + ok ( list_prev_entry ( &list_tests[7], list, list ) == &list_tests[1] ); + ok ( list_next_entry ( &list_tests[7], list, list ) == NULL ); + list_del ( &list_tests[7].list ); + ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[3] ); + ok ( list_next_entry ( &list_tests[1], list, list ) == NULL ); + list_del ( &list_tests[3].list ); + ok ( list_prev_entry ( &list_tests[5], list, list ) == NULL ); + ok ( list_next_entry ( &list_tests[5], list, list ) == &list_tests[1] ); + ok ( list_prev_entry ( &list_tests[1], list, list ) == &list_tests[5] ); + ok ( list_next_entry ( &list_tests[1], list, list ) == NULL ); + + /* Test list_is_first_entry() and list_is_last_entry() */ + INIT_LIST_HEAD ( list ); + list_add_tail ( &list_tests[4].list, list ); + list_add_tail ( &list_tests[8].list, list ); + list_add_tail ( &list_tests[3].list, list ); + list_add_tail ( &list_tests[6].list, list ); + ok ( list_is_first_entry ( &list_tests[4], list, list ) ); + ok ( ! list_is_first_entry ( &list_tests[8], list, list ) ); + ok ( ! list_is_first_entry ( &list_tests[3], list, list ) ); + ok ( ! list_is_first_entry ( &list_tests[6], list, list ) ); + ok ( ! list_is_last_entry ( &list_tests[4], list, list ) ); + ok ( ! list_is_last_entry ( &list_tests[8], list, list ) ); + ok ( ! list_is_last_entry ( &list_tests[3], list, list ) ); + ok ( list_is_last_entry ( &list_tests[6], list, list ) ); + list_del ( &list_tests[4].list ); + ok ( list_is_first_entry ( &list_tests[8], list, list ) ); + list_del ( &list_tests[8].list ); + list_del ( &list_tests[6].list ); + ok ( list_is_first_entry ( &list_tests[3], list, list ) ); + ok ( list_is_last_entry ( &list_tests[3], list, list ) ); + /* Test list_for_each() */ INIT_LIST_HEAD ( list ); list_add_tail ( &list_tests[6].list, list ); diff --git a/src/tests/md4_test.c b/src/tests/md4_test.c new file mode 100644 index 000000000..b6528c6ec --- /dev/null +++ b/src/tests/md4_test.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 tests + * + * Test inputs borrowed from NIST SHA-1 tests, with results calculated + * using "openssl dgst -md4" + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include "digest_test.h" + +/* Empty test vector */ +DIGEST_TEST ( md4_empty, &md4_algorithm, DIGEST_EMPTY, + DIGEST ( 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, 0xb7, + 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( md4_nist_abc, &md4_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52, 0x5f, + 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d ) ); + +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( md4_nist_abc_opq, &md4_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x46, 0x91, 0xa9, 0xec, 0x81, 0xb1, 0xa6, 0xbd, 0x1a, + 0xb8, 0x55, 0x72, 0x40, 0xb2, 0x45, 0xc5 ) ); + +/** + * Perform MD4 self-test + * + */ +static void md4_test_exec ( void ) { + + /* Correctness tests */ + digest_ok ( &md4_empty ); + digest_ok ( &md4_nist_abc ); + digest_ok ( &md4_nist_abc_opq ); + + /* Speed tests */ + DBG ( "MD4 required %ld cycles per byte\n", + digest_cost ( &md4_algorithm ) ); +} + +/** MD4 self-test */ +struct self_test md4_test __self_test = { + .name = "md4", + .exec = md4_test_exec, +}; diff --git a/src/tests/ntlm_test.c b/src/tests/ntlm_test.c new file mode 100644 index 000000000..65a8b8c67 --- /dev/null +++ b/src/tests/ntlm_test.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 + * + * NTLM authentication self-tests + * + * The test vectors are taken from the MS-NLMP specification document. + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include + +/** A key generation test */ +struct ntlm_key_test { + /** Domain name (or NULL) */ + const char *domain; + /** User name (or NULL) */ + const char *username; + /** Password (or NULL) */ + const char *password; + /** Expected key */ + struct ntlm_key expected; +}; + +/** An authentication test */ +struct ntlm_authenticate_test { + /** Domain name (or NULL) */ + const char *domain; + /** User name (or NULL) */ + const char *username; + /** Password (or NULL) */ + const char *password; + /** Workstation (or NULL) */ + const char *workstation; + /** Nonce */ + struct ntlm_nonce nonce; + /** Challenge message */ + struct ntlm_challenge *challenge; + /** Length of Challenge message */ + size_t challenge_len; + /** Expected Authenticate message */ + struct ntlm_authenticate *expected; + /** Expected length of Authenticate message */ + size_t expected_len; +}; + +/** Define inline message data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a key generation digest test */ +#define KEY_TEST( name, DOMAIN, USERNAME, PASSWORD, EXPECTED ) \ + static struct ntlm_key_test name = { \ + .domain = DOMAIN, \ + .username = USERNAME, \ + .password = PASSWORD, \ + .expected = { \ + .raw = EXPECTED, \ + }, \ + }; + +/** Define an authentication test */ +#define AUTHENTICATE_TEST( name, DOMAIN, USERNAME, PASSWORD, \ + WORKSTATION, NONCE, CHALLENGE, EXPECTED ) \ + static const uint8_t name ## _challenge[] = CHALLENGE; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct ntlm_authenticate_test name = { \ + .domain = DOMAIN, \ + .username = USERNAME, \ + .password = PASSWORD, \ + .workstation = WORKSTATION, \ + .nonce = { \ + .raw = NONCE, \ + }, \ + .challenge = ( ( void * ) name ## _challenge ), \ + .challenge_len = sizeof ( name ## _challenge ), \ + .expected = ( ( void * ) name ## _expected ), \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** NTOWFv2() test from MS-NLMP specification */ +KEY_TEST ( msnlmp_ntowfv2, "Domain", "User", "Password", + DATA ( 0x0c, 0x86, 0x8a, 0x40, 0x3b, 0xfd, 0x7a, 0x93, 0xa3, 0x00, + 0x1e, 0xf2, 0x2e, 0xf0, 0x2e, 0x3f ) ); + +/** Authentication test from MS-NLMP specification */ +AUTHENTICATE_TEST ( msnlmp_authenticate, + "Domain", "User", "Password", "COMPUTER", + DATA ( 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa ), + DATA ( 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x33, 0x82, 0x8a, 0xe2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x24, 0x00, 0x44, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00 ), + DATA ( 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x54, 0x00, 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x5c, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xd8, 0x00, 0x00, 0x00, + 0x35, 0x82, 0x88, 0xe2, 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, + 0x00, 0x0f, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x50, 0x00, + 0x55, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x86, 0xc3, + 0x50, 0x97, 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, + 0x57, 0xcc, 0xcc, 0x19, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, + 0xaa, 0xbc, 0x92, 0x7b, 0xeb, 0xef, 0x6a, 0x1c, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, + 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xda, 0xd2, 0x54, + 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, + 0xd0, 0x3e ) ); + +/** + * Report key generation test result + * + * @v test Key generation test + * @v file Test code file + * @v line Test code line + */ +static void ntlm_key_okx ( struct ntlm_key_test *test, + const char *file, unsigned int line ) { + struct ntlm_key key; + + ntlm_key ( test->domain, test->username, test->password, &key ); + okx ( memcmp ( &key, &test->expected, sizeof ( key ) ) == 0, + file, line ); +} +#define ntlm_key_ok( test ) \ + ntlm_key_okx ( test, __FILE__, __LINE__ ) + +/** + * Report NTLM variable-length data test result + * + * @v msg Message header + * @v msg_len Length of message + * @v data Variable-length data descriptor + * @v expected Expected message header + * @v expected_data Expected variable-length data descriptor + * @v field Field name + * @v file Test code file + * @v line Test code line + */ +static void ntlm_data_okx ( struct ntlm_header *msg, size_t msg_len, + struct ntlm_data *data, + struct ntlm_header *expected, + struct ntlm_data *expected_data, + const char *field, const char *file, + unsigned int line ) { + size_t offset; + size_t len; + void *raw; + void *expected_raw; + + /* Verify data lies within message */ + okx ( data->len == data->max_len, file, line ); + offset = le32_to_cpu ( data->offset ); + len = le16_to_cpu ( data->len ); + okx ( offset <= msg_len, file, line ); + okx ( len <= ( msg_len - offset ), file, line ); + + /* Verify content matches expected content */ + raw = ( ( ( void * ) msg ) + offset ); + expected_raw = ( ( ( void * ) expected ) + + le32_to_cpu ( expected_data->offset ) ); + DBGC ( msg, "NTLM %s expected:\n", field ); + DBGC_HDA ( msg, 0, expected_raw, le16_to_cpu ( expected_data->len ) ); + DBGC ( msg, "NTLM %s actual:\n", field ); + DBGC_HDA ( msg, 0, raw, len ); + okx ( data->len == expected_data->len, file, line ); + okx ( memcmp ( raw, expected_raw, len ) == 0, file, line ); +} +#define ntlm_data_ok( msg, msg_len, data, expected, expected_data ) \ + ntlm_data_okx ( msg, msg_len, data, expected, expected_data, \ + __FILE__, __LINE__ ) + +/** + * Report NTLM authentication test result + * + * @v test Authentication test + * @v file Test code file + * @v line Test code line + */ +static void ntlm_authenticate_okx ( struct ntlm_authenticate_test *test, + const char *file, unsigned int line ) { + struct ntlm_authenticate *expected = test->expected; + struct ntlm_challenge_info info; + struct ntlm_authenticate *auth; + struct ntlm_key key; + struct ntlm_lm_response lm; + struct ntlm_nt_response nt; + size_t len; + + /* Parse Challenge message */ + okx ( ntlm_challenge ( test->challenge, test->challenge_len, + &info ) == 0, file, line ); + + /* Generate key */ + ntlm_key ( test->domain, test->username, test->password, &key ); + + /* Generate responses */ + ntlm_response ( &info, &key, &test->nonce, &lm, &nt ); + + /* Allocate buffer for Authenticate message */ + len = ntlm_authenticate_len ( &info, test->domain, test->username, + test->workstation ); + okx ( len >= sizeof ( *auth ), file, line ); + auth = malloc ( len ); + okx ( auth != NULL, file, line ); + + /* Construct Authenticate message */ + okx ( ntlm_authenticate ( &info, test->domain, test->username, + test->workstation, &lm, &nt, auth ) == len, + file, line ); + + /* Verify header */ + okx ( memcmp ( &auth->header, &expected->header, + sizeof ( auth->header ) ) == 0, file, line ); + + /* Verify LAN Manager response */ + ntlm_data_okx ( &auth->header, len, &auth->lm, &expected->header, + &expected->lm, "LM", file, line ); + + /* Verify NT response */ + ntlm_data_okx ( &auth->header, len, &auth->nt, &expected->header, + &expected->nt, "NT", file, line ); + + /* Verify domain name */ + ntlm_data_okx ( &auth->header, len, &auth->domain, &expected->header, + &expected->domain, "domain", file, line ); + + /* Verify user name */ + ntlm_data_okx ( &auth->header, len, &auth->user, &expected->header, + &expected->user, "user", file, line ); + + /* Verify workstation name */ + ntlm_data_okx ( &auth->header, len, &auth->workstation, + &expected->header, &expected->workstation, + "workstation",file, line ); + + /* Verify session key */ + if ( auth->flags & NTLM_NEGOTIATE_KEY_EXCH ) { + ntlm_data_okx ( &auth->header, len, &auth->session, + &expected->header, &expected->session, + "session", file, line ); + } + + /* Free Authenticate message */ + free ( auth ); +} +#define ntlm_authenticate_ok( test ) \ + ntlm_authenticate_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform NTLM self-test + * + */ +static void ntlm_test_exec ( void ) { + + /* Verify key generation */ + ntlm_key_ok ( &msnlmp_ntowfv2 ); + + /* Verify authentication response */ + ntlm_authenticate_ok ( &msnlmp_authenticate ); +} + +/** NTLM self-test */ +struct self_test ntlm_test __self_test = { + .name = "ntlm", + .exec = ntlm_test_exec, +}; diff --git a/src/tests/ocsp_test.c b/src/tests/ocsp_test.c index c6d458596..a3349346a 100644 --- a/src/tests/ocsp_test.c +++ b/src/tests/ocsp_test.c @@ -110,7 +110,7 @@ static void ocsp_prepare_test ( struct ocsp_test *test ) { x509_invalidate ( cert ); /* Force-validate issuer certificate */ - issuer->valid = 1; + issuer->flags |= X509_FL_VALIDATED; issuer->path_remaining = ( issuer->extensions.basic.path_len + 1 ); } diff --git a/src/tests/pem_test.c b/src/tests/pem_test.c new file mode 100644 index 000000000..df47ad501 --- /dev/null +++ b/src/tests/pem_test.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 + * + * PEM self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "asn1_test.h" + +/** Define inline expected digest */ +#define DIGEST(...) { { __VA_ARGS__ } } + +/** Single RSA private key */ +ASN1 ( single, &pem_image_type, + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC6loItAgMBAAECBCqhYIkCAwDyVwIDAMUbAgMAr9kCAmr9AgIaWQ==\n" + "-----END RSA PRIVATE KEY-----\n", + DIGEST ( 0xb9, 0x38, 0x83, 0xcd, 0xf4, 0x58, 0xa9, 0xa2, 0x84, 0x11, + 0xfa, 0x0b, 0x6f, 0xdc, 0x3e, 0xa3, 0x7c, 0x90, 0x7c, 0x2d ) ); + +/** Three concatenated RSA private keys */ +ASN1 ( multiple, &pem_image_type, + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQDtbjyVAgMBAAECBQCEOtJxAgMA+xsCAwDyDwICLGsCAgqTAgIxVQ==\n" + "-----END RSA PRIVATE KEY-----\n" + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC3VlyxAgMBAAECBGakxDUCAwDanwIDANavAgIBWQICTuECAwCmWg==\n" + "-----END RSA PRIVATE KEY-----\n" + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC89dS1AgMBAAECBQCxjnLBAgMA3qcCAwDZQwICP3cCAgpRAgI57A==\n" + "-----END RSA PRIVATE KEY-----\n", + DIGEST ( 0x9c, 0xb2, 0xc1, 0xa0, 0x9c, 0xcb, 0x11, 0xbf, 0x80, 0xd0, + 0x8c, 0xe5, 0xda, 0xf2, 0x3b, 0x2c, 0xca, 0x64, 0x25, 0x8a ), + DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed, + 0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ), + DIGEST ( 0x01, 0xd2, 0x8a, 0x74, 0x42, 0x08, 0x0f, 0xb0, 0x03, 0x82, + 0xcd, 0xa3, 0xdc, 0x78, 0xfe, 0xd7, 0xa3, 0x28, 0xfc, 0x29 ) ); + +/** Two RSA private keys with various bits of noise added */ +ASN1 ( noisy, &pem_image_type, + "Hello world! This is uninteresting stuff before the actual data.\n" + "-----BEGIN RSA PRIVATE KEY-----\n" + "MCwCAQACBQC3VlyxAgMBAAECBGakxDUCAwDanwIDANavAgIBWQICTuECAwCmWg==\n" + "-----END RSA PRIVATE KEY-----\n" + "Here is some more uninteresting stuff.\n" + "Followed by what is actually another RSA private key, but with " + "extra whitespace added, and the description change to pretend " + "it's a certificate\n" + "-----BEGIN CERTIFICATE-----\n" + " MCwCAQACBQC6loItAgMBAAECBCqhYIkCAwD\r\n" + " yVwIDAMUbAgMAr9kCAmr9AgIaWQ== \r\n" + "-----END CERTIFICATE-----\n" + "and some trailing garbage as well\n" + "and more garbage with no final newline", + DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed, + 0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ), + DIGEST ( 0xb9, 0x38, 0x83, 0xcd, 0xf4, 0x58, 0xa9, 0xa2, 0x84, 0x11, + 0xfa, 0x0b, 0x6f, 0xdc, 0x3e, 0xa3, 0x7c, 0x90, 0x7c, 0x2d ) ); + +/** + * Perform PEM self-test + * + */ +static void pem_test_exec ( void ) { + + /* Perform tests */ + asn1_ok ( &single ); + asn1_ok ( &multiple ); + asn1_ok ( &noisy ); +} + +/** PEM self-test */ +struct self_test pem_test __self_test = { + .name = "pem", + .exec = pem_test_exec, +}; diff --git a/src/tests/rsa_test.c b/src/tests/rsa_test.c index c0d05d263..91066faab 100644 --- a/src/tests/rsa_test.c +++ b/src/tests/rsa_test.c @@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Forcibly enable assertions */ #undef NDEBUG +#include #include #include #include diff --git a/src/tests/setjmp_test.c b/src/tests/setjmp_test.c index 50ad13f3c..deafcee09 100644 --- a/src/tests/setjmp_test.c +++ b/src/tests/setjmp_test.c @@ -111,8 +111,9 @@ static void setjmp_return_ok ( struct setjmp_test *test, int value ) { * @v file Test code file * @v line Test code line */ -static void longjmp_okx ( struct setjmp_test *test, int value, - const char *file, unsigned int line ) { +static void __attribute__ (( noreturn )) +longjmp_okx ( struct setjmp_test *test, int value, + const char *file, unsigned int line ) { /* Record expected value. A zero passed to longjmp() should * result in setjmp() returning a value of one. diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 89203d42d..828901b06 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -422,7 +422,9 @@ static void settings_test_exec ( void ) { /* "busdevfn" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_busdevfn_setting, - RAW ( 0x03, 0x45 ), "03:08.5" ); + RAW ( 0x03, 0x45 ), "0000:03:08.5" ); + fetchf_ok ( &test_settings, &test_busdevfn_setting, + RAW ( 0x00, 0x02, 0x0a, 0x21 ), "0002:0a:04.1" ); /* Clear and unregister test settings block */ clear_settings ( &test_settings ); diff --git a/src/tests/tests.c b/src/tests/tests.c index 54ce86677..2e812d6ff 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -46,6 +46,7 @@ REQUIRE_OBJECT ( tcpip_test ); REQUIRE_OBJECT ( ipv4_test ); REQUIRE_OBJECT ( ipv6_test ); REQUIRE_OBJECT ( crc32_test ); +REQUIRE_OBJECT ( md4_test ); REQUIRE_OBJECT ( md5_test ); REQUIRE_OBJECT ( sha1_test ); REQUIRE_OBJECT ( sha256_test ); @@ -67,3 +68,8 @@ REQUIRE_OBJECT ( profile_test ); REQUIRE_OBJECT ( setjmp_test ); REQUIRE_OBJECT ( pccrc_test ); REQUIRE_OBJECT ( linebuf_test ); +REQUIRE_OBJECT ( iobuf_test ); +REQUIRE_OBJECT ( bitops_test ); +REQUIRE_OBJECT ( der_test ); +REQUIRE_OBJECT ( pem_test ); +REQUIRE_OBJECT ( ntlm_test ); diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 42c1c43d4..92c2f9037 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -499,6 +499,18 @@ static struct uri_test uri_mailto = { { .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" } }; +/** Basic path-only URI */ +static struct uri_test uri_path = { + "/var/lib/tftpboot/pxelinux.0", + { .path = "/var/lib/tftpboot/pxelinux.0" }, +}; + +/** Path-only URI with escaped characters */ +static struct uri_test uri_path_escaped = { + "/hello%20world%3F", + { .path = "/hello world?" }, +}; + /** HTTP URI with all the trimmings */ static struct uri_test uri_http_all = { "http://anon:password@example.com:3001/~foo/cgi-bin/foo.pl?a=b&c=d#bit", @@ -598,6 +610,34 @@ static struct uri_test uri_iscsi = { }, }; +/** File URI with relative (opaque) path */ +static struct uri_test uri_file_relative = { + "file:boot/script.ipxe", + { + .scheme = "file", + .opaque = "boot/script.ipxe", + }, +}; + +/** File URI with absolute path */ +static struct uri_test uri_file_absolute = { + "file:/boot/script.ipxe", + { + .scheme = "file", + .path = "/boot/script.ipxe", + }, +}; + +/** File URI with volume name */ +static struct uri_test uri_file_volume = { + "file://hpilo/boot/script.ipxe", + { + .scheme = "file", + .host = "hpilo", + .path = "/boot/script.ipxe", + }, +}; + /** URI with port number */ static struct uri_port_test uri_explicit_port = { "http://192.168.0.1:8080/boot.php", @@ -713,9 +753,9 @@ static struct uri_pxe_test uri_pxe_absolute_path = { { .scheme = "tftp", .host = "192.168.0.2", - .path = "/absolute/path", + .path = "//absolute/path", }, - "tftp://192.168.0.2/absolute/path", + "tftp://192.168.0.2//absolute/path", }; /** PXE URI with relative path */ @@ -731,7 +771,7 @@ static struct uri_pxe_test uri_pxe_relative_path = { { .scheme = "tftp", .host = "192.168.0.3", - .path = "relative/path", + .path = "/relative/path", }, "tftp://192.168.0.3/relative/path", }; @@ -749,7 +789,7 @@ static struct uri_pxe_test uri_pxe_icky = { { .scheme = "tftp", .host = "10.0.0.6", - .path = "C:\\tftpboot\\icky#path", + .path = "/C:\\tftpboot\\icky#path", }, "tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path", }; @@ -769,9 +809,9 @@ static struct uri_pxe_test uri_pxe_port = { .scheme = "tftp", .host = "192.168.0.1", .port = "4069", - .path = "/another/path", + .path = "//another/path", }, - "tftp://192.168.0.1:4069/another/path", + "tftp://192.168.0.1:4069//another/path", }; /** Current working URI test */ @@ -877,6 +917,8 @@ static void uri_test_exec ( void ) { uri_parse_format_dup_ok ( &uri_empty ); uri_parse_format_dup_ok ( &uri_boot_ipxe_org ); uri_parse_format_dup_ok ( &uri_mailto ); + uri_parse_format_dup_ok ( &uri_path ); + uri_parse_format_dup_ok ( &uri_path_escaped ); uri_parse_format_dup_ok ( &uri_http_all ); uri_parse_format_dup_ok ( &uri_http_escaped ); uri_parse_ok ( &uri_http_escaped_improper ); /* Parse only */ @@ -885,6 +927,9 @@ static void uri_test_exec ( void ) { uri_parse_format_dup_ok ( &uri_ipv6_local ); uri_parse_ok ( &uri_ipv6_local_non_conforming ); /* Parse only */ uri_parse_format_dup_ok ( &uri_iscsi ); + uri_parse_format_dup_ok ( &uri_file_relative ); + uri_parse_format_dup_ok ( &uri_file_absolute ); + uri_parse_format_dup_ok ( &uri_file_volume ); /** URI port number tests */ uri_port_ok ( &uri_explicit_port ); diff --git a/src/tests/vsprintf_test.c b/src/tests/vsprintf_test.c index 0ad4f1c56..f388b3ded 100644 --- a/src/tests/vsprintf_test.c +++ b/src/tests/vsprintf_test.c @@ -39,21 +39,32 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Report an snprintf() test result * + * @v len Buffer length + * @v expected Expected result + * @v file Test code file + * @v line Test code line + * @v format Format string + * @v ... Arguments */ -#define snprintf_ok( len, result, format, ... ) do { \ - char actual[ (len) ]; \ - const char expected[] = result; \ - size_t actual_len; \ - \ - actual_len = snprintf ( actual, sizeof ( actual ), \ - format, ##__VA_ARGS__ ); \ - ok ( actual_len >= strlen ( result ) ); \ - ok ( strcmp ( actual, expected ) == 0 ); \ - if ( strcmp ( actual, expected ) != 0 ) { \ - DBG ( "SNPRINTF expected \"%s\", got \"%s\"\n", \ - expected, actual ); \ - } \ - } while ( 0 ) +static void snprintf_okx ( size_t len, const char *expected, const char *file, + unsigned int line, const char *fmt, ... ) { + char actual[len]; + size_t actual_len; + va_list args; + + va_start ( args, fmt ); + actual_len = vsnprintf ( actual, sizeof ( actual ), fmt, args ); + va_end ( args ); + okx ( actual_len >= strlen ( expected ), file, line ); + okx ( strcmp ( actual, expected ) == 0, file, line ); + if ( strcmp ( actual, expected ) != 0 ) { + DBG ( "SNPRINTF expected \"%s\", got \"%s\"\n", + expected, actual ); + } +} +#define snprintf_ok( len, result, format, ... ) \ + snprintf_okx ( len, result, __FILE__, __LINE__, format, \ + ##__VA_ARGS__ ) /** * Perform vsprintf() self-tests @@ -97,6 +108,10 @@ static void vsprintf_test_exec ( void ) { snprintf_ok ( 64, "PCI 00:1f.3", "PCI %02x:%02x.%x", 0x00, 0x1f, 0x03 ); snprintf_ok ( 64, "Region [1000000,3f000000)", "Region [%llx,%llx)", 0x1000000ULL, 0x3f000000ULL ); + + /* Null string (used for debug messages) */ + snprintf_ok ( 16, "", "%s", ( ( char * ) NULL ) ); + snprintf_ok ( 16, "", "%ls", ( ( wchar_t * ) NULL ) ); } /** vsprintf() self-test */ diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 0c48b601c..1a3b5f862 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -109,8 +109,10 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, * Boot from filename and root-path URIs * * @v filename Filename - * @v root_path Root path + * @v root_paths Root path(s) + * @v root_path_count Number of root paths * @v drive SAN drive (if applicable) + * @v san_filename SAN filename (or NULL to use default) * @v flags Boot action flags * @ret rc Return status code * @@ -120,14 +122,19 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, * provide backwards compatibility for the "keep-san" and * "skip-san-boot" options. */ -int uriboot ( struct uri *filename, struct uri *root_path, int drive, - unsigned int flags ) { +int uriboot ( struct uri *filename, struct uri **root_paths, + unsigned int root_path_count, int drive, + const char *san_filename, unsigned int flags ) { struct image *image; int rc; /* Hook SAN device, if applicable */ - if ( root_path ) { - if ( ( rc = san_hook ( root_path, drive ) ) != 0 ) { + if ( root_path_count ) { + drive = san_hook ( drive, root_paths, root_path_count, + ( ( flags & URIBOOT_NO_SAN_DESCRIBE ) ? + SAN_NO_DESCRIBE : 0 ) ); + if ( drive < 0 ) { + rc = drive; printf ( "Could not open SAN device: %s\n", strerror ( rc ) ); goto err_san_hook; @@ -136,10 +143,10 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, } /* Describe SAN device, if applicable */ - if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) { - if ( ( rc = san_describe ( drive ) ) != 0 ) { - printf ( "Could not describe SAN device %#02x: %s\n", - drive, strerror ( rc ) ); + if ( ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) { + if ( ( rc = san_describe() ) != 0 ) { + printf ( "Could not describe SAN devices: %s\n", + strerror ( rc ) ); goto err_san_describe; } } @@ -170,10 +177,12 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, } /* Attempt SAN boot if applicable */ - if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_BOOT ) ) { + if ( ! ( flags & URIBOOT_NO_SAN_BOOT ) ) { if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) { - printf ( "Booting from SAN device %#02x\n", drive ); - rc = san_boot ( drive ); + printf ( "Booting%s%s from SAN device %#02x\n", + ( san_filename ? " " : "" ), + ( san_filename ? san_filename : "" ), drive ); + rc = san_boot ( drive, san_filename ); printf ( "Boot from SAN device %#02x failed: %s\n", drive, strerror ( rc ) ); } else { @@ -188,7 +197,7 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, err_download: err_san_describe: /* Unhook SAN device, if applicable */ - if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) { + if ( ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) { if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) { san_unhook ( drive ); printf ( "Unregistered SAN device %#02x\n", drive ); @@ -295,10 +304,10 @@ static struct uri * fetch_root_path ( struct settings *settings ) { root_path = expand_settings ( raw_root_path ); if ( ! root_path ) goto err_expand; - - /* Parse root path */ if ( root_path[0] ) printf ( "Root path: %s\n", root_path ); + + /* Parse root path */ uri = parse_uri ( root_path ); if ( ! uri ) goto err_parse; @@ -311,6 +320,35 @@ static struct uri * fetch_root_path ( struct settings *settings ) { return uri; } +/** + * Fetch san-filename setting + * + * @v settings Settings block + * @ret san_filename SAN filename, or NULL on failure + */ +static char * fetch_san_filename ( struct settings *settings ) { + char *raw_san_filename; + char *san_filename = NULL; + + /* Fetch san-filename setting */ + fetch_string_setting_copy ( settings, &san_filename_setting, + &raw_san_filename ); + if ( ! raw_san_filename ) + goto err_fetch; + + /* Expand san-filename setting */ + san_filename = expand_settings ( raw_san_filename ); + if ( ! san_filename ) + goto err_expand; + if ( san_filename[0] ) + printf ( "SAN filename: %s\n", san_filename ); + + err_expand: + free ( raw_san_filename ); + err_fetch: + return san_filename; +} + /** * Check whether or not we have a usable PXE menu * @@ -346,6 +384,7 @@ static int have_pxe_menu ( void ) { int netboot ( struct net_device *netdev ) { struct uri *filename; struct uri *root_path; + char *san_filename; int rc; /* Close all other network devices */ @@ -374,13 +413,16 @@ int netboot ( struct net_device *netdev ) { /* Fetch root path (if any) */ root_path = fetch_root_path ( NULL ); + /* Fetch SAN filename (if any) */ + san_filename = fetch_san_filename ( NULL ); + /* If we have both a filename and a root path, ignore an * unsupported or missing URI scheme in the root path, since * it may represent an NFS root. */ if ( filename && root_path && - ( ! ( uri_is_absolute ( root_path ) || - ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) ) ) { + ( ( ! uri_is_absolute ( root_path ) ) || + ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) ) { printf ( "Ignoring unsupported root path\n" ); uri_put ( root_path ); root_path = NULL; @@ -394,12 +436,14 @@ int netboot ( struct net_device *netdev ) { } /* Boot using next server, filename and root path */ - if ( ( rc = uriboot ( filename, root_path, san_default_drive(), + if ( ( rc = uriboot ( filename, &root_path, ( root_path ? 1 : 0 ), + san_default_drive(), san_filename, ( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 ) goto err_uriboot; err_uriboot: err_no_boot: + free ( san_filename ); uri_put ( root_path ); uri_put ( filename ); err_pxe_menu_boot: diff --git a/src/usr/certmgmt.c b/src/usr/certmgmt.c new file mode 100644 index 000000000..2f233fe4f --- /dev/null +++ b/src/usr/certmgmt.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Certificate management + * + */ + +/** + * Display status of a certificate + * + * @v cert X.509 certificate + */ +void certstat ( struct x509_certificate *cert ) { + struct digest_algorithm *digest = &sha1_algorithm; + uint8_t fingerprint[ digest->digestsize ]; + char buf[ base16_encoded_len ( sizeof ( fingerprint ) ) + 1 /* NUL */ ]; + + /* Generate fingerprint */ + x509_fingerprint ( cert, digest, fingerprint ); + base16_encode ( fingerprint, sizeof ( fingerprint ), + buf, sizeof ( buf ) ); + + /* Print certificate status */ + printf ( "%s : %s", x509_name ( cert ), buf ); + if ( cert->flags & X509_FL_PERMANENT ) + printf ( " [PERMANENT]" ); + if ( cert->flags & X509_FL_EXPLICIT ) + printf ( " [EXPLICIT]" ); + if ( x509_is_valid ( cert ) ) + printf ( " [VALIDATED]" ); + printf ( "\n" ); +} diff --git a/src/usr/ibmgmt.c b/src/usr/ibmgmt.c new file mode 100644 index 000000000..7857664d6 --- /dev/null +++ b/src/usr/ibmgmt.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband device management + * + */ + +/** + * Print status of Infiniband device + * + * @v ibdev Infiniband device + */ +void ibstat ( struct ib_device *ibdev ) { + struct ib_queue_pair *qp; + + printf ( "%s: " IB_GUID_FMT " using %s on %s port %d (%s)\n", + ibdev->name, IB_GUID_ARGS ( &ibdev->gid.s.guid ), + ibdev->dev->driver_name, ibdev->dev->name, ibdev->port, + ( ib_is_open ( ibdev ) ? "open" : "closed" ) ); + if ( ib_link_ok ( ibdev ) ) { + printf ( " [Link:up LID %d prefix " IB_GUID_FMT "]\n", + ibdev->lid, IB_GUID_ARGS ( &ibdev->gid.s.prefix ) ); + } else { + printf ( " [Link:down, port state %d]\n", ibdev->port_state ); + } + list_for_each_entry ( qp, &ibdev->qps, list ) { + printf ( " QPN %#lx send %d/%d recv %d/%d %s\n", + qp->qpn, qp->send.fill, qp->send.num_wqes, + qp->recv.fill, qp->recv.num_wqes, qp->name ); + } +} diff --git a/src/usr/ifmgmt.c b/src/usr/ifmgmt.c index aefdaa45d..f367149f7 100644 --- a/src/usr/ifmgmt.c +++ b/src/usr/ifmgmt.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -50,6 +51,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EADDRNOTAVAIL, 0x01, \ "No configuration methods succeeded" ) +/** Human-readable error message */ +struct errortab ifmgmt_errors[] __errortab = { + __einfo_errortab ( EINFO_EADDRNOTAVAIL_CONFIG ), +}; + /** * Open network device * diff --git a/src/usr/imgmgmt.c b/src/usr/imgmgmt.c index 352dd0242..a01d6e291 100644 --- a/src/usr/imgmgmt.c +++ b/src/usr/imgmgmt.c @@ -50,16 +50,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int imgdownload ( struct uri *uri, unsigned long timeout, struct image **image ) { - const char *password; + struct uri uri_redacted; char *uri_string_redacted; int rc; /* Construct redacted URI */ - password = uri->password; - if ( password ) - uri->password = "***"; - uri_string_redacted = format_uri_alloc ( uri ); - uri->password = password; + memcpy ( &uri_redacted, uri, sizeof ( uri_redacted ) ); + uri_redacted.user = NULL; + uri_redacted.password = NULL; + uri_redacted.query = NULL; + uri_redacted.fragment = NULL; + uri_string_redacted = format_uri_alloc ( &uri_redacted ); if ( ! uri_string_redacted ) { rc = -ENOMEM; goto err_uri_string; diff --git a/src/usr/imgtrust.c b/src/usr/imgtrust.c index a269833a6..595ea6b25 100644 --- a/src/usr/imgtrust.c +++ b/src/usr/imgtrust.c @@ -50,30 +50,28 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int imgverify ( struct image *image, struct image *signature, const char *name ) { - size_t len; - void *data; + struct asn1_cursor *data; struct cms_signature *sig; struct cms_signer_info *info; time_t now; + int next; int rc; /* Mark image as untrusted */ image_untrust ( image ); - /* Copy signature to internal memory */ - len = signature->len; - data = malloc ( len ); - if ( ! data ) { - rc = -ENOMEM; - goto err_alloc; + /* Get raw signature data */ + next = image_asn1 ( signature, 0, &data ); + if ( next < 0 ) { + rc = next; + goto err_asn1; } - copy_from_user ( data, signature->data, 0, len ); /* Parse signature */ - if ( ( rc = cms_signature ( data, len, &sig ) ) != 0 ) + if ( ( rc = cms_signature ( data->data, data->len, &sig ) ) != 0 ) goto err_parse; - /* Free internal copy of signature */ + /* Free raw signature data */ free ( data ); data = NULL; @@ -107,7 +105,7 @@ int imgverify ( struct image *image, struct image *signature, cms_put ( sig ); err_parse: free ( data ); - err_alloc: + err_asn1: syslog ( LOG_ERR, "Image \"%s\" signature bad: %s\n", image->name, strerror ( rc ) ); return rc; diff --git a/src/usr/lotest.c b/src/usr/lotest.c index 6b328713c..6b75b5048 100644 --- a/src/usr/lotest.c +++ b/src/usr/lotest.c @@ -188,13 +188,15 @@ static int loopback_wait ( void *data, size_t len ) { * @v sender Sending network device * @v receiver Received network device * @v mtu Packet size (excluding link-layer headers) + * @v broadcast Use broadcast link-layer address * @ret rc Return status code */ int loopback_test ( struct net_device *sender, struct net_device *receiver, - size_t mtu ) { + size_t mtu, int broadcast ) { uint8_t *buf; uint32_t *seq; struct io_buffer *iobuf; + const void *ll_dest; unsigned int i; unsigned int successes; int rc; @@ -219,9 +221,13 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, return -ENOMEM; seq = ( ( void * ) buf ); + /* Determine destination address */ + ll_dest = ( broadcast ? sender->ll_broadcast : receiver->ll_addr ); + /* Print initial statistics */ - printf ( "Performing loopback test from %s to %s with %zd byte MTU\n", - sender->name, receiver->name, mtu ); + printf ( "Performing %sloopback test from %s to %s with %zd byte MTU\n", + ( broadcast ? "broadcast " : "" ), sender->name, + receiver->name, mtu ); ifstat ( sender ); ifstat ( receiver ); @@ -250,7 +256,7 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, /* Transmit packet */ if ( ( rc = net_tx ( iob_disown ( iobuf ), sender, - &lotest_protocol, receiver->ll_addr, + &lotest_protocol, ll_dest, sender->ll_addr ) ) != 0 ) { printf ( "\nFailed to transmit packet: %s", strerror ( rc ) ); diff --git a/src/usr/ntpmgmt.c b/src/usr/ntpmgmt.c new file mode 100644 index 000000000..765c6dc9e --- /dev/null +++ b/src/usr/ntpmgmt.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Michael Brown . + * + * 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 ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * NTP management + * + */ + +/** + * Get time and date via NTP + * + * @v hostname Hostname + * @ret rc Return status code + */ +int ntp ( const char *hostname ) { + int rc; + + /* Start NTP client */ + if ( ( rc = start_ntp ( &monojob, hostname ) ) != 0 ) + return rc; + + /* Wait for NTP to complete */ + if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 ) + return rc; + + return 0; +} diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c index 2d05d3f51..5e497f990 100644 --- a/src/usr/pxemenu.c +++ b/src/usr/pxemenu.c @@ -378,7 +378,7 @@ int pxe_menu_boot ( struct net_device *netdev ) { return -ENOMEM; /* Attempt boot */ - rc = uriboot ( uri, NULL, 0, URIBOOT_NO_SAN ); + rc = uriboot ( uri, NULL, 0, 0, NULL, URIBOOT_NO_SAN ); uri_put ( uri ); return rc; } diff --git a/src/util/efirom.c b/src/util/efirom.c index a982c19ae..943a66910 100644 --- a/src/util/efirom.c +++ b/src/util/efirom.c @@ -81,9 +81,11 @@ static void read_pe_info ( void *pe, uint16_t *machine, *machine = nt->nt32.FileHeader.Machine; switch ( *machine ) { case EFI_IMAGE_MACHINE_IA32: + case EFI_IMAGE_MACHINE_ARMTHUMB_MIXED: *subsystem = nt->nt32.OptionalHeader.Subsystem; break; case EFI_IMAGE_MACHINE_X64: + case EFI_IMAGE_MACHINE_AARCH64: *subsystem = nt->nt64.OptionalHeader.Subsystem; break; default: diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index dee70ee7a..6718df777 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -17,9 +17,6 @@ * 02110-1301, USA. */ -#define _GNU_SOURCE -#define PACKAGE "elf2efi" -#define PACKAGE_VERSION "1" #define FILE_LICENCE(...) extern void __file_licence ( void ) #include #include @@ -30,15 +27,89 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include #include #include -#include #define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) +#ifdef EFI_TARGET32 + +#define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS32 +#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC +#define EFI_IMAGE_FILE_MACHINE EFI_IMAGE_FILE_32BIT_MACHINE +#define ELFCLASS ELFCLASS32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Addr Elf32_Addr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE ELF32_R_TYPE +#define ELF_R_SYM ELF32_R_SYM + +#elif defined(EFI_TARGET64) + +#define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS64 +#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC +#define EFI_IMAGE_FILE_MACHINE 0 +#define ELFCLASS ELFCLASS64 +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Addr Elf64_Addr +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_R_TYPE ELF64_R_TYPE +#define ELF_R_SYM ELF64_R_SYM + +#endif + +#define ELF_MREL( mach, type ) ( (mach) | ( (type) << 16 ) ) + +/* Allow for building with older versions of elf.h */ +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_NONE 0 +#define R_AARCH64_ABS64 257 +#define R_AARCH64_CALL26 283 +#define R_AARCH64_JUMP26 282 +#define R_AARCH64_ADR_PREL_LO21 274 +#define R_AARCH64_ADR_PREL_PG_HI21 275 +#define R_AARCH64_ADD_ABS_LO12_NC 277 +#define R_AARCH64_LDST8_ABS_LO12_NC 278 +#define R_AARCH64_LDST16_ABS_LO12_NC 284 +#define R_AARCH64_LDST32_ABS_LO12_NC 285 +#define R_AARCH64_LDST64_ABS_LO12_NC 286 +#endif /* EM_AARCH64 */ +#ifndef R_ARM_CALL +#define R_ARM_CALL 28 +#endif +#ifndef R_ARM_THM_JUMP24 +#define R_ARM_THM_JUMP24 30 +#endif +#ifndef R_ARM_V4BX +#define R_ARM_V4BX 40 +#endif + +/* Seems to be missing from elf.h */ +#ifndef R_AARCH64_NULL +#define R_AARCH64_NULL 256 +#endif + #define EFI_FILE_ALIGN 0x20 +struct elf_file { + void *data; + size_t len; + const Elf_Ehdr *ehdr; +}; + struct pe_section { struct pe_section *next; EFI_IMAGE_SECTION_HEADER hdr; @@ -57,11 +128,7 @@ struct pe_relocs { struct pe_header { EFI_IMAGE_DOS_HEADER dos; uint8_t padding[128]; -#if defined(EFI_TARGET_IA32) - EFI_IMAGE_NT_HEADERS32 nt; -#elif defined(EFI_TARGET_X64) - EFI_IMAGE_NT_HEADERS64 nt; -#endif + EFI_IMAGE_NT_HEADERS nt; }; static struct pe_header efi_pe_header = { @@ -72,26 +139,15 @@ static struct pe_header efi_pe_header = { .nt = { .Signature = EFI_IMAGE_NT_SIGNATURE, .FileHeader = { -#if defined(EFI_TARGET_IA32) - .Machine = EFI_IMAGE_MACHINE_IA32, -#elif defined(EFI_TARGET_X64) - .Machine = EFI_IMAGE_MACHINE_X64, -#endif .TimeDateStamp = 0x10d1a884, .SizeOfOptionalHeader = sizeof ( efi_pe_header.nt.OptionalHeader ), .Characteristics = ( EFI_IMAGE_FILE_DLL | -#if defined(EFI_TARGET_IA32) - EFI_IMAGE_FILE_32BIT_MACHINE | -#endif + EFI_IMAGE_FILE_MACHINE | EFI_IMAGE_FILE_EXECUTABLE_IMAGE ), }, .OptionalHeader = { -#if defined(EFI_TARGET_IA32) - .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC, -#elif defined(EFI_TARGET_X64) - .Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC, -#endif + .Magic = EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC, .MajorLinkerVersion = 42, .MinorLinkerVersion = 42, .SectionAlignment = EFI_FILE_ALIGN, @@ -235,110 +291,175 @@ static size_t output_pe_reltab ( struct pe_relocs *pe_reltab, } /** - * Open input BFD file + * Read input ELF file * - * @v filename File name - * @ret ibfd BFD file + * @v name File name + * @v elf ELF file */ -static bfd * open_input_bfd ( const char *filename ) { - bfd *bfd; +static void read_elf_file ( const char *name, struct elf_file *elf ) { + static const unsigned char ident[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS, ELFDATA2LSB + }; + struct stat stat; + const Elf_Ehdr *ehdr; + const Elf_Shdr *shdr; + void *data; + size_t offset; + unsigned int i; + int fd; - /* Open the file */ - bfd = bfd_openr ( filename, NULL ); - if ( ! bfd ) { - eprintf ( "Cannot open %s: ", filename ); - bfd_perror ( NULL ); + /* Open file */ + fd = open ( name, O_RDONLY ); + if ( fd < 0 ) { + eprintf ( "Could not open %s: %s\n", name, strerror ( errno ) ); exit ( 1 ); } - /* The call to bfd_check_format() must be present, otherwise - * we get a segfault from later BFD calls. - */ - if ( ! bfd_check_format ( bfd, bfd_object ) ) { - eprintf ( "%s is not an object file: ", filename ); - bfd_perror ( NULL ); + /* Get file size */ + if ( fstat ( fd, &stat ) < 0 ) { + eprintf ( "Could not get size of %s: %s\n", + name, strerror ( errno ) ); exit ( 1 ); } + elf->len = stat.st_size; - return bfd; + /* Map file */ + data = mmap ( NULL, elf->len, PROT_READ, MAP_SHARED, fd, 0 ); + if ( data == MAP_FAILED ) { + eprintf ( "Could not map %s: %s\n", name, strerror ( errno ) ); + exit ( 1 ); + } + elf->data = data; + + /* Close file */ + close ( fd ); + + /* Check header */ + ehdr = elf->data; + if ( ( elf->len < sizeof ( *ehdr ) ) || + ( memcmp ( ident, ehdr->e_ident, sizeof ( ident ) ) != 0 ) ) { + eprintf ( "Invalid ELF header in %s\n", name ); + exit ( 1 ); + } + elf->ehdr = ehdr; + + /* Check section headers */ + for ( i = 0 ; i < ehdr->e_shnum ; i++ ) { + offset = ( ehdr->e_shoff + ( i * ehdr->e_shentsize ) ); + if ( elf->len < ( offset + sizeof ( *shdr ) ) ) { + eprintf ( "ELF section header outside file in %s\n", + name ); + exit ( 1 ); + } + shdr = ( data + offset ); + if ( ( shdr->sh_type != SHT_NOBITS ) && + ( ( elf->len < shdr->sh_offset ) || + ( ( ( elf->len - shdr->sh_offset ) < shdr->sh_size ) ))){ + eprintf ( "ELF section %d outside file in %s\n", + i, name ); + exit ( 1 ); + } + if ( shdr->sh_link >= ehdr->e_shnum ) { + eprintf ( "ELF section %d link section %d out of " + "range\n", i, shdr->sh_link ); + exit ( 1 ); + } + } } /** - * Read symbol table + * Get ELF string * - * @v bfd BFD file + * @v elf ELF file + * @v section String table section number + * @v offset String table offset + * @ret string ELF string */ -static asymbol ** read_symtab ( bfd *bfd ) { - long symtab_size; - asymbol **symtab; - long symcount; +static const char * elf_string ( struct elf_file *elf, unsigned int section, + size_t offset ) { + const Elf_Ehdr *ehdr = elf->ehdr; + const Elf_Shdr *shdr; + char *string; + char *last; - /* Get symbol table size */ - symtab_size = bfd_get_symtab_upper_bound ( bfd ); - if ( symtab_size < 0 ) { - bfd_perror ( "Could not get symbol table upper bound" ); + /* Locate section header */ + if ( section >= ehdr->e_shnum ) { + eprintf ( "Invalid ELF string section %d\n", section ); + exit ( 1 ); + } + shdr = ( elf->data + ehdr->e_shoff + ( section * ehdr->e_shentsize ) ); + + /* Sanity check section */ + if ( shdr->sh_type != SHT_STRTAB ) { + eprintf ( "ELF section %d (type %d) is not a string table\n", + section, shdr->sh_type ); + exit ( 1 ); + } + last = ( elf->data + shdr->sh_offset + shdr->sh_size - 1 ); + if ( *last != '\0' ) { + eprintf ( "ELF section %d is not NUL-terminated\n", section ); exit ( 1 ); } - /* Allocate and read symbol table */ - symtab = xmalloc ( symtab_size ); - symcount = bfd_canonicalize_symtab ( bfd, symtab ); - if ( symcount < 0 ) { - bfd_perror ( "Cannot read symbol table" ); + /* Locate string */ + if ( offset >= shdr->sh_size ) { + eprintf ( "Invalid ELF string offset %zd in section %d\n", + offset, section ); exit ( 1 ); } + string = ( elf->data + shdr->sh_offset + offset ); - return symtab; + return string; } /** - * Read relocation table + * Set machine architecture * - * @v bfd BFD file - * @v symtab Symbol table - * @v section Section - * @v symtab Symbol table - * @ret reltab Relocation table + * @v elf ELF file + * @v pe_header PE file header */ -static arelent ** read_reltab ( bfd *bfd, asymbol **symtab, - asection *section ) { - long reltab_size; - arelent **reltab; - long numrels; +static void set_machine ( struct elf_file *elf, struct pe_header *pe_header ) { + const Elf_Ehdr *ehdr = elf->ehdr; + uint16_t machine; - /* Get relocation table size */ - reltab_size = bfd_get_reloc_upper_bound ( bfd, section ); - if ( reltab_size < 0 ) { - bfd_perror ( "Could not get relocation table upper bound" ); + /* Identify machine architecture */ + switch ( ehdr->e_machine ) { + case EM_386: + machine = EFI_IMAGE_MACHINE_IA32; + break; + case EM_X86_64: + machine = EFI_IMAGE_MACHINE_X64; + break; + case EM_ARM: + machine = EFI_IMAGE_MACHINE_ARMTHUMB_MIXED; + break; + case EM_AARCH64: + machine = EFI_IMAGE_MACHINE_AARCH64; + break; + default: + eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); exit ( 1 ); } - /* Allocate and read relocation table */ - reltab = xmalloc ( reltab_size ); - numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab ); - if ( numrels < 0 ) { - bfd_perror ( "Cannot read relocation table" ); - exit ( 1 ); - } - - return reltab; + /* Set machine architecture */ + pe_header->nt.FileHeader.Machine = machine; } /** * Process section * - * @v bfd BFD file + * @v elf ELF file + * @v shdr ELF section header * @v pe_header PE file header - * @v section Section * @ret new New PE section */ -static struct pe_section * process_section ( bfd *bfd, - struct pe_header *pe_header, - asection *section ) { +static struct pe_section * process_section ( struct elf_file *elf, + const Elf_Shdr *shdr, + struct pe_header *pe_header ) { struct pe_section *new; + const char *name; size_t section_memsz; size_t section_filesz; - unsigned long flags = bfd_get_section_flags ( bfd, section ); unsigned long code_start; unsigned long code_end; unsigned long data_start; @@ -349,12 +470,15 @@ static struct pe_section * process_section ( bfd *bfd, unsigned long *applicable_start; unsigned long *applicable_end; + /* Get section name */ + name = elf_string ( elf, elf->ehdr->e_shstrndx, shdr->sh_name ); + /* Extract current RVA limits from file header */ code_start = pe_header->nt.OptionalHeader.BaseOfCode; code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode ); -#if defined(EFI_TARGET_IA32) +#if defined(EFI_TARGET32) data_start = pe_header->nt.OptionalHeader.BaseOfData; -#elif defined(EFI_TARGET_X64) +#elif defined(EFI_TARGET64) data_start = code_end; #endif data_mid = ( data_start + @@ -363,21 +487,21 @@ static struct pe_section * process_section ( bfd *bfd, pe_header->nt.OptionalHeader.SizeOfUninitializedData ); /* Allocate PE section */ - section_memsz = bfd_section_size ( bfd, section ); - section_filesz = ( ( flags & SEC_LOAD ) ? + section_memsz = shdr->sh_size; + section_filesz = ( ( shdr->sh_type == SHT_PROGBITS ) ? efi_file_align ( section_memsz ) : 0 ); new = xmalloc ( sizeof ( *new ) + section_filesz ); memset ( new, 0, sizeof ( *new ) + section_filesz ); /* Fill in section header details */ - strncpy ( ( char * ) new->hdr.Name, section->name, - sizeof ( new->hdr.Name ) ); + strncpy ( ( char * ) new->hdr.Name, name, sizeof ( new->hdr.Name ) ); new->hdr.Misc.VirtualSize = section_memsz; - new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section ); + new->hdr.VirtualAddress = shdr->sh_addr; new->hdr.SizeOfRawData = section_filesz; /* Fill in section characteristics and update RVA limits */ - if ( flags & SEC_CODE ) { + if ( ( shdr->sh_type == SHT_PROGBITS ) && + ( shdr->sh_flags & SHF_EXECINSTR ) ) { /* .text-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_CODE | @@ -386,7 +510,8 @@ static struct pe_section * process_section ( bfd *bfd, EFI_IMAGE_SCN_MEM_READ ); applicable_start = &code_start; applicable_end = &code_end; - } else if ( flags & SEC_DATA ) { + } else if ( ( shdr->sh_type == SHT_PROGBITS ) && + ( shdr->sh_flags & SHF_WRITE ) ) { /* .data-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | @@ -395,7 +520,7 @@ static struct pe_section * process_section ( bfd *bfd, EFI_IMAGE_SCN_MEM_WRITE ); applicable_start = &data_start; applicable_end = &data_mid; - } else if ( flags & SEC_READONLY ) { + } else if ( shdr->sh_type == SHT_PROGBITS ) { /* .rodata-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | @@ -403,7 +528,7 @@ static struct pe_section * process_section ( bfd *bfd, EFI_IMAGE_SCN_MEM_READ ); applicable_start = &data_start; applicable_end = &data_mid; - } else if ( ! ( flags & SEC_LOAD ) ) { + } else if ( shdr->sh_type == SHT_NOBITS ) { /* .bss-type section */ new->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA | @@ -413,19 +538,15 @@ static struct pe_section * process_section ( bfd *bfd, applicable_start = &data_mid; applicable_end = &data_end; } else { - eprintf ( "Unrecognised characteristics %#lx for section %s\n", - flags, section->name ); + eprintf ( "Unrecognised characteristics for section %s\n", + name ); exit ( 1 ); } /* Copy in section contents */ - if ( flags & SEC_LOAD ) { - if ( ! bfd_get_section_contents ( bfd, section, new->contents, - 0, section_memsz ) ) { - eprintf ( "Cannot read section %s: ", section->name ); - bfd_perror ( NULL ); - exit ( 1 ); - } + if ( shdr->sh_type == SHT_PROGBITS ) { + memcpy ( new->contents, ( elf->data + shdr->sh_offset ), + shdr->sh_size ); } /* Update RVA limits */ @@ -445,7 +566,7 @@ static struct pe_section * process_section ( bfd *bfd, /* Write RVA limits back to file header */ pe_header->nt.OptionalHeader.BaseOfCode = code_start; pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start ); -#if defined(EFI_TARGET_IA32) +#if defined(EFI_TARGET32) pe_header->nt.OptionalHeader.BaseOfData = data_start; #endif pe_header->nt.OptionalHeader.SizeOfInitializedData = @@ -465,43 +586,106 @@ static struct pe_section * process_section ( bfd *bfd, /** * Process relocation record * - * @v bfd BFD file - * @v section Section - * @v rel Relocation entry + * @v elf ELF file + * @v shdr ELF section header + * @v syms Symbol table + * @v nsyms Number of symbol table entries + * @v rel Relocation record * @v pe_reltab PE relocation table to fill in */ -static void process_reloc ( bfd *bfd __attribute__ (( unused )), - asection *section, arelent *rel, - struct pe_relocs **pe_reltab ) { - reloc_howto_type *howto = rel->howto; - asymbol *sym = *(rel->sym_ptr_ptr); - unsigned long offset = ( bfd_get_section_vma ( bfd, section ) + - rel->address ); +static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, + const Elf_Sym *syms, unsigned int nsyms, + const Elf_Rel *rel, struct pe_relocs **pe_reltab ) { + unsigned int type = ELF_R_TYPE ( rel->r_info ); + unsigned int sym = ELF_R_SYM ( rel->r_info ); + unsigned int mrel = ELF_MREL ( elf->ehdr->e_machine, type ); + size_t offset = ( shdr->sh_addr + rel->r_offset ); - if ( bfd_is_abs_section ( sym->section ) ) { + /* Look up symbol and process relocation */ + if ( sym >= nsyms ) { + eprintf ( "Symbol out of range\n" ); + exit ( 1 ); + } + if ( syms[sym].st_shndx == SHN_ABS ) { /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ - } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) { - /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ - } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { - /* Generate an 8-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, 8 ); - } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) { - /* Generate a 4-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, 4 ); - } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { - /* Generate a 2-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, 2 ); - } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) { - /* Skip PC-relative relocations; all relative offsets - * remain unaltered when the object is loaded. - */ } else { - eprintf ( "Unrecognised relocation type %s\n", howto->name ); - exit ( 1 ); + switch ( mrel ) { + case ELF_MREL ( EM_386, R_386_NONE ) : + case ELF_MREL ( EM_ARM, R_ARM_NONE ) : + case ELF_MREL ( EM_X86_64, R_X86_64_NONE ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_NONE ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_NULL ) : + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ + break; + case ELF_MREL ( EM_386, R_386_32 ) : + case ELF_MREL ( EM_ARM, R_ARM_ABS32 ) : + /* Generate a 4-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 4 ); + break; + case ELF_MREL ( EM_X86_64, R_X86_64_64 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ABS64 ) : + /* Generate an 8-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 8 ); + break; + case ELF_MREL ( EM_386, R_386_PC32 ) : + case ELF_MREL ( EM_ARM, R_ARM_CALL ) : + case ELF_MREL ( EM_ARM, R_ARM_REL32 ) : + case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) : + case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) : + case ELF_MREL ( EM_ARM, R_ARM_V4BX ): + case ELF_MREL ( EM_X86_64, R_X86_64_PC32 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_CALL26 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_JUMP26 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ADR_PREL_LO21 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ADR_PREL_PG_HI21 ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_ADD_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST8_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST16_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST32_ABS_LO12_NC ) : + case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST64_ABS_LO12_NC ) : + /* Skip PC-relative relocations; all relative + * offsets remain unaltered when the object is + * loaded. + */ + break; + default: + eprintf ( "Unrecognised relocation type %d\n", type ); + exit ( 1 ); + } + } +} + +/** + * Process relocation records + * + * @v elf ELF file + * @v shdr ELF section header + * @v stride Relocation record size + * @v pe_reltab PE relocation table to fill in + */ +static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr, + size_t stride, struct pe_relocs **pe_reltab ) { + const Elf_Shdr *symtab; + const Elf_Sym *syms; + const Elf_Rel *rel; + unsigned int nsyms; + unsigned int nrels; + unsigned int i; + + /* Identify symbol table */ + symtab = ( elf->data + elf->ehdr->e_shoff + + ( shdr->sh_link * elf->ehdr->e_shentsize ) ); + syms = ( elf->data + symtab->sh_offset ); + nsyms = ( symtab->sh_size / sizeof ( syms[0] ) ); + + /* Process each relocation */ + rel = ( elf->data + shdr->sh_offset ); + nrels = ( shdr->sh_size / stride ); + for ( i = 0 ; i < nrels ; i++ ) { + process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab ); + rel = ( ( ( const void * ) rel ) + stride ); } } @@ -696,53 +880,60 @@ static void write_pe_file ( struct pe_header *pe_header, static void elf2pe ( const char *elf_name, const char *pe_name, struct options *opts ) { char pe_name_tmp[ strlen ( pe_name ) + 1 ]; - bfd *bfd; - asymbol **symtab; - asection *section; - arelent **reltab; - arelent **rel; struct pe_relocs *pe_reltab = NULL; struct pe_section *pe_sections = NULL; struct pe_section **next_pe_section = &pe_sections; struct pe_header pe_header; + struct elf_file elf; + const Elf_Shdr *shdr; + size_t offset; + unsigned int i; FILE *pe; /* Create a modifiable copy of the PE name */ memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) ); - /* Open the file */ - bfd = open_input_bfd ( elf_name ); - symtab = read_symtab ( bfd ); + /* Read ELF file */ + read_elf_file ( elf_name, &elf ); /* Initialise the PE header */ memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); - pe_header.nt.OptionalHeader.AddressOfEntryPoint = - bfd_get_start_address ( bfd ); + set_machine ( &elf, &pe_header ); + pe_header.nt.OptionalHeader.AddressOfEntryPoint = elf.ehdr->e_entry; pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; - /* For each input section, build an output section and create - * the appropriate relocation records - */ - for ( section = bfd->sections ; section ; section = section->next ) { - /* Discard non-allocatable sections */ - if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) ) - continue; - /* Create output section */ - *(next_pe_section) = process_section ( bfd, &pe_header, - section ); - next_pe_section = &(*next_pe_section)->next; - /* Add relocations from this section */ - reltab = read_reltab ( bfd, symtab, section ); - for ( rel = reltab ; *rel ; rel++ ) - process_reloc ( bfd, section, *rel, &pe_reltab ); - free ( reltab ); + /* Process input sections */ + for ( i = 0 ; i < elf.ehdr->e_shnum ; i++ ) { + offset = ( elf.ehdr->e_shoff + ( i * elf.ehdr->e_shentsize ) ); + shdr = ( elf.data + offset ); + + /* Process section */ + if ( shdr->sh_flags & SHF_ALLOC ) { + + /* Create output section */ + *(next_pe_section) = process_section ( &elf, shdr, + &pe_header ); + next_pe_section = &(*next_pe_section)->next; + + } else if ( shdr->sh_type == SHT_REL ) { + + /* Process .rel relocations */ + process_relocs ( &elf, shdr, sizeof ( Elf_Rel ), + &pe_reltab ); + + } else if ( shdr->sh_type == SHT_RELA ) { + + /* Process .rela relocations */ + process_relocs ( &elf, shdr, sizeof ( Elf_Rela ), + &pe_reltab ); + } } /* Create the .reloc section */ *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); next_pe_section = &(*next_pe_section)->next; - /* Create the .reloc section */ + /* Create the .debug section */ *(next_pe_section) = create_debug_section ( &pe_header, basename ( pe_name_tmp ) ); next_pe_section = &(*next_pe_section)->next; @@ -757,8 +948,8 @@ static void elf2pe ( const char *elf_name, const char *pe_name, write_pe_file ( &pe_header, pe_sections, pe ); fclose ( pe ); - /* Close BFD file */ - bfd_close ( bfd ); + /* Unmap ELF file */ + munmap ( elf.data, elf.len ); } /** @@ -825,9 +1016,6 @@ int main ( int argc, char **argv ) { const char *infile; const char *outfile; - /* Initialise libbfd */ - bfd_init(); - /* Parse command-line arguments */ infile_index = parse_options ( argc, argv, &opts ); if ( argc != ( infile_index + 2 ) ) { diff --git a/src/util/genefidsk b/src/util/genefidsk new file mode 100755 index 000000000..7064f99b6 --- /dev/null +++ b/src/util/genefidsk @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Generate an EFI bootable disk image + +set -e + +function help() { + echo "Usage: ${0} [OPTIONS] " + echo + echo "where OPTIONS are:" + echo " -h Show this help" + echo " -b Specify boot file name (e.g. bootx64.efi)" + echo " -o FILE Save disk image to file" +} + +BOOT=bootx64.efi + +while getopts "hb:o:" opt; do + case ${opt} in + h) + help + exit 0 + ;; + b) + BOOT="${OPTARG}" + ;; + o) + OUT="${OPTARG}" + ;; + esac +done + +shift $((OPTIND - 1)) +IN=$1 + +if [ -z "${IN}" ]; then + echo "${0}: no input file given" >&2 + help + exit 1 +fi + +if [ -z "${OUT}" ]; then + echo "${0}: no output file given" >&2 + help + exit 1 +fi + +# Create sparse output file +rm -f ${OUT} +truncate -s 1440K ${OUT} + +# Format disk +mformat -i ${OUT} -f 1440 :: + +# Create directory structure +mmd -i ${OUT} ::efi +mmd -i ${OUT} ::efi/boot + +# Copy bootable image +mcopy -i ${OUT} ${IN} ::efi/boot/${BOOT} diff --git a/src/util/niclist.pl b/src/util/niclist.pl index 0600c8232..2668a1c03 100755 --- a/src/util/niclist.pl +++ b/src/util/niclist.pl @@ -19,7 +19,7 @@ use Getopt::Long qw(GetOptions); GetOptions( 'help' => \( my $help = 0 ), 'format=s' => \( my $format = 'text' ), - 'sort=s' => \( my $sort = 'bus,ipxe_driver,ipxe_name' ), + 'sort=s' => \( my $sort = 'bus-,ipxe_driver,ipxe_name' ), 'columns=s' => \( my $columns = 'bus,vendor_id,device_id,' . 'vendor_name,device_name,ipxe_driver,' . 'ipxe_name,ipxe_description,file,legacy_api' @@ -47,26 +47,26 @@ Output formats: Column names (default order): bus, vendor_id, device_id, vendor_name, device_name, ipxe_driver, ipxe_name, ipxe_description, file, legacy_api + +Default sort order (minus at the end means reverse sort): + bus-, ipxe_driver, ipxe_name EOM # Only load runtime requirements if actually in use -given($format) { - when( /csv/ ) { - eval { require Text::CSV; }; - die("Please install Text::CSV CPAN module to use this feature.\n") - if $@; - } - when( /json/ ) { - eval { require JSON; }; - die("Please install JSON CPAN module to use this feature.\n") - if $@; - } - when( /html/ ) { - eval { require HTML::Entities; }; - die("Please install HTML::Entities CPAN module to use this feature.\n") - if $@; - } - default { } +if ( $format =~ /csv/ ) { + eval { require Text::CSV; }; + die("Please install Text::CSV CPAN module to use this feature.\n") + if $@; +} +if ( $format =~ /json/ ) { + eval { require JSON; }; + die("Please install JSON CPAN module to use this feature.\n") + if $@; +} +if ( $format =~ /html/ ) { + eval { require HTML::Entities; }; + die("Please install HTML::Entities CPAN module to use this feature.\n") + if $@; } # Scan source dir and build NIC list @@ -339,8 +339,16 @@ sub sort_ipxe_nic_list { my @sorted_list = @{ $ipxe_nic_list }; while(@sort_column_names) { my $column_name = pop @sort_column_names; - @sorted_list = sort { ( $a->{$column_name} || "" ) cmp ( $b->{$column_name} || "" ) } - @sorted_list; + my $reverse = substr($column_name, -1) eq '-' ? 1 : 0; # use reverse order if last character is minus + $column_name = substr($column_name, 0, -1) if $reverse; # chop of the minus + if ( $reverse ) { + @sorted_list = sort { ( $b->{$column_name} || "" ) cmp ( $a->{$column_name} || "" ) } + @sorted_list; + } + else { + @sorted_list = sort { ( $a->{$column_name} || "" ) cmp ( $b->{$column_name} || "" ) } + @sorted_list; + } } return \@sorted_list; } @@ -359,7 +367,7 @@ sub parse_columns_param { sub is_valid_column { my ($name) = @_; my $valid_column_map = { - map { $_ => 1 } + map { $_ => 1, $_ . "-" => 1 } # also supports keyword with a - suffix qw( bus file legacy_api ipxe_driver ipxe_name ipxe_description diff --git a/src/util/parserom.pl b/src/util/parserom.pl index 28df60652..5a849a540 100755 --- a/src/util/parserom.pl +++ b/src/util/parserom.pl @@ -157,7 +157,7 @@ sub process_isa_rom { # Output Makefile rules for the specified ROM declarations sub print_make_rules { - my ( $state, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; + my ( $state, $image, $desc, $vendor, $device, $dup ) = @_; unless ( $state->{'is_header_printed'} ) { print "# NIC\t\n"; print "# NIC\tfamily\t$state->{family}\n"; diff --git a/src/util/zbin.c b/src/util/zbin.c index 1862a3827..75fba583f 100644 --- a/src/util/zbin.c +++ b/src/util/zbin.c @@ -144,6 +144,7 @@ static int read_zinfo_file ( const char *filename, static int alloc_output_file ( size_t max_len, struct output_file *output ) { output->len = 0; + output->hdr_len = 0; output->max_len = ( max_len ); output->buf = malloc ( max_len ); if ( ! output->buf ) { @@ -241,19 +242,41 @@ static void bcj_filter ( void *data, size_t len ) { }; } +#define CRCPOLY 0xedb88320 +#define CRCSEED 0xffffffff + +static uint32_t crc32_le ( uint32_t crc, const void *data, size_t len ) { + const uint8_t *src = data; + uint32_t mult; + unsigned int i; + + while ( len-- ) { + crc ^= *(src++); + for ( i = 0 ; i < 8 ; i++ ) { + mult = ( ( crc & 1 ) ? CRCPOLY : 0 ); + crc = ( ( crc >> 1 ) ^ mult ); + } + } + return crc; +} + static int process_zinfo_pack ( struct input_file *input, struct output_file *output, union zinfo_record *zinfo ) { struct zinfo_pack *pack = &zinfo->pack; size_t offset = pack->offset; size_t len = pack->len; + size_t start_len; size_t packed_len = 0; - size_t remaining = ( output->max_len - output->len ); + size_t remaining; lzma_options_lzma options; const lzma_filter filters[] = { { .id = LZMA_FILTER_LZMA1, .options = &options }, { .id = LZMA_VLI_UNKNOWN } }; + void *packed; + uint32_t *len32; + uint32_t *crc32; if ( ( offset + len ) > input->len ) { fprintf ( stderr, "Input buffer overrun on pack\n" ); @@ -261,6 +284,9 @@ static int process_zinfo_pack ( struct input_file *input, } output->len = align ( output->len, pack->align ); + start_len = output->len; + len32 = ( output->buf + output->len ); + output->len += sizeof ( *len32 ); if ( output->len > output->max_len ) { fprintf ( stderr, "Output buffer overrun on pack\n" ); return -1; @@ -268,28 +294,34 @@ static int process_zinfo_pack ( struct input_file *input, bcj_filter ( ( input->buf + offset ), len ); + packed = ( output->buf + output->len ); + remaining = ( output->max_len - output->len ); lzma_lzma_preset ( &options, LZMA_PRESET ); options.lc = LZMA_LC; options.lp = LZMA_LP; options.pb = LZMA_PB; if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ), - len, ( output->buf + output->len ), - &packed_len, remaining ) != LZMA_OK ) { + len, packed, &packed_len, + remaining ) != LZMA_OK ) { fprintf ( stderr, "Compression failure\n" ); return -1; } - - if ( DEBUG ) { - fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", - offset, ( offset + len ), output->len, - ( output->len + packed_len ) ); - } - output->len += packed_len; + + crc32 = ( output->buf + output->len ); + output->len += sizeof ( *crc32 ); if ( output->len > output->max_len ) { fprintf ( stderr, "Output buffer overrun on pack\n" ); return -1; } + *len32 = ( packed_len + sizeof ( *crc32 ) ); + *crc32 = crc32_le ( CRCSEED, packed, packed_len ); + + if ( DEBUG ) { + fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx) crc %#08x\n", + offset, ( offset + len ), start_len, output->len, + *crc32 ); + } return 0; }