diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a09a30d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +cscope.* diff --git a/Android.mk b/Android.mk new file mode 100755 index 0000000..69e4ee9 --- /dev/null +++ b/Android.mk @@ -0,0 +1,94 @@ +EFIWRAPPER_LOCAL_PATH := $(call my-dir) +EFIWRAPPER_CFLAGS := -Wall -Wextra -Werror + +ifeq ($(TARGET_UEFI_ARCH),x86_64) +EFIWRAPPER_CFLAGS += -DBUILD_X64 +EFIWRAPPER_CFLAGS += -D__STDC_VERSION__=199901L +endif + +ifeq ($(TARGET_BUILD_VARIANT),user) + EFIWRAPPER_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BOARD_PLATFORM),icelakeu) + EFIWRAPPER_CFLAGS += -DPLATFORM_ICELAKE +endif + +ifeq ($(TARGET_BOARD_PLATFORM),tigerlake) + EFIWRAPPER_CFLAGS += -DPLATFORM_TIGERLAKE +endif + +ifeq ($(TARGET_BOARD_PLATFORM),kabylake) + EFIWRAPPER_CFLAGS += -DPLATFORM_KABYLAKE +endif + +ifeq ($(TARGET_BOARD_PLATFORM),broxton) + EFIWRAPPER_CFLAGS += -DPLATFORM_BROXTON +endif + +ifeq ($(EFIWRAPPER_USE_EC_UART),true) + EFIWRAPPER_CFLAGS += -DEFIWRAPPER_USE_EC_UART +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EFIWRAPPER_CFLAGS += -DUSERDEBUG +endif + +ifeq ($(CAPSULE_SOURCE),abl) + EFIWRAPPER_CFLAGS += -DCAPSULE4ABL +endif + +ifeq ($(CAPSULE_SOURCE),sbl) + EFIWRAPPER_CFLAGS += -DCAPSULE4SBL +endif + +EFIWRAPPER_CFLAGS += -DPRODUCT_NAME=\"$(TARGET_PRODUCT)\" + +ifeq ($(PRODUCT_MANUFACTURER),) + EFIWRAPPER_CFLAGS += -DPRODUCT_MANUFACTURER=\"unknown\" +else + EFIWRAPPER_CFLAGS += -DPRODUCT_MANUFACTURER=\"$(PRODUCT_MANUFACTURER)\" +endif + +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + EFIWRAPPER_CFLAGS += -DDISABLE_DEBUG_PRINT +endif + +ifeq ($(IOC_USE_SLCAN),true) + EFIWRAPPER_CFLAGS += -DIOC_USE_SLCAN +else ifeq ($(IOC_USE_CBC),true) + EFIWRAPPER_CFLAGS += -DIOC_USE_CBC +endif + +EFIWRAPPER_HOST_CFLAGS := \ + $(EFIWRAPPER_CFLAGS) \ + -fshort-wchar \ + -DEFI_FUNCTION_WRAPPER \ + -DGNU_EFI_USE_MS_ABI \ + -DHOST + +ifeq ($(TARGET_IAFW_ARCH),x86_64) + EFIWRAPPER_HOST_ARCH += x86_64 +else + EFIWRAPPER_HOST_ARCH += x86 +endif + +EFIWRAPPER_HOST_C_INCLUDES := \ + external/gnu-efi/gnu-efi-3.0/inc \ + external/gnu-efi/gnu-efi-3.0/inc/$(TARGET_EFI_ARCH_NAME) \ + external/gnu-efi/gnu-efi-3.0/inc/protocol + +include $(call all-subdir-makefiles) + +include $(CLEAR_VARS) +LOCAL_PATH := $(EFIWRAPPER_LOCAL_PATH) +LOCAL_MODULE := efiwrapper-$(TARGET_BUILD_VARIANT) +LOCAL_STATIC_LIBRARIES := \ + libefiwrapper-$(TARGET_BUILD_VARIANT) \ + libgnuefi \ + libefi \ + libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(EFIWRAPPER_CFLAGS) +LOCAL_SRC_FILES := \ + main.c +include $(BUILD_IAFW_STATIC_LIBRARY) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..517d5b1 --- /dev/null +++ b/COPYING @@ -0,0 +1,25 @@ +Copyright (c) 2016, Intel Corportaion +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 +HOLDER 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. diff --git a/Make.defaults b/Make.defaults new file mode 100644 index 0000000..fdd54ae --- /dev/null +++ b/Make.defaults @@ -0,0 +1,17 @@ +ARCH := $(shell uname -m | sed s/i[3-9]86/ia32/) +GNU_EFI_PATH ?= /usr/local/include/efi +GNU_EFI_INCS := -I$(GNU_EFI_PATH) \ + -I$(GNU_EFI_PATH)/$(ARCH) \ + -I$(GNU_EFI_PATH)/protocol + +EW_INCS = -I$(SRC_DIR)/include/libefiwrapper +EW_LIB = $(SRC_DIR)/libefiwrapper/libefiwrapper_host-$(TARGET_BUILD_VARIANT).a + +PRODUCT_MANUFACTURER ?= unknown +PRODUCT_NAME ?= default_name + +CFLAGS = -Wall -Werror -fshort-wchar -DGNU_EFI_USE_MS_ABI $(EXTRA_CFLAGS) + +#default rule +%.o: %.c + $(CC) $(CFLAGS) $(GNU_EFI_INCS) $(EW_INCS) -c $< -o $@ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..247c7b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +SRC_DIR := . +include $(SRC_DIR)/Make.defaults + +SUB_DIRS := libefiwrapper host +define submake + $(foreach d,$(SUB_DIRS),$(MAKE) -C $(d) $(1);) +endef + +efiwrapper_host-eng: export EXTRA_CFLAGS := -DHOST +efiwrapper_host-userdebug: export EXTRA_CFLAGS := -DHOST -DUSERDEBUG +efiwrapper_host-user: export EXTRA_CFLAGS := -DHOST -DUSERDEBUG -DUSER + +efiwrapper_host-eng \ +efiwrapper_host-userdebug \ +efiwrapper_host-user: + $(eval export TARGET_BUILD_VARIANT := $(subst efiwrapper_host-,,$@)) + @$(MAKE) efiwrapper_host + +efiwrapper_host: $(EW_LIB) + @$(MAKE) -C host + +$(EW_LIB): + @$(MAKE) -C libefiwrapper + +.PHONY: clean +clean: + @$(call submake,clean) + +mrproper: clean + @$(call submake,mrproper) diff --git a/README.md b/README.md new file mode 100644 index 0000000..dbcfebb --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +EfiWrapper +========== + +Overview +-------- + +EfiWrapper is a library which simulate a UEFI firmware +implementation. Its first purpose is to run a subset of the +[Kernelflinger](https://github.com/01org/kernelflinger/) OS loader to +run in a non-UEFI environment. + +Basic architecture +------------------ +* `libefiwrapper`: library that provides a basic implementation of the + Boot services and Runtime services. It includes basic EFI variable + management (no storage) and serial IO protocol support. It also + provides an abstraction for storage class implementation. This + library is system independent and MUST be kept that way. + +* `libefiwrapper_drivers`: library including all the protocols + specified by the `LIBEFIWRAPPER_DRIVERS` Makefile variable. Drivers + are in the drivers/DRIVER\_NAME directories and might rely on + external libraries like the `libpayload` from the + [Coreboot](https://www.coreboot.org/) project. + +* `host`: produce an `efiwrapper_host` host executable that can run an + EFI binary. See `Run an EFI binary on host` section. + +* efiwrapper: library that provides the `main()` entry point which + initialize the libefiwrapper library and all the drivers before + calling the `efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)` + function. + +Run an EFI binary on host +------------------------- + +To build `efiwrapper_host`, run the following command in your Android +build environment: + +``` bash +$ make efiwrapper_host- +``` +Where `` is either `user`, `userdebug` or `eng`. + +``` bash +$ efiwrapper_host --help +Usage: efiwrapper_host [OPTIONS] [ARGS] + OPTIONS: + -h,--help Print this help + --list-drivers List available drivers + --disable-drivers=DRV1,DRV2 Disable drivers DRV1 and DRV2 +``` + +The `efiwrapper_host` has built-in drivers: +``` bash +$ efiwrapper_host --list-drivers +Drivers list: +- disk: Emulate eMMC storage +- event: Event management for host +- tcp4: TCP/IP protocol +- fileio: File System Protocol support +- gop: Graphics Output Protocol support based on Xlib +- image: PE/COFF image +``` + +Drivers can be independently deactivated. For instance, if you want to +run Kernelflinger EFI binary witout the Graphic Output Protocol support: + +``` bash +$ efiwrapper_host --disable-drivers=gop kernelflinger.efi -f +``` + +Dependencies +------------ +* gnu-efi: libefiwrapper and efiwrapper libraries depends on the + gnu-efi library for EFI types definitions. + +* drivers/*: most of these drivers depends on the `libpayload` library + from the [Coreboot](https://www.coreboot.org/) project. + +* host/gop.c: libx11 devel library + +Copyright and Licence +--------------------- +EfiWrapper is licensed under the terms of the BSD 2-Clause. diff --git a/drivers/Android.mk b/drivers/Android.mk new file mode 100644 index 0000000..a65bcfb --- /dev/null +++ b/drivers/Android.mk @@ -0,0 +1,41 @@ +LOCAL_PATH := $(call my-dir) + +# LIBEFIWRAPPER_DRIVERS must be defined for the current product with +# the list of drivers (see directories of this very directory). + +include $(CLEAR_VARS) +LOCAL_MODULE := libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +LOCAL_STATIC_LIBRARIES := \ + libpayload \ + libefiwrapper-$(TARGET_BUILD_VARIANT) \ + libgnuefi \ + libefi +LOCAL_SRC_FILES := \ + $(foreach drv, $(LIBEFIWRAPPER_DRIVERS), \ + $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/$(drv)/*.c))) +GEN := $(local-generated-sources-dir)/drivers.c +ifeq ($(LIBEFIWRAPPER_DRIVERS),) +$(GEN): + $(hide) mkdir -p $(dir $@) + $(hide) echo "/* Do not modify this auto-generated file. */" > $@ + $(hide) echo "#error \"no driver selected. Cf. LIBEFIWRAPPER_DRIVERS Makefile variable.\"" >> $@ +else +$(GEN): + $(hide) mkdir -p $(dir $@) + $(hide) echo "/* Do not modify this auto-generated file. */" > $@ + $(hide) echo "#include \"ewdrv.h\"" >> $@ + $(hide) $(foreach drv, $(LIBEFIWRAPPER_DRIVERS), echo "#include" \"$(drv)/$(drv).h\" >> $@;) + $(hide) echo "" >> $@ + $(hide) echo "static ewdrv_t *drivers[] = {" >> $@ + $(hide) $(foreach drv, $(LIBEFIWRAPPER_DRIVERS), echo "&"$(drv)_drv, >> $@;) + $(hide) echo "NULL" >> $@ + $(hide) echo "};" >> $@ + $(hide) echo "" >> $@ + $(hide) echo "ewdrv_t **ew_drivers = drivers;" >> $@ +endif +LOCAL_GENERATED_SOURCES := $(GEN) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_CFLAGS := $(EFIWRAPPER_CFLAGS) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include/hardware +include $(BUILD_IAFW_STATIC_LIBRARY) diff --git a/drivers/abl/abl.c b/drivers/abl/abl.c new file mode 100755 index 0000000..7854a6b --- /dev/null +++ b/drivers/abl/abl.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "abl/abl.h" + +/* ABL hwver structure is defined at follow: + * struct hwver { + * uint8_t cpu; // Cpu stepping + * uint8_t cpuNcores; + * uint16_t cpuFreq; + * uint8_t sku; + * uint8_t resetCause; + * uint16_t platformId; + * uint16_t moduleId; + * }; + * + * This structure is serialized as the "ABL.hwver" command line + * parameter built as follow: + * + * "$cpu,$cpuNcores,$cpuFreq,$platformId,$sku,$MRC_amap.TOM" + * + * where $MRC_amap.TOM is the total amount of memory present. + */ +static const char *get_hwver_token(const char *hwver, size_t index, + char *token, size_t token_size) +{ + char *data, *cur; + size_t i; + + data = strdup(hwver); + if (!data) + return SMBIOS_UNDEFINED; + + cur = strtok(data, ","); + if (!cur) + return SMBIOS_UNDEFINED; + + for (i = 0; i < index; i++) { + cur = strtok(NULL, ","); + if (!cur) + return SMBIOS_UNDEFINED; + } + + if (strlen(cur) > token_size) + return SMBIOS_UNDEFINED; + + strcpy(token, cur); + free(data); + return token; +} + +static const char *get_platform_id(const char *str) +{ + static char platform_id[5]; + return get_hwver_token(str, 3, platform_id, sizeof(platform_id)); +} + +static const char *get_cpu_stepping(const char *str) +{ + static char stepping[3]; + return get_hwver_token(str, 0, stepping, sizeof(stepping)); +} + +static const char *identity(const char *str) +{ + return str; +} + +static const struct { + const char *name; + UINT8 type; + UINT8 offset; + const char *(*convert)(const char *); +} ARG_TO_SMBIOS[] = { + { "androidboot.serialno", 1, offsetof(SMBIOS_TYPE1, SerialNumber), identity }, + { "ABL.version", 0, offsetof(SMBIOS_TYPE0, BiosVersion), identity }, + { "androidboot.name", 1, offsetof(SMBIOS_TYPE1, ProductName), identity }, + { "androidboot.brand", 2, offsetof(SMBIOS_TYPE2, Manufacturer), identity }, + { "androidboot.device", 2, offsetof(SMBIOS_TYPE2, ProductName), identity }, + { "ABL.hwver", 2, offsetof(SMBIOS_TYPE2, Version), get_platform_id }, + { "ABL.hwver", 1, offsetof(SMBIOS_TYPE1, Version), get_cpu_stepping } +}; + +static EFI_STATUS set_smbios_fields(void) +{ + EFI_STATUS ret; + size_t i; + const char *val; + + for (i = 0; i < ARRAY_SIZE(ARG_TO_SMBIOS); i++) { + val = ewarg_getval(ARG_TO_SMBIOS[i].name); + if (!val) + continue; + + ret = smbios_set(ARG_TO_SMBIOS[i].type, + ARG_TO_SMBIOS[i].offset, + ARG_TO_SMBIOS[i].convert(val)); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; +} + +extern ewvar_storage_t reboot_target_storage; + +static EFI_STATUS abl_init(__attribute__((__unused__)) EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + ewvar_register_storage(&reboot_target_storage); + + return set_smbios_fields(); +} + +static EFI_STATUS abl_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + ewvar_unregister_storage(); + + return EFI_SUCCESS; +} + +ewdrv_t abl_drv = { + .name = "abl", + .description = "Automotive BootLoader support", + .init = abl_init, + .exit = abl_exit +}; diff --git a/drivers/abl/abl.h b/drivers/abl/abl.h new file mode 100644 index 0000000..ea15d14 --- /dev/null +++ b/drivers/abl/abl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _ABL_H_ +#define _ABL_H_ + +#include + +extern ewdrv_t abl_drv; + +#endif /* _ABL_H_ */ diff --git a/drivers/abl/abl_reboot_target.c b/drivers/abl/abl_reboot_target.c new file mode 100755 index 0000000..6057fb5 --- /dev/null +++ b/drivers/abl/abl_reboot_target.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Guillaume Betous + * Ji Wu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "capsule_msg.h" + +#define RTC_PORT(x) (0x70 + (x)) +#define LOADER_ENTRY_ONESHOT L"LoaderEntryOneShot" +#define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __attribute__((__packed__)); + +struct name2id { + const CHAR16 *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + uint32_t cdata_payload_size; + uint32_t crc; +} __attribute__((__packed__)); + +static const struct name2id NAME2ID[] = { + { L"", 0x00 }, + { L"boot", 0x00 }, + { L"bootloader", 0x01 }, + { L"fastboot", 0x01 }, + { L"recovery", 0x03 }, + { L"dnx", 0x05 }, +}; + +static size_t offset; /* memorize offset between each call */ + +extern size_t str16len(const CHAR16 *str); + +static size_t write_data_to_nvram(char *data, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + offset += size; + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram((char*)nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram(nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + write_data_to_nvram((char*)(&(nvram_msg->crc)), sizeof(nvram_msg->crc)); +} + +static EFI_STATUS reboot_target_name2id(const CHAR16 *name, int *id) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (str16len(NAME2ID[i].name) == str16len(name) && + !memcmp(NAME2ID[i].name, name, str16len(name) * sizeof(*name))) { + if (id) { + *id = NAME2ID[i].id; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS set_reboot_target(const CHAR16 *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + EFI_STATUS ret; + + if (!name) + return EFI_INVALID_PARAMETER; + + ret = reboot_target_name2id(name, &id); + if (EFI_ERROR(ret)) { + ewerr("Error in %s: '%s' is not a valid target", + __func__, (char*)name); + return EFI_INVALID_PARAMETER; + } + if (id == 0) { + ewdbg("Target 'boot' no need write to nvram."); + return EFI_SUCCESS; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = (char*)&reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg((char *)&msg, offsetof(struct nvram_msg, cdata_payload), msg.cdata_payload, (size_t)msg.cdata_payload_size); + + write_msg_to_nvram(&msg); + + return EFI_SUCCESS; +} + +static EFI_STATUS reboot_target_save(ewvar_t *var) +{ + const CHAR16* name; + + if (!var) + return EFI_INVALID_PARAMETER; + + name = LOADER_ENTRY_ONESHOT; + if (str16len(var->name) == str16len(name) && + !memcmp(var->name, name, str16len(name) * sizeof(*name))) { + return set_reboot_target(var->data); + } + + name = IFWI_CAPSULE_UPDATE; + if (str16len(var->name) == str16len(name) && + !memcmp(var->name, name, str16len(name) * sizeof(*name))) { + return capsule_store(var->data); + } + + return EFI_SUCCESS; +} + +ewvar_storage_t reboot_target_storage = { + .load = NULL, + .save = reboot_target_save, + .delete = NULL +}; diff --git a/drivers/abl/capsule_msg.c b/drivers/abl/capsule_msg.c new file mode 100644 index 0000000..f8c4c5b --- /dev/null +++ b/drivers/abl/capsule_msg.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "capsule_msg.h" +#include "heci/heci_protocol.h" +#include + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +uint32_t crc32c_msg(const char *msg, UINTN offset, const void *addr, size_t len) +{ + uint32_t crc; + + crc = crc32c_buf(~0, msg, offset); + crc = crc32c_buf(crc, addr, len); + return crc; +} + +typedef union _MKHI_MESSAGE_HEADER { + uint32_t Data; + struct { + uint32_t GroupId : 8; + uint32_t Command : 7; + uint32_t IsResponse : 1; + uint32_t Reserved : 8; + uint32_t Result : 8; + } Fields; +} MKHI_MESSAGE_HEADER; + +/* + * User command message + */ +#define CSE_USRCMD_SIZE 128 // <64 or ==64 will send fail +typedef struct __attribute__( (packed) ) _HECI_USER_CMD_REQUEST +{ + MKHI_MESSAGE_HEADER MKHIHeader; + uint8_t sub_command; + uint8_t data[CSE_USRCMD_SIZE]; +} HECI_USER_CMD_REQUEST; + +typedef struct _HECI_USER_CMD_RESPONSE { + MKHI_MESSAGE_HEADER Header; +} HECI_USER_CMD_RESPONSE; + +#define MBP_APP_ABL_SIG 0x20 +#define MBP_ITEM_ID_IAFW_IBB_SIG 0x7 +#define BIOS_FIXED_HOST_ADDR 0 +static unsigned heci_send_user_command(uint8_t *data, uint8_t length) +{ + EFI_STATUS status; + uint32_t HeciSendLength; + uint32_t HeciRecvLength; + HECI_USER_CMD_REQUEST *SendCommand; + HECI_USER_CMD_RESPONSE *CommandResp; + EFI_GUID guid = HECI_PROTOCOL_GUID; + EFI_HECI_PROTOCOL *protocol = NULL; + uint32_t SeCMode; + uint8_t DataBuffer[sizeof(HECI_USER_CMD_REQUEST)]; + + if (length == 0) { + ewerr("No need Sending HeciSendUserCommandClear."); + return 1; + } + if (length > CSE_USRCMD_SIZE) + length = CSE_USRCMD_SIZE; + + status = LibLocateProtocol(&guid, (void **)&protocol); + if (EFI_ERROR(status)) { + ewerr("Failed to get heciprotocol"); + return 1; + } + + status = uefi_call_wrapper(protocol->GetSeCMode, 1, &SeCMode); + if (EFI_ERROR(status) || (SeCMode != SEC_MODE_NORMAL)) { + ewerr("Failed to get hecisecmode"); + return 1; + } + ewdbg("HECI sec_mode %X", SeCMode); + + memset (DataBuffer, 0, sizeof(DataBuffer)); + SendCommand= (HECI_USER_CMD_REQUEST*)DataBuffer; + SendCommand->MKHIHeader.Fields.GroupId = MBP_APP_ABL_SIG; + SendCommand->MKHIHeader.Fields.Command = MBP_ITEM_ID_IAFW_IBB_SIG; + SendCommand->sub_command = 1; + memcpy(SendCommand->data, data, length); + + HeciSendLength = sizeof(HECI_USER_CMD_REQUEST); + HeciRecvLength = sizeof(HECI_USER_CMD_RESPONSE); + status = uefi_call_wrapper(protocol->SendwACK, 5, (UINT32 *)DataBuffer, + HeciSendLength, &HeciRecvLength, + BIOS_FIXED_HOST_ADDR, 0x7); + if (status != 0) { + ewerr("Heci send fail: %x", (UINT32)status); + return status; + } + ewdbg("uefi_call_wrapper(SendwACK) = %d", (UINT32)status); + + CommandResp = (HECI_USER_CMD_RESPONSE*)DataBuffer; + ewdbg( "Group =%08x\n", CommandResp->Header.Fields.GroupId); + ewdbg( "Command =%08x\n", CommandResp->Header.Fields.Command); + ewdbg( "IsRespone=%08x\n", CommandResp->Header.Fields.IsResponse); + ewdbg( "Result =%08x\n", CommandResp->Header.Fields.Result); + if (CommandResp->Header.Fields.Result != 0) { + status = CommandResp->Header.Fields.Result; + ewerr("Send cmd fail: %x", (UINT32)status); + } + + return status; +} + +#ifdef CAPSULE4SBL +typedef enum { + EnumFileSystemTypeFat, + EnumFileSystemTypeExt2, + EnumFileSystemTypeAuto, + EnumFileSystemMax +} SBL_OS_FILE_SYSTEM_TYPE; + +static EFI_STATUS cse4sbl_capsule_msg_write(CSE_MSG *msg) +{ + uint8_t *msg_buf; + unsigned status; + + msg_buf = malloc(CSE_USRCMD_SIZE); + if (!msg_buf) + return EFI_OUT_OF_RESOURCES; + + memset(msg_buf, 0, CSE_USRCMD_SIZE); + memcpy(msg_buf, (uint8_t *)msg, sizeof(CSE_MSG)); + + status = heci_send_user_command(msg_buf, CSE_USRCMD_SIZE); + + free(msg_buf); + return status; +} + +static EFI_STATUS cse4sbl_capsule_cmd_create(CSE_CMD **cmd, size_t *cmd_size, const char *buf) +{ + char name[32]; /* Length of capsule file name can't exceed 30. */ + int partition; + + /* storage_type mapping */ + SBL_OS_BOOT_MEDIUM_TYPE device_map[] = { + [STORAGE_EMMC] = OsBootDeviceEmmc, + [STORAGE_UFS] = OsBootDeviceUfs, + [STORAGE_SDCARD] = OsBootDeviceSd, + [STORAGE_SATA] = OsBootDeviceSata, + [STORAGE_NVME] = OsBootDeviceNvme, + [STORAGE_VIRTUAL] = OsBootDeviceVirtual, + [STORAGE_ALL] = OsBootDeviceEmmc + }; + + boot_dev_t *boot_dev; + + ewdbg("capsule buffer: %s", buf); /* Buffer format example: "m1:@0" */ + + partition = buf[1] - '0'; + memset(name, 0, sizeof(name)); + strncpy(name, buf + 3, sizeof(name) - 1); /* Number 3 is start index of name in buffer. */ + + boot_dev = get_boot_media(); + + ewdbg("capsule parameters: DEVICE=%d PARTITION=%d NAME=%s", + device_map[boot_dev->type], partition, name); + if (name[0] != '@') + return EFI_INVALID_PARAMETER; + + *cmd_size = sizeof(CSE_CMD); + + *cmd = malloc(*cmd_size); + if (!(*cmd)) + return EFI_OUT_OF_RESOURCES; + + memset(*cmd, 0, sizeof(CSE_CMD)); + (*cmd)->dev_addr = boot_dev->diskbus; + (*cmd)->dev_type = device_map[boot_dev->type]; + if (buf[0] == 'm') + (*cmd)->fs_type = EnumFileSystemMax; + else + (*cmd)->fs_type = EnumFileSystemTypeFat; + + if (name[0] == '@') + (*cmd)->LbaAddr = strtoull(name+1, NULL, 0); + else { + memset((*cmd)->FileName, 0, MAX_FILE_LEN); + strncpy((char *)(*cmd)->FileName, name, MAX_FILE_LEN - 1); + } + + (*cmd)->hw_part = 0; + (*cmd)->sw_part = partition; + return EFI_SUCCESS; +} + +/* If multiple payload is needed, the cmd and cmd_size could be changed to array */ +static EFI_STATUS cse4sbl_capsule_msg_create(CSE_MSG **msg, CSE_CMD *cmd, __attribute__((__unused__)) size_t cmd_size) +{ + *msg = malloc(sizeof(CSE_MSG)); + if (!(*msg)) + return EFI_OUT_OF_RESOURCES; + + memset(*msg, 0, sizeof(CSE_MSG)); + cdata_blob_t *cdb = &(*msg)->cdb; + cdb->Signature = CFG_DATA_SIGNATURE; + cdb->HeaderLength = sizeof(cdata_blob_t); + cdb->UsedLength = sizeof(CSE_MSG); + cdb->TotalLength = CSE_USRCMD_SIZE; + + cdata_header_t *cdh = &(*msg)->cdh; + cdh->ncond = 1; + cdh->length = sizeof(cdata_header_t) + sizeof(CSE_CMD); + cdh->version = 1; + cdh->tag = CDATA_CAPSULE_TAG; + cdh->Condition.Value = 0xFFFFFFFF; + + memcpy(&((*msg)->cmd), cmd, sizeof(CSE_CMD)); + + return EFI_SUCCESS; +} +#else +static EFI_STATUS cse4abl_capsule_msg_write(CSE_MSG *msg) +{ + uint8_t *msg_buf, *msg_buf_p, msg_slen; + unsigned status; + + msg_buf = malloc(CSE_USRCMD_SIZE); + if (!msg_buf) + return EFI_OUT_OF_RESOURCES; + memset(msg_buf, 0, CSE_USRCMD_SIZE); + msg_buf_p = msg_buf; + + msg_slen = offsetof(CSE_MSG, cdata_payload); + msg_buf_p = (uint8_t *)memcpy(msg_buf_p, (uint8_t *)msg, msg_slen) + msg_slen; + msg_slen = msg->cdata_payload_size; + msg_buf_p = (uint8_t *)memcpy(msg_buf_p, msg->cdata_payload, msg_slen) + msg_slen; + msg_slen = sizeof(msg->crc); + memcpy(msg_buf_p, (uint8_t *)(&(msg->crc)), msg_slen); + + status = heci_send_user_command(msg_buf, CSE_USRCMD_SIZE); + + free(msg_buf); + return status; +} + +enum abl_capsule_device_type { + EMMC = 2, + SDCARD = 4 +}; + +static EFI_STATUS cse4abl_capsule_cmd_create(CSE_CMD **cmd, size_t *cmd_size, const char *buf) +{ + char name[32]; /* Length of capsule file name can't exceed 30. */ + int name_len, partition; + enum abl_capsule_device_type device; + + ewdbg("capsule buffer: %s", buf); /* Buffer format example: "m1:@0" */ + + device = (buf[0] == 'm' ? EMMC : SDCARD); + partition = buf[1] - '0'; + memset(name, 0, sizeof(name)); + strncpy(name, buf + 3, sizeof(name) - 1); /* Number 3 is start index of name in buffer. */ + name_len = strlen(name) + 1; + ewdbg("capsule parameters: DEVICE=%d PARTITION=%d NAME=%s", + device, partition, name); + + *cmd_size = (offsetof(CSE_CMD, file_name) + name_len + 3) & ~3; + + *cmd = malloc(*cmd_size); + if (!(*cmd)) + return EFI_OUT_OF_RESOURCES; + + memset(*cmd, 0, *cmd_size); + (*cmd)->action = USERCMD_UPDATE_IFWI(name_len + 2); + (*cmd)->device = device; + (*cmd)->partition = partition; + strlcpy((*cmd)->file_name, name, name_len); + return EFI_SUCCESS; +} + +static EFI_STATUS cse4abl_capsule_msg_create(CSE_MSG **msg, CSE_CMD *cmd, size_t cmd_size) +{ + union _cdata_header cdh; + + cdh.data = 0; + cdh.tag = CDATA_TAG_USER_CMD; + cdh.length = (sizeof(cdh) + cmd_size) / 4; + + *msg = malloc(sizeof(CSE_MSG)); + if (!(*msg)) + return EFI_OUT_OF_RESOURCES; + + memset(*msg, 0, sizeof(CSE_MSG)); + (*msg)->magic = NVRAM_VALID_FLAG; + (*msg)->size = offsetof(CSE_MSG, cdata_payload) + cmd_size + sizeof((*msg)->crc); + (*msg)->cdata_header.data = cdh.data; + + (*msg)->cdata_payload = (char *)cmd; + (*msg)->cdata_payload_size = cmd_size; + (*msg)->crc = crc32c_msg((char *)(*msg), offsetof(CSE_MSG, cdata_payload), (*msg)->cdata_payload, (size_t)(*msg)->cdata_payload_size); + return EFI_SUCCESS; +} +#endif +static void capsule_free_all(CSE_CMD **cmd, CSE_MSG **msg) +{ + if (msg != NULL && *msg != NULL) { + free(*msg); + *msg = NULL; + } + if (cmd != NULL && *cmd != NULL) { + free(*cmd); + *cmd = NULL; + } +} + +static struct capsule_opt { + EFI_STATUS (*cmd_create)(CSE_CMD **cmd, size_t *cmd_size, const char *buf); + EFI_STATUS (*msg_create)(CSE_MSG **msg, CSE_CMD *cmd, size_t cmd_size); + EFI_STATUS (*msg_write)(CSE_MSG *msg); + void (*free_all)(CSE_CMD **cmd, CSE_MSG **msg); +} CAPSULE_OPT = { +#ifdef CAPSULE4SBL + cse4sbl_capsule_cmd_create, + cse4sbl_capsule_msg_create, + cse4sbl_capsule_msg_write, +#else + cse4abl_capsule_cmd_create, + cse4abl_capsule_msg_create, + cse4abl_capsule_msg_write, +#endif + capsule_free_all, +}; + +EFI_STATUS capsule_store(const char *buf) +{ + CSE_MSG *capsule_msg = NULL; + CSE_CMD *capsule_cmd = NULL; + size_t capsule_cmd_size; + EFI_STATUS ret = EFI_SUCCESS; + + ret = CAPSULE_OPT.cmd_create((CSE_CMD **)&capsule_cmd, &capsule_cmd_size, buf); + if (EFI_ERROR(ret)) { + ewerr("Error in %s: create capsule command failed!", __func__); + goto error; + } + + ret = CAPSULE_OPT.msg_create((CSE_MSG **)&capsule_msg, (CSE_CMD *)capsule_cmd, capsule_cmd_size); + if (EFI_ERROR(ret)) { + ewerr("Error in %s: create capsule message failed!", __func__); + goto error; + } + + ret = CAPSULE_OPT.msg_write((CSE_MSG *)capsule_msg); + if (EFI_ERROR(ret)) { + ewerr("Error in %s: write capsule message failed!", __func__); + goto error; + } + + CAPSULE_OPT.free_all((CSE_CMD **)&capsule_cmd, (CSE_MSG **)&capsule_msg); + return EFI_SUCCESS; + +error: + CAPSULE_OPT.free_all((CSE_CMD **)&capsule_cmd, (CSE_MSG **)&capsule_msg); + return ret; +} diff --git a/drivers/abl/capsule_msg.h b/drivers/abl/capsule_msg.h new file mode 100644 index 0000000..5cdf563 --- /dev/null +++ b/drivers/abl/capsule_msg.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _CAPSULE_MSG_H_ +#define _CAPSULE_MSG_H_ + +#include +#include +#include +#include +#include +#include + +/* ABL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) +#define USERCMD_UPDATE_IFWI(len) _USERCMD_(2, len) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +#ifdef CAPSULE4SBL +/* SBL Conventions */ +#define SIGNATURE_16(A, B) ((A) | (B << 8)) +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) +#define CFG_DATA_SIGNATURE SIGNATURE_32('C', 'F', 'G', 'D') +#define MAX_FILE_LEN 16 +#define CDATA_CAPSULE_TAG 0x80 + +typedef struct cdata_cond { + uint32_t Value; // Bit masks on supported platforms +} __attribute__((__packed__)) cdata_cond_t; + +typedef struct cdata_header { + uint32_t ncond : 2; // [1:0] #of condition words present + uint32_t length : 10; // [11:2] total size of item (in dwords) + uint32_t flags : 4; // [15:12] unused/reserved so far + uint32_t version : 4; // [19:16] item (payload) format version + uint32_t tag : 12; // [31:20] identifies item (in payload) + struct cdata_cond Condition; +} __attribute__((__packed__)) cdata_header_t ; + +typedef struct cdata_blob { + uint32_t Signature; + uint8_t HeaderLength; + uint8_t Attribute; + uint8_t Reserved[2]; + uint32_t UsedLength; + uint32_t TotalLength; +} __attribute__((__packed__)) cdata_blob_t; + +typedef struct cse4sbl_capsule_cmd { + uint32_t dev_addr; + uint8_t dev_type; + uint8_t hw_part; + uint8_t sw_part; + uint8_t fs_type; + uint8_t FileName[MAX_FILE_LEN]; + uint32_t LbaAddr; +} __attribute__((__packed__)) CSE_CMD; + +typedef struct cse4sbl_capsule_msg { + struct cdata_blob cdb; + struct cdata_header cdh; + struct cse4sbl_capsule_cmd cmd; +} __attribute__((__packed__)) CSE_MSG; +#else +typedef struct cse4abl_capsule_cmd { + char action; + char device; + char partition; + char file_name[1]; +} __attribute__((__packed__)) CSE_CMD; + +typedef struct cse4abl_capsule_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + uint32_t cdata_payload_size; + uint32_t crc; +} __attribute__((__packed__)) CSE_MSG; +#endif + +uint32_t crc32c_msg(const char *msg, UINTN offset, const void *addr, size_t len); +EFI_STATUS capsule_store(const char *buf); + +#endif /* ifndef _CAPSULE_MSG_H_ */ diff --git a/drivers/acpi/AcpiTableProtocol.h b/drivers/acpi/AcpiTableProtocol.h new file mode 100644 index 0000000..5924cae --- /dev/null +++ b/drivers/acpi/AcpiTableProtocol.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + */ + +#ifndef _ACPI_TABLE_PROTOCOL_H_ +#define _ACPI_TABLE_PROTOCOL_H_ + +#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; + +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); + +typedef EFI_STATUS(EFIAPI *EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE)( + IN EFI_ACPI_TABLE_PROTOCOL *This, IN UINTN TableKey); + +struct _EFI_ACPI_TABLE_PROTOCOL { + EFI_ACPI_TABLE_INSTALL_ACPI_TABLE InstallAcpiTable; + EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE UninstallAcpiTable; +}; + +#endif diff --git a/drivers/acpi/acpi.c b/drivers/acpi/acpi.c new file mode 100755 index 0000000..f01003e --- /dev/null +++ b/drivers/acpi/acpi.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ +#include +#include +#include +#include "acpi.h" +#include "AcpiTableProtocol.h" +#include "acpi50.h" +#include "interface.h" +#include "ewlog.h" + +#define AML_EXT_REGION_OP 0x80 + +#define MAX_XSDT_HEADER_ENTRIES 20 // MUST <= the value in ABL/SBL + +static const char RSDP_MAGIC[8] = "RSD PTR "; + +static struct RSDP_TABLE *Rsdp; +static EFI_GUID acpi_protocol_guid = EFI_ACPI_TABLE_PROTOCOL_GUID; +static EFI_HANDLE handle; // handle of acpi_protocol +static EFI_SYSTEM_TABLE *p_st; // pointer to system table + +static EFI_STATUS acpi_mem_alloc(UINTN Size, VOID **Buffer, UINTN *no_page) +{ + if (!p_st) + return EFI_NOT_READY; + + *no_page = EFI_SIZE_TO_PAGES(Size); + return p_st->BootServices->AllocatePages( + AllocateAnyPages, EfiACPIReclaimMemory, *no_page, + (EFI_PHYSICAL_ADDRESS *)Buffer); +} + +static EFI_STATUS acpi_mem_free(VOID **Buffer, UINTN no_page) +{ + EFI_STATUS ret = EFI_SUCCESS; + + if (!p_st) + return EFI_NOT_READY; + + ret = p_st->BootServices->FreePages(*(EFI_PHYSICAL_ADDRESS *)Buffer, + no_page); + *Buffer = NULL; + return ret; +} + +struct RSDP_TABLE { + char signature[8]; /* "RSD PTR " */ + uint8_t checksum; /* RSDP Checksum (bytes 0-19) */ + char oem_id[6]; /* OEM ID String */ + char revision; /* ACPI Revision (0=1.0,2=2.0) */ + uint32_t rsdt_address; /* 32-bit RSDT Pointer */ + uint32_t length; /* RSDP Length */ + uint64_t xsdt_address; /* 64-bit XSDT Pointer */ + uint8_t extended_checksum; /* rsdp Checksum (full) */ + char reserved[3]; /* Reserved */ +} __attribute__((packed)); + +static uint8_t checksum(uint8_t *buf, size_t size) +{ + uint8_t sum; + size_t i; + + for (sum = 0, i = 0; i < size; i++) + sum += buf[i]; + + return !sum ? 0 : 0x100 - sum; +} + +static struct RSDP_TABLE *lookup_for_rdsp(char *from) +{ + char *p; + + if (!from) + from = (char *)0xE0000; + + for (p = from; p < (char *)0x100000; p += 16) + if (!memcmp(p, RSDP_MAGIC, sizeof(RSDP_MAGIC))) + return (struct RSDP_TABLE *)p; + + ewerr("ACPI:can't find RSDP\n"); + + return NULL; +} + +static EFI_STATUS get_rsdp(struct RSDP_TABLE **rsdp, EFI_GUID *guid) +{ + EFI_GUID acpi_guid = ACPI_TABLE_GUID; + EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; + struct RSDP_TABLE *table; + + for (table = NULL;; table += 16) { + table = lookup_for_rdsp((char *)table); + if (!table) + return EFI_NOT_FOUND; + if (table->revision == 0) { + if (checksum((uint8_t *)table, + offsetof(struct RSDP_TABLE, length))) + continue; + memcpy(guid, &acpi_guid, sizeof(*guid)); + break; + } else if (table->revision == 2) { + if (checksum((uint8_t *)table, sizeof(*table))) + continue; + memcpy(guid, &acpi2_guid, sizeof(*guid)); + break; + } + } + + *rsdp = table; + return EFI_SUCCESS; +} + +#define SIGNATURE_16(A, B) ((A) | (B << 8)) +#define SIGNATURE_32(A, B, C, D) \ + (SIGNATURE_16(A, B) | (SIGNATURE_16(C, D) << 16)) + +static void UpdateAcpiGnvs(EFI_ACPI_DESCRIPTION_HEADER *newDsdt, + EFI_ACPI_DESCRIPTION_HEADER *oldDsdt) +{ + UINT8 *Ptr; + UINT8 *End; + UINT32 GnvsBase = 0; + UINT16 GnvsSize; + + Ptr = (UINT8 *)oldDsdt; + End = (UINT8 *)oldDsdt + oldDsdt->Length; + + /* + * Loop through the ASL looking for values that we must fix up. + */ + for (; Ptr < End; Ptr++) { + if (*(UINT32 *)Ptr != SIGNATURE_32('G', 'N', 'V', 'S')) + continue; + + if (*(Ptr - 1) != AML_EXT_REGION_OP) + continue; + + GnvsBase = *(UINT32 *)(Ptr + 6); + GnvsSize = *(UINT16 *)(Ptr + 11); + break; + } + + if (GnvsBase) { + Ptr = (UINT8 *)newDsdt; + End = (UINT8 *)newDsdt + newDsdt->Length; + for (; Ptr < End; Ptr++) { + if (*(UINT32 *)Ptr != + SIGNATURE_32('G', 'N', 'V', 'S')) { + continue; + } + if (*(Ptr - 1) != AML_EXT_REGION_OP) + continue; + + *(UINT32 *)(Ptr + 6) = GnvsBase; + *(UINT16 *)(Ptr + 11) = GnvsSize; + break; + } + } +} + +static EFI_ACPI_DESCRIPTION_HEADER * +FindAcpiTableBySignature(EFI_ACPI_DESCRIPTION_HEADER *Xsdt, UINT32 Signature, + UINT32 *EntryIndex, UINT64 OemTableId, UINT32 OemRevision) +{ + EFI_ACPI_DESCRIPTION_HEADER *CurrHdr; + UINT64 *XsdtEntry; + UINT32 EntryNum; + UINT32 Index; + + XsdtEntry = + (UINT64 *)((UINT8 *)Xsdt + sizeof(EFI_ACPI_DESCRIPTION_HEADER)); + EntryNum = (Xsdt->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) / + sizeof(UINT64); + + for (Index = 0; Index < EntryNum; Index++) { + CurrHdr = (EFI_ACPI_DESCRIPTION_HEADER *)XsdtEntry[Index]; + + if ((CurrHdr != NULL) && (CurrHdr->Signature == Signature) && + ((OemTableId == 0) || ((OemTableId == CurrHdr->OemTableId) && + (CurrHdr->OemRevision <= OemRevision)))) { + if (EntryIndex != NULL) + *EntryIndex = Index; + + return CurrHdr; + } + } + + return NULL; +} + +static EFIAPI EFI_STATUS InstallAcpiTable(__attribute__((__unused__)) + EFI_ACPI_TABLE_PROTOCOL * This, + VOID *AcpiTableBuffer, + UINTN AcpiTableBufferSize, + __attribute__((__unused__)) + UINTN *TableKey) +{ + + EFI_ACPI_DESCRIPTION_HEADER *Xsdt; + EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *Facp; + EFI_ACPI_DESCRIPTION_HEADER *AcpiHdr; + UINT8 *NewTable; + UINT64 *XsdtEntry; + UINT32 EntryIndex; + UINT32 Size; + UINT32 EntryNum; + EFI_STATUS Status; + UINT64 OemTableId; + UINT32 OemRevision; + + if (Rsdp == NULL) + return EFI_NOT_READY; + + if ((AcpiTableBuffer == NULL) || + (AcpiTableBufferSize < sizeof(EFI_ACPI_DESCRIPTION_HEADER))) { + return EFI_INVALID_PARAMETER; + } + + Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Rsdp->xsdt_address; + XsdtEntry = + (UINT64 *)((UINT8 *)Xsdt + sizeof(EFI_ACPI_DESCRIPTION_HEADER)); + EntryNum = (Xsdt->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) / + sizeof(UINT64); + + Status = EFI_SUCCESS; + Size = 0; + + while (Size < AcpiTableBufferSize) { + AcpiHdr = + (EFI_ACPI_DESCRIPTION_HEADER *)(AcpiTableBuffer + Size); + + if (checksum((UINT8 *)AcpiHdr, AcpiHdr->Length) != 0) { + Status = EFI_ABORTED; + break; + } + + if (Size + AcpiHdr->Length > Size) { + Size += AcpiHdr->Length; + } else { + Status = EFI_ABORTED; + break; + } + + Status = acpi_mem_alloc(AcpiHdr->Length, (VOID **)&NewTable, + TableKey); + + if (Status != EFI_SUCCESS) { + ewerr("ACPI: can't allocate memory\n"); + break; + } + + memcpy(NewTable, AcpiHdr, AcpiHdr->Length); + + // Update the ACPI header to pointer to the new copy + // And then update the table if required + AcpiHdr = (EFI_ACPI_DESCRIPTION_HEADER *)NewTable; + if (AcpiHdr->Signature == + EFI_ACPI_5_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { + //need to check OEM table ID and revision for SSDT + OemTableId = AcpiHdr->OemTableId; + OemRevision = AcpiHdr->OemRevision; + } else { + OemTableId = 0; + OemRevision = 0; + } + if (AcpiHdr->Signature == + EFI_ACPI_5_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { + Facp = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *) + FindAcpiTableBySignature( + Xsdt, + EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, + &EntryIndex, OemTableId, OemRevision); + + if (Facp != NULL) { // DSDT override + EFI_ACPI_DESCRIPTION_HEADER *oldDsdt; + + oldDsdt = + (EFI_ACPI_DESCRIPTION_HEADER *)Facp->XDsdt; + Facp->Dsdt = (UINT32)(UINTN)AcpiHdr; + Facp->XDsdt = (UINT64)(UINTN)AcpiHdr; + + /* update FADT checksum */ + Facp->Header.Checksum = 0; + Facp->Header.Checksum = + checksum( + (void *)Facp, + sizeof( + EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE)); + UpdateAcpiGnvs(AcpiHdr, oldDsdt); + ewdbg("DSDT override\n"); + } else { + Status = EFI_ABORTED; // can't find FACP + acpi_mem_free((VOID **)&NewTable, *TableKey); + break; + } + } else { + // Try to find the table to replace + if (FindAcpiTableBySignature(Xsdt, AcpiHdr->Signature, + &EntryIndex, OemTableId, OemRevision) != NULL) { + XsdtEntry[EntryIndex] = (UINT32)(UINTN)AcpiHdr; + } else { // new table, to add + if (EntryNum >= MAX_XSDT_HEADER_ENTRIES) { + Status = EFI_OUT_OF_RESOURCES; + acpi_mem_free((VOID **)&NewTable, *TableKey); + break; + } + XsdtEntry[EntryNum] = (UINT32)(UINTN)AcpiHdr; + EntryNum++; + } + } + + AcpiHdr->Checksum = 0; + AcpiHdr->Checksum = checksum((void *)AcpiHdr, AcpiHdr->Length); + } + + Xsdt->Length = + sizeof(EFI_ACPI_DESCRIPTION_HEADER) + EntryNum * sizeof(UINT64); + Xsdt->Checksum = 0; + Xsdt->Checksum = checksum((void *)Xsdt, Xsdt->Length); + + return Status; +} + +static EFIAPI EFI_STATUS UninstallAcpiTable(__attribute__((__unused__)) + EFI_ACPI_TABLE_PROTOCOL * This, + __attribute__((__unused__)) + UINTN TableKey) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS acpi_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + EFI_CONFIGURATION_TABLE *table; + EFI_GUID guid; + + static + + struct _EFI_ACPI_TABLE_PROTOCOL acpi_protocol_default = { + .InstallAcpiTable = InstallAcpiTable, + .UninstallAcpiTable = UninstallAcpiTable, + }; + + struct _EFI_ACPI_TABLE_PROTOCOL *acpi_protocol; + + if (!st) + return EFI_INVALID_PARAMETER; + + p_st = st; + + ret = get_rsdp(&Rsdp, &guid); + + if (EFI_ERROR(ret)) + return ret; + + ret = interface_init( + st, &acpi_protocol_guid, &handle, &acpi_protocol_default, + sizeof(acpi_protocol_default), (void **)&acpi_protocol); + + if (EFI_ERROR(ret)) + return ret; + + ret = conf_table_new(st, &guid, &table); + + if (EFI_ERROR(ret)) + return ret; + + table->VendorTable = Rsdp; + + return EFI_SUCCESS; +} + +static EFI_STATUS acpi_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + struct RSDP_TABLE *rsdp; + EFI_GUID guid; + + if (!st) + return EFI_INVALID_PARAMETER; + + ret = get_rsdp(&rsdp, &guid); + + if (EFI_ERROR(ret)) + return ret; + + ret = interface_free(st, &acpi_protocol_guid, handle); + + if (EFI_ERROR(ret)) + return ret; + + return conf_table_free(st, &guid); +} + +ewdrv_t acpi_drv = {.name = "acpi", + .description = "Look-up for ACPI tables and provide them " + "via the System Table,support ACPI protocol", + .init = acpi_init, + .exit = acpi_exit}; diff --git a/drivers/acpi/acpi.h b/drivers/acpi/acpi.h new file mode 100644 index 0000000..16a2290 --- /dev/null +++ b/drivers/acpi/acpi.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _ACPI_H_ +#define _ACPI_H_ + +#include + +extern ewdrv_t acpi_drv; + +#endif /* _ACPI_H_ */ diff --git a/drivers/acpi/acpi50.h b/drivers/acpi/acpi50.h new file mode 100755 index 0000000..6753fdb --- /dev/null +++ b/drivers/acpi/acpi50.h @@ -0,0 +1,602 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 2015-2017 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#ifndef _ACPI_5_0_H_ +#define _ACPI_5_0_H_ + +/* only copy part of Acpi50.h we need */ +// +// General use definitions (ACPI_1.0) +// +#define EFI_ACPI_RESERVED_BYTE 0x00 +#define EFI_ACPI_RESERVED_WORD 0x0000 +#define EFI_ACPI_RESERVED_DWORD 0x00000000 +#define EFI_ACPI_RESERVED_QWORD 0x0000000000000000 + +#pragma pack(1) +/// +/// The common ACPI description table header. This structure prefaces most ACPI +/// tables. (ACPI_1.0) +/// +typedef struct { + UINT32 Signature; + UINT32 Length; + UINT8 Revision; + UINT8 Checksum; + UINT8 OemId[6]; + UINT64 OemTableId; + UINT32 OemRevision; + UINT32 CreatorId; + UINT32 CreatorRevision; +} EFI_ACPI_DESCRIPTION_HEADER; +#pragma pack() + +// +// Ensure proper structure formats +// +#pragma pack(1) + +/// +/// ACPI 5.0 Generic Address Space definition +/// +typedef struct { + UINT8 AddressSpaceId; + UINT8 RegisterBitWidth; + UINT8 RegisterBitOffset; + UINT8 AccessSize; + UINT64 Address; +} EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE; + +// +// Generic Address Space Address IDs +// +#define EFI_ACPI_5_0_SYSTEM_MEMORY 0 +#define EFI_ACPI_5_0_SYSTEM_IO 1 +#define EFI_ACPI_5_0_PCI_CONFIGURATION_SPACE 2 +#define EFI_ACPI_5_0_EMBEDDED_CONTROLLER 3 +#define EFI_ACPI_5_0_SMBUS 4 +#define EFI_ACPI_5_0_PLATFORM_COMMUNICATION_CHANNEL 0x0A +#define EFI_ACPI_5_0_FUNCTIONAL_FIXED_HARDWARE 0x7F + +// +// Generic Address Space Access Sizes +// +#define EFI_ACPI_5_0_UNDEFINED 0 +#define EFI_ACPI_5_0_BYTE 1 +#define EFI_ACPI_5_0_WORD 2 +#define EFI_ACPI_5_0_DWORD 3 +#define EFI_ACPI_5_0_QWORD 4 + +// +// ACPI 5.0 table structures +// + +/// +/// Root System Description Pointer Structure +/// +typedef struct { + UINT64 Signature; + UINT8 Checksum; + UINT8 OemId[6]; + UINT8 Revision; + UINT32 RsdtAddress; + UINT32 Length; + UINT64 XsdtAddress; + UINT8 ExtendedChecksum; + UINT8 Reserved[3]; +} EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER; + +/// +/// RSD_PTR Revision (as defined in ACPI 5.0 spec.) +/// +#define EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION \ + 0x02 ///< ACPISpec (Revision 5.0) says current value is 2 + +/// +/// XSDT Revision (as defined in ACPI 5.0 spec.) +/// +#define EFI_ACPI_5_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_REVISION 0x01 + +/// +/// Fixed ACPI Description Table Structure (FADT) +/// +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 FirmwareCtrl; + UINT32 Dsdt; + UINT8 Reserved0; + UINT8 PreferredPmProfile; + UINT16 SciInt; + UINT32 SmiCmd; + UINT8 AcpiEnable; + UINT8 AcpiDisable; + UINT8 S4BiosReq; + UINT8 PstateCnt; + UINT32 Pm1aEvtBlk; + UINT32 Pm1bEvtBlk; + UINT32 Pm1aCntBlk; + UINT32 Pm1bCntBlk; + UINT32 Pm2CntBlk; + UINT32 PmTmrBlk; + UINT32 Gpe0Blk; + UINT32 Gpe1Blk; + UINT8 Pm1EvtLen; + UINT8 Pm1CntLen; + UINT8 Pm2CntLen; + UINT8 PmTmrLen; + UINT8 Gpe0BlkLen; + UINT8 Gpe1BlkLen; + UINT8 Gpe1Base; + UINT8 CstCnt; + UINT16 PLvl2Lat; + UINT16 PLvl3Lat; + UINT16 FlushSize; + UINT16 FlushStride; + UINT8 DutyOffset; + UINT8 DutyWidth; + UINT8 DayAlrm; + UINT8 MonAlrm; + UINT8 Century; + UINT16 IaPcBootArch; + UINT8 Reserved1; + UINT32 Flags; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE ResetReg; + UINT8 ResetValue; + UINT8 Reserved2[3]; + UINT64 XFirmwareCtrl; + UINT64 XDsdt; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XGpe0Blk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE XGpe1Blk; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE SleepControlReg; + EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE SleepStatusReg; +} EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE; + +/// +/// FADT Version (as defined in ACPI 5.0 spec.) +/// +#define EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION 0x05 + +// +// Fixed ACPI Description Table Preferred Power Management Profile +// +#define EFI_ACPI_5_0_PM_PROFILE_UNSPECIFIED 0 +#define EFI_ACPI_5_0_PM_PROFILE_DESKTOP 1 +#define EFI_ACPI_5_0_PM_PROFILE_MOBILE 2 +#define EFI_ACPI_5_0_PM_PROFILE_WORKSTATION 3 +#define EFI_ACPI_5_0_PM_PROFILE_ENTERPRISE_SERVER 4 +#define EFI_ACPI_5_0_PM_PROFILE_SOHO_SERVER 5 +#define EFI_ACPI_5_0_PM_PROFILE_APPLIANCE_PC 6 +#define EFI_ACPI_5_0_PM_PROFILE_PERFORMANCE_SERVER 7 +#define EFI_ACPI_5_0_PM_PROFILE_TABLET 8 + +// +// Fixed ACPI Description Table Boot Architecture Flags +// All other bits are reserved and must be set to 0. +// +#define EFI_ACPI_5_0_LEGACY_DEVICES BIT0 +#define EFI_ACPI_5_0_8042 BIT1 +#define EFI_ACPI_5_0_VGA_NOT_PRESENT BIT2 +#define EFI_ACPI_5_0_MSI_NOT_SUPPORTED BIT3 +#define EFI_ACPI_5_0_PCIE_ASPM_CONTROLS BIT4 +#define EFI_ACPI_5_0_CMOS_RTC_NOT_PRESENT BIT5 + +// +// Fixed ACPI Description Table Fixed Feature Flags +// All other bits are reserved and must be set to 0. +// +#define EFI_ACPI_5_0_WBINVD BIT0 +#define EFI_ACPI_5_0_WBINVD_FLUSH BIT1 +#define EFI_ACPI_5_0_PROC_C1 BIT2 +#define EFI_ACPI_5_0_P_LVL2_UP BIT3 +#define EFI_ACPI_5_0_PWR_BUTTON BIT4 +#define EFI_ACPI_5_0_SLP_BUTTON BIT5 +#define EFI_ACPI_5_0_FIX_RTC BIT6 +#define EFI_ACPI_5_0_RTC_S4 BIT7 +#define EFI_ACPI_5_0_TMR_VAL_EXT BIT8 +#define EFI_ACPI_5_0_DCK_CAP BIT9 +#define EFI_ACPI_5_0_RESET_REG_SUP BIT10 +#define EFI_ACPI_5_0_SEALED_CASE BIT11 +#define EFI_ACPI_5_0_HEADLESS BIT12 +#define EFI_ACPI_5_0_CPU_SW_SLP BIT13 +#define EFI_ACPI_5_0_PCI_EXP_WAK BIT14 +#define EFI_ACPI_5_0_USE_PLATFORM_CLOCK BIT15 +#define EFI_ACPI_5_0_S4_RTC_STS_VALID BIT16 +#define EFI_ACPI_5_0_REMOTE_POWER_ON_CAPABLE BIT17 +#define EFI_ACPI_5_0_FORCE_APIC_CLUSTER_MODEL BIT18 +#define EFI_ACPI_5_0_FORCE_APIC_PHYSICAL_DESTINATION_MODE BIT19 +#define EFI_ACPI_5_0_HW_REDUCED_ACPI BIT20 +#define EFI_ACPI_5_0_LOW_POWER_S0_IDLE_CAPABLE BIT21 + +/// +/// Firmware ACPI Control Structure +/// +typedef struct { + UINT32 Signature; + UINT32 Length; + UINT32 HardwareSignature; + UINT32 FirmwareWakingVector; + UINT32 GlobalLock; + UINT32 Flags; + UINT64 XFirmwareWakingVector; + UINT8 Version; + UINT8 Reserved0[3]; + UINT32 OspmFlags; + UINT8 Reserved1[24]; +} EFI_ACPI_5_0_FIRMWARE_ACPI_CONTROL_STRUCTURE; + +/// +/// FACS Version (as defined in ACPI 5.0 spec.) +/// +#define EFI_ACPI_5_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION 0x02 + +/// +/// Firmware Control Structure Feature Flags +/// All other bits are reserved and must be set to 0. +/// +#define EFI_ACPI_5_0_S4BIOS_F BIT0 +#define EFI_ACPI_5_0_64BIT_WAKE_SUPPORTED_F BIT1 + +/// +/// OSPM Enabled Firmware Control Structure Flags +/// All other bits are reserved and must be set to 0. +/// +#define EFI_ACPI_5_0_OSPM_64BIT_WAKE_F BIT0 + +// +// Differentiated System Description Table, +// Secondary System Description Table +// and Persistent System Description Table, +// no definition needed as they are common description table header, the same +// with EFI_ACPI_DESCRIPTION_HEADER, followed by a definition block. +// +#define EFI_ACPI_5_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_REVISION 0x02 +#define EFI_ACPI_5_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_REVISION 0x02 + +/// +/// Multiple APIC Description Table header definition. The rest of the table +/// must be defined in a platform specific manner. +/// +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 LocalApicAddress; + UINT32 Flags; +} EFI_ACPI_5_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER; + +/// +/// MADT Revision (as defined in ACPI 5.0 spec.) +/// +#define EFI_ACPI_5_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION 0x03 + +/// +/// Multiple APIC Flags +/// All other bits are reserved and must be set to 0. +/// +#define EFI_ACPI_5_0_PCAT_COMPAT BIT0 + +// +// Multiple APIC Description Table APIC structure types +// All other values between 0x0D and 0x7F are reserved and +// will be ignored by OSPM. 0x80 ~ 0xFF are reserved for OEM. +// +#define EFI_ACPI_5_0_PROCESSOR_LOCAL_APIC 0x00 +#define EFI_ACPI_5_0_IO_APIC 0x01 +#define EFI_ACPI_5_0_INTERRUPT_SOURCE_OVERRIDE 0x02 +#define EFI_ACPI_5_0_NON_MASKABLE_INTERRUPT_SOURCE 0x03 +#define EFI_ACPI_5_0_LOCAL_APIC_NMI 0x04 +#define EFI_ACPI_5_0_LOCAL_APIC_ADDRESS_OVERRIDE 0x05 +#define EFI_ACPI_5_0_IO_SAPIC 0x06 +#define EFI_ACPI_5_0_LOCAL_SAPIC 0x07 +#define EFI_ACPI_5_0_PLATFORM_INTERRUPT_SOURCES 0x08 +#define EFI_ACPI_5_0_PROCESSOR_LOCAL_X2APIC 0x09 +#define EFI_ACPI_5_0_LOCAL_X2APIC_NMI 0x0A +#define EFI_ACPI_5_0_GIC 0x0B +#define EFI_ACPI_5_0_GICD 0x0C + +// +// APIC Structure Definitions +// + +/// +/// Processor Local APIC Structure Definition +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 AcpiProcessorId; + UINT8 ApicId; + UINT32 Flags; +} EFI_ACPI_5_0_PROCESSOR_LOCAL_APIC_STRUCTURE; + +/// +/// Local APIC Flags. All other bits are reserved and must be 0. +/// +#define EFI_ACPI_5_0_LOCAL_APIC_ENABLED BIT0 + +/// +/// IO APIC Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 IoApicId; + UINT8 Reserved; + UINT32 IoApicAddress; + UINT32 GlobalSystemInterruptBase; +} EFI_ACPI_5_0_IO_APIC_STRUCTURE; + +/// +/// Interrupt Source Override Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 Bus; + UINT8 Source; + UINT32 GlobalSystemInterrupt; + UINT16 Flags; +} EFI_ACPI_5_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE; + +/// +/// Platform Interrupt Sources Structure Definition +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Flags; + UINT8 InterruptType; + UINT8 ProcessorId; + UINT8 ProcessorEid; + UINT8 IoSapicVector; + UINT32 GlobalSystemInterrupt; + UINT32 PlatformInterruptSourceFlags; + UINT8 CpeiProcessorOverride; + UINT8 Reserved[31]; +} EFI_ACPI_5_0_PLATFORM_INTERRUPT_APIC_STRUCTURE; + +// +// MPS INTI flags. +// All other bits are reserved and must be set to 0. +// +#define EFI_ACPI_5_0_POLARITY (3 << 0) +#define EFI_ACPI_5_0_TRIGGER_MODE (3 << 2) + +/// +/// Non-Maskable Interrupt Source Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Flags; + UINT32 GlobalSystemInterrupt; +} EFI_ACPI_5_0_NON_MASKABLE_INTERRUPT_SOURCE_STRUCTURE; + +/// +/// Local APIC NMI Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 AcpiProcessorId; + UINT16 Flags; + UINT8 LocalApicLint; +} EFI_ACPI_5_0_LOCAL_APIC_NMI_STRUCTURE; + +/// +/// Local APIC Address Override Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Reserved; + UINT64 LocalApicAddress; +} EFI_ACPI_5_0_LOCAL_APIC_ADDRESS_OVERRIDE_STRUCTURE; + +/// +/// IO SAPIC Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 IoApicId; + UINT8 Reserved; + UINT32 GlobalSystemInterruptBase; + UINT64 IoSapicAddress; +} EFI_ACPI_5_0_IO_SAPIC_STRUCTURE; + +/// +/// Local SAPIC Structure +/// This struct followed by a null-terminated ASCII string - ACPI Processor UID +/// String +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 AcpiProcessorId; + UINT8 LocalSapicId; + UINT8 LocalSapicEid; + UINT8 Reserved[3]; + UINT32 Flags; + UINT32 ACPIProcessorUIDValue; +} EFI_ACPI_5_0_PROCESSOR_LOCAL_SAPIC_STRUCTURE; + +/// +/// Platform Interrupt Sources Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Flags; + UINT8 InterruptType; + UINT8 ProcessorId; + UINT8 ProcessorEid; + UINT8 IoSapicVector; + UINT32 GlobalSystemInterrupt; + UINT32 PlatformInterruptSourceFlags; +} EFI_ACPI_5_0_PLATFORM_INTERRUPT_SOURCES_STRUCTURE; + +/// +/// Platform Interrupt Source Flags. +/// All other bits are reserved and must be set to 0. +/// +#define EFI_ACPI_5_0_CPEI_PROCESSOR_OVERRIDE BIT0 + +/// +/// Processor Local x2APIC Structure Definition +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 Reserved[2]; + UINT32 X2ApicId; + UINT32 Flags; + UINT32 AcpiProcessorUid; +} EFI_ACPI_5_0_PROCESSOR_LOCAL_X2APIC_STRUCTURE; + +/// +/// Local x2APIC NMI Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Flags; + UINT32 AcpiProcessorUid; + UINT8 LocalX2ApicLint; + UINT8 Reserved[3]; +} EFI_ACPI_5_0_LOCAL_X2APIC_NMI_STRUCTURE; + +/// +/// GIC Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Reserved; + UINT32 GicId; + UINT32 AcpiProcessorUid; + UINT32 Flags; + UINT32 ParkingProtocolVersion; + UINT32 PerformanceInterruptGsiv; + UINT64 ParkedAddress; + UINT64 PhysicalBaseAddress; +} EFI_ACPI_5_0_GIC_STRUCTURE; + +/// +/// GIC Flags. All other bits are reserved and must be 0. +/// +#define EFI_ACPI_5_0_GIC_ENABLED BIT0 +#define EFI_ACPI_5_0_PERFORMANCE_INTERRUPT_MODEL BIT1 + +/// +/// GIC Distributor Structure +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Reserved1; + UINT32 GicId; + UINT64 PhysicalBaseAddress; + UINT32 SystemVectorBase; + UINT32 Reserved2; +} EFI_ACPI_5_0_GIC_DISTRIBUTOR_STRUCTURE; + +#pragma pack() + +// +// Known table signatures +// + +/// +/// "RSD PTR " Root System Description Pointer +/// +#define EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE \ + SIGNATURE_64('R', 'S', 'D', ' ', 'P', 'T', 'R', ' ') + +/// +/// "APIC" Multiple APIC Description Table +/// +#define EFI_ACPI_5_0_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE \ + SIGNATURE_32('A', 'P', 'I', 'C') + +/// +/// "DSDT" Differentiated System Description Table +/// +#define EFI_ACPI_5_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE \ + SIGNATURE_32('D', 'S', 'D', 'T') + +/// +/// "FACP" Fixed ACPI Description Table +/// +#define EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE \ + SIGNATURE_32('F', 'A', 'C', 'P') + +/// +/// "FACS" Firmware ACPI Control Structure +/// +#define EFI_ACPI_5_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE \ + SIGNATURE_32('F', 'A', 'C', 'S') + +/// +/// "HPET" IA-PC High Precision Event Timer Table +/// +#define EFI_ACPI_5_0_HIGH_PRECISION_EVENT_TIMER_TABLE_SIGNATURE \ + SIGNATURE_32('H', 'P', 'E', 'T') + +/// +/// "MCFG" PCI Express Memory Mapped Configuration Space Base Address +/// Description Table +/// +#define EFI_ACPI_5_0_PCI_EXPRESS_MEMORY_MAPPED_CONFIGURATION_SPACE_BASE_ADDRESS_DESCRIPTION_TABLE_SIGNATURE \ + SIGNATURE_32('M', 'C', 'F', 'G') + +/// +/// "XSDT" Extended System Description Table +/// +#define EFI_ACPI_5_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE \ + SIGNATURE_32('X', 'S', 'D', 'T') + +/// +/// "TPM2" Trusted Computing Platform 1 Table +/// +#define EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE \ + SIGNATURE_32('T', 'P', 'M', '2') + +/// +/// Non-HDA Link Table +/// +#define EFI_ACPI_5_0_NON_HIGH_DEFINITION_AUDIO_LINK_TABLE_SIGNATURE \ + SIGNATURE_32('N', 'H', 'L', 'T') + +/// +/// "SSDT" Secondary System Description Table +/// +#define EFI_ACPI_5_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE \ + SIGNATURE_32('S', 'S', 'D', 'T') + +#endif /* ACPI_5_0_H_ */ diff --git a/drivers/cf9/cf9.c b/drivers/cf9/cf9.c new file mode 100644 index 0000000..6bd2e8c --- /dev/null +++ b/drivers/cf9/cf9.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + + +#include +#include +#include +#include + +#include "cf9/cf9.h" + +static EFIAPI EFI_STATUS +cf9_reset_system(EFI_RESET_TYPE ResetType, + __attribute__((__unused__)) EFI_STATUS ResetStatus, + __attribute__((__unused__)) UINTN DataSize, + __attribute__((__unused__)) CHAR16 *ResetData) +{ + UINT8 code; + UINT8 cf9; + UINT32 port = 0xcf9; + + if (ResetType == EfiResetShutdown) + return EFI_UNSUPPORTED; + + switch (ResetType) { + case EfiResetWarm: + code = 0x06; + break; + + case EfiResetCold: + code = 0x0E; + break; + + case EfiResetShutdown: + default: + return EFI_UNSUPPORTED; + } + + cf9 = inb(port) & ~code; + outb(cf9 | 2, port); + udelay(50); + + outb(cf9 | code, port); + udelay(500); + + return EFI_DEVICE_ERROR; +} + +static EFI_RESET_SYSTEM saved_reset_rs; + +static EFI_STATUS cf9_init(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + saved_reset_rs = st->RuntimeServices->ResetSystem; + st->RuntimeServices->ResetSystem = cf9_reset_system; + + return EFI_SUCCESS; +} + +static EFI_STATUS cf9_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + st->RuntimeServices->ResetSystem = saved_reset_rs; + + return EFI_SUCCESS; +} + +ewdrv_t cf9_drv = { + .name = "cf9", + .description = "Provide reset support based on CF9 IO port", + .init = cf9_init, + .exit = cf9_exit +}; + diff --git a/drivers/cf9/cf9.h b/drivers/cf9/cf9.h new file mode 100644 index 0000000..3adae83 --- /dev/null +++ b/drivers/cf9/cf9.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _CF9_H_ +#define _CF9_H_ + +#include + +extern ewdrv_t cf9_drv; + +#endif /* _CF9_H_ */ diff --git a/drivers/dw3/Usb.h b/drivers/dw3/Usb.h new file mode 100644 index 0000000..299fb4c --- /dev/null +++ b/drivers/dw3/Usb.h @@ -0,0 +1,360 @@ +/** @file + Support for USB 2.0 standard. + + Copyright (c) 2006 - 2008, 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 __USB_H__ +#define __USB_H__ + +// +// Standard device request and request type +// USB 2.0 spec, Section 9.4 +// +#define USB_DEV_GET_STATUS 0x00 +#define USB_DEV_GET_STATUS_REQ_TYPE_D 0x80 // Receiver : Device +#define USB_DEV_GET_STATUS_REQ_TYPE_I 0x81 // Receiver : Interface +#define USB_DEV_GET_STATUS_REQ_TYPE_E 0x82 // Receiver : Endpoint + +#define USB_DEV_CLEAR_FEATURE 0x01 +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_FEATURE 0x03 +#define USB_DEV_SET_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_SET_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_SET_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_ADDRESS 0x05 +#define USB_DEV_SET_ADDRESS_REQ_TYPE 0x00 + +#define USB_DEV_GET_DESCRIPTOR 0x06 +#define USB_DEV_GET_DESCRIPTOR_REQ_TYPE 0x80 + +#define USB_DEV_SET_DESCRIPTOR 0x07 +#define USB_DEV_SET_DESCRIPTOR_REQ_TYPE 0x00 + +#define USB_DEV_GET_CONFIGURATION 0x08 +#define USB_DEV_GET_CONFIGURATION_REQ_TYPE 0x80 + +#define USB_DEV_SET_CONFIGURATION 0x09 +#define USB_DEV_SET_CONFIGURATION_REQ_TYPE 0x00 + +#define USB_DEV_GET_INTERFACE 0x0A +#define USB_DEV_GET_INTERFACE_REQ_TYPE 0x81 + +#define USB_DEV_SET_INTERFACE 0x0B +#define USB_DEV_SET_INTERFACE_REQ_TYPE 0x01 + +#define USB_DEV_SYNCH_FRAME 0x0C +#define USB_DEV_SYNCH_FRAME_REQ_TYPE 0x82 + + +// +// USB standard descriptors and reqeust +// +#pragma pack(1) + +/// +/// Format of Setup Data for USB Device Requests +/// USB 2.0 spec, Section 9.3 +/// +typedef struct { + UINT8 RequestType; + UINT8 Request; + UINT16 Value; + UINT16 Index; + UINT16 Length; +} USB_DEVICE_REQUEST; + +/// +/// Standard Device Descriptor +/// USB 2.0 spec, Section 9.6.1 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 BcdUSB; + UINT8 DeviceClass; + UINT8 DeviceSubClass; + UINT8 DeviceProtocol; + UINT8 MaxPacketSize0; + UINT16 IdVendor; + UINT16 IdProduct; + UINT16 BcdDevice; + UINT8 StrManufacturer; + UINT8 StrProduct; + UINT8 StrSerialNumber; + UINT8 NumConfigurations; +} USB_DEVICE_DESCRIPTOR; + +/// +/// Standard Configuration Descriptor +/// USB 2.0 spec, Section 9.6.3 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 TotalLength; + UINT8 NumInterfaces; + UINT8 ConfigurationValue; + UINT8 Configuration; + UINT8 Attributes; + UINT8 MaxPower; +} USB_CONFIG_DESCRIPTOR; + +/// +/// Standard Interface Descriptor +/// USB 2.0 spec, Section 9.6.5 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 InterfaceNumber; + UINT8 AlternateSetting; + UINT8 NumEndpoints; + UINT8 InterfaceClass; + UINT8 InterfaceSubClass; + UINT8 InterfaceProtocol; + UINT8 Interface; +} USB_INTERFACE_DESCRIPTOR; + +/// +/// Standard Endpoint Descriptor +/// USB 2.0 spec, Section 9.6.6 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 EndpointAddress; + UINT8 Attributes; + UINT16 MaxPacketSize; + UINT8 Interval; +} USB_ENDPOINT_DESCRIPTOR; + +/// +/// UNICODE String Descriptor +/// USB 2.0 spec, Section 9.6.7 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + CHAR16 String[1]; +} EFI_USB_STRING_DESCRIPTOR; + +#pragma pack() + + +typedef enum { + // + // USB request type + // + USB_REQ_TYPE_STANDARD = (0x00 << 5), + USB_REQ_TYPE_CLASS = (0x01 << 5), + USB_REQ_TYPE_VENDOR = (0x02 << 5), + + // + // Standard control transfer request type, or the value + // to fill in EFI_USB_DEVICE_REQUEST.Request + // + USB_REQ_GET_STATUS = 0x00, + USB_REQ_CLEAR_FEATURE = 0x01, + USB_REQ_SET_FEATURE = 0x03, + USB_REQ_SET_ADDRESS = 0x05, + USB_REQ_GET_DESCRIPTOR = 0x06, + USB_REQ_SET_DESCRIPTOR = 0x07, + USB_REQ_GET_CONFIG = 0x08, + USB_REQ_SET_CONFIG = 0x09, + USB_REQ_GET_INTERFACE = 0x0A, + USB_REQ_SET_INTERFACE = 0x0B, + USB_REQ_SYNCH_FRAME = 0x0C, + + // + // Usb control transfer target + // + USB_TARGET_DEVICE = 0, + USB_TARGET_INTERFACE = 0x01, + USB_TARGET_ENDPOINT = 0x02, + USB_TARGET_OTHER = 0x03, + + // + // USB Descriptor types + // + USB_DESC_TYPE_DEVICE = 0x01, + USB_DESC_TYPE_CONFIG = 0x02, + USB_DESC_TYPE_STRING = 0x03, + USB_DESC_TYPE_INTERFACE = 0x04, + USB_DESC_TYPE_ENDPOINT = 0x05, + USB_DESC_TYPE_HID = 0x21, + USB_DESC_TYPE_REPORT = 0x22, + + // + // Features to be cleared by CLEAR_FEATURE requests + // + USB_FEATURE_ENDPOINT_HALT = 0, + + // + // USB endpoint types: 00: control, 01: isochronous, 10: bulk, 11: interrupt + // + USB_ENDPOINT_CONTROL = 0x00, + USB_ENDPOINT_ISO = 0x01, + USB_ENDPOINT_BULK = 0x02, + USB_ENDPOINT_INTERRUPT = 0x03, + + USB_ENDPOINT_TYPE_MASK = 0x03, + USB_ENDPOINT_DIR_IN = 0x80, + + // + //Use 200 ms to increase the error handling response time + // + EFI_USB_INTERRUPT_DELAY = 2000000 +} USB_TYPES_DEFINITION; + + +// +// HID constants definition, see Device Class Definition +// for Human Interface Devices (HID) rev1.11 +// + +// +// HID standard GET_DESCRIPTOR request. +// +#define USB_HID_GET_DESCRIPTOR_REQ_TYPE 0x81 + +// +// HID specific requests. +// +#define USB_HID_CLASS_GET_REQ_TYPE 0xa1 +#define USB_HID_CLASS_SET_REQ_TYPE 0x21 + +// +// HID report item format +// +#define HID_ITEM_FORMAT_SHORT 0 +#define HID_ITEM_FORMAT_LONG 1 + +// +// Special tag indicating long items +// +#define HID_ITEM_TAG_LONG 15 + +// +// HID report descriptor item type (prefix bit 2,3) +// +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 + +// +// HID report descriptor main item tags +// +#define HID_MAIN_ITEM_TAG_INPUT 8 +#define HID_MAIN_ITEM_TAG_OUTPUT 9 +#define HID_MAIN_ITEM_TAG_FEATURE 11 +#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 +#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 + +// +// HID report descriptor main item contents +// +#define HID_MAIN_ITEM_CONSTANT 0x001 +#define HID_MAIN_ITEM_VARIABLE 0x002 +#define HID_MAIN_ITEM_RELATIVE 0x004 +#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_NONLINEAR 0x010 +#define HID_MAIN_ITEM_NO_PREFERRED 0x020 +#define HID_MAIN_ITEM_NULL_STATE 0x040 +#define HID_MAIN_ITEM_VOLATILE 0x080 +#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 + +// +// HID report descriptor collection item types +// +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 +#define HID_COLLECTION_LOGICAL 2 + +// +// HID report descriptor global item tags +// +#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 +#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 +#define HID_GLOBAL_ITEM_TAG_UNIT 6 +#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 +#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 +#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 +#define HID_GLOBAL_ITEM_TAG_PUSH 10 +#define HID_GLOBAL_ITEM_TAG_POP 11 + +// +// HID report descriptor local item tags +// +#define HID_LOCAL_ITEM_TAG_USAGE 0 +#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 +#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 +#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 +#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 +#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 +#define HID_LOCAL_ITEM_TAG_DELIMITER 10 + +// +// HID report types +// +#define HID_INPUT_REPORT 1 +#define HID_OUTPUT_REPORT 2 +#define HID_FEATURE_REPORT 3 + +// +// HID class protocol request +// +#define EFI_USB_GET_REPORT_REQUEST 0x01 +#define EFI_USB_GET_IDLE_REQUEST 0x02 +#define EFI_USB_GET_PROTOCOL_REQUEST 0x03 +#define EFI_USB_SET_REPORT_REQUEST 0x09 +#define EFI_USB_SET_IDLE_REQUEST 0x0a +#define EFI_USB_SET_PROTOCOL_REQUEST 0x0b + +#pragma pack(1) +/// +/// Descriptor header for Report/Physical Descriptors +/// HID 1.1, section 6.2.1 +/// +typedef struct hid_class_descriptor { + UINT8 DescriptorType; + UINT16 DescriptorLength; +} EFI_USB_HID_CLASS_DESCRIPTOR; + +/// +/// The HID descriptor identifies the length and type +/// of subordinate descriptors for a device. +/// HID 1.1, section 6.2.1 +/// +typedef struct hid_descriptor { + UINT8 Length; + UINT8 DescriptorType; + UINT16 BcdHID; + UINT8 CountryCode; + UINT8 NumDescriptors; + EFI_USB_HID_CLASS_DESCRIPTOR HidClassDesc[1]; +} EFI_USB_HID_DESCRIPTOR; + +#pragma pack() + +#endif diff --git a/drivers/dw3/UsbDeviceLib.h b/drivers/dw3/UsbDeviceLib.h new file mode 100644 index 0000000..765be25 --- /dev/null +++ b/drivers/dw3/UsbDeviceLib.h @@ -0,0 +1,223 @@ +/** @file + Copyright (c) 2009 - 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 _EFI_XDCI_LIB_H_ +#define _EFI_XDCI_LIB_H_ + +#include "dw3/UsbIo.h" + +#define MAX_DESCRIPTOR_SIZE 64 +#define STRING_ARR_SIZE (MAX_DESCRIPTOR_SIZE - 2) +#define USB_ADDRESS_TABLE_SIZE 16 //4 + +// +// Endpoint Zero +// +#define USB_EP0_MAX_PKT_SIZE_HS 0x40 // High Speed mode is explicitly set as 64 bytes +#define USB_EP0_MAX_PKT_SIZE_SS 0x9 // Must be 0x9 (2^9 = 512 Bytes) in SuperSpeed mode +#define USB_EPO_MAX_PKT_SIZE_ALL 512 // Overall max bytes for any type + +// +// Bulk Endpoints +// +#define USB_BULK_EP_PKT_SIZE_HS 0x200 // Bulk-Endpoint HighSpeed +#define USB_BULK_EP_PKT_SIZE_SS 0x400 // Bulk-Endpoint SuperSpeed +#define USB_BULK_EP_PKT_SIZE_MAX USB_BULK_EP_PKT_SIZE_SS + +// +// Transmit Direction Bits +// +#define USB_ENDPOINT_DIR_OUT 0x00 + +// +// Endpoint Companion Bulk Attributes +// +#define USB_EP_BULK_BM_ATTR_MASK 0x1F + +// +// Configuration Modifiers (Attributes) +// +#define USB_BM_ATTR_RESERVED 0x80 +#define USB_BM_ATTR_SELF_POWERED 0x40 +#define USB_BM_ATTR_REMOTE_WAKE 0X20 + +// +// USB BCD version +// +#define USB_BCD_VERSION_LS 0x0110 +#define USB_BCD_VERSION_HS 0x0200 +#define USB_BCD_VERSION_SS 0x0300 + +// +// Device RequestType Flags +// +#define USB_RT_TX_DIR_H_TO_D (0x0) // Tx direction Host to Device +#define USB_RT_TX_DIR_D_TO_H (0x1 << 7) // Tx direction Device to Host +#define USB_RT_TX_DIR_MASK (0x80) + +// +// USB request type +// +#define USB_REQ_TYPE_MASK (0x60) + +// +// Usb control transfer target +// +#define USB_TARGET_MASK (0x1F) + +// +// Device GetStatus bits +// +#define USB_STATUS_SELFPOWERED (0x01) +#define USB_STATUS_REMOTEWAKEUP (0x02) + +// +// USB Device class identifiers +// +#define USB_DEVICE_MS_CLASS (0x08) +#define USB_DEVICE_VENDOR_CLASS (0xFF) + +// +// USB Descriptor types +// +#define USB_DESC_TYPE_SS_ENDPOINT_COMPANION 0x30 + + +// +// USB device states from USB spec sec 9.1 +// +typedef enum { + UsbDevStateOff = 0, + UsbDevStateInit, + UsbDevStateAttached, + UsbDevStatePowered, + UsbDevStateDefault, + UsbDevStateAddress, + UsbDevStateConfigured, + UsbDevStateSuspended, + UsbDevStateError +} USB_DEVICE_STATE; + + +// +// The following set of structs are used during USB data transaction +// operatitions, including requests and completion events. +// +#pragma pack(1) + +typedef struct { + UINT32 EndpointNum; + UINT8 EndpointDir; + UINT8 EndpointType; + UINT32 Length; + VOID *Buffer; +} EFI_USB_DEVICE_XFER_INFO; + +// +// SuperSpeed Endpoint companion descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 MaxBurst; + UINT8 Attributes; + UINT16 BytesPerInterval; +} EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR; + +typedef struct { + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc; + EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EndpointCompDesc; +} USB_DEVICE_ENDPOINT_INFO, USB_DEVICE_ENDPOINT_OBJ; + +typedef struct { + VOID *Buffer; + UINT32 Length; +} USB_DEVICE_IO_INFO; + +typedef struct { + USB_DEVICE_IO_INFO IoInfo; + USB_DEVICE_ENDPOINT_INFO EndpointInfo; +} USB_DEVICE_IO_REQ; + +// +// Optional string descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 LangID[STRING_ARR_SIZE]; +} USB_STRING_DESCRIPTOR; + + +// +// The following structures abstract the device descriptors a class +// driver needs to provide to the USBD core. +// These structures are filled & owned by the class/function layer. +// +typedef struct { + EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc; + USB_DEVICE_ENDPOINT_OBJ *EndpointObjs; +} USB_DEVICE_INTERFACE_OBJ; + +typedef struct { + EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc; + VOID *ConfigAll; + USB_DEVICE_INTERFACE_OBJ *InterfaceObjs; +} USB_DEVICE_CONFIG_OBJ; + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_CONFIG_CALLBACK) ( + IN UINT8 CfgVal + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_SETUP_CALLBACK) ( + IN EFI_USB_DEVICE_REQUEST *CtrlRequest, + IN USB_DEVICE_IO_INFO *IoInfo + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DATA_CALLBACK) ( + IN EFI_USB_DEVICE_XFER_INFO *XferInfo + ); + +typedef struct { + USB_DEVICE_DESCRIPTOR *DeviceDesc; + USB_DEVICE_CONFIG_OBJ *ConfigObjs; + USB_STRING_DESCRIPTOR *StringTable; + UINT8 StrTblEntries; + EFI_USB_CONFIG_CALLBACK ConfigCallback; + EFI_USB_SETUP_CALLBACK SetupCallback; + EFI_USB_DATA_CALLBACK DataCallback; +} USB_DEVICE_OBJ; + + +// +// Main USBD driver object structure containing all data necessary +// for USB device mode processing at this layer +// +typedef struct { + USB_DEVICE_OBJ *UsbdDevObj; /* pointer to a Device Object */ + VOID *DciDrvObj; /* Opaque handle to DCI driver */ + UINT32 MmioBar; /* MMIO BAR */ + BOOLEAN DciInitialized; /* flag to specify if the DCI driver is initialized */ + USB_DEVICE_CONFIG_OBJ *ActiveConfigObj; /* pointer to currently active configuraiton */ + USB_DEVICE_STATE State; /* current state of the USB Device state machine */ + UINT8 Address; /* configured device address */ +} USB_DEVICE_DRIVER_OBJ; + +#pragma pack() + +#endif diff --git a/drivers/dw3/UsbDeviceModeProtocol.h b/drivers/dw3/UsbDeviceModeProtocol.h new file mode 100644 index 0000000..2b64b25 --- /dev/null +++ b/drivers/dw3/UsbDeviceModeProtocol.h @@ -0,0 +1,105 @@ +/** @file + The runtime cryptographic protocol. + Only limited crypto primitives (SHA-256 and RSA) are provided for runtime + authenticated variable service. + + Copyright (c) 2010 - 2013, 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 _USB_DEVICE_MODE_PROTOCOL_H_ +#define _USB_DEVICE_MODE_PROTOCOL_H_ + +#include "UsbDeviceLib.h" + +/// +/// UsbDeviceMode Protocol GUID. +/// +#define EFI_USB_DEVICE_MODE_PROTOCOL_GUID \ + {0xC9923F7E, 0x1746, 0x4802, { 0x86, 0x2e, 0x1, 0x1c, 0x2c, 0x2d, 0x9d, 0x86 } } + +typedef struct _EFI_USB_DEVICE_MODE_PROTOCOL EFI_USB_DEVICE_MODE_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_INIT_XDCI) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_CONNECT) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_DISCONNECT) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_EP_TX_DATA) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN USB_DEVICE_IO_REQ *IoRequest + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_EP_RX_DATA) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN USB_DEVICE_IO_REQ *IoRequest + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_BIND) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN USB_DEVICE_OBJ *UsbdDevObj + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_UNBIND) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_STOP) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_RUN) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN UINT32 TimeoutMs + ); + +/// +/// Runtime Usb Device Mode Protocol Structure. +/// +struct _EFI_USB_DEVICE_MODE_PROTOCOL { + EFI_USB_DEVICE_MODE_INIT_XDCI InitXdci; + EFI_USB_DEVICE_MODE_CONNECT Connect; + EFI_USB_DEVICE_MODE_DISCONNECT DisConnect; + EFI_USB_DEVICE_EP_TX_DATA EpTxData; + EFI_USB_DEVICE_EP_RX_DATA EpRxData; + EFI_USB_DEVICE_MODE_BIND Bind; + EFI_USB_DEVICE_MODE_UNBIND UnBind; + EFI_USB_DEVICE_MODE_RUN Run; + EFI_USB_DEVICE_MODE_STOP Stop; +}; + +extern EFI_GUID gEfiUsbDeviceModeProtocolGuid; + +#endif diff --git a/drivers/dw3/UsbIo.h b/drivers/dw3/UsbIo.h new file mode 100644 index 0000000..2625e73 --- /dev/null +++ b/drivers/dw3/UsbIo.h @@ -0,0 +1,512 @@ +/** @file + EFI Usb I/O Protocol as defined in UEFI specification. + This protocol is used by code, typically drivers, running in the EFI + boot services environment to access USB devices like USB keyboards, + mice and mass storage devices. In particular, functions for managing devices + on USB buses are defined here. + + Copyright (c) 2006 - 2008, 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 __USB_IO_H__ +#define __USB_IO_H__ + +#include "dw3/Usb.h" + +// +// Global ID for the USB I/O Protocol +// +#define EFI_USB_IO_PROTOCOL_GUID \ + { \ + 0x2B2F68D6, 0x0CD2, 0x44cf, {0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 } \ + } + +typedef struct _EFI_USB_IO_PROTOCOL EFI_USB_IO_PROTOCOL; + +// +// Related Definition for EFI USB I/O protocol +// + +// +// USB standard descriptors and reqeust +// +typedef USB_DEVICE_REQUEST EFI_USB_DEVICE_REQUEST; +typedef USB_DEVICE_DESCRIPTOR EFI_USB_DEVICE_DESCRIPTOR; +typedef USB_CONFIG_DESCRIPTOR EFI_USB_CONFIG_DESCRIPTOR; +typedef USB_INTERFACE_DESCRIPTOR EFI_USB_INTERFACE_DESCRIPTOR; +typedef USB_ENDPOINT_DESCRIPTOR EFI_USB_ENDPOINT_DESCRIPTOR; + +/// +/// USB data transfer direction +/// +typedef enum { + EfiUsbDataIn, + EfiUsbDataOut, + EfiUsbNoData +} EFI_USB_DATA_DIRECTION; + +// +// USB Transfer Results +// +#define EFI_USB_NOERROR 0x00 +#define EFI_USB_ERR_NOTEXECUTE 0x01 +#define EFI_USB_ERR_STALL 0x02 +#define EFI_USB_ERR_BUFFER 0x04 +#define EFI_USB_ERR_BABBLE 0x08 +#define EFI_USB_ERR_NAK 0x10 +#define EFI_USB_ERR_CRC 0x20 +#define EFI_USB_ERR_TIMEOUT 0x40 +#define EFI_USB_ERR_BITSTUFF 0x80 +#define EFI_USB_ERR_SYSTEM 0x100 + +/** + Async USB transfer callback routine. + + @param Data Data received or sent via the USB Asynchronous Transfer, if the + transfer completed successfully. + @param DataLength The length of Data received or sent via the Asynchronous + Transfer, if transfer successfully completes. + @param Context Data passed from UsbAsyncInterruptTransfer() request. + @param Status Indicates the result of the asynchronous transfer. + + @retval EFI_SUCCESS The asynchronous USB transfer request has been successfully executed. + @retval EFI_DEVICE_ERROR The asynchronous USB transfer request failed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ASYNC_USB_TRANSFER_CALLBACK)( + IN VOID *Data, + IN UINTN DataLength, + IN VOID *Context, + IN UINT32 Status + ); + +// +// Prototype for EFI USB I/O protocol +// + + +/** + This function is used to manage a USB device with a control transfer pipe. A control transfer is + typically used to perform device initialization and configuration. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param Request A pointer to the USB device request that will be sent to the USB + device. + @param Direction Indicates the data direction. + @param Timeout Indicating the transfer should be completed within this time frame. + The units are in milliseconds. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + @param Status A pointer to the result of the USB transfer. + + @retval EFI_SUCCESS The control transfer has been successfully executed. + @retval EFI_DEVICE_ERROR The transfer failed. The transfer status is returned in Status. + @retval EFI_INVALID_PARAMETE One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_TIMEOUT The control transfer fails due to timeout. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_CONTROL_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT32 Timeout, + IN OUT VOID *Data OPTIONAL, + IN UINTN DataLength OPTIONAL, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with the bulk transfer pipe. Bulk Transfers are + typically used to transfer large amounts of data to/from USB devices. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + On input, the size, in bytes, of the data buffer specified by Data. + On output, the number of bytes that were actually transferred. + @param Timeout Indicating the transfer should be completed within this time frame. + The units are in milliseconds. If Timeout is 0, then the + caller must wait for the function to be completed until + EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + @param Status This parameter indicates the USB transfer status. + + @retval EFI_SUCCESS The bulk transfer has been successfully executed. + @retval EFI_DEVICE_ERROR The transfer failed. The transfer status is returned in Status. + @retval EFI_INVALID_PARAMETE One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The control transfer fails due to timeout. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_BULK_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with an interrupt transfer pipe. An Asynchronous + Interrupt Transfer is typically used to query a device's status at a fixed rate. For example, + keyboard, mouse, and hub devices use this type of transfer to query their interrupt endpoints at + a fixed rate. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param IsNewTransfer If TRUE, a new transfer will be submitted to USB controller. If + FALSE, the interrupt transfer is deleted from the device's interrupt + transfer queue. + @param PollingInterval Indicates the periodic rate, in milliseconds, that the transfer is to be + executed.This parameter is required when IsNewTransfer is TRUE. The + value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned. + The units are in milliseconds. + @param DataLength Specifies the length, in bytes, of the data to be received from the + USB device. This parameter is only required when IsNewTransfer is TRUE. + @param InterruptCallback The Callback function. This function is called if the asynchronous + interrupt transfer is completed. This parameter is required + when IsNewTransfer is TRUE. + @param Context Data passed to the InterruptCallback function. This is an optional + parameter and may be NULL. + + @retval EFI_SUCCESS The asynchronous USB transfer request transfer has been successfully executed. + @retval EFI_DEVICE_ERROR The asynchronous USB transfer request failed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN BOOLEAN IsNewTransfer, + IN UINTN PollingInterval OPTIONAL, + IN UINTN DataLength OPTIONAL, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK InterruptCallBack OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + This function is used to manage a USB device with an interrupt transfer pipe. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength On input, then size, in bytes, of the buffer Data. On output, the + amount of data actually transferred. + @param Timeout The time out, in seconds, for this transfer. If Timeout is 0, + then the caller must wait for the function to be completed + until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. If the + transfer is not completed in this time frame, then EFI_TIMEOUT is returned. + @param Status This parameter indicates the USB transfer status. + + @retval EFI_SUCCESS The sync interrupt transfer has been successfully executed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The sync interrupt transfer request failed. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The transfer fails due to timeout. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_SYNC_INTERRUPT_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with an isochronous transfer pipe. An Isochronous + transfer is typically used to transfer streaming data. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + @param Status This parameter indicates the USB transfer status. + + @retval EFI_SUCCESS The isochronous transfer has been successfully executed. + @retval EFI_INVALID_PARAMETER The parameter DeviceEndpoint is not valid. + @retval EFI_DEVICE_ERROR The transfer failed due to the reason other than timeout, The error status + is returned in Status. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The transfer fails due to timeout. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_ISOCHRONOUS_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN UINTN DataLength, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with an isochronous transfer pipe. An Isochronous + transfer is typically used to transfer streaming data. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + This is an optional parameter and may be NULL. + @param IsochronousCallback The IsochronousCallback() function.This function is + called if the requested isochronous transfer is completed. + @param Context Data passed to the IsochronousCallback() function. + + @retval EFI_SUCCESS The asynchronous isochronous transfer has been successfully submitted + to the system. + @retval EFI_INVALID_PARAMETER The parameter DeviceEndpoint is not valid. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_ASYNC_ISOCHRONOUS_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN UINTN DataLength, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context OPTIONAL + ); + +/** + Resets and reconfigures the USB controller. This function will work for all USB devices except + USB Hub Controllers. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The USB controller was reset. + @retval EFI_INVALID_PARAMETER If the controller specified by This is a USB hub. + @retval EFI_DEVICE_ERROR An error occurred during the reconfiguration process. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_PORT_RESET)( + IN EFI_USB_IO_PROTOCOL *This + ); + +/** + Retrieves the USB Device Descriptor. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceDescriptor A pointer to the caller allocated USB Device Descriptor. + + @retval EFI_SUCCESS The device descriptor was retrieved successfully. + @retval EFI_INVALID_PARAMETER DeviceDescriptor is NULL. + @retval EFI_NOT_FOUND The device descriptor was not found. The device may not be configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_DEVICE_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + OUT EFI_USB_DEVICE_DESCRIPTOR *DeviceDescriptor + ); + +/** + Retrieves the USB Device Descriptor. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param ConfigurationDescriptor A pointer to the caller allocated USB Active Configuration + Descriptor. + @retval EFI_SUCCESS The active configuration descriptor was retrieved successfully. + @retval EFI_INVALID_PARAMETER ConfigurationDescriptor is NULL. + @retval EFI_NOT_FOUND An active configuration descriptor cannot be found. The device may not + be configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_CONFIG_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + OUT EFI_USB_CONFIG_DESCRIPTOR *ConfigurationDescriptor + ); + +/** + Retrieves the Interface Descriptor for a USB Device Controller. As stated earlier, an interface + within a USB device is equivalently to a USB Controller within the current configuration. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param InterfaceDescriptor A pointer to the caller allocated USB Interface Descriptor within + the configuration setting. + @retval EFI_SUCCESS The interface descriptor retrieved successfully. + @retval EFI_INVALID_PARAMETER InterfaceDescriptor is NULL. + @retval EFI_NOT_FOUND The interface descriptor cannot be found. The device may not be + correctly configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_INTERFACE_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + OUT EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor + ); + +/** + Retrieves an Endpoint Descriptor within a USB Controller. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param EndpointIndex Indicates which endpoint descriptor to retrieve. + @param EndpointDescriptor A pointer to the caller allocated USB Endpoint Descriptor of + a USB controller. + + @retval EFI_SUCCESS The endpoint descriptor was retrieved successfully. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_NOT_FOUND The endpoint descriptor cannot be found. The device may not be + correctly configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + OUT EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor + ); + +/** + Retrieves a string stored in a USB Device. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param LangID The Language ID for the string being retrieved. + @param StringID The ID of the string being retrieved. + @param String A pointer to a buffer allocated by this function with + AllocatePool() to store the string.If this function + returns EFI_SUCCESS, it stores the string the caller + wants to get. The caller should release the string + buffer with FreePool() after the string is not used any more. + + @retval EFI_SUCCESS The string was retrieved successfully. + @retval EFI_NOT_FOUND The string specified by LangID and StringID was not found. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the return buffer String. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_STRING_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT16 LangID, + IN UINT8 StringID, + OUT CHAR16 **String + ); + +/** + Retrieves all the language ID codes that the USB device supports. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param LangIDTable Language ID for the string the caller wants to get. + This is a 16-bit ID defined by Microsoft. This + buffer pointer is allocated and maintained by + the USB Bus Driver, the caller should not modify + its contents. + @param TableSize The size, in bytes, of the table LangIDTable. + + @retval EFI_SUCCESS The support languages were retrieved successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_SUPPORTED_LANGUAGE)( + IN EFI_USB_IO_PROTOCOL *This, + OUT UINT16 **LangIDTable, + OUT UINT16 *TableSize + ); + +/// +/// The EFI_USB_IO_PROTOCOL provides four basic transfers types described +/// in the USB 1.1 Specification. These include control transfer, interrupt +/// transfer, bulk transfer and isochronous transfer. The EFI_USB_IO_PROTOCOL +/// also provides some basic USB device/controller management and configuration +/// interfaces. A USB device driver uses the services of this protocol to manage USB devices. +/// +struct _EFI_USB_IO_PROTOCOL { + // + // IO transfer + // + EFI_USB_IO_CONTROL_TRANSFER UsbControlTransfer; + EFI_USB_IO_BULK_TRANSFER UsbBulkTransfer; + EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER UsbAsyncInterruptTransfer; + EFI_USB_IO_SYNC_INTERRUPT_TRANSFER UsbSyncInterruptTransfer; + EFI_USB_IO_ISOCHRONOUS_TRANSFER UsbIsochronousTransfer; + EFI_USB_IO_ASYNC_ISOCHRONOUS_TRANSFER UsbAsyncIsochronousTransfer; + + // + // Common device request + // + EFI_USB_IO_GET_DEVICE_DESCRIPTOR UsbGetDeviceDescriptor; + EFI_USB_IO_GET_CONFIG_DESCRIPTOR UsbGetConfigDescriptor; + EFI_USB_IO_GET_INTERFACE_DESCRIPTOR UsbGetInterfaceDescriptor; + EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptor; + EFI_USB_IO_GET_STRING_DESCRIPTOR UsbGetStringDescriptor; + EFI_USB_IO_GET_SUPPORTED_LANGUAGE UsbGetSupportedLanguages; + + // + // Reset controller's parent port + // + EFI_USB_IO_PORT_RESET UsbPortReset; +}; + +extern EFI_GUID gEfiUsbIoProtocolGuid; + +#endif diff --git a/drivers/dw3/XdciCommon.h b/drivers/dw3/XdciCommon.h new file mode 100644 index 0000000..8ef192b --- /dev/null +++ b/drivers/dw3/XdciCommon.h @@ -0,0 +1,168 @@ +/*++ + + Copyright (c) 1999 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + +#ifndef _XDCI_COMMON_H_ +#define _XDCI_COMMON_H_ + +#define USB_DEBUG 0 +#if USB_DEBUG +#define usb_dbg(a,...) {printf(a);printf(__VA_ARGS__);} +#else +#define usb_dbg(...) +#endif + +#define DEBUG_INFO "USB Debug: " +#ifdef DEBUG +#undef DEBUG +#endif +#define DEBUG(a) usb_dbg a + +#define USB_SETUP_DATA_PHASE_DIRECTION_MASK (0x80) + +// +// EP direction +// +typedef enum { + UsbEpDirOut = 0, + UsbEpDirIn = 1 +} USB_EP_DIR; + +// +// USB speeds +// +typedef enum { + USB_SPEED_HIGH = 0, + USB_SPEED_FULL, + USB_SPEED_LOW, + USB_SPEED_SUPER = 4 +} USB_SPEED; + +typedef enum { + USB_ID_DWC_XDCI = 0, + USB_CORE_ID_MAX +} USB_CONTROLLER_ID; + +typedef enum { + USB_ROLE_HOST = 1, + USB_ROLE_DEVICE, + USB_ROLE_OTG +} USB_ROLE; + +typedef enum { + USB_XFER_QUEUED = 0, + USB_XFER_SUCCESSFUL, + USB_XFER_STALL +} USB_XFER_STATUS; + +typedef enum { + USB_DEVICE_DISCONNECT_EVENT = 0, + USB_DEVICE_RESET_EVENT, + USB_DEVICE_CONNECTION_DONE, + USB_DEVICE_STATE_CHANGE_EVENT, + USB_DEVICE_WAKEUP_EVENT, + USB_DEVICE_HIBERNATION_REQ_EVENT, + USB_DEVICE_SOF_EVENT = 7, + USB_DEVICE_ERRATIC_ERR_EVENT = 9, + USB_DEVICE_CMD_CMPLT_EVENT, + USB_DEVICE_BUFF_OVERFLOW_EVENT, + USB_DEVICE_TEST_LMP_RX_EVENT, + USB_DEVICE_SETUP_PKT_RECEIVED, + USB_DEVICE_XFER_NRDY, + USB_DEVICE_XFER_DONE +} USB_DEVICE_EVENT_ID; + +typedef enum { + U0 = 0, + U1, + U2, + U3, + SS_DIS, + RX_DET, + SS_INACT, + POLL, + RECOV, + HRESET, + CMPLY, + LPBK, + RESUME_RESET = 15 +} USB_DEVICE_SS_LINK_STATE; + +typedef enum { + CTRL_SETUP_PHASE, + CTRL_DATA_PHASE, + CTRL_STATUS_PHASE +} USB_CONTROL_XFER_PHASE; + +typedef enum { + USB_EP_STATE_DISABLED = 0, + USB_EP_STATE_ENABLED, + USB_EP_STATE_STALLED, + USB_EP_STATE_SETUP, + USB_EP_STATE_IN_DATA, + USB_EP_STATE_OUT_DATA, + USB_EP_STATE_DATA, + USB_EP_STATE_STATUS +} USB_EP_STATE; + +typedef struct { + VOID *parent_handle; + UINT32 hird; + UINT32 ep_num; + USB_SPEED speed; + USB_EP_STATE ep_state; + USB_EP_DIR ep_dir; + UINT8 ep_type; + USB_DEVICE_SS_LINK_STATE link_state; + UINT8 *buffer; + BOOLEAN ss_event; +} USB_DEVICE_CALLBACK_PARAM; + +// +// USB endpoint +// +typedef struct { + UINT32 ep_num; + USB_EP_DIR ep_dir; + UINT8 ep_type; + UINT32 max_pkt_size; + UINT32 max_streams; + UINT32 burst_size; + UINT32 interval; + UINT32 mult; +} USB_EP_INFO; + +// +// USB transfer request +// +typedef struct _USB_XFER_REQUEST USB_XFER_REQUEST; + +typedef +VOID +(EFIAPI *USB_XFER_DONE_CALLBACK) ( + IN VOID *XdciHndl, + IN USB_XFER_REQUEST *XferReq + ); + +struct _USB_XFER_REQUEST { + VOID *xfer_buffer; // Buffer address. bus-width aligned + UINT32 xfer_len; // Requested transfer length + UINT32 actual_xfer_len; // Actual transfer length at completion callback stage + UINT32 stream_id; // Stream ID. Only relevant for bulk streaming + UINT32 frame_num; // Only relevant for periodic transfer + USB_XFER_STATUS xfer_status; // Transfer status + USB_EP_INFO ep_info; // EP info + USB_XFER_DONE_CALLBACK xfer_done; // Transfer completion callback + BOOLEAN zlp; // Do zero-length transfer +}; + +#endif diff --git a/drivers/dw3/XdciDWC.c b/drivers/dw3/XdciDWC.c new file mode 100644 index 0000000..bfae5b4 --- /dev/null +++ b/drivers/dw3/XdciDWC.c @@ -0,0 +1,3793 @@ +/*++ + + Copyright (c) 1999 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + +#include +#include +#include +#include +#include +#include + +#include "dw3/UsbDeviceModeProtocol.h" +#include "dw3/XdciDWC.h" +#include "dw3/XdciDevice.h" + +UINT32 +usb_reg_read ( + IN UINT32 base, + IN UINT32 offset + ) +{ + volatile UINT32 *addr = (volatile UINT32 *)(UINTN)(base + offset); + return *addr; +} + +VOID +usb_reg_write ( + IN UINT32 base, + IN UINT32 offset, + IN UINT32 val + ) +{ + volatile UINT32 *addr = (volatile UINT32 *)(UINTN)(base + offset); + *addr = val; +} + + +/** + Internal utility function: + This function is used to obtain physical endpoint number + xDCI needs physical endpoint number for EP registers + We also use it to index into our EP array + Note: Certain data structures/commands use logical EP numbers + as opposed to physical endpoint numbers so one should be + careful when interpreting EP numbers + @ep_num: Logical endpoint number + @ep_dir: Direction for the endpoint + +**/ +STATIC +UINT32 +dwc_xdci_get_physical_ep_num ( + IN UINT32 EndpointNum, + IN USB_EP_DIR EndpointDir + ) +{ + return EndpointDir? ((EndpointNum << 1) | EndpointDir) : (EndpointNum << 1); +} + + +/** + Internal utility function: + This function is used to obtain the MPS for control transfers + based on the speed. If this is called before bus reset completes + then it returns MPS based on desired speed. If it is after bus + reset then MPS returned is based on actual negotiated speed + @core_handle: xDCI controller handle address + @mps: address of 32-bit variable to return the MPS + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_get_ctrl_mps ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 *mps + ) +{ + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_get_ctrl_mps: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (mps == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_get_ctrl_mps: INVALID parameter\n")); + return EFI_INVALID_PARAMETER; + } + + switch (core_handle->actual_speed) { + case USB_SPEED_HIGH: + *mps = DWC_XDCI_HS_CTRL_EP_MPS; + break; + case USB_SPEED_FULL: + *mps = DWC_XDCI_FS_CTRL_EP_MPS; + break; + case USB_SPEED_LOW: + *mps = DWC_XDCI_LS_CTRL_EP_MPS; + break; + case USB_SPEED_SUPER: + *mps = DWC_XDCI_SS_CTRL_EP_MPS; + break; + default: + *mps = 0; + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_get_ctrl_mps: UNKNOWN speed\n")); + break; + } + + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to initialize the parameters required + for executing endpoint command + @core_handle: xDCI controller handle address + @ep_info: EP info address + @config_action: Configuration action specific to EP command + @ep_cmd: xDCI EP command for which parameters are initialized + @ep_cmd_params: address of struct to return EP params + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_init_ep_cmd_params ( + IN XDCI_CORE_HANDLE *core_handle, + IN USB_EP_INFO *ep_info, + IN UINT32 config_action, + IN DWC_XDCI_ENDPOINT_CMD ep_cmd, + IN DWC_XDCI_ENDPOINT_CMD_PARAMS *ep_cmd_params + ) +{ + EFI_STATUS status = EFI_SUCCESS; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_init_ep_cmd_params: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Reset params */ + ep_cmd_params->param0 = ep_cmd_params->param1 = ep_cmd_params->param2 = 0; + + switch (ep_cmd) { + case EPCMD_SET_EP_CONFIG: + /* Issue DEPCFG command for EP */ + /* Issue a DEPCFG (Command 1) command for endpoint */ + + if (ep_info->max_streams) { + ep_cmd_params->param1 = DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK; + } + + if (ep_info->interval) { + ep_cmd_params->param1 |= ((ep_info->interval-1) << DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS); + } + + /* Set EP num */ + ep_cmd_params->param1 |= (ep_info->ep_num << DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_BIT_POS); + + /* Set EP direction */ + ep_cmd_params->param1 |= (ep_info->ep_dir << DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_BIT_POS); + + /* Set EP-specific Event enable for not ready and + * complete events + */ + ep_cmd_params->param1 &= ~DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK; + + /* Setup the events we want enabled for this EP */ + ep_cmd_params->param1 |= (DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK | + DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK | + DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK); + + /* We only have one interrupt line for this core. + * Set interrupt number to 0 + */ + ep_cmd_params->param1 &= ~DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK; + + /* Set FIFOnum = 0 for control EP0 */ + ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_MASK; + + /* Program FIFOnum for non-EP0 EPs */ + if (ep_info->ep_num && ep_info->ep_dir) { + ep_cmd_params->param0 |= (ep_info->ep_num << DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_BIT_POS); + } + + /* Program max packet size */ + ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_MPS_MASK; + ep_cmd_params->param0 |= (ep_info->max_pkt_size << DWC_XDCI_PARAM0_SET_EP_CFG_MPS_BIT_POS); + + /* Set Burst size. 0 means burst size of 1 */ + ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_MASK; + ep_cmd_params->param0 |= (ep_info->burst_size << DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_BIT_POS); + + /* Set EP type */ + ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_MASK; + ep_cmd_params->param0 |= (ep_info->ep_type << DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_BIT_POS); + + /* Set config action */ + ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MASK; + ep_cmd_params->param0 |= (config_action << DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_BIT_POS); + break; + + case EPCMD_SET_EP_XFER_RES_CONFIG: + /* Set param0 to 1. Same for all EPs when resource + * configuration is done + */ + ep_cmd_params->param0 = 1; + break; + + case EPCMD_END_XFER: + /* Nothing to set. Already reset params for all cmds */ + break; + + case EPCMD_START_NEW_CONFIG: + /* Nothing to set. Already reset params for all cmds */ + break; + + default: + status = EFI_INVALID_PARAMETER; + DEBUG ((DEBUG_INFO, "\ndwc_xdci_core_init_ep_cmd_params: INVALID Parameter")); + break; + } + + return status; +} + + +/** + Internal utility function: + This function is used to issue the xDCI endpoint command + @core_handle: xDCI controller handle address + @ep_num: Physical EP num + @ep_cmd: xDCI EP command + @ep_cmd_params: EP command parameters address + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_issue_ep_cmd ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 ep_num, + IN UINT32 ep_cmd, + IN DWC_XDCI_ENDPOINT_CMD_PARAMS *ep_cmd_params + ) +{ + UINT32 base_addr; + UINT32 max_delay_iter = 5000;//DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_issue_ep_cmd: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = core_handle->base_address; + + /* Set EP command parameter values */ + usb_reg_write ( + base_addr, + DWC_XDCI_EPCMD_PARAM2_REG(ep_num), + ep_cmd_params->param2 + ); + + usb_reg_write ( + base_addr, + DWC_XDCI_EPCMD_PARAM1_REG(ep_num), + ep_cmd_params->param1 + ); + + usb_reg_write ( + base_addr, + DWC_XDCI_EPCMD_PARAM0_REG(ep_num), + ep_cmd_params->param0 + ); + + /* Set the command code and activate it */ + usb_reg_write ( + base_addr, + DWC_XDCI_EPCMD_REG(ep_num), + ep_cmd | DWC_XDCI_EPCMD_CMD_ACTIVE_MASK + ); + + /* Wait until command completes */ + do { + if (!(usb_reg_read (base_addr, DWC_XDCI_EPCMD_REG(ep_num)) & DWC_XDCI_EPCMD_CMD_ACTIVE_MASK)) + break; + else + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_issue_ep_cmd. ERROR: Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to flush all FIFOs + @core_handle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_flush_all_fifos ( + IN XDCI_CORE_HANDLE *core_handle + ) +{ + UINT32 base_addr; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_flush_all_fifos: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = core_handle->base_address; + + /* Write the command to flush all FIFOs */ + usb_reg_write( + base_addr, + DWC_XDCI_DGCMD_REG, + (usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_ALL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + /* Wait until command completes */ + do { + if (!(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to flush Tx FIFO specific to an endpoint + @core_handle: xDCI controller handle address + @ep_num: Physical EP num + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_flush_ep_tx_fifo ( + IN XDCI_CORE_HANDLE *core_handle, + __attribute__((__unused__)) IN UINT32 ep_num + ) +{ + UINT32 base_addr; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + //UINT32 fifo_num; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_flush_ep_tx_fifo: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = core_handle->base_address; + + /* Translate to FIFOnum + * NOTE: Assuming this is a Tx EP + */ + //fifo_num = (ep_num >> 1); + + /* TODO: Currently we are only using TxFIFO 0. Later map these + * Write the FIFO num/dir param for the generic command. + */ + usb_reg_write ( + base_addr, + DWC_XDCI_DGCMD_PARAM_REG, + ((usb_reg_read (base_addr, DWC_XDCI_DGCMD_PARAM_REG) & ~DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK) | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK) + ); + + /* Write the command to flush all FIFOs */ + usb_reg_write ( + base_addr, + DWC_XDCI_DGCMD_REG, + (usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + + /* Wait until command completes */ + do { + if (!(usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + + +STATIC +EFI_STATUS +dwc_xdci_core_prepare_one_trb ( + IN DWC_XDCI_TRB *trb, + IN DWC_XDCI_TRB_CONTROL trb_ctrl, + IN UINT32 LastBit, + IN UINT32 ChainBit, + IN UINT8 *buffer_ptr, + IN UINT32 size + ) +{ + DEBUG ((DEBUG_INFO, "trb is 0x%x, buffer_ptr is 0x%x, size is 0x%x\n", (unsigned) trb, (unsigned) buffer_ptr, size)); + + trb->buff_ptr_low = (UINT32)(UINTN)buffer_ptr; + trb->buff_ptr_high = 0; + trb->len_xfer_params = size; + trb->trb_ctrl = trb_ctrl << DWC_XDCI_TRB_CTRL_TYPE_BIT_POS; + + if (ChainBit) + trb->trb_ctrl |= ChainBit << DWC_XDCI_TRB_CTRL_CHAIN_BUFF_BIT_POS; + + if (LastBit) + trb->trb_ctrl |= LastBit << DWC_XDCI_TRB_CTRL_LST_TRB_BIT_POS; + + trb->trb_ctrl |= DWC_XDCI_TRB_CTRL_IOSP_MISOCH_MASK| DWC_XDCI_TRB_CTRL_HWO_MASK; + + DEBUG ((DEBUG_INFO, "(dwc_xdci_core_prepare_one_trb) trb->buff_ptr_low = 0x%x, trb->len_xfer_params is 0x%x, trb->trb_ctrl is 0x%x\n", + trb->buff_ptr_low, trb->len_xfer_params, trb->trb_ctrl)); + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to initialize transfer request block + @core_handle: xDCI controller handle address + @trb: Address of TRB to initialize + @trb_ctrl: TRB control value + @buff_ptr: Transfer buffer address + @size: Size of the transfer + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_init_trb ( + IN XDCI_CORE_HANDLE *core_handle, + IN DWC_XDCI_TRB *trb, + IN DWC_XDCI_TRB_CONTROL trb_ctrl, + IN UINT8 *buffer_ptr, + IN UINT32 size + ) +{ +#define ONE_TRB_SIZE (DWC_XDCI_TRB_BUFF_SIZE_MASK & 0x00F00000) + UINT8 *TrbBuffer; + UINT32 TrbCtrlLast; + UINT32 TrbCtrlChain; + UINT32 TrbIndex; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_init_trb: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (trb == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_init_trb: INVALID handle\n")); + return EFI_INVALID_PARAMETER; + } + + /* Init TRB fields + * NOTE: Assuming we are only using 32-bit addresses + * TODO: update for 64-bit addresses + */ + if (size <= DWC_XDCI_TRB_BUFF_SIZE_MASK) { + // + // Can transfer in one TRB + // + TrbCtrlChain = 0; + TrbCtrlLast = 1; + dwc_xdci_core_prepare_one_trb (trb, trb_ctrl, TrbCtrlLast, TrbCtrlChain, buffer_ptr, size); + return EFI_SUCCESS; + } + + // + // Can't transfer in one TRB. + // Seperate it in every ONE_TRB_SIZE of TRB + // + TrbBuffer = buffer_ptr; + TrbIndex = 0; + while (size > ONE_TRB_SIZE) { + TrbCtrlChain = 1; + TrbCtrlLast = 0; + dwc_xdci_core_prepare_one_trb (trb, trb_ctrl, TrbCtrlLast, TrbCtrlChain, TrbBuffer, ONE_TRB_SIZE); + TrbBuffer += ONE_TRB_SIZE; + size -= ONE_TRB_SIZE; + trb++; + TrbIndex++; + if (TrbIndex >= DWC_XDCI_TRB_NUM) + return EFI_OUT_OF_RESOURCES; + } + TrbCtrlChain = 0; + TrbCtrlLast = 1; + dwc_xdci_core_prepare_one_trb (trb, trb_ctrl, TrbCtrlLast, TrbCtrlChain, TrbBuffer, size); + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to start a SETUP phase on control endpoint + @core_handle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +dwc_xdci_core_start_ep0_setup_xfer ( + IN XDCI_CORE_HANDLE *core_handle + ) +{ + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS status = EFI_DEVICE_ERROR; + DWC_XDCI_TRB *trb; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_start_ep0_setup_xfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (core_handle->ep_handles[0].state == USB_EP_STATE_SETUP) { + DEBUG ((DEBUG_INFO, "EP0 was already in SETUP phase\n")); + return EFI_SUCCESS; + } + + core_handle->ep_handles[0].state = USB_EP_STATE_SETUP; + trb = core_handle->trbs; + DEBUG ((DEBUG_INFO, "(dwc_xdci_core_start_ep0_setup_xfer)\n")); + + status = dwc_xdci_core_init_trb ( + core_handle, + trb, + TRBCTL_SETUP, + core_handle->aligned_setup_buffer, + 8 + ); + + if (status) + return status; + + // + // Issue a DEPSTRTXFER for EP0 + // Reset params + // + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + // + // Init the lower re-bits for TRB address + // + ep_cmd_params.param1 = (UINT32)(UINTN)trb; + + // + // Issue the command to start transfer on physical + // endpoint 0 + // + status = dwc_xdci_core_issue_ep_cmd ( + core_handle, + 0, + EPCMD_START_XFER, + &ep_cmd_params + ); + + /* Save new resource index for this transfer */ + core_handle->ep_handles[0].currentXferRscIdx = ((usb_reg_read ( + core_handle->base_address, + DWC_XDCI_EPCMD_REG(0)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS + ); + + return status; +} + + +/** + Internal function: + This function is used to process the state change event + @core_handle: xDCI controller handle address + @event: device event dword + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_device_state_change_event ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 event + ) +{ + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_device_state_change_event: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + core_handle->hird_val = (event & DWC_XDCI_EVENT_BUFF_DEV_HIRD_MASK) >> DWC_XDCI_EVENT_BUFF_DEV_HIRD_BIT_POS; + + core_handle->link_state = ((event & DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_MASK) >> DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_BIT_POS); + + if (core_handle->event_callbacks.dev_link_state_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.link_state = core_handle->link_state; + core_handle->event_callbacks.cb_event_params.hird = core_handle->hird_val; + core_handle->event_callbacks.cb_event_params.ss_event = (event & DWC_XDCI_EVENT_BUFF_DEV_SS_EVENT_MASK) ? 1 : 0; + core_handle->event_callbacks.dev_link_state_callback (&core_handle->event_callbacks.cb_event_params); + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to issue a command to end transfer + @core_handle: xDCI controller handle address + @ep_num: Physical EP num for which transfer is to be ended + +**/ +STATIC +EFI_STATUS +dwc_xdci_end_xfer ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 ep_num + ) +{ + EFI_STATUS status; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + UINT32 cmd_params; + DWC_XDCI_TRB *TrbPtr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_end_xfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + core_handle->ep_handles[ep_num].CheckFlag = FALSE; + + /* Issue a DEPENDXFER for EP */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + cmd_params = ((core_handle->ep_handles[ep_num].currentXferRscIdx << DWC_XDCI_EPCMD_RES_IDX_BIT_POS) | DWC_XDCI_EPCMD_FORCE_RM_MASK); + + if (core_handle->ep_handles[ep_num].currentXferRscIdx == 0) { + return EFI_SUCCESS; + } + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd( + core_handle, + ep_num, + cmd_params | DWC_XDCI_EPCMD_END_XFER, + &ep_cmd_params + ); + + if (!status) { + core_handle->ep_handles[ep_num].currentXferRscIdx = 0; + TrbPtr = core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM); + memset (TrbPtr, 0, DWC_XDCI_TRB_NUM * sizeof (DWC_XDCI_TRB)); + } + + return status; +} + + +/** + Internal function: + This function is used to process bus reset detection event + @core_handle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_device_reset_det ( + IN XDCI_CORE_HANDLE *core_handle + ) +{ + EFI_STATUS status = EFI_SUCCESS; + + if (core_handle == NULL) { + return EFI_DEVICE_ERROR; + } + + /* Flush all FIFOs */ + status = dwc_xdci_core_flush_all_fifos(core_handle); + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_reset_det: Failed to flush FIFOs\n")); + } + + /* NOTE: Not treating flush FIFOs status to be fatal */ + + /* Start SETUP phase on EP0 */ + status = dwc_xdci_core_start_ep0_setup_xfer(core_handle); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_reset_det: Failed to start SETUP phase for EP0\n")); + return status; + } + + /* Notify upper layer if a callback is registerd for + * this event + */ + if (core_handle->event_callbacks.dev_bus_reset_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + status = core_handle->event_callbacks.dev_bus_reset_callback (&core_handle->event_callbacks.cb_event_params); + } + + return status; +} + + +/** + Internal function: + This function is used to process connection done (means reset + complete) event + @core_handle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_device_reset_done ( + IN XDCI_CORE_HANDLE *core_handle + ) +{ + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + UINT32 base_addr; + EFI_STATUS status = EFI_SUCCESS; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_device_reset_done: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = core_handle->base_address; + core_handle->actual_speed = (usb_reg_read (base_addr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_CONN_SPEED_MASK); + DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_reset_done core_handle->actual_speed is %x\n", core_handle->actual_speed)); + + /* Program MPS based on the negotiated speed */ + dwc_xdci_core_get_ctrl_mps (core_handle, &core_handle->ep_handles[0].ep_info.max_pkt_size); + dwc_xdci_core_get_ctrl_mps (core_handle, &core_handle->ep_handles[1].ep_info.max_pkt_size); + + /* Init DEPCFG cmd params for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + core_handle, + &core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + core_handle, + 0, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + return status; + } + + /* Init DEPCFG cmd params for EP1 */ + status = dwc_xdci_core_init_ep_cmd_params ( + core_handle, + &core_handle->ep_handles[1].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + core_handle, + 1, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + /* Put the other PHY into suspend */ + if (core_handle->actual_speed == USB_SPEED_SUPER) { + /* Put HS PHY to suspend */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) | DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + + /* Clear SS PHY's suspend mask */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB3PIPECTL_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + } else { + /* Put SS PHY to suspend */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB3PIPECTL_REG(0), + (usb_reg_read(base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) | DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + /* Clear HS PHY's suspend mask */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB2PHYCFG_REG(0), + (usb_reg_read(base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + } + + /* Notify upper layer if callback is registered */ + if (core_handle->event_callbacks.dev_reset_done_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.speed = core_handle->actual_speed; + core_handle->event_callbacks.dev_reset_done_callback (&core_handle->event_callbacks.cb_event_params); + } + + return status; +} + + +/** + Internal function: + This function is used to process device event + @core_handle: xDCI controller handle address + @int_line_event_buffer: event buffer pointing to device event + @processed_event_size: address of variable to save the size of + the event that was processed + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_device_event ( + IN XDCI_CORE_HANDLE *core_handle, + IN DWC_XDCI_EVENT_BUFFER *int_line_event_buffer, + IN UINT32 *processed_event_size + ) +{ + UINT32 event; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_device_event: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Extract device event */ + event = (int_line_event_buffer->event & DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK); + event >>= DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS; + + /* Assume default event size. Change it in switch case if + * different + */ + *processed_event_size = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; + + switch (event) { + case DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT\n")); + dwc_xdci_process_device_reset_det (core_handle); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT\n")); + dwc_xdci_process_device_reset_done (core_handle); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT\n")); + dwc_xdci_process_device_state_change_event (core_handle, int_line_event_buffer->event); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT\n")); + *processed_event_size = DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES; + break; + + default: + DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_event: UNHANDLED device event: %x\n", event)); + break; + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process EP not ready for + non-control endpoints + @core_handle: xDCI controller handle address + @ep_num: Physical endpoint number + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_ep_xfer_not_ready ( + __attribute__((__unused__)) IN XDCI_CORE_HANDLE *core_handle, + __attribute__((__unused__)) IN UINT32 ep_num + ) +{ + /* TODO: Not doing on-demand transfers + * Revisit if required for later use + */ + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process EP not ready for + control endpoints + @core_handle: xDCI controller handle address + @ep_num: Physical endpoint number + @data_stage: EP not ready when data stage token was received + @status_stage: EP not ready when status stage token was received + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_ep0_xfer_not_ready ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 ep_num, + IN UINT32 ep_event_status + ) +{ + USB_EP_STATE ep_state = USB_EP_STATE_SETUP; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep0_xfer_not_ready: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + /* Is it data stage or status stage */ + if (ep_event_status & DWC_XDCI_EVENT_BUFF_EP_CTRL_DATA_REQ_MASK) { + ep_state = USB_EP_STATE_DATA; + } else if (ep_event_status & DWC_XDCI_EVENT_BUFF_EP_CTRL_STATUS_REQ_MASK) { + ep_state = USB_EP_STATE_STATUS; + } + + if ((ep_num == 0) && (ep_state == USB_EP_STATE_STATUS)) { + if (ep_event_status & DWC_XDCI_EVENT_BUFF_EP_XFER_ACTIVE_MASK) { + DEBUG ((DEBUG_INFO, "XFER_ACTIVE\n")); + } else { + DEBUG ((DEBUG_INFO, "XFER_NOT_ACTIVE\n")); + } + dwc_xdci_ep0_receive_status_pkt (core_handle); + } + + /* Notify upper layer if a callback is registered for + * this event + */ + if (core_handle->event_callbacks.dev_xfer_nrdy_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.ep_state = ep_state; + core_handle->event_callbacks.dev_xfer_nrdy_callback (&core_handle->event_callbacks.cb_event_params); + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process transfer phone done for EP0 + @core_handle: xDCI controller handle address + @ep_num: Physical endpoint number (0 for OUT and 1 for IN) + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_ep0_xfer_phase_done ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 ep_num + ) +{ + DWC_XDCI_ENDPOINT *ep_handle; + DWC_XDCI_TRB *trb; + EFI_STATUS status = EFI_SUCCESS; + UINT32 trb_sts; + UINT32 trb_ctrl; + UINT32 trb_bufsize; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep0_xfer_phase_done: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + ep_handle = &core_handle->ep_handles[ep_num]; + trb = core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM); + DEBUG ((DEBUG_INFO, "(dwc_xdci_process_ep0_xfer_phase_done)ep_num is %d\n", ep_num)); + + if (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep0_xfer_phase_done. HW owns TRB: %x!!!\n", (UINT32)(UINTN)trb)); + } + + ep_handle->currentXferRscIdx = 0; + ep_handle->state = USB_EP_STATE_ENABLED; + trb_ctrl = (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_TYPE_MASK) >> DWC_XDCI_TRB_CTRL_TYPE_BIT_POS; + trb_sts = (trb->len_xfer_params & DWC_XDCI_TRB_STATUS_MASK) >> DWC_XDCI_TRB_STATUS_BIT_POS; + trb_bufsize = trb->len_xfer_params & DWC_XDCI_TRB_BUFF_SIZE_MASK; + + switch (trb_ctrl) { + case DWC_XDCI_TRB_CTRL_TYPE_SETUP: + DEBUG ((DEBUG_INFO, "SETUP\n")); + DEBUG ((DEBUG_INFO, "param buff: 0x%x\n", (unsigned) core_handle->aligned_setup_buffer)); + + if (core_handle->event_callbacks.dev_setup_pkt_received_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.buffer = core_handle->aligned_setup_buffer; + status = core_handle->event_callbacks.dev_setup_pkt_received_callback (&core_handle->event_callbacks.cb_event_params); + } + + if (!(core_handle->aligned_setup_buffer[0] & USB_SETUP_DATA_PHASE_DIRECTION_MASK)) { + /* Keep a buffer ready for setup phase */ + dwc_xdci_core_start_ep0_setup_xfer (core_handle); + } + + break; + + case DWC_XDCI_TRB_CTRL_TYPE_STATUS2: + DEBUG ((DEBUG_INFO, "STATUS2\n")); + break; + + case DWC_XDCI_TRB_CTRL_TYPE_STATUS3: + DEBUG ((DEBUG_INFO, "STATUS3\n")); + /* Notify upper layer of control transfer completion + * if a callback function was registerd + */ + if (core_handle->event_callbacks.dev_xfer_done_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.ep_num = (ep_num >> 1); + core_handle->event_callbacks.cb_event_params.ep_dir = (ep_num & 1); + core_handle->event_callbacks.cb_event_params.buffer = (UINT8 *)(UINTN)(trb->buff_ptr_low); + core_handle->event_callbacks.dev_xfer_done_callback (&core_handle->event_callbacks.cb_event_params); + } + + /* Status phase done. Queue next SETUP packet */ + status = dwc_xdci_core_start_ep0_setup_xfer(core_handle); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep0_xfer_phase_done: FAILED to queue SETUP\n")); + } + break; + + case DWC_XDCI_TRB_CTRL_TYPE_DATA: + DEBUG ((DEBUG_INFO, "DATA\n")); + if (trb_sts == DWC_XDCI_TRB_STATUS_SETUP_PENDING || trb_bufsize != 0) { + DEBUG ((DEBUG_INFO, "ERROR: Control transfert aborted by host: Setup pending\n")); + dwc_xdci_core_start_ep0_setup_xfer (core_handle); + } + + if (core_handle->event_callbacks.dev_xfer_done_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.ep_num = (ep_num >> 1); + core_handle->event_callbacks.cb_event_params.ep_dir = (ep_num & 1); + core_handle->event_callbacks.cb_event_params.buffer = (UINT8 *)(UINTN)(trb->buff_ptr_low); + core_handle->event_callbacks.dev_xfer_done_callback (&core_handle->event_callbacks.cb_event_params); + } + break; + + default: + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep0_xfer_phase_done: UNHANDLED STATE in TRB\n")); + break; + } + + return status; +} + + +/** + Internal function: + This function is used to process transfer done for + non-control endpoints + @core_handle: xDCI controller handle address + @ep_num: Physical endpoint number + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_ep_xfer_done ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 ep_num + ) +{ + DWC_XDCI_ENDPOINT *ep_handle; + DWC_XDCI_TRB *trb; + USB_XFER_REQUEST *xfer_req; + UINT32 remaining_len; + + if (ep_num > DWC_XDCI_MAX_ENDPOINTS) { + ep_num = DWC_XDCI_MAX_ENDPOINTS; + } + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_xfer_done: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + ep_handle = &core_handle->ep_handles[ep_num]; + ep_handle->currentXferRscIdx = 0; + trb = ep_handle->trb; + xfer_req = &ep_handle->xfer_handle; + + // + // if transfer done, set CheckFlag to FALSE for allow next transfer request. + // + ep_handle->CheckFlag = FALSE; + + if ((trb == NULL) || (xfer_req == NULL)) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_xfer_done: INVALID parameter\n")); + return EFI_INVALID_PARAMETER; + } + + /* Compute the actual transfer length */ + xfer_req->actual_xfer_len = xfer_req->xfer_len; + remaining_len = (trb->len_xfer_params & DWC_XDCI_TRB_BUFF_SIZE_MASK); + + if (remaining_len > xfer_req->xfer_len) { + /* Buffer overrun? This should never happen */ + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_xfer_done: Possible buffer overrun\n")); + } else { + xfer_req->actual_xfer_len -= remaining_len; + } + + /* Notify upper layer of request-specific transfer completion + * if there is a callback specifically for this request + */ + if (xfer_req->xfer_done) { + xfer_req->xfer_done(core_handle->parent_handle, xfer_req); + } + + /* Notify upper layer if a callback was registered */ + if (core_handle->event_callbacks.dev_xfer_done_callback) { + core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle; + core_handle->event_callbacks.cb_event_params.ep_num = (ep_num >> 1); + core_handle->event_callbacks.cb_event_params.ep_dir = (ep_num & 1); + core_handle->event_callbacks.cb_event_params.ep_type = ep_handle->ep_info.ep_type; + core_handle->event_callbacks.cb_event_params.buffer = (UINT8 *)(UINTN)(ep_handle->trb->buff_ptr_low); + core_handle->event_callbacks.dev_xfer_done_callback (&core_handle->event_callbacks.cb_event_params); + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process endpoint events + @core_handle: xDCI controller handle address + @int_line_event_buffer: address of buffer containing event + to process + @processed_event_size: address to save the size of event + processed + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_ep_event ( + IN XDCI_CORE_HANDLE *core_handle, + IN DWC_XDCI_EVENT_BUFFER *int_line_event_buffer, + IN UINT32 *processed_event_size + ) +{ + UINT32 ep_num; + UINT32 ep_event; + UINT32 ep_event_status; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_event: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + ep_event = int_line_event_buffer->event; + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep_event event: 0x%x\n", int_line_event_buffer->event)); + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep_event lmp1: 0x%x\n", int_line_event_buffer->dev_tst_lmp1)); + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep_event lmp2: 0x%x\n", int_line_event_buffer->dev_tst_lmp2)); + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep_event reserved: 0x%x\n", int_line_event_buffer->reserved)); + + *processed_event_size = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; + + /* Get EP num */ + ep_num = ((ep_event & DWC_XDCI_EVENT_BUFF_EP_NUM_MASK) >> DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS); + ep_event_status = (ep_event & DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK); + + /* Interpret event and handle transfer completion here */ + ep_event = ((ep_event & DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK) >> DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS); + + switch (ep_event) { + case DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT: + DEBUG ((DEBUG_INFO, "XFER_CMPLT ep %d\n", ep_num)); + if (ep_num > 1) { + dwc_xdci_process_ep_xfer_done (core_handle, ep_num); + } else { + dwc_xdci_process_ep0_xfer_phase_done (core_handle, ep_num); + } + break; + + case DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS: + DEBUG ((DEBUG_INFO, "IN_PROGRESS\n")); + break; + + case DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY: + DEBUG ((DEBUG_INFO, "NOT_READY ep %d\n", ep_num)); + if (ep_num > 1) { + /* Endpoint transfer is not ready */ + dwc_xdci_process_ep_xfer_not_ready (core_handle, ep_num); + } else { + dwc_xdci_process_ep0_xfer_not_ready (core_handle, ep_num, ep_event_status); + } + break; + + default: + DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep_event: UNKNOWN EP event 0x%x\n", ep_event)); + break; + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process events on single interrupt line + @core_handle: xDCI controller handle address + @event_count: event bytes to process + @processed_event_count: address to save the size + (in bytes) of event processed + processed + +**/ +STATIC +EFI_STATUS +dwc_xdci_process_interrupt_line_events ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 event_count, + IN UINT32 *processed_event_count + ) +{ + UINT32 processed_event_size = 0; + UINT32 current_event_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_interrupt_line_events: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (core_handle->current_event_buffer == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_interrupt_line_events: INVALID event buffer\n")); + return EFI_INVALID_PARAMETER; + } + + current_event_addr = (UINT32)(UINTN)(core_handle->current_event_buffer); + + /* Process event_count/event_size number of events + * in this run + */ + while (event_count) { + if (core_handle->current_event_buffer->event & DWC_XDCI_EVENT_DEV_MASK) { + dwc_xdci_process_device_event ( + core_handle, + core_handle->current_event_buffer, + &processed_event_size + ); + } else { + dwc_xdci_process_ep_event ( + core_handle, + core_handle->current_event_buffer, + &processed_event_size); + } + + event_count -= processed_event_size; + *processed_event_count += processed_event_size; + if ((current_event_addr + processed_event_size) >= + ((UINT32)(UINTN)(core_handle->aligned_event_buffers) + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)) + ) { + current_event_addr = (UINT32)(UINTN)(core_handle->aligned_event_buffers); + DEBUG ((DEBUG_INFO, "dwc_xdci_process_interrupt_line_events: Event buffer bound reached\n")); + } else { + current_event_addr += processed_event_size; + } + + core_handle->current_event_buffer = (DWC_XDCI_EVENT_BUFFER *)(UINTN)current_event_addr; + } + + return EFI_SUCCESS; +} + + +/* DWC XDCI APIs */ + +/** + Interface: + + This function is used to initialize the xDCI core + @config_params: Parameters from app to configure the core + @device_core_ptr: HW-independent APIs handle for device core + @core_handle: xDCI controller handle retured + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_init ( + IN USB_DEV_CONFIG_PARAMS *ConfigParams, + IN VOID *device_core_ptr, + IN VOID **core_handle + ) +{ + EFI_STATUS status = EFI_DEVICE_ERROR; + UINT32 base_addr; + XDCI_CORE_HANDLE *local_core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT8 i; + + if (core_handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + local_core_handle = (XDCI_CORE_HANDLE *)calloc (1, sizeof(XDCI_CORE_HANDLE)); + + if (local_core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to allocate handle for xDCI\n")); + return EFI_OUT_OF_RESOURCES; + } + + memset (local_core_handle, 0, sizeof(XDCI_CORE_HANDLE)); + + local_core_handle->parent_handle = device_core_ptr; + + *core_handle = (VOID *)local_core_handle; + + local_core_handle->id = ConfigParams->ControllerId; + local_core_handle->base_address = base_addr = ConfigParams->BaseAddress; + local_core_handle->flags = ConfigParams->Flags; + local_core_handle->desired_speed = local_core_handle->actual_speed = ConfigParams->Speed; + local_core_handle->role = ConfigParams->Role; + + DEBUG ((DEBUG_INFO, "Resetting the USB core\n")); + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_CSFTRST_MASK + ); + + /* Wait until core soft reset completes */ + do { + if (!(usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & DWC_XDCI_DCTL_CSFTRST_MASK)) { + break; + } else { + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to reset device controller\n")); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "USB core has been reset\n")); + + /* All FIFOs are flushed at this point */ + + /* Ensure we have EP0 Rx/Tx handles initialized */ + local_core_handle->ep_handles[0].ep_info.ep_num = 0; + local_core_handle->ep_handles[0].ep_info.ep_dir = UsbEpDirOut; + local_core_handle->ep_handles[0].ep_info.ep_type = USB_ENDPOINT_CONTROL; + local_core_handle->ep_handles[0].ep_info.max_pkt_size = DWC_XDCI_SS_CTRL_EP_MPS; + /* 0 means burst size of 1 */ + local_core_handle->ep_handles[0].ep_info.burst_size = 0; + + local_core_handle->ep_handles[1].ep_info.ep_num = 0; + local_core_handle->ep_handles[1].ep_info.ep_dir = UsbEpDirIn; + local_core_handle->ep_handles[1].ep_info.ep_type = USB_ENDPOINT_CONTROL; + local_core_handle->ep_handles[1].ep_info.max_pkt_size = DWC_XDCI_SS_CTRL_EP_MPS; + /* 0 means burst size of 1 */ + local_core_handle->ep_handles[1].ep_info.burst_size = 0; + + local_core_handle->dev_state = UsbDevStateDefault; + + /* Clear KeepConnect bit so we can allow disconnect and + * re-connect. Stay in RX_DETECT state + */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & + (~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & + ((~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK) | (DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS)) + ); + + DEBUG ((DEBUG_INFO, "Device controller Synopsys ID: %x\n", usb_reg_read (base_addr, DWC_XDCI_GSNPSID_REG))); + DEBUG ((DEBUG_INFO, "Default value of xDCI GSBUSCFG0 and GSBUSCFG1: %x, %x\n", + usb_reg_read (base_addr, DWC_XDCI_GSBUSCFG0_REG), + usb_reg_read (base_addr, DWC_XDCI_GSBUSCFG1_REG))); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GTXTHRCFG and GRXTHRCFG: %x, %x\n", + usb_reg_read (base_addr, DWC_XDCI_GTXTHRCFG_REG), + usb_reg_read (base_addr, DWC_XDCI_GRXTHRCFG_REG))); + + /* Clear ULPI auto-resume bit */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG (0)) & ~DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GUSB2PHYCFG and GUSB3PIPECTL: %x, %x\n", + usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG (0)), + usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG (0)))); + + /* Only one RxFIFO */ + DEBUG ((DEBUG_INFO, "Default value of DWC_XDCI_GRXFIFOSIZ: %x\n", + usb_reg_read (base_addr, DWC_XDCI_GRXFIFOSIZ_REG (0)))); + + for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++) { + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_GTXFIFOSIZ %d: %x\n", + i, usb_reg_read (base_addr, DWC_XDCI_GTXFIFOSIZ_REG (i)))); + } + + /* TODO: Need to check if TxFIFO should start where RxFIFO ends + * or default is correct i.e. TxFIFO starts at 0 just like RxFIFO + */ + + /* Allocate and Initialize Event Buffers */ + local_core_handle->max_dev_int_lines = ((usb_reg_read (base_addr, DWC_XDCI_GHWPARAMS1_REG) & + DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >> + DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS); + + DEBUG ((DEBUG_INFO, "Max dev int lines: %d\n", local_core_handle->max_dev_int_lines)); + + /* One event buffer per interrupt line. + * Need to align it to size of event buffer + * Buffer needs to be big enough. Otherwise the core + * won't operate + */ + local_core_handle->aligned_event_buffers = (DWC_XDCI_EVENT_BUFFER *) + ((UINT32)(UINTN)(local_core_handle->event_buffers) + + ((sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) - + (((UINT32)(UINTN)(local_core_handle->event_buffers)) % + (sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)))); + + for (i = 0; i < local_core_handle->max_dev_int_lines; i++) { + usb_reg_write ( + base_addr, + DWC_XDCI_GEVNTADR_REG (i), + (UINT32)(UINTN)(local_core_handle->aligned_event_buffers + i * sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) + ); + + // + // Clear High 32bit address register, GEVNTADR register is 64-bit register + // default is 0xffffffffffffffff + // + usb_reg_write (base_addr, DWC_XDCI_GEVNTADR_REG (i) + 4, 0x00000000); + + local_core_handle->current_event_buffer = local_core_handle->aligned_event_buffers; + /* Write size and clear the mask */ + usb_reg_write ( + base_addr, + DWC_XDCI_EVNTSIZ_REG (i), + sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER + ); + + /* Write 0 to the event count register as the last step + * for event configuration + */ + usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (i), 0); + + DEBUG ((DEBUG_INFO, "Value of xDCI Event buffer %d: %x%x, Size: %x, Count: %x\n", + i, + usb_reg_read (base_addr, DWC_XDCI_GEVNTADR_REG (i) + 4), + usb_reg_read (base_addr, DWC_XDCI_GEVNTADR_REG (i)), + usb_reg_read (base_addr, DWC_XDCI_EVNTSIZ_REG (i)), + usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (i)))); + } + +// /* Program Global Control Register to disable scaledown, +// * disable clock gating +// */ +// usb_reg_write ( +// base_addr, +// DWC_XDCI_GCTL_REG, +// ((usb_reg_read (base_addr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK) +// ); + /* Program Global Control Register to disable scaledown, + * disable clock gating + */ + usb_reg_write ( + base_addr, + DWC_XDCI_GCTL_REG, + ((usb_reg_read(base_addr, DWC_XDCI_GCTL_REG) & +//HSLE_DEBUG ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | +//HSLE_DEBUG DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK)); + ~(DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK + DWC_XDCI_GCTL_RAMCLKSEL_MASK + DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK)) | + DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK | + (DWC_XDCI_GCTL_PRT_CAP_DEVICE << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS))); + +// // +// //HSLE_DEBUG +// // +// usb_reg_write(base_addr, DWC_XDCI_GCTL_REG, +// ((usb_reg_read(base_addr, DWC_XDCI_GCTL_REG) & +// ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | +// DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK)); + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_GCTL_REG))); + + + /* TODO: Program desired speed and set LPM capable + * We will do this when Superspeed works. For now, + * force into High-speed mode to aVOID anyone trying this + * on Super speed port + */ +#ifdef SUPPORT_SUPER_SPEED + usb_reg_write ( + base_addr, + DWC_XDCI_DCFG_REG, + (usb_reg_read (base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | local_core_handle->desired_speed + ); +#else + usb_reg_write ( + base_addr, + DWC_XDCI_DCFG_REG, + (usb_reg_read (base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | DWC_XDCI_DCFG_DESIRED_HS_SPEED + ); +#endif + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DCFG_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DSTS_REG))); + + /* Enable Device Interrupt Events */ + usb_reg_write ( + base_addr, + DWC_XDCI_DEVTEN_REG, + DWC_XDCI_DEVTEN_DEVICE_INTS + ); + + /* Program the desired role */ + usb_reg_write ( + base_addr, + DWC_XDCI_GCTL_REG, + (usb_reg_read (base_addr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_PRT_CAP_DIR_MASK) | (local_core_handle->role << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS) + ); + + /* Clear USB2 suspend for start new config command */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + + /* Clear USB3 suspend for start new config command */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB3PIPECTL_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + /* Issue DEPSTARTCFG command for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_START_NEW_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for START_NEW_CONFIG EP command on xDCI\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_START_NEW_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue START_NEW_CONFIG EP command on xDCI\n")); + return status; + } + + /* Issue DEPCFG command for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue DEPCFG command for EP1 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[1].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 1, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Issue DEPXFERCFG command for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue DEPXFERCFG command for EP1 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[1].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 1, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Prepare a buffer for SETUP packet */ + local_core_handle->trbs = (DWC_XDCI_TRB *)(UINTN)((UINT32)(UINTN) + local_core_handle->unaligned_trbs + + (DWC_XDCI_TRB_BYTE_ALIGNMENT - + ((UINT32)(UINTN)local_core_handle->unaligned_trbs % + DWC_XDCI_TRB_BYTE_ALIGNMENT))); + + DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ unaligned_trbs address is 0x%x\n", (unsigned) local_core_handle->unaligned_trbs)); + DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ TRB address is 0x%x\n", (unsigned) local_core_handle->trbs)); + + /* Allocate Setup buffer that is 8-byte aligned */ + local_core_handle->aligned_setup_buffer = local_core_handle->default_setup_buffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(local_core_handle->default_setup_buffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + /* Aligned buffer for status phase */ + local_core_handle->aligned_misc_buffer = local_core_handle->misc_buffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(local_core_handle->aligned_misc_buffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + /* We will queue SETUP request when we see bus reset */ + + /* Enable Physical Endpoints 0 */ + usb_reg_write ( + base_addr, + DWC_XDCI_EP_DALEPENA_REG, + usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 0) + ); + + /* Enable Physical Endpoints 1 */ + usb_reg_write ( + base_addr, + DWC_XDCI_EP_DALEPENA_REG, + usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 1) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DEVTEN_REG))); + return status; +} + + +/** + Interface: + This function is used to de-initialize the xDCI core + @core_handle: xDCI controller handle + @flags: Special flags for de-initializing the core in + particular way + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_deinit ( + IN VOID *core_handle, + __attribute__((__unused__)) IN UINT32 flags + ) +{ + /* TODO: Need to implement this */ + free (core_handle); + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to register event callback function + @core_handle: xDCI controller handle + @event: Event for which callback is to be registered + @callback_fn: Callback function to invoke after event occurs + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_register_callback ( + IN VOID *core_handle, + IN USB_DEVICE_EVENT_ID event, + IN USB_DEVICE_CALLBACK_FUNC CallbackFunc + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + + if (local_core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_register_callback: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "dwc_xdci_core_register_callback: event is %d\n", event)); + switch (event) { + case USB_DEVICE_DISCONNECT_EVENT: + local_core_handle->event_callbacks.dev_disconnect_callback = CallbackFunc; + break; + + case USB_DEVICE_RESET_EVENT: + local_core_handle->event_callbacks.dev_bus_reset_callback = CallbackFunc; + break; + + case USB_DEVICE_CONNECTION_DONE: + local_core_handle->event_callbacks.dev_reset_done_callback = CallbackFunc; + break; + + case USB_DEVICE_STATE_CHANGE_EVENT: + local_core_handle->event_callbacks.dev_link_state_callback = CallbackFunc; + break; + + case USB_DEVICE_WAKEUP_EVENT: + local_core_handle->event_callbacks.dev_wakeup_callback = CallbackFunc; + break; + + case USB_DEVICE_HIBERNATION_REQ_EVENT: + local_core_handle->event_callbacks.dev_hibernation_callback = CallbackFunc; + break; + + case USB_DEVICE_SOF_EVENT: + local_core_handle->event_callbacks.dev_sof_callback = CallbackFunc; + break; + + case USB_DEVICE_ERRATIC_ERR_EVENT: + local_core_handle->event_callbacks.dev_erratic_err_callback = CallbackFunc; + break; + + case USB_DEVICE_CMD_CMPLT_EVENT: + local_core_handle->event_callbacks.dev_cmd_cmplt_callback = CallbackFunc; + break; + + case USB_DEVICE_BUFF_OVERFLOW_EVENT: + local_core_handle->event_callbacks.dev_buff_ovflw_callback = CallbackFunc; + break; + + case USB_DEVICE_TEST_LMP_RX_EVENT: + local_core_handle->event_callbacks.dev_test_lmp_rx_callback = CallbackFunc; + break; + + case USB_DEVICE_SETUP_PKT_RECEIVED: + local_core_handle->event_callbacks.dev_setup_pkt_received_callback = CallbackFunc; + break; + + case USB_DEVICE_XFER_NRDY: + local_core_handle->event_callbacks.dev_xfer_nrdy_callback = CallbackFunc; + break; + + case USB_DEVICE_XFER_DONE: + local_core_handle->event_callbacks.dev_xfer_done_callback = CallbackFunc; + break; + + default: + break; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to unregister event callback function + @core_handle: xDCI controller handle + @event: Event for which callback function is to be unregistered + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_unregister_callback ( + IN VOID *core_handle, + IN USB_DEVICE_EVENT_ID event + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + + if (local_core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_unregister_callback: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + switch (event) { + case USB_DEVICE_DISCONNECT_EVENT: + local_core_handle->event_callbacks.dev_disconnect_callback = NULL; + break; + + case USB_DEVICE_RESET_EVENT: + local_core_handle->event_callbacks.dev_bus_reset_callback = NULL; + break; + + case USB_DEVICE_CONNECTION_DONE: + local_core_handle->event_callbacks.dev_reset_done_callback = NULL; + break; + + case USB_DEVICE_STATE_CHANGE_EVENT: + local_core_handle->event_callbacks.dev_link_state_callback = NULL; + break; + + case USB_DEVICE_WAKEUP_EVENT: + local_core_handle->event_callbacks.dev_wakeup_callback = NULL; + break; + + case USB_DEVICE_HIBERNATION_REQ_EVENT: + local_core_handle->event_callbacks.dev_hibernation_callback = NULL; + break; + + case USB_DEVICE_SOF_EVENT: + local_core_handle->event_callbacks.dev_sof_callback = NULL; + break; + + case USB_DEVICE_ERRATIC_ERR_EVENT: + local_core_handle->event_callbacks.dev_erratic_err_callback = NULL; + break; + + case USB_DEVICE_CMD_CMPLT_EVENT: + local_core_handle->event_callbacks.dev_cmd_cmplt_callback = NULL; + break; + + case USB_DEVICE_BUFF_OVERFLOW_EVENT: + local_core_handle->event_callbacks.dev_buff_ovflw_callback = NULL; + break; + + case USB_DEVICE_TEST_LMP_RX_EVENT: + local_core_handle->event_callbacks.dev_test_lmp_rx_callback = NULL; + break; + + case USB_DEVICE_SETUP_PKT_RECEIVED: + local_core_handle->event_callbacks.dev_setup_pkt_received_callback = NULL; + break; + + case USB_DEVICE_XFER_NRDY: + local_core_handle->event_callbacks.dev_xfer_nrdy_callback = NULL; + break; + + case USB_DEVICE_XFER_DONE: + local_core_handle->event_callbacks.dev_xfer_done_callback = NULL; + break; + + default: + break; + } + + return EFI_SUCCESS; +} + +/* + inline void + clflush(volatile void *p) + { + asm volatile ("clflush (%0)" :: "r"(p)); + } +*/ + +/** + Interface: + This function is used as an interrupt service routine + @core_handle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_isr_routine ( + IN VOID *core_handle + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 base_addr; + UINT32 event_count; + UINT32 processed_event_count; + UINT32 i; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_isr_routine: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (local_core_handle->interrup_processing == TRUE) { + DEBUG ((DEBUG_INFO, "interrup_processing.........\n")); + return EFI_SUCCESS; + } + + base_addr = local_core_handle->base_address; + + /* Event buffer corresponding to each interrupt line needs + * to be processed + */ + local_core_handle->interrup_processing = TRUE; + for (i = 0; i < local_core_handle->max_dev_int_lines; i++) { + /* Get the number of events HW has written for this + * interrupt line + */ + event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (i)); + event_count &= DWC_XDCI_EVNTCOUNT_MASK; + processed_event_count = 0; + + /* Process interrupt line buffer only if count is non-zero */ + if (event_count) { + + /* Process events in this buffer */ + dwc_xdci_process_interrupt_line_events (local_core_handle, event_count, &processed_event_count); + + /* Write back the processed number of events so HW decrements it from current + * event count + */ + usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (i), processed_event_count); + } + } + local_core_handle->interrup_processing = FALSE; + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used as an interrupt service routine and it processes only one event at a time. + @core_handle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_isr_routine_timer_based ( + IN VOID *core_handle + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 base_addr; + UINT32 event_count; + UINT32 processed_event_count; + UINT32 current_event_addr; + UINT32 processed_event_size = 0; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_isr_routine_timer_based: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (local_core_handle->current_event_buffer == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_isr_routine_timer_based: INVALID event buffer\n")); + return EFI_INVALID_PARAMETER; + } + + base_addr = local_core_handle->base_address; + + event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (0)) & DWC_XDCI_EVNTCOUNT_MASK; + + if (local_core_handle->interrup_processing == TRUE) { + DEBUG ((DEBUG_INFO, "interrup_processing.........\n")); + return EFI_SUCCESS; + } + + local_core_handle->interrup_processing = TRUE; + + processed_event_count = 0; + current_event_addr = (UINT32)(UINTN)(local_core_handle->current_event_buffer); + + if (local_core_handle->current_event_buffer->event & DWC_XDCI_EVENT_DEV_MASK) { + dwc_xdci_process_device_event ( + local_core_handle, + local_core_handle->current_event_buffer, + &processed_event_size + ); + } else { + dwc_xdci_process_ep_event ( + local_core_handle, + local_core_handle->current_event_buffer, + &processed_event_size); + } + + event_count -= processed_event_size; + processed_event_count += processed_event_size; + if ((current_event_addr + processed_event_size) >= + ((UINT32)(UINTN)(local_core_handle->aligned_event_buffers) + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)) + ) { + current_event_addr = (UINT32)(UINTN)(local_core_handle->aligned_event_buffers); + DEBUG ((DEBUG_INFO, "dwc_xdci_process_interrupt_line_events: Event buffer bound reached\n")); + } else { + current_event_addr += processed_event_size; + } + + local_core_handle->current_event_buffer = (DWC_XDCI_EVENT_BUFFER *)(UINTN)current_event_addr; + usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (0), processed_event_count); + local_core_handle->interrup_processing = FALSE; + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to enable xDCI to connect to the host + @core_handle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_connect ( + IN VOID *core_handle + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_connect: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* Clear KeepConnect bit so we can allow disconnect and re-connect + * Also issue No action on state change to aVOID any link change + */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + (usb_reg_read(base_addr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & ~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK + ); + + /* Set Run bit to connect to the host */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_RUN_STOP_MASK + ); + + /* Wait until core starts running */ + do { + if (!(usb_reg_read (base_addr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK)) { + break; + } else { + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to run the device controller\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to disconnect xDCI from the host + @core_handle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_disconnect ( + IN VOID *core_handle + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 base_addr; + UINT32 event_count; + UINT32 dsts; + UINT32 i; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (0)); + event_count &= DWC_XDCI_EVNTCOUNT_MASK; + + DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: event_count=%d\n", event_count)); + while (event_count) { + dwc_xdci_core_isr_routine(local_core_handle); + event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (0)); + event_count &= DWC_XDCI_EVNTCOUNT_MASK; + DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: event_count=%d\n", event_count)); + } + + /* Issue DEPENDXFER for active transfers */ + for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++){ + if (local_core_handle->ep_handles[i].currentXferRscIdx){ + dwc_xdci_end_xfer(local_core_handle, i); + } + } + /* Clear Run bit to disconnect from host */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read(base_addr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_RUN_STOP_MASK); + + /* Wait until core is halted */ + do { +// if ((usb_reg_read (base_addr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_LINK_STATE_MASK) == DWC_XDCI_DSTS_LINK_STATE_DISCONNECT) + dsts = usb_reg_read (base_addr, DWC_XDCI_DSTS_REG); + DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: waiting halt: DSTS=0x%x\n", dsts)); + if ((dsts & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK) != 0){ + break; + } else { + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: Failed to halt the device controller\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to obtain current USB bus speed + @core_handle: xDCI controller handle + @speed: Address of variable to save the speed + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_get_speed ( + IN VOID *core_handle, + IN USB_SPEED *speed + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_get_speed: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (speed == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_get_speed: INVALID parameter\n")); + return EFI_INVALID_PARAMETER; + } + + *speed = usb_reg_read (local_core_handle->base_address, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_CONN_SPEED_MASK; + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to obtain current USB bus speed + @core_handle: xDCI controller handle + @address: USB address to set (assigned by USB host) + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_set_address ( + IN VOID *core_handle, + IN UINT32 address + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_address: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_address is 0x%x \n", address)); + /* Program USB device address */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCFG_REG, + (usb_reg_read(base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DEV_ADDRESS_MASK) | (address << DWC_XDCI_DCFG_DEV_ADDRESS_BIT_POS) + ); + + local_core_handle->dev_state = UsbDevStateAddress; + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_GCTL_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DEVTEN_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DCFG_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DSTS_REG))); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to set configuration + @core_handle: xDCI controller handle + @config_num: config num to set (assigned by USB host) + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_core_set_config ( + IN VOID *core_handle, + __attribute__((__unused__)) IN UINT32 config_num + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS status; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_config: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* TODO: Disable all non-CTRL endpoints here if + * stack is not doing it + */ + /* TODO: Cancel all non-CTRL transfers here on all EPs + * if stack is not doing it + */ + /* TODO: Change EP1 TXFIFO allocation if necessary */ + + /* Re-initialize transfer resource allocation */ + + /* Issue DEPSTARTCFG command on EP0 (new config for + * non-control EPs) + */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_START_NEW_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_config: Failed to init params for EPCMD_START_NEW_CONFIG command\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + (EPCMD_START_NEW_CONFIG | (2 << DWC_XDCI_EPCMD_RES_IDX_BIT_POS)), + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_config: Failed to issue EPCMD_START_NEW_CONFIG command\n")); + return status; + } + + return status; +} + + +/** + Interface: + This function is used to set link state + @core_handle: xDCI controller handle + @state: Desired link state + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_set_link_state ( + IN VOID *core_handle, + IN USB_DEVICE_SS_LINK_STATE state + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_set_link_state: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* Clear old mask */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK + ); + + /* Request new state */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | (state << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS) + ); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to initialize endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + to be initialized + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_init_ep ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS status; + UINT32 ep_num; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + + /* Save EP properties */ + memcpy (&(local_core_handle->ep_handles[ep_num].ep_info), ep_info, sizeof (USB_EP_INFO)); + + // Init CheckFlag + local_core_handle->ep_handles[ep_num].CheckFlag = FALSE; + + /* Init DEPCFG cmd params for EP */ + status = dwc_xdci_core_init_ep_cmd_params ( + core_handle, + &local_core_handle->ep_handles[ep_num].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to init params for EPCMD_SET_EP_CONFIG command\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + core_handle, + ep_num, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to issue EPCMD_SET_EP_CONFIG command\n")); + return status; + } + + /* Issue a DEPXFERCFG command for endpoint */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[ep_num].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + ep_num, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command\n")); + } + + return status; +} + + +/** + Interface: + This function is used to enable non-Ep0 endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_enable ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 ep_num; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_enable: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + + /* Enable Physical Endpoint ep_num */ + usb_reg_write ( + base_addr, + DWC_XDCI_EP_DALEPENA_REG, + usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << ep_num) + ); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to disable non-Ep0 endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_disable ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 ep_num; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_disable: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + + /* Disable Physical Endpoint ep_num */ + usb_reg_write ( + base_addr, + DWC_XDCI_EP_DALEPENA_REG, + usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) & ~(1 << ep_num) + ); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to STALL and endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_stall ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS status; + UINT32 ep_num; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_stall: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + + /* Set Ep State Info */ + if (local_core_handle->ep_handles[ep_num].state != USB_EP_STATE_STALLED) { + local_core_handle->ep_handles[ep_num].Orgstate = local_core_handle->ep_handles[ep_num].state; + local_core_handle->ep_handles[ep_num].state = USB_EP_STATE_STALLED; + } + /* Issue a DWC_XDCI_EPCMD_SET_STALL for EP */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + ep_num, + DWC_XDCI_EPCMD_SET_STALL, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_stall: Failed to issue EP stall command\n")); + } + + return status; +} + + +/** + Interface: + This function is used to clear endpoint STALL + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_clear_stall ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS status; + UINT32 ep_num; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_clear_stall: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + + /* Set Ep State Info */ + local_core_handle->ep_handles[ep_num].state = local_core_handle->ep_handles[ep_num].Orgstate; + + /* Issue a DWC_XDCI_EPCMD_CLEAR_STALL for EP */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + ep_num, + DWC_XDCI_EPCMD_CLEAR_STALL, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_stall: Failed to issue EP clea stall command\n")); + } + + return status; +} + + +/** + Interface: + This function is used to set endpoint in NOT READY state + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_set_nrdy ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + UINT32 ep_num; + UINT32 base_addr; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_set_nrdy: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + + /* Program the EP number in command's param reg */ + usb_reg_write (base_addr, DWC_XDCI_DGCMD_PARAM_REG, ep_num); + + /* Issue EP not ready generic device command */ + usb_reg_write ( + base_addr, + DWC_XDCI_DGCMD_REG, + (usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SET_EP_NRDY) + ); + + /* Activate the command */ + usb_reg_write ( + base_addr, + DWC_XDCI_DGCMD_REG, + (usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + /* Wait until command completes */ + do { + if (!(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to queue receive SETUP packet request + @core_handle: xDCI controller handle + @buffer: Address of buffer to receive SETUP packet + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep0_receive_setup_pkt ( + IN VOID *core_handle, + IN UINT8 *buffer + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS Status = EFI_DEVICE_ERROR; + DWC_XDCI_TRB *trb; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_setup_pkt: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + local_core_handle->ep_handles[0].ep_info.ep_num = 0; + local_core_handle->ep_handles[0].ep_info.ep_dir = 0; + local_core_handle->ep_handles[0].state = USB_EP_STATE_SETUP; + trb = local_core_handle->trbs; + DEBUG ((DEBUG_INFO, "(dwc_xdci_ep0_receive_setup_pkt)\n")); + + Status = dwc_xdci_core_init_trb ( + local_core_handle, + trb, + TRBCTL_SETUP, + buffer, + 8 + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_setup_pkt: Init TRB Failed \n")); + return Status; + } + + /* Issue a DEPSTRTXFER for EP0 */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Init the lower re-bits for TRB address */ + ep_cmd_params.param1 = (UINT32)(UINTN)trb; + + /* Issue the command */ + Status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_START_XFER, + &ep_cmd_params + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "\ndwc_xdci_ep0_receive_setup_pkt: Failed to issue Start Transfer command")); + } + + /* Save new resource index for this transfer */ + local_core_handle->ep_handles[0].currentXferRscIdx = ((usb_reg_read(local_core_handle->base_address, DWC_XDCI_EPCMD_REG(0)) & + DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS + ); + + return Status; +} + + +/** + Interface: + This function is used to queue receive status packet on EP0 + @core_handle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep0_receive_status_pkt ( + IN VOID *core_handle + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_TRB *trb; + DWC_XDCI_TRB_CONTROL trb_ctrl; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS Status; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_status_pkt: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* We are receiving on EP0 so physical EP is 0 */ + trb = local_core_handle->trbs; + DEBUG ((DEBUG_INFO, "(dwc_xdci_ep0_receive_status_pkt)\n")); + if (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + DEBUG ((DEBUG_INFO, "status_pkt still not transferred.\n")); + return EFI_SUCCESS; + } + + local_core_handle->ep_handles[0].ep_info.ep_num = 0; + local_core_handle->ep_handles[0].ep_info.ep_dir = 0; + + /* OUT data phase for 3-phased control transfer */ + trb_ctrl = TRBCTL_3_PHASE; + + /* Init TRB for the transfer */ + Status = dwc_xdci_core_init_trb ( + local_core_handle, + trb, + trb_ctrl, + local_core_handle->aligned_setup_buffer, + 0 + ); + + if (!Status) { + /* Issue a DEPSTRTXFER for EP0 */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Init the lower bits for TRB address */ + ep_cmd_params.param1 = (UINT32)(UINTN)trb; + + /* Issue the command */ + Status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_START_XFER, + &ep_cmd_params + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_status_pkt: Failed to issue Start Transfer command for EP0\n")); + } + + /* Save new resource index for this transfer */ + local_core_handle->ep_handles[0].currentXferRscIdx = ((usb_reg_read(base_addr, DWC_XDCI_EPCMD_REG(0)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + + /* TODO: We are not using the EP state for control transfers + * right now simply because we're only supporting IN + * data phase. For the current use case, we don't + * need OUT data phase. We can add that later and we will + * add some of the state and SETUP packet awareness code + */ + local_core_handle->ep_handles[0].state = USB_EP_STATE_STATUS; + } + + return Status; +} + + +/** + Interface: + This function is used to send status packet on EP0 + @core_handle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep0_send_status_pkt ( + IN VOID *core_handle + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_TRB *trb; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + EFI_STATUS Status; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_send_status_pkt: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = local_core_handle->base_address; + + /* We are sending on EP0 so physical EP is 1 */ + trb = (local_core_handle->trbs + (1 * DWC_XDCI_TRB_NUM)); + DEBUG ((DEBUG_INFO, "(dwc_xdci_ep0_send_status_pkt)\n")); + + local_core_handle->ep_handles[0].state = USB_EP_STATE_STATUS; + Status = dwc_xdci_core_init_trb ( + local_core_handle, + trb, + TRBCTL_2_PHASE, + local_core_handle->aligned_misc_buffer, + 0 + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_send_status_pkt: TRB failed during status phase\n")); + return Status; + } + + /* Issue a DEPSTRTXFER for EP1 */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Init the lower re-bits for TRB address */ + ep_cmd_params.param1 = (UINT32)(UINTN)trb; + + /* Issue the command */ + Status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 1, + EPCMD_START_XFER, + &ep_cmd_params + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_send_status_pkt: Failed to issue Start Transfer on EP0\n")); + } + + /* Save new resource index for this transfer */ + local_core_handle->ep_handles[1].currentXferRscIdx = ((usb_reg_read(base_addr, DWC_XDCI_EPCMD_REG(1)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + local_core_handle->ep_handles[0].state = USB_EP_STATE_STATUS; + + return Status; +} + + +/** + Interface: + This function is used to send data on non-EP0 endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + @buffer: buffer containing data to transmit + @size: Size of transfer (in bytes) + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_tx_data ( + IN VOID *core_handle, + IN USB_XFER_REQUEST *xfer_req + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + DWC_XDCI_TRB *trb; + DWC_XDCI_TRB_CONTROL trb_ctrl; + EFI_STATUS Status; + UINT32 ep_num; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (xfer_req == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: INVALID transfer request\n")); + return EFI_INVALID_PARAMETER; + } + + base_addr = local_core_handle->base_address; + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num ( + xfer_req->ep_info.ep_num, + xfer_req->ep_info.ep_dir + ); + + trb = (local_core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM)); + DEBUG ((DEBUG_INFO, "(dwc_xdci_ep_tx_data)ep_num is %d\n", ep_num)); + + + if (ep_num > 1) + trb_ctrl = TRBCTL_NORMAL; + else + trb_ctrl = TRBCTL_CTRL_DATA_PHASE; + + if (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + Status = dwc_xdci_end_xfer (local_core_handle, ep_num); + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: Failed to end previous transfer\n")); + } + + Status = dwc_xdci_core_flush_ep_tx_fifo (local_core_handle, ep_num); + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: Failed to end previous transfer\n")); + } + } + + /* Data phase */ +// local_core_handle->ep_handles[ep_num].xfer_handle = *xfer_req; + memcpy (&(local_core_handle->ep_handles[ep_num].xfer_handle), xfer_req, sizeof (USB_XFER_REQUEST)); + local_core_handle->ep_handles[ep_num].state = USB_EP_STATE_DATA; + + local_core_handle->ep_handles[ep_num].trb = trb; + + Status = dwc_xdci_core_init_trb ( + local_core_handle, + trb, + trb_ctrl, + xfer_req->xfer_buffer, + xfer_req->xfer_len + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: TRB failed\n")); + return Status; + } + + /* Issue a DEPSTRTXFER for EP */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Init the lower re-bits for TRB address */ + ep_cmd_params.param1 = (UINT32)(UINTN)trb; + + /* Issue the command */ + Status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + ep_num, + EPCMD_START_XFER, + &ep_cmd_params + ); + + /* Save new resource index for this transfer */ + local_core_handle->ep_handles[ep_num].currentXferRscIdx = ((usb_reg_read (base_addr, DWC_XDCI_EPCMD_REG(ep_num)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + + return Status; +} + + +/** + Interface: + This function is used to receive data on non-EP0 endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + @buffer: buffer containing data to transmit + @size: Size of transfer (in bytes) + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_rx_data ( + IN VOID *core_handle, + IN USB_XFER_REQUEST *xfer_req + ) +{ + XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + DWC_XDCI_TRB *trb; + DWC_XDCI_TRB_CONTROL trb_ctrl; + EFI_STATUS Status; + UINT32 ep_num; + UINT32 base_addr; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (xfer_req == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: INVALID transfer request\n")); + return EFI_INVALID_PARAMETER; + } + + base_addr = local_core_handle->base_address; + + /* Convert to physical endpoint */ + ep_num = dwc_xdci_get_physical_ep_num (xfer_req->ep_info.ep_num, xfer_req->ep_info.ep_dir); + + trb = (local_core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM)); + DEBUG ((DEBUG_INFO, "(dwc_xdci_ep_rx_data)ep_num is %d\n", ep_num)); + + if (ep_num > 1) + trb_ctrl = TRBCTL_NORMAL; + else + trb_ctrl = TRBCTL_CTRL_DATA_PHASE; + + // + // If CheckFlag didn't set to FALSE, means the previous transfer request didn't complete, + // need to wait the previous request done. + // + if (local_core_handle->ep_handles[ep_num].CheckFlag == TRUE) { + return EFI_NOT_READY; + } + + local_core_handle->ep_handles[ep_num].CheckFlag = TRUE; + + /* Data phase */ + memcpy (&(local_core_handle->ep_handles[ep_num].xfer_handle), xfer_req, sizeof (USB_XFER_REQUEST)); + + local_core_handle->ep_handles[ep_num].state = USB_EP_STATE_DATA; + + local_core_handle->ep_handles[ep_num].trb = trb; + + DEBUG ((DEBUG_INFO, "(dwc_xdci_ep_rx_data)xfer_req->xfer_len is 0x%x\n", xfer_req->xfer_len)); + + Status = dwc_xdci_core_init_trb ( + local_core_handle, + trb, + trb_ctrl, + xfer_req->xfer_buffer, + xfer_req->xfer_len + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: TRB failed\n")); + return Status; + } + + /* Issue a DEPSTRTXFER for EP */ + /* Reset params */ + ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0; + + /* Init the lower re-bits for TRB address */ + ep_cmd_params.param1 = (UINT32)(UINTN)trb; + + /* Issue the command */ + Status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + ep_num, + EPCMD_START_XFER, + &ep_cmd_params + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: Failed to start transfer\n")); + } + + /* Save new resource index for this transfer */ + local_core_handle->ep_handles[ep_num].currentXferRscIdx = ((usb_reg_read(base_addr, DWC_XDCI_EPCMD_REG(ep_num)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + + return Status; +} + + + +STATIC +EFI_STATUS +dwc_xdci_core_flush_ep_fifo ( + IN XDCI_CORE_HANDLE *core_handle, + IN UINT32 ep_num + ) +{ + UINT32 base_addr; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 fifo_num; + UINT32 Param; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_flush_ep_tx_fifo: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + base_addr = core_handle->base_address; + + /* Translate to FIFOnum + * NOTE: Assuming this is a Tx EP + */ + fifo_num = (ep_num >> 1); + + /* TODO: Currently we are only using TxFIFO 0. Later map these + * Write the FIFO num/dir param for the generic command. + */ + + Param = usb_reg_read (base_addr, DWC_XDCI_DGCMD_PARAM_REG); + Param &= ~(DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK); + + if ((ep_num & 0x01) != 0) { + Param |= (fifo_num | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK); + } else { + Param |= fifo_num; + } + + DEBUG ((DEBUG_INFO, "USB FU Flash: CMD 0x%08x :: Param 0x%08x\n", + (usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK), + Param)); + + usb_reg_write ( + base_addr, + DWC_XDCI_DGCMD_PARAM_REG, + Param + ); + + /* Write the command to flush all FIFOs */ + usb_reg_write ( + base_addr, + DWC_XDCI_DGCMD_REG, + (usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + + /* Wait until command completes */ + do { + if (!(usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Interface: + This function is used to cancel a transfer on non-EP0 endpoint + @core_handle: xDCI controller handle + @ep_info: Address of structure describing properties of EP + +**/ +EFI_STATUS +EFIAPI +dwc_xdci_ep_cancel_transfer ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT32 ep_num; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_cancel_transfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Get physical EP num */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + Status = dwc_xdci_end_xfer(core_handle, ep_num); + dwc_xdci_core_flush_ep_fifo(core_handle, ep_num); + + return Status; +} + + +EFI_STATUS +usb_process_device_reset_det ( + IN XDCI_CORE_HANDLE *core_handle + ) +{ + return dwc_xdci_process_device_reset_det (core_handle); +} + +EFI_STATUS +usb_process_device_reset_done ( + IN XDCI_CORE_HANDLE *core_handle + ) +{ + return dwc_xdci_process_device_reset_done (core_handle); +} + +UINT32 +usb_get_physical_ep_num ( + IN UINT32 EndpointNum, + IN USB_EP_DIR EndpointDir + ) +{ + return dwc_xdci_get_physical_ep_num( + EndpointNum, + EndpointDir + ); +} + + +EFI_STATUS +EFIAPI +usb_xdci_core_reinit ( +// IN USB_DEV_CONFIG_PARAMS *ConfigParams, +// IN VOID *device_core_ptr, + IN VOID *core_handle + ) +{ + EFI_STATUS status = EFI_DEVICE_ERROR; + UINT32 base_addr; + XDCI_CORE_HANDLE *local_core_handle; + DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params; + UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT8 i; + +// local_core_handle = (XDCI_CORE_HANDLE *)AllocateZeroPool (sizeof(XDCI_CORE_HANDLE)); + + local_core_handle = core_handle; + + if (core_handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (local_core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to allocate handle for xDCI\n")); + return EFI_OUT_OF_RESOURCES; + } + +// ZeroMem (local_core_handle, sizeof(XDCI_CORE_HANDLE)); +// +// local_core_handle->parent_handle = device_core_ptr; + +// *core_handle = (VOID *)local_core_handle; +// +// local_core_handle->id = ConfigParams->ControllerId; +// local_core_handle->base_address = base_addr = ConfigParams->BaseAddress; +// local_core_handle->flags = ConfigParams->Flags; +// local_core_handle->desired_speed = local_core_handle->actual_speed = ConfigParams->Speed; +// local_core_handle->role = ConfigParams->Role; + base_addr = local_core_handle->base_address; + + DEBUG ((DEBUG_INFO, "Resetting the USB core\n")); + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_CSFTRST_MASK + ); + + /* Wait until core soft reset completes */ + do { + if (!(usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & DWC_XDCI_DCTL_CSFTRST_MASK)) { + break; + } else { + udelay(DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--max_delay_iter); + + if (!max_delay_iter) { + DEBUG ((DEBUG_INFO, "Failed to reset device controller\n")); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "USB core has been reset\n")); + + /* All FIFOs are flushed at this point */ + +// /* Ensure we have EP0 Rx/Tx handles initialized */ +// local_core_handle->ep_handles[0].ep_info.ep_num = 0; +// local_core_handle->ep_handles[0].ep_info.ep_dir = UsbEpDirOut; +// local_core_handle->ep_handles[0].ep_info.ep_type = USB_ENDPOINT_CONTROL; +// local_core_handle->ep_handles[0].ep_info.max_pkt_size = DWC_XDCI_SS_CTRL_EP_MPS; +// /* 0 means burst size of 1 */ +// local_core_handle->ep_handles[0].ep_info.burst_size = 0; +// +// local_core_handle->ep_handles[1].ep_info.ep_num = 0; +// local_core_handle->ep_handles[1].ep_info.ep_dir = UsbEpDirIn; +// local_core_handle->ep_handles[1].ep_info.ep_type = USB_ENDPOINT_CONTROL; +// local_core_handle->ep_handles[1].ep_info.max_pkt_size = DWC_XDCI_SS_CTRL_EP_MPS; +// /* 0 means burst size of 1 */ +// local_core_handle->ep_handles[1].ep_info.burst_size = 0; + + local_core_handle->dev_state = UsbDevStateDefault; + + /* Clear KeepConnect bit so we can allow disconnect and + * re-connect. Stay in RX_DETECT state + */ + usb_reg_write ( + base_addr, + DWC_XDCI_DCTL_REG, + (usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & + (~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & + (~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK)) | + (DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS) + ); + + DEBUG ((DEBUG_INFO, "Device controller Synopsys ID: %x\n", usb_reg_read (base_addr, DWC_XDCI_GSNPSID_REG))); + DEBUG ((DEBUG_INFO, "Default value of xDCI GSBUSCFG0 and GSBUSCFG1: %x, %x\n", + usb_reg_read (base_addr, DWC_XDCI_GSBUSCFG0_REG), + usb_reg_read (base_addr, DWC_XDCI_GSBUSCFG1_REG))); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GTXTHRCFG and GRXTHRCFG: %x, %x\n", + usb_reg_read (base_addr, DWC_XDCI_GTXTHRCFG_REG), + usb_reg_read (base_addr, DWC_XDCI_GRXTHRCFG_REG))); + + /* Clear ULPI auto-resume bit */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG (0)) & ~DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GUSB2PHYCFG and GUSB3PIPECTL: %x, %x\n", + usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG (0)), + usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG (0)))); + + /* Only one RxFIFO */ + DEBUG ((DEBUG_INFO, "Default value of DWC_XDCI_GRXFIFOSIZ: %x\n", + usb_reg_read (base_addr, DWC_XDCI_GRXFIFOSIZ_REG (0)))); + + for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++) { + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_GTXFIFOSIZ %d: %x\n", + i, usb_reg_read (base_addr, DWC_XDCI_GTXFIFOSIZ_REG (i)))); + } + + /* TODO: Need to check if TxFIFO should start where RxFIFO ends + * or default is correct i.e. TxFIFO starts at 0 just like RxFIFO + */ + + /* Allocate and Initialize Event Buffers */ + local_core_handle->max_dev_int_lines = ((usb_reg_read (base_addr, DWC_XDCI_GHWPARAMS1_REG) & + DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >> + DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS); + + DEBUG ((DEBUG_INFO, "Max dev int lines: %d\n", local_core_handle->max_dev_int_lines)); + + /* One event buffer per interrupt line. + * Need to align it to size of event buffer + * Buffer needs to be big enough. Otherwise the core + * won't operate + */ + local_core_handle->aligned_event_buffers = (DWC_XDCI_EVENT_BUFFER *) + ((UINT32)(UINTN)(local_core_handle->event_buffers) + + ((sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) - + (((UINT32)(UINTN)(local_core_handle->event_buffers)) % + (sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)))); + + for (i = 0; i < local_core_handle->max_dev_int_lines; i++) { + usb_reg_write ( + base_addr, + DWC_XDCI_GEVNTADR_REG (i), + (UINT32)(UINTN)(local_core_handle->aligned_event_buffers + i * sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) + ); + + // + // Clear High 32bit address register, GEVNTADR register is 64-bit register + // default is 0xffffffffffffffff + // + usb_reg_write (base_addr, DWC_XDCI_GEVNTADR_REG (i) + 4, 0x00000000); + + local_core_handle->current_event_buffer = local_core_handle->aligned_event_buffers; + /* Write size and clear the mask */ + usb_reg_write ( + base_addr, + DWC_XDCI_EVNTSIZ_REG (i), + sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER + ); + + /* Write 0 to the event count register as the last step + * for event configuration + */ + usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (i), 0); + + DEBUG ((DEBUG_INFO, "Value of xDCI Event buffer %d: %x, Size: %x, Count: %x\n", + i, + usb_reg_read (base_addr, DWC_XDCI_GEVNTADR_REG (i)), + usb_reg_read (base_addr, DWC_XDCI_EVNTSIZ_REG (i)), + usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (i)))); + } + +// /* Program Global Control Register to disable scaledown, +// * disable clock gating +// */ +// usb_reg_write ( +// base_addr, +// DWC_XDCI_GCTL_REG, +// ((usb_reg_read (base_addr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK) +// ); + /* Program Global Control Register to disable scaledown, + * disable clock gating + */ + usb_reg_write ( + base_addr, + DWC_XDCI_GCTL_REG, + ((usb_reg_read(base_addr, DWC_XDCI_GCTL_REG) & +//HSLE_DEBUG ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | +//HSLE_DEBUG DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK)); + ~(DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK + DWC_XDCI_GCTL_RAMCLKSEL_MASK + DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK)) | + DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK | + (DWC_XDCI_GCTL_PRT_CAP_DEVICE << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS))); + +// // +// //HSLE_DEBUG +// // +// usb_reg_write(base_addr, DWC_XDCI_GCTL_REG, +// ((usb_reg_read(base_addr, DWC_XDCI_GCTL_REG) & +// ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | +// DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK)); + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_GCTL_REG))); + + + /* TODO: Program desired speed and set LPM capable + * We will do this when Superspeed works. For now, + * force into High-speed mode to aVOID anyone trying this + * on Super speed port + */ +#ifdef SUPPORT_SUPER_SPEED + usb_reg_write ( + base_addr, + DWC_XDCI_DCFG_REG, + (usb_reg_read (base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | local_core_handle->desired_speed + ); +#else + usb_reg_write ( + base_addr, + DWC_XDCI_DCFG_REG, + (usb_reg_read (base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | DWC_XDCI_DCFG_DESIRED_HS_SPEED + ); +#endif + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DCFG_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DSTS_REG))); + + /* Enable Device Interrupt Events */ + usb_reg_write ( + base_addr, + DWC_XDCI_DEVTEN_REG, + DWC_XDCI_DEVTEN_DEVICE_INTS + ); + + /* Program the desired role */ + usb_reg_write ( + base_addr, + DWC_XDCI_GCTL_REG, + (usb_reg_read (base_addr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_PRT_CAP_DIR_MASK) | (local_core_handle->role << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS) + ); + + /* Clear USB2 suspend for start new config command */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + + /* Clear USB3 suspend for start new config command */ + usb_reg_write ( + base_addr, + DWC_XDCI_GUSB3PIPECTL_REG (0), + (usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + /* Issue DEPSTARTCFG command for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_START_NEW_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for START_NEW_CONFIG EP command on xDCI\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_START_NEW_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue START_NEW_CONFIG EP command on xDCI\n")); + return status; + } + + /* Issue DEPCFG command for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue DEPCFG command for EP1 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[1].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 1, + EPCMD_SET_EP_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Issue DEPXFERCFG command for EP0 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[0].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 0, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + return status; + } + + /* Issue DEPXFERCFG command for EP1 */ + status = dwc_xdci_core_init_ep_cmd_params ( + local_core_handle, + &local_core_handle->ep_handles[1].ep_info, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Issue the command */ + status = dwc_xdci_core_issue_ep_cmd ( + local_core_handle, + 1, + EPCMD_SET_EP_XFER_RES_CONFIG, + &ep_cmd_params + ); + + if (status) { + DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + return status; + } + + /* Prepare a buffer for SETUP packet */ + local_core_handle->trbs = (DWC_XDCI_TRB *)(UINTN)((UINT32)(UINTN) + local_core_handle->unaligned_trbs + + (DWC_XDCI_TRB_BYTE_ALIGNMENT - + ((UINT32)(UINTN)local_core_handle->unaligned_trbs % + DWC_XDCI_TRB_BYTE_ALIGNMENT))); + + DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ unaligned_trbs address is 0x%x\n", (unsigned) local_core_handle->unaligned_trbs)); + DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ TRB address is 0x%x\n", (unsigned) local_core_handle->trbs)); + + /* Allocate Setup buffer that is 8-byte aligned */ + local_core_handle->aligned_setup_buffer = local_core_handle->default_setup_buffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(local_core_handle->default_setup_buffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + /* Aligned buffer for status phase */ + local_core_handle->aligned_misc_buffer = local_core_handle->misc_buffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(local_core_handle->aligned_misc_buffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + /* We will queue SETUP request when we see bus reset */ + + /* Enable Physical Endpoints 0 */ + usb_reg_write ( + base_addr, + DWC_XDCI_EP_DALEPENA_REG, + usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 0) + ); + + /* Enable Physical Endpoints 1 */ + usb_reg_write ( + base_addr, + DWC_XDCI_EP_DALEPENA_REG, + usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 1) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DEVTEN_REG))); + return status; + + +} + + +EFI_STATUS +usb_xdci_core_flush_ep_fifo ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT32 ep_num; + + if (core_handle == NULL) { + DEBUG ((DEBUG_INFO, "dwc_xdci_ep_cancel_transfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + /* Get physical EP num */ + ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir); + dwc_xdci_core_flush_ep_fifo(core_handle, ep_num); + + return Status; +} diff --git a/drivers/dw3/XdciDWC.h b/drivers/dw3/XdciDWC.h new file mode 100644 index 0000000..87de205 --- /dev/null +++ b/drivers/dw3/XdciDWC.h @@ -0,0 +1,695 @@ +/*++ + + Copyright (c) 1999 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + +#ifndef _XDCI_DWC_H_ +#define _XDCI_DWC_H_ + +#include "XdciCommon.h" +#include "XdciDevice.h" + +#define DWC_XDCI_MAX_ENDPOINTS (16) +#define DWC_XDCI_SS_CTRL_EP_MPS (512) +#define DWC_XDCI_HS_CTRL_EP_MPS (64) +#define DWC_XDCI_FS_CTRL_EP_MPS (64) +#define DWC_XDCI_LS_CTRL_EP_MPS (8) +#define DWC_XDCI_SS_CTRL_BUF_SIZE (512) +#define DWC_XDCI_SETUP_BUFF_SIZE (8) +#define DWC_XDCI_MAX_EVENTS_PER_BUFFER (16) +#define DWC_XDCI_TRB_BYTE_ALIGNMENT (16) +#define DWC_XDCI_DEFAULT_TX_FIFO_SIZE (1024) +#define DWC_XDCI_TRB_NUM (32) +#define DWC_XDCI_MASK (DWC_XDCI_TRB_NUM - 1) + +/* TODO: Platform-specific define. Later move it to platform-specific + * header file + * */ +#define DWC_XDCI_MAX_DELAY_ITERATIONS (1000) + +/* Top-level register offsets from base address */ +#define DWC_XDCI_GLOBAL_REG_OFFSET (0xC100) +#define DWC_XDCI_DEVICE_REG_OFFSET (0xC700) +#define DWC_XDCI_OTG_BC_REG_OFFSET (0xCC00) + +#define DWC_XDCI_GSBUSCFG0_REG (0xC100) +#define DWC_XDCI_GSBUSCFG1_REG (0xC104) +#define DWC_XDCI_GTXTHRCFG_REG (0xC108) +#define DWC_XDCI_GRXTHRCFG_REG (0xC10C) + +/* Global Control Register and bit definitions */ +#define DWC_XDCI_GCTL_REG (0xC110) +#define DWC_XDCI_GCTL_PWRDNSCALE_MASK (0xFFF80000) +#define DWC_XDCI_GCTL_PWRDNSCALE_VAL (0x13880000) +#define DWC_XDCI_GCTL_U2RSTECN_MASK (0x00010000) +#define DWC_XDCI_GCTL_PRT_CAP_DIR_MASK (0x00003000) +#define DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS (12) +#define DWC_XDCI_GCTL_PRT_CAP_HOST (1) +#define DWC_XDCI_GCTL_PRT_CAP_DEVICE (2) +#define DWC_XDCI_GCTL_PRT_CAP_OTG (3) +#define DWC_XDCI_GCTL_RAMCLKSEL_MASK (0x000000C0) +#define DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK (0x00000030) +#define DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK (0x00000001) +#define DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK (0x00000008) + +#define DWC_XDCI_GSTS_REG (0xC118) +#define DWC_XDCI_GSNPSID_REG (0xC120) +#define DWC_XDCI_GGPIO_REG (0xC124) +#define DWC_XDCI_GUID_REG (0xC128) +#define DWC_XDCI_GUCTL_REG (0xC12C) +#define DWC_XDCI_GBUSERRADDR (0xC130) + +/* Global Hardware Parameters Registers */ +#define DWC_XDCI_GHWPARAMS0_REG (0xC140) +#define DWC_XDCI_GHWPARAMS1_REG (0xC144) +#define DWC_XDCI_GHWPARAMS1_NUM_INT_MASK (0x1F8000) +#define DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS (15) + +#define DWC_XDCI_GHWPARAMS2_REG (0xC148) +#define DWC_XDCI_GHWPARAMS3_REG (0xC14C) +#define DWC_XDCI_GHWPARAMS4_REG (0xC150) +#define DWC_XDCI_GHWPARAMS4_CACHE_TRBS_PER_XFER_MASK (0x0000003F) +#define DWC_XDCI_GHWPARAMS5_REG (0xC154) +#define DWC_XDCI_GHWPARAMS6_REG (0xC158) +#define DWC_XDCI_GHWPARAMS7_REG (0xC15C) +#define DWC_XDCI_GHWPARAMS8_REG (0xC600) + +#define DWC_XDCI_GDBGFIFOSPACE_REG (0xC160) + +#define DWC_XDCI_GUSB2PHYCFG_REG(n) (0xC200 + (n << 2)) +#define DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK (0x00008000) +#define DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK (0x00000040) + +#define DWC_XDCI_GUSB3PIPECTL_REG(n) (0xC2C0 + (n << 2)) +#define DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK (0x00020000) + +#define DWC_XDCI_GTXFIFOSIZ_REG(n) (0xC300 + (n << 2)) +#define DWC_XDCI_GTXFIFOSIZ_START_ADDRESS_MASK (0xFFFF0000) +#define DWC_XDCI_GTXFIFOSIZ_START_ADDRESS_BIT_POS (16) +#define DWC_XDCI_GRXFIFOSIZ_REG(n) (0xC380 + (n << 2)) + +/* Global Event Buffer Registers */ +#define DWC_XDCI_GEVNTADR_REG(n) (0xC400 + (n << 4)) +#define DWC_XDCI_EVNTSIZ_REG(n) (0xC408 + (n << 4)) +#define DWC_XDCI_EVNTSIZ_MASK (0x0000FFFF) +#define DWC_XDCI_EVNT_INTR_MASK (0x80000000) +#define DWC_XDCI_EVNTCOUNT_REG(n) (0xC40C + (n << 4)) +#define DWC_XDCI_EVNTCOUNT_MASK (0x0000FFFF) + +/* Device Configuration Register and Bit Definitions */ +#define DWC_XDCI_DCFG_REG (0xC700) +#define DWC_XDCI_DCFG_LPM_CAPABLE_MASK (0x00400000) +#define DWC_XDCI_DCFG_DEV_ADDRESS_MASK (0x000003F8) +#define DWC_XDCI_DCFG_DEV_ADDRESS_BIT_POS (3) +#define DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK (0x00000007) +#define DWC_XDCI_DCFG_DESIRED_SS_SPEED (0x00000004) +#define DWC_XDCI_DCFG_DESIRED_FS_SPEED (0x00000001) +#define DWC_XDCI_DCFG_DESIRED_HS_SPEED (0x00000000) + +/* Device Control Register */ +#define DWC_XDCI_DCTL_REG (0xC704) +#define DWC_XDCI_DCTL_RUN_STOP_MASK (0x80000000) +#define DWC_XDCI_DCTL_RUN_STOP_BIT_POS (31) +#define DWC_XDCI_DCTL_CSFTRST_MASK (0x40000000) +#define DWC_XDCI_DCTL_CSFTRST_BIT_POS (30) +#define DWC_XDCI_DCTL_KEEP_CONNECT_MASK (0x00080000) +#define DWC_XDCI_DCTL_KEEP_CONNECT_BIT_POS (19) +#define DWC_XDCI_DCTL_CSFTRST_BIT_POS (30) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK (0x000001E0) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS (5) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_NO_ACTION (1) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_SS_DISABLED (4) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT (5) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_SS_INACTIVE (6) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_RECOVERY (8) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_COMPLIANCE (10) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_REMOTE_WAKEUP (8) + +/* Device Event Enable Register */ +#define DWC_XDCI_DEVTEN_REG (0xC708) +#define DWC_XDCI_DEVTEN_DISCONN_DET_EN_MASK (0x00000001) +#define DWC_XDCI_DEVTEN_RESET_DET_EN_MASK (0x00000002) +#define DWC_XDCI_DEVTEN_CONN_DONE_DET_EN_MASK (0x00000004) +#define DWC_XDCI_DEVTEN_LINK_STATE_CHANGE_DET_EN_MASK (0x00000008) +#define DWC_XDCI_DEVTEN_RESUME_WAKEUP_DET_EN_MASK (0x00000010) +#define DWC_XDCI_DEVTEN_HIBERNATION_REQ_EN_MASK (0x00000020) +#define DWC_XDCI_DEVTEN_U3L2L1_DET_EN_MASK (0x00000040) +#define DWC_XDCI_DEVTEN_SOF_DET_EN_MASK (0x00000080) +#define DWC_XDCI_DEVTEN_ERRATIC_ERR_DET_EN_MASK (0x00000200) +#define DWC_XDCI_DEVTEN_VNDR_DEV_TST_RX_DET_EN_MASK (0x00001000) + +#define DWC_XDCI_DEVTEN_DEVICE_INTS (DWC_XDCI_DEVTEN_DISCONN_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_RESET_DET_EN_MASK | DWC_XDCI_DEVTEN_CONN_DONE_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_LINK_STATE_CHANGE_DET_EN_MASK | DWC_XDCI_DEVTEN_RESUME_WAKEUP_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_HIBERNATION_REQ_EN_MASK | DWC_XDCI_DEVTEN_U3L2L1_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_ERRATIC_ERR_DET_EN_MASK) + +#define DWC_XDCI_EVENT_BUFF_BULK_STREAM_ID_MASK (0xFFFF0000) +#define DWC_XDCI_EVENT_BUFF_ISOCH_UFRAME_NUM_MASK (0xFFFF0000) +#define DWC_XDCI_EVENT_BUFF_EP_CMD_TYPE_MASK (0x0F000000) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_RES_INDEX_MASK (0x007F0000) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_ACTIVE_MASK (0x00008000) +#define DWC_XDCI_EVENT_BUFF_EP_CTRL_DATA_REQ_MASK (0x00001000) +#define DWC_XDCI_EVENT_BUFF_EP_CTRL_STATUS_REQ_MASK (0x00002000) +#define DWC_XDCI_EVENT_BUFF_EP_LST_MASK (0x00008000) +#define DWC_XDCI_EVENT_BUFF_EP_MISSED_ISOCH_MASK (0x00008000) +#define DWC_XDCI_EVENT_BUFF_EP_IOC_MASK (0x00004000) +#define DWC_XDCI_EVENT_BUFF_EP_LAST_PKT_MASK (0x00002000) +#define DWC_XDCI_EVENT_BUFF_EP_STREAM_NOT_FND_MASK (0x00002000) +#define DWC_XDCI_EVENT_BUFF_EP_STREAM_FND_MASK (0x00001000) +#define DWC_XDCI_EVENT_BUFF_EP_ERR_NO_RES_MASK (0x00001000) +#define DWC_XDCI_EVENT_BUFF_EP_INVALID_RES_MASK (0x00001000) + +#define DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK (0x000003C0) +#define DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS (6) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT (1) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS (2) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY (3) +#define DWC_XDCI_EVENT_BUFF_EP_STREAM_EVENT (6) +#define DWC_XDCI_EVENT_BUFF_EP_CMD_CMPLT (7) + +#define DWC_XDCI_EVENT_BUFF_EP_NUM_MASK (0x0000003E) +#define DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS (1) + +#define DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK (0x0000F000) + + +#define DWC_XDCI_EVENT_BUFF_DEV_HIRD_MASK (0x01E00000) +#define DWC_XDCI_EVENT_BUFF_DEV_HIRD_BIT_POS (21) +#define DWC_XDCI_EVENT_BUFF_DEV_SS_EVENT_MASK (0x00100000) +#define DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_MASK (0x000F0000) +#define DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_BIT_POS (16) + +#define DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK (0x00000F00) +#define DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS (8) +#define DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT (12) +#define DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT (11) +#define DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT (10) +#define DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT (9) +#define DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT (7) +#define DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT (5) +#define DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT (4) +#define DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT (3) +#define DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT (2) +#define DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT (1) +#define DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT (0) + +#define DWC_XDCI_EVENT_DEV_MASK (0x00000001) + +/* Device Status Register and Bit Definitions */ +#define DWC_XDCI_DSTS_REG (0xC70C) +#define DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK (0x00400000) +#define DWC_XDCI_DSTS_DEV_CTRL_HALTED_BIT_POS (22) +#define DWC_XDCI_DSTS_CORE_IDLE (1 << 23) +#define DWC_XDCI_DSTS_CONN_SPEED_MASK (0x00000007) +#define DWC_XDCI_DSTS_LINK_STATE_MASK (0x003C0000) +#define DWC_XDCI_DSTS_LINK_STATE_DISCONNECT (0x00100000) + +/* Device Generic Command Parameter Register */ +#define DWC_XDCI_DGCMD_PARAM_REG (0xC710) +#define DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK (0x0000001F) +#define DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK (0x00000020) +#define DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_BIT_POS (5) + +/* Device Generic Command Register */ +#define DWC_XDCI_DGCMD_REG (0xC714) +#define DWC_XDCI_DGCMD_CMD_STATUS_MASK (0x00008000) +#define DWC_XDCI_DGCMD_CMD_ACTIVE_MASK (0x00000400) +#define DWC_XDCI_DGCMD_CMD_IOC_MASK (0x00000100) +#define DWC_XDCI_DGCMD_CMD_TYPE_MASK (0x000000FF) +#define DWC_XDCI_DGCMD_CMD_SET_PERIODIC_PARAMS (0x2) +#define DWC_XDCI_DGCMD_CMD_SET_SCRATCH_PAD_BUFF_ARR_LO (0x4) +#define DWC_XDCI_DGCMD_CMD_SET_SCRATCH_PAD_BUFF_ARR_HI (0x5) +#define DWC_XDCI_DGCMD_CMD_XMIT_DEVICE_NOTIFICATION (0x7) +#define DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH (0x9) +#define DWC_XDCI_DGCMD_CMD_ALL_FIFO_FLUSH (0xA) +#define DWC_XDCI_DGCMD_CMD_SET_EP_NRDY (0xC) +#define DWC_XDCI_DGCMD_CMD_RUN_SOC_BUS_LPBK (0x10) + +/* Device Active USB EP Enable Register */ +#define DWC_XDCI_EP_DALEPENA_REG (0xC720) + +/* Device Physical EP CMD Param 2 Register. Value is 32-bit */ +#define DWC_XDCI_EPCMD_PARAM2_REG(n) (0xC800 + (n << 4)) + +/* Device Physical EP CMD Param 1 Register. Value is 32-bit */ +#define DWC_XDCI_EPCMD_PARAM1_REG(n) (0xC804 + (n << 4)) + +/* Device Physical EP CMD Param 0 Register. Value is 32-bit */ +#define DWC_XDCI_EPCMD_PARAM0_REG(n) (0xC808 + (n << 4)) + +/* Device Physical EP Command Registers and Bit Definitions */ +#define DWC_XDCI_EPCMD_REG(n) (0xC80C + (n << 4)) +#define DWC_XDCI_EPCMD_RES_IDX_MASK (0x007F0000) +#define DWC_XDCI_EPCMD_RES_IDX_BIT_POS (16) +#define DWC_XDCI_EPCMD_CMDTYPE_MASK (0x0000000F) +#define DWC_XDCI_EPCMD_SET_EP_CONFIG (0x1) +#define DWC_XDCI_EPCMD_SET_EP_XFER_RES_CONFIG (0x2) +#define DWC_XDCI_EPCMD_GET_EP_STATE (0x3) +#define DWC_XDCI_EPCMD_SET_STALL (0x4) +#define DWC_XDCI_EPCMD_CLEAR_STALL (0x5) +#define DWC_XDCI_EPCMD_START_XFER (0x6) +#define DWC_XDCI_EPCMD_UPDATE_XFER (0x7) +#define DWC_XDCI_EPCMD_END_XFER (0x8) +#define DWC_XDCI_EPCMD_START_NEW_CONFIG (0x9) + +#define DWC_XDCI_EPCMD_CMD_IOC_MASK (0x00000100) +#define DWC_XDCI_EPCMD_CMD_ACTIVE_MASK (0x00000400) +#define DWC_XDCI_EPCMD_HIGH_PRIO_MASK (0x00000800) +#define DWC_XDCI_EPCMD_FORCE_RM_MASK (0x00000800) + +/* Command status and parameter values same as event status and parameters values */ +#define DWC_XDCI_EPCMD_CMD_STATUS_MASK (0x0000F000) + +/* Command Params bit masks */ +#define DWC_XDCI_PARAM1_SET_EP_CFG_FIFO_BASED_MASK (0x80000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BULK_BASED_MASK (0x40000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_MASK (0x3C000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_MASK (0x02000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK (0x01000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_MASK (0x00FF0000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS (16) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EBC_MASK (0x00008000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK (0x00003F00) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_BIT_POS (8) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_STRM_MASK (0x00002000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK (0x00000400) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK (0x00000200) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK (0x00000100) +#define DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK (0x0000001F) + +/* CMD 1 param 0 */ +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MASK (0xC0000000) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_BIT_POS (30) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE (0) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_RESTORE_ST (1) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE (2) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE (3) +#define DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_MASK (0x03C00000) +#define DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_BIT_POS (22) +#define DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_MASK (0x003E0000) +#define DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_BIT_POS (17) +#define DWC_XDCI_PARAM0_SET_EP_CFG_MPS_MASK (0x00003FF8) +#define DWC_XDCI_PARAM0_SET_EP_CFG_MPS_BIT_POS (3) +#define DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_MASK (0x00000006) +#define DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_BIT_POS (1) +#define DWC_XDCI_PARAM0_EP_TYPE_CTRL (0) +#define DWC_XDCI_PARAM0_EP_TYPE_ISOCH (1) +#define DWC_XDCI_PARAM0_EP_TYPE_BULK (2) +#define DWC_XDCI_PARAM0_EP_TYPE_INTR (3) + +/* CMD 1 param 1 */ +#define DWC_XDCI_PARAM1_SET_EP_CFG_BULK_BASED_MASK (0x40000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_MASK (0x3C000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_BIT_POS (26) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_MASK (0x02000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_BIT_POS (25) +#define DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK (0x01000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_MASK (0x00FF0000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS (16) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EBC_MASK (0x00008000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK (0x00003F00) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_BIT_POS (8) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_STRM_MASK (0x00002000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK (0x00000400) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK (0x00000200) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK (0x00000100) +#define DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK (0x0000001F) + +/* CMD 2 param 0 */ +#define DWC_XDCI_PARAM0_SET_EP_XFER_RES_NUM_MASK (0x0000FFFF) + +/* CMD 3 param 2 */ +#define DWC_XDCI_PARAM2_GET_EP_STATE_MASK (0xFFFFFFFF) + +/* CMD 6 param 1 */ +#define DWC_XDCI_PARAM1_STRT_XFER_TD_ADDR_LO_MASK (0xFFFFFFFF) + +/* CMD 6 param 0 */ +#define DWC_XDCI_PARAM0_STRT_XFER_TD_ADDR_HI_MASK (0xFFFFFFFF) + +/* Transfer Request Block Fields' Bit Definitions */ +#define DWC_XDCI_TRB_BUFF_SIZE_MASK (0x00FFFFFF) +#define DWC_XDCI_TRB_PCM1_MASK (0x03000000) +#define DWC_XDCI_TRB_PCM1_BIT_POS (24) +#define DWC_XDCI_TRB_STATUS_MASK (0xF0000000) +#define DWC_XDCI_TRB_STATUS_BIT_POS (28) +#define DWC_XDCI_TRB_STATUS_OK (0) +#define DWC_XDCI_TRB_STATUS_MISSED_ISOCH (1) +#define DWC_XDCI_TRB_STATUS_SETUP_PENDING (2) + +#define DWC_XDCI_TRB_CTRL_HWO_MASK (0x00000001) +#define DWC_XDCI_TRB_CTRL_LST_TRB_MASK (0x00000002) +#define DWC_XDCI_TRB_CTRL_LST_TRB_BIT_POS (1) +#define DWC_XDCI_TRB_CTRL_CHAIN_BUFF_MASK (0x00000004) +#define DWC_XDCI_TRB_CTRL_CHAIN_BUFF_BIT_POS (2) +#define DWC_XDCI_TRB_CTRL_CSP_MASK (0x00000008) +#define DWC_XDCI_TRB_CTRL_CSP_BIT_POS (3) +#define DWC_XDCI_TRB_CTRL_TYPE_MASK (0x000003F0) +#define DWC_XDCI_TRB_CTRL_TYPE_BIT_POS (4) +#define DWC_XDCI_TRB_CTRL_TYPE_NORMAL (1) +#define DWC_XDCI_TRB_CTRL_TYPE_SETUP (2) +#define DWC_XDCI_TRB_CTRL_TYPE_STATUS2 (3) +#define DWC_XDCI_TRB_CTRL_TYPE_STATUS3 (4) +#define DWC_XDCI_TRB_CTRL_TYPE_DATA (5) +#define DWC_XDCI_TRB_CTRL_TYPE_ISOCH_FIRST (6) +#define DWC_XDCI_TRB_CTRL_TYPE_ISOCH (7) +#define DWC_XDCI_TRB_CTRL_TYPE_LINK_TRB (8) +#define DWC_XDCI_TRB_CTRL_IOSP_MISOCH_MASK (0x00000400) +#define DWC_XDCI_TRB_CTRL_IOSP_MISOCH_BIT_POS (10) +#define DWC_XDCI_TRB_CTRL_IOC_MASK (0x00000800) +#define DWC_XDCI_TRB_CTRL_IOC_BIT_POS (11) +#define DWC_XDCI_TRB_CTRL_STRM_ID_SOF_NUM_MASK (0x3FFFC000) +#define DWC_XDCI_TRB_CTRL_STRM_ID_SOF_BIT_POS (14) + +#define DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES (4) +#define DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES (12) + +typedef enum { + EPCMD_SET_EP_CONFIG = 1, + EPCMD_SET_EP_XFER_RES_CONFIG, + EPCMD_GET_EP_STATE, + EPCMD_SET_STALL, + EPCMD_CLEAR_STALL, + EPCMD_START_XFER, + EPCMD_UPDATE_XFER, + EPCMD_END_XFER, + EPCMD_START_NEW_CONFIG = 9 +} DWC_XDCI_ENDPOINT_CMD; + +typedef enum { + ON = 0, + SLEEP = 2, + SUSPEND, + DISCONNECTED, + EARLY_SUSPEND, + RESET = 14, + RESUME = 15 +} DWC_XDCI_HS_LINK_STATE; + +typedef enum { + TRBCTL_NORMAL = 1, + TRBCTL_SETUP, + TRBCTL_2_PHASE, + TRBCTL_3_PHASE, + TRBCTL_CTRL_DATA_PHASE, + TRBCTL_ISOCH_FIRST, + TRBCTL_ISOCH, + TRBCTL_LINK +} DWC_XDCI_TRB_CONTROL; + +/* TODO: Make sure none of the mechanisms and data structures + * are leaving any holes + */ + +/* DWC XDCI Endpoint Commands Parameters struct */ +typedef struct { + UINT32 param2; + UINT32 param1; + UINT32 param0; +} DWC_XDCI_ENDPOINT_CMD_PARAMS; + +/* Event Buffer Struct */ +typedef struct { + UINT32 event; + UINT32 dev_tst_lmp1; + UINT32 dev_tst_lmp2; + UINT32 reserved; +} DWC_XDCI_EVENT_BUFFER; + +/* Transfer Request Block */ +typedef struct { + UINT32 buff_ptr_low; + UINT32 buff_ptr_high; + UINT32 len_xfer_params; + UINT32 trb_ctrl; +} DWC_XDCI_TRB; + +typedef struct { + USB_EP_INFO ep_info; + DWC_XDCI_TRB *trb; + USB_XFER_REQUEST xfer_handle; + UINT32 currentXferRscIdx; + VOID *core_handle; + USB_EP_STATE state; + USB_EP_STATE Orgstate; + BOOLEAN CheckFlag; +} DWC_XDCI_ENDPOINT; + +typedef struct { + /* cb_event_params must be copied over by upper layer if + * it defers event processing + */ + USB_DEVICE_CALLBACK_PARAM cb_event_params; + + /* Callback function list */ + USB_DEVICE_CALLBACK_FUNC dev_disconnect_callback; + USB_DEVICE_CALLBACK_FUNC dev_bus_reset_callback; + USB_DEVICE_CALLBACK_FUNC dev_reset_done_callback; + USB_DEVICE_CALLBACK_FUNC dev_link_state_callback; + USB_DEVICE_CALLBACK_FUNC dev_wakeup_callback; + USB_DEVICE_CALLBACK_FUNC dev_hibernation_callback; + USB_DEVICE_CALLBACK_FUNC dev_sof_callback; + USB_DEVICE_CALLBACK_FUNC dev_erratic_err_callback; + USB_DEVICE_CALLBACK_FUNC dev_cmd_cmplt_callback; + USB_DEVICE_CALLBACK_FUNC dev_buff_ovflw_callback; + USB_DEVICE_CALLBACK_FUNC dev_test_lmp_rx_callback; + USB_DEVICE_CALLBACK_FUNC dev_setup_pkt_received_callback; + USB_DEVICE_CALLBACK_FUNC dev_xfer_nrdy_callback; + USB_DEVICE_CALLBACK_FUNC dev_xfer_done_callback; +} USB_DEV_CALLBACK_LIST; + +typedef struct { + VOID *parent_handle; // Pointer to the parent this driver is associated + USB_CONTROLLER_ID id; // ID of the controllers supported in our DCD + USB_SPEED desired_speed; // Desired SS, HS, FS or LS speeds for the core + USB_ROLE role; // Desired role i.e. host, device or OTG + USB_SPEED actual_speed; // Actual speed + USB_DEVICE_STATE dev_state; // Device state + UINT32 base_address; // Register base address + UINT32 flags; // Init flags + UINT32 max_dev_int_lines; // One event buffer per interrupt line + DWC_XDCI_EVENT_BUFFER event_buffers [DWC_XDCI_MAX_EVENTS_PER_BUFFER * 2]; // Event buffer pool + DWC_XDCI_EVENT_BUFFER *aligned_event_buffers; // Aligned event buffer pool + DWC_XDCI_EVENT_BUFFER *current_event_buffer; // Current event buffer address + DWC_XDCI_TRB unaligned_trbs [(DWC_XDCI_MAX_ENDPOINTS + 1) * DWC_XDCI_TRB_NUM]; // TRBs. + DWC_XDCI_TRB *trbs; // 16-bytes aligned TRBs. + DWC_XDCI_ENDPOINT ep_handles [DWC_XDCI_MAX_ENDPOINTS]; // EPs + UINT8 default_setup_buffer [DWC_XDCI_SETUP_BUFF_SIZE * 2]; // Unaligned setup buffer + UINT8 *aligned_setup_buffer; // Aligned setup buffer. Aligned to 8-byte boundary + UINT8 misc_buffer [528]; // Unaligned misc buffer + UINT8 *aligned_misc_buffer; // Aligned misc buffer + UINT32 link_state; // Link state + UINT32 hird_val; // HIRD value + USB_DEV_CALLBACK_LIST event_callbacks; + volatile BOOLEAN interrup_processing; +} XDCI_CORE_HANDLE; + +/* DWC XDCI API prototypes */ +EFI_STATUS +EFIAPI +dwc_xdci_core_init ( + IN USB_DEV_CONFIG_PARAMS *config_params, + IN VOID *parent_handle, + IN VOID **core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_deinit ( + IN VOID *core_handle, + IN UINT32 flags + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_register_callback ( + IN VOID *core_handle, + IN USB_DEVICE_EVENT_ID event, + IN USB_DEVICE_CALLBACK_FUNC CallbackFunc + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_unregister_callback ( + IN VOID *core_handle, + IN USB_DEVICE_EVENT_ID event + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_isr_routine ( + IN VOID *core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_isr_routine_timer_based ( + IN VOID *core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_connect ( + IN VOID *core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_disconnect ( + IN VOID *core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_get_speed ( + IN VOID *core_handle, + IN USB_SPEED *speed + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_set_address ( + IN VOID *core_handle, + IN UINT32 address + ); + +EFI_STATUS +EFIAPI +dwc_xdci_core_set_config ( + IN VOID *core_handle, + IN UINT32 config_num + ); + +EFI_STATUS +EFIAPI +dwc_xdci_set_link_state ( + IN VOID *core_handle, + IN USB_DEVICE_SS_LINK_STATE state + ); + +EFI_STATUS +EFIAPI +dwc_xdci_init_ep ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_enable ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_disable ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_stall ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_clear_stall ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_set_nrdy ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep0_receive_setup_pkt ( + IN VOID *core_handle, + IN UINT8 *buffer + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep0_receive_status_pkt ( + IN VOID *core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep0_send_status_pkt ( + IN VOID *core_handle + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_tx_data ( + IN VOID *core_handle, + IN USB_XFER_REQUEST *xfer_req + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_rx_data( + IN VOID *core_handle, + IN USB_XFER_REQUEST *xfer_req + ); + +EFI_STATUS +EFIAPI +dwc_xdci_ep_cancel_transfer ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); + +EFI_STATUS +usb_process_device_reset_det ( + IN XDCI_CORE_HANDLE *core_handle + ); + +EFI_STATUS +usb_process_device_reset_done ( + IN XDCI_CORE_HANDLE *core_handle + ); + +UINT32 +usb_get_physical_ep_num ( + IN UINT32 EndpointNum, + IN USB_EP_DIR EndpointDir + ); + +UINT32 +usb_reg_read ( + IN UINT32 base, + IN UINT32 offset + ); + +VOID +usb_reg_write ( + IN UINT32 base, + IN UINT32 offset, + IN UINT32 val + ); + +EFI_STATUS +usb_xdci_core_flush_ep_fifo ( + IN VOID *core_handle, + IN USB_EP_INFO *ep_info + ); +#endif diff --git a/drivers/dw3/XdciDevice.h b/drivers/dw3/XdciDevice.h new file mode 100644 index 0000000..0080c29 --- /dev/null +++ b/drivers/dw3/XdciDevice.h @@ -0,0 +1,34 @@ +/*++ + + Copyright (c) 1999 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + +#ifndef _USB_DEVICE_H_ +#define _USB_DEVICE_H_ + +/* @USB_DEV_CONFIG_PARAMS: Struct to be filled in with configuration + * parameters and passed to the init routine for device controller + */ +typedef struct { + USB_CONTROLLER_ID ControllerId; // Controller ID of the core + UINT32 BaseAddress; // Base address of the controller registers and on-chip memory + UINT32 Flags; // Initialization flags + USB_SPEED Speed; // Desired USB bus speed + USB_ROLE Role; // Default USB role +} USB_DEV_CONFIG_PARAMS; + +typedef +EFI_STATUS +(EFIAPI *USB_DEVICE_CALLBACK_FUNC) ( + IN USB_DEVICE_CALLBACK_PARAM *Param + ); + +#endif /* _USB_DEVICE_H_ */ diff --git a/drivers/dw3/XdciUtility.c b/drivers/dw3/XdciUtility.c new file mode 100644 index 0000000..72ea29b --- /dev/null +++ b/drivers/dw3/XdciUtility.c @@ -0,0 +1,148 @@ +/*++ + + Copyright (c) 1999 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + +#include +#include + +#include "dw3/XdciUtility.h" +#include "dw3/XdciCommon.h" + +VOID +PrintDeviceDescriptor ( + __attribute__((__unused__)) IN USB_DEVICE_DESCRIPTOR *DevDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Device Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", DevDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", DevDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "BcdUSB : 0x%x\n", DevDesc->BcdUSB)); + DEBUG ((DEBUG_INFO, "DeviceClass : 0x%x\n", DevDesc->DeviceClass)); + DEBUG ((DEBUG_INFO, "DeviceSubClass : 0x%x\n", DevDesc->DeviceSubClass)); + DEBUG ((DEBUG_INFO, "DeviceProtocol : 0x%x\n", DevDesc->DeviceProtocol)); + DEBUG ((DEBUG_INFO, "MaxPacketSize0 : 0x%x\n", DevDesc->MaxPacketSize0)); + DEBUG ((DEBUG_INFO, "IdVendor : 0x%x\n", DevDesc->IdVendor)); + DEBUG ((DEBUG_INFO, "IdProduct : 0x%x\n", DevDesc->IdProduct)); + DEBUG ((DEBUG_INFO, "BcdDevice : 0x%x\n", DevDesc->BcdDevice)); + DEBUG ((DEBUG_INFO, "StrManufacturer : 0x%x\n", DevDesc->StrManufacturer)); + DEBUG ((DEBUG_INFO, "StrProduct : 0x%x\n", DevDesc->StrProduct)); + DEBUG ((DEBUG_INFO, "StrSerialNumber : 0x%x\n", DevDesc->StrSerialNumber)); + DEBUG ((DEBUG_INFO, "NumConfigurations : 0x%x\n", DevDesc->NumConfigurations)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintConfigDescriptor ( + __attribute__((__unused__)) IN EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Configuration Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", ConfigDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", ConfigDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "TotalLength : 0x%x\n", ConfigDesc->TotalLength)); + DEBUG ((DEBUG_INFO, "NumInterfaces : 0x%x\n", ConfigDesc->NumInterfaces)); + DEBUG ((DEBUG_INFO, "ConfigurationValue : 0x%x\n", ConfigDesc->ConfigurationValue)); + DEBUG ((DEBUG_INFO, "Configuration : 0x%x\n", ConfigDesc->Configuration)); + DEBUG ((DEBUG_INFO, "Attributes : 0x%x\n", ConfigDesc->Attributes)); + DEBUG ((DEBUG_INFO, "MaxPower : 0x%x\n", ConfigDesc->MaxPower)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintInterfaceDescriptor ( + __attribute__((__unused__)) IN EFI_USB_INTERFACE_DESCRIPTOR *IfDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Interface Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", IfDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", IfDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "InterfaceNumber : 0x%x\n", IfDesc->InterfaceNumber)); + DEBUG ((DEBUG_INFO, "AlternateSetting : 0x%x\n", IfDesc->AlternateSetting)); + DEBUG ((DEBUG_INFO, "NumEndpoints : 0x%x\n", IfDesc->NumEndpoints)); + DEBUG ((DEBUG_INFO, "InterfaceClass : 0x%x\n", IfDesc->InterfaceClass)); + DEBUG ((DEBUG_INFO, "InterfaceSubClass : 0x%x\n", IfDesc->InterfaceSubClass)); + DEBUG ((DEBUG_INFO, "InterfaceProtocol : 0x%x\n", IfDesc->InterfaceProtocol)); + DEBUG ((DEBUG_INFO, "Interface : 0x%x\n", IfDesc->Interface)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintEpDescriptor ( + __attribute__((__unused__)) IN EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Endpoint Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", EpDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", EpDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "EndpointAddress : 0x%x\n", EpDesc->EndpointAddress)); + DEBUG ((DEBUG_INFO, "Attributes : 0x%x\n", EpDesc->Attributes)); + DEBUG ((DEBUG_INFO, "MaxPacketSize : 0x%x\n", EpDesc->MaxPacketSize)); + DEBUG ((DEBUG_INFO, "Interval : 0x%x\n", EpDesc->Interval)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintEpCompDescriptor ( + __attribute__((__unused__)) IN EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Endpoint Companion Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", EpDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", EpDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "MaxBurst : 0x%x\n", EpDesc->MaxBurst)); + DEBUG ((DEBUG_INFO, "Attributes : 0x%x\n", EpDesc->Attributes)); + DEBUG ((DEBUG_INFO, "BytesPerInterval : 0x%x\n", EpDesc->BytesPerInterval)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintStringDescriptor ( + IN USB_STRING_DESCRIPTOR *StrDesc + ) +{ + if (StrDesc->Length > 2) { + DEBUG ((DEBUG_INFO, "--- String Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", StrDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", StrDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "String : %s\n", (char *) StrDesc->LangID)); + } + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintDeviceRequest ( + __attribute__((__unused__)) IN EFI_USB_DEVICE_REQUEST *DevReq + ) +{ + DEBUG ((DEBUG_INFO, "--- Device Request ---\n")); + DEBUG ((DEBUG_INFO, "RequestType : 0x%x\n", DevReq->RequestType)); + DEBUG ((DEBUG_INFO, "Request : 0x%x\n", DevReq->Request)); + DEBUG ((DEBUG_INFO, "Value : 0x%x\n", DevReq->Value)); + DEBUG ((DEBUG_INFO, "Index : 0x%x\n", DevReq->Index)); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", DevReq->Length)); + DEBUG ((DEBUG_INFO, "\n")); +} + +#ifdef SUPPORT_SUPER_SPEED +VOID +PrintBOSDescriptor ( + IN EFI_USB_BOS_DESCRIPTOR *BosDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- BOS Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", BosDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", BosDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "TotalLength : 0x%x\n", BosDesc->TotalLength)); + DEBUG ((DEBUG_INFO, "NumDeviceCaps : 0x%x\n", BosDesc->NumDeviceCaps)); + DEBUG ((DEBUG_INFO, "\n")); +} +#endif diff --git a/drivers/dw3/XdciUtility.h b/drivers/dw3/XdciUtility.h new file mode 100644 index 0000000..63fb27f --- /dev/null +++ b/drivers/dw3/XdciUtility.h @@ -0,0 +1,61 @@ +/*++ + + Copyright (c) 1999 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + +#ifndef _XDCI_UTILITY_H_ +#define _XDCI_UTILITY_H_ + +#include "dw3/UsbDeviceLib.h" + +VOID +PrintDeviceDescriptor ( + IN USB_DEVICE_DESCRIPTOR *DevDesc + ); + +VOID +PrintConfigDescriptor ( + IN EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc + ); + +VOID +PrintInterfaceDescriptor ( + IN EFI_USB_INTERFACE_DESCRIPTOR *IfDesc + ); + +VOID +PrintEpDescriptor ( + IN EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc + ); + +VOID +PrintEpCompDescriptor ( + IN EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpDesc + ); + +VOID +PrintStringDescriptor ( + IN USB_STRING_DESCRIPTOR *StrDesc + ); + +VOID +PrintDeviceRequest ( + IN EFI_USB_DEVICE_REQUEST *DevReq + ); + +#ifdef SUPPORT_SUPER_SPEED +VOID +PrintBOSDescriptor ( + IN EFI_USB_BOS_DESCRIPTOR *BosDesc + ); +#endif + +#endif diff --git a/drivers/dw3/dw3.c b/drivers/dw3/dw3.c new file mode 100644 index 0000000..fe514c0 --- /dev/null +++ b/drivers/dw3/dw3.c @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dw3/UsbDeviceModeProtocol.h" +#include "dw3/XdciCommon.h" +#include "dw3/XdciDWC.h" +#include "dw3/XdciUtility.h" +#include "dw3/dw3.h" + +USB_DEVICE_DRIVER_OBJ mDrvObj; +USB_DEVICE_IO_REQ mCtrlIoReq = { + // + // IO information containing the buffer and data size + // + { + NULL, + 0, + }, + // + // Note: This object is used for Control Ep transfers only + // therefore the endpoint info must always be NULL + // + { + NULL, + NULL, + } +}; + +/* parent handle: us */ +static void *device_core_ptr = NULL; + +/* initialized by dwc_xdci_core_init */ +static void *core_handle = NULL; + +/* controller config for device role */ +/* register offset */ +#define R_XHCI_MEM_DUAL_ROLE_CFG0 0x80D8 +/* programming value for device */ +#define CFG0_DEVICE_ROLE_CONFIG 0x1310800 + +/* USB XDCI, XHCI PCI IDs */ +#define INTEL_VID 0x8086 + +static USB_DEV_CONFIG_PARAMS config_params = { .ControllerId = USB_ID_DWC_XDCI, + .BaseAddress = 0, + .Flags = 0, + .Speed = USB_SPEED_SUPER, + .Role = USB_ROLE_DEVICE}; + +BOOLEAN mXdciRun = FALSE; + +EFI_STATUS +EFIAPI +UsbdResetEvtHndlr ( + __attribute__((__unused__)) IN USB_DEVICE_CALLBACK_PARAM *Param + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdResetEvtHndlr\n")); + + /* reset device address to 0 */ + Status = dwc_xdci_core_set_address (core_handle, 0x0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdResetHdlr() - Failed to set address in XDCI\n")); + } + + return Status; +} + +EFI_STATUS +EFIAPI +UsbdConnDoneEvtHndlr ( + __attribute__((__unused__)) IN USB_DEVICE_CALLBACK_PARAM *Param + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdConnDoneEvtHndlr\n")); + + /* reset device address to 0 */ + Status = dwc_xdci_core_set_address (core_handle, 0x0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdConnDoneHdlr() - Failed to set address in XDCI\n")); + } + + /* set the device state to attached/connected */ + mDrvObj.State = UsbDevStateAttached; + + return Status; +} + +VOID +UsbdSetEpInfo ( + IN USB_EP_INFO *EpDest, + IN USB_DEVICE_ENDPOINT_INFO *EpSrc + ) +{ + EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc = NULL; + EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpCompDesc = NULL; + + /* start by clearing all data in the destination */ + memset (EpDest, 0, sizeof(USB_EP_INFO)); + EpDesc = EpSrc->EndpointDesc; + EpCompDesc = EpSrc->EndpointCompDesc; + + if (EpDesc != NULL) { + EpDest->ep_num = EpDesc->EndpointAddress & 0x0F; /* Bits 0-3 are ep num */ + EpDest->ep_dir = ((EpDesc->EndpointAddress & USB_ENDPOINT_DIR_IN) > 0) ? UsbEpDirIn : UsbEpDirOut; + EpDest->ep_type = EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK; + EpDest->max_pkt_size = EpDesc->MaxPacketSize; + EpDest->interval = EpDesc->Interval; + } + if (EpCompDesc != NULL) { + EpDest->max_streams = EpCompDesc->Attributes & USB_EP_BULK_BM_ATTR_MASK; + EpDest->burst_size = EpCompDesc->MaxBurst; + EpDest->mult = EpCompDesc->BytesPerInterval; + } + + return; +} + +VOID +EFIAPI +UsbdXferDoneHndlr ( + __attribute__((__unused__)) IN VOID *XdciHndl, + IN USB_XFER_REQUEST *XferReq + ) +{ + EFI_USB_DEVICE_XFER_INFO XferInfo; + + DEBUG ((DEBUG_INFO, "UsbdXferDoneHndlr\n")); + + XferInfo.EndpointNum = (UINT8)XferReq->ep_info.ep_num; + XferInfo.EndpointDir = XferReq->ep_info.ep_dir; + XferInfo.EndpointType = XferReq->ep_info.ep_type; + XferInfo.Buffer = XferReq->xfer_buffer; + XferInfo.Length = XferReq->actual_xfer_len; + + // + // If this is a non-control transfer complete, notify the class driver + // + if (XferInfo.EndpointNum > 0) { + if (mDrvObj.UsbdDevObj->DataCallback != NULL) { + mDrvObj.UsbdDevObj->DataCallback (&XferInfo); + } + } + + return; +} + +EFI_STATUS +UsbdEpTxData ( + IN VOID *XdciHndl, + IN USB_DEVICE_IO_REQ *IoReq + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_XFER_REQUEST TxReq; + + /* set endpoint data */ + UsbdSetEpInfo (&(TxReq.ep_info), &(IoReq->EndpointInfo)); + + /* if this is a control endpoint, set the number and direction */ + if (IoReq->EndpointInfo.EndpointDesc == NULL) { + TxReq.ep_info.ep_num = 0; + TxReq.ep_info.ep_dir = UsbEpDirIn; + } + + /* setup the trasfer request */ + TxReq.xfer_buffer = IoReq->IoInfo.Buffer; + TxReq.xfer_len = IoReq->IoInfo.Length; + TxReq.xfer_done = UsbdXferDoneHndlr; + + DEBUG ((DEBUG_INFO, "TX REQUEST: epNum: 0x%x, epDir: 0x%x, epType: 0x%x, MaxPktSize: 0x%x\n",\ + TxReq.ep_info.ep_num, TxReq.ep_info.ep_dir, TxReq.ep_info.ep_type, TxReq.ep_info.max_pkt_size)); + + Status = dwc_xdci_ep_tx_data (XdciHndl, &TxReq); + + return Status; +} + +EFI_STATUS +UsbdGetConfigDesc ( + IN VOID *Buffer, + IN UINT8 DescIndex, + IN UINT32 ReqLen, + IN UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT8 NumConfigs = 0; + UINT32 ConfigLen = 0; + USB_DEVICE_CONFIG_OBJ *ConfigObj = NULL; + VOID *Descriptor = 0; + UINT32 Length = 0; + + DEBUG ((DEBUG_INFO, "UsbdGetConfigDesc()\n")); + + /* + * For a CONFIGURATION request we send back all descriptors branching out + * from this descriptor including the INTERFACE and ENDPOINT descriptors + */ + + /* Verify the requested configuration exists - check valid index */ + NumConfigs = mDrvObj.UsbdDevObj->DeviceDesc->NumConfigurations; + + if (DescIndex < NumConfigs) { + /* get the configuration object using the index offset */ + ConfigObj = (mDrvObj.UsbdDevObj->ConfigObjs + DescIndex); + /* get the complete configuration buffer block including Interface and Endpoint data */ + Descriptor = ConfigObj->ConfigAll; + /* The config descriptor TotalLength has the full value for all desc buffers */ + ConfigLen = ConfigObj->ConfigDesc->TotalLength; + /* copy the data to the output buffer */ + Length = MIN (ReqLen, ConfigLen); + memcpy (Buffer, Descriptor, Length); + *DataLen = Length; + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdGetConfigDesc() - Invalid Config index: %i\n", DescIndex)); + } + + if (Status == EFI_SUCCESS) { + if (ConfigObj != NULL) { + PrintConfigDescriptor (ConfigObj->ConfigDesc); + } + } + + return Status; +} + +EFI_STATUS +UsbdGetStringDesc ( + VOID *Buffer, + UINT8 DescIndex, + UINT16 LangId, + UINT32 ReqLen, + UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT32 Length = 0; + USB_STRING_DESCRIPTOR *StringDesc; + UINT8 Index = 0; + UINT8 StrLangEntries = 0; + BOOLEAN StrLangFound = FALSE; + + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: Index: 0x%x, LangId: 0x%x, ReqLen: 0x%x\n", DescIndex, LangId, ReqLen)); + + /* index zero of the string table contains the supported language codes */ + if (DescIndex == 0) { + StringDesc = (mDrvObj.UsbdDevObj->StringTable); + Length = MIN (ReqLen, StringDesc->Length); + memcpy (Buffer, StringDesc, Length); + *DataLen = Length; + Status = EFI_SUCCESS; + } else { + + /* + * Verify the requested language ID is supported. String descriptor Zero + * (First entry in the string table) is expected to contain the language list. + * The requested language ID is specified in the Index member of the request. + */ + StringDesc = mDrvObj.UsbdDevObj->StringTable; /* get language string descriptor */ + StrLangEntries = ((StringDesc->Length - 2) >> 1); + DEBUG ((DEBUG_INFO, "StrLangEntries=%x\n", StrLangEntries)); + + DEBUG ((DEBUG_INFO, "Looking LangID: \n")); + + for (Index = 0; Index < StrLangEntries; Index++) { + DEBUG ((DEBUG_INFO, "LangID [%x]= %x\n", Index, StringDesc->LangID [Index])); + + if (StringDesc->LangID [Index] == LangId) { + DEBUG ((DEBUG_INFO, "Found it\n")); + StrLangFound = TRUE; + } + } + + + + /* If we found a matching language, attempt to get the string index requested */ + if (StrLangFound == TRUE) { + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: StrLangFound=Found, DescIndex=%x, StrTblEntries=%x\n", DescIndex, mDrvObj.UsbdDevObj->StrTblEntries)); + + if (DescIndex < mDrvObj.UsbdDevObj->StrTblEntries) { + /* get the string descriptor for the requested index */ + StringDesc = (mDrvObj.UsbdDevObj->StringTable + DescIndex); + + Length = MIN (ReqLen, StringDesc->Length); + DEBUG ((DEBUG_INFO, "ReqLen=%x, StringLength=%x, Length=%x\n", ReqLen, StringDesc->Length, Length)); + + memcpy (Buffer, StringDesc, Length); + *DataLen = Length; + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: Invalid String index in USB_REQ_GET_DESCRIPTOR request\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: Unsupported String Language ID for USB_REQ_GET_DESCRIPTOR request\n")); + } + } + + if (Status == EFI_SUCCESS) { + PrintStringDescriptor (StringDesc); + } + return Status; +} + +EFI_STATUS +UsbdGetConfig ( + VOID *Buffer, + UINT32 ReqLen, + UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdGetConfig()\n")); + + if (ReqLen >= 1) { /* length of data expected must be 1 */ + if (mDrvObj.ActiveConfigObj != NULL) { /* assure we have a config active */ + *DataLen = 1; /* one byte for ConfigurationValue */ + *(UINT8*)Buffer = mDrvObj.ActiveConfigObj->ConfigDesc->ConfigurationValue; + + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdGetConfig() - No active configuration available\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdGetConfig() - Invalid data length\n")); + } + + return Status; +} + +EFI_STATUS +UsbdInitEp ( + IN VOID *XdciHndl, + IN USB_DEVICE_ENDPOINT_INFO *DevEpInfo + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_EP_INFO EpInfo; + + UsbdSetEpInfo (&EpInfo, DevEpInfo); + Status = dwc_xdci_init_ep (XdciHndl, &EpInfo); + + return Status; +} + +EFI_STATUS +UsbdSetAddress ( + UINT8 Address + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdSetAddress: setting address: 0x%x\n", Address)); + + if (Address <= 0x7F) { /* address must not be > 127 */ + mDrvObj.Address = Address; + + /* Configure Address in the XDCI */ + Status = dwc_xdci_core_set_address (core_handle, mDrvObj.Address); + if (!EFI_ERROR (Status)) { + mDrvObj.State = UsbDevStateAddress; + } else { + DEBUG ((DEBUG_INFO, "UsbdSetAddress: Failed to set address in XDCI\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdSetAddress: Invalid address: 0x%x\n", Address)); + } + + return Status; +} + +EFI_STATUS +UsbdGetStatus ( + VOID *Buffer, + UINT8 ReqType, + UINT32 ReqLen, + UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdGetStatus()\n")); + + if (ReqLen >= 2) { /* length of data must be at least 2 bytes */ + switch (ReqType & USB_TARGET_MASK) { + case USB_TARGET_DEVICE: + *DataLen = 2; /* two byte for status */ + *(UINT16*)Buffer = USB_STATUS_SELFPOWERED; + Status = EFI_SUCCESS; + break; + + case USB_TARGET_INTERFACE: + /* No implementation needed at this time */ + break; + + case USB_TARGET_ENDPOINT: + /* No implementation needed at this time */ + /* Should specify if endpoint is halted. Implement as necessary. */ + break; + + case USB_TARGET_OTHER: + /* No implementation needed at this time */ + break; + + default: + break; + } + } else { + DEBUG ((DEBUG_INFO, "UsbdGetStatus() - Invalid data length\n")); + } + + return Status; +} + +EFI_STATUS +UsbdSetConfig ( + UINT8 CfgValue + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT8 numConfigs = 0; + USB_DEVICE_CONFIG_OBJ *pConfigObj = NULL; + USB_DEVICE_INTERFACE_OBJ *pIfObj = NULL; + USB_DEVICE_ENDPOINT_OBJ *pEpObj = NULL; + UINT8 cfgItr = 0; + UINT8 ifItr = 0; + UINT8 epItr = 0; + USB_DEVICE_ENDPOINT_INFO epInfo; + USB_EP_INFO UsbEpInfo; + + DEBUG ((DEBUG_INFO, "UsbdSetConfig()\n")); + + /* Verify the requested configuration exists - check valid index */ + numConfigs = mDrvObj.UsbdDevObj->DeviceDesc->NumConfigurations; + + if (CfgValue != 0) { + /* Search for a matching configuration */ + for (cfgItr = 0; cfgItr < numConfigs; cfgItr++) { + pConfigObj = (mDrvObj.UsbdDevObj->ConfigObjs + cfgItr); + if (pConfigObj->ConfigDesc->ConfigurationValue == CfgValue) { + + /* Set the active configuration object */ + mDrvObj.ActiveConfigObj = pConfigObj; + + /* Find all interface objects for this configuration */ + for (ifItr = 0; ifItr < pConfigObj->ConfigDesc->NumInterfaces; ifItr++) { + pIfObj = (pConfigObj->InterfaceObjs + ifItr); + + /* Configure the Endpoints in the XDCI */ + for (epItr = 0; epItr < pIfObj->InterfaceDesc->NumEndpoints; epItr++) { + pEpObj = (pIfObj->EndpointObjs + epItr); + + epInfo.EndpointDesc = pEpObj->EndpointDesc; + epInfo.EndpointCompDesc = pEpObj->EndpointCompDesc; + + if (UsbdInitEp (core_handle, &epInfo) == EFI_SUCCESS) { + UsbdSetEpInfo(&UsbEpInfo, &epInfo); + if (dwc_xdci_ep_enable (core_handle, &UsbEpInfo) == EFI_SUCCESS) { + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdSetConfig() - Failed to enable endpoint\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdSetConfig() - Failed to initialize endpoint\n")); + } + } + } + + /* Let the class driver know it is configured */ + if (Status == EFI_SUCCESS) { + if (mDrvObj.UsbdDevObj->ConfigCallback != NULL) { + mDrvObj.UsbdDevObj->ConfigCallback (CfgValue); + } + } + + mDrvObj.State = UsbDevStateConfigured; /* we are now configured */ + + break; /* break from config search loop */ + } + } + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetConfig() - Invalid requested configuration value: %i\n", CfgValue)); + } + + return Status; +} + + +EFI_STATUS +UsbdSetupHdlr ( + IN EFI_USB_DEVICE_REQUEST *CtrlRequest + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT8 DescIndex = 0; + USB_DEVICE_DESCRIPTOR *DevDesc = 0; + + /* Initialize the IO object */ + mCtrlIoReq.IoInfo.Length = 0; + + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr start\n")); + PrintDeviceRequest (CtrlRequest); + + /* Handle Standard Device Requests */ + if ((CtrlRequest->RequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { + switch (CtrlRequest->Request) { + case USB_REQ_GET_DESCRIPTOR: + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Host requests get descriptor\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_D_TO_H) { + DescIndex = (CtrlRequest->Value & 0xff); /* low byte is the index requested */ + switch (CtrlRequest->Value >> 8) { /* high byte contains request type */ + case USB_DESC_TYPE_DEVICE: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Device\n")); + DevDesc = mDrvObj.UsbdDevObj->DeviceDesc; + /* copy the data to the output buffer */ + mCtrlIoReq.IoInfo.Length = MIN (CtrlRequest->Length, DevDesc->Length); + memcpy (mCtrlIoReq.IoInfo.Buffer, DevDesc, mCtrlIoReq.IoInfo.Length); + PrintDeviceDescriptor (DevDesc); + break; + + case USB_DESC_TYPE_CONFIG: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Configuration\n")); + Status = UsbdGetConfigDesc ( + mCtrlIoReq.IoInfo.Buffer, + DescIndex, + CtrlRequest->Length, + &(mCtrlIoReq.IoInfo.Length) + ); + break; + + case USB_DESC_TYPE_STRING: + DEBUG ((DEBUG_INFO, "Descriptor tyep: String\n")); + Status = UsbdGetStringDesc ( + mCtrlIoReq.IoInfo.Buffer, + DescIndex, + CtrlRequest->Index, + CtrlRequest->Length, + &(mCtrlIoReq.IoInfo.Length) + ); + break; + +#ifdef SUPPORT_SUPER_SPEED + case USB_DESC_TYPE_BOS: + DEBUG ((DEBUG_INFO, "Descriptor tyep: BOS\n")); + Status = UsbdGetBOSDesc ( + mCtrlIoReq.IoInfo.Buffer, + CtrlRequest->Length, + &(mCtrlIoReq.IoInfo.Length) + ); + break; + + case USB_DESC_TYPE_SS_ENDPOINT_COMPANION: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Endpoint Companion\n")); + break; +#endif + + default: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Unsupported, USB_REQ_GET_DESCRIPTOR request: 0x%x\n", (CtrlRequest->Value >> 8))); + break; + } + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr() - Invalid direction for USB_REQ_GET_DESCRIPTOR request\n")); + } + break; + + case USB_REQ_GET_CONFIG: + DEBUG ((DEBUG_INFO, "USB_REQ_GET_CONFIG\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_D_TO_H) { + Status = UsbdGetConfig (mCtrlIoReq.IoInfo.Buffer, CtrlRequest->Length, &(mCtrlIoReq.IoInfo.Length)); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_GET_CONFIG request\n")); + } + break; + + case USB_REQ_SET_CONFIG: + DEBUG ((DEBUG_INFO, "USB_REQ_SET_CONFIG\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_H_TO_D) { + Status = UsbdSetConfig ((UINT8)CtrlRequest->Value); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_SET_CONFIG request\n")); + } + break; + + case USB_REQ_SET_ADDRESS: + DEBUG ((DEBUG_INFO, "USB_REQ_SET_ADDRESS\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_H_TO_D) { + Status = UsbdSetAddress ((UINT8)CtrlRequest->Value); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_SET_ADDRESS request\n")); + } + break; + + case USB_REQ_GET_STATUS: + DEBUG ((DEBUG_INFO, "USB_REQ_GET_STATUS\n")); + if (CtrlRequest->RequestType & USB_RT_TX_DIR_D_TO_H) { + Status = UsbdGetStatus (mCtrlIoReq.IoInfo.Buffer, CtrlRequest->RequestType, CtrlRequest->Length, &(mCtrlIoReq.IoInfo.Length)); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_GET_STATUS request\n")); + } + break; +#ifdef SUPPORT_SUPER_SPEED + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_SYNCH_FRAME: +#endif + default: + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Unsupported Standard Request: 0x%x\n", CtrlRequest->Request)); + break; + } + } else { /* This is not a Standard request, it specifies Class/Vendor handling */ + /* Forward request to class driver */ + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Class/Vendor Request\n")); + if (mDrvObj.UsbdDevObj->SetupCallback != NULL) { + mDrvObj.UsbdDevObj->SetupCallback (CtrlRequest, &(mCtrlIoReq.IoInfo)); + } + } + + DEBUG ((DEBUG_INFO, "dataLen=%x\n", mCtrlIoReq.IoInfo.Length)); + + /* Transfer data according to request if necessary */ + if (mCtrlIoReq.IoInfo.Length> 0) { + Status = UsbdEpTxData (core_handle, &mCtrlIoReq); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Failed to TX data\n")); + } + } else { + /* If we are not responding with data, send control status */ + Status = dwc_xdci_ep0_send_status_pkt (core_handle); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Failed to Tx Ep0 Status\n")); + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +UsbdSetupEvtHndlr ( + IN USB_DEVICE_CALLBACK_PARAM *Param + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + EFI_USB_DEVICE_REQUEST Req; + + DEBUG ((DEBUG_INFO, "UsbdSetupEvtHndlr\n")); + + /* Fill out request object from the incomming buffer */ + memcpy (&Req, Param->buffer, sizeof(EFI_USB_DEVICE_REQUEST)); + + Status = UsbdSetupHdlr (&Req); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetupEvtHndlr: EFI_DEVICE_ERROR\n")); + } + + return Status; +} + +EFI_STATUS +EFIAPI +UsbdNrdyEvtHndlr ( + __attribute__((__unused__)) IN USB_DEVICE_CALLBACK_PARAM *Param + ) +{ + DEBUG ((DEBUG_INFO, "UsbdNrdyEvtHndlr\n")); + return EFI_SUCCESS; +} + +EFI_STATUS +UsbdRegisterCallbacks ( + IN VOID *XdciHndl + ) +{ + if (dwc_xdci_core_register_callback (XdciHndl, USB_DEVICE_RESET_EVENT, UsbdResetEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + if (dwc_xdci_core_register_callback (XdciHndl, USB_DEVICE_CONNECTION_DONE, UsbdConnDoneEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + if (dwc_xdci_core_register_callback (XdciHndl, USB_DEVICE_SETUP_PKT_RECEIVED, UsbdSetupEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + if (dwc_xdci_core_register_callback (XdciHndl, USB_DEVICE_XFER_NRDY, UsbdNrdyEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + return EFI_SUCCESS; + +UdciRegCallbackError: + return EFI_DEVICE_ERROR; +} + +EFI_STATUS +UsbdEpRxData ( + IN VOID *XdciHndl, + IN USB_DEVICE_IO_REQ *IoReq + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_XFER_REQUEST RxReq; + UINT32 ReqPacket; + + DEBUG ((DEBUG_INFO, "RX REQUEST in: IoReq->IoInfo.Length: 0x%x\n", IoReq->IoInfo.Length)); + DEBUG ((DEBUG_INFO, "RX REQUEST in: MaxPacketSize: 0x%x\n", IoReq->EndpointInfo.EndpointDesc->MaxPacketSize)); + + if (IoReq->EndpointInfo.EndpointDesc->MaxPacketSize == 0) { + return EFI_DEVICE_ERROR; + } + + /* set endpoint data */ + UsbdSetEpInfo (&(RxReq.ep_info), &(IoReq->EndpointInfo)); + + /* setup the trasfer request */ + RxReq.xfer_buffer = IoReq->IoInfo.Buffer; + + // + // Transfer length should be multiple of USB packet size. + // + ReqPacket = IoReq->IoInfo.Length / IoReq->EndpointInfo.EndpointDesc->MaxPacketSize; + ReqPacket = ((IoReq->IoInfo.Length % IoReq->EndpointInfo.EndpointDesc->MaxPacketSize) == 0)? ReqPacket : ReqPacket + 1; + RxReq.xfer_len = ReqPacket * IoReq->EndpointInfo.EndpointDesc->MaxPacketSize; + + RxReq.xfer_done = UsbdXferDoneHndlr; + + DEBUG ((DEBUG_INFO, "RX REQUEST: epNum: 0x%x, epDir: 0x%x, epType: 0x%x\n",\ + RxReq.ep_info.ep_num, RxReq.ep_info.ep_dir, RxReq.ep_info.ep_type)); + DEBUG ((DEBUG_INFO, "RX REQUEST send: xfer_len: 0x%x\n", RxReq.xfer_len)); + + Status = dwc_xdci_ep_rx_data(XdciHndl, &RxReq); + + return Status; +} + +static EFIAPI EFI_STATUS +_usb_init_xdci(EFI_USB_DEVICE_MODE_PROTOCOL *This) +{ + EFI_STATUS status; + uint32_t addr, pci_command; + pcidev_t pci_dev; + +#if defined(FB_SET_USB_DEVICE_MODE) + /*need to set device mode in fastboot,p2sb pci is hide, access base address directly*/ + uint32_t value; + + value = *(uint32_t *)(P2SB_BASE_ADDR|USB_DAP_COMM_CTRL_REG_OFFSET); + *(uint32_t *)(P2SB_BASE_ADDR|USB_DAP_COMM_CTRL_REG_OFFSET) = value | 0x01000000; + value = *(uint32_t *)(P2SB_BASE_ADDR|USB_DAP_USB2_CTRL0_REG_OFFSET); + *(uint32_t *)(P2SB_BASE_ADDR|USB_DAP_USB2_CTRL0_REG_OFFSET) = value | 0x00000160; +#endif + /* actually not used, could stay NULL */ + device_core_ptr = (void *) This; + + /* get xDCI base address */ + pci_find_device(INTEL_VID, XDCI_PID, &pci_dev); + + ewdbg("PCI xDCI [%x:%x] %d.%d.%d", INTEL_VID, XDCI_PID, + PCI_BUS(pci_dev), PCI_SLOT(pci_dev), PCI_FUNC(pci_dev)); + + addr = pci_read_config32(pci_dev, PCI_BASE_ADDRESS_0); + addr = addr & ~0xf; + + config_params.BaseAddress = addr; + + /* configure xDCI as a system bus master */ + pci_command = pci_read_config32(pci_dev, PCI_COMMAND); + pci_command |= PCI_COMMAND_MASTER; + pci_write_config32(pci_dev, PCI_COMMAND, pci_command); + + /* get xHCI base address */ + pci_find_device(INTEL_VID, XHCI_PID, &pci_dev); + + ewdbg("PCI xHCI [%x:%x] %d.%d.%d", INTEL_VID, XHCI_PID, + PCI_BUS(pci_dev), PCI_SLOT(pci_dev), PCI_FUNC(pci_dev)); + + addr = pci_read_config32(pci_dev, PCI_BASE_ADDRESS_0); + addr = addr & ~0xf; + + /* enable xHCI bus master and I/O access */ + pci_command = pci_read_config32(pci_dev, PCI_COMMAND); + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config32(pci_dev, PCI_COMMAND, pci_command); + + /* configure device role */ + usb_reg_write(addr, R_XHCI_MEM_DUAL_ROLE_CFG0, CFG0_DEVICE_ROLE_CONFIG); + + /* disable xHCI bus master and I/O access */ + pci_command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + pci_write_config32(pci_dev, PCI_COMMAND, pci_command); + + /* init device driver */ + status = dwc_xdci_core_init(&config_params, device_core_ptr, &core_handle); + if (status) + return EFI_DEVICE_ERROR; + + status = UsbdRegisterCallbacks(core_handle); + if (status) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +_usb_connect(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This) +{ + EFI_STATUS status; + + status = dwc_xdci_core_connect(core_handle); + + if (status) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +_usb_disconnect(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This) +{ + EFI_STATUS status = EFI_DEVICE_ERROR; + + if (dwc_xdci_core_disconnect(core_handle) == EFI_SUCCESS) { + mDrvObj.State = UsbDevStateInit; + status = EFI_SUCCESS; + } + + return status; +} + +static EFIAPI EFI_STATUS +_usb_ep_tx_data(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This, + USB_DEVICE_IO_REQ *IoRequest) +{ + EFI_STATUS Status; + + Status = UsbdEpTxData (core_handle, IoRequest); + return Status; +} + +static EFIAPI EFI_STATUS +_usb_ep_rx_data(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This, + USB_DEVICE_IO_REQ *IoRequest) +{ + EFI_STATUS Status; + + Status = UsbdEpRxData (core_handle, IoRequest); + return Status; +} + +static EFIAPI EFI_STATUS +_usb_bind(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This, + USB_DEVICE_OBJ *UsbdDevObj) +{ + EFI_STATUS Status = EFI_SUCCESS; + + ewdbg("USB bind:"); + ewdbg("IdVendor : 0x%x", UsbdDevObj->DeviceDesc->IdVendor); + ewdbg("IdProduct : 0x%x", UsbdDevObj->DeviceDesc->IdProduct); + + /* allocate Tx buffer */ + mCtrlIoReq.IoInfo.Buffer = calloc (1, USB_EPO_MAX_PKT_SIZE_ALL); + if (mCtrlIoReq.IoInfo.Buffer != NULL) { + mDrvObj.UsbdDevObj = UsbdDevObj; + mDrvObj.ActiveConfigObj = NULL; + mDrvObj.Address = 0; + mDrvObj.State = UsbDevStateInit; + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceBind() - Failed to allocate IO Buffer\n")); + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +static EFIAPI EFI_STATUS +_usb_unbind(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This) +{ + mDrvObj.UsbdDevObj = NULL; + mDrvObj.ActiveConfigObj = NULL; + mDrvObj.Address = 0; + mDrvObj.State = UsbDevStateOff; + + /* release allocated buffer data */ + if (mCtrlIoReq.IoInfo.Buffer) { + free (mCtrlIoReq.IoInfo.Buffer); + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +_usb_stop(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This) +{ + mXdciRun = FALSE; + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +_usb_run(__attribute__((__unused__)) EFI_USB_DEVICE_MODE_PROTOCOL *This, + UINT32 TimeoutMs) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + /* TODO: check initialized */ + + mXdciRun = TRUE; + Status = EFI_SUCCESS; + + while (TRUE) { + if (dwc_xdci_core_isr_routine(core_handle) != EFI_SUCCESS) { + DEBUG ((DEBUG_INFO, "UsbDeviceRun() - Failed to execute event ISR\n")); + } + + /* check for timeout */ + if (TimeoutMs == 0) { + return EFI_TIMEOUT; + } + udelay(50); + TimeoutMs--; + } + + return Status; +} + +static EFI_USB_DEVICE_MODE_PROTOCOL usb_struct = { + .InitXdci = _usb_init_xdci, + .Connect = _usb_connect, + .DisConnect = _usb_disconnect, + .EpTxData = _usb_ep_tx_data, + .EpRxData = _usb_ep_rx_data, + .Bind = _usb_bind, + .UnBind = _usb_unbind, + .Run = _usb_run, + .Stop = _usb_stop +}; + +static EFI_HANDLE dw3_handle; +static EFI_GUID usb_guid = EFI_USB_DEVICE_MODE_PROTOCOL_GUID; + +static EFI_STATUS dw3_init(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + if (dw3_handle) + return EFI_ALREADY_STARTED; + + return uefi_call_wrapper(st->BootServices->InstallProtocolInterface, 4, + &dw3_handle, &usb_guid, + EFI_NATIVE_INTERFACE, &usb_struct); +} + +static EFI_STATUS dw3_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!dw3_handle) + return EFI_INVALID_PARAMETER; + + + ret = uefi_call_wrapper(st->BootServices->UninstallProtocolInterface, 3, + dw3_handle, &usb_guid, &usb_struct); + if (!EFI_ERROR(ret)) + dw3_handle = NULL; + + return ret; +} + +ewdrv_t dw3_drv = { + .name = "dw3", + .description = "XDCI driver", + .init = dw3_init, + .exit = dw3_exit +}; diff --git a/drivers/dw3/dw3.h b/drivers/dw3/dw3.h new file mode 100644 index 0000000..5bc25df --- /dev/null +++ b/drivers/dw3/dw3.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _DW3_H_ +#define _DW3_H_ + +#include + +extern ewdrv_t dw3_drv; + +#endif /* _DW3_H_ */ diff --git a/drivers/gop/gop.c b/drivers/gop/gop.c new file mode 100644 index 0000000..4710b54 --- /dev/null +++ b/drivers/gop/gop.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gop.h" + +typedef struct gop { + EFI_GRAPHICS_OUTPUT_PROTOCOL prot; + pcidev_t dev; + uint8_t *fb; + size_t pipe; +} gop_t; + +static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +static EFI_HANDLE gop_handle; +static gop_t *gop; + +static EFI_GRAPHICS_OUTPUT_MODE_INFORMATION info = { + .Version = 1, + .PixelFormat = PixelRedGreenBlueReserved8BitPerColor +}; + +static EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE mode = { + .MaxMode = 1, + .Info = &info, + .SizeOfInfo = sizeof(info) +}; + +static EFIAPI EFI_STATUS +query_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + UINT32 ModeNumber, + UINTN *SizeOfInfo, + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info) +{ + if (!This || ModeNumber != 0 || !SizeOfInfo || !Info) + return EFI_INVALID_PARAMETER; + + *SizeOfInfo = sizeof(info); + *Info = &info; + + return EFI_SUCCESS; +} + +#define PIPE_NUMBER 3 +#define PIPE_OFFSET 0x1000 +#define PLANE_SIZE 0x70190 +#define PLANE_SURF 0x7019C +#define PLANE_CTL 0x70180 +#define PLANE_CTL_ORDER_RGBX (1 << 20) + +/* + * It assumes that the BIOS (ABL) configured the monitors and setup + * Surface 1 of PIPE A appropriately. The two following function are + * reading back register set by the BIOS to determine the surface + * resolution and surface address. + * + * Registers information are available on 01.org in: + * - intel-gfx-prm-osrc-skl-vol02c-commandreference-registers-part1.pdf + * - intel-gfx-prm-osrc-skl-vol02c-commandreference-registers-part2.pdf + */ +static void get_resolution(pcidev_t dev, UINT32 *width, UINT32 *height, + size_t *pipe) +{ + uint32_t mmio, size; + size_t i; + + mmio = pci_read_config32(dev, 0x10) & ~0xf; + for (i = 0; i < PIPE_NUMBER; i++) { + uint32_t offset = i * PIPE_OFFSET; + size = read32((void *)mmio + PLANE_SIZE + offset); + if (!size) + continue; + *pipe = i; + *width = (size & 0xffff) + 1; + *height = (size >> 16) + 1; + return; + } +} + +static uint8_t *get_framebuffer(pcidev_t dev, size_t pipe) +{ + uint32_t mmio, gmaddr, surf; + + mmio = pci_read_config32(dev, 0x10) & ~0xf; + surf = read32((void *)(mmio + PLANE_SURF + pipe * PIPE_OFFSET)); + gmaddr = pci_read_config32(dev, 0x18) & ~0xf; + + return (uint8_t *)(gmaddr + surf); +} + +static void set_efi_color_order(pcidev_t dev) +{ + uint32_t mmio, ctl, surf; + size_t i; + + mmio = pci_read_config32(dev, 0x10) & ~0xf; + for (i = 0; i < PIPE_NUMBER; i++) { + uint32_t offset = i * PIPE_OFFSET; + ctl = read32((void *)(mmio + PLANE_CTL + offset)); + if (ctl & PLANE_CTL_ORDER_RGBX) { + write32((void *)(mmio + PLANE_CTL + offset), + ctl & ~PLANE_CTL_ORDER_RGBX); + surf = read32((void *)(mmio + PLANE_SURF + offset)); + write32((void *)(mmio + PLANE_SURF + offset), surf); + } + } +} + +static EFIAPI EFI_STATUS +set_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + UINT32 ModeNumber) +{ + if (!This || (gop_t *)This != gop || ModeNumber != 0) + return EFI_INVALID_PARAMETER; + + gop->fb = get_framebuffer(gop->dev, gop->pipe); + set_efi_color_order(gop->dev); + return gop->fb ? EFI_SUCCESS : EFI_DEVICE_ERROR; +} + +static void fill_buffer(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, + UINTN destx, + UINTN desty, + UINTN width, + UINTN height) +{ + size_t x, y; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *data; + + data = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)gop->fb; + for (y = desty; y < desty + height; y++) { + UINTN cur_y = y * info.HorizontalResolution; + for (x = destx; x < destx + width; x++) + data[cur_y + x] = *color; + } +} + +static void copy_to_buffer(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buffer, + UINTN srcx, + UINTN srcy, + UINTN destx, + UINTN desty, + UINTN width, + UINTN height) +{ + size_t i, y, sy; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *data, *dst, *src; + + data = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)gop->fb; + for (y = desty, sy = srcy; y < desty + height; y++, sy++) { + dst = &data[y * info.HorizontalResolution + destx]; + src = &buffer[sy * width + srcx]; + for (i = 0; i < width; i++) + dst[i] = src[i]; + } +} + +static EFIAPI EFI_STATUS +blt(EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + UINTN SourceX, + UINTN SourceY, + UINTN DestinationX, + UINTN DestinationY, + UINTN Width, + UINTN Height, + __attribute__((__unused__)) UINTN Delta) +{ + if (!This || (gop_t *)This != gop || !BltBuffer) + return EFI_INVALID_PARAMETER; + + if (DestinationX + Width > info.HorizontalResolution || + DestinationY + Height > info.VerticalResolution) + return EFI_INVALID_PARAMETER; + + if (!gop->fb) + return EFI_NOT_STARTED; + + switch (BltOperation) { + case EfiBltVideoFill: + fill_buffer(BltBuffer, DestinationX, DestinationY, + Width, Height); + break; + + case EfiBltBufferToVideo: + copy_to_buffer(BltBuffer, SourceX, SourceY, + DestinationX, DestinationY, + Width, Height); + break; + + default: + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +static struct supported_device { + u16 vid; + u16 did; +} SUPPORTED_DEVICES[] ={ + { .vid = 0x8086, .did = VGA_PID }, + { .vid = 0x8086, .did = VGA_PID2 }, +}; + +static EFI_STATUS gop_init(EFI_SYSTEM_TABLE *st) +{ + static gop_t gop_default = { + .prot = { + .QueryMode = query_mode, + .SetMode = set_mode, + .Blt = blt, + .Mode = &mode + } + }; + pcidev_t pci_dev = 0; + size_t i; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (gop_handle) + return EFI_ALREADY_STARTED; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_DEVICES); i++) + if (pci_find_device(SUPPORTED_DEVICES[i].vid, + SUPPORTED_DEVICES[i].did, + &pci_dev)) + break; + + if (!pci_dev) + return EFI_UNSUPPORTED; + + gop_default.dev = pci_dev; + get_resolution(pci_dev, &info.HorizontalResolution, + &info.VerticalResolution, + &gop_default.pipe); + + /* If the BIOS has not programmed any surface, silently + disable the Graphical Output Protocol. */ + if (!info.HorizontalResolution || !info.VerticalResolution) + return EFI_SUCCESS; + + return interface_init(st, &gop_guid, &gop_handle, + &gop_default, sizeof(gop_default), + (void **)&gop); +} + +static EFI_STATUS gop_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!gop_handle) + return EFI_SUCCESS; + + ret = interface_free(st, &gop_guid, gop_handle); + if (EFI_ERROR(ret)) + return ret; + + gop = NULL; + gop_handle = NULL; + + return EFI_SUCCESS; +} + +ewdrv_t gop_drv = { + .name = "gop", + .description = "Graphics Output Protocol", + .init = gop_init, + .exit = gop_exit +}; diff --git a/drivers/gop/gop.h b/drivers/gop/gop.h new file mode 100644 index 0000000..3ee99b2 --- /dev/null +++ b/drivers/gop/gop.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _GOP_H_ +#define _GOP_H_ + +#include + +extern ewdrv_t gop_drv; + +#endif /* _GOP_H_ */ diff --git a/drivers/heci/heci.c b/drivers/heci/heci.c new file mode 100644 index 0000000..7117310 --- /dev/null +++ b/drivers/heci/heci.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "ewlog.h" +#include "heci_impl.h" +#include "heci_protocol.h" + +#define UNUSED_PARAM __attribute__((__unused__)) + +#define CPMS 19200 +#define HPET_BASE_ADDRESS 0xFED00000 +#define ClockCycles() read32((void *)(HPET_BASE_ADDRESS + 0xf0)) + +static void init_timer(void) +{ + uint32_t reg; + /* Clear HPET Timer 0 Lower and Upper Comparator Value. */ + write32((void *)(HPET_BASE_ADDRESS + 0x108), 0); + write32((void *)(HPET_BASE_ADDRESS + 0x10c), 0); + + /* Enable HPET main counter. */ + reg = read32((void *)(HPET_BASE_ADDRESS + 0x10)); + write32((void *)(HPET_BASE_ADDRESS + 0x10c), reg | 1); +} + +static int wait_event(uint32_t timeout, int (*fun)(uint32_t), uint32_t arg) +{ + init_timer(); + uint32_t t0 = ClockCycles(); + uint32_t elapsed; + int res; + + timeout *= CPMS; + for (;;) { + if (fun != NULL) { + res = fun(arg); + if (res > 0) + return res; + } + + if (!timeout) + continue; + + elapsed = ClockCycles() - t0; + if (elapsed >= timeout) + return -1; + } + + return 0; +} + +static uint8_t heci_pci_read8(uint32_t reg) +{ + return (uint8_t)read8((void *)(HECI_PCI_DEV + reg)); +} + +static uint32_t heci_pci_read32(uint32_t reg) +{ + return read32((void *)(HECI_PCI_DEV + reg)); +} + +static void heci_pci_write32(uint32_t reg, uint32_t val) +{ + write32((void *)(HECI_PCI_DEV + reg), val); +} + +static void heci_pci_set16(uint32_t reg, uint32_t val) +{ + uint16_t v; + + v = read16((void *)(HECI_PCI_DEV + reg)); + write16((void *)(HECI_PCI_DEV + reg), v | val); +} + +static uint32_t heci_reg_read(uint32_t base, uint32_t offset) +{ + return read32((void *)(UINTN)(base + offset)); +} + +static void heci_reg_write(uint32_t base, uint32_t offset, uint32_t val) +{ + write32((void *)(UINTN)(base + offset), val); +} + +static int is_dev_ready(uint32_t base) +{ + DEV_CTRL_REG reg; + + reg.data = heci_reg_read(base, SEC_CSR_HA); + if (reg.bit.SEC_RDY_HRA == 0) + return 0; + + return 1; +} + +/* wait for dev ready */ +static int is_dev_data_ready(uint32_t base) +{ + DEV_CTRL_REG reg; + + reg.data = heci_reg_read(base, SEC_CSR_HA); + if (reg.bit.SEC_CBRP_HRA == reg.bit.SEC_CBWP_HRA) + return 0; + + return 1; +} + +static int is_interrupt_raised(uint32_t base) +{ + HOST_CTRL_REG reg; + + reg.data = heci_reg_read(base, H_CSR); + if (reg.bit.H_IS == 0) + return 0; + + return 1; +} + +static int is_host_ready(uint32_t base) +{ + HOST_CTRL_REG reg; + + reg.data = heci_reg_read(base, H_CSR); + if (reg.bit.H_RDY == 1) + return 0; + + return 1; +} + +/* + * Verify that the HECI cmd and MBAR regs in its PCI cfg space are setup + * properly and that the local mHeciContext variable matches this info. + */ +static uint32_t heci_get_base_addr(void) +{ + uint32_t u32HeciBase; + uint32_t val; + uint32_t mask; + + /* Read HECI_MBAR in case it has changed */ + val = heci_pci_read32(HECI_MBAR0) & 0xFFFFFFF0; + + heci_pci_write32(HECI_MBAR0, 0xFFFFFFFF); + u32HeciBase = heci_pci_read32(HECI_MBAR0) & 0xFFFFFFF0; + heci_pci_write32(HECI_MBAR0, val); + u32HeciBase = heci_pci_read32(HECI_MBAR0) & 0xFFFFFFF0; + ewdbg("HeciMemBase=%x\n", u32HeciBase); + + /* Check if HECI_MBAR is disabled */ + mask = (EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER); + if ((heci_pci_read8(PCI_COMMAND_OFFSET) & mask) != mask) + heci_pci_set16(PCI_COMMAND_OFFSET, mask | EFI_PCI_COMMAND_SERR); + + return u32HeciBase; +} + + +/* + * Send HECI message implement + */ +static int heci_send_impl(uint32_t u32HeciBase, uint32_t *Message, uint32_t Length, uint8_t HostAddress, uint8_t DevAddr) +{ + uint32_t LeftSize; + uint32_t MaxBuffer; + uint32_t WriteSize; + uint32_t Size; + uint32_t Index; + HECI_MSG_HDR head; + HOST_CTRL_REG hcr; + + if (Message == NULL || Length == 0) + return 1; + + ewdbg("heci_send Start\n"); + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + MaxBuffer = hcr.bit.H_CBD; + + /* The first DWORD used for send MessageHeader, so useable Buffer Size + * should be MaxBuffer -1; + */ + MaxBuffer -= 1; + LeftSize = (Length + 3)/4; + WriteSize = 0; + hcr.bit.H_RDY = 1; + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + while (LeftSize > 0) { + ewdbg("Wait DEV, CSR %x\n", heci_reg_read(u32HeciBase, SEC_CSR_HA)); + + if (wait_event(HECI_EVENT_TIMEOUT, is_dev_ready, u32HeciBase) < 0) { + ewerr("Timeout waiting heci device\n"); + return 1; + } + + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + hcr.bit.H_RDY = 1; + hcr.bit.H_IE = 0; + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + Size = (LeftSize > MaxBuffer) ? MaxBuffer : LeftSize; + + LeftSize -= Size; + + /* Prepare message header */ + head.data = 0; + head.bit.sec_address = DevAddr; + head.bit.host_address = HostAddress; + head.bit.message_complete = (LeftSize > 0) ? 0 : 1; + if (LeftSize > 0) + head.bit.length = Size * sizeof(uint32_t); + else + head.bit.length = Length - WriteSize * sizeof(uint32_t); + + ewdbg("heci Message Header: %08x\n", head.data); + heci_reg_write(u32HeciBase, H_CB_WW, head.data); + for (Index = 0; Index < Size; Index++) { + heci_reg_write(u32HeciBase, H_CB_WW, Message[Index + WriteSize]); + } + + /* Send the Interrupt; */ + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + hcr.bit.H_IS = 1; + hcr.bit.H_RDY = 1; + hcr.bit.H_IE = 0; + hcr.bit.H_IG = 1; + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + + WriteSize += Size; + if (LeftSize > 0) { + ewdbg("HostControlReg %x\n", heci_reg_read(u32HeciBase, SEC_CSR_HA)); + + if (wait_event(HECI_EVENT_TIMEOUT, is_interrupt_raised, u32HeciBase)) { + ewerr("Timeout waiting interrupt\n"); + return 1; + } + } + } + ewdbg("heci_send End\n"); + return 0; +} + +static int heci_send(uint32_t *Message, uint32_t Length, uint8_t HostAddress, uint8_t DevAddr) +{ + int ret; + uint32_t u32HeciBase; + + u32HeciBase = heci_get_base_addr(); + if (u32HeciBase == 0) + return 1; + + ret = heci_send_impl(u32HeciBase, Message, Length, HostAddress, DevAddr); + + return ret; +} + +/* + * Receive HECI message + */ +static int heci_receive_impl(uint32_t u32HeciBase, uint32_t *Message, uint32_t *Length) +{ + uint32_t ReadSize = 0; + uint32_t Index; + uint32_t BufSize = 0; + uint32_t value; + HECI_MSG_HDR head; + + HOST_CTRL_REG hcr; + + ewdbg("heci_receive Start\n"); + + if (Length != NULL) + BufSize = *Length; + while (1) { + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + hcr.bit.H_RDY = 1; + hcr.bit.H_IE = 0; + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + ewdbg("Disable Interrupt, HCR: %08x\n", heci_reg_read(u32HeciBase, H_CSR)); + + if (wait_event(HECI_EVENT_TIMEOUT, is_dev_data_ready, u32HeciBase) < 0) + goto rx_failed; + + head.data = heci_reg_read(u32HeciBase, SEC_CB_RW); + ewdbg("Get Message Header: %08x\n", head.data); + for (Index = 0; Index < (uint32_t)((head.bit.length + 3)/4); Index++) { + if (wait_event(HECI_EVENT_TIMEOUT, is_dev_data_ready, u32HeciBase) < 0) + goto rx_failed; + + value = heci_reg_read(u32HeciBase, SEC_CB_RW); + ewdbg("heci data[%x] = %08x\n", Index, value); + if (Message != NULL && (BufSize == 0 || BufSize >= (uint32_t)(Index * 4))) + Message[Index + ReadSize] = value; + } + + if (Length != NULL) { + if ((uint32_t)(Index * 4) > head.bit.length) + *Length = head.bit.length; + else + *Length = (uint32_t)(Index * 4); + } + + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + hcr.bit.H_IS = 1; + hcr.bit.H_RDY = 1; + hcr.bit.H_IE = 0; + hcr.bit.H_IG = 1; + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + if (head.bit.message_complete == 1) + break; + } + ewdbg("heci_receive End\n"); + return 0; + +rx_failed: + ewerr("Timeout during data recv\n"); + return 1; +} + +static int heci_receive(uint32_t *Message, uint32_t *Length) +{ + int ret; + + uint32_t u32HeciBase; + + u32HeciBase = heci_get_base_addr(); + if (u32HeciBase == 0) + return 1; + + ret = heci_receive_impl(u32HeciBase, Message, Length); + return ret; +} + +/* + * Reset HECI interface + */ +static int heci_reset_interface(void) +{ + HOST_CTRL_REG hcr; + uint32_t u32HeciBase; + + u32HeciBase = heci_get_base_addr(); + if (u32HeciBase == 0) + return 1; + + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + hcr.bit.H_IG = 1; + hcr.bit.H_IS = 1; + hcr.bit.H_RST = 1; + ewdbg("Assert HECI interface reset\n"); + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + + /* Make sure that the reset started */ + if (wait_event(HECI_RESET_TIMEOUT, is_host_ready, u32HeciBase) < 0) { + ewerr("Timeout waiting reset\n"); + return 1; + } + + /* Wait for SEC to perform reset */ + if (wait_event(HECI_RESET_TIMEOUT, is_dev_ready, u32HeciBase) < 0) { + ewerr("Timeout waiting dev ready\n"); + return 1; + } + + /* Make sure IS has been signaled on the HOST side */ + if (wait_event(HECI_RESET_TIMEOUT, is_interrupt_raised, u32HeciBase) < 0) { + ewerr("Timeout waiting interrupt\n"); + return 1; + } + + hcr.data = heci_reg_read(u32HeciBase, H_CSR); + hcr.bit.H_RST = 0; + hcr.bit.H_IG = 1; + hcr.bit.H_RDY = 1; + /* Enable host side interface */ + heci_reg_write(u32HeciBase, H_CSR, hcr.data); + + ewdbg("Reset HECI interface done\n"); + return 0; +} + +/* + * Reset HECI Interface + */ +static int heci_issue_reset(_Bool *reset_status) +{ + uint32_t status; + uint32_t u32HeciBase; + DEV_CTRL_REG DevCtrlReg; + + u32HeciBase = heci_get_base_addr(); + if (u32HeciBase == 0) + return -1; + + if (reset_status != NULL) + *reset_status = 0; + + DevCtrlReg.data = heci_reg_read(u32HeciBase, SEC_CSR_HA); + if (DevCtrlReg.bit.SEC_RST_HRA == 1) { + status = heci_reset_interface(); + if (status) { + ewerr("Reset HECI failed: %d\n", status); + } else { + if (reset_status != NULL) + *reset_status = 1; + } + } + + return 0; +} + +static int heci_reset(void) +{ + return heci_issue_reset(NULL); +} + +static EFI_STATUS EFIAPI HeciSendwACK( + uint32_t *Message, + uint32_t Length, + uint32_t *RecLength, + uint8_t HostAddress, + uint8_t SECAddress + ) +{ + int status = 0; + uint32_t u32HeciBase; + + u32HeciBase = heci_get_base_addr(); + if (u32HeciBase == 0) + return EFI_DEVICE_ERROR; + + heci_reset_interface(); + + /* Send the message */ + status = heci_send_impl(u32HeciBase, Message, Length, HostAddress, SECAddress); + if (status) + return EFI_DEVICE_ERROR; + + /* Wait for ACK message */ + status = heci_receive_impl(u32HeciBase, Message, RecLength); + if (status) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI HeciSendMsg( + uint32_t *Message, + uint32_t Length, + uint8_t HostAddress, + uint8_t SECAddress) +{ + int status = 0; + + status = heci_send(Message, Length, HostAddress, SECAddress); + if (status) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI HeciReadMsg( + UNUSED_PARAM uint32_t Blocking, + uint32_t *MessageBody, + uint32_t *Length) +{ + int status = 0; + + status = heci_receive(MessageBody, Length); + if (status) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI HeciResetHeci(VOID) +{ + int status = 0; + + status = heci_reset(); + if (status) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + + +static EFI_STATUS EFIAPI HeciInitHeci(VOID) +{ + return HeciResetHeci(); +} + +static EFI_STATUS EFIAPI HeciReInitHeci(VOID) +{ + return HeciResetHeci(); +} + +static EFI_STATUS EFIAPI HeciGetSeCMode(uint32_t *Mode) +{ + uint32_t reg; + uint32_t mode; + + reg = heci_pci_read32(HECI_SEC_FW_STS0); + /* bit 16:19 - Management Engine Current Operation Mode */ + mode = (reg >> 16) & 0x0f; + *Mode = mode; + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI HeciSeCResetWait(UNUSED_PARAM uint32_t Delay) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI HeciGetSeCStatus(UNUSED_PARAM uint32_t *Status) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI HeciDisableSeCPG(VOID) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI HeciEnableSeCPG(VOID) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI HeciSubmitCommand( + UNUSED_PARAM EFI_HECI_PROTOCOL *This, + UNUSED_PARAM uint32_t InputParameterBlockSize, + UNUSED_PARAM uint8_t *InputParameterBlock, + UNUSED_PARAM uint32_t OutputParameterBlockSize, + UNUSED_PARAM uint8_t *OutputParameterBlock + ) +{ + return EFI_UNSUPPORTED; +} + + +static EFI_GUID heci_guid = HECI_PROTOCOL_GUID; +static EFI_HANDLE handle; + +static EFI_STATUS heci_init(EFI_SYSTEM_TABLE * st) +{ + static EFI_HECI_PROTOCOL heci_default = { + .SendwACK = HeciSendwACK, + .ReadMsg = HeciReadMsg, + .SendMsg = HeciSendMsg, + .ResetHeci = HeciResetHeci, + .InitHeci = HeciInitHeci, + .SeCResetWait = HeciSeCResetWait, + .ReInitHeci = HeciReInitHeci, + .GetSeCStatus = HeciGetSeCStatus, + .GetSeCMode = HeciGetSeCMode, + .DisableSeCPG = HeciDisableSeCPG, + .EnableSeCPG = HeciEnableSeCPG, + .HeciSubmitCommand = HeciSubmitCommand, + }; + EFI_HECI_PROTOCOL *heci_drv; + + return interface_init(st, &heci_guid, &handle, + &heci_default, sizeof(heci_default), + (void **)&heci_drv); +} + +static EFI_STATUS heci_exit(EFI_SYSTEM_TABLE * st) +{ + return interface_free(st, &heci_guid, handle); +} + +ewdrv_t heci_drv = { + .name = "heci_driver", + .description = "heci_driver", + .init = heci_init, + .exit = heci_exit +}; + + diff --git a/drivers/heci/heci.h b/drivers/heci/heci.h new file mode 100644 index 0000000..8ffd987 --- /dev/null +++ b/drivers/heci/heci.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef __HECI_H__ +#define __HECI_H__ + +#include + +#include + +extern ewdrv_t heci_drv; + +#endif /* __HECI_H__ */ + + diff --git a/drivers/heci/heci_impl.h b/drivers/heci/heci_impl.h new file mode 100644 index 0000000..c28ab94 --- /dev/null +++ b/drivers/heci/heci_impl.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + + +#ifndef __HECI_IMPL__ +#define __HECI_IMPL__ + +#include +#include + +/* Rom Base Address in Bridge, defined in PCI-to-PCI Bridge Architecure Specification */ +#define PCI_COMMAND_OFFSET 0x04 + +/* PCI devices, addressed by (bus, device, function + register offset) */ +#define EC_BASE 0xE0000000 +#define PCI_BDF(b, d, f) ((void *)(EC_BASE | ((b) << 20) | ((d) << 15) | ((f) << 12))) + +#define HECI_PCI_DEV PCI_BDF(HECI_BUS, HECI_DEVICE_NUMBER, HECI_FUNCTION_NUMBER) + + +/* HECI registers */ +/* H_CB_WW - Host Circular Buffer (CB) Write Window register */ +#define H_CB_WW 0x00 +/* H_CSR - Host Control Status register */ +#define H_CSR 0x04 +/* ME_CB_RW - ME Circular Buffer Read Window register (read only) */ +#define SEC_CB_RW 0x08 +/* ME_CSR_HA - ME Control Status Host Access register (read only) */ +#define SEC_CSR_HA 0x0C + +/* PCI access functions for SEC access */ +#define HECI_MBAR0 0x10 +#define HECI_MBAR1 0x14 + +#define HECI_BUS 0 +#define HECI_DEVICE_NUMBER 15 +#define HECI_FUNCTION_NUMBER 0x00 + +#define HECI_HOST_ADDR 0 + +#define HECI_SEC_FW_STS0 0x40 + + +#define EFI_PCI_COMMAND_MEMORY_SPACE (1<<1) +#define EFI_PCI_COMMAND_BUS_MASTER (1<<2) +#define EFI_PCI_COMMAND_SERR (1<<8) + + +#define HECI_RESET_TIMEOUT 8000 /* ms */ +#define HECI_EVENT_TIMEOUT 3000 /* ms */ + + +/* S_CSR - SEC Control Status */ +typedef union { + uint32_t data; + struct { + uint32_t SEC_IE_HRA : 1; /* 0 - SEC Interrupt Enable */ + uint32_t SEC_IS_HRA : 1; /* 1 - SEC Interrupt Status */ + uint32_t SEC_IG_HRA : 1; /* 2 - SEC Interrupt Generate */ + uint32_t SEC_RDY_HRA : 1; /* 3 - SEC Ready */ + uint32_t SEC_RST_HRA : 1; /* 4 - SEC Reset */ + uint32_t reserved : 3; /* 7:5 */ + uint32_t SEC_CBRP_HRA : 8; /* 15:8 - SEC CB Read Pointer */ + uint32_t SEC_CBWP_HRA : 8; /* 23:16 - SEC CB Write Pointer */ + uint32_t SEC_CBD_HRA : 8; /* 31:24 - SEC Circular Buffer Depth */ + } bit; +} DEV_CTRL_REG; + +/* H_CSR - Host Control Status */ +typedef union { + uint32_t data; + struct { + uint32_t H_IE : 1; /* 0 - Host Interrupt Enable SEC */ + uint32_t H_IS : 1; /* 1 - Host Interrupt Status SEC */ + uint32_t H_IG : 1; /* 2 - Host Interrupt Generate */ + uint32_t H_RDY : 1; /* 3 - Host Ready */ + uint32_t H_RST : 1; /* 4 - Host Reset */ + uint32_t reserved : 3; /* 7:5 */ + uint32_t H_CBRP : 8; /* 15:8 - Host CB Read Pointer */ + uint32_t H_CBWP : 8; /* 23:16 - Host CB Write Pointer */ + uint32_t H_CBD : 8; /* 31:24 - Host Circular Buffer Depth */ + } bit; +} HOST_CTRL_REG; + +typedef union { + uint32_t data; + struct { + /* logical address of the TXE client of the message */ + uint32_t sec_address : 8; + /* logical address of the Host client of the message. */ + uint32_t host_address : 8; + /* message length in bytes, not including header */ + uint32_t length : 9; + uint32_t reserved : 6; + /* indicate the last message of HECI transfer */ + uint32_t message_complete : 1; + } bit; +} HECI_MSG_HDR; + +#endif /* __HECI_IMPL__ */ + diff --git a/drivers/heci/heci_protocol.h b/drivers/heci/heci_protocol.h new file mode 100644 index 0000000..2b1ccdc --- /dev/null +++ b/drivers/heci/heci_protocol.h @@ -0,0 +1,183 @@ +/* + This file contains an 'Intel Peripheral Driver' and is + licensed for Intel CPUs and chipsets under the terms of your + license agreement with Intel or your vendor. This file may + be modified by the user, subject to additional terms of the + license agreement +*/ +/*++ + + Copyright (c) 1999 - 2017 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + + +/*++ + Module Name: + + Heci.h + + Abstract: + + Interface definition for EFI_HECI_PROTOCOL + + --*/ + +#ifndef _EFI_HECI_H_ +#define _EFI_HECI_H_ + +#define HECI_PROTOCOL_GUID \ + { 0xcfb33810, 0x6e87, 0x4284, {0xb2, 0x3, 0xa6, 0x6a, 0xbe, 0x7, 0xf6, 0xe8 } } + + +/* #define EFI_HECI_PROTOCOL_GUID HECI_PROTOCOL_GUID */ + +typedef struct _EFI_HECI_PROTOCOL EFI_HECI_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_SENDWACK) ( + IN OUT uint32_t *Message, + IN OUT uint32_t Length, + IN OUT uint32_t *RecLength, + IN uint8_t HostAddress, + IN uint8_t SECAddress + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_READ_MESSAGE) ( + IN uint32_t Blocking, + IN uint32_t *MessageBody, + IN OUT uint32_t *Length + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_SEND_MESSAGE) ( + IN uint32_t *Message, + IN uint32_t Length, + IN uint8_t HostAddress, + IN uint8_t SECAddress + ); +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_RESET) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_INIT) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_REINIT) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_RESET_WAIT) ( + IN uint32_t Delay + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_GET_SEC_STATUS) ( + IN uint32_t *Status + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_GET_SEC_MODE) ( + IN uint32_t *Mode + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_DISABLE_PG) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_ENABLE_PG) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_HECI_SUBMIT_COMMAND) ( + IN EFI_HECI_PROTOCOL *This, + IN uint32_t InputParameterBlockSize, + IN uint8_t *InputParameterBlock, + IN uint32_t OutputParameterBlockSize, + IN uint8_t *OutputParameterBlock + ); + +typedef struct _EFI_HECI_PROTOCOL { + EFI_HECI_SENDWACK SendwACK; + EFI_HECI_READ_MESSAGE ReadMsg; + EFI_HECI_SEND_MESSAGE SendMsg; + EFI_HECI_RESET ResetHeci; + EFI_HECI_INIT InitHeci; + EFI_HECI_RESET_WAIT SeCResetWait; + EFI_HECI_REINIT ReInitHeci; + EFI_HECI_GET_SEC_STATUS GetSeCStatus; + EFI_HECI_GET_SEC_MODE GetSeCMode; + EFI_HECI_DISABLE_PG DisableSeCPG; + EFI_HECI_ENABLE_PG EnableSeCPG; + EFI_HECI_SUBMIT_COMMAND HeciSubmitCommand; +} EFI_HECI_PROTOCOL; + + +#pragma pack(1) + +/* Abstract SEC Mode Definitions */ +#define SEC_MODE_NORMAL 0x00 + +#define SEC_DEBUG_MODE_ALT_DIS 0x02 +#define SEC_MODE_TEMP_DISABLED 0x03 +#define SEC_MODE_SECOVER 0x04 +#define SEC_MODE_FAILED 0x06 + +/* Abstract SEC Status definitions */ +#define SEC_READY 0x00 +#define SEC_INITIALIZING 0x01 +#define SEC_IN_RECOVERY_MODE 0x02 +#define SEC_DISABLE_WAIT 0x06 +#define SEC_TRANSITION 0x07 +#define SEC_NOT_READY 0x0F +#define SEC_FW_INIT_COMPLETE 0x80 +#define SEC_FW_BOOT_OPTIONS_PRESENT 0x100 +#define SEC_FW_UPDATES_IN_PROGRESS 0x200 + +typedef struct { + uint32_t CodeMinor; + uint32_t CodeMajor; + uint32_t CodeBuildNo; + uint32_t CodeHotFix; +} SEC_VERSION_INFO; + +#pragma pack() + +/* HECI APIs */ +extern EFI_GUID gEfiHeciProtocolGuid; + +EFI_STATUS HeciHciSendMessage(uint8_t *pmsg, uint32_t MsgSize); + +EFI_STATUS HeciHciRecvMessage(uint32_t *pbytesRead, uint8_t *prxBuff); + +EFI_STATUS GetSeCFwVersion(SEC_VERSION_INFO *SeCVersion); + +#endif /* _EFI_HECI_H_ */ diff --git a/drivers/ioc_uart/ioc_uart.c b/drivers/ioc_uart/ioc_uart.c new file mode 100644 index 0000000..7875d93 --- /dev/null +++ b/drivers/ioc_uart/ioc_uart.c @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: jeremy.compostella@intel.com + * Author: kui.wen@intel.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "ioc_uart/ioc_uart.h" +#include "ioc_uart/ioc_uart_protocol.h" +#include "ewlog.h" + +#ifndef IOC_USE_CBC //default to use SLCAN +#define CAN_STACK_READY "T0000FFFF70A005555555555\r" +#define CAN_SUPPRES_HEART_BEAT_1MIN "T0000FFFF701015555555555\r" +#define CAN_SUPPRES_HEART_BEAT_5MIN "T0000FFFF701025555555555\r" +#define CAN_SUPPRES_HEART_BEAT_10MIN "T0000FFFF701035555555555\r" +#define CAN_SUPPRES_HEART_BEAT_30MIN "T0000FFFF701045555555555\r" +#define CAN_CONFIG_RESTART_SYSTEM "T0000FFFF703015555555555\r" +#define CAN_CONFIG_SHUTDOWN_SYSTEM "T0000FFFF703005555555555\r" +#define CAN_NUMBER_SUS_STAT_TOGGLES_3 "T0000FFFF705035555555555\r" +#define CAN_NUMBER_SUS_STAT_TOGGLES_2 "T0000FFFF705025555555555\r" +#define CAN_NUMBER_SUS_STAT_TOGGLES_1 "T0000FFFF705015555555555\r" +#define CAN_ENTER_IOC_BOOTLOADER "T0000FFFF7FFAF5555555555\r" +#define CAN_MSG_LENGTH 25 +#else //CBC frames +static char CBC_SUPPRES_HEART_BEAT_1MIN[] = {0x05,0x00,0x0E,0x04,0x60,0xEA,0x00,0x9F}; +static char CBC_SUPPRES_HEART_BEAT_5MIN[] = {0x05,0x00,0x0E,0x04,0xE0,0x93,0x04,0x72}; +static char CBC_SUPPRES_HEART_BEAT_10MIN[] = {0x05,0x00,0x0E,0x04,0xC0,0x27,0x09,0xF9}; +static char CBC_SUPPRES_HEART_BEAT_30MIN[] = {0x05,0x00,0x0E,0x04,0x40,0x77,0x1B,0x17}; +static char CBC_NUMBER_SUS_STAT_TOGGLES_2_HALT[] = {0x05,0x00,0x0E,0x02,0x00,0x05,0x00,0xE6}; +static char CBC_NUMBER_SUS_STAT_TOGGLES_1_HALT[] = {0x05,0x00,0x0E,0x02,0x00,0x03,0x00,0xE8}; +static char CBC_NUMBER_SUS_STAT_TOGGLES_2_REBOOT[] = {0x05,0x00,0x0E,0x02,0x00,0x06,0x00,0xE5}; +static char CBC_NUMBER_SUS_STAT_TOGGLES_1_REBOOT[] = {0x05,0x00,0x0E,0x02,0x00,0x04,0x00,0xE7}; +static char CBC_CONFIG_RESTART_SYSTEM[] = {0x05,0x00,0x0E,0x02,0x00,0x02,0x00,0xE9}; +static char CBC_CONFIG_SHUTDOWN_SYSTEM[] = {0x05,0x00,0x0E,0x02,0x00,0x01,0x00,0xEA}; +static char CBC_ENTER_IOC_BOOTLOADER[] = {0x05,0x00,0x20,0x01,0x30,0x10,0x80,0x1a, + 0x05,0x00,0x20,0x01,0x30,0x10,0x80,0x11, + 0x05,0x00,0x20,0x01,0x30,0x10,0x80,0x1a, + 0x05,0x00,0x20,0x01,0x30,0x10,0x80,0x11}; +#define CBC_ENTER_IOC_BOOTLOADER_MSG_NUM 4 +#define CBC_MSG_LENGTH 8 +#endif + +#define SUPPRESS_HEART_BEAT_TIMEOUT_1_MIN 1 +#define SUPPRESS_HEART_BEAT_TIMEOUT_5_MIN 5 +#define SUPPRESS_HEART_BEAT_TIMEOUT_10_MIN 10 +#define SUPPRESS_HEART_BEAT_TIMEOUT_30_MIN 30 + +#define NORTH 0xc5 +#define GPIO_PADBAR 0x500 +#define RW_BARRIER() asm volatile ("" ::: "memory") +#define SBREG_BASE_ADDRESS 0xF8000000 /* bar #0 */ +#define _SB_MMIO_PORT_BASE(port) (SBREG_BASE_ADDRESS + ((port) << 16)) +#define EC_BASE 0xE0000000 +#define BDF_(b, d, f) ((pci_device_t) (EC_BASE | ((b) << 20) | ((d) << 15) | ((f) << 12))) +#define BIT_( n) (1U << (n)) + +#define R_UART_BAUD_THR 0 +#define R_UART_BAUD_LOW 0 +#define R_UART_BAUD_HIGH 0x04 +#define R_UART_IER 0x04 +#define R_UART_FCR 0x08 +#define B_UARY_FCR_TRFIFIE BIT_(0) +#define B_UARY_FCR_RESETRF BIT_(1) +#define B_UARY_FCR_RESETTF BIT_(2) +#define R_UART_LCR 0x0C +#define B_UARY_LCR_DLAB BIT_(7) +#define R_UART_MCR 0x10 +#define B_UART_MCR_AFC 0x20 +#define R_UART_LSR 0x14 +#define B_UART_LSR_RXRDY BIT_(0) +#define B_UART_LSR_TXRDY BIT_(5) +#define B_UART_LSR_TEMT BIT_(6) +#define R_UART_MSR 0x18 +#define R_UART_SCR 0x1C +#define R_UART_CLOCKS 0x200 /* private clock configuration */ +#define R_UART_RESETS 0x204 /* software reset */ + +#define UART_CLK 64000000 + +#define UART_IPC_BASE_ADDRESS 0xFC010000 +#define BAUD_RATE 4000000 + +#define ClockCycles() *((volatile const uint32_t *) 0xfed000f0) /* HPET_MCV */ +#define CPMS 19200 + +#define TIMER_START(msec) (ClockCycles() + (msec) * CPMS) +#define TIMER_ELAPSED(tt) ((int) (ClockCycles() - (tt)) >= 0) + +static UINTN uart_base_addr; +static unsigned int old_state[7]; +#ifndef IOC_USE_CBC +static char can_message_buf[CAN_MSG_LENGTH + 1]; +#else +static char * cbc_message_buf = NULL; +#endif +uint8_t frame_number_send, frame_number_received = 0; + +union _pci_config_space { + uint32_t cfg_word [16]; + /* common type 0/1 header part */ + struct _common_cfg_header { + uint16_t vendor_id; + uint16_t device_id; + uint16_t command; + uint16_t status; + uint8_t revision_id; + uint8_t class_code [3]; + uint8_t cache_line_size; + uint8_t latency_timer; + uint8_t header_type; + uint8_t BIST; + } cfg; + struct { + struct _common_cfg_header _common; + uint32_t base_address [6]; + uint32_t cardbus_CIS_pointer; + uint16_t subsystem_vendor_id; + uint16_t subsystem_device_id; + uint32_t expansion_ROM_base_address; + uint8_t capabilities_pointer; + uint8_t _reserved [7]; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint8_t min_gnt; + uint8_t max_lat; + } cfg0; + struct { + struct _common_cfg_header _common; + uint32_t base_address [2]; + uint8_t primary_bus_number; + uint8_t secondary_bus_number; + uint8_t subordinate_bus_number; + uint8_t secondary_latency_timer; + uint8_t io_base; + uint8_t io_limit; + uint16_t secondary_status; + uint16_t memory_base; + uint16_t memory_limit; + uint16_t prefetchable_memory_base; + uint16_t prefetchable_memory_limit; + uint32_t prefetchable_base_upper32; + uint32_t prefetchable_limit_upper32; + uint16_t io_base_upper16; + uint16_t io_limit_upper16; + uint8_t capabilities_pointer; + uint8_t _reserved [3]; + uint32_t expansion_ROM_base_address; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint16_t bridge_control; + } cfg1; +}; + +typedef enum ignore_sus_stat_toggles { + IGNORE_SUS_STAT_1, + IGNORE_SUS_STAT_2, + IGNORE_SUS_STAT_3 +} EFI_IGNORE_SUS_STAT_TOGGLES; + +typedef enum configure_shutdown_behaviour { + RESTART_SYSTEM, + SHUTDOWN_SYSTEM +} EFI_CONFIGURE_SHUTDOWN_BEHAVIOUR; + + +typedef volatile union _pci_config_space *pci_device_t; +extern size_t str16len(const CHAR16 *str); + +static struct _device_params +{ + pcidev_t cfg; /* PCI device (configuration space) */ + unsigned char cfio_group; /* CFIO community */ + unsigned short cfio_offset; /* Rx .DW0 register offset (.DW1, Tx .DW0, .DW1 follow) */ + unsigned int cfio_values[4]; /* to configure Rx+Tx DW0+DW1 */ +} device_params = {0, NORTH, GPIO_PADBAR+0x0150, { 0x44000702, 0x304b, 0x44000700, 0x304c}}; + +static unsigned int msgbus32(unsigned int port, unsigned int reg) +{ + return read32((void *)(UINTN)(_SB_MMIO_PORT_BASE(port) + reg)); +} + +static void msgbus32_set(unsigned int port, unsigned int reg, unsigned int val) +{ + write32((void *)(UINTN)(_SB_MMIO_PORT_BASE(port) + reg), val); +} + +static void init_uart_ioc(unsigned int saved[7]) +{ + struct _device_params *p = &device_params; + unsigned int i; + unsigned int lcr = 0; + unsigned int divisor; + + if (p->cfg == 0) + pci_find_device(PCI_VENDOR_ID_INTEL, SERIAL_IOC_PCI_DID, &p->cfg); + + for (i = 0 ; i < 4 ; i += 1) { + saved[i] = msgbus32(p->cfio_group, p->cfio_offset + 4 * i); + msgbus32_set(p->cfio_group, p->cfio_offset + 4 * i, p->cfio_values[i]); + } + + saved[4] = pci_read_config32(p->cfg, PCI_BASE_ADDRESS_0); + saved[5] = pci_read_config16(p->cfg, PCI_COMMAND); + + uart_base_addr = pci_read_config32(p->cfg, PCI_BASE_ADDRESS_0) & PCI_BASE_ADDRESS_MEM_MASK; + + /* Take the UART out of reset, then update and enable the (M,N) clock divider. */ + write8((void *)(UINTN)(uart_base_addr + R_UART_RESETS), 0x00); + write8((void *)(UINTN)(uart_base_addr + R_UART_RESETS), 0x07); + saved[6] = read32((void *)(UINTN)(uart_base_addr + R_UART_CLOCKS)); + write32((void *)(UINTN)(uart_base_addr + R_UART_CLOCKS), 0x00640081); + write32((void *)(UINTN)(uart_base_addr + R_UART_CLOCKS), 0x80640081); + + lcr |= 0x03; /* 8 data bits */ + lcr &= 0xfb; /* 1 stop bit */ + lcr &= 0xf7; /* no parity */ + write8((void *)(UINTN)(uart_base_addr + R_UART_LCR), lcr | B_UARY_LCR_DLAB); + + divisor = UART_CLK / BAUD_RATE / 16; + write8((void *)(UINTN)(uart_base_addr + R_UART_BAUD_HIGH), divisor >> 8); + write8((void *)(UINTN)(uart_base_addr + R_UART_BAUD_LOW), divisor); + + /* switch back to bank 0 */ + write8((void *)(UINTN)(uart_base_addr + R_UART_LCR), lcr); + + /* enable & reset (receive and transmit) FIFO */ + write8((void *)(UINTN)(uart_base_addr + R_UART_FCR), 0); + write8((void *)(UINTN)(uart_base_addr + R_UART_FCR), (B_UARY_FCR_TRFIFIE | B_UARY_FCR_RESETRF | B_UARY_FCR_RESETTF)); +} + +/* +** Restore the original hardware state. +*/ +static void ioc_uart_restore_device(unsigned int saved[7]) +{ + struct _device_params *p = &device_params; + unsigned int to, i; + + if (p->cfg == 0) + pci_find_device(PCI_VENDOR_ID_INTEL, SERIAL_IOC_PCI_DID, &p->cfg); + + /* Wait until the transmit FIFO is empty (ensure that they got our ACK) */ + while ((read8((void *)(UINTN)(uart_base_addr + R_UART_LSR)) & B_UART_LSR_TEMT) == 0) + ; + + /* Restore UART (M,N) clock divider */ + write32((void *)(UINTN)(uart_base_addr + R_UART_CLOCKS), saved[6]); + + /* Restore PCI configuration space registers */ + pci_write_config32(p->cfg, PCI_BASE_ADDRESS_0, saved[4]); + pci_write_config16(p->cfg, PCI_COMMAND, saved[5]); + + /* Wait a few milliconds before returning the CFIO pins to their + original state; this avoids sending them a BREAK (i.e. an error) + condition immediately after the acknowledge */ + to = TIMER_START(5); + while (!TIMER_ELAPSED(to)) + ; + + /* Restore CFIO pin mux */ + for (i = 0 ; i < 4; i += 1) + msgbus32_set(p->cfio_group, p->cfio_offset + 4 * i, saved[i]); +} + +/* +** Send a single character to the IOC device. +*/ +static void ioc_uart_send (int ch) +{ + unsigned lsr; + static unsigned fifo_size = 16; + + lsr = read8((void *)(UINTN)(uart_base_addr + R_UART_LSR)); + while (fifo_size >= 16 && (lsr & B_UART_LSR_TXRDY) == 0) + lsr = read8((void *)(UINTN)(uart_base_addr + R_UART_LSR)); + + if ((lsr & B_UART_LSR_TXRDY) != 0) + fifo_size = 0; + + write8((void *)(UINTN)(uart_base_addr + R_UART_BAUD_THR), ch); + fifo_size += 1; +} + +static void ioc_uart_send_data(char *s, unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + ioc_uart_send(s[i]); + } +} + +static int ioc_uart_recv(uint8_t *buffer, unsigned int len) +{ + unsigned pos = 0; + unsigned lsr; + + for (;;) { + lsr = read8((void *)(UINTN)(uart_base_addr + R_UART_LSR)); + + /* ignore transmit status bits */ + lsr &= ~(B_UART_LSR_TXRDY | B_UART_LSR_TEMT); + + if ((lsr & ~B_UART_LSR_RXRDY) != 0) { + /* yes, drain Rx FIFO */ + do { + read8((void *)(UINTN)(uart_base_addr + R_UART_BAUD_THR)); + lsr = read8((void *)(UINTN)(uart_base_addr + R_UART_LSR)); + } while ((lsr & ~(B_UART_LSR_TXRDY | B_UART_LSR_TEMT)) != 0); + + return -1; + } + + if (lsr != 0) + buffer[pos++] = read8((void *)(UINTN)(uart_base_addr + R_UART_BAUD_THR)); + + /* frame complete or time-out */ + if (pos >= len) + return pos; + } +} + + +static EFIAPI EFI_STATUS +set_suppress_heart_beat_timeout(__attribute__((__unused__)) IOC_UART_PROTOCOL *This, + UINT32 timeout) +{ + init_uart_ioc(old_state); +#ifndef IOC_USE_CBC + switch (timeout) { + case SUPPRESS_HEART_BEAT_TIMEOUT_1_MIN: + strncpy((char *)can_message_buf, (char *)CAN_SUPPRES_HEART_BEAT_1MIN, CAN_MSG_LENGTH); + break; + case SUPPRESS_HEART_BEAT_TIMEOUT_5_MIN: + strncpy((char *)can_message_buf, (char *)CAN_SUPPRES_HEART_BEAT_5MIN, CAN_MSG_LENGTH); + break; + case SUPPRESS_HEART_BEAT_TIMEOUT_10_MIN: + strncpy((char *)can_message_buf, (char *)CAN_SUPPRES_HEART_BEAT_10MIN, CAN_MSG_LENGTH); + break; + case SUPPRESS_HEART_BEAT_TIMEOUT_30_MIN: + default: + strncpy((char *)can_message_buf, (char *)CAN_SUPPRES_HEART_BEAT_30MIN, CAN_MSG_LENGTH); + break; + } + + ioc_uart_send_data(can_message_buf, CAN_MSG_LENGTH); +#else + switch (timeout) { + case SUPPRESS_HEART_BEAT_TIMEOUT_1_MIN: + cbc_message_buf = CBC_SUPPRES_HEART_BEAT_1MIN; + break; + case SUPPRESS_HEART_BEAT_TIMEOUT_5_MIN: + cbc_message_buf = CBC_SUPPRES_HEART_BEAT_5MIN; + break; + case SUPPRESS_HEART_BEAT_TIMEOUT_10_MIN: + cbc_message_buf = CBC_SUPPRES_HEART_BEAT_10MIN; + break; + case SUPPRESS_HEART_BEAT_TIMEOUT_30_MIN: + default: + cbc_message_buf = CBC_SUPPRES_HEART_BEAT_30MIN; + break; + } + ioc_uart_send_data(cbc_message_buf, CBC_MSG_LENGTH); +#endif + ioc_uart_restore_device(old_state); + + return EFI_SUCCESS; +} +#ifndef IOC_USE_CBC +static EFIAPI EFI_STATUS +set_shutdown_behaviour(EFI_CONFIGURE_SHUTDOWN_BEHAVIOUR shutdown) +{ + init_uart_ioc(old_state); + switch (shutdown) { + case RESTART_SYSTEM: + strncpy((char *)can_message_buf, (char *)CAN_CONFIG_RESTART_SYSTEM, CAN_MSG_LENGTH); + break; + case SHUTDOWN_SYSTEM: + default: + strncpy((char *)can_message_buf, (char *)CAN_CONFIG_SHUTDOWN_SYSTEM, CAN_MSG_LENGTH); + break; + } + + ioc_uart_send_data(can_message_buf, CAN_MSG_LENGTH); + ioc_uart_restore_device(old_state); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +set_ignore_sus_stat_toggles(EFI_IGNORE_SUS_STAT_TOGGLES num_ignore_sus_stat) +{ + init_uart_ioc(old_state); + switch (num_ignore_sus_stat) { + case IGNORE_SUS_STAT_1: + strncpy((char *)can_message_buf, (char *)CAN_NUMBER_SUS_STAT_TOGGLES_1, CAN_MSG_LENGTH); + break; + case IGNORE_SUS_STAT_2: + strncpy((char *)can_message_buf, (char *)CAN_NUMBER_SUS_STAT_TOGGLES_2, CAN_MSG_LENGTH); + break; + case IGNORE_SUS_STAT_3: + strncpy((char *)can_message_buf, (char *)CAN_NUMBER_SUS_STAT_TOGGLES_3, CAN_MSG_LENGTH); + break; + default: + strncpy((char *)can_message_buf, (char *)CAN_NUMBER_SUS_STAT_TOGGLES_1, CAN_MSG_LENGTH); + break; + } + + ioc_uart_send_data(can_message_buf, CAN_MSG_LENGTH); + ioc_uart_restore_device(old_state); + + return EFI_SUCCESS; +} +#else +static EFIAPI EFI_STATUS +set_ignore_sus_stat_toggles_shutdown_behaviour(EFI_IGNORE_SUS_STAT_TOGGLES num_ignore_sus_stat,EFI_CONFIGURE_SHUTDOWN_BEHAVIOUR shutdown) +{ + init_uart_ioc(old_state); + switch (shutdown) { + case RESTART_SYSTEM: + if(IGNORE_SUS_STAT_3 == num_ignore_sus_stat) + cbc_message_buf = CBC_NUMBER_SUS_STAT_TOGGLES_2_REBOOT; + else if(IGNORE_SUS_STAT_2 == num_ignore_sus_stat) + cbc_message_buf = CBC_NUMBER_SUS_STAT_TOGGLES_1_REBOOT; + else + cbc_message_buf = CBC_CONFIG_RESTART_SYSTEM; + break; + case SHUTDOWN_SYSTEM: + default: + if(IGNORE_SUS_STAT_3 == num_ignore_sus_stat) + cbc_message_buf = CBC_NUMBER_SUS_STAT_TOGGLES_2_HALT; + else if(IGNORE_SUS_STAT_2 == num_ignore_sus_stat) + cbc_message_buf = CBC_NUMBER_SUS_STAT_TOGGLES_1_HALT; + else + cbc_message_buf = CBC_CONFIG_SHUTDOWN_SYSTEM; + break; + } + + ioc_uart_send_data(cbc_message_buf, CBC_MSG_LENGTH); + ioc_uart_restore_device(old_state); + + return EFI_SUCCESS; +} +#endif + +static EFIAPI EFI_STATUS +notify_ioc_cm_ready(__attribute__((__unused__)) IOC_UART_PROTOCOL *This) +{ + init_uart_ioc(old_state); +#ifndef IOC_USE_CBC + strncpy((char *)can_message_buf, (char *)CAN_STACK_READY, CAN_MSG_LENGTH); + ioc_uart_send_data(can_message_buf, CAN_MSG_LENGTH); + strncpy((char *)can_message_buf, (char *)CAN_NUMBER_SUS_STAT_TOGGLES_3, CAN_MSG_LENGTH); + ioc_uart_send_data(can_message_buf, CAN_MSG_LENGTH); +#else +#ifdef DEBUG + printf("CBC Kmod-no handshake required\n"); +#endif +#endif + ioc_uart_restore_device(old_state); + + return EFI_SUCCESS; +} + +static EFI_STATUS +ioc_reboot(EFI_RESET_TYPE ResetType, CHAR16 *ResetData) +{ + EFI_IGNORE_SUS_STAT_TOGGLES numberignoretoggles; + size_t i; + CHAR16 *fastboot_name[] = {L"bootloader", L"fastboot", L"recovery"}; + BOOLEAN is_fastboot = FALSE; + EFI_STATUS ret; + + if (ResetData) { + for (i = 0; i < ARRAY_SIZE(fastboot_name); i++) { + if ((str16len(fastboot_name[i]) == str16len(ResetData)) && + !memcmp(fastboot_name[i], ResetData, str16len(ResetData) * sizeof(*ResetData))) { + is_fastboot = TRUE; + break; + } + } + } + + if (is_fastboot || ResetType == EfiResetWarm) + numberignoretoggles = IGNORE_SUS_STAT_2; + else + numberignoretoggles = IGNORE_SUS_STAT_1; +#ifndef IOC_USE_CBC + ret = set_ignore_sus_stat_toggles(numberignoretoggles); + if (EFI_ERROR(ret)) { + return ret; + } + + ret = set_shutdown_behaviour(RESTART_SYSTEM); +#else + ret = set_ignore_sus_stat_toggles_shutdown_behaviour(numberignoretoggles,RESTART_SYSTEM); +#endif + return ret; +} + +ias_frame init_frame(char command) +{ + ias_frame data; + + data.frame_counter = 0; + data.payload_length = 0; + data.command = command; + memset(data.content, 0x0, IAS_MAX_PAYLOAD_LENGTH); + data.checksum = 0; + data.valid = 0; + return data; +} + +/*! + * Converts given ASCII hex number to int + * \param [in] *str - two ASCII string + * \return integer value + */ +int c_to_i(const char *str) +{ + unsigned char device_id[2]; + + device_id[0] = (int) str[0] < 58 ? (int) str[0] - 48 : tolower((int) str[0]) - 87; + device_id[1] = (int) str[1] < 58 ? (int) str[1] - 48 : tolower((int) str[1]) - 87; + + return (unsigned int) ((device_id[0] << 4) | device_id[1]); +} + +/*! + * Send raw frame, no response for frame is expected + * \param [in] tty_fd - file descriptor for the desired serial port + * \param [in] *data - pointer to the frame containing data + * \return value of write operations + */ +int send_frame_raw(ias_frame *data) +{ + int data_index = 0; + uint8_t send_buffer[sizeof(ias_frame)]; + uint32_t send_index = 0; + + data->frame_counter = ++frame_number_send; + data->checksum = data->frame_counter + data->payload_length + data->command; + + send_buffer[send_index++] = data->frame_counter; + send_buffer[send_index++] = data->payload_length; + send_buffer[send_index++] = data->command; + + for (data_index = 0; data_index < data->payload_length; data_index++) { + send_buffer[send_index++] = data->content[data_index]; + data->checksum += data->content[data_index]; + } + send_buffer[send_index++] = (data->checksum & 0xff); + send_buffer[send_index++] = (data->checksum >> 8); + +#ifdef DEBUG + int a, b = 1; + + printf("------------------------------\n"); + printf("Send frame content\n"); + printf("------------------------------\n"); + printf("Counter: %"PRIu8", payload length: %"PRIu8", command: 0x%"PRIX8", checksum: %"PRIu16"\n", + data->frame_counter, data->payload_length, data->command, data->checksum); + for (a = 0; a < data->payload_length; a++) { + printf(" 0x%02"PRIX8" ", data->content[a]); + if (!(b++ % 8)) + printf("\n"); + } + printf("\n"); +#endif + + ioc_uart_send_data((char *)send_buffer, send_index); + + return 0; +} + +/*! + * Blocking function to receive a frame on UART + * \param [in] tty_fd - file descriptor for serial port + * \param [out] *frame - frame where received data is stored + */ +void receive_frame(ias_frame *frame, unsigned char skip_frame_counter) +{ + uint16_t checksum = 0; + int i = 0; + + if (skip_frame_counter == 0) + ioc_uart_recv(&frame->frame_counter, 1); + else + frame->frame_counter = 1; + + ioc_uart_recv(&frame->payload_length, 1); + ioc_uart_recv(&frame->command, 1); + + if (frame->payload_length != 0) + ioc_uart_recv(frame->content, frame->payload_length); + + ioc_uart_recv((unsigned char *) &frame->checksum, 1); + frame->checksum <<= 8; + ioc_uart_recv((unsigned char *) &frame->checksum, 1); + + checksum = frame->frame_counter + frame->payload_length + frame->command; + for (i = 0; i < frame->payload_length; i++) + checksum += frame->content[i]; + +#ifdef DEBUG + int a, b = 1; + + printf("------------------------------\n"); + printf("Frame content\n"); + printf("------------------------------\n"); + printf("Counter: %"PRIu8", payload length: %"PRIu8", command: 0x%"PRIX8", checksum: %"PRIu16", checksum expected: %"PRIu16"\n", + frame->frame_counter, frame->payload_length, frame->command, frame->checksum, checksum); + for (a = 0; a < frame->payload_length; a++) { + printf(" 0x%02"PRIX8" ", frame->content[a]); + if (!(b++ % 8)) + printf("\n"); + } + printf("\n"); +#endif + + if (checksum == frame->checksum) + frame->valid = 1; + else + frame->valid = 0; + + if ((frame->frame_counter == 0) && (frame->payload_length == 0) && (frame->command == 0)) { + printf("ERROR: receive_frame - empty string received\n"); + frame->valid = 0; + } + + frame_number_received++; +} + +int send_frame(ias_frame *data) +{ + int return_value = 0; + int attempts = 0; + unsigned char done = 0; + ias_frame rec_frame = init_frame(0x0); + + while (done == 0) { + send_frame_raw(data); + receive_frame(&rec_frame, 0); + + /* check if frame is valid and if no frame loss occurred */ + if (rec_frame.valid == 1) { + if (rec_frame.frame_counter == frame_number_received) { + /* acknowledge received */ + if (rec_frame.command == 0x06) { + return_value = 0; + done = 1; + } + /* not acknowledge received */ + else if (rec_frame.command == 0x07) { + attempts++; + printf("Retransmit was requested. Attempt: %i\n", attempts); + } else if (rec_frame.command == 0x08) { + done = 1; + return_value = 3; + } else { + /* frame error detected */ + done = 1; + return_value = 4; + } + } else { + /* TODO */ + /* frame loss detected */ + done = 1; + return_value = 1; + } + + } else { + /* TODO */ + /* frame not valid, normally send not acknowledge, but here it is already a acknowledge */ + done = 1; + return_value = 2; + } + } + + return return_value; +} /* send_frame() */ + +ias_flash_result ias_ioc_handshake(ias_hardware_revision *hardware_revision) +{ + uint8_t received = 0; + + printf("Waiting for IOC bootloader\n"); + + /* wait for charakter to receive */ + + /* reset protokoll information */ + frame_number_send = 0; + frame_number_received = 0; + + ioc_uart_recv(&received, 1); + + while (received != 0x55) { + ioc_uart_recv(&received, 1); + }; + + if (received == 0x55) { + printf("Bootloader request detected.\n"); + ioc_uart_send_data("\xAA", 1); + + + while (received == 0x55) { + ioc_uart_recv(&received, 1); + }; + + printf("Waiting for IOC information.\n\n"); + + /* get frame first send frame with target information */ + ias_frame rec_frame = init_frame(0x0); + + receive_frame(&rec_frame, 1); + if (rec_frame.valid == 1 && rec_frame.frame_counter == frame_number_received) { + if (rec_frame.command == 0x01) { + *hardware_revision = rec_frame.content[2]; + + switch (*hardware_revision) { + case e_ias_hardware_revision_fab_a: + printf("Hardware revision: BfH - FAB A/ GR with FBL version 2.2\n"); + break; + case e_ias_hardware_revision_fab_b: + printf("Hardware revision: BfH - FAB B\n"); + break; + case e_ias_hardware_revision_fab_c: + printf("Hardware revision: BfH - FAB C\n"); + break; + case e_ias_hardware_revision_gr_fab_a: + printf("Hardware revision: GR FAB A/B\n"); + break; + case e_ias_hardware_revision_gr_fab_b: + printf("Hardware revision: GR FAB A/B\n"); + break; + case e_ias_hardware_revision_gr_fab_c: + printf("Hardware revision: GR FAB C\n"); + break; + case e_ias_hardware_revision_gr_fab_d: + printf("Hardware revision: GR FAB D\n"); + break; + case e_ias_hardware_revision_sdc_fab_a: + printf("Hardware revision: SDC FAB A\n"); + break; + case e_ias_hardware_revision_carlake_fab_a: + printf("Hardware revision: CAR LAKE FAB A\n"); + break; + default: + printf("Unknown hardware revision 0x%02X\n", *hardware_revision); + break; + } + printf("Flash boot loader major-minor: %"PRIu8"-%"PRIu8"\n", rec_frame.content[0], rec_frame.content[1]); + return e_ias_flash_result_ok; + } else { + printf("Received a frame that is illegal during handshake. No initial handshake possible. Application will exit.\n"); + return e_ias_flash_result_handshake_failed; + } + } else { + printf("Frame not valid! No initial handshake possible. Application will exit.\n"); + return e_ias_flash_result_handshake_failed; + } + } else { + printf("Received illegal request. Initial handshake failed.\n"); + return e_ias_flash_result_error; + } +} + +void ias_ioc_reset(void) +{ + ias_frame go_application = init_frame(0x20); + + go_application.command = 0x90; + go_application.payload_length = 1; + + send_frame_raw(&go_application); + printf("Restarting the IOC\n"); +} + +static ias_flash_result ias_flash_enter_fbl_mode(ias_hardware_revision *hardware_revision) +{ +#ifndef IOC_USE_CBC + printf("Restarting the IOC into the bootloader (slcan request)\n"); + strncpy((char *)can_message_buf, (char *)CAN_ENTER_IOC_BOOTLOADER, CAN_MSG_LENGTH); + ioc_uart_send_data(can_message_buf, CAN_MSG_LENGTH); +#else + unsigned int to; + printf("Restarting the IOC into the bootloader (CBC request)\n"); + cbc_message_buf = CBC_ENTER_IOC_BOOTLOADER; + ioc_uart_send_data(cbc_message_buf, CBC_MSG_LENGTH*CBC_ENTER_IOC_BOOTLOADER_MSG_NUM); + to = TIMER_START(5); + while (!TIMER_ELAPSED(to)) + ; +#endif + return ias_ioc_handshake(hardware_revision); +} + +ias_flash_result ias_parse_file(uint8_t *file_content, uint32_t file_size, ias_frame_list **frame_list) +{ + uint32_t offset = 0; + uint32_t i = 0; + uint32_t record_length = 0; + uint32_t line = 0; + uint32_t calc_checksum = 0; + uint32_t read_checksum = 0; + uint8_t s3_record_offset = 0; + + ias_frame_list **next_frame_list_entry = frame_list; + + if (frame_list == NULL) + return e_ias_flash_result_out_of_memory; + + while (offset < file_size) { + ++line; + + /* Search start of next record. */ + while ((offset + 1 < file_size) + && (file_content[offset] != 'S')) + ++offset; + /* Check the record type (we only process S2 & S3 records). */ + if ((offset + 1 < file_size) && ((file_content[offset + 1] == '3') + || (file_content[offset + 1] == '2'))) { + if (file_content[offset + 1] == '3') + s3_record_offset = 1; + else + s3_record_offset = 0; + + /* Get the record length. */ + if ((offset + 4 + (s3_record_offset * 10)) < file_size) { + record_length = c_to_i((char *)(&file_content[offset + 2])); + if (record_length - 1 > IAS_MAX_PAYLOAD_LENGTH) { + printf("ERROR: Payload too long (%"PRIu32")\n", record_length - 1); + return e_ias_flash_result_parse_error; + } + } + + /* Ensure that a complete frame is remaining in the file. */ + if ((offset + 4 + (s3_record_offset * 3) + (2*record_length)) < file_size) { + /* Calculate the checksum. */ + calc_checksum = 0; + for (i = 0; i < record_length; ++i) + calc_checksum += c_to_i((char *)(&file_content[offset + 2 + 2*i])); + + calc_checksum = (~calc_checksum) & 0x000000ff; + + /* Validate the checksum. */ + read_checksum = c_to_i((char *)(&file_content[offset + 2*record_length + 2])); + if (calc_checksum != read_checksum) { + printf("ERROR: Checksum mismatch in line %"PRIu32"\n", line); + return e_ias_flash_result_checksum_error; + } + + /* Initialize a new frame. */ + ias_frame s_frame = init_frame(0x0); + + /* Extract the address */ + s_frame.content[0] = c_to_i((char *) (&file_content[offset + 4])); + s_frame.content[1] = c_to_i((char *) (&file_content[offset + 6])); + s_frame.content[2] = c_to_i((char *) (&file_content[offset + 8])); + if (s3_record_offset != 0) + s_frame.content[3] = c_to_i((char *) (&file_content[offset + 10])); + + /* Extract data. */ + for (i = 0; i < record_length - 4; i++) + s_frame.content[3 + s3_record_offset + i] = c_to_i((char *) (&file_content[offset + 10 + (s3_record_offset * 2) + i * 2])); + + /* Set the payload length. */ + s_frame.payload_length = record_length - 1; + + /* Add the frame to the frame list. */ + *next_frame_list_entry = (ias_frame_list *)malloc(sizeof(ias_frame_list)); + if ((*next_frame_list_entry) == NULL) { + printf("ERROR: Allocating frame failed. Aborting!\n"); + return e_ias_flash_result_out_of_memory; + } + (*next_frame_list_entry)->next = NULL; + memcpy(&(*next_frame_list_entry)->frame, &s_frame, sizeof((*next_frame_list_entry)->frame)); + + /* Advance the next frame pointer */ + next_frame_list_entry = (ias_frame_list **) &(*next_frame_list_entry)->next; + } else { + printf("ERROR: Invalid S-Record, expecting more bytes than remaining\n"); + return e_ias_flash_result_parse_error; + } + } + + /* Skip beyond the next line break. */ + while ((offset < file_size) && (file_content[offset] != '\n')) + ++offset; + + if (offset < file_size) + ++offset; + } + + return e_ias_flash_result_ok; +} + + +/*! + * Sending whole bunch out of buffer, buffer has to be in srecord format with 128 byte lines + * \param [in] - tty_fd - file descriptor for the desired serial port + * \param [in] - *filecontent - pointer to buffer with file content + * \param [in] - file_size - size of buffer + * \param [in] - command - overwrite the default frame command + * \return output if successful or not + */ +int flash_content(ias_frame_list const *frame_list, unsigned char command) +{ + int error = 0; + uint32_t data_frame_counter = 1; + uint32_t num_data_frames = 0; + ias_frame s_frame; + ias_frame_list const *current_frame = NULL; + + /* Count data frames. */ + current_frame = frame_list; + while (current_frame != NULL) { + ++num_data_frames; + current_frame = (ias_frame_list const *) current_frame->next; + } + + /* Send data frames. */ + current_frame = frame_list; + while ((current_frame != NULL) && (error == 0)) { + if (((data_frame_counter % 50) == 0) || + (data_frame_counter == num_data_frames) || + (data_frame_counter < 10)) { + if (data_frame_counter == 1) + printf("Erasing flash.\n"); + else + printf("Data frame number send: %4"PRIu32"|%4"PRIu32"\r", data_frame_counter, num_data_frames); + } + + memcpy(&s_frame, ¤t_frame->frame, sizeof(s_frame)); + s_frame.command = command; + + /* Send the frame. */ + error = send_frame(&s_frame); + if (error != 0) + printf("Error occurred: %d\n", error); + + ++data_frame_counter; + current_frame = (ias_frame_list const *) current_frame->next; + } + printf("\n"); + + return error; +} + +int ias_flash_file(unsigned char *file_content, int file_size, unsigned char command) +{ + int error = 0; + ias_frame_list *frame_list = NULL; + ias_frame_list *frame_iterator = NULL; + + /* Try to parse the input file. */ + error = ias_parse_file(file_content, file_size, &frame_list); + + /* Try to flash the input file .*/ + if (error == 0) { + error = flash_content(frame_list, command); + + if (error == 0) + printf("\nFlashing was successful.\n"); + else if (error == 1) + printf("An error occurred during flashing the new firmware. A frame loss was detected.\n"); + else if (error == 3) + printf("An error occurred during flashing the new firmware. The number of retransmits exceeded the maximum or IOC detected frame loss. The IOC is in fail mode.\n"); + } + + /* Clean up allocated frames. */ + frame_iterator = frame_list; + while (frame_iterator != NULL) { + frame_list = frame_iterator; + frame_iterator = (ias_frame_list *)frame_iterator->next; + free(frame_list); + } + + return error; +} + +static EFI_STATUS EFIAPI ias_flash_ioc_firmware(__attribute__((__unused__)) IOC_UART_PROTOCOL * This, + UINT8 *file_content, + UINT32 file_size) + +{ + ias_hardware_revision hardware_revision; + + init_uart_ioc(old_state); + if (ias_flash_enter_fbl_mode(&hardware_revision)) + return EFI_UNSUPPORTED; + + ias_flash_file(file_content, file_size, 0x30); + + /* Start the newly flashed firmware. */ + ias_ioc_reset(); + + ioc_uart_restore_device(old_state); + + return EFI_SUCCESS; +} + +static EFI_GUID ioc_uart_guid = EFI_IOC_UART_PROTOCOL_GUID; +static EFI_HANDLE handle; +static EFI_RESET_SYSTEM saved_reset_rs; + +static EFIAPI EFI_STATUS +ioc_reset_system(EFI_RESET_TYPE ResetType, + EFI_STATUS ResetStatus, + UINTN DataSize, + CHAR16 *ResetData) +{ + EFI_STATUS ret; + ret = ioc_reboot(ResetType, ResetData); + if (EFI_ERROR(ret)) { + return ret; + } + + return saved_reset_rs ? saved_reset_rs(ResetType, ResetStatus, DataSize, ResetData) : ret; +} + +static EFI_STATUS ioc_uart_init(EFI_SYSTEM_TABLE *st) +{ + static IOC_UART_PROTOCOL ioc_uart_default = { + .SetSuppressHeartBeatTimeout = set_suppress_heart_beat_timeout, + .NotifyIOCCMReady = notify_ioc_cm_ready, + .flash_ioc_firmware = ias_flash_ioc_firmware + }; + IOC_UART_PROTOCOL *ioc_uart; + + saved_reset_rs = st->RuntimeServices->ResetSystem; + st->RuntimeServices->ResetSystem = ioc_reset_system; + + return interface_init(st, &ioc_uart_guid, &handle, + &ioc_uart_default, sizeof(ioc_uart_default), + (void **)&ioc_uart); +} + +static EFI_STATUS ioc_uart_exit(EFI_SYSTEM_TABLE *st) +{ + st->RuntimeServices->ResetSystem = saved_reset_rs; + + return interface_free(st, &ioc_uart_guid, handle); +} + +ewdrv_t ioc_uart_drv = { + .name = "ioc_uart", + .description = "Provide reset support based on IOC CAN", + .init = ioc_uart_init, + .exit = ioc_uart_exit +}; + diff --git a/drivers/ioc_uart/ioc_uart.h b/drivers/ioc_uart/ioc_uart.h new file mode 100644 index 0000000..d540fed --- /dev/null +++ b/drivers/ioc_uart/ioc_uart.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kui.wen@intel.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + */ + +#ifndef _IOC_UART_H_ +#define _IOC_UART_H_ + +#include + +extern ewdrv_t ioc_uart_drv; + +typedef enum { + e_ias_hardware_revision_fab_a = 0x0F, + e_ias_hardware_revision_fab_b = 0x0E, + e_ias_hardware_revision_fab_c = 0x0D, + e_ias_hardware_revision_gr_fab_a = 0x07, + e_ias_hardware_revision_gr_fab_b = 0x03, + e_ias_hardware_revision_gr_fab_c = 0x05, + e_ias_hardware_revision_gr_fab_d = 0x06, + e_ias_hardware_revision_sdc_fab_a = 0x09, + e_ias_hardware_revision_carlake_fab_a = 0x0A +} +ias_hardware_revision; + +typedef enum { + e_ias_flash_result_ok, + e_ias_flash_result_timeout, + e_ias_flash_result_handshake_failed, + e_ias_flash_result_out_of_memory, + e_ias_flash_result_parse_error, + e_ias_flash_result_checksum_error, + e_ias_flash_result_error +} +ias_flash_result; + +#define IAS_MAX_PAYLOAD_LENGTH 255u + +typedef struct ias_frame { + uint8_t frame_counter; + uint8_t payload_length; + uint8_t command; + uint8_t content[IAS_MAX_PAYLOAD_LENGTH]; + uint16_t checksum; + uint8_t valid; +} +ias_frame; + +typedef struct ias_frame_list { + struct ias_frame_list *next; + ias_frame frame; +} +ias_frame_list; + +#endif /* _IOC_UART_H_ */ diff --git a/drivers/ioc_uart/ioc_uart_protocol.h b/drivers/ioc_uart/ioc_uart_protocol.h new file mode 100644 index 0000000..cb20164 --- /dev/null +++ b/drivers/ioc_uart/ioc_uart_protocol.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kui.wen@intel.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + */ + +#ifndef _IOC_UART_PROTOCOL_H_ +#define _IOC_UART_PROTOCOL_H_ + +#include + +#define EFI_IOC_UART_PROTOCOL_GUID \ + {0x6152f300, 0x957f, 0x40b2, {0x9e, 0x4b, 0xe9, 0x22, 0x37, 0xa6, 0x66, 0xed}} + +typedef struct _IOC_UART_PROTOCOL IOC_UART_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_SET_SUPPRESS_HEART_BEAT_TIMEOUT) ( + IN IOC_UART_PROTOCOL *This, + IN UINT32 timeout + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_NOTIFY_IOC_CM_READY) ( + IN IOC_UART_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_FLASH_IOC_FIRMWARE) ( + IN IOC_UART_PROTOCOL *This, + IN UINT8 *file_content, + IN UINT32 file_size + ); + +struct _IOC_UART_PROTOCOL { + EFI_SET_SUPPRESS_HEART_BEAT_TIMEOUT SetSuppressHeartBeatTimeout; + EFI_NOTIFY_IOC_CM_READY NotifyIOCCMReady; + EFI_FLASH_IOC_FIRMWARE flash_ioc_firmware; +} __attribute__((packed)); + +#endif /* _IOC_UART_PROTOCOL_H_ */ diff --git a/drivers/lifecycle/LifeCycleProtocol.h b/drivers/lifecycle/LifeCycleProtocol.h new file mode 100644 index 0000000..d168b02 --- /dev/null +++ b/drivers/lifecycle/LifeCycleProtocol.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + */ + +#ifndef _LIFE_CYCLE_PROTOCOL_H_ +#define _LIFE_CYCLE_PROTOCOL_H_ + +#include + +#define EFI_LIFE_CYCLE_STATE_PROTOCOL_GUID \ + {0xf3c1138e, 0xcd89, 0x4e20,{0x9e, 0x68, 0x25, 0xa6, 0x76, 0x95, 0xa5, 0x6a}} + +#define EFI_LIFE_CYCLE_STATE_PROTOCOL_REVISION1 0x00000001 + +typedef enum life_cycle_state { + LC_STATE_MANUFACTURING, + LC_STATE_ENDUSER, + LC_STATE_RND, + LC_STATE_CARE +} EFI_LIFE_CYCLE_STATE; + +typedef struct _EFI_LIFE_CYCLE_STATE_PROTOCOL EFI_LIFE_CYCLE_STATE_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_GET_LIFE_CYCLE_STATE) ( + IN EFI_LIFE_CYCLE_STATE_PROTOCOL *This, + OUT EFI_LIFE_CYCLE_STATE *LifeCycleState + ); + +struct _EFI_LIFE_CYCLE_STATE_PROTOCOL { + UINT32 Revision; + EFI_GET_LIFE_CYCLE_STATE GetLifeCycleState; +} __attribute__((packed)); + +#endif /* _LIFE_CYCLE_PROTOCOL_H_ */ diff --git a/drivers/lifecycle/lifecycle.c b/drivers/lifecycle/lifecycle.c new file mode 100644 index 0000000..aebc4ec --- /dev/null +++ b/drivers/lifecycle/lifecycle.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include + +#include "lifecycle/LifeCycleProtocol.h" +#include "lifecycle/lifecycle.h" + +#define HECI1_HFS 0x40 + +typedef union hfs1 { + struct { + u32 working_state: 4; /* Current working state */ + u32 manuf_mode: 1; /* Manufacturing mode */ + u32 part_tbl_status: 1; /* Indicates status of flash + * partition table */ + u32 reserved: 25; /* Reserved for further use */ + u32 d0i3_support: 1; /* Indicates D0i3 support */ + } field; + u32 data; +} hfs1_t; + +static struct { + u16 vid; + u16 did; +} SUPPORTED_DEVICES[] ={ + { .vid = 0x8086, .did = 0x5a9a } +}; + +static EFIAPI EFI_STATUS +get_life_cycle_state(__attribute__((__unused__)) EFI_LIFE_CYCLE_STATE_PROTOCOL *This, + EFI_LIFE_CYCLE_STATE *LifeCycleState) +{ + pcidev_t pci_dev = 0; + hfs1_t status; + size_t i; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_DEVICES); i++) + if (pci_find_device(SUPPORTED_DEVICES[i].vid, + SUPPORTED_DEVICES[i].did, + &pci_dev)) + break; + + if (!pci_dev) + return EFI_UNSUPPORTED; + + status.data = pci_read_config32(pci_dev, HECI1_HFS); + + if (status.field.manuf_mode) + *LifeCycleState = LC_STATE_MANUFACTURING; + else + *LifeCycleState = LC_STATE_ENDUSER; + + return EFI_SUCCESS; +} + +static EFI_GUID lifecycle_guid = EFI_LIFE_CYCLE_STATE_PROTOCOL_GUID; +static EFI_HANDLE handle; + +static EFI_STATUS lifecycle_init(EFI_SYSTEM_TABLE *st) +{ + static EFI_LIFE_CYCLE_STATE_PROTOCOL lifecycle_default = { + .Revision = EFI_LIFE_CYCLE_STATE_PROTOCOL_REVISION1, + .GetLifeCycleState = get_life_cycle_state + }; + EFI_LIFE_CYCLE_STATE_PROTOCOL *lifecycle; + + return interface_init(st, &lifecycle_guid, &handle, + &lifecycle_default, sizeof(lifecycle_default), + (void **)&lifecycle); +} + +static EFI_STATUS lifecycle_exit(EFI_SYSTEM_TABLE *st) +{ + return interface_free(st, &lifecycle_guid, handle); +} + +ewdrv_t lifecycle_drv = { + .name = "lifecycle", + .description = "Life Cycle Protocol", + .init = lifecycle_init, + .exit = lifecycle_exit +}; diff --git a/drivers/lifecycle/lifecycle.h b/drivers/lifecycle/lifecycle.h new file mode 100644 index 0000000..ea408d5 --- /dev/null +++ b/drivers/lifecycle/lifecycle.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _LIFECYCLE_H_ +#define _LIFECYCLE_H_ + +#include + +extern ewdrv_t lifecycle_drv; + +#endif /* _LIFECYCLE_H_ */ diff --git a/drivers/lpmemmap/lpmemmap.c b/drivers/lpmemmap/lpmemmap.c new file mode 100644 index 0000000..ed23833 --- /dev/null +++ b/drivers/lpmemmap/lpmemmap.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include + +#include "lpmemmap/lpmemmap.h" +#include + +static UINTN efimemmap_nb; +static EFI_MEMORY_DESCRIPTOR *efimemmap; + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 +#define EFI_MAX_ADDRESS ((UINTN)~0) + +static EFI_STATUS e820_to_efi(unsigned int e820, UINT32 *efi) +{ + switch (e820) { + case E820_RAM: + *efi = EfiConventionalMemory; + return EFI_SUCCESS; + + case E820_RESERVED: + *efi = EfiReservedMemoryType; + return EFI_SUCCESS; + + case E820_ACPI: + *efi = EfiACPIReclaimMemory; + return EFI_SUCCESS; + + case E820_NVS: + *efi = EfiACPIMemoryNVS; + return EFI_SUCCESS; + + case E820_UNUSABLE: + *efi = EfiUnusableMemory; + return EFI_SUCCESS; + + default: + return EFI_NOT_FOUND; + } +} + +static int cmp_mem_descr(const void *a, const void *b) +{ + const EFI_MEMORY_DESCRIPTOR *m1 = a, *m2 = b; + + if (m1->PhysicalStart < m2->PhysicalStart) + return -1; + if (m1->PhysicalStart > m2->PhysicalStart) + return 1; + return 0; +} + +static void free_efimemmap(void) +{ + free(efimemmap); + efimemmap = NULL; + efimemmap_nb = 0; +} + +static EFI_STATUS lpmemmap_to_efimemmap(struct memrange *ranges, size_t nb) +{ + EFI_STATUS ret; + size_t i; + bool sorted = true; + EFI_PHYSICAL_ADDRESS start; + UINT64 size; + + efimemmap = malloc(nb * sizeof(*efimemmap)); + if (!efimemmap) + return EFI_OUT_OF_RESOURCES; + + for (i = 0; i < nb; i++) { + if (ranges[i].base % EFI_PAGE_SIZE || + ranges[i].size % EFI_PAGE_SIZE) { + ewerr("Memory ranges are not %d bytes aligned", + EFI_PAGE_SIZE); + ret = EFI_INVALID_PARAMETER; + goto err; + } + + efimemmap[i].NumberOfPages = ranges[i].size / EFI_PAGE_SIZE; + efimemmap[i].PhysicalStart = ranges[i].base; + ret = e820_to_efi(ranges[i].type, &efimemmap[i].Type); + if (EFI_ERROR(ret)) + goto err; + + if (i > 0 && cmp_mem_descr(&efimemmap[i - 1], &efimemmap[i])) + sorted = false; + } + + if (!sorted) + qsort(efimemmap, nb, sizeof(*efimemmap), cmp_mem_descr); + + /* Sanity check: verify that ranges do not overlap */ + for (i = 0; i < nb - 1; i++) { + start = efimemmap[i].PhysicalStart; + size = efimemmap[i].NumberOfPages * EFI_PAGE_SIZE; + if (start + size > efimemmap[i + 1].PhysicalStart) { + ewerr("Memory ranges are overlapping"); + ret = EFI_INVALID_PARAMETER; + goto err; + } + } + + efimemmap_nb = nb; + return EFI_SUCCESS; + +err: + free_efimemmap(); + return ret; +} + +static void set_mem_descr(EFI_MEMORY_DESCRIPTOR *descr, + EFI_PHYSICAL_ADDRESS start, EFI_PHYSICAL_ADDRESS end, + EFI_MEMORY_TYPE type) +{ + descr->PhysicalStart = start; + descr->NumberOfPages = (end - start) / EFI_PAGE_SIZE; + descr->Type = type; +} + +static EFI_STATUS insert_mem_descr_at(EFI_PHYSICAL_ADDRESS start, EFI_PHYSICAL_ADDRESS end, + EFI_MEMORY_TYPE type, size_t pos) +{ + efimemmap = realloc(efimemmap, ++efimemmap_nb * sizeof(*efimemmap)); + if (!efimemmap) + return EFI_OUT_OF_RESOURCES; + + memmove(&efimemmap[pos + 1], + &efimemmap[pos], + (efimemmap_nb - pos - 1) * sizeof(*efimemmap)); + set_mem_descr(&efimemmap[pos], start, end, type); + + return EFI_SUCCESS; +} + +/* Insert START:END memory descriptor of type TYPE into the first + * memory range of type EfiConventionalMemory that include START:END + * memory region. */ +static EFI_STATUS insert_mem_descr(EFI_PHYSICAL_ADDRESS start, + EFI_PHYSICAL_ADDRESS end, + EFI_MEMORY_TYPE type) +{ + EFI_STATUS ret; + EFI_PHYSICAL_ADDRESS cur_start, cur_end; + EFI_MEMORY_TYPE cur_type; + size_t i; + + if (start >= end) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < efimemmap_nb; i++) { + cur_start = efimemmap[i].PhysicalStart; + cur_end = cur_start + efimemmap[i].NumberOfPages * EFI_PAGE_SIZE; + cur_type = efimemmap[i].Type; + + if (cur_start > start || end > cur_end) + continue; + + if (efimemmap[i].Type != EfiConventionalMemory) + return EFI_INVALID_PARAMETER; + + if (start > cur_start) { + ret = insert_mem_descr_at(cur_start, start, + cur_type, i++); + if (EFI_ERROR(ret)) + return ret; + } + + set_mem_descr(&efimemmap[i], start, end, type); + + if (end < cur_end) + return insert_mem_descr_at(end, cur_end, + cur_type, i + 1); + + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} + +static EFI_CALCULATE_CRC32 crc32; + +static EFIAPI EFI_STATUS +get_memory_map(UINTN *MemoryMapSize, EFI_MEMORY_DESCRIPTOR *MemoryMap, + UINTN *MapKey, UINTN *DescriptorSize, UINT32 *DescriptorVersion) +{ + EFI_STATUS ret; + UINT32 key; + UINTN size; + + if (!MemoryMapSize || !MemoryMap || !MapKey || + !DescriptorSize || !DescriptorVersion) + return EFI_INVALID_PARAMETER; + + if (!efimemmap_nb || !efimemmap) + return EFI_UNSUPPORTED; + + size = efimemmap_nb * sizeof(*efimemmap); + if (size > *MemoryMapSize) { + *MemoryMapSize = size; + return EFI_BUFFER_TOO_SMALL; + } + + ret = uefi_call_wrapper(crc32, 3, efimemmap, size, &key); + if (EFI_ERROR(ret)) + return ret; + + *MemoryMapSize = size; + memcpy(MemoryMap, efimemmap, size); + *MapKey = key; + *DescriptorSize = sizeof(*efimemmap); + *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS allocate_pages(EFI_ALLOCATE_TYPE Type, + EFI_MEMORY_TYPE MemoryType, + UINTN NoPages, + EFI_PHYSICAL_ADDRESS *Memory) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT64 start; + UINT64 end = EFI_MAX_ADDRESS; + UINT64 max_address; + UINT64 number_of_bytes; + EFI_PHYSICAL_ADDRESS cur_start, cur_end; + EFI_MEMORY_TYPE cur_type; + size_t i; + UINTN alignment = EFI_PAGE_SIZE; + + if (Type < AllocateAnyPages || Type >= (UINTN) MaxAllocateType) + return EFI_INVALID_PARAMETER; + + if (NoPages == 0) + return EFI_INVALID_PARAMETER; + + if (((MemoryType >= EfiMaxMemoryType) && (MemoryType <= 0x7fffffff)) || + (MemoryType == EfiConventionalMemory)) + return EFI_INVALID_PARAMETER; + + if ((Type == AllocateAddress) && ((*Memory & (alignment - 1)) != 0)) + return EFI_INVALID_PARAMETER; + + NoPages += EFI_SIZE_TO_PAGES(alignment) - 1; + NoPages &= ~(EFI_SIZE_TO_PAGES(alignment) - 1); + + start = *Memory; + + max_address = EFI_MAX_ADDRESS; + + if (Type == AllocateAddress) { + if (NoPages > RShiftU64(max_address, EFI_PAGE_SHIFT)) + return EFI_NOT_FOUND; + + number_of_bytes = LShiftU64(NoPages, EFI_PAGE_SHIFT); + end = start + number_of_bytes; + + if ((start >= end) || + (start > EFI_MAX_ADDRESS) || + (end > EFI_MAX_ADDRESS)) + return EFI_NOT_FOUND; + } + if (Type == AllocateMaxAddress) + max_address = start; + + if ((max_address < EFI_PAGE_MASK)) + return EFI_NOT_FOUND; + + if ((max_address & EFI_PAGE_MASK) != EFI_PAGE_MASK) { + max_address -= (EFI_PAGE_MASK + 1); + max_address &= ~(UINT64) EFI_PAGE_MASK; + max_address |= EFI_PAGE_MASK; + } + + number_of_bytes = LShiftU64(NoPages, EFI_PAGE_SHIFT); + + for (i = 0; i < efimemmap_nb; i++) { + cur_start = efimemmap[i].PhysicalStart; + cur_end = cur_start + efimemmap[i].NumberOfPages * EFI_PAGE_SIZE; + cur_type = efimemmap[i].Type; + + /* skip the E820 memory range from 0x00000000 to 0x00001000 as allocated address, + which would cause NULL pointer */ + if (cur_start == 0x00000000) { + cur_start = 0x1000; + cur_end = cur_start + (efimemmap[i].NumberOfPages - 1) * EFI_PAGE_SIZE; + } + + if (efimemmap[i].Type != EfiConventionalMemory) + continue; + + if (Type == AllocateAddress) { + if (start < cur_start || cur_end < end) + continue; + } else { + if ((efimemmap[i].NumberOfPages < NoPages) || + (cur_start + number_of_bytes > max_address)) + continue; + start = cur_start; + end = start + number_of_bytes; + } + + if (start > cur_start) { + ret = insert_mem_descr_at(cur_start, start, + cur_type, i++); + if (EFI_ERROR(ret)) + break; + } + + set_mem_descr(&efimemmap[i], start, end, MemoryType); + + if (end < cur_end) + ret = insert_mem_descr_at(end, cur_end, + cur_type, i + 1); + break; + } + + if (i == efimemmap_nb) + return EFI_NOT_FOUND; + + if (!EFI_ERROR(ret)) + *Memory = start; + + return ret; +} + +static EFIAPI EFI_STATUS free_pages(EFI_PHYSICAL_ADDRESS Memory, UINTN NoPages) +{ + EFI_STATUS ret = EFI_SUCCESS; + EFI_PHYSICAL_ADDRESS cur_start, cur_end; + UINT8 i; + UINTN alignment = EFI_PAGE_SIZE; + + if ((Memory & (alignment - 1)) != 0) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < efimemmap_nb; i++) { + cur_start = efimemmap[i].PhysicalStart; + cur_end = cur_start + efimemmap[i].NumberOfPages * EFI_PAGE_SIZE; + + if ((cur_start <= Memory) && (Memory < cur_end)) + break; + } + + if (i == efimemmap_nb) + return EFI_NOT_FOUND; + + NoPages += EFI_SIZE_TO_PAGES(alignment) - 1; + NoPages &= ~(EFI_SIZE_TO_PAGES(alignment) - 1); + + set_mem_descr(&efimemmap[i], cur_start, cur_end, EfiConventionalMemory); + + return ret; +} + +/* Libpayload binary boundaries */ +extern char _start[], _heap[], _end[]; + +static EFI_GET_MEMORY_MAP saved_memmap_bs; + +static EFI_STATUS lpmemmap_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + EFI_PHYSICAL_ADDRESS start, data, end; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!lib_sysinfo.n_memranges) + return EFI_NOT_FOUND; + + ret = lpmemmap_to_efimemmap(lib_sysinfo.memrange, + lib_sysinfo.n_memranges); + if (EFI_ERROR(ret)) + return ret; + + start = ALIGN_DOWN((EFI_PHYSICAL_ADDRESS)(UINTN)_start, EFI_PAGE_SIZE); + data = ALIGN_UP((EFI_PHYSICAL_ADDRESS)(UINTN)_heap, EFI_PAGE_SIZE); + ret = insert_mem_descr(start, data, EfiLoaderCode); + if (EFI_ERROR(ret)) + goto err; + + end = ALIGN_UP((EFI_PHYSICAL_ADDRESS)(UINTN)_end, EFI_PAGE_SIZE); + ret = insert_mem_descr(data, end, EfiLoaderData); + if (EFI_ERROR(ret)) + goto err; + + saved_memmap_bs = st->BootServices->GetMemoryMap; + st->BootServices->GetMemoryMap = get_memory_map; + st->BootServices->AllocatePages = allocate_pages; + st->BootServices->FreePages = free_pages; + crc32 = st->BootServices->CalculateCrc32; + + return EFI_SUCCESS; + +err: + free_efimemmap(); + return ret; +} + +static EFI_STATUS lpmemmap_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + if (efimemmap) { + st->BootServices->GetMemoryMap = saved_memmap_bs; + free_efimemmap(); + } + + return EFI_SUCCESS; +} + +ewdrv_t lpmemmap_drv = { + .name = "lpmemmap", + .description = "Convert Libpayload sysinfo memory map to EFI memory map", + .init = lpmemmap_init, + .exit = lpmemmap_exit +}; diff --git a/drivers/lpmemmap/lpmemmap.h b/drivers/lpmemmap/lpmemmap.h new file mode 100644 index 0000000..908621e --- /dev/null +++ b/drivers/lpmemmap/lpmemmap.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _LPMEMMAP_H_ +#define _LPMEMMAP_H_ + +#include + +extern ewdrv_t lpmemmap_drv; + +#endif /* _LPMEMMAP_H_ */ diff --git a/drivers/lprtc/lprtc.c b/drivers/lprtc/lprtc.c new file mode 100644 index 0000000..918acc7 --- /dev/null +++ b/drivers/lprtc/lprtc.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include + +#include "lprtc/lprtc.h" + +static EFIAPI EFI_STATUS +lprtc_get_time(EFI_TIME *Time, + __attribute__((__unused__)) EFI_TIME_CAPABILITIES *Capabilities) +{ + struct tm now; + + if (!Time) + return EFI_INVALID_PARAMETER; + + rtc_read_clock(&now); + + memset(Time, 0, sizeof(*Time)); + Time->Year = now.tm_year + 1900; + Time->Month = now.tm_mon + 1; + Time->Day = now.tm_mday; + Time->Hour = now.tm_hour; + Time->Minute = now.tm_min; + Time->Second = now.tm_sec; + Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; + + return EFI_SUCCESS; +} + +static EFI_GET_TIME saved_gettime_rs; + +static EFI_STATUS lprtc_init(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + saved_gettime_rs = st->RuntimeServices->GetTime; + st->RuntimeServices->GetTime = lprtc_get_time; + + return EFI_SUCCESS; +} + +static EFI_STATUS lprtc_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + st->RuntimeServices->GetTime = saved_gettime_rs; + + return EFI_SUCCESS; +} + +ewdrv_t lprtc_drv = { + .name = "lprtc", + .description = "Provide the GetTime runtime service support based \ +on the libpayload rtc_read_clock() function.", + .init = lprtc_init, + .exit = lprtc_exit +}; diff --git a/drivers/lprtc/lprtc.h b/drivers/lprtc/lprtc.h new file mode 100644 index 0000000..ee43337 --- /dev/null +++ b/drivers/lprtc/lprtc.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _LPRTC_H_ +#define _LPRTC_H_ + +#include + +extern ewdrv_t lprtc_drv; + +#endif /* _LPRTC_H_ */ diff --git a/drivers/mmc_serial/mmc_serial.c b/drivers/mmc_serial/mmc_serial.c new file mode 100644 index 0000000..741224b --- /dev/null +++ b/drivers/mmc_serial/mmc_serial.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mmc_serial/mmc_serial.h" + +struct cid { + uint8_t mid: 8; + uint8_t reserved: 6; + uint8_t cbx: 2; + uint8_t oid: 8; + char pnm[6]; + uint8_t prv; + uint32_t psn; + uint8_t mdt; + uint8_t crc: 7; + uint8_t unused: 1; +} __attribute__((packed)); + +/* Must be provided by a mmc driver */ +extern int mmc_cid(uint8_t cid[16]); + +/* The serial number is the concatenation of the CID PNM and PSN + * fields. */ +static char *build_serial(void) +{ + static char serial[15]; + struct cid m_cid; + uint8_t *cid = (uint8_t *) &m_cid; + uint32_t sn; + int ret; + + ret = mmc_cid((uint8_t *)&m_cid); + if (ret) + return NULL; + + sn = (cid[9]<<24) | (cid[8]<<16) | (cid[15]<<8) | (cid[14]); + serial[0] = cid[0]; + serial[1] = cid[7]; + serial[2] = cid[6]; + serial[3] = cid[5]; + serial[4] = cid[4]; + serial[5] = cid[11]; + + snprintf(&serial[6], 9, "%08x", sn); + return serial; +} + +static EFI_STATUS mmc_serial_init(__attribute__((__unused__)) EFI_SYSTEM_TABLE *st) +{ + char *serial; + + serial = build_serial(); + if (serial) + smbios_set(1, offsetof(SMBIOS_TYPE1, SerialNumber), serial); + + return EFI_SUCCESS; +} + +ewdrv_t mmc_serial_drv = { + .name = "mmc_serial", + .description = "Set the serial number of the SMBIOS table based on the MMC CID", + .init = mmc_serial_init +}; diff --git a/drivers/mmc_serial/mmc_serial.h b/drivers/mmc_serial/mmc_serial.h new file mode 100644 index 0000000..289482c --- /dev/null +++ b/drivers/mmc_serial/mmc_serial.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _MMC_SERIAL_H_ +#define _MMC_SERIAL_H_ + +#include + +extern ewdrv_t mmc_serial_drv; + +#endif diff --git a/drivers/nvme/NvmCtrlLib.h b/drivers/nvme/NvmCtrlLib.h new file mode 100644 index 0000000..ca93b04 --- /dev/null +++ b/drivers/nvme/NvmCtrlLib.h @@ -0,0 +1,152 @@ +/** @file + +@copyright + Copyright (c) 2008 - 2015 Intel Corporation. All rights reserved. + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + This file contains an 'Intel Pre-EFI Module' and is licensed + for Intel CPUs and Chipsets under the terms of your license + agreement with Intel or your vendor. This file may be + modified by the user, subject to additional terms of the + license agreement. +**/ + +#ifndef __NVM_CTRL_LIB_H__ +#define __NVM_CTRL_LIB_H__ + +typedef UINT64 EFI_PEI_LBA; + +typedef struct { + UINT64 BlockNum; + UINT32 BlockSize; +} DEVICE_BLOCK_INFO; + +typedef enum { + DevInitAll, + DevInitOnlyPhase1, + DevInitOnlyPhase2 +} DEVICE_INIT_PHASE; + + +#pragma pack(1) +typedef struct { + UINT8 Bus; + UINT8 Device; + UINT8 Func; +} PciAddrNvm; + +typedef struct { + PciAddrNvm DeviceAddress; +} NvmCtrlPlatformInfo; +#pragma pack() + + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[out] DevBlockInfo The Block Io information of the specified block partition. + + @retval EFI_SUCCESS The Block Io information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +NvmeGetMediaInfo ( + IN UINTN DeviceIndex, + OUT DEVICE_BLOCK_INFO *DevBlockInfo +); + +/** + This function reads data from Nvme device to Memory. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +NvmeReadBlocks ( + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer +); + +/** + This function writes data from Memory to Nvme device + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartBlock Target EMMC block number(LBA) where data will be written + @param[in] DataSize Total data size to be written in bytes unit + @param[out] DataAddress Data address in Memory to be copied to EMMC + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +NvmeWriteBlocks ( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN DataSize, + IN VOID *DataAddress +); + + +/** + This function initializes Nvme device + @param[in] NvmeHcPciBase MMC Host Controller's PCI ConfigSpace Base address + @param[in] NvmeInitMode For the performance optimization, device initialization can be + separated to early init and the + rest of init. + + DevInitAll : Execute generic device initialization + DevInitOnlyPhase1 : Execute only early phase initialization + DevInitOnlyPhase2 : Skip early phase initialization, and then + initialize the rest of initialization + + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS EFIAPI NvmeInitialize (IN UINTN NvmeHcPciBase); + +EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeGetPassthru(void); + +EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *NvmeGetSecurityInterface(void); + +#endif diff --git a/drivers/nvme/NvmExpress.c b/drivers/nvme/NvmExpress.c new file mode 100644 index 0000000..d0bdf87 --- /dev/null +++ b/drivers/nvme/NvmExpress.c @@ -0,0 +1,463 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + Copyright (c) 2013 - 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 + 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. + +**/ + +#include +#include +#include +#include + +#include "NvmExpress.h" +#include "NvmCtrlLib.h" + +#define PCI_BASE_ADDRESSREG_OFFSET 0x10 + +NVME_CONTROLLER_PRIVATE_DATA *mNvmeCtrlPrivate; +NVME_DEVICE_PRIVATE_DATA *mMultiNvmeDrive[10]; //maxium 10 +NvmCtrlPlatformInfo NvmCtrlInfo = { {1,0,0} }; + +EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeGetPassthru(void) +{ + return (void *)&mNvmeCtrlPrivate->Passthru; +} + +EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *NvmeGetSecurityInterface(void) +{ + NVME_DEVICE_PRIVATE_DATA *device = mMultiNvmeDrive[0]; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *security; + + if (device == NULL) + return NULL; + + security = &device->StorageSecurity; + if (security->SendData == NULL || security->ReceiveData == NULL) + return NULL; + + return security; +} + + +// +// Template for NVM Express Pass Thru Mode data structure. +// +EFI_NVM_EXPRESS_PASS_THRU_MODE gEfiNvmExpressPassThruMode = { + EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL | + EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL | + EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_NONBLOCKIO | + EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM, + sizeof (UINTN), + 0x10100 +}; + +/** + Check if the specified Nvm Express device namespace is active, and create child handles + for them with BlockIo and DiskInfo protocol instances. + + @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be + allocated and built. Caller must set the NamespaceId to zero if the + device path node will contain a valid UUID. + + @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated. + @return Others Some error occurs when enumerating the namespaces. + +**/ +EFI_STATUS +EnumerateNvmeDevNamespace ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + UINT32 NamespaceId + ) +{ + NVME_ADMIN_NAMESPACE_DATA *NamespaceData; + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + UINT32 Lbads; + UINT32 Flbas; + UINT32 LbaFmtIdx; + UINT8 Sn[21]; + UINT8 Mn[41]; + Device = NULL; + + // + // Allocate a buffer for Identify Namespace data + // + NamespaceData = MallocZero(sizeof (NVME_ADMIN_NAMESPACE_DATA)); + if (NamespaceData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Identify Namespace + // + Status = NvmeIdentifyNamespace ( + Private, + NamespaceId, + (VOID *)NamespaceData + ); + if (EFI_ERROR(Status)) { + goto Exit; + } + + // + // Validate Namespace + // + if (NamespaceData->Ncap == 0) { + Status = EFI_DEVICE_ERROR; + } else { + // + // allocate device private data for each discovered namespace + // + Device = MallocZero(sizeof(NVME_DEVICE_PRIVATE_DATA)); + if (Device == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Initialize SSD namespace instance data + // + Device->Signature = NVME_DEVICE_PRIVATE_DATA_SIGNATURE; + Device->NamespaceId = NamespaceId; + Device->NamespaceUuid = NamespaceData->Eui64; + Device->Controller = Private; + + // + // Build BlockIo media structure + // + Device->Media.MediaId = 0; + Device->Media.RemovableMedia = FALSE; + Device->Media.MediaPresent = TRUE; + Device->Media.LogicalPartition = FALSE; + Device->Media.ReadOnly = FALSE; + Device->Media.WriteCaching = FALSE; + Device->Media.IoAlign = Private->PassThruMode.IoAlign; + + Flbas = NamespaceData->Flbas; + LbaFmtIdx = Flbas & 0xF; + Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads; + Device->Media.BlockSize = (UINT32)1 << Lbads; + + Device->Media.LastBlock = NamespaceData->Nsze - 1; + Device->Media.LogicalBlocksPerPhysicalBlock = 1; + Device->Media.LowestAlignedLba = 1; + + // + // Create BlockIo Protocol instance + // + Device->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION2; + Device->BlockIo.Media = &Device->Media; + Device->BlockIo.Reset = (EFI_BLOCK_RESET)NvmeBlockIoReset; + Device->BlockIo.ReadBlocks = (EFI_BLOCK_READ)NvmeBlockIoReadBlocks; + Device->BlockIo.WriteBlocks = (EFI_BLOCK_WRITE)NvmeBlockIoWriteBlocks; + Device->BlockIo.FlushBlocks = (EFI_BLOCK_FLUSH)NvmeBlockIoFlushBlocks; + + InitializeListHead (&Device->AsyncQueue); + + CopyMem (&Device->NamespaceData, NamespaceData, sizeof (NVME_ADMIN_NAMESPACE_DATA)); + mMultiNvmeDrive[NamespaceId - 1] = Device; //NamespaceId is 1 based + + // + // Create StorageSecurityProtocol Instance + // + Device->StorageSecurity.ReceiveData = NvmeStorageSecurityReceiveData; + Device->StorageSecurity.SendData = NvmeStorageSecuritySendData; + DEBUG_NVME((EFI_D_INFO, "## SECURITY_SEND_RECEIVE %d ####\n", (Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED))); + if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) == 0) { + Device->StorageSecurity.ReceiveData = NULL; + Device->StorageSecurity.SendData = NULL; + } + + // + // Dump NvmExpress Identify Namespace Data + // + DEBUG_NVME ((EFI_D_INFO, " == NVME IDENTIFY NAMESPACE [%d] DATA ==\n", NamespaceId)); + DEBUG_NVME ((EFI_D_INFO, " NSZE : 0x%x\n", NamespaceData->Nsze)); + DEBUG_NVME ((EFI_D_INFO, " NCAP : 0x%x\n", NamespaceData->Ncap)); + DEBUG_NVME ((EFI_D_INFO, " NUSE : 0x%x\n", NamespaceData->Nuse)); + DEBUG_NVME ((EFI_D_INFO, " LBAF0.LBADS : 0x%x\n", (NamespaceData->LbaFormat[0].Lbads))); + + // + // Build controller name for Component Name (2) protocol. + // + CopyMem (Sn, Private->ControllerData->Sn, sizeof (Private->ControllerData->Sn)); + Sn[20] = 0; + CopyMem (Mn, Private->ControllerData->Mn, sizeof (Private->ControllerData->Mn)); + Mn[40] = 0; + snprintf(Device->ModelName, sizeof (Device->ModelName), (const char *)"%a-%a-%x", Sn, Mn, NamespaceData->Eui64); + } + +Exit: + if (NamespaceData != NULL) { + FreeZero (NamespaceData); + } + + if (EFI_ERROR(Status) && (Device != NULL)) { + FreeZero (Device); + } + return Status; +} + +/** + Discover all Nvm Express device namespaces, and create child handles for them with BlockIo + and DiskInfo protocol instances. + + @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated. + @return Others Some error occurs when enumerating the namespaces. + +**/ +EFI_STATUS +DiscoverAllNamespaces ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 NamespaceId; + EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *Passthru; + + NamespaceId = 0xFFFFFFFF; + Passthru = &Private->Passthru; + + while (TRUE) { + Status = Passthru->GetNextNamespace ( + Passthru, + (UINT32 *)&NamespaceId + ); + + if (EFI_ERROR (Status)) { + break; + } + + Status = EnumerateNvmeDevNamespace ( + Private, + NamespaceId + ); + + if (EFI_ERROR(Status)) { + continue; + } + } + + return EFI_SUCCESS; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] VOID + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +NvmeInitialize ( + IN UINTN NvmeHcPciBase + ) +{ + void *aligned_buf; + EFI_STATUS Status; + NVME_CONTROLLER_PRIVATE_DATA *Private; + + DEBUG_NVME ((EFI_D_INFO, "NvmeInitialize:\n")); + + Private = NULL; + + // + // Check EFI_ALREADY_STARTED to reuse the original NVME_CONTROLLER_PRIVATE_DATA. + // + Private = MallocZero (sizeof (NVME_CONTROLLER_PRIVATE_DATA)); + if (Private == NULL) { + DEBUG_NVME ((EFI_D_ERROR, "NvmeInitialize: allocating pool for Nvme Private Data failed!\n")); + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + //DEBUG_NVME ((EFI_D_INFO, "NvmeControllerInit: NvmeHCBase = 0x%X\n", NvmeHcPciBase)); + + // + // 6 x 4kB aligned buffers will be carved out of this buffer. + // 1st 4kB boundary is the start of the admin submission queue. + // 2nd 4kB boundary is the start of the admin completion queue. + // 3rd 4kB boundary is the start of I/O submission queue #1. + // 4th 4kB boundary is the start of I/O completion queue #1. + // 5th 4kB boundary is the start of I/O submission queue #2. + // 6th 4kB boundary is the start of I/O completion queue #2. + // + // Allocate 6 pages of memory, then map it for bus master read and write. + // + aligned_buf = nvme_alloc_pages(6); + + Private->Buffer = (UINT8 *)aligned_buf; + Private->Signature = NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE; + Private->NvmeHCBase = read32((void *)(NvmeHcPciBase + PCI_BASE_ADDRESSREG_OFFSET)) & 0xFFFFF000; + Private->Passthru.Mode = &Private->PassThruMode; + Private->Passthru.PassThru = (EFI_NVM_EXPRESS_PASS_THRU_PASSTHRU)NvmExpressPassThru; + Private->Passthru.GetNextNamespace = NvmExpressGetNextNamespace; + Private->Passthru.GetNamespace = NvmExpressGetNamespace; + CopyMem (&Private->PassThruMode, &gEfiNvmExpressPassThruMode, sizeof (EFI_NVM_EXPRESS_PASS_THRU_MODE)); + InitializeListHead (&Private->AsyncPassThruQueue); + InitializeListHead (&Private->UnsubmittedSubtasks); + + uint32_t addr; + addr = pci_read_config32(NvmeHcPciBase, PCI_BASE_ADDRESS_0); + Private->NvmeHCBase = (addr & ~0xf); + DEBUG_NVME ((EFI_D_INFO, "NvmeControllerInit: NvmeHCBase = 0x%X\n", addr)); + + Status = NvmeControllerInit (Private); + if (EFI_ERROR(Status)) { + goto Exit; + } + mNvmeCtrlPrivate = Private; + + Status = DiscoverAllNamespaces ( + Private + ); + + DEBUG_NVME ((EFI_D_INFO, "NvmeInitialize: end successfully\n")); + return EFI_SUCCESS; + +Exit: + if (EFI_ERROR (Status)) { + if ((Private != NULL) && (Private->ControllerData != NULL)) { + FreeZero (Private->ControllerData); + } + } + + DEBUG_NVME ((EFI_D_INFO, "NvmeInitialize: end with 0x%X\n", Status)); + + return Status; +} + + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[out] DevBlockInfo The Block Io information of the specified block partition. + + @retval EFI_SUCCESS The Block Io information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +NvmeGetMediaInfo ( + IN UINTN DeviceIndex, + OUT DEVICE_BLOCK_INFO *DevBlockInfo + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + + DevBlockInfo->BlockNum = 0; + DevBlockInfo->BlockSize = 0; + if (mMultiNvmeDrive[DeviceIndex] == NULL) + return EFI_DEVICE_ERROR; + + Media = &mMultiNvmeDrive[DeviceIndex]->Media; + + DevBlockInfo->BlockNum = Media->LastBlock+1; + DevBlockInfo->BlockSize = Media->BlockSize; + return EFI_SUCCESS; +} + +/** + This function reads data from Nvme device to Memory. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +NvmeReadBlocks ( + IN UINTN DeviceIndex __attribute__((unused)), + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + Status = NvmeBlockIoReadBlocks(&mMultiNvmeDrive[0]->BlockIo, 0, StartLBA, BufferSize, Buffer); + + return Status; +} + +/** + This function writes data from Memory to Nvme device + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartBlock Target EMMC block number(LBA) where data will be written + @param[in] DataSize Total data size to be written in bytes unit + @param[out] DataAddress Data address in Memory to be copied to EMMC + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +NvmeWriteBlocks ( + IN UINTN DeviceIndex __attribute__((unused)), + IN EFI_LBA StartLBA, + IN UINTN DataSize, + IN VOID *DataAddress + ) +{ + EFI_STATUS Status; + + Status = NvmeBlockIoWriteBlocks(&mMultiNvmeDrive[0]->BlockIo, 0, StartLBA, DataSize, DataAddress); + + return Status; +} + diff --git a/drivers/nvme/NvmExpress.h b/drivers/nvme/NvmExpress.h new file mode 100644 index 0000000..2abef50 --- /dev/null +++ b/drivers/nvme/NvmExpress.h @@ -0,0 +1,448 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2013 - 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 + 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 _EFI_NVM_EXPRESS_H_ +#define _EFI_NVM_EXPRESS_H_ + +#include +#include +#include + +#include "NvmInternal.h" +#include "protocol/StorageSecurityCommand.h" + +#define nvme_dbg(a, ...) { printf(__VA_ARGS__); } +#define DEBUG_NVME(a) (nvme_dbg a) + +#define DEBUG_CODE_BEGIN() if (0) { +#define DEBUG_CODE_END() } + + +typedef struct _NVME_CONTROLLER_PRIVATE_DATA NVME_CONTROLLER_PRIVATE_DATA; +typedef struct _NVME_DEVICE_PRIVATE_DATA NVME_DEVICE_PRIVATE_DATA; +typedef struct _NVME_BLKIO2_SUBTASK NVME_BLKIO2_SUBTASK; +typedef +VOID +(EFIAPI *ASYNC_IO_CALL_BACK)( + IN NVME_BLKIO2_SUBTASK *SubtaskPtr +); + +#include "NvmExpressPassthru.h" +#include "NvmExpressBlockIo.h" +#include "NvmExpressHci.h" + + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT30 0x40000000 + +#define MallocZero(size) calloc(size, 1) +#define FreeZero free + +VOID *EFIAPI nvme_alloc_pages (IN UINTN Pages); +VOID EFIAPI nvme_free_pages (IN VOID *Buffer); + +UINTN NanoSecondDelay (UINTN NanoSeconds); + + +/** + Returns a 16-bit signature built from 2 ASCII characters. + + This macro returns a 16-bit value built from the two ASCII characters specified + by A and B. + + @param A The first ASCII character. + @param B The second ASCII character. + + @return A 16-bit value built from the two ASCII characters specified by A and B. + +**/ +#define SIGNATURE_16(A, B) ((A) | (B << 8)) + +/** + Returns a 32-bit signature built from 4 ASCII characters. + + This macro returns a 32-bit value built from the four ASCII characters specified + by A, B, C, and D. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + + @return A 32-bit value built from the two ASCII characters specified by A, B, + C and D. + +**/ +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) + +#define EFI_TIMER_PERIOD_MILLISECONDS(Milliseconds) MultU64x32((UINT64)(Milliseconds), 10000) +#define EFI_TIMER_PERIOD_SECONDS(Seconds) MultU64x32((UINT64)(Seconds), 10000000) + +#define PCI_CLASS_MASS_STORAGE_NVM 0x08 // mass storage sub-class non-volatile memory. +#define PCI_IF_NVMHCI 0x02 // mass storage programming interface NVMHCI. + +#define NVME_ASQ_SIZE 1 // Number of admin submission queue entries, which is 0-based +#define NVME_ACQ_SIZE 1 // Number of admin completion queue entries, which is 0-based + +#define NVME_CSQ_SIZE 1 // Number of I/O submission queue entries, which is 0-based +#define NVME_CCQ_SIZE 1 // Number of I/O completion queue entries, which is 0-based + +// +// Number of asynchronous I/O submission queue entries, which is 0-based. +// The asynchronous I/O submission queue size is 4kB in total. +// +#define NVME_ASYNC_CSQ_SIZE 63 +// +// Number of asynchronous I/O completion queue entries, which is 0-based. +// The asynchronous I/O completion queue size is 4kB in total. +// +#define NVME_ASYNC_CCQ_SIZE 255 + +#define NVME_MAX_QUEUES 3 // Number of queues supported by the driver + +#define NVME_CONTROLLER_ID 0 + +// +// Time out value for Nvme transaction execution +// +#define NVME_GENERIC_TIMEOUT EFI_TIMER_PERIOD_SECONDS (5) + +// +// Nvme async transfer timer interval, set by experience. +// +#define NVME_HC_ASYNC_TIMER EFI_TIMER_PERIOD_MILLISECONDS (1) + +// +// Unique signature for private data structure. +// +#define NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('N','V','M','E') + +// +// Nvme private data structure. +// +struct _NVME_CONTROLLER_PRIVATE_DATA { + UINT32 Signature; + UINT32 NvmeHCBase; + UINT64 PciAttributes; + EFI_NVM_EXPRESS_PASS_THRU_MODE PassThruMode; + EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL Passthru; + + // + // pointer to identify controller data + // + NVME_ADMIN_CONTROLLER_DATA *ControllerData; + + // + // 6 x 4kB aligned buffers will be carved out of this buffer. + // 1st 4kB boundary is the start of the admin submission queue. + // 2nd 4kB boundary is the start of the admin completion queue. + // 3rd 4kB boundary is the start of I/O submission queue #1. + // 4th 4kB boundary is the start of I/O completion queue #1. + // 5th 4kB boundary is the start of I/O submission queue #2. + // 6th 4kB boundary is the start of I/O completion queue #2. + // + UINT8 *Buffer; + + // + // Pointers to 4kB aligned submission & completion queues. + // + NVME_SQ *SqBuffer[NVME_MAX_QUEUES]; + NVME_CQ *CqBuffer[NVME_MAX_QUEUES]; + + // + // Submission and completion queue indices. + // + NVME_SQTDBL SqTdbl[NVME_MAX_QUEUES]; + NVME_CQHDBL CqHdbl[NVME_MAX_QUEUES]; + UINT16 AsyncSqHead; + + UINT8 Pt[NVME_MAX_QUEUES]; + UINT16 Cid[NVME_MAX_QUEUES]; + + // + // Nvme controller capabilities + // + NVME_CAP Cap; + + VOID *Mapping; + + // + // For Non-blocking operations. + // + EFI_EVENT TimerEvent; + LIST_ENTRY AsyncPassThruQueue; + LIST_ENTRY UnsubmittedSubtasks; +}; + +#define NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU(a) \ + CR (a, \ + NVME_CONTROLLER_PRIVATE_DATA, \ + Passthru, \ + NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE \ + ) + +// +// Unique signature for private data structure. +// +#define NVME_DEVICE_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('X','S','S','D') + +// +// Nvme device private data structure +// +struct _NVME_DEVICE_PRIVATE_DATA { + UINT32 Signature; + UINT32 NamespaceId; + UINT64 NamespaceUuid; + + EFI_BLOCK_IO_MEDIA Media; + EFI_BLOCK_IO_PROTOCOL BlockIo; + + LIST_ENTRY AsyncQueue; + + EFI_LBA NumBlocks; + + char ModelName[80]; + NVME_ADMIN_NAMESPACE_DATA NamespaceData; + + NVME_CONTROLLER_PRIVATE_DATA *Controller; + + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL StorageSecurity; +}; + +// +// Statments to retrieve the private data from produced protocols. +// +#define NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO(a) \ + CR (a, \ + NVME_DEVICE_PRIVATE_DATA, \ + BlockIo, \ + NVME_DEVICE_PRIVATE_DATA_SIGNATURE \ + ) + +#define NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2(a) \ + CR (a, \ + NVME_DEVICE_PRIVATE_DATA, \ + BlockIo2, \ + NVME_DEVICE_PRIVATE_DATA_SIGNATURE \ + ) + +#define NVME_DEVICE_PRIVATE_DATA_FROM_DISK_INFO(a) \ + CR (a, \ + NVME_DEVICE_PRIVATE_DATA, \ + DiskInfo, \ + NVME_DEVICE_PRIVATE_DATA_SIGNATURE \ + ) + +#define NVME_DEVICE_PRIVATE_DATA_FROM_STORAGE_SECURITY(a)\ + CR (a, \ + NVME_DEVICE_PRIVATE_DATA, \ + StorageSecurity, \ + NVME_DEVICE_PRIVATE_DATA_SIGNATURE \ + ) + +// +// Nvme block I/O 2 request. +// +#define NVME_BLKIO2_REQUEST_SIGNATURE SIGNATURE_32 ('N', 'B', '2', 'R') + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + +// EFI_BLOCK_IO2_TOKEN *Token; + UINTN UnsubmittedSubtaskNum; + BOOLEAN LastSubtaskSubmitted; + // + // The queue for Nvme read/write sub-tasks of a BlockIo2 request. + // + LIST_ENTRY SubtasksQueue; +} NVME_BLKIO2_REQUEST; + +#define NVME_BLKIO2_REQUEST_FROM_LINK(a) \ + CR (a, NVME_BLKIO2_REQUEST, Link, NVME_BLKIO2_REQUEST_SIGNATURE) + +#define NVME_BLKIO2_SUBTASK_SIGNATURE SIGNATURE_32 ('N', 'B', '2', 'S') + + +struct _NVME_BLKIO2_SUBTASK{ + UINT32 Signature; + LIST_ENTRY Link; + + BOOLEAN IsLast; + UINT32 NamespaceId; + ASYNC_IO_CALL_BACK Event; + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *CommandPacket; + // + // The BlockIo2 request this subtask belongs to + // + NVME_BLKIO2_REQUEST *BlockIo2Request; +}; + +#define NVME_BLKIO2_SUBTASK_FROM_LINK(a) \ + CR (a, NVME_BLKIO2_SUBTASK, Link, NVME_BLKIO2_SUBTASK_SIGNATURE) + +#define NVME_BLKIO2_SUBTASK_FROM_EVENT(a) \ + CR(a, NVME_BLKIO2_SUBTASK, Event, NVME_BLKIO2_SUBTASK_SIGNATURE) + +// +// Nvme asynchronous passthru request. +// +#define NVME_PASS_THRU_ASYNC_REQ_SIG SIGNATURE_32 ('N', 'P', 'A', 'R') + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet; + UINT16 CommandId; + VOID *MapPrpList; + UINTN PrpListNo; + VOID *PrpListHost; + VOID *MapData; + VOID *MapMeta; + ASYNC_IO_CALL_BACK CallerEvent; +} NVME_PASS_THRU_ASYNC_REQ; + +#define NVME_PASS_THRU_ASYNC_REQ_FROM_THIS(a) \ + CR (a, \ + NVME_PASS_THRU_ASYNC_REQ, \ + Link, \ + NVME_PASS_THRU_ASYNC_REQ_SIG \ + ) + +/** + Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports + both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking + I/O functionality is optional. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will be sent. + A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace + ID specifies that the command packet should be sent to all valid namespaces. + @param[in,out] Packet A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified + by NamespaceId. + + + @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred + to, or from DataBuffer. + @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred + is returned in TransferLength. + @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller + may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet. + @retval EFI_INVALID_PARAMETER Namespace, or the contents of EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM + Express Command Packet was not sent, so no additional status information is available. + @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the host adapter. + The NVM Express Command Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute. + +**/ +EFI_STATUS +EFIAPI +NvmExpressPassThru ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet, + IN ASYNC_IO_CALL_BACK *Event OPTIONAL + ); + +/** + Used to retrieve the next namespace ID for this NVM Express controller. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves the next valid + namespace ID on this NVM Express controller. + + If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first valid namespace + ID defined on the NVM Express controller is returned in the location pointed to by NamespaceId + and a status of EFI_SUCCESS is returned. + + If on input the value pointed to by NamespaceId is an invalid namespace ID other than 0xFFFFFFFF, + then EFI_INVALID_PARAMETER is returned. + + If on input the value pointed to by NamespaceId is a valid namespace ID, then the next valid + namespace ID on the NVM Express controller is returned in the location pointed to by NamespaceId, + and EFI_SUCCESS is returned. + + If the value pointed to by NamespaceId is the namespace ID of the last namespace on the NVM + Express controller, then EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express + namespace present on the NVM Express controller. On output, a + pointer to the next NamespaceId of an NVM Express namespace on + an NVM Express controller. An input value of 0xFFFFFFFF retrieves + the first NamespaceId for an NVM Express namespace present on an + NVM Express controller. + + @retval EFI_SUCCESS The Namespace ID of the next Namespace was returned. + @retval EFI_NOT_FOUND There are no more namespaces defined on this controller. + @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than 0xFFFFFFFF. + +**/ +EFI_STATUS +EFIAPI +NvmExpressGetNextNamespace ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN OUT UINT32 *NamespaceId + ); + + +/** + Used to translate a device path node to a namespace ID. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamespace() function determines the namespace ID associated with the + namespace described by DevicePath. + + If DevicePath is a device path node type that the NVM Express Pass Thru driver supports, then the NVM Express + Pass Thru driver will attempt to translate the contents DevicePath into a namespace ID. + + If this translation is successful, then that namespace ID is returned in NamespaceId, and EFI_SUCCESS is returned + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on + the NVM Express controller. + @param[out] NamespaceId The NVM Express namespace ID contained in the device path node. + + @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId. + @retval EFI_INVALID_PARAMETER If DevicePath or NamespaceId are NULL, then EFI_INVALID_PARAMETER is returned. + @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver + supports, then EFI_UNSUPPORTED is returned. + @retval EFI_NOT_FOUND If DevicePath is a device path node type that the NVM Express Pass Thru driver + supports, but there is not a valid translation from DevicePath to a namespace ID, + then EFI_NOT_FOUND is returned. +**/ +EFI_STATUS +EFIAPI +NvmExpressGetNamespace ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT32 *NamespaceId + ); + + +/** + Dump the execution status from a given completion queue entry. + + @param[in] Cq A pointer to the NVME_CQ item. + +**/ +VOID +NvmeDumpStatus ( + IN NVME_CQ *Cq + ); + +#endif diff --git a/drivers/nvme/NvmExpressBlockIo.c b/drivers/nvme/NvmExpressBlockIo.c new file mode 100644 index 0000000..a045b1f --- /dev/null +++ b/drivers/nvme/NvmExpressBlockIo.c @@ -0,0 +1,847 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + Copyright (c) 2013 - 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 + 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. +**/ + +#include +#include + +#include "NvmExpress.h" + +/** + Read some sectors from the device. + + @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure. + @param Buffer The buffer used to store the data read from the device. + @param Lba The start block number. + @param Blocks Total block number to be read. + + @retval EFI_SUCCESS Datum are read from the device. + @retval Others Fail to read all the datum. +**/ +EFI_STATUS +ReadSectors ( + IN NVME_DEVICE_PRIVATE_DATA *Device, + IN UINT64 Buffer, + IN UINT64 Lba, + IN UINT32 Blocks +) +{ + NVME_CONTROLLER_PRIVATE_DATA *Private; + UINT32 Bytes; + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + UINT32 BlockSize; + + Private = Device->Controller; + BlockSize = Device->Media.BlockSize; + Bytes = Blocks * BlockSize; + + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC; + CommandPacket.NvmeCmd->Nsid = Device->NamespaceId; + CommandPacket.TransferBuffer = (VOID *)(UINTN)Buffer; + + CommandPacket.TransferLength = Bytes; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_IO_QUEUE; + + CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba; + CommandPacket.NvmeCmd->Cdw11 = (UINT32)RShiftU64(Lba, 32); + CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF; + + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + Device->NamespaceId, + &CommandPacket, + NULL + ); + + return Status; +} + +/** + Write some sectors to the device. + + @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure. + @param Buffer The buffer to be written into the device. + @param Lba The start block number. + @param Blocks Total block number to be written. + + @retval EFI_SUCCESS Datum are written into the buffer. + @retval Others Fail to write all the datum. +**/ +EFI_STATUS +WriteSectors ( + IN NVME_DEVICE_PRIVATE_DATA *Device, + IN UINT64 Buffer, + IN UINT64 Lba, + IN UINT32 Blocks +) +{ + NVME_CONTROLLER_PRIVATE_DATA *Private; + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + UINT32 Bytes; + UINT32 BlockSize; + + Private = Device->Controller; + BlockSize = Device->Media.BlockSize; + Bytes = Blocks * BlockSize; + + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC; + CommandPacket.NvmeCmd->Nsid = Device->NamespaceId; + CommandPacket.TransferBuffer = (VOID *)(UINTN)Buffer; + + CommandPacket.TransferLength = Bytes; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_IO_QUEUE; + + CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba; + CommandPacket.NvmeCmd->Cdw11 = (UINT32)RShiftU64(Lba, 32); + // + // Set Force Unit Access bit (bit 30) to use write-through behaviour + // + CommandPacket.NvmeCmd->Cdw12 = ((Blocks - 1) & 0xFFFF) | BIT30; + + CommandPacket.MetadataBuffer = NULL; + CommandPacket.MetadataLength = 0; + + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + Device->NamespaceId, + &CommandPacket, + NULL + ); + + return Status; +} + +/** + Read some blocks from the device. + + @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure. + @param Buffer The buffer used to store the data read from the device. + @param Lba The start block number. + @param Blocks Total block number to be read. + + @retval EFI_SUCCESS Datum are read from the device. + @retval Others Fail to read all the datum. +**/ +EFI_STATUS +NvmeRead ( + IN NVME_DEVICE_PRIVATE_DATA *Device, + OUT VOID *Buffer, + IN UINT64 Lba, + IN UINTN Blocks +) +{ + EFI_STATUS Status; + UINT32 BlockSize; + NVME_CONTROLLER_PRIVATE_DATA *Private; + UINT32 MaxTransferBlocks; + BOOLEAN IsEmpty; + + // + // Wait for the device's asynchronous I/O queue to become empty. + // + while (TRUE) { + IsEmpty = IsListEmpty (&Device->AsyncQueue); + + if (IsEmpty) + break; + + NanoSecondDelay(100 * 1000); + } + + Status = EFI_SUCCESS; + Private = Device->Controller; + BlockSize = Device->Media.BlockSize; + + if (Private->ControllerData->Mdts != 0) { + MaxTransferBlocks = (1 << (Private->ControllerData->Mdts)) * (1 << (Private->Cap.Mpsmin + 12)) / BlockSize; + } else { + MaxTransferBlocks = 1024; + } + + while (Blocks > 0) { + if (Blocks >= MaxTransferBlocks) { + Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks); + Blocks -= MaxTransferBlocks; + Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize); + Lba += MaxTransferBlocks; + } else { + Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks); + Blocks = 0; + } + + if (EFI_ERROR(Status)) + break; + } + + return Status; +} + +/** + Write some blocks to the device. + + @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure. + @param Buffer The buffer to be written into the device. + @param Lba The start block number. + @param Blocks Total block number to be written. + + @retval EFI_SUCCESS Datum are written into the buffer. + @retval Others Fail to write all the datum. +**/ +EFI_STATUS +NvmeWrite ( + IN NVME_DEVICE_PRIVATE_DATA *Device, + IN VOID *Buffer, + IN UINT64 Lba, + IN UINTN Blocks +) +{ + EFI_STATUS Status; + UINT32 BlockSize; + NVME_CONTROLLER_PRIVATE_DATA *Private; + UINT32 MaxTransferBlocks; + BOOLEAN IsEmpty; + + // + // Wait for the device's asynchronous I/O queue to become empty. + // + while (TRUE) { + IsEmpty = IsListEmpty (&Device->AsyncQueue); + + if (IsEmpty) { + break; + } + + NanoSecondDelay(100 * 1000); + } + + Status = EFI_SUCCESS; + Private = Device->Controller; + BlockSize = Device->Media.BlockSize; + + if (Private->ControllerData->Mdts != 0) { + MaxTransferBlocks = (1 << (Private->ControllerData->Mdts)) * (1 << (Private->Cap.Mpsmin + 12)) / BlockSize; + } else { + MaxTransferBlocks = 1024; + } + + while (Blocks > 0) { + if (Blocks >= MaxTransferBlocks) { + Status = WriteSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks); + + Blocks -= MaxTransferBlocks; + Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize); + Lba += MaxTransferBlocks; + } else { + Status = WriteSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks); + Blocks = 0; + } + + if (EFI_ERROR(Status)) { + break; + } + } + + return Status; +} + +/** + Flushes all modified data to the device. + + @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS Datum are written into the buffer. + @retval Others Fail to write all the datum. +**/ +EFI_STATUS +NvmeFlush ( + IN NVME_DEVICE_PRIVATE_DATA *Device +) +{ + NVME_CONTROLLER_PRIVATE_DATA *Private; + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + Private = Device->Controller; + + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC; + CommandPacket.NvmeCmd->Nsid = Device->NamespaceId; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_IO_QUEUE; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + Device->NamespaceId, + &CommandPacket, + NULL + ); + + return Status; +} + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification __attribute__((unused)) +) +{ + NVME_CONTROLLER_PRIVATE_DATA *Private; + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // For Nvm Express subsystem, reset block device means reset controller. + // + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This); + + Private = Device->Controller; + + Status = NvmeControllerInit (Private); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param 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 data was read correctly from the device. + @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 does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the 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. +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer +) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN IoAlign; + + // + // Check parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Media = This->Media; + if (MediaId != Media->MediaId) + return EFI_MEDIA_CHANGED; + + if (Buffer == NULL) + return EFI_INVALID_PARAMETER; + + if (BufferSize == 0) + return EFI_SUCCESS; + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) + return EFI_BAD_BUFFER_SIZE; + + NumberOfBlocks = BufferSize / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) + return EFI_INVALID_PARAMETER; + + IoAlign = Media->IoAlign; + if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) + return EFI_INVALID_PARAMETER; + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This); + Status = NvmeRead (Device, Buffer, Lba, NumberOfBlocks); + + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @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_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. +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer +) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN IoAlign; + + // + // Check parameters. + // + if (This == NULL) + return EFI_INVALID_PARAMETER; + + Media = This->Media; + + if (MediaId != Media->MediaId) + return EFI_MEDIA_CHANGED; + + if (Buffer == NULL) + return EFI_INVALID_PARAMETER; + + if (BufferSize == 0) + return EFI_SUCCESS; + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) + return EFI_BAD_BUFFER_SIZE; + + NumberOfBlocks = BufferSize / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) + return EFI_INVALID_PARAMETER; + + IoAlign = Media->IoAlign; + if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) + return EFI_INVALID_PARAMETER; + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This); + Status = NvmeWrite (Device, Buffer, Lba, NumberOfBlocks); + + return Status; +} + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device. + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data. + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This +) +{ + NVME_DEVICE_PRIVATE_DATA *Device; + EFI_STATUS Status; + + // + // Check parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This); + + Status = NvmeFlush (Device); + + return Status; +} + +/** + Trust transfer data from/to NVMe device. + + This function performs one NVMe transaction to do a trust transfer from/to NVMe device. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Buffer The pointer to the current transaction buffer. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param TransferLength The block number or sector count of the transfer. + @param IsTrustSend Indicates whether it is a trust send operation or not. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param TransferLengthOut A pointer to a buffer to store the size in bytes of the data + written to the buffer. Ignore it when IsTrustSend is TRUE. + + @retval EFI_SUCCESS The data transfer is complete successfully. + @return others Some error occurs when transferring data. + +**/ +EFI_STATUS +TrustTransferNvmeDevice ( + IN OUT NVME_CONTROLLER_PRIVATE_DATA *Private, + IN OUT VOID *Buffer, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN TransferLength, + IN BOOLEAN IsTrustSend, + IN UINT64 Timeout, + OUT UINTN *TransferLengthOut +) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + if (IsTrustSend) { + Command.Cdw0.Opcode = NVME_ADMIN_SECURITY_SEND_CMD; + CommandPacket.TransferBuffer = Buffer; + CommandPacket.TransferLength = (UINT32)TransferLength; + CommandPacket.NvmeCmd->Cdw10 = (UINT32)((SecurityProtocolId << 24) | (SecurityProtocolSpecificData << 8)); + CommandPacket.NvmeCmd->Cdw11 = (UINT32)TransferLength; + } else { + Command.Cdw0.Opcode = NVME_ADMIN_SECURITY_RECEIVE_CMD; + CommandPacket.TransferBuffer = Buffer; + CommandPacket.TransferLength = (UINT32)TransferLength; + CommandPacket.NvmeCmd->Cdw10 = (UINT32)((SecurityProtocolId << 24) | (SecurityProtocolSpecificData << 8)); + CommandPacket.NvmeCmd->Cdw11 = (UINT32)TransferLength; + } + + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + CommandPacket.NvmeCmd->Nsid = NVME_CONTROLLER_ID; + CommandPacket.CommandTimeout = Timeout; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + NVME_CONTROLLER_ID, + &CommandPacket, + NULL + ); + + if (!IsTrustSend) { + if (EFI_ERROR (Status)) { + *TransferLengthOut = 0; + } else { + *TransferLengthOut = (UINTN) TransferLength; + } + } + + return Status; +} + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @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_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +NvmeStorageSecurityReceiveData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize +) +{ + EFI_STATUS Status; + NVME_DEVICE_PRIVATE_DATA *Device; + + Status = EFI_SUCCESS; + + if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL) || (PayloadBufferSize == 0)) + return EFI_INVALID_PARAMETER; + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_STORAGE_SECURITY(This); + + if (MediaId != Device->BlockIo.Media->MediaId) + return EFI_MEDIA_CHANGED; + + if (!Device->BlockIo.Media->MediaPresent) + return EFI_NO_MEDIA; + + Status = TrustTransferNvmeDevice( + Device->Controller, + PayloadBuffer, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + FALSE, + Timeout, + PayloadTransferSize + ); + + return Status; +} + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the send data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @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_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +NvmeStorageSecuritySendData( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer +) +{ + EFI_STATUS Status; + NVME_DEVICE_PRIVATE_DATA *Device; + + Status = EFI_SUCCESS; + + if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) + return EFI_INVALID_PARAMETER; + + Device = NVME_DEVICE_PRIVATE_DATA_FROM_STORAGE_SECURITY(This); + + if (MediaId != Device->BlockIo.Media->MediaId) + return EFI_MEDIA_CHANGED; + + if (!Device->BlockIo.Media->MediaPresent) + return EFI_NO_MEDIA; + + Status = TrustTransferNvmeDevice( + Device->Controller, + PayloadBuffer, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + TRUE, + Timeout, + NULL + ); + + return Status; +} + diff --git a/drivers/nvme/NvmExpressBlockIo.h b/drivers/nvme/NvmExpressBlockIo.h new file mode 100644 index 0000000..587b33d --- /dev/null +++ b/drivers/nvme/NvmExpressBlockIo.h @@ -0,0 +1,271 @@ +/** @file +Header file for EFI_BLOCK_IO_PROTOCOL interface. + +Copyright (c) 2013 - 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 +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 _EFI_NVME_BLOCKIO_H_ +#define _EFI_NVME_BLOCKIO_H_ + +#ifndef EFI_BLOCK_IO_PROTOCOL +#define EFI_BLOCK_IO_PROTOCOL EFI_BLOCK_IO +#endif + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification +); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param 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 data was read correctly from the device. + @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 does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the 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. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer +); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @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_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. +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer +); + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This +); + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @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_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +NvmeStorageSecurityReceiveData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize +); + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @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_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +NvmeStorageSecuritySendData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer +); + + +#endif diff --git a/drivers/nvme/NvmExpressHci.c b/drivers/nvme/NvmExpressHci.c new file mode 100644 index 0000000..f5fffae --- /dev/null +++ b/drivers/nvme/NvmExpressHci.c @@ -0,0 +1,972 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + Copyright (c) 2013 - 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 + 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. + +**/ + +#include +#include +#include + +#include "NvmExpress.h" + +/** + Used to serialize load and store operations. + + All loads and stores that proceed calls to this function are guaranteed to be + globally visible when this function returns. + +**/ +static inline VOID EFIAPI MemoryFence (VOID) +{ + // This is a little bit of overkill and it is more about the compiler that it is + // actually processor synchronization. This is like the _ReadWriteBarrier + // Microsoft specific intrinsic + __asm__ __volatile__ ("":::"memory"); +} + +/** + Read/Write specified NVM host controller mmio register. + + @param[in] Address Host controller mmio base address. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The Data is NULL or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +NvmHcRwMmio ( + IN UINT32 Address, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data +) +{ + if ((Address == 0x0) || (Data == NULL)) + return EFI_INVALID_PARAMETER; + + if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) + return EFI_INVALID_PARAMETER; + + MemoryFence (); + switch (Count) { + case 1: + if (Read) + *(UINT8 *)Data = read8((void *)(Address + Offset)); + else + write8((void *)(Address + Offset), *(UINT8 *)Data); + break; + + case 2: + if (Read) + *(UINT16 *)Data = read16((void *)(Address + Offset)); + else + write16((void *)(Address + Offset), *(UINT16 *)Data); + break; + + case 4: + if (Read) + *(UINT32 *)Data = read32((void *)(Address + Offset)); + else + write32((void *)(Address + Offset), *(UINT32 *)Data); + break; + + case 8: + if (Read) + *(UINT64 *)Data = *(volatile UINT64 *)(Address + Offset); + else + *(volatile UINT64 *)(Address + Offset) = *(UINT64 *)Data; + break; + + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + MemoryFence (); + + return EFI_SUCCESS; +} + +/** + Read Nvm Express controller capability register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Cap The buffer used to store capability register content. + + @return EFI_SUCCESS Successfully read the controller capability register content. + @return EFI_DEVICE_ERROR Fail to read the controller capability register. + +**/ +EFI_STATUS +ReadNvmeControllerCapabilities ( + IN UINT32 Address, + IN NVME_CAP *Capability +) +{ + EFI_STATUS Status; + UINT64 Cap; + + Status = NvmHcRwMmio (Address, NVME_CAP_OFFSET, TRUE, sizeof (Cap), &Cap); + if (EFI_ERROR (Status)) + return Status; + + DEBUG_NVME ((EFI_D_INFO, "ReadNvmeControllerCapabilities: Cap = 0x%llX\n", Cap)); + + CopyMem (Capability, &Cap, sizeof (Cap)); + + return EFI_SUCCESS; +} + +/** + Read Nvm Express controller configuration register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Cc The buffer used to store configuration register content. + + @return EFI_SUCCESS Successfully read the controller configuration register content. + @return EFI_DEVICE_ERROR Fail to read the controller configuration register. + +**/ +EFI_STATUS +ReadNvmeControllerConfiguration ( + IN UINT32 Address, + IN NVME_CC *Cc +) +{ + EFI_STATUS Status; + UINT32 Data; + + Status = NvmHcRwMmio (Address, NVME_CC_OFFSET, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + CopyMem (Cc, &Data, sizeof (Data)); + return EFI_SUCCESS; +} + +/** + Write Nvm Express controller configuration register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Cc The buffer used to store the content to be written into configuration register. + + @return EFI_SUCCESS Successfully write data into the controller configuration register. + @return EFI_DEVICE_ERROR Fail to write data into the controller configuration register. + +**/ +EFI_STATUS +WriteNvmeControllerConfiguration ( + IN UINT32 Address, + IN NVME_CC *Cc +) +{ + EFI_STATUS Status; + UINT32 Data; + + Data = *((UINT32 *)Cc); + + Status = NvmHcRwMmio (Address, NVME_CC_OFFSET, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + DEBUG_NVME ((EFI_D_INFO, "Cc.En: %d\n", Cc->En)); + DEBUG_NVME ((EFI_D_INFO, "Cc.Css: %d\n", Cc->Css)); + DEBUG_NVME ((EFI_D_INFO, "Cc.Mps: %d\n", Cc->Mps)); + DEBUG_NVME ((EFI_D_INFO, "Cc.Ams: %d\n", Cc->Ams)); + DEBUG_NVME ((EFI_D_INFO, "Cc.Shn: %d\n", Cc->Shn)); + DEBUG_NVME ((EFI_D_INFO, "Cc.Iosqes: %d\n", Cc->Iosqes)); + DEBUG_NVME ((EFI_D_INFO, "Cc.Iocqes: %d\n", Cc->Iocqes)); + + return EFI_SUCCESS; +} + +/** + Read Nvm Express controller status register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Csts The buffer used to store status register content. + + @return EFI_SUCCESS Successfully read the controller status register content. + @return EFI_DEVICE_ERROR Fail to read the controller status register. + +**/ +EFI_STATUS +ReadNvmeControllerStatus ( + IN UINT32 Address, + IN NVME_CSTS *Csts +) +{ + EFI_STATUS Status; + UINT32 Data; + + Status = NvmHcRwMmio (Address, NVME_CSTS_OFFSET, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + CopyMem (Csts, &Data, sizeof (Data)); + return EFI_SUCCESS; +} + +/** + Read Nvm Express admin queue attributes register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Aqa The buffer used to store admin queue attributes register content. + + @return EFI_SUCCESS Successfully read the admin queue attributes register content. + @return EFI_DEVICE_ERROR Fail to read the admin queue attributes register. + +**/ +EFI_STATUS +ReadNvmeAdminQueueAttributes ( + IN UINT32 Address, + IN NVME_AQA *Aqa +) +{ + EFI_STATUS Status; + UINT32 Data; + + Status = NvmHcRwMmio (Address, NVME_AQA_OFFSET, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + CopyMem (Aqa, &Data, sizeof (Data)); + return EFI_SUCCESS; +} + +/** + Write Nvm Express admin queue attributes register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Aqa The buffer used to store the content to be written into admin queue attributes register. + + @return EFI_SUCCESS Successfully write data into the admin queue attributes register. + @return EFI_DEVICE_ERROR Fail to write data into the admin queue attributes register. + +**/ +EFI_STATUS +WriteNvmeAdminQueueAttributes ( + IN UINT32 Address, + IN NVME_AQA *Aqa +) +{ + EFI_STATUS Status; + UINT32 Data; + + Data = *((UINT32 *)Aqa); + Status = NvmHcRwMmio (Address, NVME_AQA_OFFSET, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + DEBUG_NVME ((EFI_D_INFO, "Aqa.Asqs: %d\n", Aqa->Asqs)); + DEBUG_NVME ((EFI_D_INFO, "Aqa.Acqs: %d\n", Aqa->Acqs)); + + return EFI_SUCCESS; +} + +/** + Read Nvm Express admin submission queue base address register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Asq The buffer used to store admin submission queue base address register content. + + @return EFI_SUCCESS Successfully read the admin submission queue base address register content. + @return EFI_DEVICE_ERROR Fail to read the admin submission queue base address register. + +**/ +EFI_STATUS +ReadNvmeAdminSubmissionQueueBaseAddress ( + IN UINT32 Address, + IN NVME_ASQ *Asq +) +{ + EFI_STATUS Status; + UINT64 Data; + + Status = NvmHcRwMmio (Address, NVME_ASQ_OFFSET, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + CopyMem (Asq, &Data, sizeof (Data)); + return EFI_SUCCESS; +} + +/** + Write Nvm Express admin submission queue base address register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Asq The buffer used to store the content to be written into admin submission queue base address register. + + @return EFI_SUCCESS Successfully write data into the admin submission queue base address register. + @return EFI_DEVICE_ERROR Fail to write data into the admin submission queue base address register. + +**/ +EFI_STATUS +WriteNvmeAdminSubmissionQueueBaseAddress ( + IN UINT32 Address, + IN NVME_ASQ *Asq +) +{ + EFI_STATUS Status; + UINT64 Data; + + Data = *((UINT64 *)Asq); + + Status = NvmHcRwMmio (Address, NVME_ASQ_OFFSET, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + DEBUG_NVME ((EFI_D_INFO, "Asq: %lx\n", *Asq)); + + return EFI_SUCCESS; +} + +/** + Read Nvm Express admin completion queue base address register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Acq The buffer used to store admin completion queue base address register content. + + @return EFI_SUCCESS Successfully read the admin completion queue base address register content. + @return EFI_DEVICE_ERROR Fail to read the admin completion queue base address register. + +**/ +EFI_STATUS +ReadNvmeAdminCompletionQueueBaseAddress ( + IN UINT32 Address, + IN NVME_ACQ *Acq +) +{ + EFI_STATUS Status; + UINT64 Data; + + Status = NvmHcRwMmio (Address, NVME_ACQ_OFFSET, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Acq, &Data, sizeof (Data)); + return EFI_SUCCESS; +} + +/** + Write Nvm Express admin completion queue base address register. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Acq The buffer used to store the content to be written into admin completion queue base address register. + + @return EFI_SUCCESS Successfully write data into the admin completion queue base address register. + @return EFI_DEVICE_ERROR Fail to write data into the admin completion queue base address register. + +**/ +EFI_STATUS +WriteNvmeAdminCompletionQueueBaseAddress ( + IN UINT32 Address, + IN NVME_ACQ *Acq +) +{ + EFI_STATUS Status; + UINT64 Data; + + Data = *((UINT64 *)Acq); + + Status = NvmHcRwMmio (Address, NVME_ACQ_OFFSET, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG_NVME ((EFI_D_INFO, "Acq: %lxh\n", *Acq)); + + return EFI_SUCCESS; +} + +/** + Disable the Nvm Express controller. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @return EFI_SUCCESS Successfully disable the controller. + @return EFI_DEVICE_ERROR Fail to disable the controller. + +**/ +EFI_STATUS +NvmeDisableController ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private +) +{ + NVME_CC Cc; + NVME_CSTS Csts; + EFI_STATUS Status; + UINT32 Index; + UINT8 Timeout; + UINT32 NvmeHCBase; + + //NVME PCI base address + NvmeHCBase = Private->NvmeHCBase; + + // + // Read Controller Configuration Register. + // + Status = ReadNvmeControllerConfiguration (NvmeHCBase, &Cc); + if (EFI_ERROR(Status)) + return Status; + + Cc.En = 0; + + // + // Disable the controller. + // + Status = WriteNvmeControllerConfiguration (NvmeHCBase, &Cc); + if (EFI_ERROR(Status)) + return Status; + + // + // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to transition from 1 to 0 after + // Cc.Enable transition from 1 to 0. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To. + // + if (Private->Cap.To == 0) + Timeout = 1; + else + Timeout = Private->Cap.To; + + for (Index = (Timeout * 500); Index != 0; --Index) { + NanoSecondDelay(1000 * 1000); + + // + // Check if the controller is initialized + // + Status = ReadNvmeControllerStatus (NvmeHCBase, &Csts); + + if (EFI_ERROR(Status)) + return Status; + + if (Csts.Rdy == 0) + break; + } + + if (Index == 0) + Status = EFI_DEVICE_ERROR; + + DEBUG_NVME ((EFI_D_INFO, "NVMe controller is disabled with status [0x%X].\n", Status)); + return Status; +} + +/** + Enable the Nvm Express controller. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @return EFI_SUCCESS Successfully enable the controller. + @return EFI_DEVICE_ERROR Fail to enable the controller. + @return EFI_TIMEOUT Fail to enable the controller in given time slot. + +**/ +EFI_STATUS +NvmeEnableController ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private +) +{ + NVME_CC Cc; + NVME_CSTS Csts; + EFI_STATUS Status; + UINT32 Index; + UINT8 Timeout; + UINT32 NvmeHCBase; + + //NVME PCI base address + NvmeHCBase = Private->NvmeHCBase; + + // + // Enable the controller. + // CC.AMS, CC.MPS and CC.CSS are all set to 0. + // + ZeroMem (&Cc, sizeof (NVME_CC)); + Cc.En = 1; + Cc.Iosqes = 6; + Cc.Iocqes = 4; + + Status = WriteNvmeControllerConfiguration (NvmeHCBase, &Cc); + if (EFI_ERROR(Status)) + return Status; + + // + // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after + // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To. + // + if (Private->Cap.To == 0) + Timeout = 1; + else + Timeout = Private->Cap.To; + + for (Index = (Timeout * 500); Index != 0; --Index) { + NanoSecondDelay(1000 * 1000); + + // + // Check if the controller is initialized + // + Status = ReadNvmeControllerStatus (NvmeHCBase, &Csts); + + if (EFI_ERROR(Status)) + return Status; + + if (Csts.Rdy) + break; + } + + if (Index == 0) + Status = EFI_TIMEOUT; + + DEBUG_NVME ((EFI_D_INFO, "NVMe controller is enabled with status [0x%x].\n", Status)); + return Status; +} + +/** + Get identify controller data. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Buffer The buffer used to store the identify controller data. + + @return EFI_SUCCESS Successfully get the identify controller data. + @return EFI_DEVICE_ERROR Fail to get the identify controller data. + +**/ +EFI_STATUS +NvmeIdentifyController ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN VOID *Buffer +) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; + // + // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h. + // For the Identify command, the Namespace Identifier is only used for the Namespace data structure. + // + Command.Nsid = 0; + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + CommandPacket.TransferBuffer = Buffer; + CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA); + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + // + // Set bit 0 (Cns bit) to 1 to identify a controller + // + Command.Cdw10 = 1; + Command.Flags = CDW10_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + NVME_CONTROLLER_ID, + &CommandPacket, + NULL + ); + + return Status; +} + +/** + Get specified identify namespace data. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param NamespaceId The specified namespace identifier. + @param Buffer The buffer used to store the identify namespace data. + + @return EFI_SUCCESS Successfully get the identify namespace data. + @return EFI_DEVICE_ERROR Fail to get the identify namespace data. + +**/ +EFI_STATUS +NvmeIdentifyNamespace ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN UINT32 NamespaceId, + IN VOID *Buffer +) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; + Command.Nsid = NamespaceId; + CommandPacket.TransferBuffer = Buffer; + CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA); + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + // + // Set bit 0 (Cns bit) to 1 to identify a namespace + // + CommandPacket.NvmeCmd->Cdw10 = 0; + CommandPacket.NvmeCmd->Flags = CDW10_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + NamespaceId, + &CommandPacket, + NULL + ); + + return Status; +} + +/** + Create io completion queue. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @return EFI_SUCCESS Successfully create io completion queue. + @return EFI_DEVICE_ERROR Fail to create io completion queue. + +**/ +EFI_STATUS +NvmeCreateIoCompletionQueue ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + NVME_ADMIN_CRIOCQ CrIoCq; + UINT32 Index; + UINT16 QueueSize; + + Status = EFI_SUCCESS; + + for (Index = 1; Index < NVME_MAX_QUEUES; Index++) { + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_CMD; + CommandPacket.TransferBuffer = Private->CqBuffer[Index]; + CommandPacket.TransferLength = EFI_PAGE_SIZE; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + + if (Index == 1) + QueueSize = NVME_CCQ_SIZE; + else { + if (Private->Cap.Mqes > NVME_ASYNC_CCQ_SIZE) + QueueSize = NVME_ASYNC_CCQ_SIZE; + else + QueueSize = Private->Cap.Mqes; + } + + CrIoCq.Qid = Index; + CrIoCq.Qsize = QueueSize; + CrIoCq.Pc = 1; + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + 0, + &CommandPacket, + NULL + ); + if (EFI_ERROR (Status)) + break; + } + + return Status; +} + +/** + Create io submission queue. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @return EFI_SUCCESS Successfully create io submission queue. + @return EFI_DEVICE_ERROR Fail to create io submission queue. + +**/ +EFI_STATUS +NvmeCreateIoSubmissionQueue ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private +) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + NVME_ADMIN_CRIOSQ CrIoSq; + UINT32 Index; + UINT16 QueueSize; + + Status = EFI_SUCCESS; + + for (Index = 1; Index < NVME_MAX_QUEUES; Index++) { + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_CMD; + CommandPacket.TransferBuffer = Private->SqBuffer[Index]; + CommandPacket.TransferLength = EFI_PAGE_SIZE; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + + if (Index == 1) + QueueSize = NVME_CSQ_SIZE; + else { + if (Private->Cap.Mqes > NVME_ASYNC_CSQ_SIZE) + QueueSize = NVME_ASYNC_CSQ_SIZE; + else + QueueSize = Private->Cap.Mqes; + } + + CrIoSq.Qid = Index; + CrIoSq.Qsize = QueueSize; + CrIoSq.Pc = 1; + CrIoSq.Cqid = Index; + CrIoSq.Qprio = 0; + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + 0, + &CommandPacket, + NULL + ); + + if (EFI_ERROR (Status)) + break; + } + + return Status; +} + +/** + Initialize the Nvm Express controller. + + @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NVM Express Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +NvmeControllerInit ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private +) +{ + EFI_STATUS Status; + NVME_AQA Aqa; + NVME_ASQ Asq; + NVME_ACQ Acq; + UINT8 Sn[21]; + UINT8 Mn[41]; + UINT32 NvmeHCBase; + + //NVME PCI base address + NvmeHCBase = Private->NvmeHCBase; + + DEBUG_NVME ((EFI_D_INFO, "NvmeControllerInit: NvmeHCBase = 0x%X\n", NvmeHCBase)); + + // + // Read the Controller Capabilities register and verify that the NVM command set is supported + // + Status = ReadNvmeControllerCapabilities (NvmeHCBase, &Private->Cap); + DEBUG_NVME ((EFI_D_INFO, "ReadNvmeControllerCapabilities: ret = 0x%X\n", Status)); + if (EFI_ERROR (Status)) + return Status; + + if (Private->Cap.Css != 0x01) { + DEBUG_NVME ((EFI_D_INFO, "NvmeControllerInit: the controller doesn't support NVMe command set\n")); + return EFI_UNSUPPORTED; + } + + // + // Currently the driver only supports 4k page size. + // + ASSERT ((Private->Cap.Mpsmin + 12) <= EFI_PAGE_SHIFT); + + Private->Cid[0] = 0; + Private->Cid[1] = 0; + Private->Cid[2] = 0; + Private->Pt[0] = 0; + Private->Pt[1] = 0; + Private->Pt[2] = 0; + Private->SqTdbl[0].Sqt = 0; + Private->SqTdbl[1].Sqt = 0; + Private->SqTdbl[2].Sqt = 0; + Private->CqHdbl[0].Cqh = 0; + Private->CqHdbl[1].Cqh = 0; + Private->CqHdbl[2].Cqh = 0; + Private->AsyncSqHead = 0; + + Status = NvmeDisableController (Private); + if (EFI_ERROR(Status)) + return Status; + + // + // set number of entries admin submission & completion queues. + // + Aqa.Asqs = NVME_ASQ_SIZE; + Aqa.Rsvd1 = 0; + Aqa.Acqs = NVME_ACQ_SIZE; + Aqa.Rsvd2 = 0; + + // + // Address of admin submission queue. + // + Asq = (UINT64)(UINTN)(Private->Buffer) & ~0xFFF; + + // + // Address of admin completion queue. + // + Acq = (UINT64)(UINTN)(Private->Buffer + EFI_PAGE_SIZE) & ~0xFFF; + + // + // Address of I/O submission & completion queue. + // + Private->SqBuffer[0] = (NVME_SQ *)(UINTN)(Private->Buffer); + Private->CqBuffer[0] = (NVME_CQ *)(UINTN)(Private->Buffer + 1 * EFI_PAGE_SIZE); + Private->SqBuffer[1] = (NVME_SQ *)(UINTN)(Private->Buffer + 2 * EFI_PAGE_SIZE); + Private->CqBuffer[1] = (NVME_CQ *)(UINTN)(Private->Buffer + 3 * EFI_PAGE_SIZE); + Private->SqBuffer[2] = (NVME_SQ *)(UINTN)(Private->Buffer + 4 * EFI_PAGE_SIZE); + Private->CqBuffer[2] = (NVME_CQ *)(UINTN)(Private->Buffer + 5 * EFI_PAGE_SIZE); + + DEBUG_NVME ((EFI_D_INFO, "Private->Buffer = [%016X]\n", (UINT64)(UINTN)Private->Buffer)); + DEBUG_NVME ((EFI_D_INFO, "Admin Submission Queue size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs)); + DEBUG_NVME ((EFI_D_INFO, "Admin Completion Queue size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs)); + DEBUG_NVME ((EFI_D_INFO, "Admin Submission Queue (SqBuffer[0]) = [%016X]\n", Private->SqBuffer[0])); + DEBUG_NVME ((EFI_D_INFO, "Admin Completion Queue (CqBuffer[0]) = [%016X]\n", Private->CqBuffer[0])); + DEBUG_NVME ((EFI_D_INFO, "Sync I/O Submission Queue (SqBuffer[1]) = [%016X]\n", Private->SqBuffer[1])); + DEBUG_NVME ((EFI_D_INFO, "Sync I/O Completion Queue (CqBuffer[1]) = [%016X]\n", Private->CqBuffer[1])); + DEBUG_NVME ((EFI_D_INFO, "Async I/O Submission Queue (SqBuffer[2]) = [%016X]\n", Private->SqBuffer[2])); + DEBUG_NVME ((EFI_D_INFO, "Async I/O Completion Queue (CqBuffer[2]) = [%016X]\n", Private->CqBuffer[2])); + + // + // Program admin queue attributes. + // + Status = WriteNvmeAdminQueueAttributes (NvmeHCBase, &Aqa); + + if (EFI_ERROR(Status)) + return Status; + + // + // Program admin submission queue address. + // + Status = WriteNvmeAdminSubmissionQueueBaseAddress (NvmeHCBase, &Asq); + if (EFI_ERROR(Status)) + return Status; + + // + // Program admin completion queue address. + // + Status = WriteNvmeAdminCompletionQueueBaseAddress (NvmeHCBase, &Acq); + if (EFI_ERROR(Status)) + return Status; + + Status = NvmeEnableController (Private); + if (EFI_ERROR(Status)) + return Status; + + // + // Allocate buffer for Identify Controller data + // + if (Private->ControllerData == NULL) { + Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)MallocZero (sizeof(NVME_ADMIN_CONTROLLER_DATA)); + if (Private->ControllerData == NULL) + return EFI_OUT_OF_RESOURCES; + } + + // + // Get current Identify Controller Data + // + Status = NvmeIdentifyController (Private, Private->ControllerData); + if (EFI_ERROR(Status)) { + FreeZero (Private->ControllerData); + Private->ControllerData = NULL; + return EFI_NOT_FOUND; + } + + // + // Dump NvmExpress Identify Controller Data + // + CopyMem (Sn, Private->ControllerData->Sn, sizeof (Private->ControllerData->Sn)); + Sn[20] = 0; + CopyMem (Mn, Private->ControllerData->Mn, sizeof (Private->ControllerData->Mn)); + Mn[40] = 0; + DEBUG_NVME ((EFI_D_INFO, " == NVME IDENTIFY CONTROLLER DATA ==\n")); + DEBUG_NVME ((EFI_D_INFO, " PCI VID : 0x%x\n", Private->ControllerData->Vid)); + DEBUG_NVME ((EFI_D_INFO, " PCI SSVID : 0x%x\n", Private->ControllerData->Ssvid)); + DEBUG_NVME ((EFI_D_INFO, " SN : %s\n", Sn)); + DEBUG_NVME ((EFI_D_INFO, " MN : %s\n", Mn)); + DEBUG_NVME ((EFI_D_INFO, " FR : 0x%x\n", *((UINT64 *)Private->ControllerData->Fr))); + DEBUG_NVME ((EFI_D_INFO, " RAB : 0x%x\n", Private->ControllerData->Rab)); + DEBUG_NVME ((EFI_D_INFO, " IEEE : 0x%x\n", *(UINT32 *)Private->ControllerData->Ieee_oui)); + DEBUG_NVME ((EFI_D_INFO, " AERL : 0x%x\n", Private->ControllerData->Aerl)); + DEBUG_NVME ((EFI_D_INFO, " SQES : 0x%x\n", Private->ControllerData->Sqes)); + DEBUG_NVME ((EFI_D_INFO, " CQES : 0x%x\n", Private->ControllerData->Cqes)); + DEBUG_NVME ((EFI_D_INFO, " NN : 0x%x\n", Private->ControllerData->Nn)); + DEBUG_NVME ((EFI_D_INFO, " Oncs : 0x%x\n", Private->ControllerData->Oncs)); + DEBUG_NVME ((EFI_D_INFO, " Oacs : 0x%x\n", Private->ControllerData->Oacs)); + + // + // Create two I/O completion queues. + // One for blocking I/O, one for non-blocking I/O. + // + Status = NvmeCreateIoCompletionQueue (Private); + if (EFI_ERROR(Status)) + return Status; + + // + // Create two I/O Submission queues. + // One for blocking I/O, one for non-blocking I/O. + // + Status = NvmeCreateIoSubmissionQueue (Private); + + return Status; +} + diff --git a/drivers/nvme/NvmExpressHci.h b/drivers/nvme/NvmExpressHci.h new file mode 100644 index 0000000..7108010 --- /dev/null +++ b/drivers/nvme/NvmExpressHci.h @@ -0,0 +1,107 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2013 - 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 + 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 _NVME_HCI_H_ +#define _NVME_HCI_H_ + +#define NVME_BAR 0 + +// +// Offset from the beginning of private data queue buffer +// +#define NVME_ASQ_BUF_OFFSET EFI_PAGE_SIZE + + +/** + Read/Write specified NVM host controller mmio register. + + @param[in] Address Host controller mmio base address. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The Data is NULL or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +NvmHcRwMmio ( + IN UINT32 Address, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data +); + +/** + Initialize the Nvm Express controller. + + @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NVM Express Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +NvmeControllerInit ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private +); + +/** + Get identify controller data. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param Buffer The buffer used to store the identify controller data. + + @return EFI_SUCCESS Successfully get the identify controller data. + @return EFI_DEVICE_ERROR Fail to get the identify controller data. + +**/ +EFI_STATUS +NvmeIdentifyController ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN VOID *Buffer +); + +/** + Get specified identify namespace data. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param NamespaceId The specified namespace identifier. + @param Buffer The buffer used to store the identify namespace data. + + @return EFI_SUCCESS Successfully get the identify namespace data. + @return EFI_DEVICE_ERROR Fail to get the identify namespace data. + +**/ +EFI_STATUS +NvmeIdentifyNamespace ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN UINT32 NamespaceId, + IN VOID *Buffer +); + +#endif + diff --git a/drivers/nvme/NvmExpressPassthru.c b/drivers/nvme/NvmExpressPassthru.c new file mode 100644 index 0000000..586fefd --- /dev/null +++ b/drivers/nvme/NvmExpressPassthru.c @@ -0,0 +1,824 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2013 - 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 + 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. + +**/ + +#include +#include +#include +#include +#include + +#include "NvmExpress.h" + +#ifndef MSG_NVME_NAMESPACE_DP +#define MSG_NVME_NAMESPACE_DP 0x17 +#endif + +UINTN NanoSecondDelay (UINTN NanoSeconds) +{ + ndelay(NanoSeconds); + return 0; +} + +/** + Allocates one or more 4KB pages of type EfiBootServicesData. + + Allocates the number of 4KB pages of type EfiBootServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID *EFIAPI nvme_alloc_pages (IN UINTN Pages) +{ + void *ptr; + ptr = memalign (EFI_PAGE_SIZE, Pages << EFI_PAGE_SHIFT); + if (ptr != NULL) + ZeroMem (ptr, Pages << EFI_PAGE_SHIFT); + + return ptr; +} + + +/** + Frees one or more 4KB pages that were previously allocated with one of the page allocation + functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer + must have been allocated on a previous call to the page allocation services of the Memory + Allocation Library. If it is not possible to free allocated pages, then this function will + perform no actions. + + If Buffer was not allocated with a page allocation function in the Memory Allocation Library, + then ASSERT(). + If Pages is zero, then ASSERT(). + + @param Buffer The pointer to the buffer of pages to free. + @param Pages The number of 4 KB pages to free. + +**/ +VOID EFIAPI nvme_free_pages (IN VOID *Buffer) +{ + free(Buffer); +} + + +/** + Dump the execution status from a given completion queue entry. + + @param[in] Cq A pointer to the NVME_CQ item. + +**/ +VOID +NvmeDumpStatus ( + IN NVME_CQ *Cq + ) +{ + DEBUG_NVME ((EFI_D_VERBOSE, "Dump NVMe Completion Entry Status from [0x%x]:\n", Cq)); + DEBUG_NVME ((EFI_D_VERBOSE, " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n", Cq->Sqid, Cq->Pt, Cq->Cid)); + DEBUG_NVME ((EFI_D_VERBOSE, " NVMe Cmd Execution Result - ")); + + switch (Cq->Sct) { + case 0x0: + switch (Cq->Sc) { + case 0x0: + DEBUG_NVME ((EFI_D_VERBOSE, "Successful Completion\n")); + break; + case 0x1: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Command Opcode\n")); + break; + case 0x2: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Field in Command\n")); + break; + case 0x3: + DEBUG_NVME ((EFI_D_VERBOSE, "Command ID Conflict\n")); + break; + case 0x4: + DEBUG_NVME ((EFI_D_VERBOSE, "Data Transfer Error\n")); + break; + case 0x5: + DEBUG_NVME ((EFI_D_VERBOSE, "Commands Aborted due to Power Loss Notification\n")); + break; + case 0x6: + DEBUG_NVME ((EFI_D_VERBOSE, "Internal Device Error\n")); + break; + case 0x7: + DEBUG_NVME ((EFI_D_VERBOSE, "Command Abort Requested\n")); + break; + case 0x8: + DEBUG_NVME ((EFI_D_VERBOSE, "Command Aborted due to SQ Deletion\n")); + break; + case 0x9: + DEBUG_NVME ((EFI_D_VERBOSE, "Command Aborted due to Failed Fused Command\n")); + break; + case 0xA: + DEBUG_NVME ((EFI_D_VERBOSE, "Command Aborted due to Missing Fused Command\n")); + break; + case 0xB: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Namespace or Format\n")); + break; + case 0xC: + DEBUG_NVME ((EFI_D_VERBOSE, "Command Sequence Error\n")); + break; + case 0xD: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid SGL Last Segment Descriptor\n")); + break; + case 0xE: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Number of SGL Descriptors\n")); + break; + case 0xF: + DEBUG_NVME ((EFI_D_VERBOSE, "Data SGL Length Invalid\n")); + break; + case 0x10: + DEBUG_NVME ((EFI_D_VERBOSE, "Metadata SGL Length Invalid\n")); + break; + case 0x11: + DEBUG_NVME ((EFI_D_VERBOSE, "SGL Descriptor Type Invalid\n")); + break; + case 0x80: + DEBUG_NVME ((EFI_D_VERBOSE, "LBA Out of Range\n")); + break; + case 0x81: + DEBUG_NVME ((EFI_D_VERBOSE, "Capacity Exceeded\n")); + break; + case 0x82: + DEBUG_NVME ((EFI_D_VERBOSE, "Namespace Not Ready\n")); + break; + case 0x83: + DEBUG_NVME ((EFI_D_VERBOSE, "Reservation Conflict\n")); + break; + } + break; + + case 0x1: + switch (Cq->Sc) { + case 0x0: + DEBUG_NVME ((EFI_D_VERBOSE, "Completion Queue Invalid\n")); + break; + case 0x1: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Queue Identifier\n")); + break; + case 0x2: + DEBUG_NVME ((EFI_D_VERBOSE, "Maximum Queue Size Exceeded\n")); + break; + case 0x3: + DEBUG_NVME ((EFI_D_VERBOSE, "Abort Command Limit Exceeded\n")); + break; + case 0x5: + DEBUG_NVME ((EFI_D_VERBOSE, "Asynchronous Event Request Limit Exceeded\n")); + break; + case 0x6: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Firmware Slot\n")); + break; + case 0x7: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Firmware Image\n")); + break; + case 0x8: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Interrupt Vector\n")); + break; + case 0x9: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Log Page\n")); + break; + case 0xA: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Format\n")); + break; + case 0xB: + DEBUG_NVME ((EFI_D_VERBOSE, "Firmware Application Requires Conventional Reset\n")); + break; + case 0xC: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Queue Deletion\n")); + break; + case 0xD: + DEBUG_NVME ((EFI_D_VERBOSE, "Feature Identifier Not Saveable\n")); + break; + case 0xE: + DEBUG_NVME ((EFI_D_VERBOSE, "Feature Not Changeable\n")); + break; + case 0xF: + DEBUG_NVME ((EFI_D_VERBOSE, "Feature Not Namespace Specific\n")); + break; + case 0x10: + DEBUG_NVME ((EFI_D_VERBOSE, "Firmware Application Requires NVM Subsystem Reset\n")); + break; + case 0x80: + DEBUG_NVME ((EFI_D_VERBOSE, "Conflicting Attributes\n")); + break; + case 0x81: + DEBUG_NVME ((EFI_D_VERBOSE, "Invalid Protection Information\n")); + break; + case 0x82: + DEBUG_NVME ((EFI_D_VERBOSE, "Attempted Write to Read Only Range\n")); + break; + } + break; + + case 0x2: + switch (Cq->Sc) { + case 0x80: + DEBUG_NVME ((EFI_D_VERBOSE, "Write Fault\n")); + break; + case 0x81: + DEBUG_NVME ((EFI_D_VERBOSE, "Unrecovered Read Error\n")); + break; + case 0x82: + DEBUG_NVME ((EFI_D_VERBOSE, "End-to-end Guard Check Error\n")); + break; + case 0x83: + DEBUG_NVME ((EFI_D_VERBOSE, "End-to-end Application Tag Check Error\n")); + break; + case 0x84: + DEBUG_NVME ((EFI_D_VERBOSE, "End-to-end Reference Tag Check Error\n")); + break; + case 0x85: + DEBUG_NVME ((EFI_D_VERBOSE, "Compare Failure\n")); + break; + case 0x86: + DEBUG_NVME ((EFI_D_VERBOSE, "Access Denied\n")); + break; + } + break; + + default: + break; + } +} + +/** + Create PRP lists for data transfer which is larger than 2 memory pages. + Note here we calcuate the number of required PRP lists and allocate them at one time. + + @param[in] PhysicalAddr The physical base address of data buffer. + @param[in] Pages The number of pages to be transfered. + @param[out] PrpListHost The host base address of PRP lists. + @param[in,out] PrpListNo The number of PRP List. + + @retval The pointer to the first PRP List of the PRP lists. + +**/ +VOID* +NvmeCreatePrpList ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddr, + IN UINTN Pages, + OUT VOID **PrpListHost, + IN OUT UINTN *PrpListNo +) +{ + UINTN PrpEntryNo; + UINT64 PrpListBase; + UINTN PrpListIndex; + UINTN PrpEntryIndex; + UINT64 Remainder; + EFI_PHYSICAL_ADDRESS PrpListPhyAddr; + VOID *PrpList; + + // + // The number of Prp Entry in a memory page. + // + PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64); + + // + // Calculate total PrpList number. + // + Remainder = Pages % (PrpEntryNo - 1); + *PrpListNo = Pages / (PrpEntryNo - 1); + if (*PrpListNo == 0) + *PrpListNo = 1; + else if ((Remainder != 0) && (Remainder != 1)) + *PrpListNo += 1; + else if (Remainder == 1) + Remainder = PrpEntryNo; + else if (Remainder == 0) + Remainder = PrpEntryNo - 1; + + PrpList = nvme_alloc_pages(*PrpListNo); + if (PrpList == NULL) { + DEBUG_NVME ((EFI_D_ERROR, "NvmeCreatePrpList: create PrpList failure!\n")); + goto EXIT; + } + + PrpListPhyAddr = (UINT64)(UINTN)PrpList; + *PrpListHost = PrpList; + + // + // Fill all PRP lists except of last one. + // + for (PrpListIndex = 0; PrpListIndex < *PrpListNo - 1; ++PrpListIndex) { + PrpListBase = *(UINT64 *)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; + + for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) { + if (PrpEntryIndex != PrpEntryNo - 1) { + // + // Fill all PRP entries except of last one. + // + *((UINT64 *)(UINTN)PrpListBase + PrpEntryIndex) = PhysicalAddr; + PhysicalAddr += EFI_PAGE_SIZE; + } else { + // + // Fill last PRP entries with next PRP List pointer. + // + *((UINT64 *)(UINTN)PrpListBase + PrpEntryIndex) = PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE; + } + } + } + + // + // Fill last PRP list. + // + PrpListBase = *(UINT64 *)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; + for (PrpEntryIndex = 0; PrpEntryIndex < Remainder; ++PrpEntryIndex) { + *((UINT64 *)(UINTN)PrpListBase + PrpEntryIndex) = PhysicalAddr; + PhysicalAddr += EFI_PAGE_SIZE; + } + + nvme_free_pages(PrpList); + return (VOID *)(UINTN)PrpListPhyAddr; + +EXIT: + nvme_free_pages(PrpList); + return NULL; +} + +/** + Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports + both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, and the non-blocking + I/O functionality is optional. + + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] NamespaceId A 32 bit namespace ID as defined in the NVMe specification to which the NVM Express Command + Packet will be sent. A value of 0 denotes the NVM Express controller, a value of all 0xFF's + (all bytes are 0xFF) in the namespace ID specifies that the command packet should be sent to + all valid namespaces. + @param[in,out] Packet A pointer to the NVM Express Command Packet. + @param[in] Event If non-blocking I/O is not supported then Event is ignored, and blocking I/O is performed. + 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 NVM + Express Command Packet completes. + + @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred + to, or from DataBuffer. + @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred + is returned in TransferLength. + @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller + may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet. + @retval EFI_INVALID_PARAMETER NamespaceId or the contents of EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM + Express Command Packet was not sent, so no additional status information is available. + @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the NVM Express + controller. The NVM Express Command Packet was not sent so no additional status information + is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute. + +**/ +EFI_STATUS +EFIAPI +NvmExpressPassThru ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet, + IN ASYNC_IO_CALL_BACK *Event +) +{ + NVME_CONTROLLER_PRIVATE_DATA *Private; + EFI_STATUS Status; + NVME_SQ *Sq; + NVME_CQ *Cq; + UINT16 QueueId; + UINT32 Bytes; + UINT16 Offset; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINT64 *Prp; + VOID *PrpListHost; + UINTN PrpListNo; + UINT32 Attributes; + UINT32 IoAlign; + UINT32 MaxTransLen; + UINT32 Data; + NVME_PASS_THRU_ASYNC_REQ *AsyncRequest; + UINT64 TimeCount; + + // + // check the data fields in Packet parameter. + // + if ((This == NULL) || (Packet == NULL)) + return EFI_INVALID_PARAMETER; + + if ((Packet->NvmeCmd == NULL) || (Packet->NvmeCompletion == NULL)) + return EFI_INVALID_PARAMETER; + + if (Packet->QueueType != NVME_ADMIN_QUEUE && Packet->QueueType != NVME_IO_QUEUE) + return EFI_INVALID_PARAMETER; + + // + // 'Attributes' with neither EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL nor + // EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL set is an illegal + // configuration. + // + Attributes = This->Mode->Attributes; + if ((Attributes & (EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL | + EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL)) == 0) + return EFI_INVALID_PARAMETER; + + // + // Buffer alignment check for TransferBuffer & MetadataBuffer. + // + IoAlign = This->Mode->IoAlign; + if (IoAlign > 0 && (((UINTN) Packet->TransferBuffer & (IoAlign - 1)) != 0)) + return EFI_INVALID_PARAMETER; + + if (IoAlign > 0 && (((UINTN) Packet->MetadataBuffer & (IoAlign - 1)) != 0)) + return EFI_INVALID_PARAMETER; + + Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This); + + // + // Check NamespaceId is valid or not. + // + if ((NamespaceId > Private->ControllerData->Nn) && + (NamespaceId != (UINT32) -1)) + return EFI_INVALID_PARAMETER; + + + // + // Check whether TransferLength exceeds the maximum data transfer size. + // + if (Private->ControllerData->Mdts != 0) { + MaxTransLen = (1 << (Private->ControllerData->Mdts)) * + (1 << (Private->Cap.Mpsmin + 12)); + + if (Packet->TransferLength > MaxTransLen) { + Packet->TransferLength = MaxTransLen; + return EFI_BAD_BUFFER_SIZE; + } + } + + PrpListHost = NULL; + PrpListNo = 0; + Prp = NULL; + Status = EFI_SUCCESS; + + if (Packet->QueueType == NVME_ADMIN_QUEUE) + QueueId = 0; + else { + if (Event == NULL || *Event == NULL) + QueueId = 1; + else { + QueueId = 2; + + // + // Submission queue full check. + // + if ((Private->SqTdbl[QueueId].Sqt + 1) % (NVME_ASYNC_CSQ_SIZE + 1) == + Private->AsyncSqHead) + return EFI_NOT_READY; + } + } + + Sq = Private->SqBuffer[QueueId] + Private->SqTdbl[QueueId].Sqt; + Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh; + + if (Packet->NvmeCmd->Nsid != NamespaceId) + return EFI_INVALID_PARAMETER; + + ZeroMem (Sq, sizeof (NVME_SQ)); + Sq->Opc = (UINT8)Packet->NvmeCmd->Cdw0.Opcode; + Sq->Fuse = (UINT8)Packet->NvmeCmd->Cdw0.FusedOperation; + Sq->Cid = Private->Cid[QueueId]++; + Sq->Nsid = Packet->NvmeCmd->Nsid; + + // + // Currently we only support PRP for data transfer, SGL is NOT supported. + // + if (Sq->Psdt != 0) { + DEBUG_NVME ((EFI_D_ERROR, "NvmExpressPassThru: doesn't support SGL mechanism\n")); + return EFI_UNSUPPORTED; + } + + Sq->Prp[0] = (UINT64)(UINTN)Packet->TransferBuffer; + // + // If the NVMe cmd has data in or out, then mapping the user buffer to the PCI controller specific addresses. + // Note here we don't handle data buffer for CreateIOSubmitionQueue and CreateIOCompletionQueue cmds because + // these two cmds are special which requires their data buffer must support simultaneous access by both the + // processor and a PCI Bus Master. It's caller's responsbility to ensure this. + // + if (((Sq->Opc & (BIT0 | BIT1)) != 0) && (Sq->Opc != NVME_ADMIN_CRIOCQ_CMD) && (Sq->Opc != NVME_ADMIN_CRIOSQ_CMD)) { + if ((Packet->TransferLength == 0) || (Packet->TransferBuffer == NULL)) + return EFI_INVALID_PARAMETER; + + PhyAddr = (UINT64)(UINTN)Packet->TransferBuffer; + + Sq->Prp[0] = PhyAddr; + Sq->Prp[1] = 0; + + if ((Packet->MetadataLength != 0) && (Packet->MetadataBuffer != NULL)) { + PhyAddr = (UINT64)(UINTN)Packet->TransferBuffer; + Sq->Mptr = PhyAddr; + } + } + + // + // If the buffer size spans more than two memory pages (page size as defined in CC.Mps), + // then build a PRP list in the second PRP submission queue entry. + // + Offset = ((UINT16)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1); + Bytes = Packet->TransferLength; + + if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) { + // + // Create PrpList for remaining data buffer. + // + PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1); + Prp = NvmeCreatePrpList (PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1, &PrpListHost, &PrpListNo); + if (Prp == NULL) + goto EXIT; + + Sq->Prp[1] = (UINT64)(UINTN)Prp; + } else if ((Offset + Bytes) > EFI_PAGE_SIZE) + Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1); + + if (Packet->NvmeCmd->Flags & CDW2_VALID) + Sq->Rsvd2 = (UINT64)Packet->NvmeCmd->Cdw2; + + if (Packet->NvmeCmd->Flags & CDW3_VALID) + Sq->Rsvd2 |= LShiftU64 ((UINT64)Packet->NvmeCmd->Cdw3, 32); + + if (Packet->NvmeCmd->Flags & CDW10_VALID) + Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10; + + if (Packet->NvmeCmd->Flags & CDW11_VALID) + Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11; + + if (Packet->NvmeCmd->Flags & CDW12_VALID) + Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12; + + if (Packet->NvmeCmd->Flags & CDW13_VALID) + Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13; + + if (Packet->NvmeCmd->Flags & CDW14_VALID) + Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14; + + if (Packet->NvmeCmd->Flags & CDW15_VALID) + Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15; + + // + // Ring the submission queue doorbell. + // + if ((Event != NULL && *Event != NULL) && (QueueId != 0)) { + Private->SqTdbl[QueueId].Sqt = + (Private->SqTdbl[QueueId].Sqt + 1) % (NVME_ASYNC_CSQ_SIZE + 1); + } else + Private->SqTdbl[QueueId].Sqt ^= 1; + + Data = *((UINT32 *)&Private->SqTdbl[QueueId]); + + Status = NvmHcRwMmio(Private->NvmeHCBase, NVME_SQTDBL_OFFSET(QueueId, Private->Cap.Dstrd), FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) + return Status; + + // + // For non-blocking requests, return directly if the command is placed + // in the submission queue. + // + if ((Event != NULL && *Event != NULL) && (QueueId != 0)) { + AsyncRequest = MallocZero (sizeof (NVME_PASS_THRU_ASYNC_REQ)); + if (AsyncRequest == NULL) { + Status = EFI_DEVICE_ERROR; + goto EXIT; + } + + AsyncRequest->Signature = NVME_PASS_THRU_ASYNC_REQ_SIG; + AsyncRequest->Packet = Packet; + AsyncRequest->CommandId = Sq->Cid; + AsyncRequest->CallerEvent = *Event; + AsyncRequest->PrpListNo = PrpListNo; + AsyncRequest->PrpListHost = PrpListHost; + + InsertTailList (&Private->AsyncPassThruQueue, &AsyncRequest->Link); + return EFI_SUCCESS; + } + + + // Wait for completion queue to get filled in. 100ns unit by EFI spec + // + TimeCount = (Packet->CommandTimeout) >> 7; //times = 128ns unit + TimeCount *= 128; + Status = EFI_TIMEOUT; + while (TimeCount--) { + if (Cq->Pt != Private->Pt[QueueId]) { + Status = EFI_SUCCESS; + break; + } + NanoSecondDelay(100); + } + + // + // Check the NVMe cmd execution result + // + if (Status != EFI_TIMEOUT) { + if ((Cq->Sct == 0) && (Cq->Sc == 0)) + Status = EFI_SUCCESS; + else { + Status = EFI_DEVICE_ERROR; + // + // Copy the Respose Queue entry for this command to the callers response buffer + // + CopyMem(Packet->NvmeCompletion, Cq, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + // + // Dump every completion entry status for debugging. + // + DEBUG_CODE_BEGIN(); + NvmeDumpStatus(Cq); + DEBUG_CODE_END(); + } + } + + Private->CqHdbl[QueueId].Cqh ^= 1; + if (Private->CqHdbl[QueueId].Cqh == 0) + Private->Pt[QueueId] ^= 1; + + Data = *((UINT32 *)&Private->CqHdbl[QueueId]); + + Status = NvmHcRwMmio(Private->NvmeHCBase, NVME_CQHDBL_OFFSET(QueueId, Private->Cap.Dstrd), FALSE, sizeof (Data), &Data); + + // + // For now, the code does not support the non-blocking feature for admin queue. + // If Event is not NULL for admin queue, signal the caller's event here. + // + if (Event != NULL && *Event != NULL) { + NVME_BLKIO2_SUBTASK *Subtask; + ASSERT (QueueId == 0); + Subtask = NVME_BLKIO2_SUBTASK_FROM_EVENT(Event); + (*Event)(Subtask); + } + +EXIT: + return Status; +} + +/** + Used to retrieve the next namespace ID for this NVM Express controller. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves the next valid + namespace ID on this NVM Express controller. + + If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first valid namespace + ID defined on the NVM Express controller is returned in the location pointed to by NamespaceId + and a status of EFI_SUCCESS is returned. + + If on input the value pointed to by NamespaceId is an invalid namespace ID other than 0xFFFFFFFF, + then EFI_INVALID_PARAMETER is returned. + + If on input the value pointed to by NamespaceId is a valid namespace ID, then the next valid + namespace ID on the NVM Express controller is returned in the location pointed to by NamespaceId, + and EFI_SUCCESS is returned. + + If the value pointed to by NamespaceId is the namespace ID of the last namespace on the NVM + Express controller, then EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express + namespace present on the NVM Express controller. On output, a + pointer to the next NamespaceId of an NVM Express namespace on + an NVM Express controller. An input value of 0xFFFFFFFF retrieves + the first NamespaceId for an NVM Express namespace present on an + NVM Express controller. + + @retval EFI_SUCCESS The Namespace ID of the next Namespace was returned. + @retval EFI_NOT_FOUND There are no more namespaces defined on this controller. + @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than 0xFFFFFFFF. + +**/ +EFI_STATUS +EFIAPI +NvmExpressGetNextNamespace ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN OUT UINT32 *NamespaceId +) +{ + NVME_CONTROLLER_PRIVATE_DATA *Private; + NVME_ADMIN_NAMESPACE_DATA *NamespaceData; + UINT32 NextNamespaceId; + EFI_STATUS Status; + + if ((This == NULL) || (NamespaceId == NULL)) + return EFI_INVALID_PARAMETER; + + NamespaceData = NULL; + Status = EFI_NOT_FOUND; + + Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This); + // + // If the NamespaceId input value is 0xFFFFFFFF, then get the first valid namespace ID + // + if (*NamespaceId == 0xFFFFFFFF) { + // + // Start with the first namespace ID + // + NextNamespaceId = 1; + // + // Allocate buffer for Identify Namespace data. + // + NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)MallocZero (sizeof (NVME_ADMIN_NAMESPACE_DATA)); + + if (NamespaceData == NULL) + return EFI_NOT_FOUND; + + Status = NvmeIdentifyNamespace (Private, NextNamespaceId, NamespaceData); + if (EFI_ERROR(Status)) + goto Done; + + *NamespaceId = NextNamespaceId; + } else { + if (*NamespaceId > Private->ControllerData->Nn) + return EFI_INVALID_PARAMETER; + + NextNamespaceId = *NamespaceId + 1; + if (NextNamespaceId > Private->ControllerData->Nn) + return EFI_NOT_FOUND; + + // + // Allocate buffer for Identify Namespace data. + // + NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)MallocZero (sizeof (NVME_ADMIN_NAMESPACE_DATA)); + if (NamespaceData == NULL) + return EFI_NOT_FOUND; + + Status = NvmeIdentifyNamespace (Private, NextNamespaceId, NamespaceData); + if (EFI_ERROR(Status)) + goto Done; + + *NamespaceId = NextNamespaceId; + } + +Done: + if (NamespaceData != NULL) + FreeZero (NamespaceData); + + return Status; +} + + +/** + Used to translate a device path node to a namespace ID. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamespace() function determines the namespace ID associated with the + namespace described by DevicePath. + + If DevicePath is a device path node type that the NVM Express Pass Thru driver supports, then the NVM Express + Pass Thru driver will attempt to translate the contents DevicePath into a namespace ID. + + If this translation is successful, then that namespace ID is returned in NamespaceId, and EFI_SUCCESS is returned + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on + the NVM Express controller. + @param[out] NamespaceId The NVM Express namespace ID contained in the device path node. + + @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId. + @retval EFI_INVALID_PARAMETER If DevicePath or NamespaceId are NULL, then EFI_INVALID_PARAMETER is returned. + @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver + supports, then EFI_UNSUPPORTED is returned. + @retval EFI_NOT_FOUND If DevicePath is a device path node type that the NVM Express Pass Thru driver + supports, but there is not a valid translation from DevicePath to a namespace ID, + then EFI_NOT_FOUND is returned. +**/ + +EFI_STATUS +EFIAPI +NvmExpressGetNamespace ( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT32 *NamespaceId +) +{ + if ((This == NULL) || (DevicePath == NULL) || (NamespaceId == NULL)) + return EFI_INVALID_PARAMETER; + + if (DevicePath->Type != MESSAGING_DEVICE_PATH) + return EFI_UNSUPPORTED; + + if (DevicePath->SubType == MSG_NVME_NAMESPACE_DP) { + *NamespaceId = 0; + + return EFI_SUCCESS; + } else + return EFI_UNSUPPORTED; +} + diff --git a/drivers/nvme/NvmExpressPassthru.h b/drivers/nvme/NvmExpressPassthru.h new file mode 100644 index 0000000..ca6ceca --- /dev/null +++ b/drivers/nvme/NvmExpressPassthru.h @@ -0,0 +1,293 @@ +/** @file + This protocol provides services that allow NVM Express commands to be sent to an + NVM Express controller or to a specific namespace in a NVM Express controller. + This protocol interface is optimized for storage. + + Copyright (c) 2013 - 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 + 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 _UEFI_NVM_EXPRESS_PASS_THRU_H_ +#define _UEFI_NVM_EXPRESS_PASS_THRU_H_ + +typedef struct { + UINT8 Type; + UINT8 SubType; + UINT8 Length[2]; +} EFI_DEVICE_PATH_PROTOCOL; + + +#define EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x52c78312, 0x8edc, 0x4233, { 0x98, 0xf2, 0x1a, 0x1a, 0xa5, 0xe3, 0x88, 0xa5 } \ + } + +typedef struct _EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL; + +typedef struct { + UINT32 Attributes; + UINT32 IoAlign; + UINT32 NvmeVersion; +} EFI_NVM_EXPRESS_PASS_THRU_MODE; + +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface is +// for directly addressable namespaces. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001 +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface is +// for a single volume logical namespace comprised of multiple namespaces. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002 +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface +// supports non-blocking I/O. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004 +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface +// supports NVM command set. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM 0x0008 + +// +// FusedOperation +// +#define NORMAL_CMD 0x00 +#define FUSED_FIRST_CMD 0x01 +#define FUSED_SECOND_CMD 0x02 + +typedef struct { + UINT32 Opcode:8; + UINT32 FusedOperation:2; + UINT32 Reserved:22; +} NVME_CDW0; + +// +// Flags +// +#define CDW2_VALID 0x01 +#define CDW3_VALID 0x02 +#define CDW10_VALID 0x04 +#define CDW11_VALID 0x08 +#define CDW12_VALID 0x10 +#define CDW13_VALID 0x20 +#define CDW14_VALID 0x40 +#define CDW15_VALID 0x80 + +// +// Queue Type +// +#define NVME_ADMIN_QUEUE 0x00 +#define NVME_IO_QUEUE 0x01 + +typedef struct { + NVME_CDW0 Cdw0; + UINT8 Flags; + UINT32 Nsid; + UINT32 Cdw2; + UINT32 Cdw3; + UINT32 Cdw10; + UINT32 Cdw11; + UINT32 Cdw12; + UINT32 Cdw13; + UINT32 Cdw14; + UINT32 Cdw15; +} EFI_NVM_EXPRESS_COMMAND; + +typedef struct { + UINT32 DW0; + UINT32 DW1; + UINT32 DW2; + UINT32 DW3; +} EFI_NVM_EXPRESS_COMPLETION; + +typedef struct { + UINT64 CommandTimeout; + VOID *TransferBuffer; + UINT32 TransferLength; + VOID *MetadataBuffer; + UINT32 MetadataLength; + UINT8 QueueType; + EFI_NVM_EXPRESS_COMMAND *NvmeCmd; + EFI_NVM_EXPRESS_COMPLETION *NvmeCompletion; +} EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET; + +// +// Protocol function prototypes +// +/** + Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports + both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, and the non-blocking + I/O functionality is optional. + + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] NamespaceId A 32 bit namespace ID as defined in the NVMe specification to which the NVM Express Command + Packet will be sent. A value of 0 denotes the NVM Express controller, a value of all 0xFF's + (all bytes are 0xFF) in the namespace ID specifies that the command packet should be sent to + all valid namespaces. + @param[in,out] Packet A pointer to the NVM Express Command Packet. + @param[in] Event If non-blocking I/O is not supported then Event is ignored, and blocking I/O is performed. + 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 NVM + Express Command Packet completes. + + @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred + to, or from DataBuffer. + @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred + is returned in TransferLength. + @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller + may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet. + @retval EFI_INVALID_PARAMETER NamespaceId or the contents of EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM + Express Command Packet was not sent, so no additional status information is available. + @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the NVM Express + controller. The NVM Express Command Packet was not sent so no additional status information + is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_PASSTHRU)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL +); + +/** + Used to retrieve the next namespace ID for this NVM Express controller. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves the next valid + namespace ID on this NVM Express controller. + + If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first valid namespace + ID defined on the NVM Express controller is returned in the location pointed to by NamespaceId + and a status of EFI_SUCCESS is returned. + + If on input the value pointed to by NamespaceId is an invalid namespace ID other than 0xFFFFFFFF, + then EFI_INVALID_PARAMETER is returned. + + If on input the value pointed to by NamespaceId is a valid namespace ID, then the next valid + namespace ID on the NVM Express controller is returned in the location pointed to by NamespaceId, + and EFI_SUCCESS is returned. + + If the value pointed to by NamespaceId is the namespace ID of the last namespace on the NVM + Express controller, then EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express + namespace present on the NVM Express controller. On output, a + pointer to the next NamespaceId of an NVM Express namespace on + an NVM Express controller. An input value of 0xFFFFFFFF retrieves + the first NamespaceId for an NVM Express namespace present on an + NVM Express controller. + + @retval EFI_SUCCESS The Namespace ID of the next Namespace was returned. + @retval EFI_NOT_FOUND There are no more namespaces defined on this controller. + @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than 0xFFFFFFFF. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN OUT UINT32 *NamespaceId +); + +/** + Used to allocate and build a device path node for an NVM Express namespace on an NVM Express controller. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.BuildDevicePath() function allocates and builds a single device + path node for the NVM Express namespace specified by NamespaceId. + + If the NamespaceId is not valid, then EFI_NOT_FOUND is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of DevicePath are + initialized to describe the NVM Express namespace specified by NamespaceId, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be + allocated and built. Caller must set the NamespaceId to zero if the + device path node will contain a valid UUID. + @param[in,out] DevicePath A pointer to a single device path node that describes the NVM Express + namespace specified by NamespaceId. This function is responsible for + allocating the buffer DevicePath with the boot service AllocatePool(). + It is the caller's responsibility to free DevicePath when the caller + is finished with DevicePath. + @retval EFI_SUCCESS The device path node that describes the NVM Express namespace specified + by NamespaceId was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The NamespaceId is not valid. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the DevicePath node. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath +); + +/** + Used to translate a device path node to a namespace ID. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamespace() function determines the namespace ID associated with the + namespace described by DevicePath. + + If DevicePath is a device path node type that the NVM Express Pass Thru driver supports, then the NVM Express + Pass Thru driver will attempt to translate the contents DevicePath into a namespace ID. + + If this translation is successful, then that namespace ID is returned in NamespaceId, and EFI_SUCCESS is returned + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on + the NVM Express controller. + @param[out] NamespaceId The NVM Express namespace ID contained in the device path node. + + @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId. + @retval EFI_INVALID_PARAMETER If DevicePath or NamespaceId are NULL, then EFI_INVALID_PARAMETER is returned. + @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver + supports, then EFI_UNSUPPORTED is returned. + @retval EFI_NOT_FOUND If DevicePath is a device path node type that the NVM Express Pass Thru driver + supports, but there is not a valid translation from DevicePath to a namespace ID, + then EFI_NOT_FOUND is returned. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_GET_NAMESPACE)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT32 *NamespaceId +); + +// +// Protocol Interface Structure +// +struct _EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL { + EFI_NVM_EXPRESS_PASS_THRU_MODE *Mode; + EFI_NVM_EXPRESS_PASS_THRU_PASSTHRU PassThru; + EFI_NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE GetNextNamespace; + EFI_NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_NVM_EXPRESS_PASS_THRU_GET_NAMESPACE GetNamespace; +}; + +extern EFI_GUID gEfiNvmExpressPassThruProtocolGuid; + +#endif + diff --git a/drivers/nvme/NvmInternal.h b/drivers/nvme/NvmInternal.h new file mode 100644 index 0000000..68419eb --- /dev/null +++ b/drivers/nvme/NvmInternal.h @@ -0,0 +1,754 @@ +/** @file + Definitions based on NVMe spec. version 1.1. + + (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright (c) 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 + 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 Specification Reference: + NVMe Specification 1.1 + +**/ + +#ifndef __NVM_E_H__ +#define __NVM_E_H__ + +#pragma pack(1) + +// +// controller register offsets +// +#define NVME_CAP_OFFSET 0x0000 // Controller Capabilities +#define NVME_VER_OFFSET 0x0008 // Version +#define NVME_INTMS_OFFSET 0x000c // Interrupt Mask Set +#define NVME_INTMC_OFFSET 0x0010 // Interrupt Mask Clear +#define NVME_CC_OFFSET 0x0014 // Controller Configuration +#define NVME_CSTS_OFFSET 0x001c // Controller Status +#define NVME_NSSR_OFFSET 0x0020 // NVM Subsystem Reset +#define NVME_AQA_OFFSET 0x0024 // Admin Queue Attributes +#define NVME_ASQ_OFFSET 0x0028 // Admin Submission Queue Base Address +#define NVME_ACQ_OFFSET 0x0030 // Admin Completion Queue Base Address +#define NVME_SQ0_OFFSET 0x1000 // Submission Queue 0 (admin) Tail Doorbell +#define NVME_CQ0_OFFSET 0x1004 // Completion Queue 0 (admin) Head Doorbell + +// +// These register offsets are defined as 0x1000 + (N * (4 << CAP.DSTRD)) +// Get the doorbell stride bit shift value from the controller capabilities. +// +#define NVME_SQTDBL_OFFSET(QID, DSTRD) 0x1000 + ((2 * (QID)) * (4 << (DSTRD))) // Submission Queue y (NVM) Tail Doorbell +#define NVME_CQHDBL_OFFSET(QID, DSTRD) 0x1000 + (((2 * (QID)) + 1) * (4 << (DSTRD))) // Completion Queue y (NVM) Head Doorbell + + +#pragma pack(1) + +// +// 3.1.1 Offset 00h: CAP - Controller Capabilities +// +typedef struct { + UINT16 Mqes; // Maximum Queue Entries Supported + UINT8 Cqr:1; // Contiguous Queues Required + UINT8 Ams:2; // Arbitration Mechanism Supported + UINT8 Rsvd1:5; + UINT8 To; // Timeout + UINT16 Dstrd:4; + UINT16 Nssrs:1; // NVM Subsystem Reset Supported NSSRS + UINT16 Css:4; // Command Sets Supported - Bit 37 + UINT16 Rsvd3:7; + UINT8 Mpsmin:4; + UINT8 Mpsmax:4; + UINT8 Rsvd4; +} NVME_CAP; + +// +// 3.1.2 Offset 08h: VS - Version +// +typedef struct { + UINT16 Mnr; // Minor version number + UINT16 Mjr; // Major version number +} NVME_VER; + +// +// 3.1.5 Offset 14h: CC - Controller Configuration +// +typedef struct { + UINT16 En:1; // Enable + UINT16 Rsvd1:3; + UINT16 Css:3; // I/O Command Set Selected + UINT16 Mps:4; // Memory Page Size + UINT16 Ams:3; // Arbitration Mechanism Selected + UINT16 Shn:2; // Shutdown Notification + UINT8 Iosqes:4; // I/O Submission Queue Entry Size + UINT8 Iocqes:4; // I/O Completion Queue Entry Size + UINT8 Rsvd2; +} NVME_CC; +#define NVME_CC_SHN_NORMAL_SHUTDOWN 1 +#define NVME_CC_SHN_ABRUPT_SHUTDOWN 2 + +// +// 3.1.6 Offset 1Ch: CSTS - Controller Status +// +typedef struct { + UINT32 Rdy:1; // Ready + UINT32 Cfs:1; // Controller Fatal Status + UINT32 Shst:2; // Shutdown Status + UINT32 Nssro:1; // NVM Subsystem Reset Occurred + UINT32 Rsvd1:27; +} NVME_CSTS; +#define NVME_CSTS_SHST_SHUTDOWN_OCCURRING 1 +#define NVME_CSTS_SHST_SHUTDOWN_COMPLETED 2 +// +// 3.1.8 Offset 24h: AQA - Admin Queue Attributes +// +typedef struct { + UINT16 Asqs:12; // Submission Queue Size + UINT16 Rsvd1:4; + UINT16 Acqs:12; // Completion Queue Size + UINT16 Rsvd2:4; +} NVME_AQA; + +// +// 3.1.9 Offset 28h: ASQ - Admin Submission Queue Base Address +// +#define NVME_ASQ UINT64 +// +// 3.1.10 Offset 30h: ACQ - Admin Completion Queue Base Address +// +#define NVME_ACQ UINT64 + +// +// 3.1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL - Submission Queue y Tail Doorbell +// +typedef struct { + UINT16 Sqt; + UINT16 Rsvd1; +} NVME_SQTDBL; + +// +// 3.1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL - Completion Queue y Head Doorbell +// +typedef struct { + UINT16 Cqh; + UINT16 Rsvd1; +} NVME_CQHDBL; + +// +// NVM command set structures +// +// Read Command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting Sector Address */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Sectors */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Af:4; /* Access Frequency */ + UINT32 Al:2; /* Access Latency */ + UINT32 Sr:1; /* Sequential Request */ + UINT32 In:1; /* Incompressible */ + UINT32 Rsvd2:24; + // + // CDW 14 + // + UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Elbat; /* Expected Logical Block Application Tag */ + UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */ +} NVME_READ; + +// +// Write Command +// + typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting Sector Address */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Sectors */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Af:4; /* Access Frequency */ + UINT32 Al:2; /* Access Latency */ + UINT32 Sr:1; /* Sequential Request */ + UINT32 In:1; /* Incompressible */ + UINT32 Rsvd2:24; + // + // CDW 14 + // + UINT32 Ilbrt; /* Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Lbat; /* Logical Block Application Tag */ + UINT16 Lbatm; /* Logical Block Application Tag Mask */ +} NVME_WRITE; + +// +// Flush +// +typedef struct { + // + // CDW 10 + // + UINT32 Flush; /* Flush */ +} NVME_FLUSH; + +// +// Write Uncorrectable command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT32 Nlb:16; /* Number of Logical Blocks */ + UINT32 Rsvd1:16; +} NVME_WRITE_UNCORRECTABLE; + +// +// Write Zeroes command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Logical Blocks */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Rsvd2; + // + // CDW 14 + // + UINT32 Ilbrt; /* Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Lbat; /* Logical Block Application Tag */ + UINT16 Lbatm; /* Logical Block Application Tag Mask */ +} NVME_WRITE_ZEROES; + +// +// Compare command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Logical Blocks */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Rsvd2; + // + // CDW 14 + // + UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Elbat; /* Expected Logical Block Application Tag */ + UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */ +} NVME_COMPARE; + +typedef union { + NVME_READ Read; + NVME_WRITE Write; + NVME_FLUSH Flush; + NVME_WRITE_UNCORRECTABLE WriteUncorrectable; + NVME_WRITE_ZEROES WriteZeros; + NVME_COMPARE Compare; +} NVME_CMD; + +typedef struct { + UINT16 Mp; /* Maximum Power */ + UINT8 Rsvd1; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Mps:1; /* Max Power Scale */ + UINT8 Nops:1; /* Non-Operational State */ + UINT8 Rsvd2:6; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Enlat; /* Entry Latency */ + UINT32 Exlat; /* Exit Latency */ + UINT8 Rrt:5; /* Relative Read Throughput */ + UINT8 Rsvd3:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rrl:5; /* Relative Read Leatency */ + UINT8 Rsvd4:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rwt:5; /* Relative Write Throughput */ + UINT8 Rsvd5:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rwl:5; /* Relative Write Leatency */ + UINT8 Rsvd6:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rsvd7[16]; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_PSDESCRIPTOR; + +// +// Identify Controller Data +// +typedef struct { + // + // Controller Capabilities and Features 0-255 + // + UINT16 Vid; /* PCI Vendor ID */ + UINT16 Ssvid; /* PCI sub-system vendor ID */ + UINT8 Sn[20]; /* Product serial number */ + + UINT8 Mn[40]; /* Proeduct model number */ + UINT8 Fr[8]; /* Firmware Revision */ + UINT8 Rab; /* Recommended Arbitration Burst */ + UINT8 Ieee_oui[3]; /* Organization Unique Identifier */ + UINT8 Cmic; /* Multi-interface Capabilities */ + UINT8 Mdts; /* Maximum Data Transfer Size */ + UINT8 Cntlid[2]; /* Controller ID */ + UINT8 Rsvd1[176]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // Admin Command Set Attributes + // + UINT16 Oacs; /* Optional Admin Command Support */ + #define NAMESPACE_MANAGEMENT_SUPPORTED BIT3 + #define FW_DOWNLOAD_ACTIVATE_SUPPORTED BIT2 + #define FORMAT_NVM_SUPPORTED BIT1 + #define SECURITY_SEND_RECEIVE_SUPPORTED BIT0 + UINT8 Acl; /* Abort Command Limit */ + UINT8 Aerl; /* Async Event Request Limit */ + UINT8 Frmw; /* Firmware updates */ + UINT8 Lpa; /* Log Page Attributes */ + UINT8 Elpe; /* Error Log Page Entries */ + UINT8 Npss; /* Number of Power States Support */ + UINT8 Avscc; /* Admin Vendor Specific Command Configuration */ + UINT8 Apsta; /* Autonomous Power State Transition Attributes */ + UINT8 Rsvd2[246]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // NVM Command Set Attributes + // + UINT8 Sqes; /* Submission Queue Entry Size */ + UINT8 Cqes; /* Completion Queue Entry Size */ + UINT16 Rsvd3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Nn; /* Number of Namespaces */ + UINT16 Oncs; /* Optional NVM Command Support */ + UINT16 Fuses; /* Fused Operation Support */ + UINT8 Fna; /* Format NVM Attributes */ + UINT8 Vwc; /* Volatile Write Cache */ + UINT16 Awun; /* Atomic Write Unit Normal */ + UINT16 Awupf; /* Atomic Write Unit Power Fail */ + UINT8 Nvscc; /* NVM Vendor Specific Command Configuration */ + UINT8 Rsvd4; /* Reserved as of Nvm Express 1.1 Spec */ + UINT16 Acwu; /* Atomic Compare & Write Unit */ + UINT16 Rsvd5; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Sgls; /* SGL Support */ + UINT8 Rsvd6[164]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // I/O Command set Attributes + // + UINT8 Rsvd7[1344]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // Power State Descriptors + // + NVME_PSDESCRIPTOR PsDescriptor[32]; + + UINT8 VendorData[1024]; /* Vendor specific data */ +} NVME_ADMIN_CONTROLLER_DATA; + +typedef struct { + UINT16 Ms; /* Metadata Size */ + UINT8 Lbads; /* LBA Data Size */ + UINT8 Rp:2; /* Relative Performance */ + #define LBAF_RP_BEST 00b + #define LBAF_RP_BETTER 01b + #define LBAF_RP_GOOD 10b + #define LBAF_RP_DEGRADED 11b + UINT8 Rsvd1:6; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_LBAFORMAT; + +// +// Identify Namespace Data +// +typedef struct { + // + // NVM Command Set Specific + // + UINT64 Nsze; /* Namespace Size (total number of blocks in formatted namespace) */ + UINT64 Ncap; /* Namespace Capacity (max number of logical blocks) */ + UINT64 Nuse; /* Namespace Utilization */ + UINT8 Nsfeat; /* Namespace Features */ + UINT8 Nlbaf; /* Number of LBA Formats */ + UINT8 Flbas; /* Formatted LBA size */ + UINT8 Mc; /* Metadata Capabilities */ + UINT8 Dpc; /* End-to-end Data Protection capabilities */ + UINT8 Dps; /* End-to-end Data Protection Type Settings */ + UINT8 Nmic; /* Namespace Multi-path I/O and Namespace Sharing Capabilities */ + UINT8 Rescap; /* Reservation Capabilities */ + UINT8 Rsvd1[88]; /* Reserved as of Nvm Express 1.1 Spec */ + UINT64 Eui64; /* IEEE Extended Unique Identifier */ + // + // LBA Format + // + NVME_LBAFORMAT LbaFormat[16]; + + UINT8 Rsvd2[192]; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 VendorData[3712]; /* Vendor specific data */ +} NVME_ADMIN_NAMESPACE_DATA; + +// +// NvmExpress Admin Identify Cmd +// +typedef struct { + // + // CDW 10 + // + UINT32 Cns:2; + UINT32 Rsvd1:30; +} NVME_ADMIN_IDENTIFY; + +// +// NvmExpress Admin Create I/O Completion Queue +// +typedef struct { + // + // CDW 10 + // + UINT32 Qid:16; /* Queue Identifier */ + UINT32 Qsize:16; /* Queue Size */ + + // + // CDW 11 + // + UINT32 Pc:1; /* Physically Contiguous */ + UINT32 Ien:1; /* Interrupts Enabled */ + UINT32 Rsvd1:14; /* reserved as of Nvm Express 1.1 Spec */ + UINT32 Iv:16; /* Interrupt Vector for MSI-X or MSI*/ +} NVME_ADMIN_CRIOCQ; + +// +// NvmExpress Admin Create I/O Submission Queue +// +typedef struct { + // + // CDW 10 + // + UINT32 Qid:16; /* Queue Identifier */ + UINT32 Qsize:16; /* Queue Size */ + + // + // CDW 11 + // + UINT32 Pc:1; /* Physically Contiguous */ + UINT32 Qprio:2; /* Queue Priority */ + UINT32 Rsvd1:13; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Cqid:16; /* Completion Queue ID */ +} NVME_ADMIN_CRIOSQ; + +// +// NvmExpress Admin Delete I/O Completion Queue +// +typedef struct { + // + // CDW 10 + // + UINT16 Qid; + UINT16 Rsvd1; +} NVME_ADMIN_DEIOCQ; + +// +// NvmExpress Admin Delete I/O Submission Queue +// +typedef struct { + // + // CDW 10 + // + UINT16 Qid; + UINT16 Rsvd1; +} NVME_ADMIN_DEIOSQ; + +// +// NvmExpress Admin Abort Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Sqid:16; /* Submission Queue identifier */ + UINT32 Cid:16; /* Command Identifier */ +} NVME_ADMIN_ABORT; + +// +// NvmExpress Admin Firmware Activate Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fs:3; /* Submission Queue identifier */ + UINT32 Aa:2; /* Command Identifier */ + UINT32 Rsvd1:27; +} NVME_ADMIN_FIRMWARE_ACTIVATE; + +// +// NvmExpress Admin Firmware Image Download Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Numd; /* Number of Dwords */ + // + // CDW 11 + // + UINT32 Ofst; /* Offset */ +} NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD; + +// +// NvmExpress Admin Get Features Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fid:8; /* Feature Identifier */ + UINT32 Sel:3; /* Select */ + UINT32 Rsvd1:21; +} NVME_ADMIN_GET_FEATURES; + +// +// NvmExpress Admin Get Log Page Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Lid:8; /* Log Page Identifier */ + #define LID_ERROR_INFO 0x1 + #define LID_SMART_INFO 0x2 + #define LID_FW_SLOT_INFO 0x3 + UINT32 Rsvd1:8; + UINT32 Numd:12; /* Number of Dwords */ + UINT32 Rsvd2:4; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_ADMIN_GET_LOG_PAGE; + +// +// NvmExpress Admin Set Features Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fid:8; /* Feature Identifier */ + UINT32 Rsvd1:23; + UINT32 Sv:1; /* Save */ +} NVME_ADMIN_SET_FEATURES; + +// +// NvmExpress Admin Format NVM Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Lbaf:4; /* LBA Format */ + UINT32 Ms:1; /* Metadata Settings */ + UINT32 Pi:3; /* Protection Information */ + UINT32 Pil:1; /* Protection Information Location */ + UINT32 Ses:3; /* Secure Erase Settings */ + UINT32 Rsvd1:20; +} NVME_ADMIN_FORMAT_NVM; + +// +// NvmExpress Admin Security Receive Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Rsvd1:8; + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + // + // CDW 11 + // + UINT32 Al; /* Allocation Length */ +} NVME_ADMIN_SECURITY_RECEIVE; + +// +// NvmExpress Admin Security Send Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Rsvd1:8; + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + // + // CDW 11 + // + UINT32 Tl; /* Transfer Length */ +} NVME_ADMIN_SECURITY_SEND; + +typedef union { + NVME_ADMIN_IDENTIFY Identify; + NVME_ADMIN_CRIOCQ CrIoCq; + NVME_ADMIN_CRIOSQ CrIoSq; + NVME_ADMIN_DEIOCQ DeIoCq; + NVME_ADMIN_DEIOSQ DeIoSq; + NVME_ADMIN_ABORT Abort; + NVME_ADMIN_FIRMWARE_ACTIVATE Activate; + NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD FirmwareImageDownload; + NVME_ADMIN_GET_FEATURES GetFeatures; + NVME_ADMIN_GET_LOG_PAGE GetLogPage; + NVME_ADMIN_SET_FEATURES SetFeatures; + NVME_ADMIN_FORMAT_NVM FormatNvm; + NVME_ADMIN_SECURITY_RECEIVE SecurityReceive; + NVME_ADMIN_SECURITY_SEND SecuritySend; +} NVME_ADMIN_CMD; + +typedef struct { + UINT32 Cdw10; + UINT32 Cdw11; + UINT32 Cdw12; + UINT32 Cdw13; + UINT32 Cdw14; + UINT32 Cdw15; +} NVME_RAW; + +typedef union { + NVME_ADMIN_CMD Admin; // Union of Admin commands + NVME_CMD Nvm; // Union of Nvm commands + NVME_RAW Raw; +} NVME_PAYLOAD; + +// +// Submission Queue +// +typedef struct { + // + // CDW 0, Common to all comnmands + // + UINT8 Opc; // Opcode + UINT8 Fuse:2; // Fused Operation + UINT8 Rsvd1:5; + UINT8 Psdt:1; // PRP or SGL for Data Transfer + UINT16 Cid; // Command Identifier + + // + // CDW 1 + // + UINT32 Nsid; // Namespace Identifier + + // + // CDW 2,3 + // + UINT64 Rsvd2; + + // + // CDW 4,5 + // + UINT64 Mptr; // Metadata Pointer + + // + // CDW 6-9 + // + UINT64 Prp[2]; // First and second PRP entries + + NVME_PAYLOAD Payload; +} NVME_SQ; + +// +// Completion Queue +// +typedef struct { + // + // CDW 0 + // + UINT32 Dword0; + // + // CDW 1 + // + UINT32 Rsvd1; + // + // CDW 2 + // + UINT16 Sqhd; // Submission Queue Head Pointer + UINT16 Sqid; // Submission Queue Identifier + // + // CDW 3 + // + UINT16 Cid; // Command Identifier + UINT16 Pt:1; // Phase Tag + UINT16 Sc:8; // Status Code + UINT16 Sct:3; // Status Code Type + UINT16 Rsvd2:2; + UINT16 Mo:1; // More + UINT16 Dnr:1; // Do Not Retry +} NVME_CQ; + +// +// Nvm Express Admin cmd opcodes +// +#define NVME_ADMIN_DEIOSQ_CMD 0x00 +#define NVME_ADMIN_CRIOSQ_CMD 0x01 +#define NVME_ADMIN_GET_LOG_PAGE_CMD 0x02 +#define NVME_ADMIN_DEIOCQ_CMD 0x04 +#define NVME_ADMIN_CRIOCQ_CMD 0x05 +#define NVME_ADMIN_IDENTIFY_CMD 0x06 +#define NVME_ADMIN_ABORT_CMD 0x08 +#define NVME_ADMIN_SET_FEATURES_CMD 0x09 +#define NVME_ADMIN_GET_FEATURES_CMD 0x0A +#define NVME_ADMIN_ASYNC_EVENT_REQUEST_CMD 0x0C +#define NVME_ADMIN_NAMESACE_MANAGEMENT_CMD 0x0D +#define NVME_ADMIN_FW_COMMIT_CMD 0x10 +#define NVME_ADMIN_FW_IAMGE_DOWNLOAD_CMD 0x11 +#define NVME_ADMIN_NAMESACE_ATTACHMENT_CMD 0x15 +#define NVME_ADMIN_FORMAT_NVM_CMD 0x80 +#define NVME_ADMIN_SECURITY_SEND_CMD 0x81 +#define NVME_ADMIN_SECURITY_RECEIVE_CMD 0x82 + + +#define NVME_IO_FLUSH_OPC 0 +#define NVME_IO_WRITE_OPC 1 +#define NVME_IO_READ_OPC 2 + +#pragma pack() + +#endif diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c new file mode 100644 index 0000000..c762ddf --- /dev/null +++ b/drivers/nvme/nvme.c @@ -0,0 +1,198 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 2017 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "NvmExpress.h" +#include "NvmCtrlLib.h" + + +#define DEVICE_INDEX_DEFAULT 0 + +static struct supported_device { + u16 vid; + u16 did; +} SUPPORTED_DEVICES[] = { + { .vid = 0x8086, .did = NVME_PCI_DID }, +}; + +static EFI_STATUS _init(storage_t *s) +{ + DEVICE_BLOCK_INFO BlockInfo; + EFI_STATUS ret; + pcidev_t pci_dev = 0; + size_t i; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_DEVICES); i++) + if (pci_find_device(SUPPORTED_DEVICES[i].vid, + SUPPORTED_DEVICES[i].did, + &pci_dev)) + break; + + DEBUG_NVME ((EFI_D_INFO, "pci_dev = 0x%X\n", pci_dev)); + if (!pci_dev) + return EFI_UNSUPPORTED; + + ret = NvmeInitialize(pci_dev); + if (ret) + DEBUG_NVME ((EFI_D_INFO, "NvmeInitialize ret = 0x%X\n", ret)); + + if (ret) + return EFI_DEVICE_ERROR; + + ret = NvmeGetMediaInfo(DEVICE_INDEX_DEFAULT, &BlockInfo); + if (EFI_ERROR(ret)) { + DEBUG_NVME ((EFI_D_ERROR, "MmcGetMediaInfo Error %d\n", ret)); + return ret; + } + + DEBUG_NVME ((EFI_D_INFO, "Index 0 BlockNum is 0x%x\n", BlockInfo.BlockNum)); + DEBUG_NVME ((EFI_D_INFO, "BlockSize is 0x%x\n", BlockInfo.BlockSize)); + s->blk_cnt = BlockInfo.BlockNum; + s->blk_sz = BlockInfo.BlockSize; + + return EFI_SUCCESS; +} + + +static EFI_LBA _read(storage_t *s, EFI_LBA start, EFI_LBA count, void *buf) +{ + EFI_STATUS ret; + + ret = NvmeReadBlocks ( + DEVICE_INDEX_DEFAULT, + start, + s->blk_sz * count, + buf); + if (!EFI_ERROR(ret)) + return count; + + return 0; +} + +static EFI_LBA _write(storage_t *s, + EFI_LBA start, + EFI_LBA count, + const void *buf) +{ + EFI_STATUS ret; + + ret = NvmeWriteBlocks (DEVICE_INDEX_DEFAULT, start, s->blk_sz * count, (void *)buf); + if (!EFI_ERROR(ret)) + return count; + + DEBUG_NVME ((EFI_D_INFO, "nvme_write Error =0x%lx\n", ret)); + return 0; +} + + +static storage_t nvme_storage = { + .init = _init, + .read = _read, + .write = _write, + .erase = NULL, + .pci_function = 0, + .pci_device = 0, +}; + + +static EFI_HANDLE nvme_handle; +static EFI_GUID nvme_pass_thru_guid = EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL_GUID; +static EFI_GUID nvme_security_guid = EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID; + +static EFI_STATUS nvme_drv_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + boot_dev_t *boot_dev; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *StorageSecurity; + EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *nvme_passthru; + + boot_dev = get_boot_media(); + if (!boot_dev) + return EFI_INVALID_PARAMETER; + if (boot_dev->type != STORAGE_NVME) + return EFI_SUCCESS; + + nvme_storage.pci_device = (NVME_DISKBUS >> 8) & 0xff; + nvme_storage.pci_function = NVME_DISKBUS & 0xff; + ret = storage_init(st, &nvme_storage, &nvme_handle); + if (EFI_ERROR(ret)) + return ret; + + StorageSecurity = NvmeGetSecurityInterface(); + if (StorageSecurity != NULL) { + ret = uefi_call_wrapper(st->BootServices->InstallProtocolInterface, + 4, + &nvme_handle, + &nvme_security_guid, + EFI_NATIVE_INTERFACE, + StorageSecurity + ); + } + + nvme_passthru = NvmeGetPassthru(); + return uefi_call_wrapper(st->BootServices->InstallProtocolInterface, 4, + &nvme_handle, &nvme_pass_thru_guid, EFI_NATIVE_INTERFACE, nvme_passthru); +} + +static EFI_STATUS nvme_drv_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + VOID *nvme_interface; + + if (!st) + return EFI_INVALID_PARAMETER; + + storage_free(st, nvme_handle); + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + nvme_handle, &nvme_pass_thru_guid, (VOID **)&nvme_interface); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(st->BootServices->UninstallProtocolInterface, 3, + nvme_handle, &nvme_pass_thru_guid, nvme_interface); + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + nvme_handle, &nvme_security_guid, (VOID **)&nvme_interface); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(st->BootServices->UninstallProtocolInterface, 3, + nvme_handle, &nvme_security_guid, nvme_interface); + + return ret; +} + +ewdrv_t nvme_drv = { + .name = "nvme", + .description = "PCI NVME driver", + .init = nvme_drv_init, + .exit = nvme_drv_exit +}; + diff --git a/drivers/nvme/nvme.h b/drivers/nvme/nvme.h new file mode 100644 index 0000000..fdc8a74 --- /dev/null +++ b/drivers/nvme/nvme.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _NVME_H_ +#define _NVME_H_ + +#include + +extern ewdrv_t nvme_drv; + +#endif /* _NVME_H_ */ + diff --git a/drivers/s8250mem32/s8250mem32.c b/drivers/s8250mem32/s8250mem32.c new file mode 100644 index 0000000..8fd6309 --- /dev/null +++ b/drivers/s8250mem32/s8250mem32.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include + +#include +#include "s8250mem32.h" + +#ifndef SERIAL_BASEADDR +#include +#define SERIAL_BASEADDR GetPciUartBase(SERIAL_PCI_DID) +#define INTEL_VID 0x8086 +static uint32_t GetPciUartBase(uint32_t pci_did) +{ + uint32_t addr; + pcidev_t pci_dev; + pci_find_device(INTEL_VID, pci_did, &pci_dev); + addr = pci_read_config32(pci_dev, PCI_BASE_ADDRESS_0); + addr = addr & ~0xf; + return addr; +} +#endif + +static EFI_STATUS s8250mem32_init(__attribute__((__unused__)) EFI_SYSTEM_TABLE *st) +{ + static struct cb_serial s; + + s.baseaddr = SERIAL_BASEADDR; + s.regwidth = HW_SERIAL_REG_WIDTH; + s.type = HW_SERIAL_TYPE; + + lib_sysinfo.serial = &s; + + serial_console_init(); + + return EFI_SUCCESS; +} + +ewdrv_t s8250mem32_drv = { + .name = "s8250mem32", + .description = "Initialize the libpayload 8250 serial driver for iomem 32bits", + .init = s8250mem32_init +}; diff --git a/drivers/s8250mem32/s8250mem32.h b/drivers/s8250mem32/s8250mem32.h new file mode 100644 index 0000000..2cc5500 --- /dev/null +++ b/drivers/s8250mem32/s8250mem32.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _S8250MEM32_H_ +#define _S8250MEM32_H_ + +#include + +extern ewdrv_t s8250mem32_drv; + +#endif /* _S8250MEM32_H_ */ diff --git a/drivers/sdhci_mmc/mmc.c b/drivers/sdhci_mmc/mmc.c new file mode 100644 index 0000000..180d6c7 --- /dev/null +++ b/drivers/sdhci_mmc/mmc.c @@ -0,0 +1,500 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 1999-2013 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "sdhci_mmc/mmc.h" +#include "sdhci_mmc/sdhci.h" +#include "sdhci_mmc/sdhci-internal.h" + + +#if defined (PLATFORM_KABYLAKE) || defined (PLATFORM_ICELAKE) +#define NO_HS400 +#endif + +/* +** Global instance of the eMMC card +*/ +struct mmc card; + +static int __mmc_send_cmd(struct cmd *c) +{ + struct mmc *m = &card; + + m->host->send_cmd(m, c); +#if MMC_DEBUG + ewdbg("=== DEBUG CMD %d args %x ==== ", c->index, c->args); + ewdbg("SDHCI_INT_ENABLE %x", sdhci_read16(m->host, SDHCI_INT_ENABLE)); + ewdbg("SDHCI_TRANSFER_MODE %x", sdhci_read16(m->host, SDHCI_TRANSFER_MODE)); + ewdbg("SDHCI_CMD_REG %x", sdhci_read16(m->host, SDHCI_CMD_REG)); + ewdbg("SDHCI_BLOCK_SIZE %x", sdhci_read16(m->host, SDHCI_BLOCK_SIZE)); + ewdbg("SDHCI_DMA_ADDR %x", sdhci_read32(m->host, SDHCI_DMA_ADDR)); + ewdbg("SDHCI_HOST_CTRL %x", sdhci_read8(m->host, SDHCI_HOST_CTRL)); + ewdbg("SDHCI_POWER_CONTROL %x", sdhci_read8(m->host, SDHCI_POWER_CONTROL)); + ewdbg("SDHCI_CLOCK_CONTROL %x", sdhci_read16(m->host, SDHCI_CLOCK_CONTROL)); + ewdbg("SDHCI_ARGUMENT %x", sdhci_read32(m->host, SDHCI_ARGUMENT)); + ewdbg("SDHCI_BLOCK_CNT %x", sdhci_read16(m->host, SDHCI_BLOCK_CNT)); + ewdbg("SDHCI_INT_STATUS %x", sdhci_read16(m->host, SDHCI_INT_STATUS)); +#endif + + return m->host->wait_cmd_done(m, c); +} + +int mmc_send_cmd(struct cmd *c) +{ + struct mmc *m = &card; + + m->host->send_cmd(m, c); +#if MMC_DEBUG + ewdbg("==== DEBUG CMD %d args %x ===== ", c->index, c->args); + ewdbg("SDHCI_INT_ENABLE %x", sdhci_read16(m->host, SDHCI_INT_ENABLE)); + ewdbg("SDHCI_TRANSFER_MODE %x", sdhci_read16(m->host, SDHCI_TRANSFER_MODE)); + ewdbg("SDHCI_CMD_REG %x", sdhci_read16(m->host, SDHCI_CMD_REG)); + ewdbg("SDHCI_BLOCK_SIZE %x", sdhci_read16(m->host, SDHCI_BLOCK_SIZE)); + ewdbg("SDHCI_DMA_ADDR %x", sdhci_read32(m->host, SDHCI_DMA_ADDR)); + ewdbg("SDHCI_HOST_CTRL %x", sdhci_read8(m->host, SDHCI_HOST_CTRL)); + ewdbg("SDHCI_POWER_CONTROL %x", sdhci_read8(m->host, SDHCI_POWER_CONTROL)); + ewdbg("SDHCI_CLOCK_CONTROL %x", sdhci_read16(m->host, SDHCI_CLOCK_CONTROL)); + ewdbg("SDHCI_ARGUMENT %x", sdhci_read32(m->host, SDHCI_ARGUMENT)); + ewdbg("SDHCI_BLOCK_CNT %x", sdhci_read16(m->host, SDHCI_BLOCK_CNT)); +#endif + return 0; +} + +uint64_t mmc_read_count(void) { + + struct mmc *m = &card; + + return (m->ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 | + m->ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 | + m->ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 | + m->ext_csd[EXT_CSD_SEC_COUNT + 3] << 24); +} + +/* ------------------------------------------------------------------------ */ +/* +** Print salient properties read from the eMMC CID and EXT_CSD registers +** (protocol version, device capacity, boot partition settings etc.) +*/ +#if DEBUG_MESSAGES +static void +emmc_show_hwinfo(struct mmc *m) +{ + // Device density, EXT_CSD.SEC_COUNT [215:212] + uint64_t sec_count; + char *uhs_timing[6] = {"SDR12", "SDR25", "SDR50", "HS200", "DDR50", "HS400"}; + sec_count = mmc_read_count() >> 11; + + // eMMC device revision, derived from EXT_CSD_REF[192] + unsigned ext_csd_rev = m->ext_csd[192]; + const char *rev = (ext_csd_rev == 5) ? "4.41" : + (ext_csd_rev == 6) ? "4.51" : + (ext_csd_rev == 7) ? "5.1" : "?"; + + // Device firmware revision, CID.PRV + unsigned cid_prv = m->cid[6-1]; + + // Manufacuring date, CID.MDT + unsigned cid_mdt = m->cid[1-1]; + + ewdbg("MMC driver: %dMB, boot %x/%x [%s %s FW %x.%x %x %d/%d]", + (unsigned int)sec_count, + m->ext_csd[179], // EXT_CSD.PARTITION_CONFIG + m->ext_csd[177], // EXT_CSD.BOOT_BUS_CONDITION + rev, uhs_timing[m->uhs_timing], + cid_prv >> 4, cid_prv & 0xf, + m->cid[2-1], // CID.PSN, product serial number + cid_mdt >> 4, (cid_mdt & 0xf) + 13); +} +#endif + +int mmc_wait_cmd_done(struct cmd *c) +{ + struct mmc *m = &card; + + return m->host->wait_cmd_done(m, c); +} + +static void mmc_reset() +{ + struct cmd c; + c.index = CMD_RESET; + c.args = 0x0; + c.flags = CMDF_NO_RESPONSE; + c.resp_len = 0; + + __mmc_send_cmd(&c); +} + +/* ------------------------------------------------------------------------ */ +/* +** eMMC: cmd API +*/ +static int mmc_send_cmd1() +{ + struct cmd c; + unsigned busy = 1; + uint64_t start = timer_us(0); + + c.index = CMD_GET_OP; + c.flags = 0; + c.resp_len = 32; + c.args = OCR_VDD_18 | OCR_CCS; + + while (busy) + { + if (timer_us(start) > 2000 * 1000) + return 1; + + if (__mmc_send_cmd(&c) != 0) + return 1; + + busy = !(c.resp[0] & OCR_BUSY); + } + + return 0; +} + +static int mmc_get_cid(struct mmc *m) +{ + struct cmd c; + int err; + + c.index = CMD_ALL_SEND_CID; + c.resp_len = 128; + c.args = 0; + c.flags = 0; + + err = __mmc_send_cmd(&c); + if (err) + { + ewerr("Error %s() failed ", __func__); + return err; + } + + memcpy(m->cid, c.resp, sizeof(m->cid)); + return 0; +} + +static void mmc_card_select(struct mmc *m) +{ + struct cmd c; + + c.index = CMD_SELECT_CARD; + c.args = m->rca << 16; + c.resp_len = 32; + c.flags = 0; + + __mmc_send_cmd(&c); +} + +static void mmc_set_rca(struct mmc *m) +{ + struct cmd c; + + m->rca = RCA_MMC; + + c.index = CMD_SEND_RCA; + c.args = m->rca << 16; + c.resp_len = 32; + c.flags = 0; + + __mmc_send_cmd(&c); +} + +static int mmc_read_ext_csd(struct mmc *m) +{ + struct cmd c; + c.index = CMD_GET_EXT_CSD; + c.addr = (uintptr_t) m->ext_csd; + c.flags = CMDF_DATA_XFER | CMDF_RD_XFER | CMDF_USE_DMA; + c.resp_len = 32; + c.nblock = 1; + c.args = 0; + + return __mmc_send_cmd(&c); +} + +int mmc_update_ext_csd() +{ + struct mmc *m = &card; + return mmc_read_ext_csd(m); +} + +int +mmc_switch(struct mmc *m, uint8_t index, uint8_t value) +{ + struct cmd c; + uint8_t state; + uint64_t start = timer_us(0); + + c.args = (MMC_SWITCH_MODE_WRITE_BYTE << 24) + | (index << 16) + | (value << 8); + c.resp_len = 32; + c.index = CMD_SWITCH; + c.flags = 0; + c.retry = 5; + + if (__mmc_send_cmd(&c) != 0) + return 1; + + mdelay(1); + /* + ** After Switch command the card can be still in + ** Programming state. Wait for it to become ready. + */ + c.resp_len = 32; + c.index = CMD_GET_STATE; + c.flags = 0; + c.args = m->rca << 16; + c.retry = 5; + + do + { + if (timer_us(start) > 100 * 1000) + return 1; + + if (__mmc_send_cmd(&c) != 0) + return 1; + + if (c.resp [0] & 0x80) /* Switch Error */ + return 1; + + state = (c.resp [0] >> 9) & 0xf; + + } while (state == 7); /* 7 = Programming State */ + + return 0; +} + +int mmc_cid(uint8_t cid[16]) +{ + struct mmc *m = &card; + memcpy(cid, m->cid, sizeof(m->cid)); + return 0; +} + +static int mmc_card_hs200(struct mmc *m) +{ + return (((m->host->caps2 & SDHCI_CAPS2_HS200) == SDHCI_CAPS2_HS200) + && (m->ext_csd[EXT_CSD_DEVICE_TYPE] & CARD_TYPE_HS200)); +} + +#ifndef NO_HS400 +static int mmc_card_hs400(struct mmc *m) +{ + return ((m->host->caps2 & SDHCI_SUPPORT_HS400) + && (m->ext_csd[EXT_CSD_DEVICE_TYPE] & CARD_TYPE_HS400)); +} +#endif + +int mmc_enable_hs200(struct mmc *m) +{ + unsigned err = 0; + err = mmc_switch(m, EXT_CSD_BUS_WIDTH, MMC_BUS_WIDTH_8); + if (err) + { + ewerr("%s() BUS_WIDTH 8", __func__); + return err; + } + + err = mmc_switch(m, EXT_CSD_HS_TIMING, EXT_CSD_HS200_ENABLE); + if (err) + { + ewerr("%s() HS_TIMING HS200", __func__); + return err; + } + + m->freq = 200000; + m->bus_width = 8; + m->uhs_timing = SDHCI_UHS_HS200; + + m->host->set_mode(m); + + return 0; +} + +#ifndef NO_HS400 +/* +** Select HS400 mode - see JEDEC84-B51 standard +** Mode selection assumes HS400 is already enabled +*/ +static int mmc_hs200_to_hs400(struct mmc *m) +{ + unsigned err = 0; + + /* + ** 7. Set the HS_TIMING [185] to 0x1 and clk <= 52 Mhz + */ + m->freq = 50000; + m->uhs_timing = SDHCI_UHS_SDR50; + + m->host->set_mode(m); + + err = mmc_switch(m, EXT_CSD_HS_TIMING, EXT_CSD_HS_ENABLE); + if (err) + { + return err; + } + + /* + ** 8. Set BUS_WIDTH [183] to 0x06 to select the dual data rate x8 bus mode + */ + err = mmc_switch(m, EXT_CSD_BUS_WIDTH, MMC_BUS_WIDTH_8_DDR); + if (err) + { + return err; + } + + /* + ** 9. Set HS_TIMING [185] to 0x3 to select HS400 + */ + err = mmc_switch(m, EXT_CSD_HS_TIMING, EXT_CSD_HS400_ENABLE); + if (err) + { + return err; + } + + m->freq = 200000; + m->bus_width = 8; + m->uhs_timing = SDHCI_UHS_HS400; + + m->host->set_mode(m); + + return 0; +} + + +/* +** Enable HS400: Restore HS200 if fails +*/ +int mmc_enable_hs400(struct mmc *m) +{ + int err = 0; + + err = mmc_hs200_to_hs400(m); + + return err; +} +#endif + +/* +** Main function for initializing SD/eMMC card. +*/ +int mmc_init_card(pcidev_t dev) +{ + struct mmc *m = &card; + int err; + + if (m->init) + return 0; + + /* + ** SD card init may fail due to some residuous + ** eMMC initialization (e.g from tuning). + ** Make sure we start with a clean structure. + */ + memset((void *)m, 0, sizeof(struct mmc)); + + struct sdhci *host = sdhci_find_controller(dev); + if (! host) + { + ewerr("Error SDHCI host controller not found"); + return 1; + } + + m->host = host; + + host->init_controller(host); + + mmc_reset(); + + /* + ** There are two ways of differentiating between an SD or eMMC card. + ** CMD1 is illegal (timeout response) for SD card thus card is + ** eMMC. Same applies for eMMC and CMD8. + ** For fastboot sake assume card is eMMC. + */ + err = mmc_send_cmd1(); + + if (err) { + ewerr("Error MMC host controller not found"); + return 1; + } + m->card_type = CARD_TYPE_MMC; + + mmc_get_cid(m); + + mmc_set_rca(m); + + mmc_card_select(m); + + // MMC + err = mmc_read_ext_csd(m); + if (err) { + ewerr(" MMC read ext csd failure"); + return err; + } + + if (mmc_card_hs200(m)) { + err = mmc_enable_hs200(m); + if (err) { + ewerr("MMC host hs200 enabling failure"); + return err; + } + + if(host->execute_tuning != NULL) + host->execute_tuning(m); + +#ifndef NO_HS400 + if (mmc_card_hs400(m)) { + err = mmc_enable_hs400(m); + if (err) { + ewerr("MMC host hs400 enabling failure"); + return err; + } + ewdbg("MMC host hs400 enabled"); + } else +#endif + ewdbg("MMC host hs200 enabled"); + } + else { + ewerr("MMC host hs200 not supported"); + return 1; + } + +#if DEBUG_MESSAGES + emmc_show_hwinfo(m); +#endif + m->init = 1; + + return 0; +} diff --git a/drivers/sdhci_mmc/mmc.h b/drivers/sdhci_mmc/mmc.h new file mode 100644 index 0000000..d2b5106 --- /dev/null +++ b/drivers/sdhci_mmc/mmc.h @@ -0,0 +1,193 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 1999-2013 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#ifndef _MMC_H_ +#define _MMC_H_ + +#include +#include +#include + +/* +** Jedec commands +*/ +#define CMD_RESET 0 +#define CMD_GET_OP 1 +#define CMD_ALL_SEND_CID 2 +#define CMD_SEND_RCA 3 +#define CMD_SWITCH 6 +#define CMD_SELECT_CARD 7 +#define CMD_GET_EXT_CSD 8 +#define CMD_MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define CMD_GET_STATE 13 +#define CMD_SET_BLOCK_LENGTH 16 +#define CMD_READ_SINGLE_BLOCK 17 +#define CMD_READ_MULTIPLE_BLOCKS 18 +#define CMD_MMC_SEND_TUNING_BLOCK_HS200 21 +#define CMD_SET_BLOCK_COUNT 23 +#define CMD_WRITE_SINGLE_BLOCK 24 +#define CMD_WRITE_MULTIPLE_BLOCKS 25 + +// SD Card +#define CMD_SEND_IF_COND 8 +#define CMD_SEND_OP_COND 41 +#define CMD_APP 55 + +/* + * SET_BUS_WIDTH parameters + */ +#define SD_SET_BUS_WIDTH4 2 +#define SD_SET_BUS_WIDTH1 0 + + +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 + +/** + * General structure reflecting the proprieties of a eMMC card + * @Host: Underlaying host controller + * @bus_width: Current bus width + * @uhs_timing: operating mode (DDR50, SDR50, HS200) + * @freq: Clock frequency + * @rca: Relative card address + * @ext_csd: Container for EXT_CSD register + * @cid: Container for CID register + * @init: Card was initialized or not + */ +struct mmc +{ + struct sdhci *host; + uint8_t bus_width; + uint8_t uhs_timing; + uint8_t card_type; + uint32_t freq; + uint32_t rca; + uint8_t ext_csd[512]; + uint8_t cid[16]; + unsigned init; +}; + +/** + * Command structure + * @index: actuall comand beeing sent + * @resp_len: length of the response + * @addr: dma buffer for data transfers + * @nblock: number of blocks to transfer + * @flags: mask for determining the transfer type + * @resp: buffer holding the response + * @retry: how many times should we resend the command if cmd fails + */ +struct cmd +{ + unsigned index; + unsigned resp_len; + uintptr_t addr; + unsigned nblock; + uint32_t flags; + uint32_t args; + uint32_t resp[32]; + uint8_t retry; + +}; + +#define MMC_COMMAND(cmd) (cmd << 5) +#define MMC_COMMAND_AC MMC_COMMAND(0) +#define MMC_COMMAND_ADTC MMC_COMMAND(1) + +#define MMC_RESPONSE_BIT(bit) (1 << bit) +#define MMC_RESPONSE_PRESENT MMC_RESPONSE_BIT(0) +#define MMC_RESPONSE_CRC MMC_RESPONSE_BIT(2) +#define MMC_RESPONSE_BUSY MMC_RESPONSE_BIT(3) +#define MMC_RESPONSE_OPCODE MMC_RESPONSE_BIT(4) +#define MMC_RESPONSE_R1 (MMC_RESPONSE_PRESENT | MMC_RESPONSE_CRC | MMC_RESPONSE_OPCODE) +#define MMC_RESPONSE_R1B (MMC_RESPONSE_PRESENT | MMC_RESPONSE_CRC | MMC_RESPONSE_OPCODE | MMC_RESPONSE_BUSY) + +#define MMC_RESPONSE_SPI_S1 MMC_RESPONSE_BIT(7) +#define MMC_RESPONSE_SPI_BUSY MMC_RESPONSE_BIT(10) +#define MMC_RESPONSE_SPI_R1B (MMC_RESPONSE_SPI_S1 | MMC_RESPONSE_SPI_BUSY) + +/* +** OCR Register constants +*/ +#define OCR_BUSY 0x80000000 +#define OCR_CCS 0x40000000 +#define OCR_VDD_18 0x00000080 +#define OCR_VDD_27_28 0x00008000 +#define OCR_VDD_28_29 0x00010000 +#define OCR_VDD_29_30 0x00020000 +#define OCR_VDD_30_31 0x00040000 +#define OCR_VDD_31_32 0x00080000 +#define OCR_VDD_32_33 0x00100000 +#define OCR_VDD_33_34 0x00200000 +#define OCR_VDD_34_35 0x00400000 +#define OCR_VDD_35_36 0x00800000 + +#define CARD_TYPE_SD10 1 +#define CARD_TYPE_SD20 2 +#define CARD_TYPE_MMC 3 + +#define RCA_MMC 1 + +/* +** EXT_CSD byte offsets used for SWITCH command +*/ +#define EXT_CSD_PARTITION_CONFIG 179 +#define EXT_CSD_BOOT_BUS_CONDITIONS 177 + +#define EXT_CSD_BUS_WIDTH 183 +#define MMC_BUS_WIDTH_4 0x01 +#define MMC_BUS_WIDTH_8 0x02 +#define MMC_BUS_WIDTH_4_DDR 0x05 +#define MMC_BUS_WIDTH_8_DDR 0x06 + + +#define EXT_CSD_HS_TIMING 185 +#define EXT_CSD_HS_ENABLE 0x01 +#define EXT_CSD_HS200_ENABLE 0x02 +#define EXT_CSD_HS400_ENABLE 0x03 + + +#define EXT_CSD_DEVICE_TYPE 196 +#define CARD_TYPE_HS 0x03 +#define CARD_TYPE_DDR50 0x0c +#define CARD_TYPE_HS200 0x30 +#define CARD_TYPE_HS400 0xc0 + +#define EXT_CSD_SEC_COUNT 212 + +extern int mmc_init_card(pcidev_t dev); +extern int mmc_send_cmd(struct cmd *c); +extern int mmc_wait_cmd_done(struct cmd *c); +extern int mmc_switch(struct mmc *m, uint8_t index, uint8_t value); +extern int mmc_cid(uint8_t cid[16]); + +extern int mmc_enable_hs200(struct mmc *m); +extern int mmc_enable_hs400(struct mmc *m); +extern int mmc_enable_ddr50(struct mmc *m); +extern int mmc_enable_sdr50(struct mmc *m); +extern int mmc_enable_sdr25(struct mmc *m); + +extern void mmc_dll_tune(void); +extern uint64_t mmc_read_count(void); +extern int mmc_update_ext_csd(void); +#endif diff --git a/drivers/sdhci_mmc/sdhci-internal.h b/drivers/sdhci_mmc/sdhci-internal.h new file mode 100644 index 0000000..866e27c --- /dev/null +++ b/drivers/sdhci_mmc/sdhci-internal.h @@ -0,0 +1,255 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 1999-2013 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#ifndef _BXT_SDHCI_H_ +#define _BXT_SDHCI_H_ + +#include "sdhci.h" + +/* + * Controller registers + */ +#define SDHCI_DMA_ADDR 0x00 +#define SDHCI_BLOCK_SIZE 0x04 + +#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +/* Block size constants */ +#define DMA_4K_BOUNDRY 0x0000 +#define DMA_8K_BOUNDRY 0x1000 +#define DMA_16K_BOUNDRY 0x2000 +#define DMA_32K_BOUNDRY 0x3000 +#define DMA_64K_BOUNDRY 0x4000 +#define DMA_128K_BOUNDRY 0x5000 +#define DMA_256K_BOUNDRY 0x6000 +#define DMA_512K_BOUNDRY 0x7000 + +#define SDHCI_BLOCK_CNT 0x06 +#define SDHCI_ARGUMENT 0x08 + +#define SDHCI_TRANSFER_MODE 0x0C +/* Transfer mode register constants */ +#define TM_USE_DMA 0x0001 +#define TM_BLOCK_CNT_ENABLE 0x0002 +#define TM_AUTO_CMD12_ENABLE 0x0004 +#define TM_AUTO_CMD23_ENABLE 0x0008 +#define TM_READ 0x0010 +#define TM_WRITE 0x0000 +#define TM_MULTI_BLOCK 0x0020 + +#define SDHCI_CMD_REG 0x0E +/* SDHC Command register definitions */ +#define SDHCI_CMD_NO_RESP 0x0000 /* No response */ +#define SDHCI_CMD_RL136 0x0001 /* 136 byte response */ +#define SDHCI_CMD_RL48 0x0002 /* 48 byte response */ +#define SDHCI_CMD_RL48_CB 0x0003 /* 48 byte response with busy check after response */ +#define SDHCI_CMD_CRC_CHECK_ENABLE 0x0008 +#define SDHCI_CMD_INDEX_CHECK_ENABLE 0x0010 +#define SDHCI_CMD_DATA_PRESENT 0x0020 +#define SDHCI_CMD_TYPE_NORMAL 0x0000 +#define SDHCI_CMD_TYPE_SUSPEND 0x0040 +#define SDHCI_CMD_TYPE_RESUME 0x0080 +#define SDHCI_CMD_TYPE_ABORT 0x00C0 +#define SDHCI_CMD_INDEX_SHIFT 8 + +#define SDHCI_TRNS_READ 0x10 + +#define SDHCI_RESPONSE 0x10 +#define SDHCI_BUFFER 0x20 + +#define SDHCI_PRESENT_STATE 0x24 +#define SDHCI_CMD_INHIBIT 0x01 +#define SDHCI_DATA_INHIBIT 0x02 + +#define SDHCI_HOST_CTRL 0x28 + +#define SDHCI_LED_ON 0x01 +#define SDHCI_WIDTH_1BIT 0x00 +#define SDHCI_WIDTH_4BITS 0x02 +#define SDHCI_WIDTH_8BITS 0x20 +#define SDHCI_HS_ENABLE 0x04 +#define SDHCI_DMA_SDMA 0x00 +#define SDHCI_DMA_ADMA32 0x10 +#define SDHCI_DMA_ADMA64 0x18 + +#define SDHCI_POWER_CONTROL 0x29 +#define SDHCI_POWER_ON 0x01 +#define SDHCI_POWER_18V 0x0A +#define SDHCI_POWER_30V 0x0C +#define SDHCI_POWER_33V 0x0E + +#define SDHCI_BLOCK_GAP_CTRL 0x2A +#define BOOT_EN 0x80 +#define BOOT_ACK_RCV 0x20 + +#define SDHCI_WAKE_UP_CONTROL 0x2B + +#define SDHCI_CLOCK_CONTROL 0x2C +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK 0xFF +#define SDHCI_DIV_MASK_LEN 8 +#define SDHCI_DIV_HI_MASK 0x300 +#define SDHCI_CLOCK_CARD_ENABLE (1 << 2) +#define SDHCI_CLOCK_STABLE (1 << 1) +#define SDHCI_CLOCK_ENABLE (1 << 0) + +#define SDHCI_TIMEOUT_CONTROL 0x2E + +#define SDHCI_SOFTWARE_RESET 0x2F +#define SDHCI_RESET_ALL 0x01 +#define SDHCI_RESET_CMD 0x02 +#define SDHCI_RESET_DATA 0x04 + +#define SDHCI_INT_STATUS 0x30 +#define SDHCI_ERR_INT_STATUS 0x32 +#define SDHCI_INT_ENABLE 0x34 +#define SDHCI_ERR_INT_ENABLE 0x36 +#define SDHCI_SIGNAL_ENABLE 0x38 + +/* Normal interrupt status constants */ +#define SDHCI_INT_CMD_COMPLETE 0x0001 +#define SDHCI_INT_XFER_COMPLETE 0x0002 +#define SDHCI_INT_BLOCK_GAP_EVENT 0x0004 +#define SDHCI_INT_DMA_INT 0x0008 +#define SDHCI_INT_BUFFER_WRITE_RDY 0x0010 +#define SDHCI_INT_BUFFER_READ_RDY 0x0020 +#define SDHCI_INT_CARD_INSERTION 0x0040 +#define SDHCI_INT_CARD_REMOVAL 0x0080 +#define SDHCI_INT_CARD_INT 0x0100 +#define SDHCI_INT_ERR_INT 0x8000 +#define SDHCI_INT_BOOT_ACK_RCV 0x2000 +#define SDHCI_INT_BOOT_TERM 0x4000 + +#define SDHCI_INT_ALL (SDHCI_INT_CMD_COMPLETE | SDHCI_INT_XFER_COMPLETE | SDHCI_INT_DMA_INT | \ + SDHCI_INT_BUFFER_READ_RDY | SDHCI_INT_BUFFER_WRITE_RDY | \ + SDHCI_INT_BOOT_ACK_RCV | SDHCI_INT_BOOT_TERM | SDHCI_INT_ERR_INT ) \ + +#define SDHCI_HOST_CTRL2 0x3e +/* Host Ctrl2 */ +#define SDHCI_SIGNALING_EN 0x08 +#define SDHCI_EXECUTE_TUNING 0x40 +#define SDHCI_CLOCK_TUNED 0x80 +#define SDHCI_UHS_MODE_SELECT 0x07 +#define SDHCI_UHS_SDR12 0x00 +#define SDHCI_UHS_SDR25 0x01 +#define SDHCI_UHS_SDR50 0x02 +#define SDHCI_UHS_HS200 0x03 +#define SDHCI_UHS_DDR50 0x04 +#define SDHCI_UHS_HS400 0x05 + +#define SDHCI_INT_DATA_AVAIL 0x00000020 +#define SDHCI_CTRL_TUNED_CLK 0x0080 + +#define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x80 +#define SDHCI_CLOCK_BASE_MASK 0x3F00 +#define SDHCI_CLOCK_V3_BASE_MASK 0xFF00 +#define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_MAX_BLOCK_MASK 0x30000 +#define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAPS_8BIT 0x40000 + +#define SDHCI_CAPABILITIES2 0x44 +#define SDHCI_CAPS2_SDR50 0x01 +#define SDHCI_CAPS2_HS200 0x02 +#define SDHCI_CAPS2_DDR50 0x04 +#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard -- Linux Kernel */ + +#define SDHCI_CAPS_VS33 0x01000000 +#define SDHCI_CAPS_VS30 0x02000000 +#define SDHCI_CAPS_VS18 0x04000000 + +#define SDHCI_MAX_CURRENT 0x48 + +/* 4C-4F reserved for more max current */ + +#define SDHCI_SET_ACMD12_ERROR 0x50 +#define SDHCI_SET_INT_ERROR 0x52 + +#define SDHCI_ADMA_ERROR 0x54 + +/* 55-57 reserved */ + +#define SDHCI_ADMA_ADDRESS 0x58 + +/* 60-FB reserved */ + +#define SDHCI_SLOT_INT_STATUS 0xFC + +#define SDHCI_BOOT_TIMEOUT_CTRL 0x70 + +#define SDHCI_HOST_VERSION 0xFE + +#define SDHCI_GET_VERSION(x) (x->version & SDHCI_SPEC_VER_MASK) + +/* These flags map directly to hardware. DO NOT CHANGE */ +#define CMDF_DATA_XFER 0x00000020 +#define CMDF_DIRECT_MASK 0x00000038 +#define CMDF_USE_DMA 0x00000100 +#define CMDF_NO_RESPONSE 0x00000200 +#define CMDF_BUSY_CHECK 0x00000800 +#define CMDF_RD_XFER 0x00001000 +#define CMDF_WR_XFER 0x00002000 +#define CMDF_BOOT_EN 0x00004000 + +#define CMDF_CRC_CHECK SDHCI_CMD_CRC_CHECK_ENABLE +#define CMDF_CMD_INDEX_CHECK SDHCI_CMD_CMD_INDEX_CHECK_ENABLE + +/* + * End of controller registers. + */ +#define SDHCI_MAX_DIV_SPEC_200 256 +#define SDHCI_MAX_DIV_SPEC_300 2046 + +#define DEFAULT_BLOCK_SIZE 512 + +// +// HS400 Tuning Definitions +// +#define RX_STROBE_DLL1_TAP_MAX_RANGE 79 +#define RX_STROBE_DLL1_TAP_MAX_RANGE_HS400 39 +#define RX_STROBE_DLL1_TAP_MIN_RANGE 0 + +#define TX_DATA_DLL_TAP_MAX_RANGE 79 +#define TX_DATA_DLL_TAP_MIN_RANGE 0 + + +#define R_SCC_MEM_SW_LTR_VALUE 0x804 // Software LTR Register +#define R_SCC_MEM_CAP_BYPASS_REG1 0x814 // Capabilities Bypass Register +#define R_SCC_MEM_IDLE_CTRL 0x81C // DevIdle Control per SCC slice +#define B_SCC_MEM_CAP_BYPASS_REG1_HS400 BIT29 +#define R_SCC_MEM_TX_CMD_DLL_CNTL 0x820 // Tx CMD Path Ctrl +#define R_SCC_MEM_TX_DATA_DLL_CNTL1 0x824 // Tx Data Path Ctrl 1 +#define R_SCC_MEM_TX_DATA_DLL_CNTL2 0x828 // Tx Data Path Ctrl 2 +#define R_SCC_MEM_RX_CMD_DATA_DLL_CNTL1 0x82C // Rx CMD&Data Path Ctrl 1 +#define R_SCC_MEM_RX_STROBE_DLL_CNTL 0x830 // Rx Strobe Ctrl Path +#define R_SCC_MEM_RX_CMD_DATA_DLL_CNTL2 0x834 // Rx CMD&Data Path Ctrl 2 +#define N_SCC_MEM_RX_CMD_DATA_DLL_CNTL2_CLKSRC_RX 16 +#define V_SCC_MEM_RX_CMD_DATA_DLL_CNTL2_CLKSRC_RX_CLK_AUTO 0x2 +#define R_SCC_MEM_CUR_XFSM 0x858 // Internal Clock Unit XFSM + +#endif diff --git a/drivers/sdhci_mmc/sdhci.c b/drivers/sdhci_mmc/sdhci.c new file mode 100644 index 0000000..e3e74f4 --- /dev/null +++ b/drivers/sdhci_mmc/sdhci.c @@ -0,0 +1,617 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 1999-2013 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "sdhci_mmc/sdhci-internal.h" +#include "sdhci_mmc/mmc.h" + +#define ms(x) ((x) * 1000) +#define RPMB_PARTITION 3 +#define EXT_CSD_PARTITION_CONFIG 179 + +#define MAX_TUNING_LOOP 40 + +static void sdhci_reset(struct sdhci *host, uint8_t mask); + +static void sdhci_start_tuning(struct sdhci *host) +{ + uint16_t ctrl; + + ctrl = sdhci_read16(host, SDHCI_HOST_CTRL2); + ctrl |= SDHCI_EXECUTE_TUNING; + ctrl |= SDHCI_SIGNALING_EN; + sdhci_write16(host, SDHCI_HOST_CTRL2, ctrl); + + sdhci_write32(host, SDHCI_INT_ENABLE, SDHCI_INT_DATA_AVAIL); + sdhci_write32(host, SDHCI_SIGNAL_ENABLE, SDHCI_INT_DATA_AVAIL); +} + +static void sdhci_reset_tuning(struct sdhci *host) +{ + uint16_t ctrl; + + ctrl = sdhci_read16(host, SDHCI_HOST_CTRL2); + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + ctrl &= ~SDHCI_EXECUTE_TUNING; + sdhci_write16(host, SDHCI_HOST_CTRL2, ctrl); +} + +static int sdhci_abort_tuning(struct sdhci *host) +{ + struct cmd tuning_cmd = {0}; + struct cmd wait_cmd; + int ret; + + sdhci_write32(host, SDHCI_INT_ENABLE, SDHCI_INT_DATA_AVAIL); + sdhci_write32(host, SDHCI_SIGNAL_ENABLE, SDHCI_INT_DATA_AVAIL); + + memset(&tuning_cmd, 0, sizeof(tuning_cmd)); + tuning_cmd.index = CMD_MMC_STOP_TRANSMISSION; + tuning_cmd.flags = MMC_RESPONSE_SPI_R1B | MMC_RESPONSE_R1B | MMC_COMMAND_AC; + tuning_cmd.flags |= CMDF_DATA_XFER | CMDF_USE_DMA; + tuning_cmd.flags |= CMDF_WR_XFER; + + ret = mmc_send_cmd(&tuning_cmd); + if (ret) + return EFI_DEVICE_ERROR; + + memset(&wait_cmd, 0, sizeof(wait_cmd)); + wait_cmd.flags = CMDF_DATA_XFER; + ret = mmc_wait_cmd_done(&wait_cmd); + + return EFI_SUCCESS; +} + +static int sdhci_send_tuning(struct sdhci *host, uint32_t opcode) +{ + struct cmd tuning_cmd = {0}; + uint32_t buf[256]={0}; + uint64_t start; + int ret; + + tuning_cmd.index = opcode; + tuning_cmd.args = 0; + tuning_cmd.flags = MMC_RESPONSE_R1 | MMC_COMMAND_ADTC; + tuning_cmd.flags |= CMDF_RD_XFER; + tuning_cmd.flags |= SDHCI_CMD_DATA_PRESENT; + tuning_cmd.flags |= TM_USE_DMA; + tuning_cmd.flags |= TM_BLOCK_CNT_ENABLE; + tuning_cmd.resp_len = 32; + tuning_cmd.addr = (uintptr_t)buf; + tuning_cmd.retry = 0; + + sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 128)); + sdhci_write16(host, SDHCI_TRANSFER_MODE, SDHCI_TRNS_READ ); + + ret = mmc_send_cmd(&tuning_cmd); + if (ret) + return EFI_DEVICE_ERROR; + + sdhci_write16(host, SDHCI_TRANSFER_MODE, SDHCI_TRNS_READ ); + + start = timer_us(0); + do + { + unsigned int intmask; + intmask = sdhci_read16(host, SDHCI_INT_STATUS); + if ((intmask & SDHCI_INT_DATA_AVAIL)) + { + sdhci_write16(host, SDHCI_INT_STATUS, intmask & SDHCI_INT_DATA_AVAIL); + return EFI_SUCCESS; + } + } while (timer_us(start) < 10 * 1000); + + return EFI_NOT_READY; +} + +static int sdhci_execute_tuning(struct mmc *m) +{ + struct sdhci *host = m->host; + uint32_t ier1; + uint32_t ier2; + uint16_t ctrl = 0; + int counter; + int ret = 0; + int err = 0; + + ier1 = sdhci_read32(host, SDHCI_INT_ENABLE); + ier2 = sdhci_read32(host, SDHCI_SIGNAL_ENABLE); + + sdhci_start_tuning(host); + + /* + * Issue TUNING_CMD repeatedly till Execute Tuning is set to 0 or the number + * of loops reaches 40 times. + */ + for (counter = 0; counter <= MAX_TUNING_LOOP; counter ++) + { + ret = sdhci_send_tuning(host, CMD_MMC_SEND_TUNING_BLOCK_HS200); + if (ret != EFI_SUCCESS) + { + sdhci_reset_tuning(host); + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_abort_tuning(host); + break; + } + + ctrl = sdhci_read16(host, SDHCI_HOST_CTRL2); + if ((ctrl & SDHCI_EXECUTE_TUNING) == 0) + break; + } + + if (!(ctrl & SDHCI_CLOCK_TUNED)) + { + err = EFI_DEVICE_ERROR; + printf("tuning failed, ctrl=0x%X\n", ctrl); + } + + sdhci_write32(host, SDHCI_INT_ENABLE, ier1); + sdhci_write32(host, SDHCI_SIGNAL_ENABLE, ier2); + + return err; +} + + +static void sdhci_reset(struct sdhci *host, uint8_t mask) +{ + uint64_t start = timer_us(0); + + sdhci_write8(host, SDHCI_SOFTWARE_RESET, mask); + + while (sdhci_read8(host, SDHCI_SOFTWARE_RESET)) + { + if (timer_us(start) > ms(100)) + { + ewerr("ERROR: Failed to reset controller"); + return ; + } + } + return; +} + +static void sdhci_set_voltage(struct sdhci *host, uint8_t power) +{ + uint8_t pwr = 0; + + /* Select voltage based on capability register */ + if (!power) + { + if (host->caps1 & SDHCI_CAPS_VS18) + power = 18; + else if (host->caps1 & SDHCI_CAPS_VS30) + power = 30; + else if (host->caps1 & SDHCI_CAPS_VS33) + power = 33; + } + + sdhci_write8(host, SDHCI_POWER_CONTROL, 0); + + switch (power) + { + case 33: + pwr = SDHCI_POWER_ON | SDHCI_POWER_33V; + break; + case 30: + pwr = SDHCI_POWER_ON | SDHCI_POWER_30V; + break; + case 18: + pwr = SDHCI_POWER_ON | SDHCI_POWER_18V; + break; + default: + break; + } + + sdhci_write8(host, SDHCI_POWER_CONTROL, pwr); + return; +} + +static int sdhci_set_clock(struct sdhci *host, uint32_t freq) +{ + uint16_t div, clk; + uint64_t start = timer_us(0); + + /* Use 400 Khz as an initialization frequency */ + if (freq == (uint32_t)-1) + freq = 400; + + sdhci_write16(host, SDHCI_CLOCK_CONTROL, 0); + + if (host->f_max <= freq) + div = 1; + else + { + for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) + { + if ((host->f_max / div) <= freq) + break; + } + } + + div >>= 1; + + clk = SDHCI_CLOCK_ENABLE | (div << SDHCI_DIVIDER_SHIFT); + + sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk); + while (!(sdhci_read16(host, SDHCI_CLOCK_CONTROL) & SDHCI_CLOCK_STABLE)) + { + if (timer_us(start) > 100 * 1000) + return 1; + } + + clk |= SDHCI_CLOCK_CARD_ENABLE; + sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk); + + return 0; +} + +static int init_controller(struct sdhci *host) +{ + /* Reset Controller */ + sdhci_reset(host, SDHCI_RESET_ALL); + + /* Set Minimum voltage */ + sdhci_set_voltage(host, 0); + + /* Set Clock to base frequency */ + if (sdhci_set_clock(host, -1) != 0) + { + ewerr("Error: Failed to set clock base frequency "); + return 1; + } + + sdhci_write8(host, SDHCI_TIMEOUT_CONTROL, 0xe); + + /* + ** Clear IRQ status, error status and + ** enable interrupts + */ + sdhci_write16(host, SDHCI_INT_STATUS, 0xffff); + sdhci_write16(host, SDHCI_ERR_INT_STATUS, 0xffff); + sdhci_write16(host, SDHCI_INT_ENABLE, SDHCI_INT_ALL); + sdhci_write16(host, SDHCI_ERR_INT_ENABLE, 0xffff); + + return 0; +} + +static int +sdhci_set_mode(struct mmc *m) +{ + struct sdhci *host = m->host; + int err; + + err = sdhci_set_clock(host, m->freq); + if (err) + { + ewerr("Failed to set clock to %d ", m->freq); + return err; + } + + uint16_t ctrl2 = sdhci_read16(host, SDHCI_HOST_CTRL2); + ctrl2 &= ~SDHCI_UHS_MODE_SELECT; + ctrl2 |= m->uhs_timing; + sdhci_write16(host, SDHCI_HOST_CTRL2, ctrl2); + + uint8_t ctrl = sdhci_read8(host, SDHCI_HOST_CTRL); + if (m->bus_width == 8) + { + ctrl &= ~SDHCI_WIDTH_4BITS; + ctrl |= SDHCI_WIDTH_8BITS; + } else if (m->bus_width == 4) + { + ctrl &= ~SDHCI_WIDTH_8BITS; + ctrl |= SDHCI_WIDTH_4BITS; + } + + if (m->freq > 25000) + ctrl |= SDHCI_HS_ENABLE; + + sdhci_write8(host, SDHCI_HOST_CTRL, ctrl); + + return 0; +} + +static uint16_t sdhci_make_cmd(struct cmd *c) +{ + int ret = c->flags & CMDF_DIRECT_MASK; + + switch (c->resp_len) + { + case 0: + ret |= SDHCI_CMD_NO_RESP; + break; + case 128: + ret |= SDHCI_CMD_RL136; + break; + case 32: + if (c->flags & CMDF_BUSY_CHECK) + ret |= SDHCI_CMD_RL48_CB; + else + ret |= SDHCI_CMD_RL48; + break; + default: + break; + } + + if (c->flags & CMDF_DATA_XFER) + ret |= SDHCI_CMD_DATA_PRESENT; + + ret |= (c->index << SDHCI_CMD_INDEX_SHIFT); + return ret; + +} + +static void +sdhci_wait_for_state_unset(struct sdhci *host, uint16_t bits, uint32_t timeout_us) +{ + uint16_t value; + + do { + value = sdhci_read16(host, SDHCI_PRESENT_STATE); + if (!(value & bits)) + break; + udelay(1); + } while (timeout_us-- > 0); + + if (!timeout_us) + ewerr("%s() timeout for 0x%x", __func__, bits); +} + +static void +sdhci_send_cmd(struct mmc *m, struct cmd *c) +{ + struct sdhci *host = m->host; + uint16_t tmode = 0; + + sdhci_wait_for_state_unset(host, SDHCI_CMD_INHIBIT, 100000); + + if (c->index != CMD_MMC_SEND_TUNING_BLOCK_HS200) + sdhci_wait_for_state_unset(host, SDHCI_DATA_INHIBIT, 180000); + + /* clear irq_status/err_sts register */ + sdhci_write32(host, SDHCI_INT_STATUS, 0xffffffff); + + if (c->flags & (CMDF_DATA_XFER | CMDF_BOOT_EN)) + { + tmode = (c->flags & CMDF_RD_XFER) ? TM_READ : TM_WRITE; + + if (c->flags & CMDF_USE_DMA) + tmode |= TM_USE_DMA; + + if (c->nblock > 1) + tmode |= (TM_MULTI_BLOCK | TM_BLOCK_CNT_ENABLE); + + sdhci_write16(host, SDHCI_BLOCK_CNT, c->nblock); + sdhci_write32(host, SDHCI_DMA_ADDR, c->addr); + + if (c->index == 21) + sdhci_write16(host, SDHCI_BLOCK_SIZE, 128 | DMA_128K_BOUNDRY); + else + sdhci_write16(host, SDHCI_BLOCK_SIZE, DEFAULT_BLOCK_SIZE | DMA_512K_BOUNDRY); + + sdhci_write16(host, SDHCI_TRANSFER_MODE, tmode); + + if (c->flags & CMDF_BOOT_EN) + { + sdhci_write32(host, SDHCI_BOOT_TIMEOUT_CTRL, 0xffff); + sdhci_write8(host, SDHCI_BLOCK_GAP_CTRL, BOOT_EN | BOOT_ACK_RCV); + } + } + sdhci_write32(host, SDHCI_ARGUMENT, c->args); + sdhci_write16(host, SDHCI_CMD_REG, sdhci_make_cmd(c)); + + return; +} + +/* ======================================================================== */ +/* +** Wait for interrupt, analize the response depending on the transfer type +*/ +static int +sdhci_wait_cmd_done(struct mmc *m, struct cmd *c) +{ + struct sdhci *host = m->host; + uint16_t nis; + uint64_t start = timer_us(0); + size_t i; + + /* Something went wrong if we do not get an interrupt in the first 100 ms */ + while (! (nis = sdhci_read16(host, SDHCI_INT_STATUS))) + { + if (timer_us(start) > 100 * 1000) + { + ewerr("CMD%d timeout err %x", c->index, sdhci_read16(host, SDHCI_ERR_INT_STATUS)); + goto exit; + } + } + + /* Handle commands that do not involve data transfer */ + if (!(c->flags & CMDF_DATA_XFER)) + { + /* handle error case first */ + if (nis & SDHCI_INT_ERR_INT) + { + ewerr("%s() CMD%d Error Int asserted %x", + __func__, c->index, sdhci_read16(host, SDHCI_ERR_INT_STATUS)); + goto exit; + } + + if (nis & SDHCI_INT_CMD_COMPLETE) + { + switch (c->resp_len) + { + case 128: + for (i = 0; i < 4; i++) + { + c->resp[i] = sdhci_read32(host, SDHCI_RESPONSE + (3-i)*4)<<8; + if (i != 3) + c->resp[i] |= sdhci_read8(host, SDHCI_RESPONSE + (3-i)*4-1); + } + break; + case 32: + c->resp[0] = sdhci_read32(host, SDHCI_RESPONSE); + break; + default: + break; + } + + sdhci_write16(host, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE); + } + } + + /* Handle Boot Partition ACK message */ + else if (c->flags & CMDF_BOOT_EN) + { + if (! (nis & SDHCI_INT_BOOT_ACK_RCV)) + goto exit; + + sdhci_write32(host, SDHCI_BOOT_TIMEOUT_CTRL, 0xffffffff); + sdhci_write16(host, SDHCI_INT_STATUS, SDHCI_INT_BOOT_ACK_RCV); + + return 0; + } + + /* Handle R/W transfers */ + else if (c->flags & CMDF_DATA_XFER) + { + /* Give slow SD cards a change to finish the transfer (4s) */ + start = timer_us(0); + + while (! (nis & SDHCI_INT_XFER_COMPLETE)) + { + nis = sdhci_read16(host, SDHCI_INT_STATUS); + + if (timer_us(start) > ms(6000)) + { + ewerr("CMD timeout"); + goto exit; + } + if (sdhci_read16(host, SDHCI_ERR_INT_STATUS)) + { + ewerr("err_sts %x auto_cmd12 %x", sdhci_read16(host, SDHCI_ERR_INT_STATUS), + sdhci_read8(host, 0x3c)); + goto exit; + } + + if (nis & SDHCI_INT_DMA_INT) + { + sdhci_write32(host, SDHCI_DMA_ADDR, sdhci_read32(host, SDHCI_DMA_ADDR)); + sdhci_write16(host, SDHCI_INT_STATUS, SDHCI_INT_DMA_INT); + } + + if (nis & SDHCI_INT_BUFFER_READ_RDY) + { + sdhci_write16(host, SDHCI_INT_STATUS, SDHCI_INT_BUFFER_READ_RDY); + return 0; + } + } + + sdhci_write16(host, SDHCI_INT_STATUS, SDHCI_INT_DMA_INT); + sdhci_write16(host, SDHCI_INT_STATUS, SDHCI_INT_XFER_COMPLETE); + } + + return 0; + +exit: + sdhci_reset(host, SDHCI_RESET_DATA | SDHCI_RESET_CMD); + sdhci_write16(host, SDHCI_INT_STATUS, 0xffff); + sdhci_write16(host, SDHCI_ERR_INT_STATUS, 0xffff); + return 1; +} + +static int sdhci_wait_boot_done(struct mmc *m, uintptr_t *dma_addr) +{ + unsigned res; + uint16_t nis = sdhci_read16(m->host, SDHCI_INT_STATUS); + unsigned sdma_addr = 0; + + res = nis & SDHCI_INT_BOOT_TERM; + + if (nis & SDHCI_INT_DMA_INT) + { + sdma_addr = sdhci_read32(m->host, SDHCI_DMA_ADDR); + sdhci_write32(m->host, SDHCI_DMA_ADDR, sdma_addr); + sdhci_write16(m->host, SDHCI_INT_STATUS, SDHCI_INT_DMA_INT); + } + + *dma_addr = sdma_addr; + + return res; +} + +static void sdhci_boot_stop(struct mmc *m) +{ + uint8_t gap_ctrl; + + gap_ctrl = sdhci_read8(m->host, SDHCI_BLOCK_GAP_CTRL); + sdhci_write8(m->host, SDHCI_BLOCK_GAP_CTRL, gap_ctrl & ~BOOT_EN); + sdhci_write16(m->host, SDHCI_INT_STATUS, 0xffff); + sdhci_write16(m->host, SDHCI_ERR_INT_STATUS, 0xffff); + + sdhci_reset(m->host, SDHCI_RESET_DATA | SDHCI_RESET_CMD); +} + +static struct sdhci host; + +struct sdhci *sdhci_find_controller(pcidev_t dev) +{ + uint32_t addr; + + host.init_controller = init_controller; + host.send_cmd = sdhci_send_cmd; + host.wait_cmd_done = sdhci_wait_cmd_done; + host.wait_boot_done = sdhci_wait_boot_done; + host.boot_stop = sdhci_boot_stop; + host.set_mode = sdhci_set_mode; + host.execute_tuning = sdhci_execute_tuning; + + addr = pci_read_config32(dev, PCI_BASE_ADDRESS_0); + host.ioaddr = (addr & ~0xf); + + /* + ** Discover controller capabilities + */ + host.voltage = 0; + host.caps1 = sdhci_read32(&host, SDHCI_CAPABILITIES); + host.caps2 = sdhci_read32(&host, SDHCI_CAPABILITIES + 4); + + host.f_max = (host.caps1 & SDHCI_CLOCK_V3_BASE_MASK) + >> SDHCI_CLOCK_BASE_SHIFT; + + host.f_max *= 1000; + + if (host.f_max == 0) { + ewerr("%s: Hardware doesn't specify base clock frequency", + __func__); + } + + sdhci_reset(&host, SDHCI_RESET_ALL); + + return &host; +} diff --git a/drivers/sdhci_mmc/sdhci.h b/drivers/sdhci_mmc/sdhci.h new file mode 100644 index 0000000..5de1906 --- /dev/null +++ b/drivers/sdhci_mmc/sdhci.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 1999-2013 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#ifndef _SDHCI_H_ +#define _SDHCI_H_ + +#include + +#include "mmc.h" +#include "pci.h" + +/** + * Generic SDHCI controller + * @init_controller: controller initialization function + * @send_cmd: send CMD to card + * @wait_cmd_done: waits for a command to finish + * @wait_boot_done: waits for boot protocol to finish + * @boot_stop: stop boot process + * @set_mode: set controller parameters + * @execute_tuning: HS200 tuning procedure + * @caps1, caps2: capabilities 1,2 registers + * @voltage: Operating voltate + * @f_max: maximum available frequency + */ +struct sdhci +{ + uintptr_t ioaddr; + + int (*init_controller)(struct sdhci *host); + void (*send_cmd)(struct mmc *m, struct cmd *c); + int (*wait_cmd_done)(struct mmc *m, struct cmd *c); + int (*wait_boot_done)(struct mmc *m, uintptr_t *dma_addr); + void (*boot_stop)(struct mmc *m); + int (*set_mode)(struct mmc *m); + int (*execute_tuning)(struct mmc *m); + + uint32_t caps1; + uint32_t caps2; + + unsigned voltage; + unsigned f_max; +}; + +extern struct sdhci *sdhci_find_controller(pcidev_t); + +#define sdhci_write32(h, o, v) write32((void *)((h)->ioaddr + o), v) +#define sdhci_write16(h, o, v) write16((void *)((h)->ioaddr + o), v) +#define sdhci_write8(h, o, v) write8((void *)((h)->ioaddr + o), v) + +#define sdhci_read32(h, o) read32((void *)((h)->ioaddr + o)) +#define sdhci_read16(h, o) read16((void *)((h)->ioaddr + o)) +#define sdhci_read8(h, o) read8((void *)((h)->ioaddr + o)) + +#endif /* _SDHCI_H_ */ diff --git a/drivers/sdhci_mmc/sdhci_mmc.c b/drivers/sdhci_mmc/sdhci_mmc.c new file mode 100644 index 0000000..cd006e0 --- /dev/null +++ b/drivers/sdhci_mmc/sdhci_mmc.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include + +#include + +#include "sdhci_mmc/mmc.h" +#include "sdhci_mmc/sdhci-internal.h" +#include "sdhci_mmc/sdhci.h" +#include "sdhci_mmc/sdhci_mmc.h" + +const EFI_LBA BLOCK_MAX = 0xffff; + +static struct supported_device { + u16 vid; + u16 did; +} SUPPORTED_DEVICES[] ={ + { .vid = 0x1b36, .did = 0x7 }, + { .vid = 0x8086, .did = EMMC_DEVICEID }, +}; + +static EFI_STATUS _init(storage_t *s) +{ + int ret; + pcidev_t pci_dev = 0; + size_t i; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_DEVICES); i++) + if (pci_find_device(SUPPORTED_DEVICES[i].vid, + SUPPORTED_DEVICES[i].did, + &pci_dev)) + break; + + if (!pci_dev) + return EFI_UNSUPPORTED; + + ret = mmc_init_card (pci_dev); + if (ret) + return EFI_DEVICE_ERROR; + + s->blk_cnt = mmc_read_count(); + s->blk_sz = DEFAULT_BLOCK_SIZE; + + return EFI_SUCCESS; +} + +static EFI_STATUS set_block_count(unsigned count) +{ + int ret; + struct cmd send_cmd; + + memset(&send_cmd, 0, sizeof(send_cmd)); + send_cmd.args = count; + send_cmd.resp_len = 32; + send_cmd.flags = 0; + send_cmd.index = CMD_SET_BLOCK_COUNT; + send_cmd.retry = 5; + ret = mmc_send_cmd(&send_cmd); + if (ret) + return EFI_DEVICE_ERROR; + + ret = mmc_wait_cmd_done(&send_cmd); + + return ret ? EFI_DEVICE_ERROR : EFI_SUCCESS; +} + +static EFI_STATUS transfer_data(bool read, EFI_LBA start, EFI_LBA count, void *buf) +{ + int ret; + struct cmd send_cmd; + struct cmd wait_cmd; + + memset(&send_cmd, 0, sizeof(send_cmd)); + send_cmd.args = start; + send_cmd.nblock = count; + send_cmd.addr = (uintptr_t)buf; + send_cmd.resp_len = 32; + send_cmd.flags = CMDF_DATA_XFER | CMDF_USE_DMA; + send_cmd.flags |= read ? CMDF_RD_XFER : CMDF_WR_XFER; + + if (read) { + if (count > 1) { + send_cmd.index = CMD_READ_MULTIPLE_BLOCKS; + ret = set_block_count(count); + if (EFI_ERROR(ret)) + return EFI_DEVICE_ERROR; + } + else + send_cmd.index = CMD_READ_SINGLE_BLOCK; + } + else { + if (count > 1) { + send_cmd.index = CMD_WRITE_MULTIPLE_BLOCKS; + ret = set_block_count(count); + if (EFI_ERROR(ret)) + return EFI_DEVICE_ERROR; + } + else + send_cmd.index = CMD_WRITE_SINGLE_BLOCK; + } + + ret = mmc_send_cmd(&send_cmd); + if (ret) + return EFI_DEVICE_ERROR; + + memset(&wait_cmd, 0, sizeof(wait_cmd)); + wait_cmd.flags = CMDF_DATA_XFER; + ret = mmc_wait_cmd_done(&wait_cmd); + + return ret ? EFI_DEVICE_ERROR : EFI_SUCCESS; +} + +static EFI_LBA split_and_transfer_data(storage_t *s, bool read, EFI_LBA start, + EFI_LBA count, void *buf) +{ + EFI_LBA cur_count, transfered = 0; + EFI_STATUS ret; + + do { + cur_count = count > BLOCK_MAX ? BLOCK_MAX : count; + ret = transfer_data(read, start, cur_count, buf); + if (EFI_ERROR(ret)) + break; + start += cur_count; + buf += cur_count * s->blk_sz; + count -= cur_count; + transfered += cur_count; + } while (count > 0); + + return transfered; +} + +static EFI_LBA _read(storage_t *s, EFI_LBA start, EFI_LBA count, void *buf) +{ + return split_and_transfer_data(s, true, start, count, buf); +} + +static EFI_LBA _write(storage_t *s, EFI_LBA start, EFI_LBA count, const void *buf) +{ + return split_and_transfer_data(s, false, start, count, (void *)buf); +} + +static storage_t sdhci_mmc_storage = { + .init = _init, + .read = _read, + .write = _write, + .erase = NULL, + .pci_function = 0, + .pci_device = 0, +}; + +static EFI_HANDLE handle; + +static EFIAPI EFI_STATUS +sdio_send_command(EFI_SD_HOST_IO_PROTOCOL *This, + UINT16 CommandIndex, + UINT32 Argument, + TRANSFER_TYPE DataType, + UINT8 *Buffer, + UINT32 BufferSize, + __attribute__((__unused__)) RESPONSE_TYPE ResponseType, + __attribute__((__unused__)) UINT32 TimeOut, + UINT32 *ResponseData) +{ + EFI_STATUS ret; + struct cmd c; + int mret; + storage_t *storage; + + ret = sdio_get_storage(This, &storage); + if (EFI_ERROR(ret)) + return ret; + + memset(&c, 0, sizeof(c)); + + c.index = CommandIndex; + + if (Buffer) { + c.addr = (uintptr_t)Buffer; + c.nblock = BufferSize / storage->blk_sz; + } + + if (DataType == InData) + c.flags |= CMDF_RD_XFER; + else if (DataType == OutData) + c.flags |= CMDF_WR_XFER; + + if (DataType != NoData) + c.flags |= CMDF_DATA_XFER | CMDF_USE_DMA; + + c.resp_len = 32; + c.args = Argument; + c.retry = 5; + + mret = mmc_send_cmd(&c); + if (mret != 0) + return EFI_DEVICE_ERROR; + + mret = mmc_wait_cmd_done(&c); + if (mret != 0) + return EFI_DEVICE_ERROR; + + if (ResponseData) + memcpy(ResponseData, &c.resp, sizeof(*ResponseData)); + + if (CommandIndex == CMD_SWITCH) { + mdelay(1); + mmc_update_ext_csd(); + } + + return EFI_SUCCESS; +} + +static EFI_STATUS sdhci_mmc_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_GUID sdio_guid = EFI_SD_HOST_IO_PROTOCOL_GUID; + boot_dev_t *boot_dev; + + if (!st) + return EFI_INVALID_PARAMETER; + + boot_dev = get_boot_media(); + if (!boot_dev) + return EFI_INVALID_PARAMETER; + if (boot_dev->type != STORAGE_EMMC) + return EFI_SUCCESS; + + sdhci_mmc_storage.pci_device = (boot_dev->diskbus >> 8) & 0xff; + sdhci_mmc_storage.pci_function = boot_dev->diskbus & 0xff; + + ret = storage_init(st, &sdhci_mmc_storage, &handle); + if (EFI_ERROR(ret)) + return ret; + + ret = sdio_init(st, handle, &sdhci_mmc_storage); + if (EFI_ERROR(ret)) + storage_free(st, handle); + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + handle, &sdio_guid, (void **)&sdio); + if (EFI_ERROR(ret)) { + sdio_free(st, handle); + storage_free(st, handle); + } + + sdio->SendCommand = sdio_send_command; + return ret; +} + +static EFI_STATUS sdhci_mmc_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + ret = sdio_free(st, handle); + if (EFI_ERROR(ret)) + return ret; + + return storage_free(st, handle); +} + +ewdrv_t sdhci_mmc_drv = { + .name = "sdhci_mmc", + .description = "SDHCI PCI eMMC driver", + .init = sdhci_mmc_init, + .exit = sdhci_mmc_exit +}; diff --git a/drivers/sdhci_mmc/sdhci_mmc.h b/drivers/sdhci_mmc/sdhci_mmc.h new file mode 100644 index 0000000..4104d35 --- /dev/null +++ b/drivers/sdhci_mmc/sdhci_mmc.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _SDHCI_MMC_H_ +#define _SDHCI_MMC_H_ + +#include + +extern ewdrv_t sdhci_mmc_drv; + +#endif /* _SDHCI_MMC_H_ */ diff --git a/drivers/tco_wdt/tco_protocol.h b/drivers/tco_wdt/tco_protocol.h new file mode 100644 index 0000000..d70dfd9 --- /dev/null +++ b/drivers/tco_wdt/tco_protocol.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + */ + +#ifndef _TCO_PROTOCOL_H_ +#define _TCO_PROTOCOL_H_ + +#include + +#define EFI_TCO_RESET_PROTOCOL_GUID \ + {0xa6a79162, 0xe325, 0x4c30,{0xbc, 0xc3, 0x59, 0x37, 0x30, 0x64, 0xef, 0xb3}} + +typedef struct _EFI_TCO_RESET_PROTOCOL EFI_TCO_RESET_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_TCO_RESET_PROTOCOL_ENABLE_WATCHDOG) ( + IN OUT UINT32 *RcrbGcsValue + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_TCO_RESET_PROTOCOL_DISABLE_WATCHDOG) ( + IN UINT32 RcrbGcsValue + ); + +struct _EFI_TCO_RESET_PROTOCOL { + EFI_TCO_RESET_PROTOCOL_ENABLE_WATCHDOG EnableTcoReset; + EFI_TCO_RESET_PROTOCOL_DISABLE_WATCHDOG DisableTcoReset; +}; + +#endif /* _TCO_PROTOCOL_H_ */ diff --git a/drivers/tco_wdt/tco_wdt.c b/drivers/tco_wdt/tco_wdt.c new file mode 100644 index 0000000..d14f35b --- /dev/null +++ b/drivers/tco_wdt/tco_wdt.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Baofeng, Tian + * Author: Xinanx, Luo + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include + +#include "tco_wdt/tco_protocol.h" +#include "tco_wdt/tco_wdt.h" + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +/* TCO Registers */ +#define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload/Curr. Value */ +#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */ +#define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */ +#define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */ +#define TCO2_CNT (TCOBASE + 0x0a) /* TCO1 Control Register */ +#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value*/ + +#define PMC_GCR_PMC_CFG_REG (0xfe043008) /*NO_REBOOT bit config reg*/ + +/* TCO Registers' bits */ +#define TCO_HALT_BIT BIT(11) +#define TCO_TIMEOUT1_BIT BIT(3) +#define TCO_TIMEOUT2_BIT BIT(2) +#define NO_REBOOT_BIT BIT(4) + +#define TCO_MIN_TIMEOUT 4 + +static EFI_GUID tco_wdt_guid = EFI_TCO_RESET_PROTOCOL_GUID; +static EFI_HANDLE handle; + +static EFIAPI EFI_STATUS tco_wdt_enable (UINT32 *timeout) +{ + UINT16 val = 0; + UINT32 tmp = 0; + + if (NULL == timeout) + return EFI_INVALID_PARAMETER; + if (*timeout < TCO_MIN_TIMEOUT) + *timeout = TCO_MIN_TIMEOUT; + + /* Halt TCO */ + val = inw(TCO1_CNT); + val |= TCO_HALT_BIT; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + + /* Clear STS */ + outw(TCO_TIMEOUT1_BIT, TCO1_STS); + outw(TCO_TIMEOUT2_BIT, TCO2_STS); + + /* Set timeout */ + val = inw(TCOv2_TMR); + val = (val & 0xfc00) | (((*timeout * 10) / 6) & 0x3ff); + outw((val & 0x3ff), TCOv2_TMR); + val = inw(TCOv2_TMR); + + /* Clear NO_REBOOT bit */ + tmp = read32((void *)(UINTN)PMC_GCR_PMC_CFG_REG); + tmp &= ~ NO_REBOOT_BIT; + write32((void *)(UINTN)PMC_GCR_PMC_CFG_REG, tmp); + /*Read back and check */ + tmp = read32((void *)(UINTN)PMC_GCR_PMC_CFG_REG); + if (tmp & NO_REBOOT_BIT) + return EFI_DEVICE_ERROR; + + /* Reload */ + outw(0x1, TCO_RLD); + + /* Start TCO */ + val = inw(TCO1_CNT); + val &= ~ TCO_HALT_BIT; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + if (val & TCO_HALT_BIT) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS tco_wdt_disable (UINT32 timeout) +{ + UINT16 val = 0; + UINT32 tmp = timeout; + + /* Halt TCO */ + val = inw(TCO1_CNT); + val |= TCO_HALT_BIT; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + if (!(val & TCO_HALT_BIT)) + return EFI_DEVICE_ERROR; + + /* Set NO_REBOOT bit */ + tmp = read32((void *)(UINTN)PMC_GCR_PMC_CFG_REG); + tmp |= NO_REBOOT_BIT; + write32((void *)(UINTN)PMC_GCR_PMC_CFG_REG, tmp); + /*Read back and check */ + tmp = read32((void *)(UINTN)PMC_GCR_PMC_CFG_REG); + if (!(tmp & NO_REBOOT_BIT)) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} +static EFI_STATUS tco_wdt_init(EFI_SYSTEM_TABLE *st) +{ + static EFI_TCO_RESET_PROTOCOL tco_wdt_default = { + .EnableTcoReset = tco_wdt_enable, + .DisableTcoReset = tco_wdt_disable + }; + EFI_TCO_RESET_PROTOCOL *tco_wdt; + + return interface_init(st, &tco_wdt_guid, &handle, + &tco_wdt_default, sizeof(tco_wdt_default), + (void **)&tco_wdt); +} + +static EFI_STATUS tco_wdt_exit(EFI_SYSTEM_TABLE *st) +{ + return interface_free(st, &tco_wdt_guid, handle); +} + +ewdrv_t tco_wdt_drv = { + .name = "tco_wdt", + .description = "TCO watchdog Protocol", + .init = tco_wdt_init, + .exit = tco_wdt_exit +}; diff --git a/drivers/tco_wdt/tco_wdt.h b/drivers/tco_wdt/tco_wdt.h new file mode 100644 index 0000000..1c41bd9 --- /dev/null +++ b/drivers/tco_wdt/tco_wdt.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _TCO_WDT_H_ +#define _TCO_WDT_H_ + +#include + +extern ewdrv_t tco_wdt_drv; + +#endif diff --git a/drivers/ufs/Scsi.h b/drivers/ufs/Scsi.h new file mode 100644 index 0000000..86542e6 --- /dev/null +++ b/drivers/ufs/Scsi.h @@ -0,0 +1,356 @@ +/** @file + Support for SCSI-2 standard + + 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 __SCSI_H__ +#define __SCSI_H__ + +// +// SCSI command OP Code +// +// +// Commands for all device types +// +#define EFI_SCSI_OP_CHANGE_DEFINITION 0x40 +#define EFI_SCSI_OP_COMPARE 0x39 +#define EFI_SCSI_OP_COPY 0x18 +#define EFI_SCSI_OP_COPY_VERIFY 0x3a +#define EFI_SCSI_OP_INQUIRY 0x12 +#define EFI_SCSI_OP_LOG_SELECT 0x4c +#define EFI_SCSI_OP_LOG_SENSE 0x4d +#define EFI_SCSI_OP_MODE_SEL6 0x15 +#define EFI_SCSI_OP_MODE_SEL10 0x55 +#define EFI_SCSI_OP_MODE_SEN6 0x1a +#define EFI_SCSI_OP_MODE_SEN10 0x5a +#define EFI_SCSI_OP_READ_BUFFER 0x3c +#define EFI_SCSI_OP_RECEIVE_DIAG 0x1c +#define EFI_SCSI_OP_REQUEST_SENSE 0x03 +#define EFI_SCSI_OP_SEND_DIAG 0x1d +#define EFI_SCSI_OP_TEST_UNIT_READY 0x00 +#define EFI_SCSI_OP_WRITE_BUFF 0x3b + +// +// Additional commands for Direct Access Devices +// +#define EFI_SCSI_OP_FORMAT 0x04 +#define EFI_SCSI_OP_LOCK_UN_CACHE 0x36 +#define EFI_SCSI_OP_PREFETCH 0x34 +#define EFI_SCSI_OP_MEDIA_REMOVAL 0x1e +#define EFI_SCSI_OP_READ6 0x08 +#define EFI_SCSI_OP_READ10 0x28 +#define EFI_SCSI_OP_READ16 0x88 +#define EFI_SCSI_OP_READ_CAPACITY 0x25 +#define EFI_SCSI_OP_READ_CAPACITY16 0x9e +#define EFI_SCSI_OP_READ_DEFECT 0x37 +#define EFI_SCSI_OP_READ_LONG 0x3e +#define EFI_SCSI_OP_REASSIGN_BLK 0x07 +#define EFI_SCSI_OP_RELEASE 0x17 +#define EFI_SCSI_OP_REZERO 0x01 +#define EFI_SCSI_OP_SEARCH_DATA_E 0x31 +#define EFI_SCSI_OP_SEARCH_DATA_H 0x30 +#define EFI_SCSI_OP_SEARCH_DATA_L 0x32 +#define EFI_SCSI_OP_SEEK6 0x0b +#define EFI_SCSI_OP_SEEK10 0x2b +#define EFI_SCSI_OP_SEND_DIAG 0x1d +#define EFI_SCSI_OP_SET_LIMIT 0x33 +#define EFI_SCSI_OP_START_STOP_UNIT 0x1b +#define EFI_SCSI_OP_SYNC_CACHE 0x35 +#define EFI_SCSI_OP_VERIFY 0x2f +#define EFI_SCSI_OP_WRITE6 0x0a +#define EFI_SCSI_OP_WRITE10 0x2a +#define EFI_SCSI_OP_WRITE16 0x8a +#define EFI_SCSI_OP_WRITE_VERIFY 0x2e +#define EFI_SCSI_OP_WRITE_LONG 0x3f +#define EFI_SCSI_OP_WRITE_SAME 0x41 + +// +// Additional commands for Sequential Access Devices +// +#define EFI_SCSI_OP_ERASE 0x19 +#define EFI_SCSI_OP_LOAD_UNLOAD 0x1b +#define EFI_SCSI_OP_LOCATE 0x2b +#define EFI_SCSI_OP_READ_BLOCK_LIMIT 0x05 +#define EFI_SCSI_OP_READ_POS 0x34 +#define EFI_SCSI_OP_READ_REVERSE 0x0f +#define EFI_SCSI_OP_RECOVER_BUF_DATA 0x14 +#define EFI_SCSI_OP_RESERVE_UNIT 0x16 +#define EFI_SCSI_OP_REWIND 0x01 +#define EFI_SCSI_OP_SPACE 0x11 +#define EFI_SCSI_OP_VERIFY_TAPE 0x13 +#define EFI_SCSI_OP_WRITE_FILEMARK 0x10 + +// +// Additional commands for Printer Devices +// +#define EFI_SCSI_OP_PRINT 0x0a +#define EFI_SCSI_OP_SLEW_PRINT 0x0b +#define EFI_SCSI_OP_STOP_PRINT 0x1b +#define EFI_SCSI_OP_SYNC_BUFF 0x10 + +// +// Additional commands for Processor Devices +// +#define EFI_SCSI_OP_RECEIVE 0x08 +#define EFI_SCSI_OP_SEND 0x0a + +// +// Additional commands for Write-Once Devices +// +#define EFI_SCSI_OP_MEDIUM_SCAN 0x38 +#define EFI_SCSI_OP_SEARCH_DAT_E10 0x31 +#define EFI_SCSI_OP_SEARCH_DAT_E12 0xb1 +#define EFI_SCSI_OP_SEARCH_DAT_H10 0x30 +#define EFI_SCSI_OP_SEARCH_DAT_H12 0xb0 +#define EFI_SCSI_OP_SEARCH_DAT_L10 0x32 +#define EFI_SCSI_OP_SEARCH_DAT_L12 0xb2 +#define EFI_SCSI_OP_SET_LIMIT10 0x33 +#define EFI_SCSI_OP_SET_LIMIT12 0xb3 +#define EFI_SCSI_OP_VERIFY10 0x2f +#define EFI_SCSI_OP_VERIFY12 0xaf +#define EFI_SCSI_OP_WRITE12 0xaa +#define EFI_SCSI_OP_WRITE_VERIFY10 0x2e +#define EFI_SCSI_OP_WRITE_VERIFY12 0xae + +// +// Additional commands for CD-ROM Devices +// +#define EFI_SCSI_OP_PLAY_AUD_10 0x45 +#define EFI_SCSI_OP_PLAY_AUD_12 0xa5 +#define EFI_SCSI_OP_PLAY_AUD_MSF 0x47 +#define EFI_SCSI_OP_PLAY_AUD_TKIN 0x48 +#define EFI_SCSI_OP_PLAY_TK_REL10 0x49 +#define EFI_SCSI_OP_PLAY_TK_REL12 0xa9 +#define EFI_SCSI_OP_READ_CD_CAPACITY 0x25 +#define EFI_SCSI_OP_READ_HEADER 0x44 +#define EFI_SCSI_OP_READ_SUB_CHANNEL 0x42 +#define EFI_SCSI_OP_READ_TOC 0x43 + +// +// Additional commands for Scanner Devices +// +#define EFI_SCSI_OP_GET_DATABUFF_STAT 0x34 +#define EFI_SCSI_OP_GET_WINDOW 0x25 +#define EFI_SCSI_OP_OBJECT_POS 0x31 +#define EFI_SCSI_OP_SCAN 0x1b +#define EFI_SCSI_OP_SET_WINDOW 0x24 + +// +// Additional commands for Optical Memory Devices +// +#define EFI_SCSI_OP_UPDATE_BLOCK 0x3d + +// +// Additional commands for Medium Changer Devices +// +#define EFI_SCSI_OP_EXCHANGE_MEDIUM 0xa6 +#define EFI_SCSI_OP_INIT_ELEMENT_STAT 0x07 +#define EFI_SCSI_OP_POS_TO_ELEMENT 0x2b +#define EFI_SCSI_OP_REQUEST_VE_ADDR 0xb5 +#define EFI_SCSI_OP_SEND_VOL_TAG 0xb6 + +// +// Additional commands for Communition Devices +// +#define EFI_SCSI_OP_GET_MESSAGE6 0x08 +#define EFI_SCSI_OP_GET_MESSAGE10 0x28 +#define EFI_SCSI_OP_GET_MESSAGE12 0xa8 +#define EFI_SCSI_OP_SEND_MESSAGE6 0x0a +#define EFI_SCSI_OP_SEND_MESSAGE10 0x2a +#define EFI_SCSI_OP_SEND_MESSAGE12 0xaa + +// +// SCSI Data Transfer Direction +// +#define EFI_SCSI_DATA_IN 0 +#define EFI_SCSI_DATA_OUT 1 + +// +// Peripheral Device Type Definitions +// +#define EFI_SCSI_TYPE_DISK 0x00 ///< Direct-access device (e.g. magnetic disk) +#define EFI_SCSI_TYPE_TAPE 0x01 ///< Sequential-access device (e.g. magnetic tape) +#define EFI_SCSI_TYPE_PRINTER 0x02 ///< Printer device +#define EFI_SCSI_TYPE_PROCESSOR 0x03 ///< Processor device +#define EFI_SCSI_TYPE_WORM 0x04 ///< Write-once device (e.g. some optical disks) +#define EFI_SCSI_TYPE_CDROM 0x05 ///< CD-ROM device +#define EFI_SCSI_TYPE_SCANNER 0x06 ///< Scanner device +#define EFI_SCSI_TYPE_OPTICAL 0x07 ///< Optical memory device (e.g. some optical disks) +#define EFI_SCSI_TYPE_MEDIUMCHANGER 0x08 ///< Medium changer device (e.g. jukeboxes) +#define EFI_SCSI_TYPE_COMMUNICATION 0x09 ///< Communications device +#define EFI_SCSI_TYPE_ASCIT8_1 0x0A ///< Defined by ASC IT8 (Graphic arts pre-press devices) +#define EFI_SCSI_TYPE_ASCIT8_2 0x0B ///< Defined by ASC IT8 (Graphic arts pre-press devices) +// +// 0Ch - 1Eh are reserved +// +#define EFI_SCSI_TYPE_UNKNOWN 0x1F ///< Unknown or no device type + +// +// Page Codes for INQUIRY command +// +#define EFI_SCSI_PAGE_CODE_SUPPORTED_VPD 0x00 +#define EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD 0xB0 + +#pragma pack(1) +/// +/// Standard INQUIRY data format +/// +typedef struct { + UINT8 Peripheral_Type : 5; + UINT8 Peripheral_Qualifier : 3; + UINT8 DeviceType_Modifier : 7; + UINT8 Rmb : 1; + UINT8 Version; + UINT8 Response_Data_Format; + UINT8 Addnl_Length; + UINT8 Reserved_5_95[95 - 5 + 1]; +} EFI_SCSI_INQUIRY_DATA; + +/// +/// Supported VPD Pages VPD page +/// +typedef struct { + UINT8 Peripheral_Type : 5; + UINT8 Peripheral_Qualifier : 3; + UINT8 PageCode; + UINT8 PageLength2; + UINT8 PageLength1; + UINT8 SupportedVpdPageList[0x100]; +} EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE; + +/// +/// Block Limits VPD page +/// +typedef struct { + UINT8 Peripheral_Type : 5; + UINT8 Peripheral_Qualifier : 3; + UINT8 PageCode; + UINT8 Reserved_2; + UINT8 PageLength; + UINT8 Reserved_4_5[2]; + UINT8 OptimalTransferLengthGranularity2; + UINT8 OptimalTransferLengthGranularity1; + UINT8 MaximumTransferLength4; + UINT8 MaximumTransferLength3; + UINT8 MaximumTransferLength2; + UINT8 MaximumTransferLength1; + UINT8 OptimalTransferLength4; + UINT8 OptimalTransferLength3; + UINT8 OptimalTransferLength2; + UINT8 OptimalTransferLength1; + UINT8 MaximumPrefetchXdreadXdwriteTransferLength4; + UINT8 MaximumPrefetchXdreadXdwriteTransferLength3; + UINT8 MaximumPrefetchXdreadXdwriteTransferLength2; + UINT8 MaximumPrefetchXdreadXdwriteTransferLength1; +} EFI_SCSI_BLOCK_LIMITS_VPD_PAGE; + +/// +/// Error codes 70h and 71h sense data format +/// +typedef struct { + UINT8 Error_Code : 7; + UINT8 Valid : 1; + UINT8 Segment_Number; + UINT8 Sense_Key : 4; + UINT8 Reserved_21 : 1; + UINT8 Ili : 1; + UINT8 Reserved_22 : 2; + UINT8 Information_3_6[4]; + UINT8 Addnl_Sense_Length; ///< Additional sense length (n-7) + UINT8 Vendor_Specific_8_11[4]; + UINT8 Addnl_Sense_Code; ///< Additional sense code + UINT8 Addnl_Sense_Code_Qualifier; ///< Additional sense code qualifier + UINT8 Field_Replaceable_Unit_Code; ///< Field replaceable unit code + UINT8 Reserved_15_17[3]; +} EFI_SCSI_SENSE_DATA; + +/// +/// SCSI Disk READ CAPACITY Data +/// +typedef struct { + UINT8 LastLba3; + UINT8 LastLba2; + UINT8 LastLba1; + UINT8 LastLba0; + UINT8 BlockSize3; + UINT8 BlockSize2; + UINT8 BlockSize1; + UINT8 BlockSize0; +} EFI_SCSI_DISK_CAPACITY_DATA; + +typedef struct { + UINT8 LastLba7; + UINT8 LastLba6; + UINT8 LastLba5; + UINT8 LastLba4; + UINT8 LastLba3; + UINT8 LastLba2; + UINT8 LastLba1; + UINT8 LastLba0; + UINT8 BlockSize3; + UINT8 BlockSize2; + UINT8 BlockSize1; + UINT8 BlockSize0; + UINT8 Protection; + UINT8 LogicPerPhysical; + UINT8 LowestAlignLogic2; + UINT8 LowestAlignLogic1; + UINT8 Reserved[16]; +} EFI_SCSI_DISK_CAPACITY_DATA16; + + +#pragma pack() + +// +// Sense Key +// +#define EFI_SCSI_SK_NO_SENSE (0x0) +#define EFI_SCSI_SK_RECOVERY_ERROR (0x1) +#define EFI_SCSI_SK_NOT_READY (0x2) +#define EFI_SCSI_SK_MEDIUM_ERROR (0x3) +#define EFI_SCSI_SK_HARDWARE_ERROR (0x4) +#define EFI_SCSI_SK_ILLEGAL_REQUEST (0x5) +#define EFI_SCSI_SK_UNIT_ATTENTION (0x6) +#define EFI_SCSI_SK_DATA_PROTECT (0x7) +#define EFI_SCSI_SK_BLANK_CHECK (0x8) +#define EFI_SCSI_SK_VENDOR_SPECIFIC (0x9) +#define EFI_SCSI_SK_RESERVED_A (0xA) +#define EFI_SCSI_SK_ABORT (0xB) +#define EFI_SCSI_SK_RESERVED_C (0xC) +#define EFI_SCSI_SK_OVERFLOW (0xD) +#define EFI_SCSI_SK_MISCOMPARE (0xE) +#define EFI_SCSI_SK_RESERVED_F (0xF) + +// +// Additional Sense Codes and Sense Code Qualifiers. +// Only some frequently used additional sense codes and qualifiers are +// defined here. Please refer to SCSI standard for full value definition. +// +#define EFI_SCSI_ASC_NOT_READY (0x04) +#define EFI_SCSI_ASCQ_IN_PROGRESS (0x01) + +#define EFI_SCSI_ASC_MEDIA_ERR1 (0x10) +#define EFI_SCSI_ASC_MEDIA_ERR2 (0x11) +#define EFI_SCSI_ASC_MEDIA_ERR3 (0x14) +#define EFI_SCSI_ASC_MEDIA_ERR4 (0x30) +#define EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN (0x06) +#define EFI_SCSI_ASC_INVALID_CMD (0x20) +#define EFI_SCSI_ASC_LBA_OUT_OF_RANGE (0x21) +#define EFI_SCSI_ASC_INVALID_FIELD (0x24) +#define EFI_SCSI_ASC_WRITE_PROTECTED (0x27) +#define EFI_SCSI_ASC_MEDIA_CHANGE (0x28) +#define EFI_SCSI_ASC_RESET (0x29) ///< Power On Reset or Bus Reset occurred +#define EFI_SCSI_ASC_ILLEGAL_FIELD (0x26) +#define EFI_SCSI_ASC_NO_MEDIA (0x3A) +#define EFI_SCSI_ASC_ILLEGAL_MODE_FOR_THIS_TRACK (0x64) + +#endif diff --git a/drivers/ufs/ScsiPassThruExt.h b/drivers/ufs/ScsiPassThruExt.h new file mode 100644 index 0000000..06ea6fe --- /dev/null +++ b/drivers/ufs/ScsiPassThruExt.h @@ -0,0 +1,394 @@ +/** @file + EFI_EXT_SCSI_PASS_THRU_PROTOCOL as defined in UEFI 2.0. + This protocol provides services that allow SCSI Pass Thru commands + to be sent to SCSI devices attached to a SCSI channel. + + Copyright (c) 2006 - 2008, 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 __EXT_SCSI_PASS_THROUGH_PROTOCOL_H__ +#define __EXT_SCSI_PASS_THROUGH_PROTOCOL_H__ + +#include + +#define EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x143b7632, 0xb81b, 0x4cb7, {0xab, 0xd3, 0xb6, 0x25, 0xa5, 0xb9, 0xbf, 0xfe } \ + } + +typedef struct _EFI_EXT_SCSI_PASS_THRU_PROTOCOL EFI_EXT_SCSI_PASS_THRU_PROTOCOL; + +#define TARGET_MAX_BYTES 0x10 + +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001 +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002 +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004 + +// +// DataDirection +// +#define EFI_EXT_SCSI_DATA_DIRECTION_READ 0 +#define EFI_EXT_SCSI_DATA_DIRECTION_WRITE 1 +#define EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL 2 +// +// HostAdapterStatus +// +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK 0x00 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND 0x09 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT 0x0b +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT 0x0d +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET 0x0e +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR 0x0f +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED 0x10 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT 0x11 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN 0x12 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE 0x13 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR 0x14 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER 0x7f +// +// TargetStatus +// +#define EFI_EXT_SCSI_STATUS_TARGET_GOOD 0x00 +#define EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION 0x02 +#define EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET 0x04 +#define EFI_EXT_SCSI_STATUS_TARGET_BUSY 0x08 +#define EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE 0x10 +#define EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET 0x14 +#define EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT 0x18 +#define EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL 0x28 +#define EFI_EXT_SCSI_STATUS_TARGET_ACA_ACTIVE 0x30 +#define EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED 0x40 + +typedef struct { + /// + /// The Target ID of the host adapter on the SCSI channel. + /// + UINT32 AdapterId; + /// + /// Additional information on the attributes of the SCSI channel. + /// + UINT32 Attributes; + /// + /// Supplies the alignment requirement for any buffer used in a data transfer. + /// + UINT32 IoAlign; +} EFI_EXT_SCSI_PASS_THRU_MODE; + +typedef struct { + /// + /// The timeout, in 100 ns units, to use for the execution of this SCSI + /// Request Packet. A Timeout value of 0 means that this function + /// will wait indefinitely for the SCSI Request Packet to execute. If + /// Timeout is greater than zero, then this function will return + /// EFI_TIMEOUT if the time required to execute the SCSI + /// Request Packet is greater than Timeout. + /// + UINT64 Timeout; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for read and bidirectional commands. + /// + VOID *InDataBuffer; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for write or bidirectional commands. + /// + VOID *OutDataBuffer; + /// + /// A pointer to the sense data that was generated by the execution of + /// the SCSI Request Packet. + /// + VOID *SenseData; + /// + /// A pointer to buffer that contains the Command Data Block to + /// send to the SCSI device specified by Target and Lun. + /// + VOID *Cdb; + /// + /// On Input, the size, in bytes, of InDataBuffer. On output, the + /// number of bytes transferred between the SCSI controller and the SCSI device. + /// + UINT32 InTransferLength; + /// + /// On Input, the size, in bytes of OutDataBuffer. On Output, the + /// Number of bytes transferred between SCSI Controller and the SCSI device. + /// + UINT32 OutTransferLength; + /// + /// The length, in bytes, of the buffer Cdb. The standard values are 6, + /// 10, 12, and 16, but other values are possible if a variable length CDB is used. + /// + UINT8 CdbLength; + /// + /// The direction of the data transfer. 0 for reads, 1 for writes. A + /// value of 2 is Reserved for Bi-Directional SCSI commands. + /// + UINT8 DataDirection; + /// + /// The status of the host adapter specified by This when the SCSI + /// Request Packet was executed on the target device. + /// + UINT8 HostAdapterStatus; + /// + /// The status returned by the device specified by Target and Lun + /// when the SCSI Request Packet was executed. + /// + UINT8 TargetStatus; + /// + /// On input, the length in bytes of the SenseData buffer. On + /// output, the number of bytes written to the SenseData buffer. + /// + UINT8 SenseDataLength; +} EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET; + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function + supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the + nonblocking I/O functionality is optional. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it represents + the id of the SCSI device to send the SCSI Request Packet. Each + transport driver may choose to utilize a subset of this size to suit the needs + of transport target representation. For example, a Fibre Channel driver + may use only 8 bytes (WWN) to represent an FC target. + @param Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param Packet A pointer to the SCSI Request Packet to send to the SCSI device + specified by Target and Lun. + @param Event If nonblocking I/O is not supported then Event is ignored, and blocking + I/O is performed. If Event is NULL, then blocking I/O is performed. If + Event is not NULL and non blocking I/O is supported, then + nonblocking I/O is performed, and Event will be signaled when the + SCSI Request Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that + could be transferred is returned in InTransferLength. For write + and bi-directional commands, OutTransferLength bytes were + transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many + SCSI Request Packets already queued. The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported + by the host adapter. This includes the case of Bi-directional SCSI + commands not supported by the implementation. The SCSI Request + Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_PASSTHRU)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These + can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal + Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the + Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI + channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target On input, a pointer to the Target ID (an array of size + TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI + channel. On output, a pointer to the LUN of the next SCSI device present + on a SCSI channel. + + @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI + channel was returned in Target and Lun. + @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were + not returned on a previous call to GetNextTargetLun(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ); + +/** + Used to allocate and build a device path node for a SCSI device on a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the + Target ID of the SCSI device for which a device path node is to be + allocated and built. Transport drivers may chose to utilize a subset of + this size to suit the representation of targets. For example, a Fibre + Channel driver may use only 8 bytes (WWN) in the array to represent a + FC target. + @param Lun The LUN of the SCSI device for which a device path node is to be + allocated and built. + @param DevicePath A pointer to a single device path node that describes the SCSI device + specified by Target and Lun. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI device specified by + Target and Lun was allocated and returned in + DevicePath. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist + on the SCSI channel. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_BUILD_DEVICE_PATH)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH **DevicePath + ); + +/** + Used to translate a device path node to a Target ID and LUN. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param DevicePath A pointer to a single device path node that describes the SCSI device + on the SCSI channel. + @param Target A pointer to the Target Array which represents the ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of a SCSI device on the SCSI channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and + LUN, and they were returned in Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN + does not exist. + @retval EFI_UNSUPPORTED This driver does not support the device path node type in + DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +/** + Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + + @retval EFI_SUCCESS The SCSI channel was reset. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel. + @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_RESET_CHANNEL)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +/** + Resets a SCSI logical unit that is connected to a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the + target port ID of the SCSI device containing the SCSI logical unit to + reset. Transport drivers may chose to utilize a subset of this array to suit + the representation of their targets. + @param Lun The LUN of the SCSI device to reset. + + @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device + specified by Target and Lun. + @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device + specified by Target and Lun. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_RESET_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +/** + Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either + be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs + for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to + see if a SCSI device is actually present at that location on the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ); + +/// +/// The EFI_EXT_SCSI_PASS_THRU_PROTOCOL provides information about a SCSI channel +/// and the ability to send SCI Request Packets to any SCSI device attached to +/// that SCSI channel. The information includes the Target ID of the host controller +/// on the SCSI channel and the attributes of the SCSI channel. +/// +struct _EFI_EXT_SCSI_PASS_THRU_PROTOCOL { + /// + /// A pointer to the EFI_EXT_SCSI_PASS_THRU_MODE data for this SCSI channel. + /// + EFI_EXT_SCSI_PASS_THRU_MODE *Mode; + EFI_EXT_SCSI_PASS_THRU_PASSTHRU PassThru; + EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET_LUN GetNextTargetLun; + EFI_EXT_SCSI_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_EXT_SCSI_PASS_THRU_GET_TARGET_LUN GetTargetLun; + EFI_EXT_SCSI_PASS_THRU_RESET_CHANNEL ResetChannel; + EFI_EXT_SCSI_PASS_THRU_RESET_TARGET_LUN ResetTargetLun; + EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET GetNextTarget; +}; + +#endif diff --git a/drivers/ufs/UfsBlockIoLib.c b/drivers/ufs/UfsBlockIoLib.c new file mode 100644 index 0000000..c631746 --- /dev/null +++ b/drivers/ufs/UfsBlockIoLib.c @@ -0,0 +1,1674 @@ +/** @file + + Copyright (c) 2014 - 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 + 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. + +**/ + +#include "UfsInternal.h" +#include "UfsBlockIoLib.h" + +#define MSG_UFS_DP 0x19 +#define EFI_EXT_SCSI_DATA_DIRECTION_READ 0 +#define EFI_EXT_SCSI_DATA_DIRECTION_WRITE 1 +#define EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL 2 + +// +// Template for UFS HC Peim Private Data. +// + +UFS_PEIM_HC_PRIVATE_DATA gUfsHcTemplate = { + UFS_PEIM_HC_SIG, // Signature + NULL, // Controller + NULL, //SCSI Protocol + NULL, // Pool + { // Media + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + }, + { + MSG_UFS_DP, + FALSE, + TRUE, + FALSE, + 0x1000, + 0 + } + }, + 0, // UfsHcBase + 0, // Capabilities + 0, // TaskTag + 0, // UtpTrlBase + 0, // Nutrs + 0, // UtpTmrlBase + 0, // Nutmrs + { // Luns + { + UFS_LUN_0, // Ufs Common Lun 0 + UFS_LUN_1, // Ufs Common Lun 1 + UFS_LUN_2, // Ufs Common Lun 2 + UFS_LUN_3, // Ufs Common Lun 3 + UFS_LUN_4, // Ufs Common Lun 4 + UFS_LUN_5, // Ufs Common Lun 5 + UFS_LUN_6, // Ufs Common Lun 6 + UFS_LUN_7, // Ufs Common Lun 7 + }, + 0x0000, // By default exposing all Luns. + 0x0 + }, + 0, + 0 +}; + +UFS_PEIM_HC_PRIVATE_DATA *gPrivate; + +UINT16 +EFIAPI +SwapBytes16( + IN UINT16 Value + ) +{ + return (UINT16)((Value << 8) | (Value >> 8)); +} + +UINT32 +EFIAPI +SwapBytes32( + IN UINT32 Value + ) +{ + UINT32 LowerBytes; + UINT32 HigherBytes; + + LowerBytes = (UINT32)SwapBytes16((UINT16)Value); + HigherBytes = (UINT32)SwapBytes16((UINT16)(Value >> 16)); + + return (LowerBytes << 16 | HigherBytes); +} + +UINT64 +EFIAPI +SwapBytes64( + IN UINT64 Value + ) +{ + UINT64 LowerBytes; + UINT64 HigherBytes; + LowerBytes = (UINT64)SwapBytes32((UINT32)Value); + HigherBytes = (UINT64)SwapBytes32((UINT32)(Value >> 32)); + + return (LowerBytes << 32 | HigherBytes); +} + +UINT16 +EFIAPI +WriteUnaligned16 ( + OUT UINT16 *Buffer, + IN UINT16 Value + ) +{ + ASSERT(Buffer != NULL); + + return *Buffer = Value; +} + +UINT32 +EFIAPI +WriteUnaligned32( + OUT UINT32 *Buffer, + IN UINT32 Value + ) +{ + ASSERT(Buffer != NULL); + + return *Buffer = Value; +} + + +UINT64 +EFIAPI +WriteUnaligned64( + OUT UINT64 *Buffer, + IN UINT64 Value + ) +{ + ASSERT(Buffer != NULL); + + return *Buffer = Value; +} + +/** +Returns UFS PEIM Private Data. +**/ +UFS_PEIM_HC_PRIVATE_DATA * +UfsGetPrivateData( + VOID + ) +{ + return gPrivate; +} + + +/** + Execute Request Sense SCSI command on a specific UFS device. + + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] DataBuffer A pointer to output sense data. + @param[out] DataBufferLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsRequestSense( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *DataBuffer, + OUT UINT32 *DataBufferLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof(UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof(Cdb)); + + Cdb[0] = EFI_SCSI_OP_REQUEST_SENSE; + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.DataDirection = UfsDataIn; + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataBufferLength; + Packet.SenseData = NULL; + Packet.SenseDataLength = 0; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (!EFI_ERROR(Status)) { + *DataBufferLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute TEST UNITY READY SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsTestUnitReady ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof(UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof(Cdb)); + + Cdb[0] = EFI_SCSI_OP_TEST_UNIT_READY; + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.DataDirection = UfsNoData; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + return Status; +} + +/** + Execute INQUIRY SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] Inquiry A pointer to Inquiry data buffer. + @param[out] InquiryLengths The length of output Inquiry data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsInquiry ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *Inquiry, + OUT UINT32 *InquiryLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof(UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof(Cdb)); + + Cdb[0] = EFI_SCSI_OP_INQUIRY; + Cdb[4] = sizeof(EFI_SCSI_INQUIRY_DATA); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof(Cdb); + Packet.InDataBuffer = Inquiry; + Packet.InTransferLength = *InquiryLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR(Status)) { + *InquiryLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ CAPACITY(10) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] DataBuffer A pointer to READ_CAPACITY data buffer. + @param[out] DataLength The length of output READ_CAPACITY data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsReadCapacity( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof(UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof(Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ_CAPACITY; + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof(Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR(Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ CAPACITY(16) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] DataBuffer A pointer to READ_CAPACITY data buffer. + @param[out] DataLength The length of output READ_CAPACITY data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsReadCapacity16( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof(UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof(Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ_CAPACITY16; + Cdb[1] = 0x10; // Service Action should be 0x10 for UFS device. + Cdb[13] = 0x20; // The maximum number of bytes for returned data. + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof(Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR(Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ (10) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[in] StartLba The start LBA. + @param[in] SectorNum The sector number to be read. + @param[out] DataBuffer A pointer to data buffer. + @param[out] DataLength The length of output data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsRead10 ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + IN UINTN StartLba, + IN UINT32 SectorNum, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ10; + WriteUnaligned32((UINT32 *)&Cdb[2], SwapBytes32((UINT32)StartLba)); + WriteUnaligned16((UINT16 *)&Cdb[7], SwapBytes16((UINT16)SectorNum)); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof(Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR(Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ (16) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[in] StartLba The start LBA. + @param[in] SectorNum The sector number to be read. + @param[out] DataBuffer A pointer to data buffer. + @param[out] DataLength The length of output data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsRead16( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + IN UINTN StartLba, + IN UINT32 SectorNum, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof(UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof(Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ16; + WriteUnaligned64((UINT64 *)&Cdb[2], SwapBytes64(StartLba)); + WriteUnaligned32((UINT32 *)&Cdb[10], SwapBytes32(SectorNum)); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof(Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR(Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Parsing Sense Keys from sense data. + + @param Media The pointer of EFI_PEI_BLOCK_IO2_MEDIA + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param NeedRetry The pointer of action which indicates what is need to retry + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to complete the parsing + +**/ +EFI_STATUS +UfsParsingSenseKeys( + IN EFI_PEI_BLOCK_IO2_MEDIA *Media, + IN EFI_SCSI_SENSE_DATA *SenseData, + OUT BOOLEAN *NeedRetry + ) +{ + if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) { + Media->MediaPresent = FALSE; + *NeedRetry = FALSE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Is No Media\n")); + return EFI_DEVICE_ERROR; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) { + *NeedRetry = TRUE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Is Media Change\n")); + return EFI_SUCCESS; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) { + *NeedRetry = TRUE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n")); + return EFI_SUCCESS; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_MEDIUM_ERROR) || + ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN))) { + *NeedRetry = FALSE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Media Error\n")); + return EFI_DEVICE_ERROR; + } + + if (SenseData->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) { + *NeedRetry = FALSE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Hardware Error\n")); + return EFI_DEVICE_ERROR; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NOT_READY) && + (SenseData->Addnl_Sense_Code_Qualifier == EFI_SCSI_ASCQ_IN_PROGRESS)) { + *NeedRetry = TRUE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n")); + return EFI_SUCCESS; + } + + *NeedRetry = FALSE; + DEBUG_UFS((EFI_D_VERBOSE, "UfsBlockIoPei: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code)); + return EFI_DEVICE_ERROR; +} + + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +UfsGetDeviceNo( + OUT UINTN *NumberBlockDevices + ) +{ + // + // For Ufs device, it has up to 8 normal Luns plus some well-known Luns. + // At PEI phase, we will only expose normal Luns to user. + // For those disabled Lun, when user try to access it, the operation would fail. + // + *NumberBlockDevices = UFS_PEIM_MAX_LUNS; + return EFI_SUCCESS; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +UfsGetMediaInfoInternal( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO2_MEDIA **MediaInfo + ) +{ + EFI_STATUS Status; + EFI_SCSI_SENSE_DATA SenseData; + UINT8 SenseDataLength; + EFI_SCSI_DISK_CAPACITY_DATA Capacity; + EFI_SCSI_DISK_CAPACITY_DATA16 Capacity16; + UINTN DataLength; + BOOLEAN NeedRetry; + + NeedRetry = TRUE; + + if (DeviceIndex >= UFS_PEIM_MAX_LUNS) { + return EFI_INVALID_PARAMETER; + } + + if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) { + return EFI_ACCESS_DENIED; + } + + ZeroMem(&SenseData, sizeof(SenseData)); + ZeroMem (&Capacity16, sizeof(Capacity16)); + SenseDataLength = sizeof(SenseData); + // + // First test unit ready + // + do { + Status = UfsTestUnitReady( + Private, + DeviceIndex, + &SenseData, + &SenseDataLength + ); + if (!EFI_ERROR(Status)) { + break; + } + + if (SenseDataLength == 0) { + continue; + } + + Status = UfsParsingSenseKeys(&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + } while(NeedRetry); + + DataLength = sizeof(EFI_SCSI_DISK_CAPACITY_DATA); + SenseDataLength = 0; + Status = UfsReadCapacity(Private, DeviceIndex, &Capacity, (UINT32 *)&DataLength, NULL, &SenseDataLength); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + if ((Capacity.LastLba3 == 0xff) && (Capacity.LastLba2 == 0xff) && + (Capacity.LastLba1 == 0xff) && (Capacity.LastLba0 == 0xff)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsReadCapacity16\n")); + DataLength = sizeof(EFI_SCSI_DISK_CAPACITY_DATA16); + SenseDataLength = 0; + Status = UfsReadCapacity16(Private, DeviceIndex, &Capacity16, (UINT32 *)&DataLength, NULL, &SenseDataLength); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + Private->Media[DeviceIndex].LastBlock = ((UINT32)Capacity16.LastLba3 << 24) | (Capacity16.LastLba2 << 16) | (Capacity16.LastLba1 << 8) | Capacity16.LastLba0; + Private->Media[DeviceIndex].LastBlock |= LShiftU64((UINT64)Capacity16.LastLba7, 56) | LShiftU64((UINT64)Capacity16.LastLba6, 48) | LShiftU64((UINT64)Capacity16.LastLba5, 40) | LShiftU64((UINT64)Capacity16.LastLba4, 32); + Private->Media[DeviceIndex].BlockSize = (Capacity16.BlockSize3 << 24) | (Capacity16.BlockSize2 << 16) | (Capacity16.BlockSize1 << 8) | Capacity16.BlockSize0; + } else { + DEBUG_UFS((EFI_D_VERBOSE, "UfsReadCapacity10\n")); + Private->Media[DeviceIndex].LastBlock = ((UINT32)Capacity.LastLba3 << 24) | (Capacity.LastLba2 << 16) | (Capacity.LastLba1 << 8) | Capacity.LastLba0; + Private->Media[DeviceIndex].BlockSize = (Capacity.BlockSize3 << 24) | (Capacity.BlockSize2 << 16) | (Capacity.BlockSize1 << 8) | Capacity.BlockSize0; + } + + *MediaInfo = &Private->Media[DeviceIndex]; + return EFI_SUCCESS; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +UfsReadBlocksInternal( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + UFS_PEIM_HC_PRIVATE_DATA *Private; + EFI_SCSI_SENSE_DATA SenseData; + UINT8 SenseDataLength; + BOOLEAN NeedRetry; + + DEBUG_UFS((EFI_D_VERBOSE, "UfsReadBlocks. DeviceIndex = %x\n", DeviceIndex)); + + Private = UfsGetPrivateData(); + if (Private == NULL) { + return EFI_NOT_FOUND; + } + + Status = EFI_SUCCESS; + NeedRetry = TRUE; + + ZeroMem(&SenseData, sizeof(SenseData)); + SenseDataLength = sizeof(SenseData); + + // + // Check parameters + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if (DeviceIndex >= UFS_PEIM_MAX_LUNS) { + return EFI_INVALID_PARAMETER; + } + + if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) { + return EFI_ACCESS_DENIED; + } + + BlockSize = Private->Media[DeviceIndex].BlockSize; + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + } + + if (StartLBA > Private->Media[DeviceIndex].LastBlock) { + Status = EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / BlockSize; + + do { + Status = UfsTestUnitReady( + Private, + DeviceIndex, + &SenseData, + &SenseDataLength + ); + if (!EFI_ERROR(Status)) { + break; + } + + if (SenseDataLength == 0) { + continue; + } + + Status = UfsParsingSenseKeys(&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + } while (NeedRetry); + + SenseDataLength = 0; + if (Private->Media[DeviceIndex].LastBlock < 0xfffffffful) { + Status = UfsRead10( + Private, + DeviceIndex, + (UINT32)StartLBA, + (UINT32)NumberOfBlocks, + Buffer, + (UINT32 *)&BufferSize, + NULL, + &SenseDataLength + ); + } else { + Status = UfsRead16( + Private, + DeviceIndex, + (UINT32)StartLBA, + (UINT32)NumberOfBlocks, + Buffer, + (UINT32 *)&BufferSize, + NULL, + &SenseDataLength + ); + } + return Status; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +UfsReadBlocks( + IN UINTN DeviceIndex, + IN EFI_LBA StartLba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT32 ReadSize; + UINT32 ReadBlockSize; + EFI_LBA LbaAddress; + + Status = EFI_SUCCESS; + ReadSize = 0; + LbaAddress = StartLba; + while (ReadSize < BufferSize) { + if (ReadSize + SIZE_64KB > BufferSize) { + ReadBlockSize = BufferSize - ReadSize; + } else { + ReadBlockSize = SIZE_64KB; + } + + Status = UfsReadBlocksInternal(DeviceIndex, LbaAddress, ReadBlockSize, (UINT8 *)Buffer + ReadSize); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsReadBlocks_internal: Status = %d\n", Status)); + DEBUG_UFS((EFI_D_VERBOSE, "EFI_TIMEOUT = %d\n", EFI_TIMEOUT)); + break; + } + ReadSize += ReadBlockSize; + LbaAddress += ReadBlockSize / SIZE_4KB; + } + + return Status; +} + +/** + Execute WRITE (10) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[in] StartLba The start LBA. + @param[in] SectorNum The sector number to be written. + @param[out] DataBuffer A pointer to data buffer. + @param[out] DataLength The length of output data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsWrite10( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + IN UINTN StartLba, + IN UINT32 SectorNum, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength +) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_WRITE10; + WriteUnaligned32((UINT32 *)&Cdb[2], SwapBytes32((UINT32)StartLba)); + WriteUnaligned16((UINT16 *)&Cdb[7], SwapBytes16((UINT16)SectorNum)); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.OutDataBuffer = DataBuffer; + Packet.OutTransferLength = *DataLength; + Packet.DataDirection = UfsDataOut; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) + *SenseDataLength = Packet.SenseDataLength; + + if (!EFI_ERROR(Status)) + *DataLength = Packet.OutTransferLength; + + return Status; +} + +/** + Execute WRITE (16) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[in] StartLba The start LBA. + @param[in] SectorNum The sector number to be written. + @param[out] DataBuffer A pointer to data buffer. + @param[out] DataLength The length of output data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsWrite16( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + IN UINTN StartLba, + IN UINT32 SectorNum, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength +) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN]; + EFI_STATUS Status; + + ZeroMem(&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem(Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_WRITE16; + WriteUnaligned64((UINT64 *)&Cdb[2], SwapBytes64(StartLba)); + WriteUnaligned32((UINT32 *)&Cdb[10], SwapBytes32(SectorNum)); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.OutDataBuffer = DataBuffer; + Packet.OutTransferLength = *DataLength; + Packet.DataDirection = UfsDataOut; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds(Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) + *SenseDataLength = Packet.SenseDataLength; + + if (!EFI_ERROR(Status)) + *DataLength = Packet.OutTransferLength; + + return Status; +} + +/** + This function writes data from Memory to UFS + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartBlock Target UFS block number(LBA) where data will be written + @param[in] DataSize Total data size to be written in bytes unit + @param[out] DataAddress Data address in Memory to be copied to UFS + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +UfsWriteBlocks( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer +) +{ + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + UFS_PEIM_HC_PRIVATE_DATA *Private; + EFI_SCSI_SENSE_DATA SenseData; + UINT8 SenseDataLength; + BOOLEAN NeedRetry; + + DEBUG_UFS((EFI_D_VERBOSE, "UfsWriteBlocks. DeviceIndex = %x\n", DeviceIndex)); + + Private = UfsGetPrivateData(); + if (Private == NULL) + return EFI_NOT_FOUND; + + Status = EFI_SUCCESS; + NeedRetry = TRUE; + + ZeroMem(&SenseData, sizeof (SenseData)); + SenseDataLength = sizeof (SenseData); + + // + // Check parameters + // + if (Buffer == NULL) + return EFI_INVALID_PARAMETER; + + if (BufferSize == 0) + return EFI_SUCCESS; + + if (DeviceIndex >= UFS_PEIM_MAX_LUNS) + return EFI_INVALID_PARAMETER; + + if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) + return EFI_ACCESS_DENIED; + + BlockSize = Private->Media[DeviceIndex].BlockSize; + + if (BufferSize % BlockSize != 0) + Status = EFI_BAD_BUFFER_SIZE; + + if (StartLBA > Private->Media[DeviceIndex].LastBlock) + Status = EFI_INVALID_PARAMETER; + + NumberOfBlocks = BufferSize / BlockSize; + + do { + Status = UfsTestUnitReady( + Private, + DeviceIndex, + &SenseData, + &SenseDataLength + ); + if (!EFI_ERROR(Status)) + break; + + if (SenseDataLength == 0) + continue; + + Status = UfsParsingSenseKeys(&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry); + if (EFI_ERROR(Status)) + return EFI_DEVICE_ERROR; + + } while (NeedRetry); + + SenseDataLength = 0; + if (Private->Media[DeviceIndex].LastBlock < 0xfffffffful) + Status = UfsWrite10( + Private, + DeviceIndex, + (UINT32)StartLBA, + (UINT32)NumberOfBlocks, + Buffer, + (UINT32 *)&BufferSize, + NULL, + &SenseDataLength + ); + else + Status = UfsWrite16( + Private, + DeviceIndex, + (UINT32)StartLBA, + (UINT32)NumberOfBlocks, + Buffer, + (UINT32 *)&BufferSize, + NULL, + &SenseDataLength + ); + + return Status; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[out] DevBlockInfo The Block Io information of the specified block partition. + + @retval EFI_SUCCESS The Block Io information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +UfsGetMediaInfo( + IN UINTN DeviceIndex, + OUT DEVICE_BLOCK_INFO *DevBlockInfo + ) +{ + EFI_STATUS Status; + UFS_PEIM_HC_PRIVATE_DATA *Private; + EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo; + + if (DevBlockInfo == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = UfsGetPrivateData(); + if (Private == NULL) { + return EFI_NOT_FOUND; + } + + Status = UfsGetMediaInfoInternal( + Private, + DeviceIndex, + &MediaInfo + ); + if (EFI_ERROR(Status)) { + return Status; + } + + DevBlockInfo->BlockSize = MediaInfo->BlockSize; + DevBlockInfo->BlockNum = MediaInfo->LastBlock + 1; + + return EFI_SUCCESS; +} + +/** + The function will initialize UFS device. + + Based on UfsHcPciBase, this function will initialize UFS host controller, allocate + necessary resources, and enumarate all the LUNs. + + @param[in] UfsHcPciBase UFS Host Controller's PCI ConfigSpace Base address + @param[in] DevInitPhase For the performance optimization, + Device initialization is separated to several phases. + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the UFS device. + +**/ +EFI_STATUS +EFIAPI +InitializeUfs( + IN pcidev_t pci_dev + ) +{ + EFI_STATUS Status; + UFS_PEIM_HC_PRIVATE_DATA *Private; + UINT32 Index; + UFS_CONFIG_DESC Config; + UINTN MmioBase; + UINT8 Controller; + UFS_HC_PEI_PRIVATE_DATA *UfsPrivateHcData; + + gPrivate = (UFS_PEIM_HC_PRIVATE_DATA *)malloc(sizeof(UFS_PEIM_HC_PRIVATE_DATA)); + if (gPrivate == NULL) { + DEBUG_UFS((EFI_D_VERBOSE, "Failed to allocate memory for UFS_PEIM_HC_PRIVATE_DATA! \n")); + return EFI_OUT_OF_RESOURCES; + } + + Private = gPrivate; + + ZeroMem(Private, sizeof(UFS_PEIM_HC_PRIVATE_DATA)); + + UfsPrivateHcData = (UFS_HC_PEI_PRIVATE_DATA *)malloc(sizeof (UFS_HC_PEI_PRIVATE_DATA)); + if (UfsPrivateHcData == NULL) { + DEBUG_UFS((EFI_D_VERBOSE, "Failed to allocate memory for UFS_HC_PEI_PRIVATE_DATA! \n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // Init Ufs data + // + InitializeUfsHcPeim(UfsPrivateHcData, pci_dev); + + + Controller = 0; + MmioBase = 0; + + while (TRUE) { + Status = GetUfsHcMmioBar(UfsPrivateHcData, Controller, &MmioBase); + // + // When status is error, meant no controller is found + // + if (EFI_ERROR(Status)) { + break; + } + + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + CopyMem(Private, &gUfsHcTemplate, sizeof(UFS_PEIM_HC_PRIVATE_DATA)); + Private->UfsHcBase = MmioBase; + + // + // Initialize the memory pool which will be used in all transactions. + // + Status = UfsInitMemPool(Private); + if (EFI_ERROR(Status)) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // Initialize UFS Host Controller H/W. + // + Status = UfsControllerInit(Private); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei: Host Controller Initialization Error, Status = %d\n", Status)); + Controller++; + continue; + } + + // + // UFS 2.0 spec Section 13.1.3.3: + // At the end of the UFS Interconnect Layer initialization on both host and device side, + // the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready. + // + Status = UfsExecNopCmds(Private); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "Ufs Sending NOP IN command Error, Status = %d\n", Status)); + Controller++; + continue; + } + + // + // The host enables the device initialization completion by setting fDeviceInit flag. + // + Status = UfsSetFlag(Private, UfsFlagDevInit); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "Ufs Set fDeviceInit Flag Error, Status = %d\n", Status)); + Controller++; + continue; + } + + // + // Get Ufs Device's Lun Info by reading Configuration Descriptor. + // + Status = UfsRwDeviceDesc(Private, TRUE, UfsConfigDesc, 0, 0, &Config, sizeof (UFS_CONFIG_DESC)); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "Ufs Get Configuration Descriptor Error, Status = %d\n", Status)); + Controller++; + continue; + } + + for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) { + if (Config.UnitDescConfParams[Index].LunEn != 0) { + Private->Luns.BitMask |= (BIT0 << Index); + DEBUG_UFS((EFI_D_VERBOSE, "Ufs %d Lun %d is enabled\n", Controller, Index)); + } + } + + Controller++; + } + + return EFI_SUCCESS; +} + +UINT8 mUfsTargetId[TARGET_MAX_BYTES]; + +EFI_STATUS +EFIAPI +UfsPassThruPassThru( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN UNUSED_PARAM EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + UFS_PEIM_HC_PRIVATE_DATA *Private; + UINT8 UfsLun; + UINT16 Index; + UINT8 OriginalDataDirection; + + Private = UfsGetPrivateData(); + if (Private == NULL) { + return EFI_NOT_FOUND; + } + + if ((Packet == NULL) || (Packet->Cdb == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Don't support variable length CDB + // + if ((Packet->CdbLength != 6) && (Packet->CdbLength != 10) && + (Packet->CdbLength != 12) && (Packet->CdbLength != 16)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SenseDataLength != 0) && (Packet->SenseData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((This->Mode->IoAlign > 1) && !IS_ALIGN(Packet->InDataBuffer, This->Mode->IoAlign)) { + return EFI_INVALID_PARAMETER; + } + + if ((This->Mode->IoAlign > 1) && !IS_ALIGN(Packet->OutDataBuffer, This->Mode->IoAlign)) { + return EFI_INVALID_PARAMETER; + } + + if ((This->Mode->IoAlign > 1) && !IS_ALIGN(Packet->SenseData, This->Mode->IoAlign)) { + return EFI_INVALID_PARAMETER; + } + + // + // For UFS 2.0 compatible device, 0 is always used to represent the location of the UFS device. + // + SetMem(mUfsTargetId, TARGET_MAX_BYTES, 0x00); + if (CompareMem(Target, mUfsTargetId, TARGET_MAX_BYTES) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // UFS 2.0 spec Section 10.6.7 - Translation of 8-bit UFS LUN to 64-bit SCSI LUN Address + // 0xC1 in the first 8 bits of the 64-bit address indicates a well known LUN address in the SAM SCSI format. + // The second 8 bits of the 64-bit address saves the corresponding 8-bit UFS LUN. + // + if ((UINT8)Lun == UFS_WLUN_PREFIX) { + UfsLun = BIT7 | (((UINT8 *)&Lun)[1] & 0xFF); + } else if ((UINT8)Lun == 0) { + UfsLun = ((UINT8 *)&Lun)[1] & 0xFF; + } else { + return EFI_NOT_FOUND; + } + + if (Lun != 0x44c1) { + for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) { + continue; + } + + if (Private->Luns.Lun[Index] == UfsLun) { + break; + } + } + + if (Index == UFS_PEIM_MAX_LUNS) { + return EFI_NOT_FOUND; + } + } + + + OriginalDataDirection = Packet->DataDirection; + if (OriginalDataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) + Packet->DataDirection = UfsDataIn; + else + Packet->DataDirection = UfsDataOut; + + Status = UfsExecScsiCmds(Private, UfsLun, (UFS_SCSI_REQUEST_PACKET *)Packet); + Packet->DataDirection = OriginalDataDirection; + + return Status; +} + + +EFI_STATUS +EFIAPI +UfsPassThruGetTargetLun( + IN UNUSED_PARAM EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +{ + UFS_PEIM_HC_PRIVATE_DATA *Private; + SCSI_DEVICE_PATH *DevicePathNode; + UINT8 Pun; + UINT8 UfsLun; + UINT16 Index; + + Private = UfsGetPrivateData(); + if (Private == NULL) { + return EFI_NOT_FOUND; + } + + // + // Validate parameters passed in. + // + if (DevicePath == NULL || Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*Target == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the DevicePath belongs to SCSI_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_UFS_DP) || + (DevicePathNodeLength(DevicePath) != sizeof(SCSI_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + DevicePathNode = (SCSI_DEVICE_PATH *)DevicePath; + + Pun = (UINT8)DevicePathNode->Pun; + UfsLun = (UINT8)DevicePathNode->Lun; + + if (Pun != 0) { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) { + continue; + } + + if (Private->Luns.Lun[Index] == UfsLun) { + break; + } + } + + if (Index == UFS_PEIM_MAX_LUNS) { + return EFI_NOT_FOUND; + } + + SetMem(*Target, TARGET_MAX_BYTES, 0x00); + *Lun = 0; + if ((UfsLun & BIT7) == BIT7) { + ((UINT8 *)Lun)[0] = UFS_WLUN_PREFIX; + ((UINT8 *)Lun)[1] = UfsLun & ~BIT7; + } else { + ((UINT8 *)Lun)[1] = UfsLun; + } + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +UfsPassThruGetNextTargetLun( + IN UNUSED_PARAM EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UNUSED_PARAM UINT8 **Target, + IN OUT UNUSED_PARAM UINT64 *Lun + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +UfsPassThruBuildDevicePath( + IN UNUSED_PARAM EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UNUSED_PARAM UINT8 *Target, + IN UNUSED_PARAM UINT64 Lun, + IN OUT UNUSED_PARAM EFI_DEVICE_PATH **DevicePath + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +UfsPassThruResetChannel( + IN UNUSED_PARAM EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +UfsPassThruResetTargetLun( + IN UNUSED_PARAM EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UNUSED_PARAM UINT8 *Target, + IN UNUSED_PARAM UINT64 Lun + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +UfsPassThruGetNextTarget( + IN UNUSED_PARAM EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UNUSED_PARAM UINT8 **Target + ) +{ + return EFI_UNSUPPORTED; +} + diff --git a/drivers/ufs/UfsBlockIoLib.h b/drivers/ufs/UfsBlockIoLib.h new file mode 100755 index 0000000..51d2f41 --- /dev/null +++ b/drivers/ufs/UfsBlockIoLib.h @@ -0,0 +1,198 @@ +/** @file + + Copyright (c) 2014 - 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 + 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 _UFS_BLOCK_IO_PEI_H_ +#define _UFS_BLOCK_IO_PEI_H_ + +#include +#include "UfsInternal.h" +#include "ScsiPassThruExt.h" +#define SIZE_1KB 0x00000400 +#define SIZE_2KB 0x00000800 +#define SIZE_4KB 0x00001000 +#define SIZE_8KB 0x00002000 +#define SIZE_16KB 0x00004000 +#define SIZE_32KB 0x00008000 +#define SIZE_64KB 0x00010000 +#define SIZE_128KB 0x00020000 +#define SIZE_256KB 0x00040000 +#define SIZE_512KB 0x00080000 + +typedef struct { + UINT64 BlockNum; + UINT32 BlockSize; +} DEVICE_BLOCK_INFO; + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +UfsReadBlocks( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + This function writes data from Memory to UFS + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartBlock Target UFS block number(LBA) where data will be written + @param[in] DataSize Total data size to be written in bytes unit + @param[out] DataAddress Data address in Memory to be copied to UFS + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +UfsWriteBlocks( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer +); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[out] DevBlockInfo The Block Io information of the specified block partition. + + @retval EFI_SUCCESS The Block Io information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +UfsGetMediaInfo( + IN UINTN DeviceIndex, + OUT DEVICE_BLOCK_INFO *DevBlockInfo + ); + +/** + The function will initialize UFS device. + + Based on UfsHcPciBase, this function will initialize UFS host controller, allocate + necessary resources, and enumarate all the LUNs. + + @param[in] UfsHcPciBase UFS Host Controller's PCI ConfigSpace Base address + @param[in] DevInitPhase For the performance optimization, + Device initialization is separated to several phases. + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the UFS device. + +**/ +EFI_STATUS +EFIAPI +InitializeUfs( + IN pcidev_t pci_dev + ); + +UFS_PEIM_HC_PRIVATE_DATA * +UfsGetPrivateData(); + +EFI_STATUS +EFIAPI +UfsPassThruGetTargetLun( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +EFI_STATUS +EFIAPI +UfsPassThruGetNextTargetLun( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ); + +EFI_STATUS +EFIAPI +UfsPassThruPassThru( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +EFI_STATUS +EFIAPI +UfsPassThruBuildDevicePath( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH **DevicePath + ); + +EFI_STATUS +EFIAPI +UfsPassThruResetChannel( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +UfsPassThruResetTargetLun( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +EFI_STATUS +EFIAPI +UfsPassThruGetNextTarget( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ); + +#endif diff --git a/drivers/ufs/UfsHcMem.c b/drivers/ufs/UfsHcMem.c new file mode 100644 index 0000000..bcd75aa --- /dev/null +++ b/drivers/ufs/UfsHcMem.c @@ -0,0 +1,488 @@ +/** @file + +Copyright (c) 2014 - 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 +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. + +**/ + +#include "UfsInternal.h" + +/** + Allocate a block of memory to be used by the buffer pool. + + @param Pages How many pages to allocate. + + @return The allocated memory block or NULL if failed. + +**/ + +EFI_STATUS alloc_aligned_page(VOID **free_addr, VOID **aligned_addr, + UINTN page_cnt) +{ + *free_addr = malloc((page_cnt + 1) * EFI_PAGE_SIZE); + if (!*free_addr) + return EFI_OUT_OF_RESOURCES; + ZeroMem(*free_addr, (page_cnt + 1) * EFI_PAGE_SIZE); + + *aligned_addr = (VOID *)(((UINTN)*free_addr + EFI_PAGE_SIZE - 1) & ~(EFI_PAGE_SIZE - 1)); + return EFI_SUCCESS; +} + +EFI_STATUS alloc_align(VOID **free_addr, VOID **aligned_addr, + UINTN size, UINTN align) +{ + ASSERT ((align & (align - 1)) == 0); + + *free_addr = malloc(size + align); + if (!*free_addr) + return EFI_OUT_OF_RESOURCES; + + ZeroMem(*free_addr, size + align); + + if (!align) { + *aligned_addr = *free_addr; + return EFI_SUCCESS; +} + + *aligned_addr = (VOID *)(((UINTN)*free_addr + align - 1) & ~(align - 1)); + return EFI_SUCCESS; +} + + +UFS_PEIM_MEM_BLOCK * +UfsAllocMemBlock( + IN UINTN Pages + ) +{ + UFS_PEIM_MEM_BLOCK *Block; + VOID *TempPtr; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS status; + VOID *BufPage = NULL; + VOID *FreeBufPage = NULL; + + TempPtr = NULL; + Block = NULL; + + TempPtr = malloc(sizeof(UFS_PEIM_MEM_BLOCK)); + if (TempPtr == NULL) { + return NULL; + } + + ZeroMem((VOID *)(UINTN)TempPtr, sizeof(UFS_PEIM_MEM_BLOCK)); + + // + // each bit in the bit array represents UFS_PEIM_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT(UFS_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block = (UFS_PEIM_MEM_BLOCK *)(UINTN)TempPtr; + Block->BufLen = EFI_PAGES_TO_SIZE(Pages); + Block->BitsLen = Block->BufLen / (UFS_PEIM_MEM_UNIT * 8); + + TempPtr = malloc(Block->BitsLen); + if (TempPtr == NULL) { + return NULL; + } + + ZeroMem((VOID *)(UINTN)TempPtr, Block->BitsLen); + + Block->Bits = (UINT8 *)(UINTN)TempPtr; + + status = alloc_align(&FreeBufPage, &BufPage, Pages * EFI_PAGE_SIZE, 4096); + if (EFI_ERROR(status)) { + return NULL; + } + Address = (UINTN)BufPage; + + ZeroMem((VOID *)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages)); + + Block->Buf = (UINT8 *)((UINTN)Address); + Block->FreeBuf = FreeBufPage; + Block->Next = NULL; + + return Block; +} + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +UfsFreeMemBlock ( + IN __attribute__((__unused__)) UFS_PEIM_MEM_POOL *Pool, + IN __attribute__((__unused__)) UFS_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT((Pool != NULL) && (Block != NULL)); +} + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return The pointer to the allocated memory. If couldn't allocate the needed memory, + the return value is NULL. + +**/ +VOID * +UfsAllocMemFromBlock( + IN UFS_PEIM_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!UFS_PEIM_MEM_BIT_IS_SET(Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + UFS_PEIM_NEXT_BIT(Byte, Bit); + + } else { + UFS_PEIM_NEXT_BIT(Byte, Bit); + + Available = 0; + StartByte = Byte; + StartBit = Bit; + } + } + + if (Available < Units) { + return NULL; + } + + // + // Mark the memory as allocated + // + Byte = StartByte; + Bit = StartBit; + + for (Count = 0; Count < Units; Count++) { + ASSERT(!UFS_PEIM_MEM_BIT_IS_SET(Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8)(Block->Bits[Byte] | (UINT8) UFS_PEIM_MEM_BIT(Bit)); + UFS_PEIM_NEXT_BIT(Byte, Bit); + } + + return Block->Buf + (StartByte * 8 + StartBit) * UFS_PEIM_MEM_UNIT; +} + +/** + Insert the memory block to the pool's list of the blocks. + + @param Head The head of the memory pool's block list. + @param Block The memory block to insert. + +**/ +VOID +UfsInsertMemBlockToPool( + IN UFS_PEIM_MEM_BLOCK *Head, + IN UFS_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT((Head != NULL) && (Block != NULL)); + Block->Next = Head->Next; + Head->Next = Block; +} + +/** + Is the memory block empty? + + @param Block The memory block to check. + + @retval TRUE The memory block is empty. + @retval FALSE The memory block isn't empty. + +**/ +BOOLEAN +UfsIsMemBlockEmpty( + IN UFS_PEIM_MEM_BLOCK *Block + ) +{ + UINTN Index; + + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +UfsUnlinkMemBlock( + IN UFS_PEIM_MEM_BLOCK *Head, + IN UFS_PEIM_MEM_BLOCK *BlockToUnlink + ) +{ + UFS_PEIM_MEM_BLOCK *Block; + + ASSERT((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + +/** + Initialize the memory management pool for the host controller. + + @param Private The Ufs Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +UfsInitMemPool( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UFS_PEIM_MEM_POOL *Pool; + VOID *TempPtr; + + TempPtr = NULL; + Pool = NULL; + + TempPtr = malloc(sizeof(UFS_PEIM_MEM_POOL)); + if (TempPtr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem((VOID *)(UINTN)TempPtr, sizeof(UFS_PEIM_MEM_POOL)); + + Pool = (UFS_PEIM_MEM_POOL *)((UINTN)TempPtr); + + Pool->Head = UfsAllocMemBlock(UFS_PEIM_MEM_DEFAULT_PAGES); + + if (Pool->Head == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Pool = Pool; + return EFI_SUCCESS; +} + +/** + Release the memory management pool. + + @param Pool The memory pool to free. + + @retval EFI_DEVICE_ERROR Fail to free the memory pool. + @retval EFI_SUCCESS The memory pool is freed. + +**/ +EFI_STATUS +UfsFreeMemPool ( + IN UFS_PEIM_MEM_POOL *Pool + ) +{ + UFS_PEIM_MEM_BLOCK *Block; + + ASSERT(Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // UfsUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + UfsFreeMemBlock(Pool, Block); + } + + UfsFreeMemBlock(Pool, Pool->Head); + + return EFI_SUCCESS; +} + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UfsAllocateMem( + IN UFS_PEIM_MEM_POOL *Pool, + IN UINTN Size + ) +{ + UFS_PEIM_MEM_BLOCK *Head; + UFS_PEIM_MEM_BLOCK *Block; + UFS_PEIM_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = UFS_PEIM_MEM_ROUND(Size); + Head = Pool->Head; + ASSERT(Head != NULL); + + // + // First check whether current memory blocks can satisfy the allocation. + // + for (Block = Head; Block != NULL; Block = Block->Next) { + Mem = UfsAllocMemFromBlock(Block, AllocSize / UFS_PEIM_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem(Mem, Size); + break; + } + } + + if (Mem != NULL) { + return Mem; + } + + // + // Create a new memory block if there is not enough memory + // in the pool. If the allocation size is larger than the + // default page number, just allocate a large enough memory + // block. Otherwise allocate default pages. + // + if (AllocSize > EFI_PAGES_TO_SIZE(UFS_PEIM_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES(AllocSize) + 1; + } else { + Pages = UFS_PEIM_MEM_DEFAULT_PAGES; + } + + NewBlock = UfsAllocMemBlock(Pages); + if (NewBlock == NULL) { + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + UfsInsertMemBlockToPool(Head, NewBlock); + Mem = UfsAllocMemFromBlock(NewBlock, AllocSize / UFS_PEIM_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem(Mem, Size); + } + + return Mem; +} + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UfsFreeMem( + IN UFS_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + UFS_PEIM_MEM_BLOCK *Head; + UFS_PEIM_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = UFS_PEIM_MEM_ROUND(Size); + ToFree = (UINT8 *)Mem; + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the memory to free. + // + if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) { + // + // compute the start byte and bit in the bit array + // + Byte = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) / 8; + Bit = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) % 8; + + // + // reset associated bits in bit array + // + for (Count = 0; Count < (AllocSize / UFS_PEIM_MEM_UNIT); Count++) { + ASSERT(UFS_PEIM_MEM_BIT_IS_SET(Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8)(Block->Bits[Byte] ^ UFS_PEIM_MEM_BIT(Bit)); + UFS_PEIM_NEXT_BIT(Byte, Bit); + } + + break; + } + } + + // + // If Block == NULL, it means that the current memory isn't + // in the host controller's pool. This is critical because + // the caller has passed in a wrong memory point + // + ASSERT(Block != NULL); + + // + // Release the current memory block if it is empty and not the head + // + if ((Block != Head) && UfsIsMemBlockEmpty(Block)) { + UfsFreeMemBlock(Pool, Block); + } + + return ; +} diff --git a/drivers/ufs/UfsHcMem.h b/drivers/ufs/UfsHcMem.h new file mode 100644 index 0000000..5942b4f --- /dev/null +++ b/drivers/ufs/UfsHcMem.h @@ -0,0 +1,63 @@ +/** @file + +Copyright (c) 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 _UFS_PEIM_MEM_H_ +#define _UFS_PEIM_MEM_H_ + +#define UFS_PEIM_MEM_BIT(a) ((UINTN)(1 << (a))) + +#define UFS_PEIM_MEM_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & UFS_PEIM_MEM_BIT(Bit)) == UFS_PEIM_MEM_BIT(Bit))) + +typedef struct _UFS_PEIM_MEM_BLOCK UFS_PEIM_MEM_BLOCK; + +struct _UFS_PEIM_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINT8 *FreeBuf; + UINTN BufLen; // Memory size in bytes + UFS_PEIM_MEM_BLOCK *Next; +}; + +typedef struct _UFS_PEIM_MEM_POOL { + UFS_PEIM_MEM_BLOCK *Head; +} UFS_PEIM_MEM_POOL; + +// +// Memory allocation unit, note that the value must meet UFS spec alignment requirement. +// +#define UFS_PEIM_MEM_UNIT 128 + +#define UFS_PEIM_MEM_UNIT_MASK (UFS_PEIM_MEM_UNIT - 1) +#define UFS_PEIM_MEM_DEFAULT_PAGES 16 + +#define UFS_PEIM_MEM_ROUND(Len) (((Len) + UFS_PEIM_MEM_UNIT_MASK) & (~UFS_PEIM_MEM_UNIT_MASK)) + +#define PAGE_SIZE 4096 +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define UFS_PEIM_NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + +#endif + diff --git a/drivers/ufs/UfsHci.c b/drivers/ufs/UfsHci.c new file mode 100644 index 0000000..5fea5bc --- /dev/null +++ b/drivers/ufs/UfsHci.c @@ -0,0 +1,2281 @@ +/** @file + + Copyright (c) 2014 - 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 + 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. + +**/ + +#include +#include +#include +#include "UfsInternal.h" + +#ifndef ClockCycles +#define ClockCycles() (*((volatile const uint32_t *) 0xfed000f0)) /* HPET_MCV */ +#define CPS 19200000 /* guessed on HPET_GCID.CTP = 0x031aba85 */ +#define CPMS 19200 +#define CPUS 19 +#endif + +#define TIMEOUT_WMS_8 10000 +#define PA_TX_GEAR 0x1568 +#define PA_RX_GEAR 0x1583 +#define PWM_G1 1 +#define PWM_G2 2 +#define PWM_G3 3 +#define PWM_G4 4 +#define PWM_G5 5 +#define PWM_G6 6 +#define PWM_G7 7 +#define HS_G1 1 +#define HS_G2 2 +#define HS_G3 3 +#define HS_G4 4 + + // Lanes Configuration +#define PA_ACTIVE_TX_DATA_LANES 0x1560 +#define PA_ACTIVE_RX_DATA_LANES 0x1580 +#define PA_AVAILABLE_TX_LANES 2 +#define PA_AVAILABLE_RX_LANES 2 + + // TX and RX Frequency Series in High Speed Mode +#define PA_HS_SERIES 0x156A +#define HS_SERIES_A 1 +#define HS_SERIES_B 2 + + // Termination +#define PA_TX_TERMINATION 0x1569 +#define PA_RX_TERMINATION 0x1584 +#define TERMINATION_OFF 0 +#define TERMINATION_ON 1 + + // Scrambling +#define PA_SCRAMBLING 0x1585 +#define SCRAMBLING_OFF 0 +#define SCRAMBLING_ON 1 + + // Triggering the Power Mode Change Request +#define PA_PWR_MODE 0x1571 + +VOID +MicroSecondDelay( + IN UINTN usecs + ) +{ + UINTN t1, t0 = ClockCycles(); + UINTN ticks = usecs * CPUS; + + if (ticks == 0) + ticks = 1; + + while ((t1 = ClockCycles()) - t0 <= ticks) + ; +} + +/** + Wait for the value of the specified system memory set to the test value. + + @param Address The system memory address to test. + @param MaskValue The mask value of memory. + @param TestValue The test value of memory. + @param Timeout The time out value for wait memory set, uses 100ns as a unit. + + @retval EFI_TIMEOUT The system memory setting is time out. + @retval EFI_SUCCESS The system memory is correct set. + +**/ +EFI_STATUS +EFIAPI +UfsWaitMemSet( + IN UINTN Address, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN UINT64 Timeout + ) +{ + UINT32 Value; + UINT64 Delay; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32(Timeout, 10, NULL) + 1; + + do { + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = read32((void *)Address) & MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 1 microseconds. + // + MicroSecondDelay(1); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + +/** + Wait for the value of the specified system memory set to the test value. + + @param Address The system memory address to test. + @param MaskValue The mask value of memory. + @param TestValue The test value of memory. + @param Timeout The time out value for wait memory set, uses 100ns as a unit. + + @retval EFI_TIMEOUT The system memory setting is time out. + @retval EFI_SUCCESS The system memory is correct set. + +**/ +EFI_STATUS +UfsWaitMemSet8( + IN UINTN Address, + IN UINT8 MaskValue, + IN UINT8 TestValue, + IN UINT32 Timeout + ) +{ + UINT32 Value; + UINT64 Delay; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = Timeout; + + do { + Value = read8((void *)Address) & MaskValue; + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 2 microseconds. + // + udelay(2); + // MicroSecondDelay (1); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + +/** + Dump UIC command execution result for debugging. + + @param[in] UicOpcode The executed UIC opcode. + @param[in] Result The result to be parsed. + +**/ +VOID +DumpUicCmdExecResult( + IN UINT8 UicOpcode, + IN UINT8 Result + ) +{ + if (UicOpcode <= UfsUicDmePeerSet) { + switch (Result) { + case 0x00: + break; + case 0x01: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n")); + break; + case 0x02: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n")); + break; + case 0x03: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n")); + break; + case 0x04: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n")); + break; + case 0x05: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - BAD_INDEX\n")); + break; + case 0x06: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n")); + break; + case 0x07: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n")); + break; + case 0x08: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n")); + break; + case 0x09: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - BUSY\n")); + break; + case 0x0A: + DEBUG_UFS((EFI_D_VERBOSE, "UIC configuration command fails - DME_FAILURE\n")); + break; + default: + ASSERT(FALSE); + break; + } + } else { + switch (Result) { + case 0x00: + break; + case 0x01: + DEBUG_UFS((EFI_D_VERBOSE, "UIC control command fails - FAILURE\n")); + break; + default: + ASSERT(FALSE); + break; + } + } +} + +/** + Dump QUERY RESPONSE UPIU result for debugging. + + @param[in] Result The result to be parsed. + +**/ +VOID +DumpQueryResponseResult ( + IN UINT8 Result + ) +{ + switch (Result) { + case 0xF6: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Parameter Not Readable\n")); + break; + case 0xF7: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Parameter Not Writeable\n")); + break; + case 0xF8: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Parameter Already Written\n")); + break; + case 0xF9: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Invalid Length\n")); + break; + case 0xFA: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Invalid Value\n")); + break; + case 0xFB: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Invalid Selector\n")); + break; + case 0xFC: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Invalid Index\n")); + break; + case 0xFD: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Invalid Idn\n")); + break; + case 0xFE: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with Invalid Opcode\n")); + break; + case 0xFF: + DEBUG_UFS((EFI_D_VERBOSE, "Query Response with General Failure\n")); + break; + default: + ASSERT(FALSE); + break; + } +} + +/** + Swap little endian to big endian. + + @param[in, out] Buffer The data buffer. In input, it contains little endian data. + In output, it will become big endian. + @param[in] BufferSize The length of converted data. + +**/ +VOID +SwapLittleEndianToBigEndian( + IN OUT UINT8 *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 Index; + UINT8 Temp; + UINT32 SwapCount; + + SwapCount = BufferSize / 2; + for (Index = 0; Index < SwapCount; Index++) { + Temp = Buffer[Index]; + Buffer[Index] = Buffer[BufferSize - 1 - Index]; + Buffer[BufferSize - 1 - Index] = Temp; + } +} + +/** + Fill TSF field of QUERY REQUEST UPIU. + + @param[in, out] TsfBase The base address of TSF field of QUERY REQUEST UPIU. + @param[in] Opcode The opcode of request. + @param[in] DescId The descriptor ID of request. + @param[in] Index The index of request. + @param[in] Selector The selector of request. + @param[in] Length The length of transferred data. The maximum is 4. + @param[in] Value The value of transferred data. + +**/ +VOID +UfsFillTsfOfQueryReqUpiu( + IN OUT UTP_UPIU_TSF *TsfBase, + IN UINT8 Opcode, + IN UINT8 DescId OPTIONAL, + IN UINT8 Index OPTIONAL, + IN UINT8 Selector OPTIONAL, + IN UINT16 Length OPTIONAL, + IN UINT32 Value OPTIONAL + ) +{ + ASSERT(TsfBase != NULL); + ASSERT(Opcode <= UtpQueryFuncOpcodeTogFlag); + + TsfBase->Opcode = Opcode; + if (Opcode != UtpQueryFuncOpcodeNop) { + TsfBase->DescId = DescId; + TsfBase->Index = Index; + TsfBase->Selector = Selector; + + if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) { + SwapLittleEndianToBigEndian((UINT8 *)&Length, sizeof (Length)); + TsfBase->Length = Length; + } + + if (Opcode == UtpQueryFuncOpcodeWrAttr) { + SwapLittleEndianToBigEndian((UINT8 *)&Value, sizeof (Value)); + TsfBase->Value = Value; + } + } +} + +/** + Initialize COMMAND UPIU. + + @param[in, out] Command The base address of COMMAND UPIU. + @param[in] Lun The Lun on which the SCSI command is executed. + @param[in] TaskTag The task tag of request. + @param[in] Cdb The cdb buffer containing SCSI command. + @param[in] CdbLength The cdb length. + @param[in] DataDirection The direction of data transfer. + @param[in] ExpDataTranLen The expected transfer data length. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitCommandUpiu( + IN OUT UTP_COMMAND_UPIU *Command, + IN UINT8 Lun, + IN UINT8 TaskTag, + IN UINT8 *Cdb, + IN UINT8 CdbLength, + IN UFS_DATA_DIRECTION DataDirection, + IN UINT32 ExpDataTranLen + ) +{ + UINT8 Flags; + ASSERT((Command != NULL) && (Cdb != NULL)); + + // + // Task attribute is hard-coded to Ordered. + // + if (DataDirection == UfsDataIn) { + Flags = BIT0 | BIT6; + } else if (DataDirection == UfsDataOut) { + Flags = BIT0 | BIT5; + } else { + Flags = BIT0; + } + + // + // Fill UTP COMMAND UPIU associated fields. + // + Command->TransCode = 0x01; + Command->Flags = Flags; + Command->Lun = Lun; + Command->TaskTag = TaskTag; + Command->CmdSet = 0x00; + SwapLittleEndianToBigEndian((UINT8 *)&ExpDataTranLen, sizeof (ExpDataTranLen)); + Command->ExpDataTranLen = ExpDataTranLen; + + CopyMem(Command->Cdb, Cdb, CdbLength); + + return EFI_SUCCESS; +} + +/** + Initialize UTP PRDT for data transfer. + + @param[in] Prdt The base address of PRDT. + @param[in] Buffer The buffer to be read or written. + @param[in] BufferSize The data size to be read or written. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitUtpPrdt( + IN UTP_TR_PRD *Prdt, + IN VOID *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 PrdtIndex; + UINT32 RemainingLen; + UINT8 *Remaining; + UINTN PrdtNumber; + + if ((BufferSize & (BIT0 | BIT1)) != 0) { + BufferSize &= ~(BIT0 | BIT1); + DEBUG_UFS((EFI_D_VERBOSE, "UfsInitUtpPrdt: The BufferSize [%d] is not dword-aligned!\n", BufferSize)); + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + ASSERT(((UINTN)Buffer & (BIT0 | BIT1)) == 0); + DEBUG_UFS((EFI_D_VERBOSE, "UfsInitUtpPrdt: Buffer addr = 0x%0x\n", (UINTN)Buffer)); + + RemainingLen = BufferSize; + Remaining = Buffer; + PrdtNumber = (UINTN)DivU64x32((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, + UFS_MAX_DATA_LEN_PER_PRD, NULL); + + for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) { + if (RemainingLen < UFS_MAX_DATA_LEN_PER_PRD) { + Prdt[PrdtIndex].DbCount = (UINT32)RemainingLen - 1; + } else { + Prdt[PrdtIndex].DbCount = UFS_MAX_DATA_LEN_PER_PRD - 1; + } + + Prdt[PrdtIndex].DbAddr = (UINT32)RShiftU64((UINT64)(UINTN)Remaining, 2); + Prdt[PrdtIndex].DbAddrU = (UINT32)RShiftU64((UINT64)(UINTN)Remaining, 32); + RemainingLen -= UFS_MAX_DATA_LEN_PER_PRD; + Remaining += UFS_MAX_DATA_LEN_PER_PRD; + } + + return EFI_SUCCESS; +} + +/** + Initialize QUERY REQUEST UPIU. + + @param[in, out] QueryReq The base address of QUERY REQUEST UPIU. + @param[in] TaskTag The task tag of request. + @param[in] Opcode The opcode of request. + @param[in] DescId The descriptor ID of request. + @param[in] Index The index of request. + @param[in] Selector The selector of request. + @param[in] DataSize The data size to be read or written. + @param[in] Data The buffer to be read or written. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitQueryRequestUpiu( + IN OUT UTP_QUERY_REQ_UPIU *QueryReq, + IN UINT8 TaskTag, + IN UINT8 Opcode, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN UINTN DataSize OPTIONAL, + IN UINT8 *Data OPTIONAL + ) +{ + ASSERT(QueryReq != NULL); + + QueryReq->TransCode = 0x16; + QueryReq->TaskTag = TaskTag; + if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeRdFlag) || (Opcode == UtpQueryFuncOpcodeRdAttr)) { + QueryReq->QueryFunc = QUERY_FUNC_STD_READ_REQ; + } else { + QueryReq->QueryFunc = QUERY_FUNC_STD_WRITE_REQ; + } + + if (Opcode == UtpQueryFuncOpcodeWrAttr) { + UfsFillTsfOfQueryReqUpiu(&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, *(UINT32 *)Data); + } else if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) { + UfsFillTsfOfQueryReqUpiu(&QueryReq->Tsf, Opcode, DescId, Index, Selector, (UINT16)DataSize, 0); + } else { + UfsFillTsfOfQueryReqUpiu(&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, 0); + } + + if (Opcode == UtpQueryFuncOpcodeWrDesc) { + CopyMem(QueryReq + 1, Data, DataSize); + } + + return EFI_SUCCESS; +} + +/** + Allocate COMMAND/RESPONSE UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The Lun on which the SCSI command is executed. + @param[in] Packet The pointer to the UFS_SCSI_REQUEST_PACKET data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsCreateScsiCommandDesc( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN UFS_SCSI_REQUEST_PACKET *Packet, + IN UTP_TRD *Trd + ) +{ + UINT8 *CommandDesc; + UINTN TotalLen; + UINTN PrdtNumber; + VOID *Buffer; + UINT32 Length; + UTP_COMMAND_UPIU *CommandUpiu; + UTP_TR_PRD *PrdtBase; + UFS_DATA_DIRECTION DataDirection; + UINT8 *Cdb = NULL; + + ASSERT((Private != NULL) && (Packet != NULL) && (Trd != NULL)); + + if (Packet->DataDirection == UfsDataIn) { + Buffer = Packet->InDataBuffer; + Length = Packet->InTransferLength; + DataDirection = UfsDataIn; + } else { + Buffer = Packet->OutDataBuffer; + Length = Packet->OutTransferLength; + DataDirection = UfsDataOut; + } + + if (Length == 0) { + DataDirection = UfsNoData; + } + + PrdtNumber = (UINTN)DivU64x32((UINT64)Length + UFS_MAX_DATA_LEN_PER_PRD - 1, + UFS_MAX_DATA_LEN_PER_PRD, NULL); + + TotalLen = ROUNDUP8(sizeof(UTP_COMMAND_UPIU)) + ROUNDUP8(sizeof(UTP_RESPONSE_UPIU)) + PrdtNumber * sizeof(UTP_TR_PRD); + CommandDesc = UfsAllocateMem(Private->Pool, TotalLen); + if (CommandDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CommandUpiu = (UTP_COMMAND_UPIU *)CommandDesc; + PrdtBase = (UTP_TR_PRD *)(CommandDesc + ROUNDUP8(sizeof(UTP_COMMAND_UPIU)) + ROUNDUP8(sizeof(UTP_RESPONSE_UPIU))); + + Cdb = (UINT8 *)Packet->Cdb; + if (!Cdb) + return EFI_INVALID_PARAMETER; + + if (Cdb[0] == EFI_SCSI_OP_REQUEST_SENSE) { + // + // According to UFS Host Controller Spec(JESD223C), data buffers that are + // associated with a UTP Transfer Request should be with Dword granularity. + // But the size of UFS Request Sense Data Response defined in UFS Spec + // (JESD220C) is 18 bytes, which is not Dword aligned in length. + // Here, we round-up 'DataLen' to be Dword aligned for a Request Sense + // command. + // + DEBUG_UFS((EFI_D_VERBOSE, "UfsCreateScsiCommandDesc: The DataLen [%d] is not dword-aligned, aligning buffer.\n", Length)); + Length = (Length + 3) & (~(BIT0 | BIT1)); + } + UfsInitCommandUpiu(CommandUpiu, Lun, Private->TaskTag++, Packet->Cdb, Packet->CdbLength, DataDirection, Length); + UfsInitUtpPrdt(PrdtBase, Buffer, Length); + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Response UPIU and the Physical Region Description Table + // *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = DataDirection; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->Ocs = UFS_HC_TRD_OCS_INIT_VALUE; + Trd->UcdBa = (UINT32)RShiftU64((UINT64)(UINTN)CommandUpiu, 7); + Trd->UcdBaU = (UINT32)RShiftU64((UINT64)(UINTN)CommandUpiu, 32); + Trd->RuL = (UINT16)DivU64x32((UINT64)ROUNDUP8(sizeof(UTP_RESPONSE_UPIU)), + sizeof(UINT32), NULL); + Trd->RuO = (UINT16)DivU64x32((UINT64)ROUNDUP8(sizeof(UTP_COMMAND_UPIU)), + sizeof(UINT32), NULL); + Trd->PrdtL = (UINT16)PrdtNumber; + Trd->PrdtO = (UINT16)DivU64x32((UINT64)(ROUNDUP8 (sizeof(UTP_COMMAND_UPIU)) + ROUNDUP8(sizeof(UTP_RESPONSE_UPIU))), + sizeof(UINT32), NULL); + return EFI_SUCCESS; +} + +/** + Allocate QUERY REQUEST/QUERY RESPONSE UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Packet The pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + @retval EFI_INVALID_PARAMETER The parameter passed in is invalid. + +**/ +EFI_STATUS +UfsCreateDMCommandDesc( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet, + IN UTP_TRD *Trd + ) +{ + UINT8 *CommandDesc; + UINTN TotalLen; + UTP_QUERY_REQ_UPIU *QueryReqUpiu; + UINT8 Opcode; + UINT32 DataSize; + UINT8 *Data; + UINT8 DataDirection; + + ASSERT((Private != NULL) && (Packet != NULL) && (Trd != NULL)); + + Opcode = Packet->Opcode; + if ((Opcode > UtpQueryFuncOpcodeTogFlag) || (Opcode == UtpQueryFuncOpcodeNop)) { + return EFI_INVALID_PARAMETER; + } + + DataDirection = Packet->DataDirection; + if (DataDirection == UfsDataIn) { + DataSize = Packet->InTransferLength; + Data = Packet->InDataBuffer; + } else if (DataDirection == UfsDataOut) { + DataSize = Packet->OutTransferLength; + Data = Packet->OutDataBuffer; + } else { + DataSize = 0; + Data = NULL; + } + + if (((Opcode != UtpQueryFuncOpcodeSetFlag) && (Opcode != UtpQueryFuncOpcodeClrFlag) && (Opcode != UtpQueryFuncOpcodeTogFlag)) + && ((DataSize == 0) || (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (((Opcode == UtpQueryFuncOpcodeSetFlag) || (Opcode == UtpQueryFuncOpcodeClrFlag) || (Opcode == UtpQueryFuncOpcodeTogFlag)) + && ((DataSize != 0) || (Data != NULL))) { + return EFI_INVALID_PARAMETER; + } + + if ((Opcode == UtpQueryFuncOpcodeWrAttr) && (DataSize != sizeof (UINT32))) { + return EFI_INVALID_PARAMETER; + } + + if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) { + TotalLen = ROUNDUP8(sizeof(UTP_QUERY_REQ_UPIU)) + ROUNDUP8(sizeof(UTP_QUERY_RESP_UPIU)) + ROUNDUP8(DataSize); + } else { + TotalLen = ROUNDUP8(sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8(sizeof (UTP_QUERY_RESP_UPIU)); + } + + CommandDesc = UfsAllocateMem(Private->Pool, TotalLen); + if (CommandDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize UTP QUERY REQUEST UPIU + // + QueryReqUpiu = (UTP_QUERY_REQ_UPIU *)CommandDesc; + UfsInitQueryRequestUpiu( + QueryReqUpiu, + Private->TaskTag++, + Opcode, + Packet->DescId, + Packet->Index, + Packet->Selector, + DataSize, + Data + ); + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Query Response UPIU *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = DataDirection; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->Ocs = UFS_HC_TRD_OCS_INIT_VALUE; + Trd->UcdBa = (UINT32)RShiftU64((UINT64)(UINTN)QueryReqUpiu, 7); + Trd->UcdBaU = (UINT32)RShiftU64((UINT64)(UINTN)QueryReqUpiu, 32); + if (Opcode == UtpQueryFuncOpcodeWrDesc) { + Trd->RuL = (UINT16)DivU64x32((UINT64)ROUNDUP8(sizeof(UTP_QUERY_RESP_UPIU)), + sizeof(UINT32), NULL); + Trd->RuO = (UINT16)DivU64x32((UINT64)ROUNDUP8(sizeof(UTP_QUERY_REQ_UPIU)) + ROUNDUP8(DataSize), + sizeof(UINT32), NULL); + } else { + Trd->RuL = (UINT16)DivU64x32((UINT64)ROUNDUP8(sizeof(UTP_QUERY_RESP_UPIU)) + ROUNDUP8(DataSize), + sizeof(UINT32), NULL); + Trd->RuO = (UINT16)DivU64x32((UINT64)ROUNDUP8(sizeof(UTP_QUERY_REQ_UPIU)), + sizeof(UINT32), NULL); + } + + return EFI_SUCCESS; +} + +/** + Allocate NOP IN and NOP OUT UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsCreateNopCommandDesc( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UTP_TRD *Trd + ) +{ + UINT8 *CommandDesc; + UINTN TotalLen; + UTP_NOP_OUT_UPIU *NopOutUpiu; + + ASSERT((Private != NULL) && (Trd != NULL)); + + TotalLen = ROUNDUP8(sizeof(UTP_NOP_OUT_UPIU)) + ROUNDUP8(sizeof(UTP_NOP_IN_UPIU)); + CommandDesc = UfsAllocateMem(Private->Pool, TotalLen); + if (CommandDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NopOutUpiu = (UTP_NOP_OUT_UPIU *)CommandDesc; + + NopOutUpiu->TaskTag = Private->TaskTag++; + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Nop Out UPIU *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = 0x00; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->Ocs = UFS_HC_TRD_OCS_INIT_VALUE; + Trd->UcdBa = (UINT32)RShiftU64((UINT64)(UINTN)NopOutUpiu, 7); + Trd->UcdBaU = (UINT32)RShiftU64((UINT64)(UINTN)NopOutUpiu, 32); + Trd->RuL = (UINT16)DivU64x32((UINT64)ROUNDUP8 (sizeof(UTP_NOP_IN_UPIU)), + sizeof(UINT32), NULL); + Trd->RuO = (UINT16)DivU64x32((UINT64)ROUNDUP8 (sizeof(UTP_NOP_OUT_UPIU)), + sizeof(UINT32), NULL); + + return EFI_SUCCESS; +} + +/** + Find out available slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[out] Slot The available slot. + + @retval EFI_SUCCESS The available slot was found successfully. + +**/ +EFI_STATUS +UfsFindAvailableSlotInTrl( + IN __attribute__((__unused__)) UFS_PEIM_HC_PRIVATE_DATA *Private, + OUT UINT8 *Slot + ) +{ + ASSERT((Private != NULL) && (Slot != NULL)); + + // + // The simplest algo to always use slot 0. + // TODO: enhance it to support async transfer with multiple slot. + // + *Slot = 0; + + return EFI_SUCCESS; +} + +/** + Find out available slot in task management transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[out] Slot The available slot. + + @retval EFI_SUCCESS The available slot was found successfully. + +**/ +EFI_STATUS +UfsFindAvailableSlotInTmrl( + IN __attribute__((__unused__)) UFS_PEIM_HC_PRIVATE_DATA *Private, + OUT UINT8 *Slot + ) +{ + ASSERT((Private != NULL) && (Slot != NULL)); + + // + // The simplest algo to always use slot 0. + // TODO: enhance it to support async transfer with multiple slot. + // + *Slot = 0; + + return EFI_SUCCESS; +} + +/** + Start specified slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Slot The slot to be started. + +**/ +VOID +UfsStartExecCmd( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + UINTN UfsHcBase; + UINTN Address; + UINT32 Data; + + UfsHcBase = Private->UfsHcBase; + + Address = UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + Data = read32((void *)Address); + if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) { + write32((void *)Address, UFS_HC_UTRLRSR); + } + + Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + write32((void *)Address, BIT0 << Slot); +} + +/** + Stop specified slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Slot The slot to be stop. + +**/ +VOID +UfsStopExecCmd( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + UINTN UfsHcBase; + UINTN Address; + UINT32 Data; + + UfsHcBase = Private->UfsHcBase; + + Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Data = read32((void *)Address); + if ((Data & (BIT0 << Slot)) != 0) { + Address = UfsHcBase + UFS_HC_UTRLCLR_OFFSET; + Data = read32((void *)Address); + write32((void *)Address, (Data & ~(BIT0 << Slot))); + } +} + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDescInternal( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UINT16 ReturnDataSize; + + ZeroMem(&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + Packet.DataDirection = UfsDataIn; + Packet.InDataBuffer = Descriptor; + Packet.InTransferLength = DescSize; + Packet.Opcode = UtpQueryFuncOpcodeRdDesc; + } else { + Packet.DataDirection = UfsDataOut; + Packet.OutDataBuffer = Descriptor; + Packet.OutTransferLength = DescSize; + Packet.Opcode = UtpQueryFuncOpcodeWrDesc; + } + Packet.DescId = DescId; + Packet.Index = Index; + Packet.Selector = Selector; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl(Private, &Slot); + if (EFI_ERROR(Status)) { + return Status; + } + + Trd = ((UTP_TRD *)Private->UtpTrlBase) + Slot; + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateDMCommandDesc(Private, &Packet, Trd); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64((UINT64)Trd->UcdBaU, 32) | LShiftU64((UINT64)Trd->UcdBa, 7)); + QueryResp = (UTP_QUERY_RESP_UPIU *)(CmdDescBase + Trd->RuO * sizeof(UINT32)); + CmdDescSize = Trd->RuO * sizeof(UINT32) + Trd->RuL * sizeof(UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd(Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet(Address, BIT0 << Slot, 0, Packet.Timeout); + if (EFI_ERROR(Status)) { + goto Exit; + } + + Status = UfsWaitMemSet8((UINTN)&QueryResp->QueryResp, 0xFF, 0, TIMEOUT_WMS_8); + if (EFI_ERROR(Status)) { + DumpQueryResponseResult(QueryResp->QueryResp); + DEBUG_UFS((EFI_D_VERBOSE, "UFS ERROR: QueryResp->QueryResp = 0x%x\n", QueryResp->QueryResp)); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + ReturnDataSize = QueryResp->Tsf.Length; + SwapLittleEndianToBigEndian((UINT8 *)&ReturnDataSize, sizeof(UINT16)); + + if (Read) { + CopyMem(Packet.InDataBuffer, (QueryResp + 1), ReturnDataSize); + Packet.InTransferLength = ReturnDataSize; + } else { + Packet.OutTransferLength = ReturnDataSize; + } + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd(Private, Slot); + UfsFreeMem(Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDesc( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ) +{ + EFI_STATUS Status; + UINT32 TryIndex; + + Status = EFI_SUCCESS; + for (TryIndex = 0; TryIndex < 3; TryIndex++) { + Status = UfsRwDeviceDescInternal(Private, Read, DescId, Index, Selector, Descriptor, DescSize); + if (!EFI_ERROR(Status)) { + break; + } + DEBUG_UFS((EFI_D_VERBOSE, "ERROR:UfsRwDeviceDescInternal (0x%x) Status = %d\n", TryIndex, Status)); + } + + return Status; +} + + +/** + Read or write specified attribute of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] AttrId The ID of Attribute. + @param[in] Index The Index of Attribute. + @param[in] Selector The Selector of Attribute. + @param[in, out] Attributes The value of Attribute to be read or written. + + @retval EFI_SUCCESS The Attribute was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the Attribute. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the Attribute. + +**/ +EFI_STATUS +UfsRwAttributes ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 AttrId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UINT32 ReturnData; + + ZeroMem(&Packet, sizeof(UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + Packet.DataDirection = UfsDataIn; + Packet.Opcode = UtpQueryFuncOpcodeRdAttr; + } else { + Packet.DataDirection = UfsDataOut; + Packet.Opcode = UtpQueryFuncOpcodeWrAttr; + } + Packet.DescId = AttrId; + Packet.Index = Index; + Packet.Selector = Selector; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl(Private, &Slot); + if (EFI_ERROR(Status)) { + return Status; + } + + Trd = ((UTP_TRD *)Private->UtpTrlBase) + Slot; + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateDMCommandDesc(Private, &Packet, Trd); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64((UINT64)Trd->UcdBaU, 32) | LShiftU64((UINT64)Trd->UcdBa, 7)); + QueryResp = (UTP_QUERY_RESP_UPIU *)(CmdDescBase + Trd->RuO * sizeof(UINT32)); + CmdDescSize = Trd->RuO * sizeof(UINT32) + Trd->RuL * sizeof(UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd(Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet(Address, BIT0 << Slot, 0, Packet.Timeout); + if (EFI_ERROR(Status)) { + goto Exit; + } + + Status = UfsWaitMemSet8((UINTN)&QueryResp->QueryResp, 0xFF, 0, TIMEOUT_WMS_8); + if (EFI_ERROR(Status)) { + DumpQueryResponseResult(QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + ReturnData = QueryResp->Tsf.Value; + SwapLittleEndianToBigEndian((UINT8 *)&ReturnData, sizeof (UINT32)); + *Attributes = ReturnData; + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd(Private, Slot); + UfsFreeMem(Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Read or write specified flag of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] FlagId The ID of flag to be read or written. + @param[in, out] Value The value to set or clear flag. + + @retval EFI_SUCCESS The flag was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the flag. + +**/ +EFI_STATUS +UfsRwFlags( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 FlagId, + IN OUT UINT8 *Value + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + + if (Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem(&Packet, sizeof(UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + ASSERT(Value != NULL); + Packet.DataDirection = UfsDataIn; + Packet.Opcode = UtpQueryFuncOpcodeRdFlag; + } else { + Packet.DataDirection = UfsDataOut; + if (*Value == 1) { + Packet.Opcode = UtpQueryFuncOpcodeSetFlag; + } else if (*Value == 0) { + Packet.Opcode = UtpQueryFuncOpcodeClrFlag; + } else { + return EFI_INVALID_PARAMETER; + } + } + Packet.DescId = FlagId; + Packet.Index = 0; + Packet.Selector = 0; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl(Private, &Slot); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Fill transfer request descriptor to this slot. + // + Trd = ((UTP_TRD *)Private->UtpTrlBase) + Slot; + Status = UfsCreateDMCommandDesc(Private, &Packet, Trd); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64((UINT64)Trd->UcdBaU, 32) | LShiftU64((UINT64)Trd->UcdBa, 7)); + QueryResp = (UTP_QUERY_RESP_UPIU *)(CmdDescBase + Trd->RuO * sizeof(UINT32)); + CmdDescSize = Trd->RuO * sizeof(UINT32) + Trd->RuL * sizeof(UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd(Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet(Address, BIT0 << Slot, 0, Packet.Timeout); + if (EFI_ERROR(Status)) { + goto Exit; + } + + Status = UfsWaitMemSet8((UINTN)&QueryResp->QueryResp, 0xFF, 0, TIMEOUT_WMS_8); + if (EFI_ERROR(Status)) { + DumpQueryResponseResult(QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + *Value = (UINT8)QueryResp->Tsf.Value; + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd(Private, Slot); + UfsFreeMem(Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Set specified flag to 1 on a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be set. + + @retval EFI_SUCCESS The flag was set successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag. + +**/ +EFI_STATUS +UfsSetFlag( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId + ) +{ + EFI_STATUS Status; + UINT8 Value; + + Value = 1; + Status = UfsRwFlags(Private, FALSE, FlagId, &Value); + + return Status; +} + +/** + Clear specified flag to 0 on a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be cleared. + + @retval EFI_SUCCESS The flag was cleared successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to clear the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of clearing the flag. + +**/ +EFI_STATUS +UfsClearFlag( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId + ) +{ + EFI_STATUS Status; + UINT8 Value; + + Value = 0; + Status = UfsRwFlags(Private, FALSE, FlagId, &Value); + + return Status; +} + +/** + Read specified flag from a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be read. + @param[out] Value The flag's value. + + @retval EFI_SUCCESS The flag was read successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to read the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of reading the flag. + +**/ +EFI_STATUS +UfsReadFlag( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId, + OUT UINT8 *Value + ) +{ + EFI_STATUS Status; + + Status = UfsRwFlags(Private, TRUE, FlagId, Value); + + return Status; +} + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmdsInternal( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT8 Slot; + UTP_TRD *Trd; + UTP_NOP_IN_UPIU *NopInUpiu; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UINTN Address; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl(Private, &Slot); + if (EFI_ERROR(Status)) { + return Status; + } + + Trd = ((UTP_TRD *)Private->UtpTrlBase) + Slot; + Status = UfsCreateNopCommandDesc(Private, Trd); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64((UINT64)Trd->UcdBaU, 32) | LShiftU64((UINT64)Trd->UcdBa, 7)); + NopInUpiu = (UTP_NOP_IN_UPIU *)(CmdDescBase + Trd->RuO * sizeof(UINT32)); + CmdDescSize = Trd->RuO * sizeof(UINT32) + Trd->RuL * sizeof(UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd(Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet(Address, BIT0 << Slot, 0, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + goto Exit; + } + + Status = UfsWaitMemSet8((UINTN)&NopInUpiu->Resp, 0xFF, 0, TIMEOUT_WMS_8); + if (EFI_ERROR(Status)) { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd(Private, Slot); + UfsFreeMem(Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmds( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 TryIndex; + + Status = EFI_SUCCESS; + for (TryIndex = 0; TryIndex < 3; TryIndex++) { + Status = UfsExecNopCmdsInternal(Private); + if (!EFI_ERROR(Status)) { + break; + } + DEBUG_UFS((EFI_D_VERBOSE, "ERROR:UfsExecNopCmdsInternal (0x%x) Status = %d\n", TryIndex, Status)); + } + + return Status; +} + +/** + Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the + UFS device. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsExecScsiCmdsInternal( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT UFS_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UTP_RESPONSE_UPIU *Response; + UINT16 SenseDataLen; + UINT32 ResTranCount; + VOID *Buffer = NULL; + VOID *FreeBuf = NULL; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl(Private, &Slot); + if (EFI_ERROR(Status)) { + return Status; + } + + Trd = ((UTP_TRD *)Private->UtpTrlBase) + Slot; + + // + // Alloc for 4 bytes aligned DataBuffer memory + // + if (Packet->DataDirection == UfsDataIn) { + if (Packet->InTransferLength && (((UINTN)Packet->InDataBuffer) % 4)) { + DEBUG_UFS((EFI_D_VERBOSE, "Read data buffer is not 4 BYTE alignment \n")); + Buffer = Packet->InDataBuffer; + Status = alloc_align(&FreeBuf, &Packet->InDataBuffer, Packet->InTransferLength, 4); + ZeroMem (Packet->InDataBuffer, Packet->InTransferLength); + } + } else { + if (Packet->OutTransferLength && (((UINTN)Packet->OutDataBuffer) % 4)) { + DEBUG_UFS((EFI_D_VERBOSE, "Write data buffer is not 4 BYTE alignment \n")); + Buffer = Packet->OutDataBuffer; + Status = alloc_align(&FreeBuf, &Packet->OutDataBuffer, Packet->OutTransferLength, 4); + CopyMem(Packet->OutDataBuffer, Buffer, Packet->OutTransferLength); + } + } + + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateScsiCommandDesc(Private, Lun, Packet, Trd); + if (EFI_ERROR(Status)) { + if (FreeBuf) + free(FreeBuf); + return Status; + } + + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64((UINT64)Trd->UcdBaU, 32) | LShiftU64((UINT64)Trd->UcdBa, 7)); + CmdDescSize = Trd->PrdtO * sizeof(UINT32) + Trd->PrdtL * sizeof(UTP_TR_PRD); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd(Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet(Address, BIT0 << Slot, 0, Packet->Timeout); + if (EFI_ERROR(Status)) { + goto Exit; + } + + // + // Get sense data if exists + // + Response = (UTP_RESPONSE_UPIU *)(CmdDescBase + Trd->RuO * sizeof (UINT32)); + SenseDataLen = Response->SenseDataLen; + SwapLittleEndianToBigEndian((UINT8 *)&SenseDataLen, sizeof (UINT16)); + + if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) { + CopyMem(Packet->SenseData, Response->SenseData, SenseDataLen); + Packet->SenseDataLength = (UINT8)SenseDataLen; + } + + // + // Check the transfer request result. + // + Packet->TargetStatus = Response->Status; + Status = UfsWaitMemSet8((UINTN)&Response->Response, 0xFF, 0, TIMEOUT_WMS_8); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsExecScsiCmds() fails with Target Failure(0x%x)\n", Response->Response)); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + DEBUG_UFS((EFI_D_VERBOSE, "SCSI Request Packet DataDirection = %d\n", Packet->DataDirection)); + if (Trd->Ocs == 0) { + if (Packet->DataDirection == UfsDataIn) { + if ((Response->Flags & BIT5) == BIT5) { + ResTranCount = Response->ResTranCount; + SwapLittleEndianToBigEndian((UINT8 *)&ResTranCount, sizeof(UINT32)); + Packet->InTransferLength -= ResTranCount; + } + } else if (Packet->DataDirection == UfsDataOut) { + if ((Response->Flags & BIT5) == BIT5) { + ResTranCount = Response->ResTranCount; + SwapLittleEndianToBigEndian((UINT8 *)&ResTranCount, sizeof(UINT32)); + Packet->OutTransferLength -= ResTranCount; + } + } + } else { + Status = EFI_DEVICE_ERROR; + } + + if (Buffer) { + if (Packet->DataDirection == UfsDataIn) { + CopyMem(Buffer, Packet->InDataBuffer, Packet->InTransferLength); + Packet->InDataBuffer = Buffer; + } else { + CopyMem(Buffer, Packet->OutDataBuffer, Packet->OutTransferLength); + Packet->OutDataBuffer = Buffer; + } + } + +Exit: + UfsStopExecCmd(Private, Slot); + UfsFreeMem(Private->Pool, CmdDescBase, CmdDescSize); + if (FreeBuf) { + free(FreeBuf); + FreeBuf = NULL; + } + + return Status; +} + +EFI_STATUS +UfsExecScsiCmds( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT UFS_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT32 TryIndex; + + Status = EFI_SUCCESS; + for (TryIndex = 0; TryIndex < 10; TryIndex++) { + Status = UfsExecScsiCmdsInternal(Private, Lun, Packet); + if (!EFI_ERROR(Status)) { + break; + } + DEBUG_UFS((EFI_D_VERBOSE, "ERROR:UfsExecScsiCmdsInternal (0x%x) Status = %d\n", TryIndex, Status)); + } + + return Status; +} + +/** + Sent UIC DME_LINKSTARTUP command to start the link startup procedure. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] UicOpcode The opcode of the UIC command. + @param[in] Arg1 The value for 1st argument of the UIC command. + @param[in] Arg2 The value for 2nd argument of the UIC command. + @param[in] Arg3 The value for 3rd argument of the UIC command. + @param[out]Arg3Out Teh value for 3rd argument for read operation. + + @return EFI_SUCCESS Successfully execute this UIC command and detect attached UFS device. + @return EFI_DEVICE_ERROR Fail to execute this UIC command and detect attached UFS device. + @return EFI_NOT_FOUND The presence of the UFS device isn't detected. + +**/ +EFI_STATUS +UfsExecUicCommands ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 UicOpcode, + IN UINT32 Arg1, + IN UINT32 Arg2, + IN UINT32 Arg3, + OUT UINT32 *Arg3Out + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + UINTN UfsHcBase; + + UfsHcBase = Private->UfsHcBase; + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Data = read32((void *)Address); + if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) { + // + // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first. + // + write32((void *)Address, UFS_HC_IS_UCCS); + } + + // + // Host software shall only set the UICCMD if HCS.UCRDY is set to 1. + // + Address = Private->UfsHcBase + UFS_HC_STATUS_OFFSET; + Status = UfsWaitMemSet(Address, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsExecUicCommands- UfsWaitMemSet = %d\n", Status)); + return Status; + } + + // + // When programming UIC command registers, host software shall set the register UICCMD + // only after all the UIC command argument registers (UICCMDARG1, UICCMDARG2 and UICCMDARG3) + // are set. + // + Address = UfsHcBase + UFS_HC_UCMD_ARG1_OFFSET; + write32((void *)Address, Arg1); + + Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET; + write32((void *)Address, Arg2); + + Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET; + write32((void *)Address, Arg3); + + + + Address = UfsHcBase + UFS_HC_UIC_CMD_OFFSET; + write32((void *)Address, (UINT32)UicOpcode); + + // + // UFS 2.0 spec section 5.3.1 Offset:0x20 IS.Bit10 UIC Command Completion Status (UCCS) + // This bit is set to '1' by the host controller upon completion of a UIC command. + // + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Data = read32((void *)Address); + Status = UfsWaitMemSet(Address, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsExecUicCommands 2 UfsWaitMemSet = %d\n", Status)); + return Status; + } + + // Clear completion Status. + write32((void *)Address, UFS_HC_IS_UCCS); + Data = read32((void *)Address); + if ((Data & UFS_HC_IS_UCCS) != 0) { + DEBUG_UFS((EFI_D_VERBOSE, " Clear completion Status.\n")); + return EFI_DEVICE_ERROR; + } + if (UicOpcode != UfsUicDmeReset) { + Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET; + Data = read32((void *)Address); + if ((Data & 0xFF) != 0) { + DumpUicCmdExecResult(UicOpcode, (UINT8)(Data & 0xFF)); + DEBUG_UFS((EFI_D_VERBOSE, "UfsExecUicCommands Data= 0x%x\n", Data)); + return EFI_DEVICE_ERROR; + } + } + + if ((Arg3Out != NULL) && ((UicOpcode == UfsUicDmeGet) || (UicOpcode == UfsUicDmePeerGet))) { + Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET; + *Arg3Out = read32((void *)Address); + } + + return EFI_SUCCESS; +} + +/** + Change power mode based on speed mode. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Mode The speed mode, 2, 5 for PWM mode, 4 is for high speed mode. + + @return EFI_SUCCESS Successfully changed power mode. + @return EFI_DEVICE_ERROR Fail to change power mode. + +**/ +EFI_STATUS +UfsChangePowerMode( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Mode + ) +{ + EFI_STATUS Status; + UINT32 Data; + UINTN UfsHcBase; + UFS_HC_STATUS *UfsHcStatus; + + DEBUG_UFS((EFI_D_VERBOSE, "\nPower Mode Change Sequence...%d\n", Mode)); + + UfsHcBase = Private->UfsHcBase; + + // Read the HC interrupt status . + Data = read32((void *)(UfsHcBase + UFS_HC_IS_OFFSET)); + DEBUG_UFS((EFI_D_VERBOSE, "UFS HC interrupt status, default value = 0x%x\n", Data)); + if ((Data & UFS_HC_IS_UE) != 0) { + write32((void *)(UfsHcBase + UFS_HC_IS_OFFSET), UFS_HC_IS_UE); + } + + if ((Data & UFS_HC_IS_UCCS) != 0) { + write32((void *)(UfsHcBase + UFS_HC_IS_OFFSET), UFS_HC_IS_UCCS); + } + + if ((Data & UFS_HC_IS_UPMS) != 0) { + write32((void *)(UfsHcBase + UFS_HC_IS_OFFSET), UFS_HC_IS_UPMS); + } + + // Check UIC Power Mode Status is clear. + Status = UfsWaitMemSet(UfsHcBase + UFS_HC_IS_OFFSET, UFS_HC_IS_UPMS, 0, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "Check UIC Power Mode Status, it is not cleared.\n")); + return EFI_DEVICE_ERROR; + } + DEBUG_UFS((EFI_D_VERBOSE, "Check UIC Power Mode Status, it is clear.\n")); + + DEBUG_UFS((EFI_D_VERBOSE, "Configuring Unipro PHY Adaptor Layer.\n")); + + if ((Mode == 2) || (Mode == 5)) { // PWM + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_TX_GEAR << 16, 0, PWM_G1, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_TX_GEAR: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_RX_GEAR << 16, 0, PWM_G1, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_RX_GEAR: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + } else { // HS + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_TX_GEAR << 16, 0, HS_G2, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_TX_GEAR: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_RX_GEAR << 16, 0, HS_G2, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_RX_GEAR: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_ACTIVE_TX_DATA_LANES << 16, 0, PA_AVAILABLE_TX_LANES, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_ACTIVE_TX_DATA_LANES: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_ACTIVE_RX_DATA_LANES << 16, 0, PA_AVAILABLE_RX_LANES, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_ACTIVE_RX_DATA_LANES: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_HS_SERIES << 16, 0, HS_SERIES_A, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_HS_SERIES: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_TX_TERMINATION << 16, 0, TERMINATION_ON, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_TX_TERMINATION: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_RX_TERMINATION << 16, 0, TERMINATION_ON, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_RX_TERMINATION: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_SCRAMBLING << 16, 0, SCRAMBLING_OFF, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_SCRAMBLING: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + UfsExecUicCommands(Private, UfsUicDmeSet, 0xd0410000, 0, 0x1FFF, NULL); // DME_LocalFC0ProtectionTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0xd0420000, 0, 0xFFFF, NULL); // DME_LocalTC0ReplayTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0xd0430000, 0, 0x7FFF, NULL); // DME_LocalAFC0ReqTimeOutVal + + UfsExecUicCommands(Private, UfsUicDmeSet, 0xd0440000, 0, 0xFFF, NULL); // DME_LocalFC1ProtectionTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0xd0450000, 0, 0xFFFF, NULL); // DME_LocalTC1ReplayTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0xd0460000, 0, 0x7FFF, NULL); // DME_LocalAFC1ReqTimeOutVal + + // PWRModeUserData 0 ~ 5 + UfsExecUicCommands(Private, UfsUicDmeSet, 0x15b0 << 16, 0, 0x1FFF, NULL); // DL_FC0ProtectionTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0x15b1 << 16, 0, 0xFFFF, NULL); // DL_TC0ReplayTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0x15b2 << 16, 0, 0x7FFF, NULL); // DL_AFC0ReqTimeOutVal + + UfsExecUicCommands(Private, UfsUicDmeSet, 0x15b3 << 16, 0, 0x1FFF, NULL); // DL_FC1ProtectionTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0x15b4 << 16, 0, 0xFFFF, NULL); // DL_TC1ReplayTimeOutVal + UfsExecUicCommands(Private, UfsUicDmeSet, 0x15b5 << 16, 0, 0x7FFF, NULL); // DL_AFC1ReqTimeOutVal + + Status = UfsExecUicCommands(Private, UfsUicDmeSet, PA_PWR_MODE << 16, 0, ((Mode & 0x0F) << 4) | (Mode & 0x0F), NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "PA_PWR_MODE: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = UfsWaitMemSet(UfsHcBase + UFS_HC_IS_OFFSET, UFS_HC_IS_UPMS, UFS_HC_IS_UPMS, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, " UFS_HC_IS_UPMS UfsWaitMemSet = %d\n", Status)); + return Status; + } + + Data = read32((void *)(UfsHcBase + UFS_HC_IS_OFFSET)); + DEBUG_UFS((EFI_D_VERBOSE, "After PA_PWR_MODE, Interrupt Status = 0x%x\n", Data)); + + // Clear UIC Power Mode Status + write32((void *)(UfsHcBase + UFS_HC_IS_OFFSET), UFS_HC_IS_UPMS); + + Status = UfsWaitMemSet(UfsHcBase + UFS_HC_IS_OFFSET, UFS_HC_IS_UPMS, 0, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "Error: UIC Power Mode Status not clear.\n")); + return Status; + } + DEBUG_UFS((EFI_D_VERBOSE, " UIC Power Mode Status is cleared\n")); + + Data = read32((void *)(UfsHcBase + UFS_HC_STATUS_OFFSET)); + UfsHcStatus = (UFS_HC_STATUS *) &Data; + if (UfsHcStatus->Upmcrs == 1) { + DEBUG_UFS((EFI_D_VERBOSE, "PWR_LOCAL, Success\n")); + } else if (UfsHcStatus->Upmcrs == 0) { + DEBUG_UFS((EFI_D_VERBOSE, "PWR_OK. Expecting PWR_LOCAL.\n")); + } else if (UfsHcStatus->Upmcrs == 2) { + DEBUG_UFS((EFI_D_VERBOSE, "PWR_REMOTE cannot be set by host! Spec does not permit.\n")); + } else if (UfsHcStatus->Upmcrs == 3) { + DEBUG_UFS((EFI_D_VERBOSE, "PWR_BUSY cannot be set by host! Spec does not permit.\n")); + } else if (UfsHcStatus->Upmcrs == 4) { + DEBUG_UFS((EFI_D_VERBOSE, "PWR_ERROR_CAP.\n")); + } else if (UfsHcStatus->Upmcrs == 5) { + DEBUG_UFS((EFI_D_VERBOSE, "PWR_FATAL_ERROR.\n")); + } else { + DEBUG_UFS((EFI_D_VERBOSE, "Check register access. Value is reserved value.\n")); + return EFI_DEVICE_ERROR; + } + + Status = UfsWaitMemSet(UfsHcBase + UFS_HC_STATUS_OFFSET, UFS_HC_HCS_DP, UFS_HC_HCS_DP, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "Device not present.\n")); + return Status; + } + + DEBUG_UFS((EFI_D_VERBOSE, "Power mode change sequence passed!\n")); + + return EFI_SUCCESS; +} + +/** + Enable the UFS host controller for accessing. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS host controller enabling was executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while enabling the UFS host controller. + +**/ +EFI_STATUS +UfsEnableHostController ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + + // + // UFS 2.0 spec section 7.1.1 - Host Controller Initialization + // + // Reinitialize the UFS host controller if HCE bit of HC register is set. + // + Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET; + Data = read32((void *)Address); + if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) { + return EFI_SUCCESS; + } + + // + // Write a 1 to the HCE register to enable the UFS host controller. + // + write32((void *)Address, UFS_HC_HCE_EN); + // + // Wait until HCE is read as '1' before continuing. + // + Status = UfsWaitMemSet(Address, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Detect if a UFS device attached. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS device detection was executed successfully. + @retval EFI_NOT_FOUND Not found a UFS device attached. + @retval EFI_DEVICE_ERROR A device error occurred while detecting the UFS device. + +**/ +EFI_STATUS +UfsDeviceDetection( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UINTN Retry; + EFI_STATUS Status; + UINT32 Data; + + Data = read32((void *)(Private->UfsHcBase + UFS_HC_STATUS_OFFSET)); + if ((Data & UFS_HC_HCS_DP) == 1) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDeviceDetection: Already Found an attached UFS device\n")); + return EFI_SUCCESS; + } + + // + // Start UFS device detection. + // Try up to 3 times for establishing data link with device. + // + for (Retry = 0; Retry < 3; Retry++) { + Status = UfsExecUicCommands(Private, UfsUicDmeLinkStartup, 0, 0, 0, NULL); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDeviceDetection: UfsExecUicCommands = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + + // + // Check value of HCS.DP and make sure that there is a device attached to the Link. + // + Data = read32((void *)(Private->UfsHcBase + UFS_HC_STATUS_OFFSET)); + if ((Data & UFS_HC_HCS_DP) == 0) { + Status = UfsWaitMemSet(Private->UfsHcBase + UFS_HC_IS_OFFSET, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDeviceDetection: UfsWaitMemSet = %d\n", Status)); + return EFI_DEVICE_ERROR; + } + } else { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDeviceDetection: Found an attached UFS device\n")); + return EFI_SUCCESS; + } + } + + DEBUG_UFS((EFI_D_VERBOSE, "UfsDeviceDetection: Not found UFS device\n")); + + return EFI_NOT_FOUND; +} + +/** + Initialize UFS task management request list related h/w context. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS task management list was initialzed successfully. + @retval EFI_DEVICE_ERROR The initialization fails. + +**/ +EFI_STATUS +UfsInitTaskManagementRequestList( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UINTN Address; + UINT32 Data; + UINT8 Nutmrs; + EFI_PHYSICAL_ADDRESS Buffer; + EFI_STATUS status; + VOID *BufPage = NULL; + VOID *FreeBufPage = NULL; + + // + // Initial h/w and s/w context for future operations. + // + Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; + Data = read32((void *)Address); + Private->Capabilities = Data; + + // + // Allocate and initialize UTP Task Management Request List. + // + Nutmrs = (UINT8) (RShiftU64((Private->Capabilities & UFS_HC_CAP_NUTMRS), 16) + 1); + // + // PAA TODO check this + // + status = alloc_align(&FreeBufPage, &BufPage, (EFI_SIZE_TO_PAGES(Nutmrs * sizeof(UTP_TMRD)) * EFI_PAGE_SIZE), 4096); + if (EFI_ERROR(status)) { + return EFI_DEVICE_ERROR; + } + Buffer = (UINTN)BufPage; + Private->FreeUtpTmrlBase = FreeBufPage; + + ZeroMem((VOID *)(UINTN)Buffer, EFI_PAGES_TO_SIZE(EFI_SIZE_TO_PAGES(Nutmrs * sizeof(UTP_TMRD)))); + + // + // Program the UTP Task Management Request List Base Address and UTP Task Management + // Request List Base Address with a 64-bit address allocated at step 6. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLBA_OFFSET; + write32((void *)Address, (UINT32)(UINTN)Buffer); + Address = Private->UfsHcBase + UFS_HC_UTMRLBAU_OFFSET; + write32((void *)Address, (UINT32)RShiftU64((UINT64)Buffer, 32)); + Private->UtpTmrlBase = (VOID *)(UINTN)Buffer; + Private->Nutmrs = Nutmrs; + + // + // Enable the UTP Task Management Request List by setting the UTP Task Management + // Request List RunStop Register (UTMRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; + write32((void *)Address, UFS_HC_UTMRLRSR); + + return EFI_SUCCESS; +} + +/** + Initialize UFS transfer request list related h/w context. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS transfer list was initialzed successfully. + @retval EFI_DEVICE_ERROR The initialization fails. + +**/ +EFI_STATUS +UfsInitTransferRequestList( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UINTN Address; + UINT32 Data; + UINT8 Nutrs; + EFI_PHYSICAL_ADDRESS Buffer; + EFI_STATUS status; + VOID *BufPage = NULL; + VOID *FreeBufPage = NULL; + + // + // Initial h/w and s/w context for future operations. + // + Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; + Data = read32((void *)Address); + Private->Capabilities = Data; + + // + // Allocate and initialize UTP Transfer Request List. + // + Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1); + + status = alloc_align(&FreeBufPage, &BufPage, (EFI_SIZE_TO_PAGES(Nutrs * sizeof(UTP_TRD)) * EFI_PAGE_SIZE), 4096); + if (EFI_ERROR(status)) { + return EFI_DEVICE_ERROR; + } + Buffer = (UINTN)BufPage; + Private->FreeUtpTrlBase = FreeBufPage; + + ZeroMem ((VOID *)(UINTN)Buffer, EFI_PAGES_TO_SIZE(EFI_SIZE_TO_PAGES(Nutrs * sizeof(UTP_TRD)))); + + // + // Program the UTP Transfer Request List Base Address and UTP Transfer Request List + // Base Address with a 64-bit address allocated at step 8. + // + Address = Private->UfsHcBase + UFS_HC_UTRLBA_OFFSET; + write32((void *)Address, (UINT32)(UINTN)Buffer); + Address = Private->UfsHcBase + UFS_HC_UTRLBAU_OFFSET; + write32((void *)Address, (UINT32)RShiftU64((UINT64)Buffer, 32)); + Private->UtpTrlBase = (VOID *)(UINTN)Buffer; + Private->Nutrs = Nutrs; + + // + // Enable the UTP Transfer Request List by setting the UTP Transfer Request List + // RunStop Register (UTRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + write32((void *)Address, UFS_HC_UTRLRSR); + + return EFI_SUCCESS; +} + +/** + Initialize the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +UfsControllerInit( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + Status = UfsEnableHostController(Private); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei: Enable Host Controller Fails, Status = %d\n", Status)); + return Status; + } + + Status = UfsDeviceDetection(Private); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei: Device Detection Fails, Status = %d\n", Status)); + return Status; + } + + Status = UfsInitTaskManagementRequestList(Private); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei: Task management list initialization Fails, Status = %d\n", Status)); + return Status; + } + + Status = UfsInitTransferRequestList(Private); + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei: Transfer list initialization Fails, Status = %d\n", Status)); + return Status; + } + + Status = UfsChangePowerMode(Private, 4);//HS mode + if (EFI_ERROR(Status)) { + DEBUG_UFS((EFI_D_VERBOSE, "UfsChangePowerMode switch HS error Status = %d\n", Status)); + return Status; + } + + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei Finished\n")); + return EFI_SUCCESS; +} + +/** + Stop the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully. + @retval Others A device error occurred while stopping the controller. + +**/ +EFI_STATUS +UfsControllerStop( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + + // + // Enable the UTP Task Management Request List by setting the UTP Task Management + // Request List RunStop Register (UTMRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; + write32((void *)Address, 0); + + // + // Enable the UTP Transfer Request List by setting the UTP Transfer Request List + // RunStop Register (UTRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + write32((void *)Address, 0); + + // + // Write a 0 to the HCE register in order to disable the host controller. + // + Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET; + Data = read32((void *)Address); + if ((Data & UFS_HC_HCE_EN) != UFS_HC_HCE_EN) { + return EFI_DEVICE_ERROR; + } + write32((void *)Address, 0); + + // + // Wait until HCE is read as '0' before continuing. + // + Status = UfsWaitMemSet(Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + DEBUG_UFS((EFI_D_VERBOSE, "UfsDevicePei: Stop the UFS Host Controller\n")); + + return EFI_SUCCESS; +} + diff --git a/drivers/ufs/UfsHci.h b/drivers/ufs/UfsHci.h new file mode 100644 index 0000000..ef65bf3 --- /dev/null +++ b/drivers/ufs/UfsHci.h @@ -0,0 +1,1374 @@ +/** @file + UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface + for upper layer application to execute UFS-supported SCSI cmds. + + Copyright (c) 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 _UFS_PASS_THRU_HCI_H_ +#define _UFS_PASS_THRU_HCI_H_ + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 + +#define BIT14 0x00004000 + +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 + +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 + +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +// +// Host Capabilities Register Offsets +// +#define UFS_HC_CAP_OFFSET 0x0000 // Controller Capabilities +#define UFS_HC_VER_OFFSET 0x0008 // Version +#define UFS_HC_DDID_OFFSET 0x0010 // Device ID and Device Class +#define UFS_HC_PMID_OFFSET 0x0014 // Product ID and Manufacturer ID +#define UFS_HC_AHIT_OFFSET 0x0018 // Auto-Hibernate Idle Timer +// +// Operation and Runtime Register Offsets +// +#define UFS_HC_IS_OFFSET 0x0020 // Interrupt Status +#define UFS_HC_IE_OFFSET 0x0024 // Interrupt Enable +#define UFS_HC_STATUS_OFFSET 0x0030 // Host Controller Status +#define UFS_HC_ENABLE_OFFSET 0x0034 // Host Controller Enable +#define UFS_HC_UECPA_OFFSET 0x0038 // Host UIC Error Code PHY Adapter Layer +#define UFS_HC_UECDL_OFFSET 0x003c // Host UIC Error Code Data Link Layer +#define UFS_HC_UECN_OFFSET 0x0040 // Host UIC Error Code Network Layer +#define UFS_HC_UECT_OFFSET 0x0044 // Host UIC Error Code Transport Layer +#define UFS_HC_UECDME_OFFSET 0x0048 // Host UIC Error Code DME +#define UFS_HC_UTRIACR_OFFSET 0x004c // UTP Transfer Request Interrupt Aggregation Control Register +// +// UTP Transfer Register Offsets +// +#define UFS_HC_UTRLBA_OFFSET 0x0050 // UTP Transfer Request List Base Address +#define UFS_HC_UTRLBAU_OFFSET 0x0054 // UTP Transfer Request List Base Address Upper 32-Bits +#define UFS_HC_UTRLDBR_OFFSET 0x0058 // UTP Transfer Request List Door Bell Register +#define UFS_HC_UTRLCLR_OFFSET 0x005c // UTP Transfer Request List CLear Register +#define UFS_HC_UTRLRSR_OFFSET 0x0060 // UTP Transfer Request Run-Stop Register +// +// UTP Task Management Register Offsets +// +#define UFS_HC_UTMRLBA_OFFSET 0x0070 // UTP Task Management Request List Base Address +#define UFS_HC_UTMRLBAU_OFFSET 0x0074 // UTP Task Management Request List Base Address Upper 32-Bits +#define UFS_HC_UTMRLDBR_OFFSET 0x0078 // UTP Task Management Request List Door Bell Register +#define UFS_HC_UTMRLCLR_OFFSET 0x007c // UTP Task Management Request List CLear Register +#define UFS_HC_UTMRLRSR_OFFSET 0x0080 // UTP Task Management Run-Stop Register +// +// UIC Command Register Offsets +// +#define UFS_HC_UIC_CMD_OFFSET 0x0090 // UIC Command Register +#define UFS_HC_UCMD_ARG1_OFFSET 0x0094 // UIC Command Argument 1 +#define UFS_HC_UCMD_ARG2_OFFSET 0x0098 // UIC Command Argument 2 +#define UFS_HC_UCMD_ARG3_OFFSET 0x009c // UIC Command Argument 3 +// +// UMA Register Offsets +// +#define UFS_HC_UMA_OFFSET 0x00b0 // Reserved for Unified Memory Extension + +#define UFS_HC_HCE_EN BIT0 +#define UFS_HC_HCS_DP BIT0 +#define UFS_HC_HCS_UCRDY BIT3 +#define UFS_HC_IS_UE BIT2 +#define UFS_HC_IS_UPMS BIT4 +#define UFS_HC_IS_ULSS BIT8 +#define UFS_HC_IS_UCCS BIT10 +#define UFS_HC_CAP_64ADDR BIT24 +#define UFS_HC_CAP_NUTMRS (BIT16 | BIT17 | BIT18) +#define UFS_HC_CAP_NUTRS (BIT0 | BIT1 | BIT2 | BIT3 | BIT4) +#define UFS_HC_UTMRLRSR BIT0 +#define UFS_HC_UTRLRSR BIT0 + +// +// The initial value of the OCS field of UTP TRD or TMRD descriptor +// defined in JEDEC JESD223 specification +// +#define UFS_HC_TRD_OCS_INIT_VALUE 0x0F + +// +// A maximum of length of 256KB is supported by PRDT entry +// +#define UFS_MAX_DATA_LEN_PER_PRD 0x40000 + +#define UFS_STORAGE_COMMAND_TYPE 0x01 + +#define UFS_REGULAR_COMMAND 0x00 +#define UFS_INTERRUPT_COMMAND 0x01 + +#define UFS_LUN_0 0x00 +#define UFS_LUN_1 0x01 +#define UFS_LUN_2 0x02 +#define UFS_LUN_3 0x03 +#define UFS_LUN_4 0x04 +#define UFS_LUN_5 0x05 +#define UFS_LUN_6 0x06 +#define UFS_LUN_7 0x07 +#define UFS_WLUN_REPORT_LUNS 0x81 +#define UFS_WLUN_UFS_DEV 0xD0 +#define UFS_WLUN_BOOT 0xB0 +#define UFS_WLUN_RPMB 0xC4 + +#pragma pack(1) + +// +// UFSHCI 2.0 Spec Section 5.2.1 Offset 00h: CAP - Controller Capabilities +// +typedef struct { + UINT8 Nutrs:4; // Number of UTP Transfer Request Slots + UINT8 Rsvd1:4; + + UINT8 NoRtt; // Number of outstanding READY TO TRANSFER (RTT) requests supported + + UINT8 Nutmrs:3; // Number of UTP Task Management Request Slots + UINT8 Rsvd2:4; + UINT8 AutoHs:1; // Auto-Hibernation Support + + UINT8 As64:1; // 64-bit addressing supported + UINT8 Oodds:1; // Out of order data delivery supported + UINT8 UicDmetms:1; // UIC DME_TEST_MODE command supported + UINT8 Ume:1; // Reserved for Unified Memory Extension + UINT8 Rsvd4:4; +} UFS_HC_CAP; + +// +// UFSHCI 2.0 Spec Section 5.2.2 Offset 08h: VER - UFS Version +// +typedef struct { + UINT8 Vs:4; // Version Suffix + UINT8 Mnr:4; // Minor version number + + UINT8 Mjr; // Major version number + + UINT16 Rsvd1; +} UFS_HC_VER; + +// +// UFSHCI 2.0 Spec Section 5.2.3 Offset 10h: HCPID - Host Controller Product ID +// +#define UFS_HC_PID UINT32 + +// +// UFSHCI 2.0 Spec Section 5.2.4 Offset 14h: HCMID - Host Controller Manufacturer ID +// +#define UFS_HC_MID UINT32 + +// +// UFSHCI 2.0 Spec Section 5.2.5 Offset 18h: AHIT - Auto-Hibernate Idle Timer +// +typedef struct { + UINT32 Ahitv:10; // Auto-Hibernate Idle Timer Value + UINT32 Ts:3; // Timer scale + UINT32 Rsvd1:19; +} UFS_HC_AHIT; + +// +// UFSHCI 2.0 Spec Section 5.3.1 Offset 20h: IS - Interrupt Status +// +typedef struct { + UINT16 Utrcs:1; // UTP Transfer Request Completion Status + UINT16 Udepri:1; // UIC DME_ENDPOINT_RESET Indication + UINT16 Ue:1; // UIC Error + UINT16 Utms:1; // UIC Test Mode Status + + UINT16 Upms:1; // UIC Power Mode Status + UINT16 Uhxs:1; // UIC Hibernate Exit Status + UINT16 Uhes:1; // UIC Hibernate Enter Status + UINT16 Ulls:1; // UIC Link Lost Status + + UINT16 Ulss:1; // UIC Link Startup Status + UINT16 Utmrcs:1; // UTP Task Management Request Completion Status + UINT16 Uccs:1; // UIC Command Completion Status + UINT16 Dfes:1; // Device Fatal Error Status + + UINT16 Utpes:1; // UTP Error Status + UINT16 Rsvd1:3; + + UINT16 Hcfes:1; // Host Controller Fatal Error Status + UINT16 Sbfes:1; // System Bus Fatal Error Status + UINT16 Rsvd2:14; +} UFS_HC_IS; + +// +// UFSHCI 2.0 Spec Section 5.3.2 Offset 24h: IE - Interrupt Enable +// +typedef struct { + UINT16 Utrce:1; // UTP Transfer Request Completion Enable + UINT16 Udeprie:1; // UIC DME_ENDPOINT_RESET Enable + UINT16 Uee:1; // UIC Error Enable + UINT16 Utmse:1; // UIC Test Mode Status Enable + + UINT16 Upmse:1; // UIC Power Mode Status Enable + UINT16 Uhxse:1; // UIC Hibernate Exit Status Enable + UINT16 Uhese:1; // UIC Hibernate Enter Status Enable + UINT16 Ullse:1; // UIC Link Lost Status Enable + + UINT16 Ulsse:1; // UIC Link Startup Status Enable + UINT16 Utmrce:1; // UTP Task Management Request Completion Enable + UINT16 Ucce:1; // UIC Command Completion Enable + UINT16 Dfee:1; // Device Fatal Error Enable + + UINT16 Utpee:1; // UTP Error Enable + UINT16 Rsvd1:3; + + UINT16 Hcfee:1; // Host Controller Fatal Error Enable + UINT16 Sbfee:1; // System Bus Fatal Error Enable + UINT16 Rsvd2:14; +} UFS_HC_IE; + +// +// UFSHCI 2.0 Spec Section 5.3.3 Offset 30h: HCS - Host Controller Status +// +typedef struct { + UINT8 Dp:1; // Device Present + UINT8 UtrlRdy:1; // UTP Transfer Request List Ready + UINT8 UtmrlRdy:1; // UTP Task Management Request List Ready + UINT8 UcRdy:1; // UIC COMMAND Ready + UINT8 Rsvd1:4; + + UINT8 Upmcrs:3; // UIC Power Mode Change Request Status + UINT8 Rsvd2:1; // UIC Hibernate Exit Status Enable + UINT8 Utpec:4; // UTP Error Code + + UINT8 TtagUtpE; // Task Tag of UTP error + UINT8 TlunUtpE; // Target LUN of UTP error +} UFS_HC_STATUS; + +// +// UFSHCI 2.0 Spec Section 5.3.4 Offset 34h: HCE - Host Controller Enable +// +typedef struct { + UINT32 Hce:1; // Host Controller Enable + UINT32 Rsvd1:31; +} UFS_HC_ENABLE; + +// +// UFSHCI 2.0 Spec Section 5.3.5 Offset 38h: UECPA - Host UIC Error Code PHY Adapter Layer +// +typedef struct { + UINT32 Ec:5; // UIC PHY Adapter Layer Error Code + UINT32 Rsvd1:26; + UINT32 Err:1; // UIC PHY Adapter Layer Error +} UFS_HC_UECPA; + +// +// UFSHCI 2.0 Spec Section 5.3.6 Offset 3ch: UECDL - Host UIC Error Code Data Link Layer +// +typedef struct { + UINT32 Ec:15; // UIC Data Link Layer Error Code + UINT32 Rsvd1:16; + UINT32 Err:1; // UIC Data Link Layer Error +} UFS_HC_UECDL; + +// +// UFSHCI 2.0 Spec Section 5.3.7 Offset 40h: UECN - Host UIC Error Code Network Layer +// +typedef struct { + UINT32 Ec:3; // UIC Network Layer Error Code + UINT32 Rsvd1:28; + UINT32 Err:1; // UIC Network Layer Error +} UFS_HC_UECN; + +// +// UFSHCI 2.0 Spec Section 5.3.8 Offset 44h: UECT - Host UIC Error Code Transport Layer +// +typedef struct { + UINT32 Ec:7; // UIC Transport Layer Error Code + UINT32 Rsvd1:24; + UINT32 Err:1; // UIC Transport Layer Error +} UFS_HC_UECT; + +// +// UFSHCI 2.0 Spec Section 5.3.9 Offset 48h: UECDME - Host UIC Error Code +// +typedef struct { + UINT32 Ec:1; // UIC DME Error Code + UINT32 Rsvd1:30; + UINT32 Err:1; // UIC DME Error +} UFS_HC_UECDME; + +// +// UFSHCI 2.0 Spec Section 5.3.10 Offset 4Ch: UTRIACR - UTP Transfer Request Interrupt Aggregation Control Register +// +typedef struct { + UINT8 IaToVal; // Interrupt aggregation timeout value + + UINT8 IacTh:5; // Interrupt aggregation counter threshold + UINT8 Rsvd1:3; + + UINT8 Ctr:1; // Counter and Timer Reset + UINT8 Rsvd2:3; + UINT8 Iasb:1; // Interrupt aggregation status bit + UINT8 Rsvd3:3; + + UINT8 IapwEn:1; // Interrupt aggregation parameter write enable + UINT8 Rsvd4:6; + UINT8 IaEn:1; // Interrupt Aggregation Enable/Disable +} UFS_HC_UTRIACR; + +// +// UFSHCI 2.0 Spec Section 5.4.1 Offset 50h: UTRLBA - UTP Transfer Request List Base Address +// +typedef struct { + UINT32 Rsvd1:10; + UINT32 UtrlBa:22; // UTP Transfer Request List Base Address +} UFS_HC_UTRLBA; + +// +// UFSHCI 2.0 Spec Section 5.4.2 Offset 54h: UTRLBAU - UTP Transfer Request List Base Address Upper 32-bits +// +#define UFS_HC_UTRLBAU UINT32 + +// +// UFSHCI 2.0 Spec Section 5.4.3 Offset 58h: UTRLDBR - UTP Transfer Request List Door Bell Register +// +#define UFS_HC_UTRLDBR UINT32 + +// +// UFSHCI 2.0 Spec Section 5.4.4 Offset 5Ch: UTRLCLR - UTP Transfer Request List CLear Register +// +#define UFS_HC_UTRLCLR UINT32 + +#if 0 +// +// UFSHCI 2.0 Spec Section 5.4.5 Offset 60h: UTRLRSR - UTP Transfer Request List Run Stop Register +// +typedef struct { + UINT32 UtrlRsr:1; // UTP Transfer Request List Run-Stop Register + UINT32 Rsvd1:31; +} UFS_HC_UTRLRSR; +#endif + +// +// UFSHCI 2.0 Spec Section 5.5.1 Offset 70h: UTMRLBA - UTP Task Management Request List Base Address +// +typedef struct { + UINT32 Rsvd1:10; + UINT32 UtmrlBa:22; // UTP Task Management Request List Base Address +} UFS_HC_UTMRLBA; + +// +// UFSHCI 2.0 Spec Section 5.5.2 Offset 74h: UTMRLBAU - UTP Task Management Request List Base Address Upper 32-bits +// +#define UFS_HC_UTMRLBAU UINT32 + +// +// UFSHCI 2.0 Spec Section 5.5.3 Offset 78h: UTMRLDBR - UTP Task Management Request List Door Bell Register +// +typedef struct { + UINT32 UtmrlDbr:8; // UTP Task Management Request List Door bell Register + UINT32 Rsvd1:24; +} UFS_HC_UTMRLDBR; + +// +// UFSHCI 2.0 Spec Section 5.5.4 Offset 7Ch: UTMRLCLR - UTP Task Management Request List CLear Register +// +typedef struct { + UINT32 UtmrlClr:8; // UTP Task Management List Clear Register + UINT32 Rsvd1:24; +} UFS_HC_UTMRLCLR; + +#if 0 +// +// UFSHCI 2.0 Spec Section 5.5.5 Offset 80h: UTMRLRSR - UTP Task Management Request List Run Stop Register +// +typedef struct { + UINT32 UtmrlRsr:1; // UTP Task Management Request List Run-Stop Register + UINT32 Rsvd1:31; +} UFS_HC_UTMRLRSR; +#endif + +// +// UFSHCI 2.0 Spec Section 5.6.1 Offset 90h: UICCMD - UIC Command +// +typedef struct { + UINT32 CmdOp:8; // Command Opcode + UINT32 Rsvd1:24; +} UFS_HC_UICCMD; + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 94h: UICCMDARG1 - UIC Command Argument 1 +// +#define UFS_HC_UICCMD_ARG1 UINT32 + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 98h: UICCMDARG2 - UIC Command Argument 2 +// +#define UFS_HC_UICCMD_ARG2 UINT32 + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 9ch: UICCMDARG3 - UIC Command Argument 3 +// +#define UFS_HC_UICCMD_ARG3 UINT32 + +// +// UIC command opcodes +// +typedef enum { + UfsUicDmeGet = 0x01, + UfsUicDmeSet = 0x02, + UfsUicDmePeerGet = 0x03, + UfsUicDmePeerSet = 0x04, + UfsUicDmePwrOn = 0x10, + UfsUicDmePwrOff = 0x11, + UfsUicDmeEnable = 0x12, + UfsUicDmeReset = 0x14, + UfsUicDmeEndpointReset = 0x15, + UfsUicDmeLinkStartup = 0x16, + UfsUicDmeHibernateEnter = 0x17, + UfsUicDmeHibernateExit = 0x18, + UfsUicDmeTestMode = 0x1A +} UFS_UIC_OPCODE; + +// +// UTP Transfer Request Descriptor +// +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:24; + UINT32 Int:1; /* Interrupt */ + UINT32 Dd:2; /* Data Direction */ + UINT32 Rsvd2:1; + UINT32 Ct:4; /* Command Type */ + + // + // DW1 + // + UINT32 Rsvd3; + + // + // DW2 + // + UINT32 Ocs:8; /* Overall Command Status */ + UINT32 Rsvd4:24; + + // + // DW3 + // + UINT32 Rsvd5; + + // + // DW4 + // + UINT32 Rsvd6:7; + UINT32 UcdBa:25; /* UTP Command Descriptor Base Address */ + + // + // DW5 + // + UINT32 UcdBaU; /* UTP Command Descriptor Base Address Upper 32-bits */ + + // + // DW6 + // + UINT16 RuL; /* Response UPIU Length */ + UINT16 RuO; /* Response UPIU Offset */ + + // + // DW7 + // + UINT16 PrdtL; /* PRDT Length */ + UINT16 PrdtO; /* PRDT Offset */ +} UTP_TRD; + +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:2; + UINT32 DbAddr:30; /* Data Base Address */ + + // + // DW1 + // + UINT32 DbAddrU; /* Data Base Address Upper 32-bits */ + + // + // DW2 + // + UINT32 Rsvd2; + + // + // DW3 + // + UINT32 DbCount:18; /* Data Byte Count */ + UINT32 Rsvd3:14; +} UTP_TR_PRD; + +// +// UFS 2.0 Spec Section 10.5.3 - UTP Command UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x01*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 CmdSet:4; /* Command Set Type */ + UINT8 Rsvd1:4; + UINT8 Rsvd2; + UINT8 Rsvd3; + UINT8 Rsvd4; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd5; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 ExpDataTranLen; /* Expected Data Transfer Length - Big Endian */ + + // + // DW4 - DW7 + // + UINT8 Cdb[16]; +} UTP_COMMAND_UPIU; + +// +// UFS 2.0 Spec Section 10.5.4 - UTP Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x21*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 CmdSet:4; /* Command Set Type */ + UINT8 Rsvd1:4; + UINT8 Rsvd2; + UINT8 Response; /* Response */ + UINT8 Status; /* Status */ + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 ResTranCount; /* Residual Transfer Count - Big Endian */ + + // + // DW4 - DW7 + // + UINT8 Rsvd3[16]; + + // + // Data Segment - Sense Data + // + UINT16 SenseDataLen; /* Sense Data Length - Big Endian */ + UINT8 SenseData[18]; /* Sense Data */ +} UTP_RESPONSE_UPIU; + +// +// UFS 2.0 Spec Section 10.5.5 - UTP Data-Out UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x02*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be sent out + // + //UINT8 Data[]; /* Data to be sent out, maximum is 65535 bytes */ +} UTP_DATA_OUT_UPIU; + +// +// UFS 2.0 Spec Section 10.5.6 - UTP Data-In UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x22*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be read + // + //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */ +} UTP_DATA_IN_UPIU; + +// +// UFS 2.0 Spec Section 10.5.7 - UTP Ready-To-Transfer UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x31*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be read + // + //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */ +} UTP_RDY_TO_TRAN_UPIU; + +// +// UFS 2.0 Spec Section 10.5.8 - UTP Task Management Request UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x04*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1; + UINT8 TskManFunc; /* Task Management Function */ + UINT8 Rsvd2[2]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 InputParam1; /* Input Parameter 1 - Big Endian */ + + // + // DW4 + // + UINT32 InputParam2; /* Input Parameter 2 - Big Endian */ + + // + // DW5 + // + UINT32 InputParam3; /* Input Parameter 3 - Big Endian */ + + // + // DW6 - DW7 + // + UINT8 Rsvd4[8]; +} UTP_TM_REQ_UPIU; + +// +// UFS 2.0 Spec Section 10.5.9 - UTP Task Management Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x24*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[2]; + UINT8 Resp; /* Response */ + UINT8 Rsvd2; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 OutputParam1; /* Output Parameter 1 - Big Endian */ + + // + // DW4 + // + UINT32 OutputParam2; /* Output Parameter 2 - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd4[12]; +} UTP_TM_RESP_UPIU; + +// +// UTP Task Management Request Descriptor +// +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:24; + UINT32 Int:1; /* Interrupt */ + UINT32 Rsvd2:7; + + // + // DW1 + // + UINT32 Rsvd3; + + // + // DW2 + // + UINT32 Ocs:8; /* Overall Command Status */ + UINT32 Rsvd4:24; + + // + // DW3 + // + UINT32 Rsvd5; + + // + // DW4 - DW11 + // + UTP_TM_REQ_UPIU TmReq; /* Task Management Request UPIU */ + + // + // DW12 - DW19 + // + UTP_TM_RESP_UPIU TmResp; /* Task Management Response UPIU */ +} UTP_TMRD; + + +typedef struct { + UINT8 Opcode; + UINT8 DescId; + UINT8 Index; + UINT8 Selector; + UINT16 Rsvd1; + UINT16 Length; + UINT32 Value; + UINT32 Rsvd2; +} UTP_UPIU_TSF; + +// +// UFS 2.0 Spec Section 10.5.10 - UTP Query Request UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x16*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2; + UINT8 QueryFunc; /* Query Function */ + UINT8 Rsvd3[2]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd4; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 - 6 + // + UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */ + + // + // DW7 + // + UINT8 Rsvd5[4]; + + // + // Data Segment - Data to be transferred + // + //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */ +} UTP_QUERY_REQ_UPIU; + +#define QUERY_FUNC_STD_READ_REQ 0x01 +#define QUERY_FUNC_STD_WRITE_REQ 0x81 + +typedef enum { + UtpQueryFuncOpcodeNop = 0x00, + UtpQueryFuncOpcodeRdDesc = 0x01, + UtpQueryFuncOpcodeWrDesc = 0x02, + UtpQueryFuncOpcodeRdAttr = 0x03, + UtpQueryFuncOpcodeWrAttr = 0x04, + UtpQueryFuncOpcodeRdFlag = 0x05, + UtpQueryFuncOpcodeSetFlag = 0x06, + UtpQueryFuncOpcodeClrFlag = 0x07, + UtpQueryFuncOpcodeTogFlag = 0x08 +} UTP_QUERY_FUNC_OPCODE; + +// +// UFS 2.0 Spec Section 10.5.11 - UTP Query Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x36*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2; + UINT8 QueryFunc; /* Query Function */ + UINT8 QueryResp; /* Query Response */ + UINT8 Rsvd3; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 - 6 + // + UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */ + + // + // DW7 + // + UINT8 Rsvd4[4]; + + // + // Data Segment - Data to be transferred + // + //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */ +} UTP_QUERY_RESP_UPIU; + +typedef enum { + UfsUtpQueryResponseSuccess = 0x00, + UfsUtpQueryResponseParamNotReadable = 0xF6, + UfsUtpQueryResponseParamNotWriteable = 0xF7, + UfsUtpQueryResponseParamAlreadyWritten = 0xF8, + UfsUtpQueryResponseInvalidLen = 0xF9, + UfsUtpQueryResponseInvalidVal = 0xFA, + UfsUtpQueryResponseInvalidSelector = 0xFB, + UfsUtpQueryResponseInvalidIndex = 0xFC, + UfsUtpQueryResponseInvalidIdn = 0xFD, + UfsUtpQueryResponseInvalidOpc = 0xFE, + UfsUtpQueryResponseGeneralFailure = 0xFF +} UTP_QUERY_RESP_CODE; + +// +// UFS 2.0 Spec Section 10.5.12 - UTP Reject UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x3F*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[2]; + UINT8 Response; /* Response - 0x01 */ + UINT8 Rsvd2; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information - 0x00 */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT8 HdrSts; /* Basic Header Status */ + UINT8 Rsvd3; + UINT8 E2ESts; /* End-to-End Status */ + UINT8 Rsvd4; + + // + // DW4 - DW7 + // + UINT8 Rsvd5[16]; +} UTP_REJ_UPIU; + +// +// UFS 2.0 Spec Section 10.5.13 - UTP NOP OUT UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x00*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 - DW7 + // + UINT8 Rsvd4[20]; +} UTP_NOP_OUT_UPIU; + +// +// UFS 2.0 Spec Section 10.5.14 - UTP NOP IN UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x20*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2[2]; + UINT8 Resp; /* Response - 0x00 */ + UINT8 Rsvd3; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information - 0x00 */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 - DW7 + // + UINT8 Rsvd4[20]; +} UTP_NOP_IN_UPIU; + +// +// UFS Descriptors +// +typedef enum { + UfsDeviceDesc = 0x00, + UfsConfigDesc = 0x01, + UfsUnitDesc = 0x02, + UfsInterConnDesc = 0x04, + UfsStringDesc = 0x05, + UfsGeometryDesc = 0x07, + UfsPowerDesc = 0x08 +} UFS_DESC_IDN; + +// +// UFS 2.0 Spec Section 14.1.6.2 - Device Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 Device; + UINT8 DevClass; + UINT8 DevSubClass; + UINT8 Protocol; + UINT8 NumLun; + UINT8 NumWLun; + UINT8 BootEn; + UINT8 DescAccessEn; + UINT8 InitPowerMode; + UINT8 HighPriorityLun; + UINT8 SecureRemovalType; + UINT8 SecurityLun; + UINT8 BgOpsTermLat; + UINT8 InitActiveIccLevel; + UINT16 SpecVersion; + UINT16 ManufactureDate; + UINT8 ManufacturerName; + UINT8 ProductName; + UINT8 SerialName; + UINT8 OemId; + UINT16 ManufacturerId; + UINT8 Ud0BaseOffset; + UINT8 Ud0ConfParamLen; + UINT8 DevRttCap; + UINT16 PeriodicRtcUpdate; + UINT8 Rsvd1[17]; + UINT8 Rsvd2[16]; +} UFS_DEV_DESC; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 Rsvd1; + UINT8 BootEn; + UINT8 DescAccessEn; + UINT8 InitPowerMode; + UINT8 HighPriorityLun; + UINT8 SecureRemovalType; + UINT8 InitActiveIccLevel; + UINT16 PeriodicRtcUpdate; + UINT8 Rsvd2[5]; +} UFS_CONFIG_DESC_GEN_HEADER; + +typedef struct { + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 MemType; + UINT32 NumAllocUnits; + UINT8 DataReliability; + UINT8 LogicBlkSize; + UINT8 ProvisionType; + UINT16 CtxCap; + UINT8 Rsvd1[3]; +} UFS_UNIT_DESC_CONFIG_PARAMS; + +// +// UFS 2.0 Spec Section 14.1.6.3 - Configuration Descriptor +// +typedef struct { + UFS_CONFIG_DESC_GEN_HEADER Header; + UFS_UNIT_DESC_CONFIG_PARAMS UnitDescConfParams[8]; +} UFS_CONFIG_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.4 - Geometry Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 MediaTech; + UINT8 Rsvd1; + UINT64 TotalRawDevCapacity; + UINT8 Rsvd2; + UINT32 SegSize; + UINT8 AllocUnitSize; + UINT8 MinAddrBlkSize; + UINT8 OptReadBlkSize; + UINT8 OptWriteBlkSize; + UINT8 MaxInBufSize; + UINT8 MaxOutBufSize; + UINT8 RpmbRwSize; + UINT8 Rsvd3; + UINT8 DataOrder; + UINT8 MaxCtxIdNum; + UINT8 SysDataTagUnitSize; + UINT8 SysDataResUnitSize; + UINT8 SupSecRemovalTypes; + UINT16 SupMemTypes; + UINT32 SysCodeMaxNumAllocUnits; + UINT16 SupCodeCapAdjFac; + UINT32 NonPersMaxNumAllocUnits; + UINT16 NonPersCapAdjFac; + UINT32 Enhance1MaxNumAllocUnits; + UINT16 Enhance1CapAdjFac; + UINT32 Enhance2MaxNumAllocUnits; + UINT16 Enhance2CapAdjFac; + UINT32 Enhance3MaxNumAllocUnits; + UINT16 Enhance3CapAdjFac; + UINT32 Enhance4MaxNumAllocUnits; + UINT16 Enhance4CapAdjFac; +} UFS_GEOMETRY_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.5 - Unit Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 UnitIdx; + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 LunQueueDep; + UINT8 Rsvd1; + UINT8 MemType; + UINT8 DataReliability; + UINT8 LogicBlkSize; + UINT64 LogicBlkCount; + UINT32 EraseBlkSize; + UINT8 ProvisionType; + UINT64 PhyMemResCount; + UINT16 CtxCap; + UINT8 LargeUnitGranularity; +} UFS_UNIT_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.6 - RPMB Unit Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 UnitIdx; + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 LunQueueDep; + UINT8 Rsvd1; + UINT8 MemType; + UINT8 Rsvd2; + UINT8 LogicBlkSize; + UINT64 LogicBlkCount; + UINT32 EraseBlkSize; + UINT8 ProvisionType; + UINT64 PhyMemResCount; + UINT8 Rsvd3[3]; +} UFS_RPMB_UNIT_DESC; + +typedef struct { + UINT16 Value:10; + UINT16 Rsvd1:4; + UINT16 Unit:2; +} UFS_POWER_PARAM_ELEMENT; + +// +// UFS 2.0 Spec Section 14.1.6.7 - Power Parameter Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVcc[16]; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ[16]; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ2[16]; +} UFS_POWER_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.8 - InterConnect Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT16 UniProVer; + UINT16 MphyVer; +} UFS_INTER_CONNECT_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.9 - 14.1.6.12 - String Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + CHAR16 Unicode[126]; +} UFS_STRING_DESC; + +// +// UFS 2.0 Spec Section 14.2 - Flags +// +typedef enum { + UfsFlagDevInit = 0x01, + UfsFlagPermWpEn = 0x02, + UfsFlagPowerOnWpEn = 0x03, + UfsFlagBgOpsEn = 0x04, + UfsFlagPurgeEn = 0x06, + UfsFlagPhyResRemoval = 0x08, + UfsFlagBusyRtc = 0x09, + UfsFlagPermDisFwUpdate = 0x0B +} UFS_FLAGS_IDN; + +// +// UFS 2.0 Spec Section 14.2 - Attributes +// +typedef enum { + UfsAttrBootLunEn = 0x00, + UfsAttrCurPowerMode = 0x02, + UfsAttrActiveIccLevel = 0x03, + UfsAttrOutOfOrderDataEn = 0x04, + UfsAttrBgOpStatus = 0x05, + UfsAttrPurgeStatus = 0x06, + UfsAttrMaxDataInSize = 0x07, + UfsAttrMaxDataOutSize = 0x08, + UfsAttrDynCapNeeded = 0x09, + UfsAttrRefClkFreq = 0x0a, + UfsAttrConfigDescLock = 0x0b, + UfsAttrMaxNumOfRtt = 0x0c, + UfsAttrExceptionEvtCtrl = 0x0d, + UfsAttrExceptionEvtSts = 0x0e, + UfsAttrSecondsPassed = 0x0f, + UfsAttrContextConf = 0x10, + UfsAttrCorrPrgBlkNum = 0x11 +} UFS_ATTR_IDN; + +typedef enum { + UfsNoData = 0, + UfsDataOut = 1, + UfsDataIn = 2, + UfsDdReserved +} UFS_DATA_DIRECTION; + + +#pragma pack() + +#endif + diff --git a/drivers/ufs/UfsInternal.h b/drivers/ufs/UfsInternal.h new file mode 100644 index 0000000..695a28e --- /dev/null +++ b/drivers/ufs/UfsInternal.h @@ -0,0 +1,411 @@ +/** @file + + Copyright (c) 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 _UFS_BLOCK_IO_LIB_INTERNAL_H_ +#define _UFS_BLOCK_IO_LIB_INTERNAL_H_ + +#include +#include + +#include "UfsHci.h" +#include "UfsPciHc.h" +#include "UfsHcMem.h" +#include "Scsi.h" +#include "ScsiPassThruExt.h" + +/** + Returns a 16-bit signature built from 2 ASCII characters. + + This macro returns a 16-bit value built from the two ASCII characters specified + by A and B. + + @param A The first ASCII character. + @param B The second ASCII character. + + @return A 16-bit value built from the two ASCII characters specified by A and B. + +**/ +#define SIGNATURE_16(A, B) ((A) | (B << 8)) + +/** + Returns a 32-bit signature built from 4 ASCII characters. + + This macro returns a 32-bit value built from the four ASCII characters specified + by A, B, C, and D. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + + @return A 32-bit value built from the two ASCII characters specified by A, B, + C and D. + +**/ +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) + +#define UFS_PEIM_HC_SIG (SIGNATURE_32 ('U', 'F', 'S', 'H')) +#define UFS_PEIM_MAX_LUNS 8 +#define UFS_MAX_LUNS 12 +#define UFS_WLUN_PREFIX 0xC1 +#define DEVICE_INDEX_DEFAULT 0 + +#define UFS_TIMEOUT MultU64x32((UINT64)(3), 10000000) + +#define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8) + +#define IS_ALIGN(addr, size) (((UINTN) (addr) & (size - 1)) == 0) + +#define EFI_PAGES_TO_SIZE(a) ((a) << EFI_PAGE_SHIFT) + +#define UFS_SCSI_OP_LENGTH_SIX 0x6 +#define UFS_SCSI_OP_LENGTH_TEN 0xa +#define UFS_SCSI_OP_LENGTH_SIXTEEN 0x10 + +#define EFI_D_VERBOSE "UFS Debug: " +#define UNUSED_PARAM __attribute__((__unused__)) + +#ifdef DISABLE_DEBUG_PRINT +#define DEBUG_MESSAGES 0 +#else +#ifdef USER +#define DEBUG_MESSAGES 0 +#else +#define DEBUG_MESSAGES 1 +#endif +#endif + +#if DEBUG_MESSAGES +#define ufs_dbg(a, ...) {printf(a); printf(__VA_ARGS__); } +#else +#define ufs_dbg(a, ...) (void)0 +#endif + +#define DEBUG_UFS(a) (ufs_dbg a) + +typedef struct { + /// + /// A type of interface that the device being referenced by DeviceIndex is + /// attached to. This field re-uses Messaging Device Path Node sub-type values + /// as defined by Section 9.3.5 Messaging Device Path of UEFI Specification. + /// When more than one sub-type is associated with the interface, sub-type with + /// the smallest number must be used. + /// + UINT8 InterfaceType; + /// + /// A flag that indicates if media is removable. + /// + BOOLEAN RemovableMedia; + /// + /// A flag that indicates if media is present. This flag is always set for + /// non-removable media devices. + /// + BOOLEAN MediaPresent; + /// + /// A flag that indicates if media is read-only. + /// + BOOLEAN ReadOnly; + /// + /// The size of a logical block in bytes. + /// + UINT32 BlockSize; + /// + /// The last logical block that the device supports. + /// + EFI_LBA LastBlock; +} EFI_PEI_BLOCK_IO2_MEDIA; + +typedef struct { + UINT8 Lun[UFS_PEIM_MAX_LUNS]; + UINT16 BitMask:12; // Bit 0~7 is for common luns. Bit 8~11 is reserved for those well known luns + UINT16 Rsvd:4; +} UFS_PEIM_EXPOSED_LUNS; + +typedef struct { + /// + /// The timeout, in 100 ns units, to use for the execution of this SCSI + /// Request Packet. A Timeout value of 0 means that this function + /// will wait indefinitely for the SCSI Request Packet to execute. If + /// Timeout is greater than zero, then this function will return + /// EFI_TIMEOUT if the time required to execute the SCSI + /// Request Packet is greater than Timeout. + /// + UINT64 Timeout; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for read and bidirectional commands. + /// + VOID *InDataBuffer; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for write or bidirectional commands. + /// + VOID *OutDataBuffer; + /// + /// A pointer to the sense data that was generated by the execution of + /// the SCSI Request Packet. + /// + VOID *SenseData; + /// + /// A pointer to buffer that contains the Command Data Block to + /// send to the SCSI device specified by Target and Lun. + /// + VOID *Cdb; + /// + /// On Input, the size, in bytes, of InDataBuffer. On output, the + /// number of bytes transferred between the SCSI controller and the SCSI device. + /// + UINT32 InTransferLength; + /// + /// On Input, the size, in bytes of OutDataBuffer. On Output, the + /// Number of bytes transferred between SCSI Controller and the SCSI device. + /// + UINT32 OutTransferLength; + /// + /// The length, in bytes, of the buffer Cdb. The standard values are 6, + /// 10, 12, and 16, but other values are possible if a variable length CDB is used. + /// + UINT8 CdbLength; + /// + /// The direction of the data transfer. 0 for reads, 1 for writes. A + /// value of 2 is Reserved for Bi-Directional SCSI commands. + /// + UINT8 DataDirection; + /// + /// The status of the host adapter specified by This when the SCSI + /// Request Packet was executed on the target device. + /// + UINT8 HostAdapterStatus; + /// + /// The status returned by the device specified by Target and Lun + /// when the SCSI Request Packet was executed. + /// + UINT8 TargetStatus; + /// + /// On input, the length in bytes of the SenseData buffer. On + /// output, the number of bytes written to the SenseData buffer. + /// + UINT8 SenseDataLength; +} UFS_SCSI_REQUEST_PACKET; + +typedef struct _UFS_PEIM_HC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ufs_ps_protocol; + + UFS_PEIM_MEM_POOL *Pool; + + EFI_PEI_BLOCK_IO2_MEDIA Media[UFS_PEIM_MAX_LUNS]; + + UINTN UfsHcBase; + UINT32 Capabilities; + + UINT8 TaskTag; + + VOID *UtpTrlBase; + UINT8 Nutrs; + VOID *UtpTmrlBase; + UINT8 Nutmrs; + UFS_PEIM_EXPOSED_LUNS Luns; + VOID *FreeUtpTrlBase; + VOID *FreeUtpTmrlBase; +} UFS_PEIM_HC_PRIVATE_DATA; + +typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET { + UINT64 Timeout; + VOID *InDataBuffer; + VOID *OutDataBuffer; + UINT8 Opcode; + UINT8 DescId; + UINT8 Index; + UINT8 Selector; + UINT32 InTransferLength; + UINT32 OutTransferLength; + UINT8 DataDirection; + UINT8 Ocs; +} UFS_DEVICE_MANAGEMENT_REQUEST_PACKET; + +/** + Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the + UFS device. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsExecScsiCmds( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT UFS_SCSI_REQUEST_PACKET *Packet + ); + +/** + Initialize the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +UfsControllerInit( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Stop the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully. + @retval Others A device error occurred while stopping the controller. + +**/ +EFI_STATUS +UfsControllerStop( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Set specified flag to 1 on a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be set. + + @retval EFI_SUCCESS The flag was set successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag. + +**/ +EFI_STATUS +UfsSetFlag( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId + ); + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDesc( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ); + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmds( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Initialize the memory management pool for the host controller. + + @param Private The Ufs Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +UfsInitMemPool( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UfsAllocateMem( + IN UFS_PEIM_MEM_POOL *Pool, + IN UINTN Size + ); + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UfsFreeMem( + IN UFS_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); +EFI_STATUS alloc_aligned_page( + VOID **free_addr, + VOID **aligned_addr, + UINTN page_count + ); + +EFI_STATUS alloc_align( + VOID **free_addr, + VOID **aligned_addr, + UINTN size, + UINTN align + ); + +#endif diff --git a/drivers/ufs/UfsPciHc.c b/drivers/ufs/UfsPciHc.c new file mode 100644 index 0000000..f63d649 --- /dev/null +++ b/drivers/ufs/UfsPciHc.c @@ -0,0 +1,78 @@ +/** @file + UfsPciHcPei driver is used to provide platform-dependent info, mainly UFS host controller + MMIO base, to upper layer UFS drivers. + + Copyright (c) 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. + +**/ + +#include + +#include "UfsInternal.h" + +/** + Get the MMIO base address of UFS host controller. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] ControllerId The ID of the UFS host controller. + @param[out] MmioBar Pointer to the UFS host controller MMIO base address. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +GetUfsHcMmioBar( + IN UFS_HC_PEI_PRIVATE_DATA *Private, + IN UINT8 ControllerId, + OUT UINTN *MmioBar + ) +{ + if ((Private == NULL) || (MmioBar == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (ControllerId >= Private->TotalUfsHcs) { + return EFI_INVALID_PARAMETER; + } + + *MmioBar = (UINTN)Private->UfsHcPciAddr[ControllerId]; + + return EFI_SUCCESS; +} + + +/** + The user code starts with this function. + + @param[out] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] UfsHcPciBase UFS Host Controller's PCI ConfigSpace Base address + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +InitializeUfsHcPeim( + OUT UFS_HC_PEI_PRIVATE_DATA *Private, + IN pcidev_t pci_dev + ) +{ + ZeroMem(Private, sizeof(UFS_HC_PEI_PRIVATE_DATA)); + + Private->Signature = UFS_HC_PEI_SIGNATURE; + Private->UfsHcPciAddr[Private->TotalUfsHcs] = (pci_read_config32(pci_dev, PCI_BASE_ADDRESS_0)) & (~0xf); + DEBUG_UFS((EFI_D_VERBOSE, "UfsPciHcBase: 0x%08x\n", Private->UfsHcPciAddr[Private->TotalUfsHcs])); + Private->TotalUfsHcs++; + + return EFI_SUCCESS; +} diff --git a/drivers/ufs/UfsPciHc.h b/drivers/ufs/UfsPciHc.h new file mode 100644 index 0000000..b92245b --- /dev/null +++ b/drivers/ufs/UfsPciHc.h @@ -0,0 +1,66 @@ +/** @file + Copyright (c) 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 _UFS_PCI_HOST_CONTROLLER_PEI_H_ +#define _UFS_PCI_HOST_CONTROLLER_PEI_H_ + +#include + +#define UFS_HC_PEI_SIGNATURE (SIGNATURE_32 ('U', 'F', 'S', 'P')) +#define MAX_UFS_HCS 8 + +typedef struct { + UINTN Signature; + UINTN TotalUfsHcs; + UINTN UfsHcPciAddr[MAX_UFS_HCS]; +} UFS_HC_PEI_PRIVATE_DATA; + + +/** + Get the MMIO base address of UFS host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the UFS host controller. + @param[out] MmioBar Pointer to the UFS host controller MMIO base address. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +GetUfsHcMmioBar ( + IN UFS_HC_PEI_PRIVATE_DATA *Private, + IN UINT8 ControllerId, + OUT UINTN *MmioBar + ); + + + +/** + The user code starts with this function. + + @param[out] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] UfsHcPciBase UFS Host Controller's PCI ConfigSpace Base address + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +InitializeUfsHcPeim ( + OUT UFS_HC_PEI_PRIVATE_DATA *Private, + IN pcidev_t pci_dev + ); + +#endif diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c new file mode 100644 index 0000000..ec27b88 --- /dev/null +++ b/drivers/ufs/ufs.c @@ -0,0 +1,190 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 2017 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#include +#include +#include + +#include "UfsInternal.h" +#include "ufs.h" +#include "ScsiPassThruExt.h" +#include "UfsBlockIoLib.h" + +const EFI_LBA UFS_BLOCK_MAX = 0xffff; + +static struct supported_device { + u16 vid; + u16 did; +} SUPPORTED_DEVICES[] = { + { .vid = 0x8086, .did = UFS_PCI_DID }, +}; + +static EFI_STATUS _init(storage_t *s) +{ + EFI_STATUS ret; + pcidev_t pci_dev = 0; + size_t i; + DEVICE_BLOCK_INFO BlockInfo; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_DEVICES); i++) + if (pci_find_device(SUPPORTED_DEVICES[i].vid, + SUPPORTED_DEVICES[i].did, + &pci_dev)) + break; + + if (!pci_dev) + return EFI_UNSUPPORTED; + + ret = InitializeUfs(pci_dev); + if (ret) + return EFI_DEVICE_ERROR; + + ret = UfsGetMediaInfo(DEVICE_INDEX_DEFAULT, &BlockInfo); + if (EFI_ERROR(ret)) { + DEBUG_UFS((EFI_D_VERBOSE, "MmcGetMediaInfo Error %d\n", ret)); + return ret; + } + + DEBUG_UFS((EFI_D_VERBOSE, "Index 0 BlockNum is 0x%x\n", BlockInfo.BlockNum)); + DEBUG_UFS((EFI_D_VERBOSE, "BlockSize is 0x%x\n", BlockInfo.BlockSize)); + s->blk_cnt = BlockInfo.BlockNum; + s->blk_sz = BlockInfo.BlockSize; + + return EFI_SUCCESS; +} + +static EFI_LBA _read(storage_t *s, EFI_LBA start, EFI_LBA count, void *buf) +{ + EFI_STATUS ret; + + ret = UfsReadBlocks ( + DEVICE_INDEX_DEFAULT, + start, + s->blk_sz * count, + buf); + if (!EFI_ERROR(ret)) + return count; + else + return 0; +} + + /* Need to implement it in ufs driver*/ +static EFI_LBA _write(storage_t *s, EFI_LBA start, EFI_LBA count, const void *buf) +{ + EFI_LBA cur_count, transfered = 0; + EFI_STATUS ret; + + do { + cur_count = count > UFS_BLOCK_MAX ? UFS_BLOCK_MAX : count; + ret = UfsWriteBlocks ( + DEVICE_INDEX_DEFAULT, + start, + s->blk_sz * cur_count, + (void *)buf); + if (EFI_ERROR(ret)) + break; + start += cur_count; + buf += cur_count * s->blk_sz; + count -= cur_count; + transfered += cur_count; + } while (count > 0); + + return transfered; +} + +static storage_t storage_ufs_storage = { + .init = _init, + .read = _read, + .write = _write, + .erase = NULL, + .pci_function = 0, + .pci_device = 0, +}; + +static EFI_HANDLE handle; +// static EFI_HANDLE handle_scsi_protocol; +static EFI_GUID ufs_ps_protocol_guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; + +static EFI_STATUS storage_ufs_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + UFS_PEIM_HC_PRIVATE_DATA *Private; + boot_dev_t *boot_dev; + static EFI_EXT_SCSI_PASS_THRU_MODE mode = { + 0xFFFFFFFF, + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, + sizeof(UINTN) + }; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL ufs_default = { + NULL, + UfsPassThruPassThru, + UfsPassThruGetNextTargetLun, + UfsPassThruBuildDevicePath, + UfsPassThruGetTargetLun, + UfsPassThruResetChannel, + UfsPassThruResetTargetLun, + UfsPassThruGetNextTarget + }; + ufs_default.Mode = &mode; + + if (!st) + return EFI_INVALID_PARAMETER; + + boot_dev = get_boot_media(); + if (!boot_dev) + return EFI_INVALID_PARAMETER; + if (boot_dev->type != STORAGE_UFS) + return EFI_SUCCESS; + + storage_ufs_storage.pci_device = (boot_dev->diskbus >> 8) & 0xff; + storage_ufs_storage.pci_function = boot_dev->diskbus & 0xff; + + ret = storage_init(st, &storage_ufs_storage, &handle); + if (EFI_ERROR(ret)) + return ret; + + Private = UfsGetPrivateData(); + if (Private == NULL) + return EFI_NOT_FOUND; + + return interface_init(st, &ufs_ps_protocol_guid, &handle, + &ufs_default, sizeof(ufs_default), + (void **)&Private->ufs_ps_protocol); +} + +static EFI_STATUS storage_ufs_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + storage_free(st, handle); + return interface_free(st, &ufs_ps_protocol_guid, handle); +} + +ewdrv_t ufs_drv = { + .name = "storage_ufs", + .description = "STORAGE PCI UFS driver", + .init = storage_ufs_init, + .exit = storage_ufs_exit +}; diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h new file mode 100644 index 0000000..7a29e3c --- /dev/null +++ b/drivers/ufs/ufs.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _UFS_H_ +#define _UFS_H_ + +#include + +extern ewdrv_t ufs_drv; + +#endif /* _UFS_H_ */ diff --git a/drivers/virtual_media/ScsiPassThruExt.h b/drivers/virtual_media/ScsiPassThruExt.h new file mode 100644 index 0000000..06ea6fe --- /dev/null +++ b/drivers/virtual_media/ScsiPassThruExt.h @@ -0,0 +1,394 @@ +/** @file + EFI_EXT_SCSI_PASS_THRU_PROTOCOL as defined in UEFI 2.0. + This protocol provides services that allow SCSI Pass Thru commands + to be sent to SCSI devices attached to a SCSI channel. + + Copyright (c) 2006 - 2008, 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 __EXT_SCSI_PASS_THROUGH_PROTOCOL_H__ +#define __EXT_SCSI_PASS_THROUGH_PROTOCOL_H__ + +#include + +#define EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x143b7632, 0xb81b, 0x4cb7, {0xab, 0xd3, 0xb6, 0x25, 0xa5, 0xb9, 0xbf, 0xfe } \ + } + +typedef struct _EFI_EXT_SCSI_PASS_THRU_PROTOCOL EFI_EXT_SCSI_PASS_THRU_PROTOCOL; + +#define TARGET_MAX_BYTES 0x10 + +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001 +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002 +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004 + +// +// DataDirection +// +#define EFI_EXT_SCSI_DATA_DIRECTION_READ 0 +#define EFI_EXT_SCSI_DATA_DIRECTION_WRITE 1 +#define EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL 2 +// +// HostAdapterStatus +// +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK 0x00 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND 0x09 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT 0x0b +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT 0x0d +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET 0x0e +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR 0x0f +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED 0x10 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT 0x11 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN 0x12 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE 0x13 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR 0x14 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER 0x7f +// +// TargetStatus +// +#define EFI_EXT_SCSI_STATUS_TARGET_GOOD 0x00 +#define EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION 0x02 +#define EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET 0x04 +#define EFI_EXT_SCSI_STATUS_TARGET_BUSY 0x08 +#define EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE 0x10 +#define EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET 0x14 +#define EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT 0x18 +#define EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL 0x28 +#define EFI_EXT_SCSI_STATUS_TARGET_ACA_ACTIVE 0x30 +#define EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED 0x40 + +typedef struct { + /// + /// The Target ID of the host adapter on the SCSI channel. + /// + UINT32 AdapterId; + /// + /// Additional information on the attributes of the SCSI channel. + /// + UINT32 Attributes; + /// + /// Supplies the alignment requirement for any buffer used in a data transfer. + /// + UINT32 IoAlign; +} EFI_EXT_SCSI_PASS_THRU_MODE; + +typedef struct { + /// + /// The timeout, in 100 ns units, to use for the execution of this SCSI + /// Request Packet. A Timeout value of 0 means that this function + /// will wait indefinitely for the SCSI Request Packet to execute. If + /// Timeout is greater than zero, then this function will return + /// EFI_TIMEOUT if the time required to execute the SCSI + /// Request Packet is greater than Timeout. + /// + UINT64 Timeout; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for read and bidirectional commands. + /// + VOID *InDataBuffer; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for write or bidirectional commands. + /// + VOID *OutDataBuffer; + /// + /// A pointer to the sense data that was generated by the execution of + /// the SCSI Request Packet. + /// + VOID *SenseData; + /// + /// A pointer to buffer that contains the Command Data Block to + /// send to the SCSI device specified by Target and Lun. + /// + VOID *Cdb; + /// + /// On Input, the size, in bytes, of InDataBuffer. On output, the + /// number of bytes transferred between the SCSI controller and the SCSI device. + /// + UINT32 InTransferLength; + /// + /// On Input, the size, in bytes of OutDataBuffer. On Output, the + /// Number of bytes transferred between SCSI Controller and the SCSI device. + /// + UINT32 OutTransferLength; + /// + /// The length, in bytes, of the buffer Cdb. The standard values are 6, + /// 10, 12, and 16, but other values are possible if a variable length CDB is used. + /// + UINT8 CdbLength; + /// + /// The direction of the data transfer. 0 for reads, 1 for writes. A + /// value of 2 is Reserved for Bi-Directional SCSI commands. + /// + UINT8 DataDirection; + /// + /// The status of the host adapter specified by This when the SCSI + /// Request Packet was executed on the target device. + /// + UINT8 HostAdapterStatus; + /// + /// The status returned by the device specified by Target and Lun + /// when the SCSI Request Packet was executed. + /// + UINT8 TargetStatus; + /// + /// On input, the length in bytes of the SenseData buffer. On + /// output, the number of bytes written to the SenseData buffer. + /// + UINT8 SenseDataLength; +} EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET; + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function + supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the + nonblocking I/O functionality is optional. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it represents + the id of the SCSI device to send the SCSI Request Packet. Each + transport driver may choose to utilize a subset of this size to suit the needs + of transport target representation. For example, a Fibre Channel driver + may use only 8 bytes (WWN) to represent an FC target. + @param Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param Packet A pointer to the SCSI Request Packet to send to the SCSI device + specified by Target and Lun. + @param Event If nonblocking I/O is not supported then Event is ignored, and blocking + I/O is performed. If Event is NULL, then blocking I/O is performed. If + Event is not NULL and non blocking I/O is supported, then + nonblocking I/O is performed, and Event will be signaled when the + SCSI Request Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that + could be transferred is returned in InTransferLength. For write + and bi-directional commands, OutTransferLength bytes were + transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many + SCSI Request Packets already queued. The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported + by the host adapter. This includes the case of Bi-directional SCSI + commands not supported by the implementation. The SCSI Request + Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_PASSTHRU)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These + can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal + Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the + Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI + channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target On input, a pointer to the Target ID (an array of size + TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI + channel. On output, a pointer to the LUN of the next SCSI device present + on a SCSI channel. + + @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI + channel was returned in Target and Lun. + @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were + not returned on a previous call to GetNextTargetLun(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ); + +/** + Used to allocate and build a device path node for a SCSI device on a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the + Target ID of the SCSI device for which a device path node is to be + allocated and built. Transport drivers may chose to utilize a subset of + this size to suit the representation of targets. For example, a Fibre + Channel driver may use only 8 bytes (WWN) in the array to represent a + FC target. + @param Lun The LUN of the SCSI device for which a device path node is to be + allocated and built. + @param DevicePath A pointer to a single device path node that describes the SCSI device + specified by Target and Lun. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI device specified by + Target and Lun was allocated and returned in + DevicePath. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist + on the SCSI channel. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_BUILD_DEVICE_PATH)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH **DevicePath + ); + +/** + Used to translate a device path node to a Target ID and LUN. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param DevicePath A pointer to a single device path node that describes the SCSI device + on the SCSI channel. + @param Target A pointer to the Target Array which represents the ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of a SCSI device on the SCSI channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and + LUN, and they were returned in Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN + does not exist. + @retval EFI_UNSUPPORTED This driver does not support the device path node type in + DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +/** + Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + + @retval EFI_SUCCESS The SCSI channel was reset. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel. + @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_RESET_CHANNEL)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +/** + Resets a SCSI logical unit that is connected to a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the + target port ID of the SCSI device containing the SCSI logical unit to + reset. Transport drivers may chose to utilize a subset of this array to suit + the representation of their targets. + @param Lun The LUN of the SCSI device to reset. + + @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device + specified by Target and Lun. + @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device + specified by Target and Lun. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_RESET_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +/** + Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either + be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs + for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to + see if a SCSI device is actually present at that location on the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ); + +/// +/// The EFI_EXT_SCSI_PASS_THRU_PROTOCOL provides information about a SCSI channel +/// and the ability to send SCI Request Packets to any SCSI device attached to +/// that SCSI channel. The information includes the Target ID of the host controller +/// on the SCSI channel and the attributes of the SCSI channel. +/// +struct _EFI_EXT_SCSI_PASS_THRU_PROTOCOL { + /// + /// A pointer to the EFI_EXT_SCSI_PASS_THRU_MODE data for this SCSI channel. + /// + EFI_EXT_SCSI_PASS_THRU_MODE *Mode; + EFI_EXT_SCSI_PASS_THRU_PASSTHRU PassThru; + EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET_LUN GetNextTargetLun; + EFI_EXT_SCSI_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_EXT_SCSI_PASS_THRU_GET_TARGET_LUN GetTargetLun; + EFI_EXT_SCSI_PASS_THRU_RESET_CHANNEL ResetChannel; + EFI_EXT_SCSI_PASS_THRU_RESET_TARGET_LUN ResetTargetLun; + EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET GetNextTarget; +}; + +#endif diff --git a/drivers/virtual_media/Virtio.h b/drivers/virtual_media/Virtio.h new file mode 100755 index 0000000..c68cc0f --- /dev/null +++ b/drivers/virtual_media/Virtio.h @@ -0,0 +1,24 @@ +/** @file + + Generic type and macro definitions corresponding to the virtio + specifications. + + Copyright (C) 2012-2016, Red Hat, Inc. + Portion of Copyright (C) 2013, ARM Ltd. + + 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 _VIRTIO_H_ +#define _VIRTIO_H_ + +#include "Virtio10.h" + +#endif // _VIRTIO_H_ diff --git a/drivers/virtual_media/Virtio095.h b/drivers/virtual_media/Virtio095.h new file mode 100755 index 0000000..81bcc50 --- /dev/null +++ b/drivers/virtual_media/Virtio095.h @@ -0,0 +1,173 @@ +/** @file + + Generic type and macro definitions corresponding to the virtio-0.9.5 + specification. + + Copyright (C) 2012-2016, Red Hat, Inc. + Portion of Copyright (C) 2013, ARM Ltd. + + 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 _VIRTIO_0_9_5_H_ +#define _VIRTIO_0_9_5_H_ + +// +// VirtIo Subsystem Device IDs +// +#define VIRTIO_SUBSYSTEM_NETWORK_CARD 1 +#define VIRTIO_SUBSYSTEM_BLOCK_DEVICE 2 +#define VIRTIO_SUBSYSTEM_CONSOLE 3 +#define VIRTIO_SUBSYSTEM_ENTROPY_SOURCE 4 +#define VIRTIO_SUBSYSTEM_MEMORY_BALLOONING 5 +#define VIRTIO_SUBSYSTEM_IO_MEMORY 6 +#define VIRTIO_SUBSYSTEM_RPMSG 7 +#define VIRTIO_SUBSYSTEM_SCSI_HOST 8 +#define VIRTIO_SUBSYSTEM_9P_TRANSPORT 9 +#define VIRTIO_SUBSYSTEM_MAC80211_WLAN 10 + +// +// Virtio IDs +// +#define VIRTIO_VENDOR_ID 0x1AF4 +#define VIRTIO_MMIO_MAGIC 0x74726976 // "virt" + + +// +// VirtIo Device Specific Configuration Offsets +// +#define VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI 20 +#define VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI_WITH_MSI_X 24 +#define VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_MMIO 0x100 + +// +// PCI VirtIo Header Offsets +// +#define VIRTIO_PCI_OFFSET_DEVICE_FEATURES 0x00 +#define VIRTIO_PCI_OFFSET_GUEST_FEATURES 0x04 +#define VIRTIO_PCI_OFFSET_QUEUE_ADDRESS 0x08 +#define VIRTIO_PCI_OFFSET_QUEUE_SIZE 0x0C +#define VIRTIO_PCI_OFFSET_QUEUE_SELECT 0x0E +#define VIRTIO_PCI_OFFSET_QUEUE_NOTIFY 0x10 +#define VIRTIO_PCI_OFFSET_QUEUE_DEVICE_STATUS 0x12 +#define VIRTIO_PCI_OFFSET_QUEUE_DEVICE_ISR 0x13 + +// +// MMIO VirtIo Header Offsets +// +#define VIRTIO_MMIO_OFFSET_MAGIC 0x00 +#define VIRTIO_MMIO_OFFSET_VERSION 0x04 +#define VIRTIO_MMIO_OFFSET_DEVICE_ID 0x08 +#define VIRTIO_MMIO_OFFSET_VENDOR_ID 0x0C +#define VIRTIO_MMIO_OFFSET_HOST_FEATURES 0x10 +#define VIRTIO_MMIO_OFFSET_HOST_FEATURES_SEL 0x14 +#define VIRTIO_MMIO_OFFSET_GUEST_FEATURES 0x20 +#define VIRTIO_MMIO_OFFSET_GUEST_FEATURES_SEL 0x24 +#define VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE 0x28 +#define VIRTIO_MMIO_OFFSET_QUEUE_SEL 0x30 +#define VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX 0x34 +#define VIRTIO_MMIO_OFFSET_QUEUE_NUM 0x38 +#define VIRTIO_MMIO_OFFSET_QUEUE_ALIGN 0x3C +#define VIRTIO_MMIO_OFFSET_QUEUE_PFN 0x40 +#define VIRTIO_MMIO_OFFSET_QUEUE_NOTIFY 0x50 +#define VIRTIO_MMIO_OFFSET_INTERRUPT_STATUS 0x60 +#define VIRTIO_MMIO_OFFSET_INTERRUPT_ACK 0x64 +#define VIRTIO_MMIO_OFFSET_STATUS 0x70 + +// +// Data in the communication area is defined as packed and accessed as +// volatile. +// +// Some structures contain arrays with dynamically determined size. In such +// cases the array and its sibling fields are replaced with pointers. +// +// All indices (variables and fields named *Idx) are free-running and wrap +// around after 0xFFFF. The queue size reported by the host is always an +// integral power of 2, not greater than 32768. Actual array indices are +// consistently calculated by taking the remainder of a given Idx object modulo +// QueueSize. Since 0x10000 is an integral multiple of the QueueSize, UINT16 +// wraparound is a correct wraparound modulo QueueSize too (it doesn't offset +// the remainder class). +// +// virtio-0.9.5, 2.3.4 Available Ring +// +#define VRING_AVAIL_F_NO_INTERRUPT BIT0 + +typedef struct { + volatile UINT16 *Flags; + volatile UINT16 *Idx; + + volatile UINT16 *Ring; // QueueSize elements + volatile UINT16 *UsedEvent; // unused as per negotiation +} VRING_AVAIL; + + +// +// virtio-0.9.5, 2.3.5 Used Ring +// +#define VRING_USED_F_NO_NOTIFY BIT0 + +#pragma pack(1) +typedef struct { + UINT32 Id; + UINT32 Len; +} VRING_USED_ELEM; +#pragma pack() + +typedef struct { + volatile UINT16 *Flags; + volatile UINT16 *Idx; + volatile VRING_USED_ELEM *UsedElem; // QueueSize elements + volatile UINT16 *AvailEvent; // unused as per negotiation +} VRING_USED; + + +// +// virtio-0.9.5, 2.3.2 Descriptor Table +// +#define VRING_DESC_F_NEXT BIT0 // more descriptors in this request +#define VRING_DESC_F_WRITE BIT1 // buffer to be written *by the host* +#define VRING_DESC_F_INDIRECT BIT2 // unused + +#pragma pack(1) +typedef struct { + UINT64 Addr; + UINT32 Len; + UINT16 Flags; + UINT16 Next; +} VRING_DESC; +#pragma pack() + +typedef struct { + UINTN NumPages; + VOID *Base; // deallocate only this field + volatile VRING_DESC *Desc; // QueueSize elements + VRING_AVAIL Avail; + VRING_USED Used; + UINT16 QueueSize; +} VRING; + +// +// virtio-0.9.5, 2.2.2.1 Device Status +// +#define VSTAT_ACK BIT0 +#define VSTAT_DRIVER BIT1 +#define VSTAT_DRIVER_OK BIT2 +#define VSTAT_FAILED BIT7 + +// +// virtio-0.9.5, Appendix B: Reserved (Device-Independent) Feature Bits +// +#define VIRTIO_F_NOTIFY_ON_EMPTY BIT24 +#define VIRTIO_F_RING_INDIRECT_DESC BIT28 +#define VIRTIO_F_RING_EVENT_IDX BIT29 + + +#endif // _VIRTIO_0_9_5_H_: diff --git a/drivers/virtual_media/Virtio10.h b/drivers/virtual_media/Virtio10.h new file mode 100755 index 0000000..5a784d2 --- /dev/null +++ b/drivers/virtual_media/Virtio10.h @@ -0,0 +1,88 @@ +/** @file: + Definitions from the VirtIo 1.0 specification (csprd05). + + Copyright (C) 2016, Red Hat, Inc. + Copyright (C) 2017, AMD, Inc. + + 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 _VIRTIO_1_0_H_ +#define _VIRTIO_1_0_H_ + +#include "Virtio095.h" + +// +// Subsystem Device IDs (to be) introduced in VirtIo 1.0 +// +#define VIRTIO_SUBSYSTEM_GPU_DEVICE 16 + +// +// Structures for parsing the VirtIo 1.0 specific PCI capabilities from the +// config space +// +#pragma pack (1) +typedef struct { + UINT8 CapId; // Capability identifier (generic) + UINT8 CapNext; // Link to next capability (generic) +} VIRTIO_PCI_CAP_LINK; + +typedef struct { + UINT8 ConfigType; // Identifies the specific VirtIo 1.0 config structure + UINT8 Bar; // The BAR that contains the structure + UINT8 Padding[3]; + UINT32 Offset; // Offset within Bar until the start of the structure + UINT32 Length; // Length of the structure +} VIRTIO_PCI_CAP; +#pragma pack () + +// +// Values for the VIRTIO_PCI_CAP.ConfigType field +// +#define VIRTIO_PCI_CAP_COMMON_CFG 1 // Common configuration +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 // Notifications +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 // Device specific configuration + +// +// Structure pointed-to by Bar and Offset in VIRTIO_PCI_CAP when ConfigType is +// VIRTIO_PCI_CAP_COMMON_CFG +// +#pragma pack (1) +typedef struct { + UINT32 DeviceFeatureSelect; + UINT32 DeviceFeature; + UINT32 DriverFeatureSelect; + UINT32 DriverFeature; + UINT16 MsixConfig; + UINT16 NumQueues; + UINT8 DeviceStatus; + UINT8 ConfigGeneration; + UINT16 QueueSelect; + UINT16 QueueSize; + UINT16 QueueMsixVector; + UINT16 QueueEnable; + UINT16 QueueNotifyOff; + UINT64 QueueDesc; + UINT64 QueueAvail; + UINT64 QueueUsed; +} VIRTIO_PCI_COMMON_CFG; +#pragma pack () + +// +// VirtIo 1.0 device status bits +// +#define VSTAT_FEATURES_OK BIT3 + +// +// VirtIo 1.0 reserved (device-independent) feature bits +// +#define VIRTIO_F_VERSION_1 BIT32 +#define VIRTIO_F_IOMMU_PLATFORM BIT33 + +#endif // _VIRTIO_1_0_H_ diff --git a/drivers/virtual_media/VirtioBlk.c b/drivers/virtual_media/VirtioBlk.c new file mode 100644 index 0000000..612bc93 --- /dev/null +++ b/drivers/virtual_media/VirtioBlk.c @@ -0,0 +1,1159 @@ +/** @file + + This driver produces Block I/O Protocol instances for virtio-blk devices. + + The implementation is basic: + + - No attach/detach (ie. removable media). + + - Although the non-blocking interfaces of EFI_BLOCK_IO2_PROTOCOL could be a + good match for multiple in-flight virtio-blk requests, we stick to + synchronous requests and EFI_BLOCK_IO_PROTOCOL for now. + + Copyright (C) 2012, Red Hat, Inc. + Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD 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. + +**/ +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioBlk.h" +#include "VirtioLib.h" +#include "VirtioBlkDevice.h" + +#ifndef SIZE_1GB +#define SIZE_1GB 0x40000000 +#endif + +#ifndef SECTOR_SHIFT +#define SECTOR_SHIFT 9 +#endif + +#define EFI_BLOCK_IO_PROTOCOL_REVISION3 0x00020031 + +/** + + Convenience macros to read and write region 0 IO space elements of the + virtio-blk device, for configuration purposes. + + The following macros make it possible to specify only the "core parameters" + for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE() + returns, the transaction will have been completed. + + @param[in] Dev Pointer to the VBLK_DEV structure whose VirtIo space + we're accessing. Dev->VirtIo must be valid. + + @param[in] Field A field name from VBLK_HDR, identifying the virtio-blk + configuration item to access. + + @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the + selected configuration item. + + @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the + value read from the configuration item. Its type must be + one of UINT8, UINT16, UINT32, UINT64. + + + @return Status code returned by Virtio->WriteDevice() / + Virtio->ReadDevice(). + +**/ + +#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \ + (Dev)->VirtIo, \ + OFFSET_OF_VBLK (Field), \ + SIZE_OF_VBLK (Field), \ + (Value) \ + )) + +#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \ + (Dev)->VirtIo, \ + OFFSET_OF_VBLK (Field), \ + SIZE_OF_VBLK (Field), \ + sizeof *(Pointer), \ + (Pointer) \ + )) + +/** + Divides a 64-bit unsigned integer by a 32-bit unsigned integer and generates + a 32-bit unsigned remainder. + + This function divides the 64-bit unsigned value Dividend by the 32-bit + unsigned value Divisor and generates a 32-bit remainder. This function + returns the 32-bit unsigned remainder. + + If Divisor is 0, then ASSERT(). + + @param Dividend A 64-bit unsigned value. + @param Divisor A 32-bit unsigned value. + + @return Dividend % Divisor. + +**/ +static UINT32 +EFIAPI +ModU64x32 ( + IN UINT64 Dividend, + IN UINT32 Divisor + ) +{ + ASSERT (Divisor != 0); + UINT64 temp1 = DivU64x32 (Dividend, Divisor, NULL); + UINT64 temp2 = MultU64x32(temp1, Divisor); + return (UINT32)(Dividend - temp2); +} + +// +// UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol +// Driver Writer's Guide for UEFI 2.3.1 v1.01, +// 24.2 Block I/O Protocol Implementations +// +EFI_STATUS +EFIAPI +VirtioBlkReset ( + IN __attribute__((unused)) EFI_BLOCK_IO_PROTOCOL *This, + IN __attribute__((unused)) BOOLEAN ExtendedVerification + ) +{ + // + // If we managed to initialize and install the driver, then the device is + // working correctly. + // + return EFI_SUCCESS; +} + +/** + + Verify correctness of the read/write (not flush) request submitted to the + EFI_BLOCK_IO_PROTOCOL instance. + + This function provides most verification steps described in: + + UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, + - EFI_BLOCK_IO_PROTOCOL.ReadBlocks() + - EFI_BLOCK_IO_PROTOCOL.WriteBlocks() + + Driver Writer's Guide for UEFI 2.3.1 v1.01, + - 24.2.2. ReadBlocks() and ReadBlocksEx() Implementation + - 24.2.3 WriteBlocks() and WriteBlockEx() Implementation + + Request sizes are limited to 1 GB (checked). This is not a practical + limitation, just conformance to virtio-0.9.5, 2.3.2 Descriptor Table: "no + descriptor chain may be more than 2^32 bytes long in total". + + Some Media characteristics are hardcoded in VirtioBlkInit() below (like + non-removable media, no restriction on buffer alignment etc); we rely on + those here without explicit mention. + + @param[in] Media The EFI_BLOCK_IO_MEDIA characteristics for + this driver instance, extracted from the + underlying virtio-blk device at initialization + time. We validate the request against this set + of attributes. + + + @param[in] Lba Logical Block Address: number of logical + blocks to skip from the beginning of the + device. + + @param[in] PositiveBufferSize Size of buffer to transfer, in bytes. The + caller is responsible to ensure this parameter + is positive. + + @param[in] RequestIsWrite TRUE iff data transfer goes from guest to + device. + + + @@return Validation result to be forwarded outwards by + ReadBlocks() and WriteBlocks, as required by + the specs above. + +**/ +STATIC +EFI_STATUS +EFIAPI +VerifyReadWriteRequest ( + IN EFI_BLOCK_IO_MEDIA *Media, + IN EFI_LBA Lba, + IN UINTN PositiveBufferSize, + IN BOOLEAN RequestIsWrite + ) +{ + UINTN BlockCount; + + ASSERT (PositiveBufferSize > 0); + + if (PositiveBufferSize > SIZE_1GB || + PositiveBufferSize % Media->BlockSize > 0) { + return EFI_BAD_BUFFER_SIZE; + } + BlockCount = PositiveBufferSize / Media->BlockSize; + + // + // Avoid unsigned wraparound on either side in the second comparison. + // + if (Lba > Media->LastBlock || BlockCount - 1 > Media->LastBlock - Lba) { + return EFI_INVALID_PARAMETER; + } + + if (RequestIsWrite && Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + return EFI_SUCCESS; +} + + + + +/** + + Format a read / write / flush request as three consecutive virtio + descriptors, push them to the host, and poll for the response. + + This is the main workhorse function. Two use cases are supported, read/write + and flush. The function may only be called after the request parameters have + been verified by + - specific checks in ReadBlocks() / WriteBlocks() / FlushBlocks(), and + - VerifyReadWriteRequest() (for read/write only). + + Parameters handled commonly: + + @param[in] Dev The virtio-blk device the request is targeted + at. + + Flush request: + + @param[in] Lba Must be zero. + + @param[in] BufferSize Must be zero. + + @param[in out] Buffer Ignored by the function. + + @param[in] RequestIsWrite Must be TRUE. + + Read/Write request: + + @param[in] Lba Logical Block Address: number of logical blocks + to skip from the beginning of the device. + + @param[in] BufferSize Size of buffer to transfer, in bytes. The caller + is responsible to ensure this parameter is + positive. + + @param[in out] Buffer The guest side area to read data from the device + into, or write data to the device from. + + @param[in] RequestIsWrite TRUE iff data transfer goes from guest to + device. + + Return values are common to both use cases, and are appropriate to be + forwarded by the EFI_BLOCK_IO_PROTOCOL functions (ReadBlocks(), + WriteBlocks(), FlushBlocks()). + + + @retval EFI_SUCCESS Transfer complete. + + @retval EFI_DEVICE_ERROR Failed to notify host side via VirtIo write, or + unable to parse host response, or host response + is not VIRTIO_BLK_S_OK or failed to map Buffer + for a bus master operation. + +**/ + +STATIC +EFI_STATUS +EFIAPI +SynchronousRequest ( + IN VBLK_DEV *Dev, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN OUT volatile VOID *Buffer, + IN BOOLEAN RequestIsWrite, + IN UINT32 RequestType + ) +{ + UINT32 BlockSize; + volatile VIRTIO_BLK_REQ Request; + VIRTIO_DISCARD_RANGE EraseRange; + volatile UINT8 *HostStatus; + VOID *HostStatusBuffer; + DESC_INDICES Indices; + VOID *RequestMapping; + VOID *StatusMapping; + VOID *BufferMapping; + VOID *EraseRangeMapping; + EFI_PHYSICAL_ADDRESS BufferDeviceAddress; + EFI_PHYSICAL_ADDRESS HostStatusDeviceAddress; + EFI_PHYSICAL_ADDRESS RequestDeviceAddress; + EFI_PHYSICAL_ADDRESS EraseRangeDeviceAddress; + EFI_STATUS Status; + EFI_STATUS UnmapStatus; + UINT32 Flags = 0; + + BlockSize = Dev->BlockIoMedia.BlockSize; + + // + // ensured by VirtioBlkInit() + // + ASSERT (BlockSize > 0); + ASSERT (BlockSize % 512 == 0); + + // + // ensured by contract above, plus VerifyReadWriteRequest() + // + ASSERT (BufferSize % BlockSize == 0); + + // + // Prepare virtio-blk request header, setting zero size for flush. + // IO Priority is homogeneously 0. + // + Request.IoPrio = 0; + Request.Sector = MultU64x32(Lba, BlockSize / 512); + + if (RequestType != VIRTIO_BLK_T_DISCARD) { + Request.Type = RequestIsWrite ? + (BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) : + VIRTIO_BLK_T_IN; + } + else { + Request.Type = RequestType; + + Flags |= VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP; + EraseRange.Sector = Request.Sector; + EraseRange.num_sectors = BufferSize >> SECTOR_SHIFT;; + EraseRange.flags = Flags; + } + + // + // Host status is bi-directional (we preset with a value and expect the + // device to update it). Allocate a host status buffer which can be mapped + // to access equally by both processor and the device. + // + Status = Dev->VirtIo->AllocateSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES (sizeof *HostStatus), + &HostStatusBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + HostStatus = HostStatusBuffer; + + // + // Map virtio-blk request header (must be done after request header is + // populated) + // + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + VirtioOperationBusMasterRead, + (VOID *) &Request, + sizeof Request, + &RequestDeviceAddress, + &RequestMapping + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto FreeHostStatusBuffer; + } + + // + // Map data buffer + // + if (BufferSize > 0) { + if (RequestType == VIRTIO_BLK_T_DISCARD) + Status = VirtioMapAllBytesInSharedBuffer( + Dev->VirtIo, + VirtioOperationBusMasterRead, + (VOID *) &EraseRange, + sizeof(EraseRange), + &EraseRangeDeviceAddress, + &EraseRangeMapping + ); + else + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + (RequestIsWrite ? + VirtioOperationBusMasterRead : + VirtioOperationBusMasterWrite), + (VOID *) Buffer, + BufferSize, + &BufferDeviceAddress, + &BufferMapping + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto UnmapRequestBuffer; + } + } + + // + // preset a host status for ourselves that we do not accept as success + // + *HostStatus = VIRTIO_BLK_S_IOERR; + + // + // Map the Status Buffer with VirtioOperationBusMasterCommonBuffer so that + // both processor and device can access it. + // + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + VirtioOperationBusMasterCommonBuffer, + HostStatusBuffer, + sizeof *HostStatus, + &HostStatusDeviceAddress, + &StatusMapping + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto UnmapDataBuffer; + } + + VirtioPrepare (&Dev->Ring, &Indices); + + // + // ensured by VirtioBlkInit() -- this predicate, in combination with the + // lock-step progress, ensures we don't have to track free descriptors. + // + ASSERT (Dev->Ring.QueueSize >= 3); + + // + // virtio-blk header in first desc + // + VirtioAppendDesc ( + &Dev->Ring, + RequestDeviceAddress, + sizeof Request, + VRING_DESC_F_NEXT, + &Indices + ); + + // + // data buffer for read/write in second desc + // + if (BufferSize > 0) { + if (RequestType == VIRTIO_BLK_T_DISCARD) + VirtioAppendDesc( + &Dev->Ring, + EraseRangeDeviceAddress, + sizeof(EraseRange), + VRING_DESC_F_NEXT, + &Indices + ); + else { + // + // From virtio-0.9.5, 2.3.2 Descriptor Table: + // "no descriptor chain may be more than 2^32 bytes long in total". + // + // The predicate is ensured by the call contract above (for flush), or + // VerifyReadWriteRequest() (for read/write). It also implies that + // converting BufferSize to UINT32 will not truncate it. + // + ASSERT (BufferSize <= SIZE_1GB); + + // + // VRING_DESC_F_WRITE is interpreted from the host's point of view. + // + VirtioAppendDesc ( + &Dev->Ring, + BufferDeviceAddress, + (UINT32) BufferSize, + VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE), + &Indices + ); + } + } + + // + // host status in last (second or third) desc + // + VirtioAppendDesc ( + &Dev->Ring, + HostStatusDeviceAddress, + sizeof *HostStatus, + VRING_DESC_F_WRITE, + &Indices + ); + + // + // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D). + // + if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, + NULL) == EFI_SUCCESS && + *HostStatus == VIRTIO_BLK_S_OK) { + Status = EFI_SUCCESS; + //DEBUG ((DEBUG_INFO, "%s:VirtioFlush Success ^_^\n", + //__FUNCTION__)); + } else { + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_INFO, "%s:VirtioFlush fail !!!!!!!!!!!!\n", + __FUNCTION__)); + } + + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, StatusMapping); + + UnmapDataBuffer: + if (BufferSize > 0) { + if (RequestType == VIRTIO_BLK_T_DISCARD) + UnmapStatus = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, EraseRangeMapping); + else + UnmapStatus = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, BufferMapping); + if (EFI_ERROR (UnmapStatus) && !RequestIsWrite && !EFI_ERROR (Status)) { + // + // Data from the bus master may not reach the caller; fail the request. + // + Status = EFI_DEVICE_ERROR; + } + } + + UnmapRequestBuffer: + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping); + + FreeHostStatusBuffer: + Dev->VirtIo->FreeSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES (sizeof *HostStatus), + HostStatusBuffer + ); + + return Status; +} + + +EFI_STATUS +EFIAPI +VirtioBlkReadBlocksInternal ( + IN VBLK_DEV *Dev, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + Status = VerifyReadWriteRequest ( + &Dev->BlockIoMedia, + Lba, + BufferSize, + FALSE // RequestIsRead + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return SynchronousRequest ( + Dev, + Lba, + BufferSize, + Buffer, + FALSE, // RequestIsRead + VIRTIO_BLK_T_INVALID + ); +} + + +/** + + ReadBlocks() operation for virtio-blk. + + See + - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, EFI_BLOCK_IO_PROTOCOL.ReadBlocks(). + - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.2. ReadBlocks() and + ReadBlocksEx() Implementation. + + Parameter checks and conformant return values are implemented in + VerifyReadWriteRequest() and SynchronousRequest(). + + A zero BufferSize doesn't seem to be prohibited, so do nothing in that case, + successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN __attribute__((unused)) UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + VBLK_DEV *Dev; + EFI_STATUS Status; + VOID *DataBuffer; + UINT32 BlockSize; + UINT32 MaxSize; + UINT32 ReadSize; + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + Dev = VIRTIO_BLK_FROM_BLOCK_IO (This); + BlockSize = Dev->BlockIoMedia.BlockSize; + MaxSize = 0x400 << EFI_PAGE_SHIFT; + + //DataBuffer + Status = Dev->VirtIo->AllocateSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES ((BufferSize > MaxSize) ? MaxSize : BufferSize), + &DataBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + while (BufferSize > 0) { + if (BufferSize >= MaxSize) + ReadSize = MaxSize; + else + ReadSize = BufferSize; + + Status = VirtioBlkReadBlocksInternal (Dev, Lba, ReadSize, DataBuffer); + CopyMem(Buffer, DataBuffer, ReadSize); + Lba += ReadSize / BlockSize; + Buffer = (VOID *)((char *)Buffer + ReadSize); + BufferSize -= ReadSize; + + if (EFI_ERROR(Status)) + break; + } + + Dev->VirtIo->FreeSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES (BufferSize), + DataBuffer + ); + + return Status; +} + + +EFI_STATUS +EFIAPI +VirtioBlkWriteBlocksInternal ( + IN VBLK_DEV *Dev, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + Status = VerifyReadWriteRequest ( + &Dev->BlockIoMedia, + Lba, + BufferSize, + TRUE // RequestIsWrite + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SynchronousRequest ( + Dev, + Lba, + BufferSize, + Buffer, + TRUE, // RequestIsWrite + VIRTIO_BLK_T_INVALID + ); + + return Status; +} + + +/** + + WriteBlocks() operation for virtio-blk. + + See + - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, EFI_BLOCK_IO_PROTOCOL.WriteBlocks(). + - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.3 WriteBlocks() and + WriteBlockEx() Implementation. + + Parameter checks and conformant return values are implemented in + VerifyReadWriteRequest() and SynchronousRequest(). + + A zero BufferSize doesn't seem to be prohibited, so do nothing in that case, + successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN __attribute__((unused)) UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + VBLK_DEV *Dev; + EFI_STATUS Status = EFI_SUCCESS; + VOID *DataBuffer; + UINT32 BlockSize; + UINT32 MaxSize; + UINT32 WriteSize; + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + Dev = VIRTIO_BLK_FROM_BLOCK_IO (This); + BlockSize = Dev->BlockIoMedia.BlockSize; + MaxSize = 0x400 << EFI_PAGE_SHIFT; + + //DataBuffer + Status = Dev->VirtIo->AllocateSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES ((BufferSize > MaxSize) ? MaxSize : BufferSize), + &DataBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + while (BufferSize > 0) { + if (BufferSize >= MaxSize) + WriteSize = MaxSize; + else + WriteSize = BufferSize; + + CopyMem(DataBuffer, Buffer, WriteSize); + Status = VirtioBlkWriteBlocksInternal (Dev, Lba, WriteSize, DataBuffer); + Lba += WriteSize / BlockSize; + Buffer = (VOID *)((char *)Buffer + WriteSize); + BufferSize -= WriteSize; + + if (EFI_ERROR(Status)) + break; + } + + Dev->VirtIo->FreeSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES (BufferSize), + DataBuffer + ); + + return Status; +} + +/** + + EraseBlocks() operation for virtio-blk. + + RequestType that pass to SynchronousRequest() is VIRTIO_BLK_T_DISCARD. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN __attribute__((unused)) UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT __attribute__((unused)) EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ) +{ + EFI_STATUS Status; + VBLK_DEV *Dev; + UINT64 Features; + + Dev = VIRTIO_BLK_FROM_ERASE_BLOCK (This); + + Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Features & VIRTIO_BLK_F_DISCARD) + Status = SynchronousRequest ( + Dev, + Lba, + Size, + NULL, + TRUE, // RequestIsWrite + VIRTIO_BLK_T_DISCARD + ); + else + Status = EFI_UNSUPPORTED; + + return Status; +} + +/** + + FlushBlocks() operation for virtio-blk. + + See + - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, EFI_BLOCK_IO_PROTOCOL.FlushBlocks(). + - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.4 FlushBlocks() and + FlushBlocksEx() Implementation. + + If the underlying virtio-blk device doesn't support flushing (ie. + write-caching), then this function should not be called by higher layers, + according to EFI_BLOCK_IO_MEDIA characteristics set in VirtioBlkInit(). + Should they do nonetheless, we do nothing, successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + VBLK_DEV *Dev; + + Dev = VIRTIO_BLK_FROM_BLOCK_IO (This); + return Dev->BlockIoMedia.WriteCaching ? + SynchronousRequest ( + Dev, + 0, // Lba + 0, // BufferSize + NULL, // Buffer + TRUE, // RequestIsWrite + VIRTIO_BLK_T_INVALID + ) : + EFI_SUCCESS; +} + +/** + + Set up all BlockIo and virtio-blk aspects of this driver for the specified + device. + + @param[in out] Dev The driver instance to configure. The caller is + responsible for Dev->VirtIo's validity (ie. working IO + access to the underlying virtio-blk device). + + @retval EFI_SUCCESS Setup complete. + + @retval EFI_UNSUPPORTED The driver is unable to work with the virtio ring or + virtio-blk attributes the host provides. + + @return Error codes from VirtioRingInit() or + VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE or + VirtioRingMap(). + +**/ +EFI_STATUS +EFIAPI +VirtioBlkInit ( + IN OUT VBLK_DEV *Dev + ) +{ + UINT8 NextDevStat; + EFI_STATUS Status; + + UINT64 Features; + UINT64 NumSectors; + UINT32 BlockSize; + UINT8 PhysicalBlockExp; + UINT8 AlignmentOffset; + UINT32 OptIoSize; + UINT16 QueueSize; + UINT64 RingBaseShift; + + PhysicalBlockExp = 0; + AlignmentOffset = 0; + OptIoSize = 0; + + // + // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. + // + NextDevStat = 0; // step 1 -- reset device + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // Set Page Size - MMIO VirtIo Specific + // + Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // step 4a -- retrieve and validate features + // + Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); + if (EFI_ERROR (Status)) { + goto Failed; + } + + Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors); + if (EFI_ERROR (Status)) { + goto Failed; + } + if (NumSectors == 0) { + Status = EFI_UNSUPPORTED; + goto Failed; + } + + if (Features & VIRTIO_BLK_F_BLK_SIZE) { + Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize); + if (EFI_ERROR (Status)) { + goto Failed; + } + if (BlockSize == 0 || BlockSize % 512 != 0 || + ModU64x32 (NumSectors, BlockSize / 512) != 0) { + // + // We can only handle a logical block consisting of whole sectors, + // and only a disk composed of whole logical blocks. + // + Status = EFI_UNSUPPORTED; + goto Failed; + } + } + else { + BlockSize = 512; + } + + if (Features & VIRTIO_BLK_F_TOPOLOGY) { + Status = VIRTIO_CFG_READ (Dev, Topology.PhysicalBlockExp, + &PhysicalBlockExp); + if (EFI_ERROR (Status)) { + goto Failed; + } + if (PhysicalBlockExp >= 32) { + Status = EFI_UNSUPPORTED; + goto Failed; + } + + Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset); + if (EFI_ERROR (Status)) { + goto Failed; + } + + Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize); + if (EFI_ERROR (Status)) { + goto Failed; + } + } + + Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO | + VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM | + VIRTIO_BLK_F_DISCARD | VIRTIO_BLK_F_WRITE_ZEROES; + + // + // In virtio-1.0, feature negotiation is expected to complete before queue + // discovery, and the device can also reject the selected set of features. + // + if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { + Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + } + + // + // step 4b -- allocate virtqueue + // + Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0); + if (EFI_ERROR (Status)) { + goto Failed; + } + Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize); + if (EFI_ERROR (Status)) { + goto Failed; + } + if (QueueSize < 3) { // SynchronousRequest() uses at most three descriptors + Status = EFI_UNSUPPORTED; + goto Failed; + } + + Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // If anything fails from here on, we must release the ring resources + // + Status = VirtioRingMap ( + Dev->VirtIo, + &Dev->Ring, + &RingBaseShift, + &Dev->RingMap + ); + if (EFI_ERROR (Status)) { + goto ReleaseQueue; + } + + // + // Additional steps for MMIO: align the queue appropriately, and set the + // size. If anything fails from here on, we must unmap the ring resources. + // + Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + + Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + + // + // step 4c -- Report GPFN (guest-physical frame number) of queue. + // + Status = Dev->VirtIo->SetQueueAddress ( + Dev->VirtIo, + &Dev->Ring, + RingBaseShift + ); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + + + // + // step 5 -- Report understood features. + // + if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { + Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); + Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + } + + // + // step 6 -- initialization complete + // + NextDevStat |= VSTAT_DRIVER_OK; + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + + // + // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI + // Block I/O Protocol. + // + Dev->BlockIo.Revision = 0; + Dev->BlockIo.Media = &Dev->BlockIoMedia; + Dev->BlockIo.Reset = &VirtioBlkReset; + Dev->BlockIo.ReadBlocks = &VirtioBlkReadBlocks; + Dev->BlockIo.WriteBlocks = &VirtioBlkWriteBlocks; + Dev->BlockIo.FlushBlocks = &VirtioBlkFlushBlocks; + Dev->EraseBlock.EraseBlocks = &VirtioBlkEraseBlocks; + Dev->BlockIoMedia.MediaId = 0; + Dev->BlockIoMedia.RemovableMedia = FALSE; + Dev->BlockIoMedia.MediaPresent = TRUE; + Dev->BlockIoMedia.LogicalPartition = FALSE; + Dev->BlockIoMedia.ReadOnly = (BOOLEAN) ((Features & VIRTIO_BLK_F_RO) != 0); + Dev->BlockIoMedia.WriteCaching = (BOOLEAN) ((Features & VIRTIO_BLK_F_FLUSH) != 0); + Dev->BlockIoMedia.BlockSize = BlockSize; + Dev->BlockIoMedia.IoAlign = 0; + Dev->BlockIoMedia.LastBlock = DivU64x32 (NumSectors, + BlockSize / 512, NULL) - 1; + + //DEBUG ((DEBUG_INFO, "%s: LbaSize=0x%x[B] NumBlocks=0x%x[Lba]\n", + // __FUNCTION__, Dev->BlockIoMedia.BlockSize, + // (UINT32)Dev->BlockIoMedia.LastBlock + 1)); + + Dev->Signature = VBLK_SIG; + + if (Features & VIRTIO_BLK_F_TOPOLOGY) { + Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + + Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset; + Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp; + Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize; + + //DEBUG ((DEBUG_INFO, "%s: FirstAligned=0x%x[Lba] PhysBlkSize=0x%x[Lba]\n", + // __FUNCTION__, (UINT32)Dev->BlockIoMedia.LowestAlignedLba, + // Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock)); + //DEBUG ((DEBUG_INFO, "%s: OptimalTransferLengthGranularity=0x%x[Lba]\n", + // __FUNCTION__, Dev->BlockIoMedia.OptimalTransferLengthGranularity)); + } + return EFI_SUCCESS; + + UnmapQueue: + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); + + ReleaseQueue: + VirtioRingUninit (Dev->VirtIo, &Dev->Ring); + + Failed: + // + // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device + // Status. VirtIo access failure here should not mask the original error. + // + NextDevStat |= VSTAT_FAILED; + Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); + + return Status; // reached only via Failed above +} + + +/** + + Uninitialize the internals of a virtio-blk device that has been successfully + set up with VirtioBlkInit(). + + @param[in out] Dev The device to clean up. + +**/ +VOID +EFIAPI +VirtioBlkUninit ( + IN OUT VBLK_DEV *Dev + ) +{ + // + // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When + // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from + // the old comms area. + // + Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); + + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); + VirtioRingUninit (Dev->VirtIo, &Dev->Ring); + + SetMem (&Dev->BlockIo, sizeof Dev->BlockIo, 0x00); + SetMem (&Dev->BlockIoMedia, sizeof Dev->BlockIoMedia, 0x00); +} diff --git a/drivers/virtual_media/VirtioBlk.h b/drivers/virtual_media/VirtioBlk.h new file mode 100755 index 0000000..3e277b5 --- /dev/null +++ b/drivers/virtual_media/VirtioBlk.h @@ -0,0 +1,102 @@ +/** @file + + Virtio Block Device specific type and macro definitions corresponding to the + virtio-0.9.5 specification. + + Copyright (C) 2012, Red Hat, Inc. + + 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 _VIRTIO_BLK_H_ +#define _VIRTIO_BLK_H_ + +#include "Virtio.h" + +// +// virtio-0.9.5, Appendix D: Block Device +// +#pragma pack(1) +typedef struct { + UINT8 PhysicalBlockExp; // # of logical blocks per physical block (log2) + UINT8 AlignmentOffset; // offset of first aligned logical block + UINT16 MinIoSize; // suggested minimum I/O size in blocks + UINT32 OptIoSize; // optimal (suggested maximum) I/O size in blocks +} VIRTIO_BLK_TOPOLOGY; + +typedef struct { + UINT64 Capacity; + UINT32 SizeMax; + UINT32 SegMax; + UINT16 Cylinders; + UINT8 Heads; + UINT8 Sectors; + UINT32 BlkSize; + VIRTIO_BLK_TOPOLOGY Topology; +} VIRTIO_BLK_CONFIG; +#pragma pack() + +#define OFFSET_OF_VBLK(Field) OFFSET_OF (VIRTIO_BLK_CONFIG, Field) +#define SIZE_OF_VBLK(Field) (sizeof ((VIRTIO_BLK_CONFIG *) 0)->Field) + +#define VIRTIO_BLK_F_BARRIER BIT0 +#define VIRTIO_BLK_F_SIZE_MAX BIT1 +#define VIRTIO_BLK_F_SEG_MAX BIT2 +#define VIRTIO_BLK_F_GEOMETRY BIT4 +#define VIRTIO_BLK_F_RO BIT5 +#define VIRTIO_BLK_F_BLK_SIZE BIT6 // treated as "logical block size" in + // practice; actual host side + // implementation negotiates "optimal" + // block size separately, via + // VIRTIO_BLK_F_TOPOLOGY +#define VIRTIO_BLK_F_SCSI BIT7 +#define VIRTIO_BLK_F_FLUSH BIT9 // identical to "write cache enabled" +#define VIRTIO_BLK_F_TOPOLOGY BIT10 // information on optimal I/O alignment +#define VIRTIO_BLK_F_DISCARD BIT13 // DISCARD is supported +#define VIRTIO_BLK_F_WRITE_ZEROES BIT14 // WRITE ZEROES is supported + +// +// We keep the status byte separate from the rest of the virtio-blk request +// header. See description of historical scattering at the end of Appendix D: +// we're going to put the status byte in a separate VRING_DESC. +// +#pragma pack(1) +typedef struct { + UINT32 Type; + UINT32 IoPrio; + UINT64 Sector; +} VIRTIO_BLK_REQ; +#pragma pack() + +typedef struct { + UINT64 Sector; + UINT32 num_sectors; + UINT32 flags; +} VIRTIO_DISCARD_RANGE; + +/* Unmap this range (only valid for write zeroes command) */ +#define VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP 0x00000001 + +#define VIRTIO_BLK_T_IN 0x00000000 +#define VIRTIO_BLK_T_OUT 0x00000001 +#define VIRTIO_BLK_T_SCSI_CMD 0x00000002 +#define VIRTIO_BLK_T_SCSI_CMD_OUT 0x00000003 +#define VIRTIO_BLK_T_FLUSH 0x00000004 +#define VIRTIO_BLK_T_FLUSH_OUT 0x00000005 +#define VIRTIO_BLK_T_DISCARD 0x0000000B +#define VIRTIO_BLK_T_WRITE_ZEROES 0x0000000D +#define VIRTIO_BLK_T_INVALID 0x000000FF +#define VIRTIO_BLK_T_BARRIER BIT31 + +#define VIRTIO_BLK_S_OK 0x00 +#define VIRTIO_BLK_S_IOERR 0x01 +#define VIRTIO_BLK_S_UNSUPP 0x02 + +#endif // _VIRTIO_BLK_H_ diff --git a/drivers/virtual_media/VirtioBlkAccessLib.c b/drivers/virtual_media/VirtioBlkAccessLib.c new file mode 100755 index 0000000..d6ea70c --- /dev/null +++ b/drivers/virtual_media/VirtioBlkAccessLib.c @@ -0,0 +1,197 @@ +/** @file + This file provides some helper functions which are specific for EMMC device. + + Copyright (c) 2015 - 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 + 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. + +**/ + +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioPciDeviceLib.h" +#include "VirtioBlkDevice.h" + +static VBLK_DEV *gBlkdev = 0; +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[out] DevBlockInfo The Block Io information of the specified block partition. + + @retval EFI_SUCCESS The Block Io information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +VirtioGetMediaInfo ( + IN __attribute__((unused)) UINTN DeviceIndex, + OUT DEVICE_BLOCK_INFO *DevBlockInfo + ) +{ + DevBlockInfo->BlockNum = gBlkdev->BlockIoMedia.LastBlock + 1; + DevBlockInfo->BlockSize = gBlkdev->BlockIoMedia.BlockSize; + + return EFI_SUCCESS; +} + +/** + This function reads data from VirtioBlk to Memory. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +VirtioReadBlocks ( + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + return VirtioBlkReadBlocks(&gBlkdev->BlockIo, DeviceIndex, StartLBA, BufferSize, Buffer); +} + +/** + This function writes data from Memory to VirtioBlk + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA Target VirtioMedia block number(LBA) where data will be written + @param[in] DataSize Total data size to be written in bytes unit + @param[in] DataAddress Data address in Memory to be copied to VirtioBlk + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +VirtioWriteBlocks ( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN DataSize, + IN VOID *DataAddress + ) +{ + return VirtioBlkWriteBlocks(&gBlkdev->BlockIo, DeviceIndex, StartLBA, DataSize, DataAddress); +} + +/** + This function erase a specified number of device blocks. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address to be erased. + The caller is responsible for erasing only legitimate locations. + @param[in] Size The size in bytes to be erased. This must be a multiple of the + physical block size of the device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +VirtioEraseBlocks ( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN Size + ) +{ + return VirtioBlkEraseBlocks(&gBlkdev->EraseBlock, DeviceIndex, StartLBA, NULL, Size); +} + + +/** + This function initializes VirtioBlk device + + @param[in] VirtioBlkPciBase VirtioMedia Host Controller's PCI ConfigSpace Base address + @param[in] VirtioBlkInitMode For the performance optimization, + VirtioBlk initialization is separated to early init and the rest of init. + + DevInitAll : Execute generic VirtioMedia device initialization + DevInitOnlyPhase1 : Execute only early phase initialization + DevInitOnlyPhase2 : Skip early phase initialization, + and then initialize the rest of initialization + + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EFIAPI +VirtioMediaInitialize ( + IN UINTN VirtioBlkPciBase + ) +{ + VIRTIO_PCI_DEVICE *VirtPci; + VBLK_DEV *Dev; + EFI_STATUS Status; + + VirtPci = (VIRTIO_PCI_DEVICE *) malloc (sizeof *VirtPci); + if (VirtPci == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = VirtioPciInit (VirtPci, VirtioBlkPciBase); + + if (EFI_ERROR (Status)) { + free (VirtPci); + } + + Dev = (VBLK_DEV *) malloc (sizeof *Dev); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Dev->VirtIo = &VirtPci->VirtioDevice; + + Status = VirtioBlkInit (Dev); + + if (EFI_ERROR (Status)) { + free (Dev); + free (VirtPci); + } + + gBlkdev = Dev; + + return EFI_SUCCESS; +} diff --git a/drivers/virtual_media/VirtioBlkAccessLib.h b/drivers/virtual_media/VirtioBlkAccessLib.h new file mode 100755 index 0000000..630b6b6 --- /dev/null +++ b/drivers/virtual_media/VirtioBlkAccessLib.h @@ -0,0 +1,143 @@ +/** @file + + Copyright (c) 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 + 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. + +**/ + +/* + * VirtioBlkAccessLib.h + */ +#ifndef _VIRTIOBLK_ACCESS_LIB_H_ +#define _VIRTIOBLK_ACCESS_LIB_H_ + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[out] DevBlockInfo The Block Io information of the specified block partition. + + @retval EFI_SUCCESS The Block Io information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +VirtioGetMediaInfo ( + IN UINTN DeviceIndex, + OUT DEVICE_BLOCK_INFO *DevBlockInfo + ); + +/** + This function reads data from VirtioBlk to Memory. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +VirtioReadBlocks ( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + This function writes data from Memory to VirtioBlk + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA Target VirtioMedia block number(LBA) where data will be written + @param[in] DataSize Total data size to be written in bytes unit + @param[in] DataAddress Data address in Memory to be copied to VirtioBlk + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +VirtioWriteBlocks ( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN DataSize, + IN VOID *DataAddress + ); + +/** + This function erase a specified number of device blocks. + + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. + @param[in] StartLBA The starting logical block address to be erased. + The caller is responsible for erasing only legitimate locations. + @param[in] Size The size in bytes to be erased. This must be a multiple of the + physical block size of the device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +VirtioEraseBlocks ( + IN UINTN DeviceIndex, + IN EFI_LBA StartLBA, + IN UINTN Size + ); + +/** + This function initializes VirtioBlk device + + @param[in] VirtioBlkPciBase VirtioMedia Host Controller's PCI ConfigSpace Base address + @param[in] VirtioBlkInitMode For the performance optimization, + VirtioBlk initialization is separated to early init and the rest of init. + + DevInitAll : Execute generic VirtioMedia device initialization + DevInitOnlyPhase1 : Execute only early phase initialization + DevInitOnlyPhase2 : Skip early phase initialization, + and then initialize the rest of initialization + + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EFIAPI +VirtioMediaInitialize ( + IN UINTN VirtioBlkPciBase + ); +#endif diff --git a/drivers/virtual_media/VirtioBlkDevice.h b/drivers/virtual_media/VirtioBlkDevice.h new file mode 100755 index 0000000..0307d68 --- /dev/null +++ b/drivers/virtual_media/VirtioBlkDevice.h @@ -0,0 +1,213 @@ +/** @file + + Internal definitions for the virtio-blk driver, which produces Block I/O + Protocol instances for virtio-blk devices. + + Copyright (C) 2012, Red Hat, Inc. + + 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 _VIRTIO_BLK_DEVICE_H_ +#define _VIRTIO_BLK_DEVICE_H_ + +#include + +#include "Virtio.h" +#include "VirtioDevice.h" +#include "protocol/EraseBlock.h" + +#ifndef EFI_BLOCK_IO_PROTOCOL +#define EFI_BLOCK_IO_PROTOCOL EFI_BLOCK_IO +#endif + +#ifndef EFI_PEI_LBA +#define EFI_PEI_LBA EFI_LBA +#endif + +#define VBLK_SIG SIGNATURE_32 ('V', 'B', 'L', 'K') + +typedef struct { + // + // Parts of this structure are initialized / torn down in various functions + // at various call depths. The table to the right should make it easier to + // track them. + // + // field init function init dpth + // --------------------- ------------------ --------- + UINT32 Signature; // DriverBindingStart 0 + VIRTIO_DEVICE_PROTOCOL *VirtIo; // DriverBindingStart 0 + EFI_EVENT ExitBoot; // DriverBindingStart 0 + VRING Ring; // VirtioRingInit 2 + EFI_BLOCK_IO_PROTOCOL BlockIo; // VirtioBlkInit 1 + EFI_ERASE_BLOCK_PROTOCOL EraseBlock; + EFI_BLOCK_IO_MEDIA BlockIoMedia; // VirtioBlkInit 1 + VOID *RingMap; // VirtioRingMap 2 +} VBLK_DEV; + +#define VIRTIO_BLK_FROM_BLOCK_IO(BlockIoPointer) \ + CR (BlockIoPointer, VBLK_DEV, BlockIo, VBLK_SIG) + +#define VIRTIO_BLK_FROM_ERASE_BLOCK(EraseBlockPointer) \ + CR (EraseBlockPointer, VBLK_DEV, EraseBlock, VBLK_SIG) + +/** + + Set up all BlockIo and virtio-blk aspects of this driver for the specified + device. + + @param[in out] Dev The driver instance to configure. The caller is + responsible for Dev->VirtIo's validity (ie. working IO + access to the underlying virtio-blk device). + + @retval EFI_SUCCESS Setup complete. + + @retval EFI_UNSUPPORTED The driver is unable to work with the virtio ring or + virtio-blk attributes the host provides. + + @return Error codes from VirtioRingInit() or + VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE or + VirtioRingMap(). + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkInit ( + IN OUT VBLK_DEV *Dev + ); + +/** + + Uninitialize the internals of a virtio-blk device that has been successfully + set up with VirtioBlkInit(). + + @param[in out] Dev The device to clean up. + +**/ +VOID +EFIAPI +VirtioBlkUninit ( + IN OUT VBLK_DEV *Dev + ); + +// +// UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol +// Driver Writer's Guide for UEFI 2.3.1 v1.01, +// 24.2 Block I/O Protocol Implementations +// +EFI_STATUS +EFIAPI +VirtioBlkReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + + +/** + + ReadBlocks() operation for virtio-blk. + + See + - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, EFI_BLOCK_IO_PROTOCOL.ReadBlocks(). + - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.2. ReadBlocks() and + ReadBlocksEx() Implementation. + + Parameter checks and conformant return values are implemented in + VerifyReadWriteRequest() and SynchronousRequest(). + + A zero BufferSize doesn't seem to be prohibited, so do nothing in that case, + successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_PEI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + + +/** + + WriteBlocks() operation for virtio-blk. + + See + - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, EFI_BLOCK_IO_PROTOCOL.WriteBlocks(). + - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.3 WriteBlocks() and + WriteBlockEx() Implementation. + + Parameter checks and conformant return values are implemented in + VerifyReadWriteRequest() and SynchronousRequest(). + + A zero BufferSize doesn't seem to be prohibited, so do nothing in that case, + successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + + EraseBlocks() operation for virtio-blk. + + A zero BufferSize doesn't seem to be prohibited, so do nothing in that case, + successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ); + +/** + + FlushBlocks() operation for virtio-blk. + + See + - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O + Protocol, EFI_BLOCK_IO_PROTOCOL.FlushBlocks(). + - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.4 FlushBlocks() and + FlushBlocksEx() Implementation. + + If the underlying virtio-blk device doesn't support flushing (ie. + write-caching), then this function should not be called by higher layers, + according to EFI_BLOCK_IO_MEDIA characteristics set in VirtioBlkInit(). + Should they do nonetheless, we do nothing, successfully. + +**/ + +EFI_STATUS +EFIAPI +VirtioBlkFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +#endif // _VIRTIO_BLK_DXE_H_ diff --git a/drivers/virtual_media/VirtioDevice.h b/drivers/virtual_media/VirtioDevice.h new file mode 100755 index 0000000..4ef5cb7 --- /dev/null +++ b/drivers/virtual_media/VirtioDevice.h @@ -0,0 +1,527 @@ +/** @file + Virtio Device + + DISCLAIMER: the VIRTIO_DEVICE_PROTOCOL introduced here is a work in progress, + and should not be used outside of the EDK II tree. + + Copyright (c) 2013, ARM Ltd. All rights reserved.
+ Copyright (c) 2017, AMD 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 __VIRTIO_DEVICE_H__ +#define __VIRTIO_DEVICE_H__ + +//#include +#include "Virtio.h" + +// +// VirtIo Specification Revision: Major[31:24].Minor[23:16].Revision[15:0] +// +#define VIRTIO_SPEC_REVISION(major,minor,revision) \ + ((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | ((revision) & 0xFFFF)) + +#define VIRTIO_DEVICE_PROTOCOL_GUID { \ + 0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a }\ + } + +typedef struct _VIRTIO_DEVICE_PROTOCOL VIRTIO_DEVICE_PROTOCOL; + +// +// VIRTIO Operation for VIRTIO_MAP_SHARED +// +typedef enum { + // + // A read operation from system memory by a bus master + // + VirtioOperationBusMasterRead, + // + // A write operation to system memory by a bus master + // + VirtioOperationBusMasterWrite, + // + // Provides both read and write access to system memory by both the + // processor and a bus master + // + VirtioOperationBusMasterCommonBuffer, +} VIRTIO_MAP_OPERATION; + +/** + + Read a word from the device-specific I/O region of the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] FieldOffset Source offset. + + @param[in] FieldSize Source field size in bytes, must be in {1, 2, 4, 8}. + + @param[in] BufferSize Number of bytes available in the target buffer. Must + equal FieldSize. + + @param[out] Buffer Target buffer. + + @retval EFI_SUCCESS The data was read successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and read size. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_DEVICE_READ) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + + Write a word to the device-specific I/O region of the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] FieldOffset Destination offset. + + @param[in] FieldSize Destination field size in bytes, + must be in {1, 2, 4, 8}. + + @param[out] Value Value to write. + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_DEVICE_WRITE) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINT64 Value + ); + +/** + Read the device features field from the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[out] DeviceFeatures The device features field. + + @retval EFI_SUCCESS The data was read successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and read size. + @retval EFI_INVALID_PARAMETER DeviceFeatures is NULL +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_GET_DEVICE_FEATURES) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT64 *DeviceFeatures + ); + +/** + Write the guest features field in the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] Features The guest features field + +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_GUEST_FEATURES) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT64 Features + ); + +/** + Write the queue address field(s) in the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] Ring The initialized VRING object to take the + addresses from. The caller is responsible for + ensuring that on input, all Ring->NumPages pages, + starting at Ring->Base, have been successfully + mapped with a single call to + This->MapSharedBuffer() for CommonBuffer bus + master operation. + + @param[in] RingBaseShift Adding this value using UINT64 arithmetic to the + addresses found in Ring translates them from + system memory to bus addresses. The caller shall + calculate RingBaseShift as + (DeviceAddress - (UINT64)(UINTN)HostAddress), + where DeviceAddress and HostAddress (i.e., + Ring->Base) were output and input parameters of + This->MapSharedBuffer(), respectively. + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_QUEUE_ADDRESS) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VRING *Ring, + IN UINT64 RingBaseShift + ); + +/** + + Write the queue select field in the Virtio Header. + + Writing to the queue select field sets the index of the queue to which + operations such as SetQueueAlign and GetQueueNumMax apply. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] Index The index of the queue to select + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_QUEUE_SEL) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Index + ); + +/** + + Write the queue notify field in the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] Address The 32-bit Queue Notify field + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_QUEUE_NOTIFY) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Index + ); + +/** + Write the queue alignment field in the Virtio Header. + + The queue to which the alignment applies is selected by the Queue Select + field. + + Note: This operation is not implemented by the VirtIo over PCI. The PCI + implementation of this protocol returns EFI_SUCCESS. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] Alignment The alignment boundary of the Used Ring in bytes. + Must be a power of 2. + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_QUEUE_ALIGN) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT32 Alignment + ); + +/** + Write the guest page size. + + Note: This operation is not implemented by the VirtIo over PCI. The PCI + implementation of this protocol returns EFI_SUCCESS. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] PageSize Size of the Guest page in bytes. + Must be a power of 2. + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_PAGE_SIZE) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT32 PageSize + ); + +/** + + Get the size of the virtqueue selected by the queue select field. + + See Virtio spec Section 2.3 + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[out] QueueNumMax The size of the virtqueue in bytes. + Always a power of 2. + + @retval EFI_SUCCESS The data was read successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and read size. + @retval EFI_INVALID_PARAMETER QueueNumMax is NULL +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_GET_QUEUE_NUM_MAX) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT16 *QueueNumMax + ); + +/** + + Write to the QueueNum field in the Virtio Header. + + This function only applies to Virtio-MMIO and may be a stub for other + implementations. See Virtio Spec appendix X. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] QueueSize The number of elements in the queue. + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_QUEUE_NUM) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 QueueSize + ); + +/** + + Get the DeviceStatus field from the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[out] DeviceStatus The 8-bit value for the Device status field + + @retval EFI_SUCCESS The data was read successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and read size. + @retval EFI_INVALID_PARAMETER DeviceStatus is NULL +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_GET_DEVICE_STATUS) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT8 *DeviceStatus + ); + +/** + + Write the DeviceStatus field in the Virtio Header. + + @param[in] This This instance of VIRTIO_DEVICE_PROTOCOL + + @param[in] DeviceStatus The 8-bit value for the Device status field + + @retval EFI_SUCCESS The data was written successfully. + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and write size. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_SET_DEVICE_STATUS) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT8 DeviceStatus + ); + +/** + + Allocates pages that are suitable for an VirtioOperationBusMasterCommonBuffer + mapping. This means that the buffer allocated by this function supports + simultaneous access by both the processor and the bus master. The device + address that the bus master uses to access the buffer must be retrieved with + a call to VIRTIO_MAP_SHARED. + + @param[in] This The protocol instance pointer. + + @param[in] Pages The number of pages to allocate. + + @param[in,out] HostAddress A pointer to store the system memory base + address of the allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_ALLOCATE_SHARED)( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN Pages, + IN OUT VOID **HostAddress + ); + +/** + Frees memory that was allocated with VIRTIO_ALLOCATE_SHARED. + + @param[in] This The protocol instance pointer. + + @param[in] Pages The number of pages to free. + + @param[in] HostAddress The system memory base address of the allocated + range. + +**/ +typedef +VOID +(EFIAPI *VIRTIO_FREE_SHARED)( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ); + +/** + Provides the virtio device address required to access system memory from a + DMA bus master. + + The interface follows the same usage pattern as defined in UEFI spec 2.6 + (Section 13.2 PCI Root Bridge I/O Protocol) + + @param[in] This The protocol instance pointer. + + @param[in] Operation Indicates if the bus master is going to + read or write to system memory. + + @param[in] HostAddress The system memory address to map to shared + buffer address. + + @param[in,out] NumberOfBytes On input the number of bytes to map. + On output the number of bytes that were + mapped. + + @param[out] DeviceAddress The resulting shared map address for the + bus master to access the hosts HostAddress. + + @param[out] Mapping A resulting token to pass to + VIRTIO_UNMAP_SHARED. + + @retval EFI_SUCCESS The range was mapped for the returned + NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a + common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to + a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the + requested address. +**/ + +typedef +EFI_STATUS +(EFIAPI *VIRTIO_MAP_SHARED) ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VIRTIO_MAP_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the VIRTIO_MAP_SHARED operation and releases any corresponding + resources. + + @param[in] This The protocol instance pointer. + + @param[in] Mapping The mapping token returned from + VIRTIO_MAP_SHARED. + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + VIRTIO_MAP_SHARED. Passing an invalid Mapping + token can cause undefined behavior. + @retval EFI_DEVICE_ERROR The data was not committed to the target + system memory. +**/ +typedef +EFI_STATUS +(EFIAPI *VIRTIO_UNMAP_SHARED)( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VOID *Mapping + ); + +/// +/// This protocol provides an abstraction over the VirtIo transport layer +/// +/// DISCLAIMER: this protocol is a work in progress, and should not be used +/// outside of the EDK II tree. +/// +struct _VIRTIO_DEVICE_PROTOCOL { + // + // VirtIo Specification Revision encoded with VIRTIO_SPEC_REVISION() + // + UINT32 Revision; + // + // From the Virtio Spec + // + INT32 SubSystemDeviceId; + + VIRTIO_GET_DEVICE_FEATURES GetDeviceFeatures; + VIRTIO_SET_GUEST_FEATURES SetGuestFeatures; + + VIRTIO_SET_QUEUE_ADDRESS SetQueueAddress; + + VIRTIO_SET_QUEUE_SEL SetQueueSel; + + VIRTIO_SET_QUEUE_NOTIFY SetQueueNotify; + + VIRTIO_SET_QUEUE_ALIGN SetQueueAlign; + VIRTIO_SET_PAGE_SIZE SetPageSize; + + VIRTIO_GET_QUEUE_NUM_MAX GetQueueNumMax; + VIRTIO_SET_QUEUE_NUM SetQueueNum; + + VIRTIO_GET_DEVICE_STATUS GetDeviceStatus; + VIRTIO_SET_DEVICE_STATUS SetDeviceStatus; + + // + // Functions to read/write Device Specific headers + // + VIRTIO_DEVICE_WRITE WriteDevice; + VIRTIO_DEVICE_READ ReadDevice; + + // + // Functions to allocate, free, map and unmap shared buffer + // + VIRTIO_ALLOCATE_SHARED AllocateSharedPages; + VIRTIO_FREE_SHARED FreeSharedPages; + VIRTIO_MAP_SHARED MapSharedBuffer; + VIRTIO_UNMAP_SHARED UnmapSharedBuffer; +}; + +extern EFI_GUID gVirtioDeviceProtocolGuid; + +#endif diff --git a/drivers/virtual_media/VirtioDeviceCommon.h b/drivers/virtual_media/VirtioDeviceCommon.h new file mode 100755 index 0000000..6c803a2 --- /dev/null +++ b/drivers/virtual_media/VirtioDeviceCommon.h @@ -0,0 +1,201 @@ +/** @file + + Copyright (c) 2018, 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 __VIRTIO_DEVICE_COMMON_H__ +#define __VIRTIO_DEVICE_COMMON_H__ + +#include + +#define VIRTUAL_MEDIA_DEBUG 0 +#if VIRTUAL_MEDIA_DEBUG +#define virtual_media_dbg(a,...) {printf(a);printf(__VA_ARGS__);} +#else +#define virtual_media_dbg(...) +#endif + +#ifdef DEBUG_INFO +#undef DEBUG_INFO +#endif +#define DEBUG_INFO "VIRTUAL MEDIA Debug: " + +#ifdef DEBUG +#undef DEBUG +#endif +#define DEBUG(a) virtual_media_dbg a + +#ifdef ASSERT +#undef ASSERT +#endif +#define ASSERT assert + +#define MemoryFence() __asm__ __volatile__("": : :"memory") +/// +/// Maximum values for common UEFI Data Types +/// +#define MAX_INT8 ((INT8)0x7F) +#define MAX_UINT8 ((UINT8)0xFF) +#define MAX_INT16 ((INT16)0x7FFF) +#define MAX_UINT16 ((UINT16)0xFFFF) +#define MAX_INT32 ((INT32)0x7FFFFFFF) +#define MAX_UINT32 ((UINT32)0xFFFFFFFF) +#define MAX_INT64 ((INT64)0x7FFFFFFFFFFFFFFFULL) +#define MAX_UINT64 ((UINT64)0xFFFFFFFFFFFFFFFFULL) + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 +#define BIT32 0x0000000100000000ULL +#define BIT33 0x0000000200000000ULL +#define BIT34 0x0000000400000000ULL +#define BIT35 0x0000000800000000ULL +#define BIT36 0x0000001000000000ULL +#define BIT37 0x0000002000000000ULL +#define BIT38 0x0000004000000000ULL +#define BIT39 0x0000008000000000ULL +#define BIT40 0x0000010000000000ULL +#define BIT41 0x0000020000000000ULL +#define BIT42 0x0000040000000000ULL +#define BIT43 0x0000080000000000ULL +#define BIT44 0x0000100000000000ULL +#define BIT45 0x0000200000000000ULL +#define BIT46 0x0000400000000000ULL +#define BIT47 0x0000800000000000ULL +#define BIT48 0x0001000000000000ULL +#define BIT49 0x0002000000000000ULL +#define BIT50 0x0004000000000000ULL +#define BIT51 0x0008000000000000ULL +#define BIT52 0x0010000000000000ULL +#define BIT53 0x0020000000000000ULL +#define BIT54 0x0040000000000000ULL +#define BIT55 0x0080000000000000ULL +#define BIT56 0x0100000000000000ULL +#define BIT57 0x0200000000000000ULL +#define BIT58 0x0400000000000000ULL +#define BIT59 0x0800000000000000ULL +#define BIT60 0x1000000000000000ULL +#define BIT61 0x2000000000000000ULL +#define BIT62 0x4000000000000000ULL +#define BIT63 0x8000000000000000ULL + +/** + The macro that returns the byte offset of a field in a data structure. + + This function returns the offset, in bytes, of field specified by Field from the + beginning of the data structure specified by TYPE. If TYPE does not contain Field, + the module will not compile. + + @param TYPE The name of the data structure that contains the field specified by Field. + @param Field The name of the field in the data structure. + + @return Offset, in bytes, of field. + +**/ +#ifdef __GNUC__ +#if __GNUC__ >= 4 +#define OFFSET_OF(TYPE, Field) ((UINTN) __builtin_offsetof(TYPE, Field)) +#endif +#endif + +#ifndef OFFSET_OF +#define OFFSET_OF(TYPE, Field) ((UINTN) &(((TYPE *)0)->Field)) +#endif + +/** + Returns a 16-bit signature built from 2 ASCII characters. + + This macro returns a 16-bit value built from the two ASCII characters specified + by A and B. + + @param A The first ASCII character. + @param B The second ASCII character. + + @return A 16-bit value built from the two ASCII characters specified by A and B. + +**/ +#define SIGNATURE_16(A, B) ((A) | (B << 8)) + +/** + Returns a 32-bit signature built from 4 ASCII characters. + + This macro returns a 32-bit value built from the four ASCII characters specified + by A, B, C, and D. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + + @return A 32-bit value built from the two ASCII characters specified by A, B, + C and D. + +**/ +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) + +/** + Returns a 64-bit signature built from 8 ASCII characters. + + This macro returns a 64-bit value built from the eight ASCII characters specified + by A, B, C, D, E, F, G,and H. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + @param E The fifth ASCII character. + @param F The sixth ASCII character. + @param G The seventh ASCII character. + @param H The eighth ASCII character. + + @return A 64-bit value built from the two ASCII characters specified by A, B, + C, D, E, F, G and H. + +**/ +#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)) + +typedef struct { + UINT64 BlockNum; + UINT32 BlockSize; +} DEVICE_BLOCK_INFO; + +#endif diff --git a/drivers/virtual_media/VirtioLib.c b/drivers/virtual_media/VirtioLib.c new file mode 100755 index 0000000..45f26d1 --- /dev/null +++ b/drivers/virtual_media/VirtioLib.c @@ -0,0 +1,567 @@ +/** @file + + Utility functions used by virtio device drivers. + + Copyright (C) 2012-2016, Red Hat, Inc. + Portion of Copyright (C) 2013, ARM Ltd. + Copyright (C) 2017, AMD 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. + +**/ + +#include +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioLib.h" + +#define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) + +#define EFI_PAGES_TO_SIZE(Pages) ((Pages) << EFI_PAGE_SHIFT) + + +/** + + Configure a virtio ring. + + This function sets up internal storage (the guest-host communication area) + and lays out several "navigation" (ie. no-ownership) pointers to parts of + that storage. + + Relevant sections from the virtio-0.9.5 spec: + - 1.1 Virtqueues, + - 2.3 Virtqueue Configuration. + + @param[in] VirtIo The virtio device which will use the ring. + + @param[in] The number of descriptors to allocate for the + virtio ring, as requested by the host. + + @param[out] Ring The virtio ring to set up. + + @return Status codes propagated from + VirtIo->AllocateSharedPages(). + + @retval EFI_SUCCESS Allocation and setup successful. Ring->Base + (and nothing else) is responsible for + deallocation. + +**/ +EFI_STATUS +EFIAPI +VirtioRingInit ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN UINT16 QueueSize, + OUT VRING *Ring + ) +{ + EFI_STATUS Status; + UINTN RingSize; + volatile UINT8 *RingPagesPtr; + + RingSize = ALIGN_VALUE ( + sizeof *Ring->Desc * QueueSize + + sizeof *Ring->Avail.Flags + + sizeof *Ring->Avail.Idx + + sizeof *Ring->Avail.Ring * QueueSize + + sizeof *Ring->Avail.UsedEvent, + EFI_PAGE_SIZE); + + RingSize += ALIGN_VALUE ( + sizeof *Ring->Used.Flags + + sizeof *Ring->Used.Idx + + sizeof *Ring->Used.UsedElem * QueueSize + + sizeof *Ring->Used.AvailEvent, + EFI_PAGE_SIZE); + + // + // Allocate a shared ring buffer + // + Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize); + Status = VirtIo->AllocateSharedPages ( + VirtIo, + Ring->NumPages, + &Ring->Base + ); + if (EFI_ERROR (Status)) { + return Status; + } + SetMem (Ring->Base, RingSize, 0x00); + RingPagesPtr = Ring->Base; + + Ring->Desc = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Desc * QueueSize; + + Ring->Avail.Flags = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.Flags; + + Ring->Avail.Idx = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.Idx; + + Ring->Avail.Ring = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize; + + Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.UsedEvent; + + RingPagesPtr = (volatile UINT8 *) Ring->Base + + ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base, + EFI_PAGE_SIZE); + + Ring->Used.Flags = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.Flags; + + Ring->Used.Idx = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.Idx; + + Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize; + + Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.AvailEvent; + + Ring->QueueSize = QueueSize; + return EFI_SUCCESS; +} + + +/** + + Tear down the internal resources of a configured virtio ring. + + The caller is responsible to stop the host from using this ring before + invoking this function: the VSTAT_DRIVER_OK bit must be clear in + VhdrDeviceStatus. + + @param[in] VirtIo The virtio device which was using the ring. + + @param[out] Ring The virtio ring to clean up. + +**/ +VOID +EFIAPI +VirtioRingUninit ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN OUT VRING *Ring + ) +{ + VirtIo->FreeSharedPages (VirtIo, Ring->NumPages, Ring->Base); + SetMem (Ring, sizeof *Ring, 0x00); +} + + +/** + + Turn off interrupt notifications from the host, and prepare for appending + multiple descriptors to the virtio ring. + + The calling driver must be in VSTAT_DRIVER_OK state. + + @param[in,out] Ring The virtio ring we intend to append descriptors to. + + @param[out] Indices The DESC_INDICES structure to initialize. + +**/ +VOID +EFIAPI +VirtioPrepare ( + IN OUT VRING *Ring, + OUT DESC_INDICES *Indices + ) +{ + // + // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device. + // We're going to poll the answer, the host should not send an interrupt. + // + *Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT; + + // + // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device. + // + // Since we support only one in-flight descriptor chain, we can always build + // that chain starting at entry #0 of the descriptor table. + // + Indices->HeadDescIdx = 0; + Indices->NextDescIdx = Indices->HeadDescIdx; +} + +/** + + Append a contiguous buffer for transmission / reception via the virtio ring. + + This function implements the following section from virtio-0.9.5: + - 2.4.1.1 Placing Buffers into the Descriptor Table + + Free space is taken as granted, since the individual drivers support only + synchronous requests and host side status is processed in lock-step with + request submission. It is the calling driver's responsibility to verify the + ring size in advance. + + The caller is responsible for initializing *Indices with VirtioPrepare() + first. + + @param[in,out] Ring The virtio ring to append the buffer to, + as a descriptor. + + @param[in] BufferDeviceAddress (Bus master device) start address of the + transmit / receive buffer. + + @param[in] BufferSize Number of bytes to transmit or receive. + + @param[in] Flags A bitmask of VRING_DESC_F_* flags. The + caller computes this mask dependent on + further buffers to append and transfer + direction. VRING_DESC_F_INDIRECT is + unsupported. The VRING_DESC.Next field is + always set, but the host only interprets + it dependent on VRING_DESC_F_NEXT. + + @param[in,out] Indices Indices->HeadDescIdx is not accessed. + On input, Indices->NextDescIdx identifies + the next descriptor to carry the buffer. + On output, Indices->NextDescIdx is + incremented by one, modulo 2^16. + +**/ +VOID +EFIAPI +VirtioAppendDesc ( + IN OUT VRING *Ring, + IN UINT64 BufferDeviceAddress, + IN UINT32 BufferSize, + IN UINT16 Flags, + IN OUT DESC_INDICES *Indices + ) +{ + volatile VRING_DESC *Desc; + + //DEBUG ((DEBUG_INFO, "VirtioAppendDesc Addr 0x%x, Size 0x%x, Flags 0x%x\n", (UINT32)BufferDeviceAddress, BufferSize, Flags)) + + Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize]; + Desc->Addr = BufferDeviceAddress; + Desc->Len = BufferSize; + Desc->Flags = Flags; + Desc->Next = Indices->NextDescIdx % Ring->QueueSize; +} + + +/** + + Notify the host about the descriptor chain just built, and wait until the + host processes it. + + @param[in] VirtIo The target virtio device to notify. + + @param[in] VirtQueueId Identifies the queue for the target device. + + @param[in,out] Ring The virtio ring with descriptors to submit. + + @param[in] Indices Indices->NextDescIdx is not accessed. + Indices->HeadDescIdx identifies the head descriptor + of the descriptor chain. + + @param[out] UsedLen On success, the total number of bytes, consecutively + across the buffers linked by the descriptor chain, + that the host wrote. May be NULL if the caller + doesn't care, or can compute the same information + from device-specific request structures linked by the + descriptor chain. + + @return Error code from VirtIo->SetQueueNotify() if it fails. + + @retval EFI_SUCCESS Otherwise, the host processed all descriptors. + +**/ +EFI_STATUS +EFIAPI +VirtioFlush ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN UINT16 VirtQueueId, + IN OUT VRING *Ring, + IN DESC_INDICES *Indices, + OUT UINT32 *UsedLen OPTIONAL + ) +{ + UINT16 NextAvailIdx; + UINT16 LastUsedIdx; + EFI_STATUS Status; + UINTN PollPeriodUsecs; + + // + // virtio-0.9.5, 2.4.1.2 Updating the Available Ring + // + // It is not exactly clear from the wording of the virtio-0.9.5 + // specification, but each entry in the Available Ring references only the + // head descriptor of any given descriptor chain. + // + NextAvailIdx = *Ring->Avail.Idx; + // + // (Due to our lock-step progress, this is where the host will produce the + // used element with the head descriptor's index in it.) + // + LastUsedIdx = NextAvailIdx; + Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] = + Indices->HeadDescIdx % Ring->QueueSize; + + // + // virtio-0.9.5, 2.4.1.3 Updating the Index Field + // + MemoryFence(); + *Ring->Avail.Idx = NextAvailIdx; + + // + // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are + // OK. + // + MemoryFence(); + Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device + // Wait until the host processes and acknowledges our descriptor chain. The + // condition we use for polling is greatly simplified and relies on the + // synchronous, lock-step progress. + // + // Keep slowing down until we reach a poll period of slightly above 1 ms. + // + PollPeriodUsecs = 1; + MemoryFence(); + while (*Ring->Used.Idx != NextAvailIdx) { + udelay(PollPeriodUsecs); + //TBD sleep + if (PollPeriodUsecs < 1024) { + PollPeriodUsecs *= 2; + } + MemoryFence(); + } + + MemoryFence(); + + if (UsedLen != NULL) { + volatile CONST VRING_USED_ELEM *UsedElem; + + UsedElem = &Ring->Used.UsedElem[LastUsedIdx % Ring->QueueSize]; + ASSERT (UsedElem->Id == Indices->HeadDescIdx); + *UsedLen = UsedElem->Len; + } + + return EFI_SUCCESS; +} + + +/** + + Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver + understands. + + In VirtIo 1.0, a device can reject a self-inconsistent feature bitmap through + the new VSTAT_FEATURES_OK status bit. (For example if the driver requests a + higher level feature but clears a prerequisite feature.) This function is a + small wrapper around VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures() that also + verifies if the VirtIo 1.0 device accepts the feature bitmap. + + @param[in] VirtIo Report feature bits to this device. + + @param[in] Features The set of feature bits that the driver wishes + to report. The caller is responsible to perform + any masking before calling this function; the + value is directly written with + VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures(). + + @param[in,out] DeviceStatus On input, the status byte most recently written + to the device's status register. On output (even + on error), DeviceStatus will be updated so that + it is suitable for further status bit + manipulation and writing to the device's status + register. + + @retval EFI_SUCCESS The device accepted the configuration in Features. + + @return EFI_UNSUPPORTED The device rejected the configuration in Features. + + @retval EFI_UNSUPPORTED VirtIo->Revision is smaller than 1.0.0. + + @return Error codes from the SetGuestFeatures(), + SetDeviceStatus(), GetDeviceStatus() member + functions. + +**/ +EFI_STATUS +EFIAPI +Virtio10WriteFeatures ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN UINT64 Features, + IN OUT UINT8 *DeviceStatus + ) +{ + EFI_STATUS Status; + + if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { + return EFI_UNSUPPORTED; + } + + Status = VirtIo->SetGuestFeatures (VirtIo, Features); + if (EFI_ERROR (Status)) { + return Status; + } + + *DeviceStatus |= VSTAT_FEATURES_OK; + Status = VirtIo->SetDeviceStatus (VirtIo, *DeviceStatus); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = VirtIo->GetDeviceStatus (VirtIo, DeviceStatus); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((*DeviceStatus & VSTAT_FEATURES_OK) == 0) { + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + Provides the virtio device address required to access system memory from a + DMA bus master. + + The interface follows the same usage pattern as defined in UEFI spec 2.6 + (Section 13.2 PCI Root Bridge I/O Protocol) + + The VirtioMapAllBytesInSharedBuffer() is similar to VIRTIO_MAP_SHARED + with exception that NumberOfBytes is IN-only parameter. The function + maps all the bytes specified in NumberOfBytes param in one consecutive + range. + + @param[in] VirtIo The virtio device for which the mapping is + requested. + + @param[in] Operation Indicates if the bus master is going to + read or write to system memory. + + @param[in] HostAddress The system memory address to map to shared + buffer address. + + @param[in] NumberOfBytes Number of bytes to map. + + @param[out] DeviceAddress The resulting shared map address for the + bus master to access the hosts HostAddress. + + @param[out] Mapping A resulting token to pass to + VIRTIO_UNMAP_SHARED. + + + @retval EFI_SUCCESS The NumberOfBytes is succesfully mapped. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a + common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to + a lack of resources. This includes the case + when NumberOfBytes bytes cannot be mapped + in one consecutive range. + @retval EFI_DEVICE_ERROR The system hardware could not map the + requested address. +**/ +EFI_STATUS +EFIAPI +VirtioMapAllBytesInSharedBuffer ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN VIRTIO_MAP_OPERATION Operation, + IN VOID *HostAddress, + IN UINTN NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + VOID *MapInfo; + UINTN Size; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + + Size = NumberOfBytes; + Status = VirtIo->MapSharedBuffer ( + VirtIo, + Operation, + HostAddress, + &Size, + &PhysicalAddress, + &MapInfo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Size < NumberOfBytes) { + goto Failed; + } + + *Mapping = MapInfo; + *DeviceAddress = PhysicalAddress; + + return EFI_SUCCESS; + + Failed: + VirtIo->UnmapSharedBuffer (VirtIo, MapInfo); + return EFI_OUT_OF_RESOURCES; +} + +/** + + Map the ring buffer so that it can be accessed equally by both guest + and hypervisor. + + @param[in] VirtIo The virtio device instance. + + @param[in] Ring The virtio ring to map. + + @param[out] RingBaseShift A resulting translation offset, to be + passed to VirtIo->SetQueueAddress(). + + @param[out] Mapping A resulting token to pass to + VirtIo->UnmapSharedBuffer(). + + @return Status code from VirtIo->MapSharedBuffer() +**/ +EFI_STATUS +EFIAPI +VirtioRingMap ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN VRING *Ring, + OUT UINT64 *RingBaseShift, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS DeviceAddress; + + Status = VirtioMapAllBytesInSharedBuffer ( + VirtIo, + VirtioOperationBusMasterCommonBuffer, + Ring->Base, + EFI_PAGES_TO_SIZE (Ring->NumPages), + &DeviceAddress, + Mapping + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base; + return EFI_SUCCESS; +} diff --git a/drivers/virtual_media/VirtioLib.h b/drivers/virtual_media/VirtioLib.h new file mode 100755 index 0000000..f52d885 --- /dev/null +++ b/drivers/virtual_media/VirtioLib.h @@ -0,0 +1,322 @@ +/** @file + + Declarations of utility functions used by virtio device drivers. + + Copyright (C) 2012-2016, Red Hat, Inc. + Copyright (C) 2017, AMD 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 _VIRTIO_LIB_H_ +#define _VIRTIO_LIB_H_ + +#include "VirtioDevice.h" + +#include "Virtio.h" + + +/** + + Configure a virtio ring. + + This function sets up internal storage (the guest-host communication area) + and lays out several "navigation" (ie. no-ownership) pointers to parts of + that storage. + + Relevant sections from the virtio-0.9.5 spec: + - 1.1 Virtqueues, + - 2.3 Virtqueue Configuration. + + @param[in] VirtIo The virtio device which will use the ring. + + @param[in] The number of descriptors to allocate for the + virtio ring, as requested by the host. + + @param[out] Ring The virtio ring to set up. + + @return Status codes propagated from + VirtIo->AllocateSharedPages(). + + @retval EFI_SUCCESS Allocation and setup successful. Ring->Base + (and nothing else) is responsible for + deallocation. + +**/ +EFI_STATUS +EFIAPI +VirtioRingInit ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN UINT16 QueueSize, + OUT VRING *Ring + ); + + +/** + + Map the ring buffer so that it can be accessed equally by both guest + and hypervisor. + + @param[in] VirtIo The virtio device instance. + + @param[in] Ring The virtio ring to map. + + @param[out] RingBaseShift A resulting translation offset, to be + passed to VirtIo->SetQueueAddress(). + + @param[out] Mapping A resulting token to pass to + VirtIo->UnmapSharedBuffer(). + + @return Status code from VirtIo->MapSharedBuffer() +**/ +EFI_STATUS +EFIAPI +VirtioRingMap ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN VRING *Ring, + OUT UINT64 *RingBaseShift, + OUT VOID **Mapping + ); + +/** + + Tear down the internal resources of a configured virtio ring. + + The caller is responsible to stop the host from using this ring before + invoking this function: the VSTAT_DRIVER_OK bit must be clear in + VhdrDeviceStatus. + + @param[in] VirtIo The virtio device which was using the ring. + + @param[out] Ring The virtio ring to clean up. + +**/ +VOID +EFIAPI +VirtioRingUninit ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN OUT VRING *Ring + ); + + +// +// Internal use structure for tracking the submission of a multi-descriptor +// request. +// +typedef struct { + UINT16 HeadDescIdx; + UINT16 NextDescIdx; +} DESC_INDICES; + + +/** + + Turn off interrupt notifications from the host, and prepare for appending + multiple descriptors to the virtio ring. + + The calling driver must be in VSTAT_DRIVER_OK state. + + @param[in,out] Ring The virtio ring we intend to append descriptors to. + + @param[out] Indices The DESC_INDICES structure to initialize. + +**/ +VOID +EFIAPI +VirtioPrepare ( + IN OUT VRING *Ring, + OUT DESC_INDICES *Indices + ); + + +/** + + Append a contiguous buffer for transmission / reception via the virtio ring. + + This function implements the following section from virtio-0.9.5: + - 2.4.1.1 Placing Buffers into the Descriptor Table + + Free space is taken as granted, since the individual drivers support only + synchronous requests and host side status is processed in lock-step with + request submission. It is the calling driver's responsibility to verify the + ring size in advance. + + The caller is responsible for initializing *Indices with VirtioPrepare() + first. + + @param[in,out] Ring The virtio ring to append the buffer to, + as a descriptor. + + @param[in] BufferDeviceAddress (Bus master device) start address of the + transmit / receive buffer. + + @param[in] BufferSize Number of bytes to transmit or receive. + + @param[in] Flags A bitmask of VRING_DESC_F_* flags. The + caller computes this mask dependent on + further buffers to append and transfer + direction. VRING_DESC_F_INDIRECT is + unsupported. The VRING_DESC.Next field is + always set, but the host only interprets + it dependent on VRING_DESC_F_NEXT. + + @param[in,out] Indices Indices->HeadDescIdx is not accessed. + On input, Indices->NextDescIdx identifies + the next descriptor to carry the buffer. + On output, Indices->NextDescIdx is + incremented by one, modulo 2^16. + +**/ +VOID +EFIAPI +VirtioAppendDesc ( + IN OUT VRING *Ring, + IN UINT64 BufferDeviceAddress, + IN UINT32 BufferSize, + IN UINT16 Flags, + IN OUT DESC_INDICES *Indices + ); + + +/** + + Notify the host about the descriptor chain just built, and wait until the + host processes it. + + @param[in] VirtIo The target virtio device to notify. + + @param[in] VirtQueueId Identifies the queue for the target device. + + @param[in,out] Ring The virtio ring with descriptors to submit. + + @param[in] Indices Indices->NextDescIdx is not accessed. + Indices->HeadDescIdx identifies the head descriptor + of the descriptor chain. + + @param[out] UsedLen On success, the total number of bytes, consecutively + across the buffers linked by the descriptor chain, + that the host wrote. May be NULL if the caller + doesn't care, or can compute the same information + from device-specific request structures linked by the + descriptor chain. + + @return Error code from VirtIo->SetQueueNotify() if it fails. + + @retval EFI_SUCCESS Otherwise, the host processed all descriptors. + +**/ +EFI_STATUS +EFIAPI +VirtioFlush ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN UINT16 VirtQueueId, + IN OUT VRING *Ring, + IN DESC_INDICES *Indices, + OUT UINT32 *UsedLen OPTIONAL + ); + + +/** + + Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver + understands. + + In VirtIo 1.0, a device can reject a self-inconsistent feature bitmap through + the new VSTAT_FEATURES_OK status bit. (For example if the driver requests a + higher level feature but clears a prerequisite feature.) This function is a + small wrapper around VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures() that also + verifies if the VirtIo 1.0 device accepts the feature bitmap. + + @param[in] VirtIo Report feature bits to this device. + + @param[in] Features The set of feature bits that the driver wishes + to report. The caller is responsible to perform + any masking before calling this function; the + value is directly written with + VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures(). + + @param[in,out] DeviceStatus On input, the status byte most recently written + to the device's status register. On output (even + on error), DeviceStatus will be updated so that + it is suitable for further status bit + manipulation and writing to the device's status + register. + + @retval EFI_SUCCESS The device accepted the configuration in Features. + + @return EFI_UNSUPPORTED The device rejected the configuration in Features. + + @retval EFI_UNSUPPORTED VirtIo->Revision is smaller than 1.0.0. + + @return Error codes from the SetGuestFeatures(), + SetDeviceStatus(), GetDeviceStatus() member + functions. + +**/ +EFI_STATUS +EFIAPI +Virtio10WriteFeatures ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN UINT64 Features, + IN OUT UINT8 *DeviceStatus + ); + +/** + Provides the virtio device address required to access system memory from a + DMA bus master. + + The interface follows the same usage pattern as defined in UEFI spec 2.6 + (Section 13.2 PCI Root Bridge I/O Protocol) + + The VirtioMapAllBytesInSharedBuffer() is similar to VIRTIO_MAP_SHARED + with exception that NumberOfBytes is IN-only parameter. The function + maps all the bytes specified in NumberOfBytes param in one consecutive + range. + + @param[in] VirtIo The virtio device for which the mapping is + requested. + + @param[in] Operation Indicates if the bus master is going to + read or write to system memory. + + @param[in] HostAddress The system memory address to map to shared + buffer address. + + @param[in] NumberOfBytes Number of bytes to map. + + @param[out] DeviceAddress The resulting shared map address for the + bus master to access the hosts HostAddress. + + @param[out] Mapping A resulting token to pass to + VIRTIO_UNMAP_SHARED. + + + @retval EFI_SUCCESS The NumberOfBytes is succesfully mapped. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a + common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to + a lack of resources. This includes the case + when NumberOfBytes bytes cannot be mapped + in one consecutive range. + @retval EFI_DEVICE_ERROR The system hardware could not map the + requested address. +**/ +EFI_STATUS +EFIAPI +VirtioMapAllBytesInSharedBuffer ( + IN VIRTIO_DEVICE_PROTOCOL *VirtIo, + IN VIRTIO_MAP_OPERATION Operation, + IN VOID *HostAddress, + IN UINTN NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); +#endif // _VIRTIO_LIB_H_ diff --git a/drivers/virtual_media/VirtioPciDevice.c b/drivers/virtual_media/VirtioPciDevice.c new file mode 100755 index 0000000..ff715e0 --- /dev/null +++ b/drivers/virtual_media/VirtioPciDevice.c @@ -0,0 +1,348 @@ +/** @file + + This driver produces Virtio Device Protocol instances for Virtio PCI devices. + + Copyright (C) 2012, Red Hat, Inc. + Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.
+ Copyright (C) 2013, ARM Ltd. + Copyright (C) 2017, AMD 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. + +**/ + +#include +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioPciDevice.h" + +static VIRTIO_DEVICE_PROTOCOL mDeviceProtocolTemplate = { + 0, // Revision + 0, // SubSystemDeviceId + VirtioPciGetDeviceFeatures, // GetDeviceFeatures + VirtioPciSetGuestFeatures, // SetGuestFeatures + VirtioPciSetQueueAddress, // SetQueueAddress + VirtioPciSetQueueSel, // SetQueueSel + VirtioPciSetQueueNotify, // SetQueueNotify + VirtioPciSetQueueAlignment, // SetQueueAlignment + VirtioPciSetPageSize, // SetPageSize + VirtioPciGetQueueSize, // GetQueueNumMax + VirtioPciSetQueueSize, // SetQueueNum + VirtioPciGetDeviceStatus, // GetDeviceStatus + VirtioPciSetDeviceStatus, // SetDeviceStatus + VirtioPciDeviceWrite, // WriteDevice + VirtioPciDeviceRead, // ReadDevice + VirtioPciAllocateSharedPages, // AllocateSharedPages + VirtioPciFreeSharedPages, // FreeSharedPages + VirtioPciMapSharedBuffer, // MapSharedBuffer + VirtioPciUnmapSharedBuffer, // UnmapSharedBuffer +}; + +/** + + Read a word from Region 0 of the device specified by PciIo. + + Region 0 must be an iomem region. This is an internal function for the PCI + implementation of the protocol. + + @param[in] Dev Virtio PCI device. + + @param[in] FieldOffset Source offset. + + @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. + + @param[in] BufferSize Number of bytes available in the target buffer. Must + equal FieldSize. + + @param[out] Buffer Target buffer. + + + @return Status code returned by PciIo->Io.Read(). + +**/ +EFI_STATUS +EFIAPI +VirtioPciIoRead ( + IN VIRTIO_PCI_DEVICE *Dev, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN __attribute__((unused)) UINTN BufferSize, + OUT VOID *Buffer + ) +{ + ASSERT (FieldSize == BufferSize); + + switch (FieldSize) { + case 1: + //*((u8*)Buffer) = inb(mPciBase + FieldOffset); + __asm__ __volatile__("inb %w1, %b0" : "=a"(*((UINT8*)Buffer)) : "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + //DEBUG ((DEBUG_INFO, "VirtioPciIoRead value8 = 0x%x\n", *((UINT8*)Buffer))); + break; + + case 2: + //*((u16*)Buffer) = inw(mPciBase + FieldOffset); + __asm__ __volatile__("inw %w1, %w0" : "=a"(*((UINT16*)Buffer)) : "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + //DEBUG ((DEBUG_INFO, "VirtioPciIoRead value16 = 0x%x\n", *((UINT16*)Buffer))); + break; + + case 8: + // + // The 64bit PCI I/O is broken down into two 32bit reads to prevent + // any alignment or width issues. + // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write(): + // + // The I/O operations are carried out exactly as requested. The caller + // is responsible for any alignment and I/O width issues which the + // bus, device, platform, or type of I/O might require. For example on + // some platforms, width requests of EfiPciIoWidthUint64 do not work. + // + //*((UINT32*)Buffer)= inl(mPciBase + FieldOffset); + __asm__ __volatile__("inl %w1, %0" : "=a"(*((UINT32*)Buffer)) : "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + //*((UINT32*)Buffer + 1)= inl(mPciBase + FieldOffset + sizeof (UINT32)); + __asm__ __volatile__("inl %w1, %0" : "=a"(*((UINT32*)Buffer + 1)) : "Nd"((UINT16)(Dev->mPciBase + FieldOffset + sizeof (UINT32)))); + //DEBUG ((DEBUG_INFO, "VirtioPciIoRead value64_1 = 0x%x\n", *((UINT32*)Buffer))); + //DEBUG ((DEBUG_INFO, "VirtioPciIoRead value64_2 = 0x%x\n", *((UINT32*)Buffer + 1))); + break; + // + // fall through + // + case 4: + //*((UINT32*)Buffer)= inl(mPciBase + FieldOffset); + __asm__ __volatile__("inl %w1, %0" : "=a"(*((UINT32*)Buffer)) : "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + //DEBUG ((DEBUG_INFO, "VirtioPciIoRead value32 = 0x%x\n", *((UINT32*)Buffer))); + break; + + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + + Write a word into Region 0 of the device specified by PciIo. + + Region 0 must be an iomem region. This is an internal function for the PCI + implementation of the protocol. + + @param[in] Dev Virtio PCI device. + + @param[in] FieldOffset Destination offset. + + @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. + + @param[in] Value Little endian value to write, converted to UINT64. + The least significant FieldSize bytes will be used. + + + @return Status code returned by PciIo->Io.Write(). + +**/ +EFI_STATUS +EFIAPI +VirtioPciIoWrite ( + IN VIRTIO_PCI_DEVICE *Dev, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINT64 Value + ) +{ + //DEBUG ((DEBUG_INFO, "VirtioPciIoWrite(Base 0x%x, Offset 0x%x, Size 0x%x, Value 0x%x)\n", + //(UINT32)mPciBase, (UINT32)FieldOffset, (UINT32)FieldSize, (UINT32)Value)); + + switch (FieldSize) { + case 1: + //outb (mPciBase + FieldOffset, (UINT8)Value); + __asm__ __volatile__("outb %b0, %w1" : : "a"((UINT8)Value), "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + break; + + case 2: + //outw (mPciBase + FieldOffset, (UINT16)Value); + __asm__ __volatile__("outw %w0, %w1" : : "a"((UINT16)Value), "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + break; + + case 8: + // + // The 64bit PCI I/O is broken down into two 32bit writes to prevent + // any alignment or width issues. + // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write(): + // + // The I/O operations are carried out exactly as requested. The caller + // is responsible for any alignment and I/O width issues which the + // bus, device, platform, or type of I/O might require. For example on + // some platforms, width requests of EfiPciIoWidthUint64 do not work + // + //outl (mPciBase + FieldOffset, (UINT32)Value); + __asm__ __volatile__("outl %0, %w1" : : "a"((UINT32)Value), "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + //outl (mPciBase + FieldOffset + sizeof (UINT32), (UINT32)(Value >> 32)); + __asm__ __volatile__("outl %0, %w1" : : "a"((UINT32)(Value >> 32)), "Nd"((UINT16)(Dev->mPciBase + FieldOffset + sizeof (UINT32)))); + break; + + // + // fall through + // + case 4: + //outl (mPciBase + FieldOffset, (UINT32)Value); + __asm__ __volatile__("outl %0, %w1" : : "a"((UINT32)Value), "Nd"((UINT16)(Dev->mPciBase + FieldOffset))); + break; + + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +static UINT8 PciCheckCap(UINTN base, UINT8 id, UINT8 offset) +{ + int i; + UINT16 status = pci_read_config8(base, 0x06); //PCI_STATUS + + if (!(status & 0x10)) //PCI_STATUS_CAP_LIST + return 0; + + if (offset == 0) { + /* find first */ + offset = pci_read_config8(base, 0x34); //PCI_CAPABILITY_LIST + } else { + /* find next */ + offset = pci_read_config8(base, offset + 1); //PCI_CAP_LIST_NEXT + } + for (i = 0; offset && i <= 0xff; i++) { + if (pci_read_config8(base, offset) == id) + return offset; + offset = pci_read_config8(base, offset + 1); //PCI_CAP_LIST_NEXT + } + + return 0; +} + + +static VOID PciMaskwConfig(UINTN base, UINT32 reg, UINT16 off, UINT16 on) +{ + UINT16 val = pci_read_config16(base, reg); + val = (val & ~off) | on; + pci_write_config16(base, reg, val); +} + + +// Verify an IO bar and return it to the caller +static UINT16 PciEnIo(UINTN base, UINT32 reg) +{ + UINT32 value = pci_read_config32(base, reg); + if (!(value & 0x1)) { //PCI_BASE_ADDRESS_SPACE_IO + return 0; + } + value &= (~0x03UL); //PCI_BASE_ADDRESS_IO_MASK (~0x03UL) + if (value == 0 || value > 0xffff) { + return 0; + } + PciMaskwConfig(base, 0x04, 0, 0x1); //PCI_COMMAND, PCI_COMMAND_IO + return value; +} + + +/** + + Initialize the VirtIo PCI Device + + @param[in, out] Dev The driver instance to configure. The caller is + responsible for Device->PciIo's validity (ie. working IO + access to the underlying virtio-pci device). + + @retval EFI_SUCCESS Setup complete. + + @retval EFI_UNSUPPORTED The underlying IO device doesn't support the + provided address offset and read size. + + @return Error codes from PciIo->Pci.Read(). + +**/ +EFI_STATUS +EFIAPI +VirtioPciInit ( + IN OUT VIRTIO_PCI_DEVICE *Device, IN UINTN PciBase + ) +{ + PCI_TYPE00 Pci; + UINT32 i; + UINT8 cap; + + ASSERT (Device != NULL); + + for (i =0; imPciBase =PciEnIo(PciBase, 0x10); //PCI_BASE_ADDRESS_0 + DEBUG ((DEBUG_INFO, "ioaddr = 0x%x\n", (UINT32)Device->mPciBase)); + if (!Device->mPciBase) + return EFI_UNSUPPORTED; + + CopyMem (&Device->VirtioDevice, &mDeviceProtocolTemplate, + sizeof (VIRTIO_DEVICE_PROTOCOL)); + + // + // Initialize the protocol interface attributes + // + Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5); + //Device->VirtioDevice.SubSystemDeviceId = Pci.Device.SubsystemID; + + // + // Note: We don't support the MSI-X capability. If we did, + // the offset would become 24 after enabling MSI-X. + // + Device->DeviceSpecificConfigurationOffset = + VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI; + Device->Signature = VIRTIO_PCI_DEVICE_SIGNATURE; + + return EFI_SUCCESS; +} + +/** + + Uninitialize the internals of a virtio-pci device that has been successfully + set up with VirtioPciInit(). + + @param[in, out] Dev The device to clean up. + +**/ +VOID +EFIAPI +VirtioPciUninit ( + IN OUT __attribute__((unused)) VIRTIO_PCI_DEVICE *Device + ) +{ + // Note: This function mirrors VirtioPciInit() that does not allocate any + // resources - there's nothing to free here. +} + diff --git a/drivers/virtual_media/VirtioPciDevice.h b/drivers/virtual_media/VirtioPciDevice.h new file mode 100755 index 0000000..58f8300 --- /dev/null +++ b/drivers/virtual_media/VirtioPciDevice.h @@ -0,0 +1,175 @@ +/** @file + + Internal definitions for the VirtIo PCI Device driver + + Copyright (C) 2013, ARM Ltd + Copyright (c) 2017, AMD 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 _VIRTIO_PCI_DEVICE_H_ +#define _VIRTIO_PCI_DEVICE_H_ + +#include "VirtioPciDeviceLib.h" + +EFI_STATUS +EFIAPI +VirtioPciIoRead ( + IN VIRTIO_PCI_DEVICE *Dev, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +EFI_STATUS +EFIAPI +VirtioPciIoWrite ( + IN VIRTIO_PCI_DEVICE *Dev, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINT64 Value + ); + +/******************************************** + * PCI Functions for VIRTIO_DEVICE_PROTOCOL + *******************************************/ +EFI_STATUS +EFIAPI +VirtioPciDeviceRead ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +EFI_STATUS +EFIAPI +VirtioPciDeviceWrite ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINT64 Value + ); + +EFI_STATUS +EFIAPI +VirtioPciGetDeviceFeatures ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT64 *DeviceFeatures + ); + +EFI_STATUS +EFIAPI +VirtioPciGetQueueSize ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT16 *QueueNumMax + ); + +EFI_STATUS +EFIAPI +VirtioPciSetQueueAlignment ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT32 Alignment + ); + +EFI_STATUS +EFIAPI +VirtioPciSetPageSize ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT32 PageSize + ); + +EFI_STATUS +EFIAPI +VirtioPciGetDeviceStatus ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT8 *DeviceStatus + ); + +EFI_STATUS +EFIAPI +VirtioPciSetGuestFeatures ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT64 Features + ); + +EFI_STATUS +EFIAPI +VirtioPciSetQueueAddress ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VRING *Ring, + IN UINT64 RingBaseShift + ); + +EFI_STATUS +EFIAPI +VirtioPciSetQueueSel ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Sel + ); + +EFI_STATUS +EFIAPI +VirtioPciSetQueueNotify ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Index + ); + +EFI_STATUS +EFIAPI +VirtioPciSetQueueSize ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Size + ); + +EFI_STATUS +EFIAPI +VirtioPciSetDeviceStatus ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT8 DeviceStatus + ); + +EFI_STATUS +EFIAPI +VirtioPciAllocateSharedPages ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN NumPages, + OUT VOID **HostAddress + ); + +VOID +EFIAPI +VirtioPciFreeSharedPages ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN NumPages, + IN VOID *HostAddress + ); + +EFI_STATUS +EFIAPI +VirtioPciMapSharedBuffer ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VIRTIO_MAP_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +EFI_STATUS +EFIAPI +VirtioPciUnmapSharedBuffer ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VOID *Mapping + ); +#endif // _VIRTIO_PCI_DEVICE_DXE_H_ diff --git a/drivers/virtual_media/VirtioPciDeviceLib.h b/drivers/virtual_media/VirtioPciDeviceLib.h new file mode 100755 index 0000000..c3e2945 --- /dev/null +++ b/drivers/virtual_media/VirtioPciDeviceLib.h @@ -0,0 +1,50 @@ +/** @file + + Internal definitions for the VirtIo PCI Device driver + + Copyright (C) 2013, ARM Ltd + Copyright (c) 2017, AMD 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 _VIRTIO_PCI_DEVICE_LIB_H_ +#define _VIRTIO_PCI_DEVICE_LIB_H_ + +#include "VirtioDevice.h" +#include "Virtio.h" + +#define VIRTIO_PCI_DEVICE_SIGNATURE SIGNATURE_32 ('V', 'P', 'C', 'I') + +typedef struct { + UINT32 Signature; + VIRTIO_DEVICE_PROTOCOL VirtioDevice; + UINT64 OriginalPciAttributes; + UINT32 DeviceSpecificConfigurationOffset; + UINTN mPciBase; +} VIRTIO_PCI_DEVICE; + +#define VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE(Device) \ + CR (Device, VIRTIO_PCI_DEVICE, VirtioDevice, VIRTIO_PCI_DEVICE_SIGNATURE) + +EFI_STATUS +EFIAPI +VirtioPciInit ( + IN OUT VIRTIO_PCI_DEVICE *Device, + IN UINTN PciBase + ); + +VOID +EFIAPI +VirtioPciUninit ( + IN OUT VIRTIO_PCI_DEVICE *Device + ); + +#endif // _VIRTIO_PCI_DEVICE_DXE_H_ diff --git a/drivers/virtual_media/VirtioPciFunctions.c b/drivers/virtual_media/VirtioPciFunctions.c new file mode 100755 index 0000000..a7accc2 --- /dev/null +++ b/drivers/virtual_media/VirtioPciFunctions.c @@ -0,0 +1,388 @@ +/** @file + + This driver produces Virtio Device Protocol instances for Virtio PCI devices. + + Copyright (C) 2012, Red Hat, Inc. + Copyright (c) 2012, Intel Corporation. All rights reserved.
+ Copyright (C) 2013, ARM Ltd. + Copyright (C) 2017, AMD 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. + +**/ +#include +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioPciDevice.h" + +/** + Allocates one or more 4KB pages of type EfiBootServicesData. + + Allocates the number of 4KB pages of type EfiBootServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +static VOID * +EFIAPI +AllocatePages ( + IN UINTN Pages + ) +{ + return memalign (EFI_PAGE_SIZE, Pages << EFI_PAGE_SHIFT); +} + + +/** + Frees one or more 4KB pages that were previously allocated with one of the page allocation + functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer + must have been allocated on a previous call to the page allocation services of the Memory + Allocation Library. If it is not possible to free allocated pages, then this function will + perform no actions. + + If Buffer was not allocated with a page allocation function in the Memory Allocation Library, + then ASSERT(). + If Pages is zero, then ASSERT(). + + @param Buffer The pointer to the buffer of pages to free. + @param Pages The number of 4 KB pages to free. + +**/ +VOID +EFIAPI +FreePages ( + IN VOID *Buffer, + IN __attribute__((unused)) UINTN Pages + ) +{ + free(Buffer); +} + + +/** + + Read a word from Region 0 of the device specified by VirtIo Device protocol. + + The function implements the ReadDevice protocol member of + VIRTIO_DEVICE_PROTOCOL. + + @param[in] This VirtIo Device protocol. + + @param[in] FieldOffset Source offset. + + @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. + + @param[in] BufferSize Number of bytes available in the target buffer. Must + equal FieldSize. + + @param[out] Buffer Target buffer. + + + @return Status code returned by PciIo->Io.Read(). + +**/ +EFI_STATUS +EFIAPI +VirtioPciDeviceRead ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoRead (Dev, + Dev->DeviceSpecificConfigurationOffset + FieldOffset, + FieldSize, BufferSize, Buffer); +} + +/** + + Write a word into Region 0 of the device specified by VirtIo Device protocol. + + @param[in] This VirtIo Device protocol. + + @param[in] FieldOffset Destination offset. + + @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. + + @param[in] Value Little endian value to write, converted to UINT64. + The least significant FieldSize bytes will be used. + + + @return Status code returned by PciIo->Io.Write(). + +**/ +EFI_STATUS +EFIAPI +VirtioPciDeviceWrite ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINT64 Value + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoWrite (Dev, + Dev->DeviceSpecificConfigurationOffset + FieldOffset, FieldSize, Value); +} + +EFI_STATUS +EFIAPI +VirtioPciGetDeviceFeatures ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT64 *DeviceFeatures + ) +{ + VIRTIO_PCI_DEVICE *Dev; + EFI_STATUS Status; + UINT32 Features32; + + if (DeviceFeatures == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + Status = VirtioPciIoRead (Dev, VIRTIO_PCI_OFFSET_DEVICE_FEATURES, + sizeof (UINT32), sizeof (UINT32), &Features32); + if (!EFI_ERROR (Status)) { + *DeviceFeatures = Features32; + } + return Status; +} + +EFI_STATUS +EFIAPI +VirtioPciGetQueueSize ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT16 *QueueNumMax + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + if (QueueNumMax == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoRead (Dev, VIRTIO_PCI_OFFSET_QUEUE_SIZE, sizeof (UINT16), + sizeof (UINT16), QueueNumMax); +} + +EFI_STATUS +EFIAPI +VirtioPciGetDeviceStatus ( + IN VIRTIO_DEVICE_PROTOCOL *This, + OUT UINT8 *DeviceStatus + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + if (DeviceStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoRead (Dev, VIRTIO_PCI_OFFSET_QUEUE_DEVICE_STATUS, + sizeof (UINT8), sizeof (UINT8), DeviceStatus); +} + +EFI_STATUS +EFIAPI +VirtioPciSetGuestFeatures ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT64 Features + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + if (Features > MAX_UINT32) { + return EFI_UNSUPPORTED; + } + return VirtioPciIoWrite (Dev, VIRTIO_PCI_OFFSET_GUEST_FEATURES, + sizeof (UINT32), Features); +} + +EFI_STATUS +EFIAPI +VirtioPciSetQueueAddress ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN VRING *Ring, + IN __attribute__((unused)) UINT64 RingBaseShift + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + ASSERT (RingBaseShift == 0); + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoWrite (Dev, VIRTIO_PCI_OFFSET_QUEUE_ADDRESS, sizeof (UINT32), + (UINT32)((UINTN)Ring->Base >> EFI_PAGE_SHIFT)); +} + +EFI_STATUS +EFIAPI +VirtioPciSetQueueSel ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Sel + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoWrite (Dev, VIRTIO_PCI_OFFSET_QUEUE_SELECT, sizeof (UINT16), + Sel); +} + +EFI_STATUS +EFIAPI +VirtioPciSetQueueAlignment ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN __attribute__((unused)) UINT32 Alignment + ) +{ + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VirtioPciSetPageSize ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN UINT32 PageSize + ) +{ + return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +VirtioPciSetQueueNotify ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT16 Index + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoWrite (Dev, VIRTIO_PCI_OFFSET_QUEUE_NOTIFY, sizeof (UINT16), + Index); +} + +EFI_STATUS +EFIAPI +VirtioPciSetQueueSize ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN __attribute__((unused)) UINT16 Size + ) +{ + // + // This function is only applicable in Virtio-MMIO. + // (The QueueSize field is read-only in Virtio proper (PCI)) + // + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VirtioPciSetDeviceStatus ( + IN VIRTIO_DEVICE_PROTOCOL *This, + IN UINT8 DeviceStatus + ) +{ + VIRTIO_PCI_DEVICE *Dev; + + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); + + return VirtioPciIoWrite (Dev, VIRTIO_PCI_OFFSET_QUEUE_DEVICE_STATUS, + sizeof (UINT8), DeviceStatus); +} + +EFI_STATUS +EFIAPI +VirtioPciAllocateSharedPages ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN NumPages, + OUT VOID **HostAddress + ) +{ + VOID *Buffer; + + Buffer = AllocatePages (NumPages); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *HostAddress = Buffer; + return EFI_SUCCESS; +} + +VOID +EFIAPI +VirtioPciFreeSharedPages ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN UINTN NumPages, + IN VOID *HostAddress + ) +{ + FreePages (HostAddress, NumPages); +} + +EFI_STATUS +EFIAPI +VirtioPciMapSharedBuffer ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN __attribute__((unused)) VIRTIO_MAP_OPERATION Operation, + IN VOID *HostAddress, + IN OUT __attribute__((unused)) UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + *DeviceAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress; + *Mapping = NULL; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VirtioPciUnmapSharedBuffer ( + IN __attribute__((unused)) VIRTIO_DEVICE_PROTOCOL *This, + IN __attribute__((unused)) VOID *Mapping + ) +{ + return EFI_SUCCESS; +} diff --git a/drivers/virtual_media/VirtioRpmb.c b/drivers/virtual_media/VirtioRpmb.c new file mode 100644 index 0000000..423483c --- /dev/null +++ b/drivers/virtual_media/VirtioRpmb.c @@ -0,0 +1,480 @@ +/* @file + * + * This driver produces Protocol instances for virtio-rpmb devices. + * + * Copyright (C) 2012, Red Hat, Inc. + * Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.
+ * Copyright (c) 2017, AMD 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. + * + */ + +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioRpmb.h" +#include "VirtioLib.h" +#include "VirtioRpmbDevice.h" +#include "VirtioRpmbAccessLib.h" + +#define QUEUE_INDEX 0 +#define SEQ_CMD_MAX 3 /*support up to 3 cmds*/ + +static RPMB_DATA_FRAME *VrpmbGetFrameAddress(VOID *virtio_buffer, UINT32 index) +{ + VIRTIO_RPMB_IOCTL_SEQ_DATA *seq_data = NULL; + VIRTIO_RPMB_CMD *cmds, *cmd; + RPMB_DATA_FRAME *frames; + UINT32 number_cmds, offset = 0; + UINT32 i; + + if (!virtio_buffer || index > MAX_COMMAND_RPMB) + return NULL; + + seq_data = (VIRTIO_RPMB_IOCTL_SEQ_DATA *)virtio_buffer; + number_cmds = seq_data->n_cmds; + if (number_cmds > MAX_COMMAND_RPMB) + return NULL; + + cmds = (VIRTIO_RPMB_CMD *)&seq_data->cmds[0]; + if (!cmds) + return NULL; + + frames = (RPMB_DATA_FRAME *)&seq_data->cmds[number_cmds + 1]; + if (!frames) + return NULL; + + for (i = 0; i < index; i++) { + cmd = &cmds[i]; + if (!cmd) + return NULL; + offset += cmd->n_rpmb_frame; + } + + return (RPMB_DATA_FRAME *)&frames[offset]; +} + +STATIC +EFI_STATUS +EFIAPI +VirtioRpmbAddVirtqueue( + IN VRPMB_DEV *Dev, + IN UINT32 BufferSize, + IN OUT VOID *Buffer, + BOOLEAN RequestIsWrite + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + DESC_INDICES Indices; + VOID *IoctlCmdMapping; + VOID *SeqCommandDataMapping; + VOID *RpmbFrameMapping[SEQ_CMD_MAX]; + EFI_PHYSICAL_ADDRESS IoctlCmdDeviceAddress; + EFI_PHYSICAL_ADDRESS RpmbFrameDeviceAddress[SEQ_CMD_MAX]; + EFI_PHYSICAL_ADDRESS SeqCommandDataDeviceAddress; + BOOLEAN DataBufferIsMapped = FALSE; + VIRTIO_RPMB_IOCTL_SEQ_DATA *SeqCmdData = NULL; + UINT64 CmdNumber; + VIRTIO_RPMB_IOC IoctlCommand; + VIRTIO_RPMB_CMD *cmds; + UINTN i; + + if (Dev == NULL || Buffer == NULL) + return EFI_INVALID_PARAMETER; + + SeqCmdData = (VIRTIO_RPMB_IOCTL_SEQ_DATA *)malloc(BufferSize); + if (!SeqCmdData) + return EFI_OUT_OF_RESOURCES; + + memcpy(SeqCmdData, Buffer, BufferSize); + + CmdNumber = SeqCmdData->n_cmds; + memset(&IoctlCommand, 0, sizeof(IoctlCommand)); + + IoctlCommand.IoctlCmd = RPMB_IOC_SEQ_CMD; + IoctlCommand.Result = 0; + IoctlCommand.Target = 0; + + cmds = &SeqCmdData->cmds[0]; + for (i = 0; i < CmdNumber; i++) { + cmds[i].rpmb_flag = SeqCmdData->cmds[i].rpmb_flag; + cmds[i].n_rpmb_frame = SeqCmdData->cmds[i].n_rpmb_frame; + cmds[i].addr_rpmb_frame = VrpmbGetFrameAddress(Buffer, i); + memcpy(cmds[i].addr_rpmb_frame, SeqCmdData->cmds[i].addr_rpmb_frame, + sizeof(RPMB_DATA_FRAME) * (SeqCmdData->cmds[i].n_rpmb_frame ? : 1)); + } + + // + // Map ioctl cmd + // + Status = VirtioMapAllBytesInSharedBuffer( + Dev->VirtIo, + VirtioOperationBusMasterRead, + (VOID *) &IoctlCommand, + sizeof(IoctlCommand), + &IoctlCmdDeviceAddress, + &IoctlCmdMapping + ); + if (EFI_ERROR(Status)) { + Status = EFI_DEVICE_ERROR; + goto UnmapIoctlCmdBuffer; + } + + // + // Map seq cmds Data + // + Status = VirtioMapAllBytesInSharedBuffer( + Dev->VirtIo, + VirtioOperationBusMasterRead, + (VOID *) SeqCmdData, + sizeof(VIRTIO_RPMB_IOCTL_SEQ_DATA), + &SeqCommandDataDeviceAddress, + &SeqCommandDataMapping + ); + if (EFI_ERROR(Status)) { + Status = EFI_DEVICE_ERROR; + goto UnmapSeqCmdBuffer; + } + + // + // Map rpmb frame + // + for (i = 0; i < CmdNumber; i++) { + Status = VirtioMapAllBytesInSharedBuffer( + Dev->VirtIo, + (RequestIsWrite ? + VirtioOperationBusMasterRead : + VirtioOperationBusMasterWrite), + (VOID *) cmds[i].addr_rpmb_frame, + sizeof(RPMB_DATA_FRAME) * (SeqCmdData->cmds[i].n_rpmb_frame ? : 1), + &RpmbFrameDeviceAddress[i], + &RpmbFrameMapping[i] + ); + if (EFI_ERROR(Status)) { + Status = EFI_DEVICE_ERROR; + goto UnmapFrameBuffer; + } + } + + VirtioPrepare(&Dev->Ring, &Indices); + + // + // ensured by VirtioRpmbInit() -- this predicate, in combination with the + // lock-step progress, ensures we don't have to track free descriptors. + // + ASSERT(Dev->Ring.QueueSize >= 5); + + // + // virtio-rpmb ioctl cmd + // + VirtioAppendDesc( + &Dev->Ring, + IoctlCmdDeviceAddress, + sizeof(IoctlCommand), + VRING_DESC_F_NEXT, + &Indices + ); + + // + // virtio-rpmb seq cmd + // + VirtioAppendDesc( + &Dev->Ring, + SeqCommandDataDeviceAddress, + sizeof(VIRTIO_RPMB_IOCTL_SEQ_DATA), + VRING_DESC_F_NEXT, + &Indices + ); + + // + // enqueue data + // + if (BufferSize > 0) { + // + // VRING_DESC_F_WRITE is interpreted from the host's point of view. + // + for (i = 0; i < CmdNumber; i++) { + if (i < (CmdNumber - 1)) + VirtioAppendDesc( + &Dev->Ring, + RpmbFrameDeviceAddress[i], + sizeof(RPMB_DATA_FRAME) * (SeqCmdData->cmds[i].n_rpmb_frame ? : 1), + VRING_DESC_F_NEXT, + &Indices + ); + else + VirtioAppendDesc( + &Dev->Ring, + RpmbFrameDeviceAddress[i], + sizeof(RPMB_DATA_FRAME) * (SeqCmdData->cmds[i].n_rpmb_frame ? : 1), + VRING_DESC_F_WRITE, + &Indices + ); + } + } + + DataBufferIsMapped = TRUE; + + if (VirtioFlush(Dev->VirtIo, QUEUE_INDEX, &Dev->Ring, + &Indices, NULL) != EFI_SUCCESS) + Status = EFI_DEVICE_ERROR; + + if (IoctlCommand.Result != 0) + Status = EFI_DEVICE_ERROR; + else + Status = EFI_SUCCESS; + +UnmapFrameBuffer: + if (DataBufferIsMapped) { + for (i = 0; i < CmdNumber; i++) + Dev->VirtIo->UnmapSharedBuffer(Dev->VirtIo, RpmbFrameMapping[i]); + } + +UnmapSeqCmdBuffer: + Dev->VirtIo->UnmapSharedBuffer(Dev->VirtIo, SeqCommandDataMapping); + +UnmapIoctlCmdBuffer: + Dev->VirtIo->UnmapSharedBuffer(Dev->VirtIo, IoctlCmdMapping); + + free(SeqCmdData); + return Status; +} + +EFI_STATUS +EFIAPI +VirtioRpmbSentData( + IN VRPMB_DEV *Dev, + IN VOID *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 MaxSize; + EFI_STATUS Status = EFI_SUCCESS; + + if (BufferSize == 0) + return EFI_SUCCESS; + + MaxSize = 0x400 << EFI_PAGE_SHIFT; + if (BufferSize > MaxSize) + return EFI_INVALID_PARAMETER; + + if (!Dev) + return EFI_INVALID_PARAMETER; + + Status = VirtioRpmbAddVirtqueue( + Dev, + BufferSize, + Buffer, + TRUE // RequestIsWrite + ); + + return Status; +} + +/* + * + * Set up all virtio-rpmb aspects of this driver for the specified + * device. + * + * @param[in out] Dev The driver instance to configure. The caller is + * responsible for Dev->VirtIo's validity (ie. working IO + * access to the underlying virtio-rpmb device). + * + * @retval EFI_SUCCESS Setup complete. + * + * @retval EFI_UNSUPPORTED The driver is unable to work with the virtio ring. + * + * @return Error codes from VirtioRingInit() or + * VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE or + * VirtioRingMap(). + */ +EFI_STATUS +EFIAPI +VirtioRpmbInit( + IN OUT VRPMB_DEV *Dev + ) +{ + UINT8 NextDevStat; + EFI_STATUS Status; + + UINT64 Features; + UINT16 QueueSize; + UINT64 RingBaseShift; + + // + // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. + // + NextDevStat = 0; // step 1 -- reset device + Status = Dev->VirtIo->SetDeviceStatus(Dev->VirtIo, NextDevStat); + if (EFI_ERROR(Status)) + goto Failed; + + NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence + Status = Dev->VirtIo->SetDeviceStatus(Dev->VirtIo, NextDevStat); + if (EFI_ERROR(Status)) + goto Failed; + + NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it + Status = Dev->VirtIo->SetDeviceStatus(Dev->VirtIo, NextDevStat); + if (EFI_ERROR(Status)) + goto Failed; + + // + // Set Page Size - MMIO VirtIo Specific + // + Status = Dev->VirtIo->SetPageSize(Dev->VirtIo, EFI_PAGE_SIZE); + if (EFI_ERROR(Status)) + goto Failed; + + // + // step 4a -- retrieve and validate features + // + Status = Dev->VirtIo->GetDeviceFeatures(Dev->VirtIo, &Features); + if (EFI_ERROR(Status)) + goto Failed; + + // + // In virtio-1.0, feature negotiation is expected to complete before queue + // discovery, and the device can also reject the selected set of features. + // + if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION(1, 0, 0)) { + Status = Virtio10WriteFeatures(Dev->VirtIo, Features, &NextDevStat); + if (EFI_ERROR(Status)) + goto Failed; + } + + // + // step 4b -- allocate virtqueue, just use #0 + // + Status = Dev->VirtIo->SetQueueSel(Dev->VirtIo, QUEUE_INDEX); + if (EFI_ERROR(Status)) + goto Failed; + + Status = Dev->VirtIo->GetQueueNumMax(Dev->VirtIo, &QueueSize); + if (EFI_ERROR(Status)) + goto Failed; + + // + // VirtioRpmbAddVirtqueue() uses one descriptor. + // + if (QueueSize < 1) { + Status = EFI_UNSUPPORTED; + goto Failed; + } + + Status = VirtioRingInit(Dev->VirtIo, QueueSize, &Dev->Ring); + if (EFI_ERROR(Status)) + goto Failed; + + // + // If anything fails from here on, we must release the ring resources. + // + Status = VirtioRingMap( + Dev->VirtIo, + &Dev->Ring, + &RingBaseShift, + &Dev->RingMap + ); + if (EFI_ERROR(Status)) + goto ReleaseQueue; + + // + // Additional steps for MMIO: align the queue appropriately, and set the + // size. If anything fails from here on, we must unmap the ring resources. + // + Status = Dev->VirtIo->SetQueueNum(Dev->VirtIo, QueueSize); + if (EFI_ERROR(Status)) + goto UnmapQueue; + + Status = Dev->VirtIo->SetQueueAlign(Dev->VirtIo, EFI_PAGE_SIZE); + if (EFI_ERROR(Status)) + goto UnmapQueue; + + // + // step 4c -- Report GPFN (guest-physical frame number) of queue. + // + Status = Dev->VirtIo->SetQueueAddress( + Dev->VirtIo, + &Dev->Ring, + RingBaseShift + ); + if (EFI_ERROR(Status)) + goto UnmapQueue; + + // + // step 5 -- Report understood features. + // + if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION(1, 0, 0)) { + Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); + Status = Dev->VirtIo->SetGuestFeatures(Dev->VirtIo, Features); + if (EFI_ERROR(Status)) + goto UnmapQueue; + } + + // + // step 6 -- initialization complete + // + NextDevStat |= VSTAT_DRIVER_OK; + Status = Dev->VirtIo->SetDeviceStatus(Dev->VirtIo, NextDevStat); + if (EFI_ERROR(Status)) + goto UnmapQueue; + + Dev->Signature = VRPMB_SIG; + + return EFI_SUCCESS; + +UnmapQueue: + Dev->VirtIo->UnmapSharedBuffer(Dev->VirtIo, Dev->RingMap); + +ReleaseQueue: + VirtioRingUninit(Dev->VirtIo, &Dev->Ring); + +Failed: + // + // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device + // Status. VirtIo access failure here should not mask the original error. + // + NextDevStat |= VSTAT_FAILED; + Dev->VirtIo->SetDeviceStatus(Dev->VirtIo, NextDevStat); + + // reached only via Failed above + return Status; +} + +/* + * + * Uninitialize the internals of a virtio-rpmb device that has been successfully + * set up with VirtioRpmbInit(). + * + * @param[in out] Dev The device to clean up. + * + */ +VOID +EFIAPI +VirtioRpmbUninit( + IN OUT VRPMB_DEV *Dev + ) +{ + // + // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When + // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from + // the old comms area. + // + Dev->VirtIo->SetDeviceStatus(Dev->VirtIo, 0); + + Dev->VirtIo->UnmapSharedBuffer(Dev->VirtIo, Dev->RingMap); + VirtioRingUninit(Dev->VirtIo, &Dev->Ring); + + SetMem(&Dev->PassThru, sizeof(Dev->PassThru), 0x00); +} diff --git a/drivers/virtual_media/VirtioRpmb.h b/drivers/virtual_media/VirtioRpmb.h new file mode 100644 index 0000000..5de2684 --- /dev/null +++ b/drivers/virtual_media/VirtioRpmb.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _VIRTIO_RPMB_H_ +#define _VIRTIO_RPMB_H_ + +#include "Virtio.h" + +#define MAX_COMMAND_RPMB 3 + +#pragma pack(1) +typedef struct { + UINT8 stuff[196]; + UINT8 key_mac[32]; + UINT8 data[256]; + UINT8 nonce[16]; + UINT32 write_counter; + UINT16 address; + UINT16 block_count; + UINT16 result; + UINT16 req_resp; +} RPMB_DATA_FRAME; +#pragma pack() + +typedef struct { + UINT32 rpmb_flag; + UINT32 n_rpmb_frame; + RPMB_DATA_FRAME *addr_rpmb_frame; +} VIRTIO_RPMB_CMD; + +typedef struct { + UINT64 n_cmds; + VIRTIO_RPMB_CMD cmds[MAX_COMMAND_RPMB + 1]; +} VIRTIO_RPMB_IOCTL_SEQ_DATA; + +typedef struct { + UINT32 IoctlCmd; + int Result; + UINT8 Target; + UINT8 Reserved[3]; +} VIRTIO_RPMB_IOC; + +struct rpmb_ioc_req_cmd { + UINT64 req_type; + VIRTIO_RPMB_CMD icmd; + VIRTIO_RPMB_CMD ocmd; +}; + +struct rpmb_ioc_seq_cmd { + UINT64 num_of_cmds; + VIRTIO_RPMB_CMD cmds[0]; +}; + +#define RPMB_IOC_SEQ_CMD 0xC008B552 + +#endif // _VIRTIO_RPMB_H_ diff --git a/drivers/virtual_media/VirtioRpmbAccessLib.c b/drivers/virtual_media/VirtioRpmbAccessLib.c new file mode 100644 index 0000000..5890293 --- /dev/null +++ b/drivers/virtual_media/VirtioRpmbAccessLib.c @@ -0,0 +1,105 @@ +/** @file + This file provides some helper functions which are specific for RPMB device. + + Copyright (c) 2015 - 2018, 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. + +**/ + +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "VirtioPciDeviceLib.h" +#include "VirtioRpmbDevice.h" +#include "ScsiPassThruExt.h" +#include "VirtioRpmbDevice.h" +#include "VirtioRpmbAccessLib.h" + +static VRPMB_DEV *gRpmbdev = NULL; + +EFI_STATUS +EFIAPI +VrpmbPassThru( + IN __attribute__((__unused__)) EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN __attribute__((__unused__)) UINT8 *Target, + IN __attribute__((__unused__)) UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN __attribute__((__unused__)) EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + UINT32 BufferSize; + VOID *Buffer; + VRPMB_DEV *Dev; + + BufferSize = Packet->OutTransferLength; + Buffer = Packet->OutDataBuffer; + + Dev = VirtioRPMBGetContext(); + if (!Dev) + return EFI_INVALID_PARAMETER; + + Status = VirtioRpmbSentData (Dev, Buffer, BufferSize); + + return Status; +} + +EFI_STATUS +EFIAPI +VirtioRpmbInitialize ( + IN UINTN VirtioRpmbPciBase + ) +{ + VIRTIO_PCI_DEVICE *VirtPci; + VRPMB_DEV *Dev; + EFI_STATUS Status; + + VirtPci = (VIRTIO_PCI_DEVICE *) malloc (sizeof *VirtPci); + if (VirtPci == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = VirtioPciInit (VirtPci, VirtioRpmbPciBase); + + if (EFI_ERROR (Status)) { + ewerr("VirtioPciInit fail Error %x\n",(UINT32)Status); + free (VirtPci); + return Status; + } + + Dev = (VRPMB_DEV *) malloc (sizeof *Dev); + if (Dev == NULL) { + free (VirtPci); + return EFI_OUT_OF_RESOURCES; + } + + Dev->VirtIo = &VirtPci->VirtioDevice; + + Status = VirtioRpmbInit (Dev); + if (EFI_ERROR (Status)) { + ewerr("VirtioRpmbInit fail Error %x\n",(UINT32)Status); + free (Dev); + free (VirtPci); + return Status; + } + + gRpmbdev = Dev; + + return EFI_SUCCESS; +} + +VRPMB_DEV *VirtioRPMBGetContext() +{ + return gRpmbdev; +} + diff --git a/drivers/virtual_media/VirtioRpmbAccessLib.h b/drivers/virtual_media/VirtioRpmbAccessLib.h new file mode 100644 index 0000000..87b65cd --- /dev/null +++ b/drivers/virtual_media/VirtioRpmbAccessLib.h @@ -0,0 +1,40 @@ +/** @file + + Copyright (c) 2018, 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. + +**/ + +/* + * VirtioRpmbAccessLib.h + */ +#ifndef _VIRTIORPMB_ACCESS_LIB_H_ +#define _VIRTIORPMB_ACCESS_LIB_H_ + +#include "ScsiPassThruExt.h" +#include "VirtioRpmbDevice.h" + +EFI_STATUS +EFIAPI +VrpmbPassThru ( + IN __attribute__((__unused__)) EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN __attribute__((__unused__)) UINT8 *Target, + IN __attribute__((__unused__)) UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN __attribute__((__unused__)) EFI_EVENT Event OPTIONAL + ); + +EFI_STATUS +EFIAPI +VirtioRpmbInitialize ( + IN UINTN VirtioRpmbPciBase + ); + +VRPMB_DEV *VirtioRPMBGetContext(); +#endif diff --git a/drivers/virtual_media/VirtioRpmbDevice.h b/drivers/virtual_media/VirtioRpmbDevice.h new file mode 100644 index 0000000..88ba0b8 --- /dev/null +++ b/drivers/virtual_media/VirtioRpmbDevice.h @@ -0,0 +1,88 @@ +/** @file + + Internal definitions for the virtio-rpmb driver. + + Copyright (C) 2012, Red Hat, Inc. + Copyright (c) 2012 - 2018, 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 _VIRTIO_RPMB_DEVICE_H_ +#define _VIRTIO_RPMB_DEVICE_H_ + +#include + +#include "Virtio.h" +#include "VirtioDevice.h" +#include "ScsiPassThruExt.h" + +#define VRPMB_SIG SIGNATURE_32 ('R', 'P', 'M', 'B') + +typedef struct { + UINT32 Signature; + VIRTIO_DEVICE_PROTOCOL *VirtIo; + EFI_EVENT ExitBoot; + VRING Ring; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru; + VOID* RingMap; +} VRPMB_DEV; + +#define VIRTIO_RPMB_FROM_RPMB_IO(RpmbIoPointer) \ + CR (RpmbIoPointer, VRPMB_DEV, PassThru, VRPMB_SIG) + +/** + + Set up all and virtio-rpmb aspects of this driver for the specified + device. + + @param[in out] Dev The driver instance to configure. The caller is + responsible for Dev->VirtIo's validity (ie. working IO + access to the underlying virtio-rpmb device). + + @retval EFI_SUCCESS Setup complete. + + @retval EFI_UNSUPPORTED The driver is unable to work with the virtio ring. + + @return Error codes from VirtioRingInit() or + VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE or + VirtioRingMap(). + +**/ + +EFI_STATUS +EFIAPI +VirtioRpmbInit ( + IN OUT VRPMB_DEV *Dev + ); + +/** + + Uninitialize the internals of a virtio-rpmb device that has been successfully + set up with VirtioRpmbInit(). + + @param[in out] Dev The device to clean up. + +**/ +VOID +EFIAPI +VirtioRpmbUninit ( + IN OUT VRPMB_DEV *Dev + ); + +EFI_STATUS +EFIAPI +VirtioRpmbSentData ( + IN VRPMB_DEV *Dev, + IN VOID *Buffer, + IN UINT32 BufferSize + ); + +#endif // _VIRTIO_RPMB_DEVICE_H_ diff --git a/drivers/virtual_media/virtual_media.c b/drivers/virtual_media/virtual_media.c new file mode 100755 index 0000000..18d2f7b --- /dev/null +++ b/drivers/virtual_media/virtual_media.c @@ -0,0 +1,199 @@ +/** @file + + Copyright (c) 2018, 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. + +**/ + + +#include +#include +#include +#include +#include +#include +#include + +#include "VirtioDeviceCommon.h" +#include "virtual_media.h" +#include "VirtioBlkAccessLib.h" +#include "VirtioRpmbAccessLib.h" +#include "ScsiPassThruExt.h" +#include "VirtioRpmbDevice.h" + +#define DEVICE_INDEX_DEFAULT 0 + +static struct supported_device { + u16 vid; + u16 did; +} SUPPORTED_DEVICES[] = { + { .vid = 0x1AF4, .did = 0x1001}, + { .vid = 0x8086, .did = 0x8601}, +}; + +enum VIRTUAL_DEVICE { + VirtualMedia, + VirtualRpmb, + VirtualDeviceMax +}; + +static EFI_STATUS _init(storage_t *s) +{ + EFI_STATUS ret; + pcidev_t pci_dev[VirtualDeviceMax] = {0}; + size_t i; + DEVICE_BLOCK_INFO BlockInfo ={0}; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_DEVICES); i++) + pci_find_device(SUPPORTED_DEVICES[i].vid, + SUPPORTED_DEVICES[i].did, + &pci_dev[i]); + + if (!pci_dev[VirtualMedia]) + return EFI_UNSUPPORTED; + + ret = VirtioMediaInitialize((UINTN)pci_dev[VirtualMedia]); + if (ret) + return EFI_DEVICE_ERROR; + + if (pci_dev[VirtualRpmb]) + ret = VirtioRpmbInitialize((UINTN)pci_dev[VirtualRpmb]); + + ret = VirtioGetMediaInfo(DEVICE_INDEX_DEFAULT, &BlockInfo); + if (EFI_ERROR(ret)) { + ewerr ("VirtioGetMediaInfo Error %x\n",(UINT32)ret); + return ret; + } + + ewdbg("BlockNum is 0x%x", (UINT32)BlockInfo.BlockNum); + ewdbg("BlockSize is 0x%x", BlockInfo.BlockSize); + s->blk_cnt = BlockInfo.BlockNum; + s->blk_sz = BlockInfo.BlockSize; + + return EFI_SUCCESS; +} + +static EFI_LBA _read(storage_t *s, EFI_LBA start, EFI_LBA count, void *buf) +{ + EFI_STATUS ret; + + ret = VirtioReadBlocks ( + DEVICE_INDEX_DEFAULT, + start, + s->blk_sz * count, + buf); + if (!EFI_ERROR(ret)) + return count; + else + return 0; +} + +static EFI_LBA _write(storage_t *s, EFI_LBA start, EFI_LBA count, const void *buf) +{ + EFI_STATUS ret; + + ret = VirtioWriteBlocks ( + DEVICE_INDEX_DEFAULT, + start, + s->blk_sz * count, + (void *)buf); + if (!EFI_ERROR(ret)) + return count; + else + return 0; +} + +static EFI_STATUS _erase(storage_t *s, EFI_LBA start, UINTN Size) +{ + EFI_STATUS ret; + UINT32 blk_sz; + + ret = VirtioEraseBlocks ( + DEVICE_INDEX_DEFAULT, + start, + Size); + blk_sz = s->blk_sz; + + return ret; +} + +static storage_t storage_virtual_media = { + .init = _init, + .read = _read, + .write = _write, + .erase = _erase, + .pci_function = 0, + .pci_device = 0, +}; + +static EFI_HANDLE handle; +static EFI_HANDLE vrpmb_handle; +static EFI_GUID passthru_guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; + +static EFI_STATUS storage_virtual_media_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + boot_dev_t *boot_dev; + VRPMB_DEV *Rpmbdev_context = NULL; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru; + + static EFI_EXT_SCSI_PASS_THRU_PROTOCOL vrpmb_default = { + NULL, + VrpmbPassThru, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + if (!st) + return EFI_INVALID_PARAMETER; + + boot_dev = get_boot_media(); + if (!boot_dev) + return EFI_INVALID_PARAMETER; + + boot_dev->type = STORAGE_VIRTUAL; + + storage_virtual_media.pci_device = (boot_dev->diskbus >> 8) & 0xff; + storage_virtual_media.pci_function = boot_dev->diskbus & 0xff; + + ret = storage_init(st, &storage_virtual_media, &handle); + + if (EFI_ERROR(ret)) + return ret; + + Rpmbdev_context = VirtioRPMBGetContext(); + if (Rpmbdev_context) + ret = interface_init(st, &passthru_guid, &vrpmb_handle, + &vrpmb_default, sizeof(vrpmb_default), + (void **)&PassThru); + else + ewdbg("Virtio RPMB device not found or initialization failed!\n"); + + return ret; + +} + +static EFI_STATUS storage_virtual_media_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + return storage_free(st, handle); +} + +ewdrv_t virtual_media_drv = { + .name = "storage_virtual_media", + .description = "STORAGE virtual media driver", + .init = storage_virtual_media_init, + .exit = storage_virtual_media_exit +}; diff --git a/drivers/virtual_media/virtual_media.h b/drivers/virtual_media/virtual_media.h new file mode 100755 index 0000000..eb90d20 --- /dev/null +++ b/drivers/virtual_media/virtual_media.h @@ -0,0 +1,22 @@ +/** @file + + Copyright (c) 2018, 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 _VIRTUAL_MEDIA_H_ +#define _VIRTUAL_MEDIA_H_ + +#include + +extern ewdrv_t virtual_media_drv; + +#endif /* _VIRTUAL_MEDIA_H_ */ diff --git a/host/Android.mk b/host/Android.mk new file mode 100644 index 0000000..ebbf424 --- /dev/null +++ b/host/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := efiwrapper_host-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_STEM := efiwrapper_host +LOCAL_CFLAGS := $(EFIWRAPPER_HOST_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + libefiwrapper_host-$(TARGET_BUILD_VARIANT) +LOCAL_SRC_FILES := \ + main.c \ + event.c \ + disk.c \ + fifo.c \ + worker.c \ + tcp4.c \ + fileio.c \ + gop.c \ + image.c \ + pe.c \ + host_time.c \ + terminal_conin.c +LOCAL_LDFLAGS := -ldl +LOCAL_MODULE_HOST_ARCH := $(EFIWRAPPER_HOST_ARCH) +LOCAL_C_INCLUDES := $(EFIWRAPPER_HOST_C_INCLUDES) +include $(BUILD_HOST_EXECUTABLE) diff --git a/host/Makefile b/host/Makefile new file mode 100644 index 0000000..a07d133 --- /dev/null +++ b/host/Makefile @@ -0,0 +1,29 @@ +SRC_DIR := .. +include $(SRC_DIR)/Make.defaults + +OBJS := main.o \ + event.o \ + disk.o \ + fifo.o \ + worker.o \ + tcp4.o \ + fileio.o \ + gop.o \ + image.o \ + pe.o \ + host_time.o \ + terminal_curses_conin.o \ + terminal_curses_conout.o \ + terminal_curses.o + +LDFLAGS := -lX11 -lpthread -lncurses + +efiwrapper_host-$(TARGET_BUILD_VARIANT): $(OBJS) $(EW_LIB) + $(CC) $(CFLAGS) $(GNU_EFI_INCS) $(LDFLAGS) $^ -o $@ + +.PHONY: clean +clean: + @rm -f $(OBJS) *~ + +mrproper: clean + @rm -f efiwrapper_host-* diff --git a/host/curses_utils.h b/host/curses_utils.h new file mode 100644 index 0000000..dd9cae2 --- /dev/null +++ b/host/curses_utils.h @@ -0,0 +1,94 @@ +#ifndef _CURSES_UTILS_H +#define _CURSES_UTILS_H + +#include +#include + +static inline UINT16 curses_key_to_efi_scancode(int key) +{ + switch (key) { + case KEY_UP: + return SCAN_UP; + case KEY_DOWN: + return SCAN_DOWN; + case KEY_RIGHT: + return SCAN_RIGHT; + case KEY_LEFT: + return SCAN_LEFT; + case KEY_HOME: + return SCAN_HOME; + case KEY_END: + return SCAN_END; + case KEY_IL: + return SCAN_INSERT; + case KEY_DL: + return SCAN_DELETE; + case KEY_PPAGE: + return SCAN_PAGE_UP; + case KEY_NPAGE: + return SCAN_PAGE_DOWN; + case KEY_F(1): + return SCAN_F1; + case KEY_F(2): + return SCAN_F2; + case KEY_F(3): + return SCAN_F3; + case KEY_F(4): + return SCAN_F4; + case KEY_F(5): + return SCAN_F5; + case KEY_F(6): + return SCAN_F6; + case KEY_F(7): + return SCAN_F7; + case KEY_F(8): + return SCAN_F8; + case KEY_F(9): + return SCAN_F9; + case KEY_F(10): + return SCAN_F10; + case KEY_F(11): + return SCAN_F11; + case KEY_F(12): + return SCAN_F12; + case 27: + return SCAN_ESC; + default: + return SCAN_NULL; + } +} + +static inline short efi_color_to_ncurses_color(UINTN color) +{ + switch (color) { + case EFI_BLACK: + return COLOR_BLACK; + case EFI_BLUE: + return COLOR_BLUE; + case EFI_GREEN: + return COLOR_GREEN; + case EFI_CYAN: + return COLOR_CYAN; + case EFI_RED: + return COLOR_RED; + case EFI_MAGENTA: + return COLOR_MAGENTA; + case EFI_BROWN: + case EFI_LIGHTGRAY: + case EFI_BRIGHT: /*also EFI_DARKGRAY*/ + case EFI_LIGHTBLUE: + case EFI_LIGHTGREEN: + case EFI_LIGHTCYAN: + case EFI_LIGHTRED: + case EFI_LIGHTMAGENTA: + return COLOR_YELLOW; + case EFI_YELLOW: + return COLOR_YELLOW; + case EFI_WHITE: + return COLOR_WHITE; + default: + return -1; + } +}; + +#endif /* _CURSES_UTILS_H */ diff --git a/host/disk.c b/host/disk.c new file mode 100644 index 0000000..98b47ac --- /dev/null +++ b/host/disk.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "disk.h" + +static const char *DISK_PATH = "./disk.img"; +static const UINT32 DISK_BLK_SZ = 512; +static const UINT64 DISK_SIZE = (uint64_t)12 * 1024 * 1024 * 1024; + +static int fd = -1; + +static EFI_STATUS _init(storage_t *s) +{ + char c = 0; + ssize_t size; + off64_t off; + int ret; + struct stat sb; + + s->blk_sz = DISK_BLK_SZ; + + fd = open(DISK_PATH, O_RDWR, 0644); + if (fd >= 0) { + ret = fstat(fd, &sb); + if (ret == -1) { + ewerr("Failed to fstat %s disk file, %s", + DISK_PATH, strerror(errno)); + return EFI_DEVICE_ERROR; + } + + if (!S_ISREG(sb.st_mode)) + return EFI_DEVICE_ERROR; + + s->blk_cnt = sb.st_size / DISK_BLK_SZ; + return EFI_SUCCESS; + } + if (errno != ENOENT) { + ewerr("Failed to open %s disk file, %s", + DISK_PATH, strerror(errno)); + return EFI_DEVICE_ERROR; + } + + s->blk_cnt = DISK_SIZE / DISK_BLK_SZ; + + fd = open(DISK_PATH, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + ewerr("Failed to create %s disk file, %s", + DISK_PATH, strerror(errno)); + return EFI_DEVICE_ERROR; + } + + off = lseek64(fd, DISK_SIZE - 1, SEEK_SET); + if (off != DISK_SIZE - 1) { + ewerr("Failed to seek till the end of disk, %s", + strerror(errno)); + return EFI_DEVICE_ERROR; + } + + size = write(fd, &c, sizeof(c)); + if (size != sizeof(c)) { + ewerr("Failed to initialize disk file, %s", + strerror(errno)); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static EFI_LBA read_or_write(storage_t *s, EFI_LBA start, EFI_LBA count, + void *buf, bool do_read) +{ + ssize_t ret; + off64_t off; + size_t remaining, total; + char *b; + + if (!s || !buf || start + count > s->blk_cnt) + return EFI_INVALID_PARAMETER; + + if (fd == -1) + return EFI_NOT_STARTED; + + off = lseek64(fd, start * s->blk_sz, SEEK_SET); + if (off != (off64_t)start * s->blk_sz) { + ewerr("Failed to seek in the disk file, %s", + strerror(errno)); + return 0; + } + + b = buf; + total = 0; + for (remaining = count * s->blk_sz; remaining > 0; remaining -= ret) { + if (do_read) + ret = read(fd, b, remaining); + else + ret = write(fd, b, remaining); + + if (do_read && ret == 0) { + ewerr("End of file detected"); + return total / s->blk_sz; + } + if (ret == -1) { + ewerr("Failed to %s disk file, %s", + do_read ? "read from" : "write to", + strerror(errno)); + return total / s->blk_sz; + } + total += ret; + b += ret; + } + + return count; +} + +static EFI_LBA _read(storage_t *s, EFI_LBA start, EFI_LBA count, + void *buf) +{ + return read_or_write(s, start, count, buf, true); +} + +static EFI_LBA _write(storage_t *s, EFI_LBA start, EFI_LBA count, + const void *buf) +{ + return read_or_write(s, start, count, (void *)buf, false); +} + +static storage_t disk_storage = { + .init = _init, + .read = _read, + .write = _write, + .erase = NULL, + .pci_function = 0, + .pci_device = 0 +}; + +static EFI_HANDLE handle; + +static EFI_STATUS disk_init(EFI_SYSTEM_TABLE *st) +{ + + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (handle) + return EFI_ALREADY_STARTED; + + ret = storage_init(st, &disk_storage, &handle); + if (EFI_ERROR(ret)) + return ret; + + ret = sdio_init(st, handle, &disk_storage); + if (EFI_ERROR(ret)) + storage_free(st, handle); + + return ret; +} + +static EFI_STATUS disk_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!handle) + return EFI_NOT_STARTED; + + if (fd != -1) + close(fd); + + ret = sdio_free(st, handle); + if (EFI_ERROR(ret)) + return ret; + + ret = storage_free(st, handle); + if (EFI_ERROR(ret)) + return ret; + + handle = NULL; + return EFI_SUCCESS; +} + +ewdrv_t disk_drv = { + .name = "disk", + .description = "Emulate eMMC storage", + .init = disk_init, + .exit = disk_exit +}; diff --git a/host/disk.h b/host/disk.h new file mode 100644 index 0000000..ead405e --- /dev/null +++ b/host/disk.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _DISK_H_ +#define _DISK_H_ + +#include + +extern ewdrv_t disk_drv; + +#endif /* _DISK_H_ */ diff --git a/host/event.c b/host/event.c new file mode 100644 index 0000000..55a8139 --- /dev/null +++ b/host/event.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include + +#include "event.h" + +typedef struct event { + UINT32 type; + EFI_TPL tpl; + EFI_EVENT_NOTIFY notify; + VOID *context; + pthread_mutex_t lock; + pthread_cond_t cond; +} event_t; + +static void *call_notify(void *arg) +{ + event_t *event = (event_t *)arg; + + event->notify(event, event->context); + + return NULL; +} + +static EFIAPI EFI_STATUS +create_event(UINT32 Type, + EFI_TPL NotifyTpl, + EFI_EVENT_NOTIFY NotifyFunction, + VOID *NotifyContext, + EFI_EVENT *Event) +{ + event_t *event; + int ret; + + if (!Event || + (Type & EVT_NOTIFY_SIGNAL && Type & EVT_NOTIFY_WAIT) || + (!NotifyFunction && (Type & EVT_NOTIFY_SIGNAL || Type & EVT_NOTIFY_WAIT))) + return EFI_INVALID_PARAMETER; + + event = malloc(sizeof(*event)); + if (!event) + return EFI_OUT_OF_RESOURCES; + + event->type = Type; + event->tpl = NotifyTpl; + event->notify = NotifyFunction; + event->context = NotifyContext; + if (event->type != EVT_NOTIFY_SIGNAL) { + ret = pthread_mutex_init(&event->lock, NULL); + if (ret) + goto err; + + ret = pthread_cond_init(&event->cond, NULL); + if (ret) + goto err; + } + + *Event = event; + + return EFI_SUCCESS; + +err: + free(event); + return EFI_DEVICE_ERROR; + +} + +static EFIAPI EFI_STATUS +set_timer(__attribute__((__unused__)) EFI_EVENT Event, + __attribute__((__unused__)) EFI_TIMER_DELAY Type, + __attribute__((__unused__)) UINT64 TriggerTime) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +wait_for_event(UINTN NumberOfEvents, + EFI_EVENT *Event, + UINTN *Index) +{ + event_t *event; + int ret; + pthread_t thread_notify; + + if (!Event || !Index) + return EFI_INVALID_PARAMETER; + + if (NumberOfEvents != 1) + return EFI_UNSUPPORTED; + + event = (event_t *)*Event; + if (event->type == EVT_NOTIFY_SIGNAL) + return EFI_INVALID_PARAMETER; + + ret = pthread_mutex_lock(&event->lock); + if (ret) + return EFI_DEVICE_ERROR; + + if (event->type == EVT_NOTIFY_WAIT) { + ret = pthread_create(&thread_notify, NULL, call_notify, event); + if (ret) { + pthread_mutex_unlock(&event->lock); + return EFI_DEVICE_ERROR; + } + ret = pthread_detach(thread_notify); + if (ret) + ewdbg("Fail to detach notify thread"); + } + + ret = pthread_cond_wait(&event->cond, &event->lock); + if (ret) { + pthread_mutex_unlock(&event->lock); + return EFI_DEVICE_ERROR; + } + + ret = pthread_mutex_unlock(&event->lock); + if (ret) + return EFI_DEVICE_ERROR; + + *Index = 0; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +signal_event(EFI_EVENT Event) +{ + event_t *event = (event_t *)Event; + int ret; + + if (!Event) + return EFI_INVALID_PARAMETER; + + if (event->type == EVT_NOTIFY_SIGNAL) { + event->notify(Event, event->context); + return EFI_SUCCESS; + } + + ret = pthread_mutex_lock(&event->lock); + if (ret) + return EFI_DEVICE_ERROR; + + ret = pthread_cond_signal(&event->cond); + if (ret) { + pthread_mutex_unlock(&event->lock); + return EFI_DEVICE_ERROR; + } + + ret = pthread_mutex_unlock(&event->lock); + if (ret) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +close_event(EFI_EVENT Event) +{ + free(Event); + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +check_event(__attribute__((__unused__)) EFI_EVENT Event) +{ + return EFI_UNSUPPORTED; +} + +static EFI_CREATE_EVENT saved_create_event; +static EFI_SET_TIMER saved_set_timer; +static EFI_WAIT_FOR_EVENT saved_wait_for_event; +static EFI_SIGNAL_EVENT saved_signal_event; +static EFI_CLOSE_EVENT saved_close_event; +static EFI_CHECK_EVENT saved_check_event; + +static EFI_STATUS event_init(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + saved_create_event = st->BootServices->CreateEvent; + saved_set_timer = st->BootServices->SetTimer; + saved_wait_for_event = st->BootServices->WaitForEvent; + saved_signal_event = st->BootServices->SignalEvent; + saved_close_event = st->BootServices->CloseEvent; + saved_check_event = st->BootServices->CheckEvent; + + st->BootServices->CreateEvent = create_event; + st->BootServices->SetTimer = set_timer; + st->BootServices->WaitForEvent = wait_for_event; + st->BootServices->SignalEvent = signal_event; + st->BootServices->CloseEvent = close_event; + st->BootServices->CheckEvent = check_event; + + return EFI_SUCCESS; +} + +static EFI_STATUS event_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + st->BootServices->CreateEvent = saved_create_event; + st->BootServices->SetTimer = saved_set_timer; + st->BootServices->WaitForEvent = saved_wait_for_event; + st->BootServices->SignalEvent = saved_signal_event; + st->BootServices->CloseEvent = saved_close_event; + st->BootServices->CheckEvent = saved_check_event; + + return EFI_SUCCESS; +} + +ewdrv_t event_drv = { + .name = "event", + .description = "Event management for host", + .init = event_init, + .exit = event_exit +}; diff --git a/host/event.h b/host/event.h new file mode 100644 index 0000000..0c641e0 --- /dev/null +++ b/host/event.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EVENT_H_ +#define _EVENT_H_ + +#include + +extern ewdrv_t event_drv; + +#endif /* _EVENT_H_ */ diff --git a/host/fifo.c b/host/fifo.c new file mode 100644 index 0000000..6788a3b --- /dev/null +++ b/host/fifo.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include + +#include "fifo.h" + +typedef struct cell { + void *data; + struct cell *next; +} cell_t; + +struct fifo { + cell_t *first; + cell_t *last; + pthread_mutex_t lock; +}; + +fifo_t fifo_new(void) +{ + int ret; + fifo_t f; + + f = calloc(sizeof(struct fifo), 1); + if (!f) + return NULL; + + ret = pthread_mutex_init(&f->lock, NULL); + if (ret) { + free(f); + return NULL; + } + + return f; +} + +void fifo_free(fifo_t f) +{ + void *data; + + while (fifo_get(f, &data) == EFI_SUCCESS) + ; + free(f); +} + +static EFI_STATUS _fifo_get(fifo_t f, void **data) +{ + cell_t *cell; + + if (!f->first) + return EFI_NOT_FOUND; + + cell = f->first; + *data = cell->data; + f->first = f->first->next; + + free(cell); + return EFI_SUCCESS; +} + +EFI_STATUS fifo_get(fifo_t f, void **data) +{ + EFI_STATUS ret; + int pret; + + if (!f) + return EFI_INVALID_PARAMETER; + + pret = pthread_mutex_lock(&f->lock); + if (pret) + return EFI_DEVICE_ERROR; + + ret = _fifo_get(f, data); + + pret = pthread_mutex_unlock(&f->lock); + if (pret) + return EFI_DEVICE_ERROR; + + return ret; +} + +static EFI_STATUS _fifo_put(fifo_t f, void *data) +{ + cell_t *cell; + + cell = calloc(sizeof(*cell), 1); + if (!cell) + return EFI_OUT_OF_RESOURCES; + cell->data = data; + + if (!f->first) { + f->first = f->last = cell; + return EFI_SUCCESS; + } + + f->last->next = cell; + if (f->first == f->last) + f->first->next = cell; + f->last = cell; + + return EFI_SUCCESS; +} + +EFI_STATUS fifo_put(fifo_t f, void *data) +{ + EFI_STATUS ret; + int pret; + + if (!f) + return EFI_INVALID_PARAMETER; + + pret = pthread_mutex_lock(&f->lock); + if (pret) + return EFI_DEVICE_ERROR; + + ret = _fifo_put(f, data); + + pret = pthread_mutex_unlock(&f->lock); + if (pret) + return EFI_DEVICE_ERROR; + + return ret; +} diff --git a/host/fifo.h b/host/fifo.h new file mode 100644 index 0000000..2061135 --- /dev/null +++ b/host/fifo.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _FIFO_H_ +#define _FIFO_H_ + +#include +#include + +typedef struct fifo *fifo_t; + +fifo_t fifo_new(void); +void fifo_free(fifo_t f); +EFI_STATUS fifo_put(fifo_t f, void *data); +EFI_STATUS fifo_get(fifo_t f, void **data); + +#endif /* _FIFO_H_ */ diff --git a/host/fileio.c b/host/fileio.c new file mode 100644 index 0000000..7d2ea7a --- /dev/null +++ b/host/fileio.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "fileio.h" + +typedef struct file { + EFI_FILE interface; + char *path; + int fd; +} file_t; + +static void str16_to_str(CHAR16 *s16, char *s) +{ + for (; *s16; s16++, s++) + *s = *s16; + + *s = '\0'; +} + +static EFIAPI EFI_STATUS +file_open(EFI_FILE_HANDLE File, + EFI_FILE_HANDLE *NewHandle, + CHAR16 *FileName, + UINT64 OpenMode, + UINT64 Attributes) +{ + EFI_STATUS ret; + file_t *file = (file_t *)File; + file_t *new_file; + int flags = 0; + + if (!File || !NewHandle || !FileName) + return EFI_INVALID_PARAMETER; + + new_file = malloc(sizeof(*new_file)); + if (!new_file) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + new_file->path = malloc(strlen(file->path) + 1 + str16len(FileName) + 1); + if (!new_file->path) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + memcpy(&new_file->interface, &file->interface, sizeof(file->interface)); + + sprintf(new_file->path, "%s/", file->path); + str16_to_str(FileName, new_file->path + strlen(new_file->path)); + + if ((Attributes & EFI_FILE_MODE_READ) && (Attributes & EFI_FILE_MODE_WRITE)) + flags |= O_RDWR; + else if (Attributes & EFI_FILE_MODE_READ) + flags |= O_RDONLY; + else if (Attributes & EFI_FILE_MODE_WRITE) + flags |= O_WRONLY; + + if (Attributes & EFI_FILE_MODE_CREATE) + flags |= O_CREAT; + + new_file->fd = open(new_file->path, flags, 0644); + if (new_file->fd == -1) { + ret = EFI_DEVICE_ERROR; + goto err; + } + + *NewHandle = (EFI_FILE_HANDLE)new_file; + return EFI_SUCCESS; + +err: + if (new_file) + free(new_file->path); + free(new_file); + + return ret; +} + +static EFIAPI EFI_STATUS +file_close(EFI_FILE_HANDLE File) +{ + file_t *file = (file_t *)File; + + if (!file || file->fd == -1) + return EFI_INVALID_PARAMETER; + + close(file->fd); + if (file->path) + free(file->path); + free(file); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +file_delete(EFI_FILE_HANDLE File) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +file_read(EFI_FILE_HANDLE File, + UINTN *BufferSize, + VOID *Buffer) +{ + file_t *file = (file_t *)File; + ssize_t nb; + size_t remaining; + + if (!file) + return EFI_INVALID_PARAMETER; + + for (remaining = *BufferSize; remaining; remaining -= nb) { + nb = read(file->fd, (char *)Buffer + *BufferSize - remaining, + remaining); + if (nb == 0) + return EFI_INVALID_PARAMETER; + + if (nb == -1) { + if (errno == EINTR) + continue; + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +file_write(EFI_FILE_HANDLE File, + UINTN *BufferSize, + VOID *Buffer) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +file_set_position(EFI_FILE_HANDLE File, + UINT64 Position) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +file_get_position(EFI_FILE_HANDLE File, + UINT64 *Position) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +file_get_info(EFI_FILE_HANDLE File, + EFI_GUID *InformationType, + UINTN *BufferSize, + VOID *Buffer) +{ + int retp; + file_t *file = (file_t *)File; + struct stat sb; + EFI_FILE_INFO *info = Buffer; + EFI_GUID fileinfo_guid = EFI_FILE_INFO_ID; + + if (!File || !InformationType || !BufferSize || !Buffer) + return EFI_INVALID_PARAMETER; + + if (guidcmp(InformationType, &fileinfo_guid)) + return EFI_UNSUPPORTED; + + if (*BufferSize < sizeof(EFI_FILE_INFO)) + return EFI_INVALID_PARAMETER; + + retp = fstat(file->fd, &sb); + if (retp == -1) + return EFI_DEVICE_ERROR; + + info->Size = sizeof(*info); + info->FileSize = sb.st_size; + + *BufferSize = sizeof(*info); + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +file_set_info(EFI_FILE_HANDLE File, + EFI_GUID *InformationType, + UINTN BufferSize, + VOID *Buffer) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +file_flush(EFI_FILE_HANDLE File) +{ + return EFI_UNSUPPORTED; +} + +static EFI_FILE file_handle = { + .Revision = EFI_FILE_HANDLE_REVISION, + .Open = file_open, + .Close = file_close, + .Delete = file_delete, + .Read = file_read, + .Write = file_write, + .GetPosition = file_get_position, + .SetPosition = file_set_position, + .GetInfo = file_get_info, + .SetInfo = file_set_info, + .Flush = file_flush +}; + +typedef struct volume { + EFI_FILE_IO_INTERFACE interface; + char *path; + file_t file; +} volume_t; + +static EFIAPI EFI_STATUS +open_volume(EFI_FILE_IO_INTERFACE *This, + EFI_FILE_HANDLE *Root) +{ + volume_t *volume = (volume_t *)This; + + if (!This || !Root) + return EFI_INVALID_PARAMETER; + + *Root = (EFI_FILE_HANDLE)&volume->file; + return EFI_SUCCESS; +} + +static EFI_FILE_IO_INTERFACE file_io_struct = { + .Revision = EFI_FILE_IO_INTERFACE_REVISION, + .OpenVolume = open_volume +}; + +static EFI_GUID fileio_guid = SIMPLE_FILE_SYSTEM_PROTOCOL; +static EFI_HANDLE fileio_handle; + +static EFI_STATUS fileio_init(EFI_SYSTEM_TABLE *st) +{ + static volume_t root_default; + EFI_STATUS ret; + volume_t *root; + EFI_GUID image_guid = LOADED_IMAGE_PROTOCOL; + EFI_HANDLE image_handle; + EFI_LOADED_IMAGE *img; + UINTN size; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (fileio_handle) + return EFI_ALREADY_STARTED; + + if (!root_default.path) { + root_default.path = root_default.file.path = "./"; + root_default.file.fd = -1; + memcpy(&root_default.file.interface, &file_handle, + sizeof(file_handle)); + memcpy(&root_default.interface, &file_io_struct, + sizeof(file_io_struct)); + } + + size = sizeof(image_handle); + ret = uefi_call_wrapper(st->BootServices->LocateHandle, 5, + ByProtocol, &image_guid, NULL, + &size, &image_handle); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + image_handle, &image_guid, (void **)&img); + if (EFI_ERROR(ret)) + return ret; + + ret = interface_init(st, &fileio_guid, &fileio_handle, + &root_default, sizeof(root_default), + (void **)&root); + if (EFI_ERROR(ret)) + return ret; + + img->DeviceHandle = fileio_handle; + return EFI_SUCCESS; +} + +static EFI_STATUS fileio_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!fileio_handle) + return EFI_NOT_STARTED; + + ret = interface_free(st, &fileio_guid, fileio_handle); + if (EFI_ERROR(ret)) + return ret; + + fileio_handle = NULL; + return EFI_SUCCESS; +} + +ewdrv_t fileio_drv = { + .name = "fileio", + .description = "File System Protocol support", + .init = fileio_init, + .exit = fileio_exit +}; diff --git a/host/fileio.h b/host/fileio.h new file mode 100644 index 0000000..d3fa161 --- /dev/null +++ b/host/fileio.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _FILEIO_H_ +#define _FILEIO_H_ + +#include + +extern ewdrv_t fileio_drv; + +#endif /* _FILEIO_H_ */ diff --git a/host/gop.c b/host/gop.c new file mode 100644 index 0000000..05d8d95 --- /dev/null +++ b/host/gop.c @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gop.h" + +typedef struct gop { + EFI_GRAPHICS_OUTPUT_PROTOCOL prot; + bool running; + Display *display; + GC gc; + Window win; + Window root; + XImage *img; + pthread_t thread; + pthread_cond_t ready; + pthread_mutex_t lock; +} gop_t; + +static const KeySym EXIT_KEYSYM = XK_End; +static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +static EFI_HANDLE gop_handle; +static gop_t *gop; + +static EFI_GRAPHICS_OUTPUT_MODE_INFORMATION info = { + .Version = 1, + .HorizontalResolution = 768, + .VerticalResolution = 1024, + .PixelFormat = PixelRedGreenBlueReserved8BitPerColor +}; + +static EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE mode = { + .MaxMode = 1, + .Info = &info, + .SizeOfInfo = sizeof(info) +}; + +static EFIAPI EFI_STATUS +query_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + UINT32 ModeNumber, + UINTN *SizeOfInfo, + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info) +{ + if (!This || ModeNumber != 0 || !SizeOfInfo || !Info) + return EFI_INVALID_PARAMETER; + + *SizeOfInfo = sizeof(info); + *Info = &info; + + return EFI_SUCCESS; +} + +static inline EFI_STATUS gop_lock(void) +{ + int ret; + + ret = pthread_mutex_lock(&gop->lock); + if (ret) { + ewerr("Failed to lock the GOP lock"); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static inline EFI_STATUS gop_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&gop->lock); + if (ret) { + ewerr("Failed to unlock the GOP lock"); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS wait_for_readiness(void) +{ + EFI_STATUS ret; + int pret; + + ret = gop_lock(); + if (EFI_ERROR(ret)) + return ret; + + pret = pthread_cond_wait(&gop->ready, &gop->lock); + if (pret) + return EFI_DEVICE_ERROR; + + return gop_unlock(); +} + +static EFI_STATUS notify_is_ready(void) +{ + EFI_STATUS ret; + int pret; + + ret = gop_lock(); + if (EFI_ERROR(ret)) + return ret; + + pret = pthread_cond_signal(&gop->ready); + if (pret) + return EFI_DEVICE_ERROR; + + return gop_unlock(); +} + +static XVisualInfo *get_visual_info(Display *display, int depth, int class) +{ + XVisualInfo template; + int nitems; + + memset(&template, 0, sizeof(template)); + template.screen = DefaultScreen(display); + template.class = class; + template.depth = depth; + + return XGetVisualInfo(display, VisualScreenMask, + &template, &nitems); +} + +static inline void put_image(void) +{ + XPutImage(gop->display, gop->win, gop->gc, gop->img, + 0, 0, 0, 0, + info.HorizontalResolution, + info.VerticalResolution); +} + +static void *draw(void *arg) +{ + EFI_STATUS ret; + XVisualInfo *vi; + XSetWindowAttributes attr; + XEvent event; + KeyCode exit_keycode; + void *data; + + gop->display = XOpenDisplay(NULL); + if (!gop->display) { + ewerr("Failed to open X display"); + return NULL; + } + + vi = get_visual_info(gop->display, 24, TrueColor); + if (!vi) { + ewerr("Failed to get appropriate visual info"); + return NULL; + } + + gop->root = DefaultRootWindow(gop->display); + + attr.event_mask = ExposureMask | KeyPressMask; + attr.colormap = XCreateColormap(gop->display, gop->root, vi->visual, + AllocNone); + gop->win = XCreateWindow(gop->display, gop->root, 0, 0, + info.HorizontalResolution, + info.VerticalResolution, + 0, vi->depth, + InputOutput, vi->visual, + CWEventMask | CWColormap, &attr); + XStoreName(gop->display, gop->win, "EfiWrapper"); + XMapWindow(gop->display, gop->win); + + gop->gc = XCreateGC(gop->display, gop->win, 0, 0); + exit_keycode = XKeysymToKeycode(gop->display, EXIT_KEYSYM); + + for (;;) { + XNextEvent(gop->display, &event); + + if (event.type == Expose && event.xexpose.count == 0) { + if (!gop->running) { + data = malloc(sizeof(unsigned int) * + info.HorizontalResolution * + info.VerticalResolution); + if (!data) + return NULL; + gop->img = XCreateImage(gop->display, CopyFromParent, + vi->depth, ZPixmap, 0, data, + info.HorizontalResolution, + info.VerticalResolution, + 32, 0); + if (!gop->img) { + ewerr("Failed to create XImage"); + break; + } + gop->running = true; + notify_is_ready(); + continue; + } + + ret = gop_lock(); + if (EFI_ERROR(ret)) + break; + + put_image(); + + ret = gop_unlock(); + if (EFI_ERROR(ret)) + break; + } else if (event.type == KeyPress && + event.xkey.keycode == exit_keycode) + break; + } + + + ret = gop_lock(); + if (EFI_ERROR(ret)) + return NULL; + + if (gop->img) { + XDestroyImage(gop->img); + gop->img = NULL; + } + XFreeGC(gop->display, gop->gc); + XUnmapWindow(gop->display, gop->win); + XDestroyWindow(gop->display, gop->win); + XFree(vi); + XCloseDisplay(gop->display); + + ret = gop_unlock(); + if (EFI_ERROR(ret)) + return NULL; + + return NULL; +} + +static EFI_STATUS stop_gop(void) +{ + Display *display; + void *retval; + int ret; + XKeyEvent event = { + .subwindow = None, + .time = CurrentTime, + .x = 1, + .y = 1, + .x_root = 1, + .y_root = 1, + .same_screen = True, + .state = 0, + .type = KeyPress + }; + Status status; + + if (!gop || !gop->running) + return EFI_SUCCESS; + + display = XOpenDisplay(NULL); + if (!display) + return EFI_DEVICE_ERROR; + + event.display = display; + event.window = gop->win; + event.root = gop->root; + event.keycode = XKeysymToKeycode(gop->display, EXIT_KEYSYM); + + status = XSendEvent(event.display, event.window, True, KeyPressMask, + (XEvent *)&event); + if (!status) + return EFI_DEVICE_ERROR; + + XCloseDisplay(display); + + ret = pthread_join(gop->thread, &retval); + if (ret) { + ewerr("Failed to join gop thread, %s", strerror(ret)); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +set_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + UINT32 ModeNumber) +{ + EFI_STATUS ret; + int pret; + + if (!This || (gop_t *)This != gop || ModeNumber != 0) + return EFI_INVALID_PARAMETER; + + ret = stop_gop(); + if (EFI_ERROR(ret)) { + ewerr("Failed to stop GOP thread"); + return ret; + } + + gop->running = false; + + pret = pthread_mutex_init(&gop->lock, NULL); + if (pret) + return EFI_DEVICE_ERROR; + + pret = pthread_cond_init(&gop->ready, NULL); + if (pret) + return EFI_DEVICE_ERROR; + + XInitThreads(); + pret = pthread_create(&gop->thread, NULL, draw, NULL); + if (pret) + return EFI_DEVICE_ERROR; + + return wait_for_readiness(); +} + +static void fill_buffer(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, + UINTN destx, + UINTN desty, + UINTN width, + UINTN height) +{ + size_t x, y; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *data; + + data = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)gop->img->data; + for (y = desty; y < desty + height; y++) + for (x = destx; x < destx + width; x++) + data[y * info.HorizontalResolution + x] = *color; +} + +static void copy_to_buffer(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buffer, + UINTN srcx, + UINTN srcy, + UINTN destx, + UINTN desty, + UINTN width, + UINTN height) +{ + size_t y, sy; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *data; + + data = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)gop->img->data; + for (y = desty, sy = srcy; y < desty + height; y++, sy++) + memcpy(&data[y * info.HorizontalResolution + destx], + &buffer[sy * width + srcx], + width * sizeof(*buffer)); +} + +static EFIAPI EFI_STATUS +blt(EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + UINTN SourceX, + UINTN SourceY, + UINTN DestinationX, + UINTN DestinationY, + UINTN Width, + UINTN Height, + __attribute__((__unused__)) UINTN Delta) +{ + EFI_STATUS ret = EFI_SUCCESS; + EFI_STATUS unlock_ret; + + if (!This || (gop_t *)This != gop || !BltBuffer) + return EFI_INVALID_PARAMETER; + + if (DestinationX + Width > info.HorizontalResolution || + DestinationY + Height > info.VerticalResolution) + return EFI_INVALID_PARAMETER; + + ret = gop_lock(); + if (EFI_ERROR(ret)) + return ret; + + if (!gop->img) { + ret = EFI_NOT_STARTED; + goto exit; + } + + switch (BltOperation) { + case EfiBltVideoFill: + fill_buffer(BltBuffer, DestinationX, DestinationY, + Width, Height); + break; + + case EfiBltBufferToVideo: + copy_to_buffer(BltBuffer, SourceX, SourceY, + DestinationX, DestinationY, + Width, Height); + break; + + default: + ret = EFI_UNSUPPORTED; + } + + if (EFI_ERROR(ret)) + goto exit; + + put_image(); + +exit: + unlock_ret = gop_unlock(); + return EFI_ERROR(ret) ? ret : unlock_ret; +} + +static EFI_STATUS gop_init(EFI_SYSTEM_TABLE *st) +{ + static gop_t gop_default = { + .prot = { + .QueryMode = query_mode, + .SetMode = set_mode, + .Blt = blt, + .Mode = &mode + } + }; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (gop_handle) + return EFI_ALREADY_STARTED; + + return interface_init(st, &gop_guid, &gop_handle, + &gop_default, sizeof(gop_default), + (void **)&gop); +} + +static EFI_STATUS gop_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!gop_handle) + return EFI_NOT_STARTED; + + ret = stop_gop(); + if (EFI_ERROR(ret)) { + ewerr("Failed to stop GOP thread"); + return ret; + } + + ret = interface_free(st, &gop_guid, gop_handle); + if (EFI_ERROR(ret)) + return ret; + + gop = NULL; + gop_handle = NULL; + + return EFI_SUCCESS; +} + +ewdrv_t gop_drv = { + .name = "gop", + .description = "Graphics Output Protocol support based on Xlib", + .init = gop_init, + .exit = gop_exit +}; diff --git a/host/gop.h b/host/gop.h new file mode 100644 index 0000000..414f3cf --- /dev/null +++ b/host/gop.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _GOP_H_ +#define _GOP_H_ + +#include + +extern ewdrv_t gop_drv; + +#endif /* _GOP_H_ */ diff --git a/host/host_time.c b/host/host_time.c new file mode 100644 index 0000000..cd45e35 --- /dev/null +++ b/host/host_time.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: Léo Sartre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include + +#include +#include + +#include "host_time.h" + +static EFIAPI EFI_STATUS +get_time(EFI_TIME *Time, + __attribute__((__unused__)) EFI_TIME_CAPABILITIES *Capabilities) +{ + struct tm *now; + struct timeval today; + int ret; + + if (!Time) + return EFI_INVALID_PARAMETER; + + ret = gettimeofday(&today, NULL); + if (ret) + return EFI_NO_RESPONSE; + + now = gmtime(&today.tv_sec); + if (!now) + return EFI_NO_RESPONSE; + + memset(Time, 0, sizeof(*Time)); + Time->Year = now->tm_year + 1900; + Time->Month = now->tm_mon + 1; + Time->Day = now->tm_mday; + Time->Hour = now->tm_hour; + Time->Minute = now->tm_min; + Time->Second = now->tm_sec; + Time->Nanosecond = today.tv_usec * 1000; + Time->TimeZone = timezone / 60; + Time->Daylight = daylight; + + return EFI_SUCCESS; +} + +static EFI_STATUS time_init(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + st->RuntimeServices->GetTime = get_time; + + return EFI_SUCCESS; +} + +static EFI_STATUS time_exit(EFI_SYSTEM_TABLE *st) +{ + return EFI_SUCCESS; +} + +ewdrv_t time_drv = { + .name = "time", + .description = "Provide the GetTime runtime service support based \ +on gmtime() function.", + .init = time_init, + .exit = time_exit +}; diff --git a/host/host_time.h b/host/host_time.h new file mode 100644 index 0000000..7497621 --- /dev/null +++ b/host/host_time.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: Léo Sartre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _HOST_TIME_H_ +#define _HOST_TIME_H_ + +#include + +extern ewdrv_t time_drv; + +#endif /* _HOST_TIME_H_ */ diff --git a/host/image.c b/host/image.c new file mode 100644 index 0000000..4e7377f --- /dev/null +++ b/host/image.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include + +#include "image.h" +#include "pe.h" + +static EFI_GUID image_guid = LOADED_IMAGE_PROTOCOL; +EFI_SYSTEM_TABLE *saved_st; + +static EFIAPI EFI_STATUS +load_image(__attribute__((__unused__)) BOOLEAN BootPolicy, + EFI_HANDLE ParentImageHandle, + EFI_DEVICE_PATH *FilePath, + VOID *SourceBuffer, + UINTN SourceSize, + EFI_HANDLE *ImageHandle) +{ + EFI_STATUS ret; + image_t tmp, *image; + + if (FilePath) + return EFI_UNSUPPORTED; + + if (!SourceBuffer || !SourceSize || !ImageHandle) + return EFI_INVALID_PARAMETER; + + memset(&tmp, 0, sizeof(tmp)); + tmp.prot.Revision = EFI_IMAGE_INFORMATION_REVISION; + tmp.prot.ParentHandle = ParentImageHandle; + tmp.prot.SystemTable = saved_st; + + ret = pe_load(SourceBuffer, SourceSize, &tmp); + if (EFI_ERROR(ret)) { + ewerr("Failed to load PE executable"); + return ret; + } + + return interface_init(saved_st, &image_guid, ImageHandle, + (void *)&tmp, sizeof(tmp), (void **)&image); +} + +static inline EFI_STATUS get_image(image_t **image, EFI_HANDLE handle) +{ + return uefi_call_wrapper(saved_st->BootServices->HandleProtocol, 3, + handle, &image_guid, (void **)image); +} + +static EFIAPI EFI_STATUS +start_image(EFI_HANDLE ImageHandle, + UINTN *ExitDataSize, + CHAR16 **ExitData) +{ + EFI_STATUS ret; + image_t *image; + int setjmpret; + + if (ExitDataSize || ExitData) + return EFI_UNSUPPORTED; + + ret = get_image(&image, ImageHandle); + if (EFI_ERROR(ret)) + return ret; + + setjmpret = setjmp(image->jmp); + if (setjmpret != 0) + return image->exit_status; + + return image->entry(ImageHandle, saved_st); +} + +static EFIAPI EFI_STATUS +efi_exit(EFI_HANDLE ImageHandle, + EFI_STATUS ExitStatus, + UINTN ExitDataSize, + __attribute__((__unused__)) CHAR16 *ExitData) +{ + EFI_STATUS ret; + image_t *image; + + if (ExitDataSize) + return EFI_UNSUPPORTED; + + ret = get_image(&image, ImageHandle); + if (EFI_ERROR(ret)) + return ret; + + image->exit_status = ExitStatus; + longjmp(image->jmp, 1); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +unload_image(EFI_HANDLE ImageHandle) +{ + EFI_STATUS ret; + image_t *image; + + ret = get_image(&image, ImageHandle); + if (EFI_ERROR(ret)) + return ret; + + pe_unload(image); + + return interface_free(saved_st, &image_guid, ImageHandle); +} + +static EFI_IMAGE_LOAD saved_load_image; +static EFI_IMAGE_START saved_start_image; +static EFI_EXIT saved_efi_exit; +static EFI_IMAGE_UNLOAD saved_unload_image; + +static EFI_STATUS image_init(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + saved_st = st; + + saved_load_image = st->BootServices->LoadImage; + saved_start_image = st->BootServices->StartImage; + saved_efi_exit = st->BootServices->Exit; + saved_unload_image = st->BootServices->UnloadImage; + + st->BootServices->LoadImage = load_image; + st->BootServices->StartImage = start_image; + st->BootServices->Exit = efi_exit; + st->BootServices->UnloadImage = unload_image; + + return EFI_SUCCESS; +} + +static EFI_STATUS image_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + st->BootServices->LoadImage = saved_load_image; + st->BootServices->StartImage = saved_start_image; + st->BootServices->Exit = saved_efi_exit; + st->BootServices->UnloadImage = saved_unload_image; + + return EFI_SUCCESS; +} + +ewdrv_t image_drv = { + .name = "image", + .description = "PE/COFF image", + .init = image_init, + .exit = image_exit +}; diff --git a/host/image.h b/host/image.h new file mode 100644 index 0000000..82c3627 --- /dev/null +++ b/host/image.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include +#include + +typedef struct image { + EFI_LOADED_IMAGE prot; + void *data; + jmp_buf jmp; + EFI_IMAGE_ENTRY_POINT entry; + EFI_STATUS exit_status; +} image_t; + +extern ewdrv_t image_drv; + +#endif /* _IMAGE_H_ */ diff --git a/host/main.c b/host/main.c new file mode 100644 index 0000000..2e13674 --- /dev/null +++ b/host/main.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "disk.h" +#include "event.h" +#include "tcp4.h" +#include "fileio.h" +#include "gop.h" +#include "image.h" +#include "host_time.h" +#include "terminal_curses.h" + +static ewdrv_t *host_drivers[] = { + &disk_drv, + &event_drv, + &tcp4_drv, + &fileio_drv, + &gop_drv, + &image_drv, + &time_drv, + &terminal_curses_drv, + NULL +}; +ewdrv_t **ew_drivers = host_drivers; + +typedef EFI_STATUS (*efi_main_t)(EFI_HANDLE image, EFI_SYSTEM_TABLE *st); +static jmp_buf jmp; +static EFI_STATUS reset_status; +static char *cmdname; + +static EFIAPI EFI_STATUS +reset_system(__attribute__((__unused__)) EFI_RESET_TYPE ResetType, + EFI_STATUS ResetStatus, + __attribute__((__unused__)) UINTN DataSize, + __attribute__((__unused__)) CHAR16 *ResetData) +{ + reset_status = ResetStatus; + longjmp(jmp, 1); + + return EFI_SUCCESS; +} + +static EFI_STATUS read_file(int fd, void *data, UINTN size) +{ + ssize_t nb; + size_t remaining; + + for (remaining = size; remaining; remaining -= nb) { + nb = read(fd, (char *)data + size - remaining, remaining); + if (nb == 0) + return EFI_INVALID_PARAMETER; + + if (nb == -1) { + if (errno == EINTR) + continue; + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS load_file(const char *path, void **data_p, + UINTN *size_p) +{ + EFI_STATUS ret;; + int fd, pret; + struct stat sb; + void *data; + + fd = open(path, O_RDONLY); + if (fd == -1) { + ewerr("Failed to open %s file, %s", path, strerror(errno)); + return EFI_INVALID_PARAMETER; + } + + pret = fstat(fd, &sb); + if (pret == -1) { + ewerr("Failed to fstat %s file, %s", path, strerror(errno)); + ret = EFI_DEVICE_ERROR; + goto err; + } + + data = malloc(sb.st_size); + if (!data) { + ewerr("Failed to allocate buffer to read %s file", path); + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + ret = read_file(fd, data, sb.st_size); + if (EFI_ERROR(ret)) { + ewerr("Failed to read %s file", path); + free(data); + goto err; + } + + *data_p = data; + *size_p = sb.st_size; + +err: + close(fd); + return ret; +} + + +static EFI_STATUS setup_load_image(EFI_SYSTEM_TABLE *st, + EFI_HANDLE parent, EFI_HANDLE child) +{ + EFI_STATUS ret; + EFI_LOADED_IMAGE *img_parent, *img_child; + EFI_GUID image_guid = LOADED_IMAGE_PROTOCOL; + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + parent, &image_guid, (void **)&img_parent); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + child, &image_guid, (void **)&img_child); + if (EFI_ERROR(ret)) + return ret; + + img_child->LoadOptions = img_parent->LoadOptions; + img_child->DeviceHandle = img_parent->DeviceHandle; + + return EFI_SUCCESS; +} + +static EFI_STATUS load_and_execute(const char *path, + EFI_HANDLE parent, + EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + EFI_RESET_SYSTEM saved_reset_rs; + EFI_HANDLE image = NULL; + void *data; + UINTN size; + int setjmpret; + + ret = load_file(path, &data, &size); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(st->BootServices->LoadImage, 6, + FALSE, parent, NULL, + data, size, &image); + free(data); + if (EFI_ERROR(ret)) { + ewerr("Failed to load image %s", path); + return ret; + } + + saved_reset_rs = st->RuntimeServices->ResetSystem; + st->RuntimeServices->ResetSystem = reset_system; + + ret = setup_load_image(st, parent, image); + if (EFI_ERROR(ret)) + goto exit; + + setjmpret = setjmp(jmp); + if (setjmpret == 0) + ret = uefi_call_wrapper(st->BootServices->StartImage, 3, + image, NULL, NULL); + else + ret = reset_status; + +exit: + st->RuntimeServices->ResetSystem = saved_reset_rs; + uefi_call_wrapper(st->BootServices->UnloadImage, 1, image); + + return ret; +} + +static void usage(int ret) __attribute__ ((noreturn)); +static void usage(int ret) +{ + printf("Usage: %s [OPTIONS] [ARGS]\n", cmdname); + printf(" OPTIONS:\n"); + printf(" -h,--help Print this help\n"); + printf(" --list-drivers List available drivers\n"); + printf(" --disable-drivers=DRV1,DRV2 Disable drivers DRV1 and DRV2\n"); + exit(ret); +} + +static void error(const char *fmt, ...) __attribute__ ((noreturn)); +static void error(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + usage(EXIT_FAILURE); +} + +static void help(char *arg) __attribute__ ((noreturn)); +static void help(__attribute__((__unused__)) char *arg) +{ + usage(EXIT_SUCCESS); +} + +static void list_drivers(char *arg) __attribute__ ((noreturn)); +static void list_drivers(__attribute__((__unused__)) char *arg) +{ + size_t i; + + printf("Drivers list:\n"); + for (i = 0; ew_drivers[i]; i++) + printf("- %s: %s\n", ew_drivers[i]->name, + ew_drivers[i]->description); + + exit(EXIT_SUCCESS); +} + +static void disable_driver(char *name) +{ + size_t i; + bool found = false; + + for (i = 0; ew_drivers[i]; i++) { + if (found || !strcmp(ew_drivers[i]->name, name)) { + ew_drivers[i] = ew_drivers[i + 1]; + found = true; + } + } + + if (!found) + error("'%s' driver not found\n", name); +} + +static void disable_drivers(char *names) +{ + char *saveptr, *name; + + name = strtok_r(names, ",", &saveptr); + while (name) { + disable_driver(name); + name = strtok_r(NULL, ",", &saveptr); + } +} + +static struct option { + const char *name; + bool has_argument; + void (*fun)(char *param); +} OPTIONS[] = { + { "-h", false, help }, + { "--help", false, help }, + { "--list-drivers", false, list_drivers }, + { "--disable-drivers", true, disable_drivers } +}; + +static struct option *get_option(char *name, char **arg) +{ + size_t i, len; + + *arg = NULL; + for (i = 0; i < ARRAY_SIZE(OPTIONS); i++) { + len = strlen(OPTIONS[i].name); + + if (strncmp(OPTIONS[i].name, name, len)) + continue; + + if (name[len] == '\0' && !OPTIONS[i].has_argument) + return &OPTIONS[i]; + + if (name[len] == '=' && OPTIONS[i].has_argument) { + *arg = &name[len + 1]; + return &OPTIONS[i]; + } + } + + return NULL; +} + +static void parse_options(int *argc, char ***argv) +{ + size_t i; + struct option *option; + char *cur, *arg; + + for (i = 1; i < (size_t)*argc; i++) { + cur = (*argv)[i]; + + if (*cur != '-') + goto exit; + + option = get_option(cur, &arg); + if (!option) + error("Unknown option '%s'\n", cur); + + option->fun(arg); + } + +exit: + *argc = *argc - i + 1; + *argv = *argv + i - 1; +} + +int main(int argc, char **argv) +{ + EFI_HANDLE image = NULL; + EFI_SYSTEM_TABLE *st; + EFI_STATUS ret; + + cmdname = basename(argv[0]); + + parse_options(&argc, &argv); + if (argc < 2) + error("Not enough parameter\n"); + + ret = efiwrapper_init(argc - 1, argv + 1, &st, &image); + if (ret) { + ewerr("efiwrapper library initialization failed"); + return EXIT_FAILURE; + } + + ret = ewdrv_init(st); + if (ret) { + ewerr("drivers initialization failed"); + return EXIT_FAILURE; + } + + ret = load_and_execute(argv[1], image, st); + if (EFI_ERROR(ret)) + ewerr("%s load and execute failed, ret=0x%zx", + argv[1], (size_t)ret); + + ret = ewdrv_exit(st); + if (ret) + ewerr("drivers release failed"); + + ret = efiwrapper_free(image); + if (EFI_ERROR(ret)) + ewerr("efiwrapper library exit failed"); + + return EFI_ERROR(ret) ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/host/pe.c b/host/pe.c new file mode 100644 index 0000000..942b246 --- /dev/null +++ b/host/pe.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include + +#include "pe.h" + +/* The following structure definitions come from: + * https://en.wikipedia.org/wiki/Portable_Executable. */ +#pragma pack(1) + +#define DOS_MAGIC 0x5A4D /* "MZ" */ + +typedef struct { + UINT16 magic; /* Magic number */ + UINT16 cblp; /* Bytes on last page of file */ + UINT16 cp; /* Pages in file */ + UINT16 crlc; /* Relocations */ + UINT16 cparhdr; /* Size of header in paragraphs */ + UINT16 minalloc; /* Minimum extra paragraphs needed */ + UINT16 maxalloc; /* Maximum extra paragraphs needed */ + UINT16 ss; /* Initial (relative) SS value */ + UINT16 sp; /* Initial SP value */ + UINT16 csum; /* Checksum */ + UINT16 ip; /* Initial IP value */ + UINT16 cs; /* Initial (relative) CS value */ + UINT16 lfarlc; /* File address of relocation table */ + UINT16 ovno; /* Overlay number */ + UINT16 res[4]; /* Reserved words */ + UINT16 oemid; /* OEM identifier (for e_oeminfo) */ + UINT16 oeminfo; /* OEM information; e_oemid specific */ + UINT16 res2[10]; /* Reserved words */ + UINT32 lfanew; /* File address of new exe header */ +} dos_header_t; + +#define COFF_HDR_SIGNATURE 0x4550 /* "PE" */ + +typedef struct { + UINT32 signature; + UINT16 machine; + UINT16 number_of_sections; + UINT32 time_date_stamp; + UINT32 pointer_to_symbol_table; /* Deprecated */ + UINT32 number_of_symbols; /* Deprecated */ + UINT16 size_of_optional_header; + UINT16 characteristics; +} coff_header_t; + +#ifdef __LP64__ +#define COFF_STD_MAGIC 0x020b +#else +#define COFF_STD_MAGIC 0x010b +#endif + +typedef struct { + UINT16 magic; + UINT8 major_linker_version; + UINT8 minor_linker_version; + UINT32 size_of_code; + UINT32 size_of_initialized_data; + UINT32 size_of_uninitialized_data; + UINT32 address_of_entry_point; + UINT32 base_of_code; +#ifndef __LP64__ + UINT32 base_of_data; +#endif +} std_coff_t; + +typedef struct { + UINTN image_base; + UINT32 section_alignment; + UINT32 file_alignment; + UINT16 major_operating_system_version; + UINT16 minor_operating_system_version; + UINT16 major_image_version; + UINT16 minor_image_version; + UINT16 major_subsystem_version; + UINT16 minor_subsystem_version; + UINT32 win32_version_value; + UINT32 size_of_image; + UINT32 size_of_headers; + UINT32 checksum; + UINT16 subsystem; + UINT16 dll_characteristics; + UINTN size_of_stack_reserve; + UINTN size_of_stack_commit; + UINTN size_of_heap_reserve; + UINTN size_of_heap_commit; + UINT32 loader_flags; + UINT32 number_of_rva_and_sizes; +} win_t; + +typedef struct { + coff_header_t hdr; + struct { + std_coff_t std; + win_t win; + struct { + UINT32 address; + UINT32 size; + } data_directory[16]; + } opt; +} pe_coff_t; + +typedef struct { + UINT8 name[8]; + UINT32 virtual_size; + UINT32 virtual_address; + UINT32 size_of_raw_data; + UINT32 pointer_to_raw_data; + UINT32 pointer_to_relocations; + UINT32 pointer_to_line_numbers; + UINT16 number_of_relocations; + UINT16 number_of_line_numbers; + UINT32 characteristics; +} section_header_t; + +#pragma pack() + +typedef struct { + void *addr; + size_t size; + UINT32 start; + UINT32 end; +} loaded_section_t; + +void get_section_boundaries(section_header_t *section, UINT16 nb, + UINT32 *start, UINT32 *end) +{ + UINT16 i; + + *start = (UINT32)-1; + *end = 0; + + for (i = 0; i < nb; i++) { + *start = min(*start, section[i].virtual_address); + *end = max(*end, section[i].virtual_address + + section[i].virtual_size); + } +} + +EFI_STATUS load_sections(section_header_t *section, UINT16 nb, + void *base, image_t *image) +{ + loaded_section_t *data; + UINT16 i; + char *dst, *src; + size_t size; + + data = malloc(sizeof(*data)); + if (!data) { + ewerr("Failed to allocate loaded section"); + return EFI_OUT_OF_RESOURCES; + } + + get_section_boundaries(section, nb, &data->start, &data->end); + + data->size = data->end - data->start; + data->addr = mmap(NULL, data->size, PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if (!data->addr) { + ewerr("Failed to allocate sections memory"); + free(data); + return EFI_OUT_OF_RESOURCES; + } + + memset(data->addr, 0, data->size); + image->data = data; + + for (i = 0; i < nb; i++) { + src = (char *)base + section[i].pointer_to_raw_data; + dst = (char *)data->addr + section[i].virtual_address - + data->start; + size = section[i].virtual_size; + + if (!size && section[i].size_of_raw_data) + size = section[i].size_of_raw_data; + memcpy(dst, src, size); + + if (size < section[i].virtual_size) + memset(dst + size, 0, section[i].virtual_size - size); + + if (!memcmp(".text", section[i].name, 6)) + ewdbg(".text section loaded at %p", dst); + } + + return EFI_SUCCESS; +} + +EFI_STATUS pe_load(void *data, UINTN size, image_t *image) +{ + EFI_STATUS ret; + dos_header_t *dos_hdr; + pe_coff_t *pe; + char *entry; + + if (!data || !size || !image) + return EFI_INVALID_PARAMETER; + + if (size < sizeof(*dos_hdr)) + return EFI_INVALID_PARAMETER; + + dos_hdr = (dos_header_t *)data; + if (dos_hdr->magic != DOS_MAGIC) + return EFI_INVALID_PARAMETER; + + if (!dos_hdr->lfanew || dos_hdr->lfanew + sizeof(*pe) > size) + return EFI_INVALID_PARAMETER; + + pe = (pe_coff_t *)((char *)data + dos_hdr->lfanew); + if (pe->hdr.signature != COFF_HDR_SIGNATURE) + return EFI_INVALID_PARAMETER; + + if (pe->opt.std.magic != COFF_STD_MAGIC) + return EFI_INVALID_PARAMETER; + + ret = load_sections((section_header_t *)(pe + 1), + pe->hdr.number_of_sections, + data, image); + if (EFI_ERROR(ret)) + return ret; + + entry = (char *)((loaded_section_t *)image->data)->addr; + entry += pe->opt.std.address_of_entry_point; + entry -= ((loaded_section_t *)image->data)->start; + image->entry = (EFI_IMAGE_ENTRY_POINT)entry; + + return EFI_SUCCESS; +} + +EFI_STATUS pe_unload(image_t *image) +{ + loaded_section_t *data; + + if (!image || !image->data) + return EFI_INVALID_PARAMETER; + + data = image->data; + munmap(data->addr, data->size); + free(data); + image->data = NULL; + + return EFI_SUCCESS; +} diff --git a/host/pe.h b/host/pe.h new file mode 100644 index 0000000..146211f --- /dev/null +++ b/host/pe.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _PE_H_ +#define _PE_H_ + +#include +#include + +#include "image.h" + +EFI_STATUS pe_load(void *data, UINTN size, image_t *image); +EFI_STATUS pe_unload(image_t *image); + +#endif /* _PE_H_ */ diff --git a/host/tcp4.c b/host/tcp4.c new file mode 100644 index 0000000..cef73d6 --- /dev/null +++ b/host/tcp4.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "worker.h" +#include "tcp4.h" + +static EFI_SYSTEM_TABLE *saved_st; + +static EFI_GUID tcp4_guid = EFI_TCP4_PROTOCOL; +static worker_t cleaner; + +typedef struct tcp4 { + EFI_TCP4 prot; + int fd; + worker_t accepter; + EFI_HANDLE handle; + EFI_TCP4_CLOSE_TOKEN *close_token; + worker_t reader; + worker_t writer; + UINT16 port; + EFI_TCP4_CONNECTION_STATE state; + EFI_TCP4_CONFIG_DATA config; + EFI_IP4_MODE_DATA mode; +} tcp4_t; + +static EFIAPI EFI_STATUS +tcp4_get_mode_data(EFI_TCP4 *This, + EFI_TCP4_CONNECTION_STATE *Tcp4State, + EFI_TCP4_CONFIG_DATA *Tcp4ConfigData, + EFI_IP4_MODE_DATA *Ip4ModeData, + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData, + EFI_SIMPLE_NETWORK_MODE *SnpModeData) +{ + tcp4_t *tcp = (tcp4_t *)This; + + if (!This) + return EFI_INVALID_PARAMETER; + + if (MnpConfigData || SnpModeData) + return EFI_UNSUPPORTED; + + if (Tcp4State) + memcpy(Tcp4State, &tcp->state, sizeof(tcp->state)); + + if (Tcp4ConfigData) + memcpy(Tcp4ConfigData, &tcp->config, sizeof(tcp->config)); + + if (Ip4ModeData) + memcpy(Ip4ModeData, &tcp->mode, sizeof(tcp->mode)); + + return EFI_SUCCESS; +} + +static EFI_STATUS do_read(tcp4_t *tcp, EFI_TCP4_IO_TOKEN *token) +{ + EFI_TCP4_RECEIVE_DATA *data; + ssize_t count; + + data = token->Packet.RxData; + count = read(tcp->fd, data->FragmentTable[0].FragmentBuffer, + data->FragmentTable[0].FragmentLength); + if (count == -1) { + ewerr("reader: failed to read, %s", strerror(errno)); + return EFI_DEVICE_ERROR; + } else if (count == 0) + token->CompletionToken.Status = EFI_CONNECTION_FIN; + else + token->CompletionToken.Status = EFI_SUCCESS; + + data->FragmentTable[0].FragmentLength = count; + + return EFI_SUCCESS; +} + +static EFI_STATUS do_write(tcp4_t *tcp, EFI_TCP4_IO_TOKEN *token) +{ + EFI_TCP4_TRANSMIT_DATA *data; + ssize_t count; + + data = token->Packet.TxData; + count = write(tcp->fd, data->FragmentTable[0].FragmentBuffer, + data->FragmentTable[0].FragmentLength); + if (count == -1) { + ewerr("writer: failed to write, %s", strerror(errno)); + return EFI_DEVICE_ERROR; + } + token->CompletionToken.Status = EFI_SUCCESS; + + data->FragmentTable[0].FragmentLength = count; + + return EFI_SUCCESS; +} + +typedef EFI_STATUS (*doer_t)(tcp4_t *, EFI_TCP4_IO_TOKEN *); + +static EFI_STATUS read_write_routine(worker_t worker, doer_t fun) +{ + EFI_STATUS ret; + tcp4_t *tcp; + EFI_TCP4_IO_TOKEN *token; + + ret = worker_get_priv(worker, (void **)&tcp); + if (EFI_ERROR(ret)) + return ret; + + for (;;) { + ret = worker_get_job(worker, (void **)&token); + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + if (EFI_ERROR(ret)) { + ewerr("reader/writer: failed to get the next job"); + return ret; + } + + ret = fun(tcp, token); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(saved_st->BootServices->SignalEvent, 1, + token->CompletionToken.Event); + if (EFI_ERROR(ret)) { + ewerr("reader/writer: failed to signal event"); + return ret; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS read_routine(worker_t worker) +{ + return read_write_routine(worker, do_read); +} + +static EFI_STATUS write_routine(worker_t worker) +{ + return read_write_routine(worker, do_write); +} + +static EFI_STATUS accept_routine(worker_t worker) +{ + EFI_STATUS ret; + struct sockaddr_in addr; + socklen_t len; + int fd; + tcp4_t *tcp, *new_tcp; + EFI_TCP4_LISTEN_TOKEN *token; + + ret = worker_get_priv(worker, (void **)&tcp); + if (EFI_ERROR(ret)) + return ret; + + for (;;) { + ret = worker_get_job(worker, (void **)&token); + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + if (EFI_ERROR(ret)) { + ewerr("accepter: failed to get next job"); + return ret; + } + + len = sizeof(addr); + fd = accept(tcp->fd, (struct sockaddr *)&addr, &len); + if (fd == -1) { + ewerr("accepter: accept failed, %s", strerror(errno)); + return EFI_DEVICE_ERROR; + } + + token->NewChildHandle = NULL; + ret = interface_init(saved_st, &tcp4_guid, &token->NewChildHandle, + tcp, sizeof(*tcp), (void **)&new_tcp); + if (EFI_ERROR(ret)) { + close(fd); + ewerr("accepter: failed to create new connection interface"); + return ret; + } + + new_tcp->fd = fd; + new_tcp->state = Tcp4StateEstablished; + new_tcp->handle = token->NewChildHandle; + + ret = worker_start(read_routine, new_tcp, &new_tcp->reader); + if (EFI_ERROR(ret)) { + ewerr("accepter: failed to start the reader"); + return ret; + } + + ret = worker_start(write_routine, new_tcp, &new_tcp->writer); + if (EFI_ERROR(ret)) { + ewerr("accepter: failed to start the writer"); + return ret; + } + + token->CompletionToken.Status = EFI_SUCCESS; + ret = uefi_call_wrapper(saved_st->BootServices->SignalEvent, 1, + token->CompletionToken.Event); + if (EFI_ERROR(ret)) { + ewerr("accepter: failed to signal the event"); + return ret; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS +clean_tcp(tcp4_t *tcp) +{ + EFI_STATUS ret; + + if (tcp->fd != -1) { + close(tcp->fd); + tcp->fd = -1; + } + + switch (tcp->state) { + case Tcp4StateListen: + ret = worker_stop(tcp->accepter); + if (EFI_ERROR(ret)) + return ret; + + return worker_free(tcp->accepter); + + case Tcp4StateEstablished: + ret = interface_free(saved_st, &tcp4_guid, tcp->handle); + if (EFI_ERROR(ret)) + ewerr("Failed to uninstall the connection interface"); + return ret; + + default: + return EFI_INVALID_PARAMETER; + } +} + +static EFIAPI EFI_STATUS +tcp4_configure(EFI_TCP4 *This, + EFI_TCP4_CONFIG_DATA *TcpConfigData) +{ + EFI_STATUS ret = EFI_DEVICE_ERROR; + tcp4_t *tcp = (tcp4_t *)This; + EFI_IPv4_ADDRESS any = { { 0, 0, 0, 0 } }; + struct sockaddr_in addr; + int pret, reuseval; + + if (!This) + return EFI_INVALID_PARAMETER; + + if (!TcpConfigData) + return clean_tcp(tcp); + + if (!TcpConfigData->AccessPoint.UseDefaultAddress || + memcmp(&any, &TcpConfigData->AccessPoint.StationAddress, sizeof(any)) || + memcmp(&any, &TcpConfigData->AccessPoint.RemoteAddress, sizeof(any)) || + TcpConfigData->AccessPoint.RemotePort != 0) + return EFI_UNSUPPORTED; + + tcp->port = TcpConfigData->AccessPoint.StationPort; + tcp->fd = socket(AF_INET, SOCK_STREAM, 0); + if (tcp->fd == -1) { + ewerr("Failed to open a TCP socket, %s", strerror(errno)); + return EFI_DEVICE_ERROR; + } + + reuseval = 1; + pret = setsockopt(tcp->fd, SOL_SOCKET, SO_REUSEADDR, &reuseval, + sizeof(reuseval)); + if (pret == -1) { + ewerr("Failed to set REUSEADDR flag, %s", strerror(errno)); + goto err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(tcp->port); + addr.sin_addr.s_addr = INADDR_ANY; + + pret = bind(tcp->fd, (struct sockaddr *)&addr, sizeof(addr)); + if (pret == -1) { + ewerr("Failed to bind the TCP socket, %s", strerror(errno)); + goto err; + } + + pret = listen(tcp->fd, 5); + if (pret == -1) { + ewerr("Failed to listen on the TCP socket, %s", + strerror(errno)); + goto err; + } + + tcp->state = Tcp4StateListen; + memcpy(&tcp->config, TcpConfigData, sizeof(tcp->config)); + + tcp->mode.ConfigData.StationAddress.Addr[0] = 127; + tcp->mode.ConfigData.StationAddress.Addr[1] = 0; + tcp->mode.ConfigData.StationAddress.Addr[2] = 0; + tcp->mode.ConfigData.StationAddress.Addr[3] = 1; + tcp->mode.IsConfigured = TRUE; + + ret = worker_start(accept_routine, tcp, &tcp->accepter); + if (EFI_ERROR(ret)) { + ewerr("Failed to start the accepter"); + goto err; + } + + return EFI_SUCCESS; + +err: + close(tcp->fd); + tcp->fd = -1; + return ret; +} + +static EFIAPI EFI_STATUS +tcp4_routes(__attribute__((__unused__)) EFI_TCP4 *This, + __attribute__((__unused__)) BOOLEAN DeleteRoute, + __attribute__((__unused__)) EFI_IPv4_ADDRESS *SubnetAddress, + __attribute__((__unused__)) EFI_IPv4_ADDRESS *SubnetMask, + __attribute__((__unused__)) EFI_IPv4_ADDRESS *GatewayAddress) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +tcp4_connect(__attribute__((__unused__)) EFI_TCP4 *This, + __attribute__((__unused__)) EFI_TCP4_CONNECTION_TOKEN *ConnectionToken) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +tcp4_accept(EFI_TCP4 *This, + EFI_TCP4_LISTEN_TOKEN *ListenToken) +{ + tcp4_t *tcp = (tcp4_t *)This; + + if (!This || !ListenToken) + return EFI_INVALID_PARAMETER; + + if (tcp->state != Tcp4StateListen || !tcp->accepter) + return EFI_NOT_STARTED; + + return worker_put_job(tcp->accepter, ListenToken); +} + +static EFIAPI EFI_STATUS +tcp4_transmit(EFI_TCP4 *This, + EFI_TCP4_IO_TOKEN *Token) +{ + tcp4_t *tcp = (tcp4_t *)This; + + if (!This || !Token) + return EFI_INVALID_PARAMETER; + + if (tcp->state != Tcp4StateEstablished || !tcp->writer) + return EFI_NOT_STARTED; + + return worker_put_job(tcp->writer, Token); +} + +static EFIAPI EFI_STATUS +tcp4_receive(EFI_TCP4 *This, + EFI_TCP4_IO_TOKEN *Token) +{ + tcp4_t *tcp = (tcp4_t *)This; + + if (!This || !Token) + return EFI_INVALID_PARAMETER; + + if (tcp->state != Tcp4StateEstablished || !tcp->reader) + return EFI_NOT_STARTED; + + return worker_put_job(tcp->reader, Token); +} + +static EFIAPI EFI_STATUS +tcp4_close(EFI_TCP4 *This, + EFI_TCP4_CLOSE_TOKEN *CloseToken) +{ + EFI_STATUS ret; + tcp4_t *tcp = (tcp4_t *)This; + + if (!This || !CloseToken) + return EFI_INVALID_PARAMETER; + + if (tcp->state != Tcp4StateEstablished || !tcp->reader || !tcp->writer) + return EFI_NOT_STARTED; + + close(tcp->fd); + + ret = worker_stop(tcp->reader); + if (EFI_ERROR(ret)) { + ewerr("Failed to stop the reader"); + return ret; + } + + ret = worker_stop(tcp->writer); + if (EFI_ERROR(ret)) { + ewerr("Failed to stop the writer"); + return ret; + } + + tcp->close_token = CloseToken; + ret = worker_put_job(cleaner, tcp); + if (EFI_ERROR(ret)) { + ewerr("Failed to push the TCP connection to the cleaner"); + return ret; + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +tcp4_cancel(__attribute__((__unused__)) EFI_TCP4 *This, + __attribute__((__unused__)) EFI_TCP4_COMPLETION_TOKEN *Token) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +tcp4_poll(__attribute__((__unused__)) EFI_TCP4 *This) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +create_child(__attribute__((__unused__)) EFI_SERVICE_BINDING *This, + EFI_HANDLE *ChildHandle) +{ + static tcp4_t tcp4_default = { + .prot = { + .GetModeData = tcp4_get_mode_data, + .Configure = tcp4_configure, + .Routes = tcp4_routes, + .Connect = tcp4_connect, + .Accept = tcp4_accept, + .Transmit = tcp4_transmit, + .Receive = tcp4_receive, + .Close = tcp4_close, + .Cancel = tcp4_cancel, + .Poll = tcp4_poll + }, + .fd = -1, + .state = Tcp4StateClosed, + }; + EFI_TCP4 *tcp4; + + if (!ChildHandle) + return EFI_INVALID_PARAMETER; + + *ChildHandle = NULL; + return interface_init(saved_st, &tcp4_guid, ChildHandle, + &tcp4_default, sizeof(tcp4_default), + (void **)&tcp4); +} + +static EFIAPI EFI_STATUS +destroy_child(__attribute__((__unused__)) EFI_SERVICE_BINDING *This, + EFI_HANDLE ChildHandle) +{ + return interface_free(saved_st, &tcp4_guid, ChildHandle); +} + +static EFI_GUID srv_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL; +static EFI_HANDLE srv_handle; + +static EFI_STATUS cleaner_routine(worker_t worker) +{ + EFI_STATUS ret; + tcp4_t *conn; + + for (;;) { + ret = worker_get_job(worker, (void **)&conn); + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + if (EFI_ERROR(ret)) { + ewerr("cleaner: failed to get the next job"); + return ret; + } + + ret = worker_free(conn->reader); + if (EFI_ERROR(ret)) + ewerr("cleaner: failed free to free the reader"); + + ret = worker_free(conn->writer); + if (EFI_ERROR(ret)) + ewerr("cleaner: failed free to free the writer"); + + ret = uefi_call_wrapper(saved_st->BootServices->SignalEvent, 1, + conn->close_token->CompletionToken.Event); + if (EFI_ERROR(ret)) { + ewerr("cleaner: failed to signal the closed connection"); + return ret; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS tcp4_init(EFI_SYSTEM_TABLE *st) +{ + static EFI_SERVICE_BINDING srv_default = { + .CreateChild = create_child, + .DestroyChild = destroy_child + }; + EFI_SERVICE_BINDING *srv; + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + saved_st = st; + + ret = interface_init(st, &srv_guid, &srv_handle, + &srv_default, sizeof(srv_default), + (void **)&srv); + if (EFI_ERROR(ret)) + return ret; + + ret = worker_start(cleaner_routine, NULL, &cleaner); + if (EFI_ERROR(ret)) { + ewerr("Failed to start the cleaner"); + srv_handle = saved_st = NULL; + interface_free(st, &srv_guid, srv_handle); + } + + return ret; +} + +static EFI_STATUS tcp4_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + if (!st) + return EFI_INVALID_PARAMETER; + + ret = worker_stop(cleaner); + if (EFI_ERROR(ret)) { + ewerr("Failed to stop the cleaner worker"); + return ret; + } + + ret = worker_free(cleaner); + if (EFI_ERROR(ret)) { + ewerr("Failed to free the cleaner worker"); + return ret; + } + + ret = interface_free(st, &srv_guid, srv_handle); + srv_handle = saved_st = NULL; + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; +} + +ewdrv_t tcp4_drv = { + .name = "tcp4", + .description = "TCP/IP protocol", + .init = tcp4_init, + .exit = tcp4_exit +}; diff --git a/host/tcp4.h b/host/tcp4.h new file mode 100644 index 0000000..b5ff091 --- /dev/null +++ b/host/tcp4.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _TCP4_H_ +#define _TCP4_H_ + +#include + +extern ewdrv_t tcp4_drv; + +#endif /* _TCP4_H_ */ diff --git a/host/terminal_curses.c b/host/terminal_curses.c new file mode 100644 index 0000000..c54fdd8 --- /dev/null +++ b/host/terminal_curses.c @@ -0,0 +1,71 @@ +/* + * Author: Léo Sartre + */ + +#include +#include + +#include +#include +#include +#include + +#include "terminal_curses.h" +#include "terminal_curses_conin.h" +#include "terminal_curses_conout.h" + +static SCREEN *screen; + +EFI_STATUS terminal_curses_init(EFI_SYSTEM_TABLE *st) +{ + int ret; + + screen = newterm(NULL, stdout, stdin); + if (!screen) { + ewerr("Failed to initialize ncurses"); + return EFI_LOAD_ERROR; + } + + ret = nodelay(stdscr, TRUE); + if (ret == ERR) { + endwin(); + ewerr("Failed to set terminal in non blocking mode"); + return EFI_LOAD_ERROR; + } + + ret = set_escdelay(0); + if (ret == ERR) + ewdbg("Warning ESC key will not be usable as signgle key"); + + ret = keypad(stdscr, TRUE); + if (ret == ERR) + ewdbg("Warning keypad keys will not be available"); + + ret = noecho(); + if (ret == ERR) + ewdbg("Warning failed to set noecho mode"); + + ret = start_color(); + if (ret == ERR) + ewdbg("Warning colors will not be available"); + + return terminal_conin_init(st) & terminal_conout_init(st); +} + +EFI_STATUS terminal_curses_exit(EFI_SYSTEM_TABLE *st) +{ + if (!st) + return EFI_INVALID_PARAMETER; + + endwin(); + delscreen(screen); + + return terminal_conin_exit(st) & terminal_conout_exit(st); +} + +ewdrv_t terminal_curses_drv = { + .name = "terminal_curses", + .description = "Terminal input output support based on ncurses", + .init = terminal_curses_init, + .exit = terminal_curses_exit +}; diff --git a/host/terminal_curses.conout.h b/host/terminal_curses.conout.h new file mode 100644 index 0000000..cb4c70d --- /dev/null +++ b/host/terminal_curses.conout.h @@ -0,0 +1,13 @@ +/* + * Author: Léo Sartre + */ + +#ifndef _TERMINAL_CURSES_CONOUT_H_ +#define _TERMINAL_CURSES_CONOUT_H_ + +#include + +EFI_STATUS terminal_conout_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS terminal_conout_exit(EFI_SYSTEM_TABLE *st); + +#endif /* _TERMINAL_CURSES_CONOUT_H_ */ diff --git a/host/terminal_curses.h b/host/terminal_curses.h new file mode 100644 index 0000000..9d7e88a --- /dev/null +++ b/host/terminal_curses.h @@ -0,0 +1,14 @@ +/* + * Author: Léo Sartre + */ + +#ifndef _TERMINAL_CURSES_H_ +#define _TERMINAL_CURSES_H_ + +#include +#include +#include + +extern ewdrv_t terminal_curses_drv; + +#endif /* _TERMINAL_CONOUT_H_ */ diff --git a/host/terminal_curses_conin.c b/host/terminal_curses_conin.c new file mode 100644 index 0000000..859d1b1 --- /dev/null +++ b/host/terminal_curses_conin.c @@ -0,0 +1,101 @@ +/* + * Author: Léo Sartre + */ + +#include + +#include "terminal_curses_conin.h" +#include "curses_utils.h" + +static EFIAPI VOID +wait_for_input_key(__attribute__((__unused__)) EFI_EVENT Event, + VOID *Context) +{ + int ret; + EFI_SYSTEM_TABLE *st = (EFI_SYSTEM_TABLE *)Context; + + ret = nodelay(stdscr, FALSE); + if (ret == ERR) + ewerr("Failed to configure blocking input mode, don't trust the trigger"); + + ret = getch(); + if (ret == ERR) + ewerr("Suspicious key read"); + /* Send the signal anyway to unblock the caller */ + uefi_call_wrapper(st->BootServices->SignalEvent, 1, + st->ConIn->WaitForKey); +} + +static EFIAPI EFI_STATUS +reset_input(__attribute__((__unused__)) struct _SIMPLE_INPUT_INTERFACE *This, + __attribute__((__unused__)) BOOLEAN ExtendedVerification) +{ + flushinp(); + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +read_key(__attribute__((__unused__)) struct _SIMPLE_INPUT_INTERFACE *This, + __attribute__((__unused__)) EFI_INPUT_KEY *Key) +{ + int k = getch(); + if (k == ERR) + return EFI_NOT_READY; + + Key->ScanCode = curses_key_to_efi_scancode(k); + if (Key->ScanCode == SCAN_NULL) + Key->UnicodeChar = k & 0xff; + else + Key->UnicodeChar = CHAR_NULL; + + return EFI_SUCCESS; +} + +static SIMPLE_INPUT_INTERFACE saved_conin; + +EFI_STATUS terminal_conin_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + static SIMPLE_INPUT_INTERFACE new_conin = { + .Reset = reset_input, + .ReadKeyStroke = read_key + }; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!st->ConsoleInHandle) + return EFI_NOT_FOUND; + + memcpy(&saved_conin, st->ConIn, sizeof(*st->ConIn)); + memcpy(st->ConIn, &new_conin, sizeof(new_conin)); + + ret = uefi_call_wrapper(st->BootServices->CreateEvent, 5, + EVT_NOTIFY_WAIT, + TPL_APPLICATION, + wait_for_input_key, + (VOID *)st, + &st->ConIn->WaitForKey); + if (EFI_ERROR(ret)) { + ewerr("Fail to create WaitForKey event"); + return ret; + } + + return EFI_SUCCESS; +} + +EFI_STATUS terminal_conin_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(st->BootServices->CloseEvent, 1, + st->ConIn->WaitForKey); + if (EFI_ERROR(ret)) { + ewerr("Fail to destroy WaitForKey event"); + return ret; + } + + memcpy(st->ConIn, &saved_conin, sizeof(saved_conin)); + + return EFI_SUCCESS; +} diff --git a/host/terminal_curses_conin.h b/host/terminal_curses_conin.h new file mode 100644 index 0000000..04e9193 --- /dev/null +++ b/host/terminal_curses_conin.h @@ -0,0 +1,13 @@ +/* + * Author: Léo Sartre + */ + +#ifndef _TERMINAL_CURSES_CONIN_H_ +#define _TERMINAL_CURSES_CONIN_H_ + +#include + +EFI_STATUS terminal_conin_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS terminal_conin_exit(EFI_SYSTEM_TABLE *st); + +#endif /* _TERMINAL_CURSES_CONIN_H_ */ diff --git a/host/terminal_curses_conout.c b/host/terminal_curses_conout.c new file mode 100644 index 0000000..e634ff6 --- /dev/null +++ b/host/terminal_curses_conout.c @@ -0,0 +1,221 @@ +/* + * Author: Léo Sartre + */ + +#include +#include /* For ARRAY_SIZE */ + +#include + +#include "terminal_curses_conout.h" +#include "curses_utils.h" + +static const SIMPLE_TEXT_OUTPUT_MODE modes[] = { + {2, 0, EFI_TEXT_ATTR(EFI_WHITE, EFI_BLACK), 80, 25, TRUE}, /* 80x25 white on black text mode */ + {2, 1, EFI_TEXT_ATTR(EFI_WHITE, EFI_BLACK), 80, 50, TRUE} /* 80x50 white on black text mode */ +}; +static SIMPLE_TEXT_OUTPUT_MODE current_mode; + +typedef union { + struct { UINTN fg: 4; UINTN bg: 3; }; + UINTN raw; +} color; + +static int current_color_pair; + +static EFIAPI EFI_STATUS +conout_output_string(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + CHAR16 *String) +{ + while (*String) { + echochar(*String++); + } + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_test_string(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) CHAR16 *String) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_set_attribute(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + UINTN Attribute) +{ + int ret; + bool support_colors; + color c; + + support_colors = has_colors(); + if (!support_colors) { + ewdbg("Device does not support colors"); + return EFI_DEVICE_ERROR; + } + + c.raw = Attribute; + if (efi_color_to_ncurses_color(c.fg) < 0 || efi_color_to_ncurses_color(c.bg) < 0) { + ewdbg("Failed to translate efi colors to ncurses colors"); + return EFI_DEVICE_ERROR; + } + + ret = attroff(COLOR_PAIR(current_color_pair++)); + if (ret == ERR) { + ewdbg("Failed to disable previous window colors attribute"); + return EFI_DEVICE_ERROR; + } + + ret = init_pair(current_color_pair, efi_color_to_ncurses_color(c.fg), efi_color_to_ncurses_color(c.bg)); + if (ret == ERR) { + ewdbg("Failed to initialize ncurses colors pair"); + return EFI_DEVICE_ERROR; + } + + ret = attron(COLOR_PAIR(current_color_pair)); + if (ret == ERR) { + ewdbg("Failed to set new window colors attribute"); + return EFI_DEVICE_ERROR; + } + + current_mode.Attribute = Attribute; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_set_cursor_position(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + UINTN Column, + UINTN Row) +{ + int ret; + + ret = move(Row, Column); + if (ret == ERR) { + ewdbg("Failed to move cursor to (%ld, %ld)", Row, Column); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_clear_screen(struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This) +{ + int ret; + + ret = clear(); + if (ret == ERR) { + ewdbg("Failed to clear the screen"); + return EFI_DEVICE_ERROR; + } + + return conout_set_cursor_position(This, 0, 0); +} + +static EFIAPI EFI_STATUS +conout_reset(struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) BOOLEAN ExtendedVerification) +{ + int ret; + + ret = resize_term(current_mode.CursorRow, current_mode.CursorColumn); + if (ret == ERR) { + ewdbg("Failed to resize terminal to %dx%d", current_mode.CursorRow, current_mode.CursorColumn); + return EFI_DEVICE_ERROR; + } + + return conout_clear_screen(This); +} + +static EFIAPI EFI_STATUS +conout_query_mode(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + UINTN ModeNumber, + UINTN *Columns, + UINTN *Rows) +{ + if (ModeNumber >= ARRAY_SIZE(modes)) + return EFI_UNSUPPORTED; + + *Rows = modes[ModeNumber].CursorRow; + *Columns = modes[ModeNumber].CursorColumn; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_set_mode(struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + UINTN ModeNumber) +{ + if (ModeNumber >= ARRAY_SIZE(modes)) + return EFI_UNSUPPORTED; + + memcpy(¤t_mode, &modes[ModeNumber], sizeof(modes[ModeNumber])); + + return conout_reset(This, FALSE); +} + +static EFIAPI EFI_STATUS +conout_enable_cursor(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + BOOLEAN Enable) +{ + int ret; + int visibility = Enable ? 1 : 0; + + ret = curs_set(visibility); + if (ret == ERR) + return EFI_DEVICE_ERROR; + + return EFI_SUCCESS; +} + +static SIMPLE_TEXT_OUTPUT_INTERFACE saved_conout; + +EFI_STATUS terminal_conout_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + static SIMPLE_TEXT_OUTPUT_INTERFACE new_conout = { + .Reset = conout_reset, + .OutputString = conout_output_string, + .TestString = conout_test_string, + .QueryMode = conout_query_mode, + .SetMode = conout_set_mode, + .SetAttribute = conout_set_attribute, + .ClearScreen = conout_clear_screen, + .SetCursorPosition = conout_set_cursor_position, + .EnableCursor = conout_enable_cursor, + .Mode = ¤t_mode + }; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (!st->ConsoleOutHandle) + return EFI_NOT_FOUND; + + /* Set the default 80x25 mode */ + ret = conout_set_mode(&new_conout, 0); + if (EFI_ERROR(ret)) { + ewerr("Failed to set default text mode (80x25)"); + return ret; + } + + ret = conout_enable_cursor(&new_conout, current_mode.CursorVisible); + if (EFI_ERROR(ret)) { + ewerr("Failed to %s cursor", current_mode.CursorVisible ? "show" : "hide"); + return ret; + } + + memcpy(&saved_conout, st->ConOut, sizeof(*st->ConOut)); + memcpy(st->ConOut, &new_conout, sizeof(new_conout)); + + return EFI_SUCCESS; +} + +EFI_STATUS terminal_conout_exit(EFI_SYSTEM_TABLE *st) +{ + memcpy(st->ConOut, &saved_conout, sizeof(saved_conout)); + + return EFI_SUCCESS; +} diff --git a/host/terminal_curses_conout.h b/host/terminal_curses_conout.h new file mode 100644 index 0000000..cb4c70d --- /dev/null +++ b/host/terminal_curses_conout.h @@ -0,0 +1,13 @@ +/* + * Author: Léo Sartre + */ + +#ifndef _TERMINAL_CURSES_CONOUT_H_ +#define _TERMINAL_CURSES_CONOUT_H_ + +#include + +EFI_STATUS terminal_conout_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS terminal_conout_exit(EFI_SYSTEM_TABLE *st); + +#endif /* _TERMINAL_CURSES_CONOUT_H_ */ diff --git a/host/worker.c b/host/worker.c new file mode 100644 index 0000000..f47136d --- /dev/null +++ b/host/worker.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "fifo.h" +#include "worker.h" + +struct worker { + worker_routine routine; + EFI_STATUS routine_ret; + void *priv; + bool running; + pthread_t thread; + fifo_t jobs; + pthread_cond_t cond; + pthread_mutex_t lock; +}; + +static inline EFI_STATUS lock(worker_t worker) +{ + int ret; + + ret = pthread_mutex_lock(&worker->lock); + if (ret) { + ewerr("Failed to lock worker"); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static inline EFI_STATUS unlock(worker_t worker) +{ + int ret; + + ret = pthread_mutex_unlock(&worker->lock); + if (ret) { + ewerr("Failed to unlock worker"); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static inline EFI_STATUS set_running(worker_t worker, bool running) +{ + EFI_STATUS ret; + + ret = lock(worker); + if (EFI_ERROR(ret)) + return ret; + + worker->running = running; + + return unlock(worker); +} + +static inline EFI_STATUS get_running(worker_t worker, bool *running) +{ + EFI_STATUS ret; + + ret = lock(worker); + if (EFI_ERROR(ret)) + return ret; + + *running = worker->running; + + return unlock(worker); +} + +static void *worker_run(void *arg) +{ + worker_t worker; + + worker = (worker_t)arg; + worker->routine_ret = worker->routine(worker); + set_running(worker, false); + + return &worker->routine_ret; +} + +EFI_STATUS worker_start(worker_routine routine, void *priv, + worker_t *worker_p) +{ + int ret; + worker_t worker; + + if (!routine || !worker_p) + return EFI_INVALID_PARAMETER; + + worker = malloc(sizeof(*worker)); + if (!worker) + return EFI_OUT_OF_RESOURCES; + + worker->jobs = fifo_new(); + if (!worker->jobs) { + free(worker); + return EFI_OUT_OF_RESOURCES; + } + + worker->routine = routine; + worker->priv = priv; + + ret = pthread_cond_init(&worker->cond, NULL); + if (ret) + goto err; + + ret = pthread_mutex_init(&worker->lock, NULL); + if (ret) + goto err; + + worker->running = true; + ret = pthread_create(&worker->thread, NULL, worker_run, worker); + if (ret) + goto err; + + *worker_p = worker; + return EFI_SUCCESS; + +err: + fifo_free(worker->jobs); + free(worker); + return EFI_DEVICE_ERROR; +} + +EFI_STATUS worker_get_priv(worker_t worker, void **priv_p) +{ + if (!worker) + return EFI_INVALID_PARAMETER; + + *priv_p = worker->priv; + return EFI_SUCCESS; +} + +static EFI_STATUS worker_sleep(worker_t worker) +{ + EFI_STATUS ret; + int pret; + + ret = lock(worker); + if (EFI_ERROR(ret)) + return ret; + + pret = pthread_cond_wait(&worker->cond, &worker->lock); + if (pret) + return EFI_DEVICE_ERROR; + + return unlock(worker); +} + +EFI_STATUS worker_get_job(worker_t worker, void **job_p) +{ + EFI_STATUS ret; + bool running; + + if (!worker || !job_p) + return EFI_INVALID_PARAMETER; + + ret = get_running(worker, &running); + if (EFI_ERROR(ret)) + return ret; + if (!running) + return EFI_NOT_FOUND; + + ret = fifo_get(worker->jobs, job_p); + if (!EFI_ERROR(ret)) + return EFI_SUCCESS; + if (ret != EFI_NOT_FOUND) + return ret; + + ret = worker_sleep(worker); + if (EFI_ERROR(ret)) + return ret; + + ret = get_running(worker, &running); + if (EFI_ERROR(ret)) + return ret; + if (!running) + return EFI_NOT_FOUND; + + return fifo_get(worker->jobs, job_p); +} + +static EFI_STATUS worker_wakeup(worker_t worker) +{ + EFI_STATUS ret; + int pret; + + ret = lock(worker); + if (EFI_ERROR(ret)) + return ret; + + pret = pthread_cond_signal(&worker->cond); + if (pret) + return EFI_DEVICE_ERROR; + + return unlock(worker); +} + +EFI_STATUS worker_put_job(worker_t worker, void *job) +{ + EFI_STATUS ret; + bool running; + + if (!worker || !job) + return EFI_INVALID_PARAMETER; + + ret = get_running(worker, &running); + if (EFI_ERROR(ret)) + return ret; + if (!running) + return EFI_NOT_FOUND; + + ret = fifo_put(worker->jobs, job); + if (EFI_ERROR(ret)) + return ret; + + return worker_wakeup(worker); +} + +EFI_STATUS worker_stop(worker_t worker) +{ + EFI_STATUS ret; + + if (!worker) + return EFI_INVALID_PARAMETER; + + ret = set_running(worker, false); + if (EFI_ERROR(ret)) { + ewerr("Failed to stop the worker"); + return ret; + } + + ret = worker_wakeup(worker); + if (EFI_ERROR(ret)) + ewerr("Failed to wakeup the worker"); + + return ret; +} + +EFI_STATUS worker_free(worker_t worker) +{ + EFI_STATUS ret; + void *retval; + + ret = pthread_join(worker->thread, &retval); + if (ret) { + ewerr("Failed to wait the thread termination, %s", + strerror(ret)); + return EFI_DEVICE_ERROR; + } + + fifo_free(worker->jobs); + free(worker); + return EFI_SUCCESS; +} diff --git a/host/worker.h b/host/worker.h new file mode 100644 index 0000000..7535368 --- /dev/null +++ b/host/worker.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _WORKER_H_ +#define _WORKER_H_ + +typedef struct worker *worker_t; +typedef EFI_STATUS (*worker_routine)(worker_t worker); + +EFI_STATUS worker_start(worker_routine routine, void *arg, + worker_t *worker_p); +EFI_STATUS worker_get_priv(worker_t worker, void **priv_p); +EFI_STATUS worker_get_job(worker_t worker, void **job_p); +EFI_STATUS worker_put_job(worker_t worker, void *job); +EFI_STATUS worker_stop(worker_t worker); +EFI_STATUS worker_free(worker_t worker); + +#endif /* _WORKER_H_ */ diff --git a/include/hardware/hw_broxton.h b/include/hardware/hw_broxton.h new file mode 100644 index 0000000..120a348 --- /dev/null +++ b/include/hardware/hw_broxton.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef __HW_BROXTON__ +#define __HW_BROXTON__ + +/* PCI device id of OTG */ +#define XDCI_PID 0x5aaa +#define XHCI_PID 0x5aa8 + +/* PCI device id of EMMC controller */ +#define EMMC_DEVICEID 0x5acc +/* UFS is not enabled on BXT, hence set it as default value*/ +#define UFS_PCI_DID 0x0000 +/* NVME is not enabled on BXT, hence set it as default value*/ +#define NVME_PCI_DID 0x0000 +#define NVME_DISKBUS 0x0000 + +#ifdef EFIWRAPPER_USE_EC_UART +#define SERIAL_BASEADDR 0x3f8 +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_IO_MAPPED +#define HW_SERIAL_REG_WIDTH 1 +#else +/* serial port base address */ +#define SERIAL_PCI_DID 0x5ac0 +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_MEMORY_MAPPED +#define HW_SERIAL_REG_WIDTH 4 +#endif + +#define SERIAL_IOC_PCI_DID 0x5abe + +/* TCO base address for Gordon peak */ +#define TCOBASE (0x00000460) + +/* APL HD Graphics 505 */ +#define VGA_PID 0x5a84 +#define VGA_PID2 0x5a85 + +#endif /* __HW_BROXTON__ */ + diff --git a/include/hardware/hw_icelake.h b/include/hardware/hw_icelake.h new file mode 100644 index 0000000..6eb24cc --- /dev/null +++ b/include/hardware/hw_icelake.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef __HW_ICELAKE__ +#define __HW_ICELAKE__ + +/* PCI device id of OTG */ +#define XDCI_PID 0x34EE +#define XHCI_PID 0x34ED + +#define FB_SET_USB_DEVICE_MODE +#define P2SB_BASE_ADDR 0xFD000000 +#define USB_DAP_COMM_CTRL_REG_OFFSET 0x700440 +#define USB_DAP_USB2_CTRL0_REG_OFFSET 0x700550 +/*MRB type-c:0x700550; type-a:0x700510; RVP type-a:0x700580*/ + + +/* PCI device id of EMMC controller */ +#define EMMC_DEVICEID 0x34C4 + +/* UFS */ +#define UFS_PCI_DID 0x34FA + +/* NVME: not enable on ICL and as temporary variable*/ +#define NVME_PCI_DID 0xFFFF +#define NVME_DISKBUS 0xFFFF + +/* serial port base address */ +#ifndef EFIWRAPPER_USE_EC_UART +#define SERIAL_PCI_DID 0x34c7 +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_MEMORY_MAPPED +#define HW_SERIAL_REG_WIDTH 4 + +#else /* EFIWRAPPER_USE_EC_UART */ + +#define SERIAL_BASEADDR 0x3f8 +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_IO_MAPPED +#define HW_SERIAL_REG_WIDTH 1 + +#endif /* EFIWRAPPER_USE_EC_UART */ + +#define SERIAL_IOC_PCI_DID 0x34a9 + +/* TCO base address, to be determined */ +#define TCOBASE (0xffffffff) + +#endif /* __HW_ICELAKE__ */ + diff --git a/include/hardware/hw_kabylake.h b/include/hardware/hw_kabylake.h new file mode 100644 index 0000000..1c8c09d --- /dev/null +++ b/include/hardware/hw_kabylake.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef __HW_KABYLAKE__ +#define __HW_KABYLAKE__ + +/* PCI device id of OTG */ +#define XDCI_PID 0x9D30 +#define XHCI_PID 0x9D2F + +/* PCI device id of EMMC controller */ +#define EMMC_DEVICEID 0x9D2B + +/* Kabylake doesn't use UFS, hence set it as default value for build successfully */ +#define UFS_PCI_DID 0x00 + +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_MEMORY_MAPPED +#define HW_SERIAL_REG_WIDTH 4 + +/* serial port base address */ +#define SERIAL_PCI_DID 0x9d66 + +/* PCI device id of NVME */ +#define NVME_PCI_DID 0xF1A5 +#define NVME_DISKBUS 0x1D00 + +/* TCO base address, to be determined */ +#define TCOBASE (0xffffffff) + +#endif /* __HW_KABYLAKE__ */ + diff --git a/include/hardware/hw_tigerlake.h b/include/hardware/hw_tigerlake.h new file mode 100644 index 0000000..32d221d --- /dev/null +++ b/include/hardware/hw_tigerlake.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef __HW_TIGERLAKE__ +#define __HW_TIGERLAKE__ + +/* PCI device id of OTG */ +#define XDCI_PID 0x9D30 +#define XHCI_PID 0x9D2F + +/* PCI device id of EMMC controller */ +#define EMMC_DEVICEID 0xA0C4 + +/* UFS */ +#define UFS_PCI_DID 0xA0FA + +/* NVME */ +#define NVME_PCI_DID 0x0953 +#define NVME_DISKBUS 0x1C04 + +/* serial port base address */ +#ifndef EFIWRAPPER_USE_EC_UART +#define SERIAL_PCI_DID 0xA0c7 +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_MEMORY_MAPPED +#define HW_SERIAL_REG_WIDTH 4 + +#else /* EFIWRAPPER_USE_EC_UART */ + +#define SERIAL_BASEADDR 0x3f8 +#define HW_SERIAL_TYPE CB_SERIAL_TYPE_IO_MAPPED +#define HW_SERIAL_REG_WIDTH 1 + +#endif /* EFIWRAPPER_USE_EC_UART */ + +/* TCO base address, to be determined */ +#define TCOBASE (0xffffffff) + +#endif /* __HW_TIGERLAKE__ */ + diff --git a/include/hardware/hwconfig.h b/include/hardware/hwconfig.h new file mode 100644 index 0000000..d027a8d --- /dev/null +++ b/include/hardware/hwconfig.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + + +#ifndef __HARDWARE_CONFIG__ +#define __HARDWARE_CONFIG__ + + +#if defined(PLATFORM_ICELAKE) + + #include "hw_icelake.h" + +#elif defined(PLATFORM_TIGERLAKE) + + #include "hw_tigerlake.h" + +#elif defined(PLATFORM_KABYLAKE) + + #include "hw_kabylake.h" + +#elif defined(PLATFORM_BROXTON) + + #include "hw_broxton.h" + +#else + + /* default: broxton */ + #include "hw_broxton.h" + +#endif + +#endif /* __HARDWARE_CONFIG__ */ + diff --git a/include/libefiwrapper/conf_table.h b/include/libefiwrapper/conf_table.h new file mode 100644 index 0000000..63d23ea --- /dev/null +++ b/include/libefiwrapper/conf_table.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _CONF_TABLE_H_ +#define _CONF_TABLE_H_ + +#include +#include + +EFI_STATUS conf_table_new(EFI_SYSTEM_TABLE *st, + EFI_GUID *guid, + EFI_CONFIGURATION_TABLE **table); +EFI_STATUS conf_table_free(EFI_SYSTEM_TABLE *st, + EFI_GUID *guid); + +#endif /* _CONF_TABLE_H_ */ diff --git a/include/libefiwrapper/efiwrapper.h b/include/libefiwrapper/efiwrapper.h new file mode 100644 index 0000000..afc42c4 --- /dev/null +++ b/include/libefiwrapper/efiwrapper.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EFIWRAPPER_H_ +#define _EFIWRAPPER_H_ + +#include + +EFI_STATUS efiwrapper_init(int argc, char **argv, EFI_SYSTEM_TABLE **st_p, + EFI_HANDLE *img_handle); +EFI_STATUS efiwrapper_free(EFI_HANDLE img_handle); + +#endif /* _EFIWRAPPER_H_ */ diff --git a/include/libefiwrapper/ewacpi.h b/include/libefiwrapper/ewacpi.h new file mode 100644 index 0000000..a9f5108 --- /dev/null +++ b/include/libefiwrapper/ewacpi.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EWACPI_H_ +#define _EWACPI_H_ + +#include +#include + +struct acpi_header { + char signature[4]; /* ASCII Table identifier */ + uint32_t length; /* Length of the table, including the header */ + char revision; /* Revision of the structure */ + char checksum; /* Sum of all fields must be 0 */ + char oem_id[6]; /* ASCII OEM identifier */ + char oem_table_id[8]; /* ASCII OEM table identifier */ + uint32_t oem_revision; /* OEM supplied revision number */ + char creator_id[4]; /* Vendor ID of utility creator of the table */ + uint32_t creator_revision; /* Revision of utility creator of the table */ +} __attribute__((packed)); + +EFI_STATUS ewacpi_get_table(EFI_SYSTEM_TABLE *st, const char *name, + struct acpi_header **table); + +#endif /* _EWACPI_H_ */ diff --git a/include/libefiwrapper/ewarg.h b/include/libefiwrapper/ewarg.h new file mode 100644 index 0000000..fe2fca9 --- /dev/null +++ b/include/libefiwrapper/ewarg.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EWARG_H_ +#define _EWARG_H_ + +#include +#include + +EFI_STATUS ewarg_init(int argc, char **argv); +const char *ewarg_getval(const char *name); +EFI_STATUS ewarg_free(void); + +#endif /* _EWARG_H_ */ diff --git a/include/libefiwrapper/ewdrv.h b/include/libefiwrapper/ewdrv.h new file mode 100644 index 0000000..7ecde25 --- /dev/null +++ b/include/libefiwrapper/ewdrv.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EWDRV_H_ +#define _EWDRV_H_ + +#include +#include + +typedef struct ewdrv { + const char *name; /* Mandatory */ + const char *description; + EFI_STATUS (*init)(EFI_SYSTEM_TABLE *st); /* Mandatory */ + EFI_STATUS (*exit)(EFI_SYSTEM_TABLE *st); + void *priv; +} ewdrv_t; + +/* NULL terminated driver list */ +extern ewdrv_t **ew_drivers; + +EFI_STATUS ewdrv_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS ewdrv_exit(EFI_SYSTEM_TABLE *st); + +#endif /* _EWDRV_H_ */ diff --git a/include/libefiwrapper/ewlib.h b/include/libefiwrapper/ewlib.h new file mode 100644 index 0000000..1492fbc --- /dev/null +++ b/include/libefiwrapper/ewlib.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EWLIB_H_ +#define _EWLIB_H_ + +#include +#include + +#include "external.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x)) +#endif + +#ifndef min +#define min(a, b) (a < b ? a : b) +#endif + +#ifndef max +#define max(a, b) (a > b ? a : b) +#endif + +int guidcmp(EFI_GUID *g1, EFI_GUID *g2); +size_t str16len(const CHAR16 *str); +int str16cmp(const CHAR16 *s1, const CHAR16 *s2); + +#endif /* _EWLIB_H_ */ diff --git a/include/libefiwrapper/ewlog.h b/include/libefiwrapper/ewlog.h new file mode 100644 index 0000000..1f3a32a --- /dev/null +++ b/include/libefiwrapper/ewlog.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EWLOG_H_ +#define _EWLOG_H_ + +#include + +#ifdef DISABLE_DEBUG_PRINT +#define DEBUG_MESSAGES 0 +#else +#ifdef USER +#define DEBUG_MESSAGES 0 +#else +#define DEBUG_MESSAGES 1 +#endif +#endif + +#define EWLOG_PREFIX "efiwrapper: " + +#if DEBUG_MESSAGES +#define ewdbg(fmt, ...) do { \ + printf(EWLOG_PREFIX fmt "\n", ##__VA_ARGS__); \ +} while(0) +#else +#define ewdbg(fmt, ...) (void)0 +#endif + +#define ewerr(fmt, ...) do { \ + printf(EWLOG_PREFIX fmt "\n", ##__VA_ARGS__); \ +} while (0) + +#endif /* _EWLOG_H_ */ diff --git a/include/libefiwrapper/ewvar.h b/include/libefiwrapper/ewvar.h new file mode 100644 index 0000000..8765781 --- /dev/null +++ b/include/libefiwrapper/ewvar.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EWVAR_H_ +#define _EWVAR_H_ + +#include +#include + +typedef struct ewvar { + struct ewvar *next; + CHAR16 *name; + EFI_GUID guid; + UINT32 attributes; + void *data; + UINTN size; +} ewvar_t; + +ewvar_t *ewvar_new(CHAR16 *name, EFI_GUID *guid, UINT32 attr, + UINTN size, VOID *data); +void ewvar_add(ewvar_t *var); + +ewvar_t *ewvar_get(const CHAR16 *name, EFI_GUID *guid, ewvar_t **prev_p); +ewvar_t *ewvar_get_first(void); +EFI_STATUS ewvar_update(ewvar_t *var, UINTN size, VOID *data); + +EFI_STATUS ewvar_del(ewvar_t *var, ewvar_t *prev); + +void ewvar_free(ewvar_t *var); +void ewvar_free_all(void); + +typedef struct ewvar_storage { + EFI_STATUS (*load)(void); + EFI_STATUS (*save)(ewvar_t *); + EFI_STATUS (*delete)(ewvar_t *); +} ewvar_storage_t; + +EFI_STATUS ewvar_register_storage(ewvar_storage_t *s); +EFI_STATUS ewvar_unregister_storage(void); + +#endif /* _EWVAR_H_ */ diff --git a/include/libefiwrapper/external.h b/include/libefiwrapper/external.h new file mode 100644 index 0000000..12a7958 --- /dev/null +++ b/include/libefiwrapper/external.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _EXTERNAL_H_ +#define _EXTERNAL_H_ + +#include +#include + +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif + +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS 0 +#endif + +#ifndef __LP64__ +typedef unsigned int size_t; +#else +typedef unsigned long size_t; +#endif + +/* The following functions support must be supplied by the platform + dependant library. */ + +/* stdlib.h */ +void *malloc(size_t size); +void free(void *ptr); +void *calloc(size_t nmemb, size_t size); +void *realloc(void *ptr, size_t size); +long int strtol(const char *s, char **nptr, int base); +unsigned long long int strtoull(const char *ptr, char **endptr, int base); +long atol(const char *nptr); + +/* string.h */ +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *strdup(const char *s); +size_t strlen(const char *s); +char *strncat(char *dest, const char *src, size_t n); +int strncmp(const char *s1, const char *s2, size_t n); + +/* stdio.h */ +int printf(const char *format, ...); +int snprintf(char *str, size_t size, const char *fmt, ...); + +/* libpayload.h */ +void ndelay(unsigned int n); + +/* EFI binary entry point */ +EFI_STATUS efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE *); + +#endif /* _EXTERNAL_H_ */ diff --git a/include/libefiwrapper/interface.h b/include/libefiwrapper/interface.h new file mode 100644 index 0000000..8da1113 --- /dev/null +++ b/include/libefiwrapper/interface.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _INTERNAL_H_ +#define _INTERNAL_H_ + +#include +#include +#include + +EFI_STATUS interface_init(EFI_SYSTEM_TABLE *st, EFI_GUID *guid, + EFI_HANDLE *handle, + void *base, size_t base_size, + void **interface); +EFI_STATUS interface_free(EFI_SYSTEM_TABLE *st, EFI_GUID *guid, + EFI_HANDLE handle); + +#endif /* _INTERNAL_H_ */ diff --git a/include/libefiwrapper/protocol/EraseBlock.h b/include/libefiwrapper/protocol/EraseBlock.h new file mode 100644 index 0000000..8889e8c --- /dev/null +++ b/include/libefiwrapper/protocol/EraseBlock.h @@ -0,0 +1,108 @@ +/** @file + This file defines the EFI Erase Block Protocol. + + Copyright (c) 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 + 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: + This Protocol is introduced in UEFI Specification 2.6 + +**/ + +#ifndef __EFI_ERASE_BLOCK_PROTOCOL_H__ +#define __EFI_ERASE_BLOCK_PROTOCOL_H__ + +#include +#include + +#define EFI_ERASE_BLOCK_PROTOCOL_GUID \ + { \ + 0x95a9a93e, 0xa86e, 0x4926, { 0xaa, 0xef, 0x99, 0x18, 0xe7, 0x72, 0xd9, 0x87 } \ + } + +typedef struct _EFI_ERASE_BLOCK_PROTOCOL EFI_ERASE_BLOCK_PROTOCOL; + +#define EFI_ERASE_BLOCK_PROTOCOL_REVISION ((2<<16) | (60)) + +/// +/// EFI_ERASE_BLOCK_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 erase request is completed. + // + EFI_EVENT Event; + // + // Defines whether the signaled event encountered an error. + // + EFI_STATUS TransactionStatus; +} EFI_ERASE_BLOCK_TOKEN; + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] LBA The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_ERASE) ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA LBA, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ); + +/// +/// The EFI Erase Block Protocol provides the ability for a device to expose +/// erase functionality. This optional protocol is installed on the same handle +/// as the EFI_BLOCK_IO_PROTOCOL or EFI_BLOCK_IO2_PROTOCOL. +/// +struct _EFI_ERASE_BLOCK_PROTOCOL { + // + // The revision to which the EFI_ERASE_BLOCK_PROTOCOL adheres. All future + // revisions must be backwards compatible. If a future version is not + // backwards compatible, it is not the same GUID. + // + UINT64 Revision; + // + // Returns the erase length granularity as a number of logical blocks. A + // value of 1 means the erase granularity is one logical block. + // + UINT32 EraseLengthGranularity; + EFI_BLOCK_ERASE EraseBlocks; +}; + +extern EFI_GUID gEfiEraseBlockProtocolGuid; + +#endif diff --git a/include/libefiwrapper/protocol/SdHostIo.h b/include/libefiwrapper/protocol/SdHostIo.h new file mode 100644 index 0000000..bb696c1 --- /dev/null +++ b/include/libefiwrapper/protocol/SdHostIo.h @@ -0,0 +1,417 @@ +// +// This file contains an 'Intel Peripheral Driver' and is +// licensed for Intel CPUs and chipsets under the terms of your +// license agreement with Intel or your vendor. This file may +// be modified by the user, subject to additional terms of the +// license agreement +// +/*++ + + Copyright (c) 1999 - 2011 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + + +/*++ + Module Name: + + SdHostIo.h + + Abstract: + + Interface definition for EFI_SD_HOST_IO_PROTOCOL + + --*/ + +#ifndef _SD_HOST_IO_H +#define _SD_HOST_IO_H + + +// Global ID for the EFI_SD_HOST_IO_PROTOCOL +// {B63F8EC7-A9C9-4472-A4C0-4D8BF365CC51} +// +#define EFI_SD_HOST_IO_PROTOCOL_GUID \ + { 0xb63f8ec7, 0xa9c9, 0x4472, { 0xa4, 0xc0, 0x4d, 0x8b, 0xf3, 0x65, 0xcc, 0x51 } } + +typedef struct _EFI_SD_HOST_IO_PROTOCOL EFI_SD_HOST_IO_PROTOCOL; + +// +// TODO: Move to Pci22.h +// +#define PCI_SUBCLASS_SD_HOST_CONTROLLER 0x05 +#define PCI_IF_STANDARD_HOST_NO_DMA 0x00 +#define PCI_IF_STANDARD_HOST_SUPPORT_DMA 0x01 + +// +// TODO: Retire +// +#define EFI_SD_HOST_IO_PROTOCOL_REVISION_01 0x01 + +// +// TODO: Do these belong in an Industry Standard include file? +// +// MMIO Registers definition for MMC/SDIO controller +// +#define MMIO_DMAADR 0x00 +#define MMIO_BLKSZ 0x04 +#define MMIO_BLKCNT 0x06 +#define MMIO_CMDARG 0x08 +#define MMIO_XFRMODE 0x0C +#define MMIO_SDCMD 0x0E +#define MMIO_RESP 0x10 +#define MMIO_BUFDATA 0x20 +#define MMIO_PSTATE 0x24 +#define MMIO_HOSTCTL 0x28 +#define MMIO_PWRCTL 0x29 +#define MMIO_BLKGAPCTL 0x2A +#define MMIO_WAKECTL 0x2B +#define MMIO_CLKCTL 0x2C +#define MMIO_TOCTL 0x2E +#define MMIO_SWRST 0x2F +#define MMIO_NINTSTS 0x30 +#define MMIO_ERINTSTS 0x32 +#define MMIO_NINTEN 0x34 +#define MMIO_ERINTEN 0x36 +#define MMIO_NINTSIGEN 0x38 +#define MMIO_ERINTSIGEN 0x3A +#define MMIO_AC12ERRSTS 0x3C +#define MMIO_HOST_CTL2 0x3E //hphang <- New in VLV2 +#define MMIO_CAP 0x40 +#define MMIO_CAP2 0x44 //hphang <- New in VLV2 +#define MMIO_MCCAP 0x48 +#define MMIO_FORCEEVENTCMD12ERRSTAT 0x50 //hphang <- New in VLV2 +#define MMIO_FORCEEVENTERRINTSTAT 0x52 //hphang <- New in VLV2 +#define MMIO_ADMAERRSTAT 0x54 //hphang <- New in VLV2 +#define MMIO_ADMASYSADDR 0x58 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE0 0x60 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE1 0x64 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE2 0x68 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE3 0x6C //hphang <- New in VLV2 +#define MMIO_BOOTTIMEOUTCTRL 0x70 //hphang <- New in VLV2 +#define MMIO_DEBUGSEL 0x74 //hphang <- New in VLV2 +#define MMIO_SHAREDBUS 0xE0 //hphang <- New in VLV2 +#define MMIO_SPIINTSUP 0xF0 //hphang <- New in VLV2 +#define MMIO_SLTINTSTS 0xFC +#define MMIO_CTRLRVER 0xFE + +typedef enum { + ResponseNo = 0, + ResponseR1, + ResponseR1b, + ResponseR2, + ResponseR3, + ResponseR4, + ResponseR5, + ResponseR5b, + ResponseR6, + ResponseR7 +} RESPONSE_TYPE; + +typedef enum { + NoData = 0, + InData, + OutData +} TRANSFER_TYPE; + +typedef enum { + Reset_Auto = 0, + Reset_DAT, + Reset_CMD, + Reset_DAT_CMD, + Reset_All, + Reset_HW +} RESET_TYPE; + + +typedef enum { + SDMA = 0, + ADMA2, + PIO +} DMA_MOD; + +typedef struct { + UINT32 HighSpeedSupport: 1; //High speed supported + UINT32 V18Support: 1; //1.8V supported + UINT32 V30Support: 1; //3.0V supported + UINT32 V33Support: 1; //3.3V supported + UINT32 SDR50Support: 1; + UINT32 SDR104Support: 1; + UINT32 DDR50Support: 1; + UINT32 Reserved0: 1; + UINT32 BusWidth4: 1; // 4 bit width + UINT32 BusWidth8: 1; // 8 bit width + UINT32 Reserved1: 6; + UINT32 SDMASupport: 1; + UINT32 ADMA2Support: 1; + UINT32 DmaMode: 2; + UINT32 ReTuneTimer: 4; + UINT32 ReTuneMode: 2; + UINT32 Reserved2: 6; + UINT32 BoundarySize; +} HOST_CAPABILITY; + +/*++ + + Routine Description: + The main function used to send the command to the card inserted into the SD host + slot. + It will assemble the arguments to set the command register and wait for the command + and transfer completed until timeout. Then it will read the response register to fill + the ResponseData + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + CommandIndex - The command index to set the command index field of command register + Argument - Command argument to set the argument field of command register + DataType - TRANSFER_TYPE, indicates no data, data in or data out + Buffer - Contains the data read from / write to the device + BufferSize - The size of the buffer + ResponseType - RESPONSE_TYPE + TimeOut - Time out value in 1 ms unit + ResponseData - Depending on the ResponseType, such as CSD or card status + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + EFI_OUT_OF_RESOURCES + EFI_TIMEOUT + EFI_DEVICE_ERROR + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SEND_COMMAND) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT16 CommandIndex, + IN UINT32 Argument, + IN TRANSFER_TYPE DataType, + IN UINT8 *Buffer, OPTIONAL + IN UINT32 BufferSize, + IN RESPONSE_TYPE ResponseType, + IN UINT32 TimeOut, + OUT UINT32 *ResponseData OPTIONAL + ); + +/*++ + + Routine Description: + Set max clock frequency of the host, the actual frequency + may not be the same as MaxFrequency. It depends on + the max frequency the host can support, divider, and host + speed mode. + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + MaxFrequency - Max frequency in HZ + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_CLOCK_FREQUENCY) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 MaxFrequency + ); + +/*++ + + Routine Description: + Set bus width of the host + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + BusWidth - Bus width in 1, 4, 8 bits + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_BUS_WIDTH) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 BusWidth + ); + +/*++ + + Routine Description: + Set voltage which could supported by the host. + Support 0(Power off the host), 1.8V, 3.0V, 3.3V + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + Voltage - Units in 0.1 V + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_HOST_VOLTAGE) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 Voltage + ); + +/*++ + + Routine Description: + Set Host High Speed + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + HighSpeed - True for High Speed Mode set, false for normal mode + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_HOST_SPEED_MODE) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 HighSpeed + ); + +/*++ + + Routine Description: + Set High Speed Mode + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + SetHostDdrMode - True for DDR Mode set, false for normal mode + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_HOST_DDR_MODE) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 DdrMode + ); + + +/*++ + + Routine Description: + Reset the host + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + ResetAll - TRUE to reset all + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_RESET_SD_HOST) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN RESET_TYPE ResetType + ); + +/*++ + + Routine Description: + Reset the host + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + Enable - TRUE to enable, FALSE to disable + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_ENABLE_AUTO_STOP_CMD) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN BOOLEAN Enable + ); + +/*++ + + Routine Description: + Find whether these is a card inserted into the slot. If so + init the host. If not, return EFI_NOT_FOUND. + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + + Returns: + EFI_SUCCESS + EFI_NOT_FOUND + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_DETECT_CARD_AND_INIT_HOST) ( + IN EFI_SD_HOST_IO_PROTOCOL *This + ); + +/*++ + + Routine Description: + Set the Block length + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + BlockLength - card supportes block length + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_BLOCK_LENGTH) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 BlockLength + ); + +typedef EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SETUP_DEVICE)( + IN EFI_SD_HOST_IO_PROTOCOL *This + ); + + + +// +// Interface structure for the EFI SD Host I/O Protocol +// +struct _EFI_SD_HOST_IO_PROTOCOL { + UINT32 Revision; + HOST_CAPABILITY HostCapability; + EFI_SD_HOST_IO_PROTOCOL_SEND_COMMAND SendCommand; + EFI_SD_HOST_IO_PROTOCOL_SET_CLOCK_FREQUENCY SetClockFrequency; + EFI_SD_HOST_IO_PROTOCOL_SET_BUS_WIDTH SetBusWidth; + EFI_SD_HOST_IO_PROTOCOL_SET_HOST_VOLTAGE SetHostVoltage; + EFI_SD_HOST_IO_PROTOCOL_SET_HOST_DDR_MODE SetHostDdrMode; + EFI_SD_HOST_IO_PROTOCOL_RESET_SD_HOST ResetSdHost; + EFI_SD_HOST_IO_PROTOCOL_ENABLE_AUTO_STOP_CMD EnableAutoStopCmd; + EFI_SD_HOST_IO_PROTOCOL_DETECT_CARD_AND_INIT_HOST DetectCardAndInitHost; + EFI_SD_HOST_IO_PROTOCOL_SET_BLOCK_LENGTH SetBlockLength; + EFI_SD_HOST_IO_PROTOCOL_SETUP_DEVICE SetupDevice; + EFI_SD_HOST_IO_PROTOCOL_SET_HOST_SPEED_MODE SetHostSpeedMode; +}; + +#endif diff --git a/include/libefiwrapper/protocol/StorageSecurityCommand.h b/include/libefiwrapper/protocol/StorageSecurityCommand.h new file mode 100644 index 0000000..4f806af --- /dev/null +++ b/include/libefiwrapper/protocol/StorageSecurityCommand.h @@ -0,0 +1,212 @@ +/** @file + EFI Storage Security Command Protocol as defined in UEFI 2.3.1 specification. + This protocol is used to abstract mass storage devices to allow code running in + the EFI boot services environment to send security protocol commands to mass + storage devices without specific knowledge of the type of device or controller + that manages the device. + + 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 __STORAGE_SECURITY_COMMAND_H__ +#define __STORAGE_SECURITY_COMMAND_H__ + +#define EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID \ + { \ + 0xC88B0B6D, 0x0DFC, 0x49A7, {0x9C, 0xB4, 0x49, 0x07, 0x4B, 0x4C, 0x3A, 0x78 } \ + } + +typedef struct _EFI_STORAGE_SECURITY_COMMAND_PROTOCOL EFI_STORAGE_SECURITY_COMMAND_PROTOCOL; + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @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_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_STORAGE_SECURITY_RECEIVE_DATA)( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ); + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @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_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_STORAGE_SECURITY_SEND_DATA) ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer +); + +/// +/// The EFI_STORAGE_SECURITY_COMMAND_PROTOCOL is used to send security protocol +/// commands to a mass storage device. Two types of security protocol commands +/// are supported. SendData sends a command with data to a device. ReceiveData +/// sends a command that receives data and/or the result of one or more commands +/// sent by SendData. +/// +/// The security protocol command formats supported shall be based on the definition +/// of the SECURITY PROTOCOL IN and SECURITY PROTOCOL OUT commands defined in SPC-4. +/// If the device uses the SCSI command set, no translation is needed in the firmware +/// and the firmware can package the parameters into a SECURITY PROTOCOL IN or SECURITY +/// PROTOCOL OUT command and send the command to the device. If the device uses a +/// non-SCSI command set, the firmware shall map the command and data payload to the +/// corresponding command and payload format defined in the non-SCSI command set +/// (for example, TRUSTED RECEIVE and TRUSTED SEND in ATA8-ACS). +/// +/// The firmware shall automatically add an EFI_STORAGE_SECURITY_COMMAND_PROTOCOL +/// for any storage devices detected during system boot that support SPC-4, ATA8-ACS +/// or their successors. +/// +struct _EFI_STORAGE_SECURITY_COMMAND_PROTOCOL { + EFI_STORAGE_SECURITY_RECEIVE_DATA ReceiveData; + EFI_STORAGE_SECURITY_SEND_DATA SendData; +}; + +extern EFI_GUID gEfiStorageSecurityCommandProtocolGuid; + +#endif diff --git a/include/libefiwrapper/sdio.h b/include/libefiwrapper/sdio.h new file mode 100644 index 0000000..ed6608e --- /dev/null +++ b/include/libefiwrapper/sdio.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _SDIO_H_ +#define _SDIO_H_ + +#include +#include + +EFI_STATUS sdio_init(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle, storage_t *s); +EFI_STATUS sdio_get_storage(EFI_SD_HOST_IO_PROTOCOL *This, storage_t **storage_p); +EFI_STATUS sdio_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle); + +#endif /* _SDIO_H_ */ diff --git a/include/libefiwrapper/smbios.h b/include/libefiwrapper/smbios.h new file mode 100644 index 0000000..b6f5dc0 --- /dev/null +++ b/include/libefiwrapper/smbios.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _SMBIOS_H_ +#define _SMBIOS_H_ + +#include +#include + +#define SMBIOS_UNDEFINED "N/A" + +EFI_STATUS smbios_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS smbios_free(EFI_SYSTEM_TABLE *st); +EFI_STATUS smbios_set(UINT8 type, UINT8 offset, const char *value); + +#endif /* _SMBIOS_H_ */ diff --git a/include/libefiwrapper/storage.h b/include/libefiwrapper/storage.h new file mode 100755 index 0000000..37a1a46 --- /dev/null +++ b/include/libefiwrapper/storage.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _STORAGE_H_ +#define _STORAGE_H_ + +#include +#include + +typedef struct storage { + EFI_STATUS (*init)(struct storage *s); + EFI_LBA (*read)(struct storage *s, EFI_LBA start, EFI_LBA count, + void *buf); + EFI_LBA (*write)(struct storage *s, EFI_LBA start, EFI_LBA count, + const void *buf); + EFI_STATUS (*erase)(struct storage *s, EFI_LBA start, UINTN Size); + UINT8 pci_function; + UINT8 pci_device; + EFI_LBA blk_cnt; + UINT32 blk_sz; + void *priv; +} storage_t; + +enum storage_type { + STORAGE_EMMC, + STORAGE_UFS, + STORAGE_SDCARD, + STORAGE_SATA, + STORAGE_NVME, + STORAGE_VIRTUAL, + STORAGE_ALL +}; + +typedef enum { + OsBootDeviceSata, + OsBootDeviceSd, + OsBootDeviceEmmc, + OsBootDeviceUfs, + OsBootDeviceSpi, + OsBootDeviceUsb, + OsBootDeviceNvme, + OsBootDeviceMemory, + OsBootDeviceVirtual, + OsBootDeviceMax +} SBL_OS_BOOT_MEDIUM_TYPE; + +typedef struct boot_dev { + enum storage_type type; + UINT32 diskbus; +} boot_dev_t; + +EFI_STATUS storage_init(EFI_SYSTEM_TABLE *st, storage_t *storage, + EFI_HANDLE *handle_p); +EFI_STATUS storage_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle); + +EFI_STATUS identify_boot_media(); + +boot_dev_t* get_boot_media(); +UINT8 get_boot_media_device_path_type(void); + +#endif /* _STORAGE_H_ */ diff --git a/libefiwrapper/Android.mk b/libefiwrapper/Android.mk new file mode 100644 index 0000000..271b8d5 --- /dev/null +++ b/libefiwrapper/Android.mk @@ -0,0 +1,47 @@ +LOCAL_PATH := $(call my-dir) + +LIBEFIWRAPPER_SRC_FILES := \ + ewvar.c \ + ewdrv.c \ + protocol.c \ + core.c \ + lib.c \ + bs.c \ + rs.c \ + conin.c \ + conout.c \ + serialio.c \ + storage.c \ + blockio.c \ + diskio.c \ + interface.c \ + media.c \ + conf_table.c \ + smbios.c \ + ewacpi.c \ + ewarg.c \ + sdio.c \ + ewlib.c \ + eraseblk.c + +include $(CLEAR_VARS) +LOCAL_MODULE := libefiwrapper-$(TARGET_BUILD_VARIANT) +LOCAL_STATIC_LIBRARIES := \ + libgnuefi \ + libefi +LOCAL_SRC_FILES := $(LIBEFIWRAPPER_SRC_FILES) +LOCAL_CFLAGS := $(EFIWRAPPER_CFLAGS) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libefiwrapper +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libefiwrapper +include $(BUILD_IAFW_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libefiwrapper_host-$(TARGET_BUILD_VARIANT) +LOCAL_SRC_FILES := $(LIBEFIWRAPPER_SRC_FILES) +LOCAL_CFLAGS := $(EFIWRAPPER_HOST_CFLAGS) +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../include/libefiwrapper \ + $(EFIWRAPPER_HOST_C_INCLUDES) +LOCAL_MODULE_HOST_ARCH := $(EFIWRAPPER_HOST_ARCH) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libefiwrapper +include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libefiwrapper/Makefile b/libefiwrapper/Makefile new file mode 100644 index 0000000..bf6463a --- /dev/null +++ b/libefiwrapper/Makefile @@ -0,0 +1,38 @@ +SRC_DIR := .. +include $(SRC_DIR)/Make.defaults + +CFLAGS += -DPRODUCT_MANUFACTURER=\"$(PRODUCT_MANUFACTURER)\" \ + -DPRODUCT_NAME=\"$(PRODUCT_NAME)\" + +OBJS := ewvar.o \ + ewdrv.o \ + protocol.o \ + core.o \ + lib.o \ + bs.o \ + rs.o \ + conin.o \ + conout.o \ + serialio.o \ + storage.o \ + blockio.o \ + diskio.o \ + interface.o \ + media.o \ + conf_table.o \ + smbios.o \ + ewacpi.o \ + ewarg.o \ + sdio.o \ + ewlib.o \ + eraseblk.o + +$(EW_LIB): $(OBJS) + $(AR) rcs $@ $^ + +.PHONY: clean +clean: + @rm -f $(OBJS) *~ + +mrproper: clean + @rm -f libefiwrapper_host-*.a diff --git a/libefiwrapper/blockio.c b/libefiwrapper/blockio.c new file mode 100644 index 0000000..86ea19c --- /dev/null +++ b/libefiwrapper/blockio.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "blockio.h" +#include "external.h" +#include "interface.h" + +#include + +static EFIAPI EFI_STATUS +blockio_reset(__attribute__((__unused__)) EFI_BLOCK_IO *This, + __attribute__((__unused__)) BOOLEAN ExtendedVerification) +{ + return EFI_SUCCESS; +} + +static EFI_STATUS blockio_access(BOOLEAN read, EFI_BLOCK_IO *This, + UINT32 MediaId, EFI_LBA LBA, + UINTN BufferSize, VOID *Buffer) +{ + media_t *media; + UINT32 blksz; + UINTN size; + EFI_LBA count; + + if (!This) + return EFI_INVALID_PARAMETER; + + if (!This->Media) + return EFI_NO_MEDIA; + + media = (media_t *)This->Media; + if (MediaId != media->m.MediaId) + return EFI_MEDIA_CHANGED; + + blksz = media->m.BlockSize; + if (!blksz) + return EFI_INVALID_PARAMETER; + + if (BufferSize % blksz) + return EFI_BAD_BUFFER_SIZE; + + size = BufferSize / blksz; + if (read) + count = media->storage->read(media->storage, LBA, size, Buffer); + else + count = media->storage->write(media->storage, LBA, size, Buffer); + + return count == size ? EFI_SUCCESS : EFI_DEVICE_ERROR; +} + +static EFIAPI EFI_STATUS +blockio_write(EFI_BLOCK_IO *This, UINT32 MediaId, EFI_LBA LBA, + UINTN BufferSize, VOID *Buffer) +{ + return blockio_access(FALSE, This, MediaId, LBA, BufferSize, Buffer); +} + +static EFIAPI EFI_STATUS +blockio_read(EFI_BLOCK_IO *This, UINT32 MediaId, EFI_LBA LBA, + UINTN BufferSize, VOID *Buffer) +{ + return blockio_access(TRUE, This, MediaId, LBA, BufferSize, Buffer); +} + +static EFIAPI EFI_STATUS +blockio_flush(__attribute__((__unused__)) EFI_BLOCK_IO *This) +{ + return EFI_SUCCESS; +} + +static EFI_GUID blockio_guid = BLOCK_IO_PROTOCOL; + +EFI_STATUS blockio_init(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle) +{ + static EFI_BLOCK_IO blockio_default = { + .Revision = EFI_BLOCK_IO_INTERFACE_REVISION3, + .Reset = blockio_reset, + .ReadBlocks = blockio_read, + .WriteBlocks = blockio_write, + .FlushBlocks = blockio_flush + }; + EFI_STATUS ret; + EFI_BLOCK_IO *blockio; + + ret = interface_init(st, &blockio_guid, handle, + &blockio_default, sizeof(blockio_default), + (void **)&blockio); + if (EFI_ERROR(ret)) + return ret; + + blockio->Media = &media->m; + + return EFI_SUCCESS; +} + +EFI_STATUS blockio_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + return interface_free(st, &blockio_guid, handle); +} diff --git a/libefiwrapper/blockio.h b/libefiwrapper/blockio.h new file mode 100644 index 0000000..b1e61a6 --- /dev/null +++ b/libefiwrapper/blockio.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _BLOCKIO_H_ +#define _BLOCKIO_H_ + +#include +#include + +#include "media.h" + +EFI_STATUS blockio_init(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle); +EFI_STATUS blockio_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle); + +#endif /* _BLOCKIO_H_ */ diff --git a/libefiwrapper/bs.c b/libefiwrapper/bs.c new file mode 100644 index 0000000..9cb1d33 --- /dev/null +++ b/libefiwrapper/bs.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "bs.h" +#include "lib.h" +#include "protocol.h" + +static EFIAPI EFI_TPL +bs_raise_TPL(__attribute__((__unused__)) EFI_TPL NewTpl) +{ + return 0; +} + +static EFIAPI VOID +bs_restore_TPL(__attribute__((__unused__)) EFI_TPL OldTpl) +{ +} + +static EFIAPI EFI_STATUS +bs_allocate_pages(__attribute__((__unused__)) EFI_ALLOCATE_TYPE Type, + __attribute__((__unused__)) EFI_MEMORY_TYPE MemoryType, + __attribute__((__unused__)) UINTN NoPages, + __attribute__((__unused__)) EFI_PHYSICAL_ADDRESS *Memory) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_free_pages(__attribute__((__unused__)) EFI_PHYSICAL_ADDRESS Memory, + __attribute__((__unused__)) UINTN NoPages) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_get_memory_map(__attribute__((__unused__)) UINTN *MemoryMapSize, + __attribute__((__unused__)) EFI_MEMORY_DESCRIPTOR *MemoryMap, + __attribute__((__unused__)) UINTN *MapKey, + __attribute__((__unused__)) UINTN *DescriptorSize, + __attribute__((__unused__)) UINT32 *DescriptorVersion) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_allocate_pool(__attribute__((__unused__)) EFI_MEMORY_TYPE PoolType, + UINTN Size, VOID **Buffer) +{ + void *buf; + + buf = malloc(Size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + *Buffer = buf; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +bs_free_pool(VOID *Buffer) +{ + free(Buffer); + return EFI_SUCCESS; +} + +typedef struct event { + UINT32 type; + EFI_TPL tpl; + EFI_EVENT_NOTIFY notify; + VOID *context; +} event_t; + +static EFIAPI EFI_STATUS +bs_create_event(UINT32 Type, + EFI_TPL NotifyTpl, + EFI_EVENT_NOTIFY NotifyFunction, + VOID *NotifyContext, + EFI_EVENT *Event) +{ + event_t *event; + + if (!Event || + (Type & EVT_NOTIFY_SIGNAL && Type & EVT_NOTIFY_WAIT) || + (!NotifyFunction && (Type & EVT_NOTIFY_SIGNAL || Type & EVT_NOTIFY_WAIT))) + return EFI_INVALID_PARAMETER; + + event = malloc(sizeof(*event)); + if (!event) + return EFI_OUT_OF_RESOURCES; + + event->type = Type; + event->tpl = NotifyTpl; + event->notify = NotifyFunction; + event->context = NotifyContext; + *Event = event; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +bs_set_timer(__attribute__((__unused__)) EFI_EVENT Event, + __attribute__((__unused__)) EFI_TIMER_DELAY Type, + __attribute__((__unused__)) UINT64 TriggerTime) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_wait_for_event(__attribute__((__unused__)) UINTN NumberOfEvents, + __attribute__((__unused__)) EFI_EVENT *Event, + __attribute__((__unused__)) UINTN *Index) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_signal_event(EFI_EVENT Event) +{ + event_t *event = (event_t *)Event; + + if (!Event) + return EFI_INVALID_PARAMETER; + + if (event->type != EVT_NOTIFY_SIGNAL) + return EFI_SUCCESS; + + event->notify(Event, event->context); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +bs_close_event(EFI_EVENT Event) +{ + free(Event); + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +bs_check_event(__attribute__((__unused__)) EFI_EVENT Event) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_PC_handle_protocol(__attribute__((__unused__)) EFI_HANDLE Handle, + __attribute__((__unused__)) EFI_GUID *Protocol, + __attribute__((__unused__)) VOID **Interface) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_register_protocol_notify(__attribute__((__unused__)) EFI_GUID *Protocol, + __attribute__((__unused__)) EFI_EVENT Event, + __attribute__((__unused__)) VOID **Registration) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_install_configuration_table(__attribute__((__unused__)) EFI_GUID *Guid, + __attribute__((__unused__)) VOID *Table) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_load_image(__attribute__((__unused__)) BOOLEAN BootPolicy, + __attribute__((__unused__)) EFI_HANDLE ParentImageHandle, + __attribute__((__unused__)) EFI_DEVICE_PATH *FilePath, + __attribute__((__unused__)) VOID *SourceBuffer, + __attribute__((__unused__)) UINTN SourceSize, + __attribute__((__unused__)) EFI_HANDLE *ImageHandle) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_start_image(__attribute__((__unused__)) EFI_HANDLE ImageHandle, + __attribute__((__unused__)) UINTN *ExitDataSize, + __attribute__((__unused__)) CHAR16 **ExitData) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_efi_exit(__attribute__((__unused__)) EFI_HANDLE ImageHandle, + __attribute__((__unused__)) EFI_STATUS ExitStatus, + __attribute__((__unused__)) UINTN ExitDataSize, + __attribute__((__unused__)) CHAR16 *ExitData) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_unload_image(__attribute__((__unused__)) EFI_HANDLE ImageHandle) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_exit_boot_services(__attribute__((__unused__)) EFI_HANDLE ImageHandle, + __attribute__((__unused__)) UINTN MapKey) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +bs_get_next_monotonic_count(__attribute__((__unused__)) UINT64 *Count) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_stall(__attribute__((__unused__)) UINTN Microseconds) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +bs_set_watchdog_timer(UINTN Timeout, + __attribute__((__unused__)) UINT64 WatchdogCode, + __attribute__((__unused__)) UINTN DataSize, + __attribute__((__unused__)) CHAR16 *WatchdogData) +{ + return Timeout == 0 ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_connect_controller(__attribute__((__unused__)) EFI_HANDLE ControllerHandle, + __attribute__((__unused__)) EFI_HANDLE *DriverImageHandle, + __attribute__((__unused__)) EFI_DEVICE_PATH *RemainingDevicePath, + __attribute__((__unused__)) BOOLEAN Recursive) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_disconnect_controller(__attribute__((__unused__)) EFI_HANDLE ControllerHandle, + __attribute__((__unused__)) EFI_HANDLE DriverImageHandle, + __attribute__((__unused__)) EFI_HANDLE ChildHandle) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_open_protocol_information(__attribute__((__unused__)) EFI_HANDLE Handle, + __attribute__((__unused__)) EFI_GUID *Protocol, + __attribute__((__unused__)) EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer, + __attribute__((__unused__)) UINTN *EntryCount) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_install_multiple_protocol_interfaces(__attribute__((__unused__)) EFI_HANDLE *Handle, + ...) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_uninstall_multiple_protocol_interfaces(__attribute__((__unused__)) EFI_HANDLE Handle, + ...) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +bs_calculate_crc32(VOID *Data, + UINTN DataSize, + UINT32 *Crc32) +{ + return crc32(Data, DataSize, Crc32); +} + +static EFIAPI VOID +bs_copy_mem(VOID *Destination, + VOID *Source, + UINTN Length) +{ + memcpy(Destination, Source, Length); +} + +static EFIAPI VOID +bs_set_mem(VOID *Buffer, + UINTN Size, + UINT8 Value) +{ + memset(Buffer, Value, Size); +} + +static EFIAPI EFI_STATUS +bs_create_event_ex(__attribute__((__unused__)) UINT32 Type, + __attribute__((__unused__)) EFI_TPL NotifyTpl, + __attribute__((__unused__)) EFI_EVENT_NOTIFY NotifyFunction, + __attribute__((__unused__)) const VOID *NotifyContext, + __attribute__((__unused__)) const EFI_GUID *EventGroup, + __attribute__((__unused__)) EFI_EVENT *Event) +{ + return EFI_UNSUPPORTED; +} + +static EFI_BOOT_SERVICES boot_services_default = { + .Hdr = { + .Signature = EFI_BOOT_SERVICES_SIGNATURE, + .Revision = EFI_BOOT_SERVICES_REVISION, + .HeaderSize = sizeof(EFI_TABLE_HEADER) + }, + + .RaiseTPL = bs_raise_TPL, + .RestoreTPL = bs_restore_TPL, + .AllocatePages = bs_allocate_pages, + .FreePages = bs_free_pages, + .GetMemoryMap = bs_get_memory_map, + .AllocatePool = bs_allocate_pool, + .FreePool = bs_free_pool, + .CreateEvent = bs_create_event, + .SetTimer = bs_set_timer, + .WaitForEvent = bs_wait_for_event, + .SignalEvent = bs_signal_event, + .CloseEvent = bs_close_event, + .CheckEvent = bs_check_event, + .PCHandleProtocol = bs_PC_handle_protocol, + .RegisterProtocolNotify = bs_register_protocol_notify, + .InstallConfigurationTable = bs_install_configuration_table, + .LoadImage = bs_load_image, + .StartImage = bs_start_image, + .Exit = bs_efi_exit, + .UnloadImage = bs_unload_image, + .ExitBootServices = bs_exit_boot_services, + .GetNextMonotonicCount = bs_get_next_monotonic_count, + .Stall = bs_stall, + .SetWatchdogTimer = bs_set_watchdog_timer, + .ConnectController = bs_connect_controller, + .DisconnectController = bs_disconnect_controller, + .OpenProtocolInformation = bs_open_protocol_information, + .InstallMultipleProtocolInterfaces = bs_install_multiple_protocol_interfaces, + .UninstallMultipleProtocolInterfaces = bs_uninstall_multiple_protocol_interfaces, + .CalculateCrc32 = bs_calculate_crc32, + .CopyMem = bs_copy_mem, + .SetMem = bs_set_mem, + .CreateEventEx = bs_create_event_ex +}; + +EFI_STATUS bs_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + EFI_BOOT_SERVICES *bs; + + if (!st || !st->BootServices) + return EFI_INVALID_PARAMETER; + + bs = st->BootServices; + memcpy(bs, &boot_services_default, sizeof(*bs)); + + ret = protocol_init_bs(bs); + if (EFI_ERROR(ret)) + return ret; + + return crc32((void *)bs, sizeof(*bs), &bs->Hdr.CRC32); +} diff --git a/libefiwrapper/bs.h b/libefiwrapper/bs.h new file mode 100644 index 0000000..46e7ff3 --- /dev/null +++ b/libefiwrapper/bs.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _BS_H_ +#define _BS_H_ + +#include +#include + +EFI_STATUS bs_init(EFI_SYSTEM_TABLE *bs); + +#endif /* _BS_H_ */ diff --git a/libefiwrapper/conf_table.c b/libefiwrapper/conf_table.c new file mode 100644 index 0000000..f127655 --- /dev/null +++ b/libefiwrapper/conf_table.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include + +#include "external.h" +#include "lib.h" + +EFI_STATUS conf_table_new(EFI_SYSTEM_TABLE *st, + EFI_GUID *guid, + EFI_CONFIGURATION_TABLE **table) +{ + EFI_CONFIGURATION_TABLE *tables; + + if (!st || !guid || !table) + return EFI_INVALID_PARAMETER; + + if (!st->ConfigurationTable) { + if (st->NumberOfTableEntries) + return EFI_INVALID_PARAMETER; + + tables = malloc(sizeof(EFI_CONFIGURATION_TABLE)); + if (!tables) + return EFI_OUT_OF_RESOURCES; + + goto success; + } + + if (!st->NumberOfTableEntries) + return EFI_INVALID_PARAMETER; + + tables = realloc(st->ConfigurationTable, + (st->NumberOfTableEntries + 1) * + sizeof(EFI_CONFIGURATION_TABLE)); + if (!tables) + return EFI_OUT_OF_RESOURCES; + + +success: + st->ConfigurationTable = tables; + *table = &tables[st->NumberOfTableEntries]; + memcpy(&(*table)->VendorGuid, guid, sizeof(*guid)); + st->NumberOfTableEntries++; + + return EFI_SUCCESS; +} + +EFI_STATUS conf_table_free(EFI_SYSTEM_TABLE *st, EFI_GUID *guid) +{ + EFI_CONFIGURATION_TABLE *tables = NULL; + size_t i; + + if (!st || !st->ConfigurationTable || !st->NumberOfTableEntries) + return EFI_INVALID_PARAMETER; + + if (st->NumberOfTableEntries == 1) { + if (guidcmp(&st->ConfigurationTable[0].VendorGuid, guid)) + return EFI_NOT_FOUND; + free(st->ConfigurationTable); + goto success; + } + + for (i = 0; i < st->NumberOfTableEntries; i++) { + if (guidcmp(&st->ConfigurationTable[i].VendorGuid, guid)) + continue; + + tables = malloc((st->NumberOfTableEntries - 1) * + sizeof(EFI_CONFIGURATION_TABLE)); + if (!tables) + return EFI_OUT_OF_RESOURCES; + + memcpy(tables, st->ConfigurationTable, i * + sizeof(EFI_CONFIGURATION_TABLE)); + memcpy(&tables[i], &st->ConfigurationTable[i + 1], + (st->NumberOfTableEntries - i) * + sizeof(EFI_CONFIGURATION_TABLE)); + break; + } + + if (i == st->NumberOfTableEntries) + return EFI_NOT_FOUND; + + free(st->ConfigurationTable); + +success: + st->ConfigurationTable = tables; + st->NumberOfTableEntries--; + return EFI_SUCCESS; +} diff --git a/libefiwrapper/conin.c b/libefiwrapper/conin.c new file mode 100644 index 0000000..b808ec0 --- /dev/null +++ b/libefiwrapper/conin.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "conin.h" +#include "interface.h" + +static EFIAPI EFI_STATUS +conin_reset(__attribute__((__unused__)) struct _SIMPLE_INPUT_INTERFACE *This, + __attribute__((__unused__)) BOOLEAN ExtendedVerification) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conin_read_key(__attribute__((__unused__)) struct _SIMPLE_INPUT_INTERFACE *This, + __attribute__((__unused__)) EFI_INPUT_KEY *Key) +{ + return EFI_NOT_FOUND; +} + +static EFI_GUID guid = SIMPLE_TEXT_OUTPUT_PROTOCOL; + +EFI_STATUS conin_init(EFI_SYSTEM_TABLE *st) +{ + static SIMPLE_INPUT_INTERFACE conin_default = { + .Reset = conin_reset, + .ReadKeyStroke = conin_read_key + }; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (st->ConsoleInHandle) + return EFI_ALREADY_STARTED; + + return interface_init(st, &guid, &st->ConsoleInHandle, + &conin_default, sizeof(conin_default), + (void **)&st->ConIn); +} + +EFI_STATUS conin_free(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + ret = interface_free(st, &guid, st->ConsoleInHandle); + if (EFI_ERROR(ret)) + return ret; + + st->ConsoleInHandle = NULL; + st->ConIn = NULL; + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/conin.h b/libefiwrapper/conin.h new file mode 100644 index 0000000..bcd3930 --- /dev/null +++ b/libefiwrapper/conin.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _CONIN_H_ +#define _CONIN_H_ + +#include +#include + +EFI_STATUS conin_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS conin_free(EFI_SYSTEM_TABLE *st); + +#endif /* _CONIN_H_ */ diff --git a/libefiwrapper/conout.c b/libefiwrapper/conout.c new file mode 100644 index 0000000..fd668c7 --- /dev/null +++ b/libefiwrapper/conout.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "conout.h" +#include "interface.h" +#include "lib.h" + +static EFIAPI EFI_STATUS +conout_reset(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) BOOLEAN ExtendedVerification) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_output_string(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + CHAR16 *String) +{ + while (*String) + printf("%c", *String++); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_test_string(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) CHAR16 *String) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +conout_query_mode(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) UINTN ModeNumber, + __attribute__((__unused__)) UINTN *Columns, + __attribute__((__unused__)) UINTN *Rows) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +conout_set_mode(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) UINTN ModeNumber) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +conout_set_attribute(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) UINTN Attribute) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +conout_clear_screen(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +conout_set_cursor_position(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) UINTN Column, + __attribute__((__unused__)) UINTN Row) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +conout_enable_cursor(__attribute__((__unused__)) struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, + __attribute__((__unused__)) BOOLEAN Enable) +{ + return EFI_UNSUPPORTED; +} + +static SIMPLE_TEXT_OUTPUT_MODE mode; + +static EFI_GUID conout_guid = SIMPLE_TEXT_OUTPUT_PROTOCOL; + +EFI_STATUS conout_init(EFI_SYSTEM_TABLE *st) +{ + static SIMPLE_TEXT_OUTPUT_INTERFACE conout_default = { + .Reset = conout_reset, + .OutputString = conout_output_string, + .TestString = conout_test_string, + .QueryMode = conout_query_mode, + .SetMode = conout_set_mode, + .SetAttribute = conout_set_attribute, + .ClearScreen = conout_clear_screen, + .SetCursorPosition = conout_set_cursor_position, + .EnableCursor = conout_enable_cursor, + .Mode = &mode + }; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (st->ConsoleOutHandle) + return EFI_ALREADY_STARTED; + + return interface_init(st, &conout_guid, &st->ConsoleOutHandle, + &conout_default, sizeof(conout_default), + (void **)&st->ConOut); +} + +EFI_STATUS conout_free(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + + ret = interface_free(st, &conout_guid, st->ConsoleOutHandle); + if (EFI_ERROR(ret)) + return ret; + + st->ConsoleOutHandle = NULL; + st->ConOut = NULL; + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/conout.h b/libefiwrapper/conout.h new file mode 100644 index 0000000..fc36499 --- /dev/null +++ b/libefiwrapper/conout.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _CONOUT_H_ +#define _CONOUT_H_ + +#include +#include + +EFI_STATUS conout_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS conout_free(EFI_SYSTEM_TABLE *st); + +#endif /* _CONOUT_H_ */ diff --git a/libefiwrapper/core.c b/libefiwrapper/core.c new file mode 100644 index 0000000..7d1ebda --- /dev/null +++ b/libefiwrapper/core.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "bs.h" +#include "conin.h" +#include "conout.h" +#include "ewarg.h" +#include "ewlog.h" +#include "ewvar.h" +#include "lib.h" +#include "rs.h" +#include "serialio.h" +#include "smbios.h" +#include "storage.h" +#include "version.h" + +static EFI_GUID image_guid = LOADED_IMAGE_PROTOCOL; +static EFI_BOOT_SERVICES bs; +static EFI_RUNTIME_SERVICES rs; + +static EFI_SYSTEM_TABLE st = { + .Hdr = { + .Signature = EFI_SYSTEM_TABLE_SIGNATURE, + .Revision = EFI_SYSTEM_TABLE_REVISION, + .HeaderSize = sizeof(EFI_TABLE_HEADER), + .CRC32 = 0, + .Reserved = 0, + }, + + .BootServices = &bs, + .RuntimeServices = &rs, + + .FirmwareVendor = L"Intel", + .FirmwareRevision = 0, +}; + +static EFI_LOADED_IMAGE img = { + .Revision = EFI_IMAGE_INFORMATION_REVISION, + .ParentHandle = NULL, + .SystemTable = &st +}; + +static EFI_GUID EFIWRAPPER_GUID = + { 0x59d0d866, 0x5637, 0x47a9, + { 0xb7, 0x50, 0x42, 0x60, 0x0a, 0x54, 0x5b, 0x63 }}; + +static struct { + UINT32 MajorRevision; + UINT32 MinorRevision; +} efiwrapper = { + .MajorRevision = EFIWRAPPER_MAJOR, + .MinorRevision = EFIWRAPPER_MINOR +}; + +static struct component { + const char *name; + EFI_STATUS (*init)(EFI_SYSTEM_TABLE *st); + EFI_STATUS (*free)(EFI_SYSTEM_TABLE *st); +} COMPONENTS[] = { + { "boot services", bs_init, NULL }, + { "runtime services", rs_init, NULL }, + { "console in", conin_init, conin_free }, + { "console out", conout_init, conout_free }, + { "serial", serialio_init, serialio_free }, + { "smbios", smbios_init, smbios_free } +}; + +EFI_STATUS set_load_options(int argc, char **argv) +{ + size_t i, size = 0; + char *opt, *p, *cur; + + if (!argc) + return EFI_SUCCESS; + + for (i = 0; i < (size_t)argc; i++) + size += strlen(argv[i]) + 1; + + opt = malloc(size); + if (!opt) + return EFI_OUT_OF_RESOURCES; + + p = opt; + for (i = 0; i < (size_t)argc; i++) { + if (i != 0) + *p++ = ' '; + cur = argv[i]; + while (*cur) + *p++ = *cur++; + } + *p = '\0'; + img.LoadOptions = str2str16_p(opt); + free(opt); + if (!img.LoadOptions) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +EFI_STATUS efiwrapper_init(int argc, char **argv, EFI_SYSTEM_TABLE **st_p, + EFI_HANDLE *img_handle) +{ + EFI_STATUS ret; + size_t i, j; + + if ((argc && !argv) || !st_p || !img_handle) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(COMPONENTS); i++) { + ret = COMPONENTS[i].init(&st); + if (EFI_ERROR(ret)) { + ewerr("%s failed to initilized", COMPONENTS[i].name); + goto err_components; + } + } + + ret = uefi_call_wrapper(st.BootServices->InstallProtocolInterface, 4, + img_handle, &image_guid, + EFI_NATIVE_INTERFACE, &img); + if (EFI_ERROR(ret)) + goto err_components; + + ret = uefi_call_wrapper(st.BootServices->InstallProtocolInterface, 4, + img_handle, &EFIWRAPPER_GUID, + EFI_NATIVE_INTERFACE, &efiwrapper); + if (EFI_ERROR(ret)) + goto err_img; + + ret = set_load_options(argc, argv); + if (EFI_ERROR(ret)) + goto err_efiwrapper; + + ret = crc32((void *)&st, sizeof(st), &st.Hdr.CRC32); + if (EFI_ERROR(ret)) + goto err_load_options; + + ret = ewarg_init(argc, argv); + if (EFI_ERROR(ret)) + goto err_load_options; + + ret = identify_boot_media(); + if (EFI_ERROR(ret)) + goto err_load_options; + + *st_p = &st; + + return EFI_SUCCESS; + +err_load_options: + free(img.LoadOptions); + +err_efiwrapper: + uefi_call_wrapper(st.BootServices->UninstallProtocolInterface, 3, + *img_handle, &EFIWRAPPER_GUID, &img); + +err_img: + uefi_call_wrapper(st.BootServices->UninstallProtocolInterface, 3, + *img_handle, &image_guid, &img); + +err_components: + for (j = 0; j < i; j++) { + if (!COMPONENTS[j].free) + continue; + COMPONENTS[j].free(&st); + } + + return ret; +} + +EFI_STATUS efiwrapper_free(EFI_HANDLE img_handle) +{ + EFI_STATUS ret; + size_t i; + + for (i = 0; i < ARRAY_SIZE(COMPONENTS); i++) { + if (!COMPONENTS[i].free) + continue; + ret = COMPONENTS[i].free(&st); + if (EFI_ERROR(ret)) { + ewerr("%s failed to exit", COMPONENTS[i].name); + return ret; + } + } + + ret = uefi_call_wrapper(st.BootServices->UninstallProtocolInterface, 3, + img_handle, &image_guid, &img); + if (EFI_ERROR(ret)) + return ret; + + ewvar_free_all(); + free(img.LoadOptions); + + ewarg_free(); + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/diskio.c b/libefiwrapper/diskio.c new file mode 100644 index 0000000..95a72cc --- /dev/null +++ b/libefiwrapper/diskio.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "diskio.h" +#include "interface.h" +#include "lib.h" + +typedef struct diskio { + EFI_DISK_IO interface; + media_t *media; +} diskio_t; + +static EFI_STATUS read_block(media_t *media, EFI_LBA lba, + unsigned char **block) +{ + EFI_LBA count; + + *block = malloc(media->m.BlockSize); + if (!*block) + return EFI_OUT_OF_RESOURCES; + + count = media->storage->read(media->storage, lba, 1, *block); + if (count != 1) { + free(*block); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +diskio_read(EFI_DISK_IO_PROTOCOL *This, + UINT32 MediaId, + UINT64 Offset, + UINTN BufferSize, + VOID *Buffer) +{ + EFI_STATUS ret; + diskio_t *diskio = (diskio_t *)This; + UINT32 count, size; + UINT32 blksz; + unsigned char *buf = Buffer, *block; + media_t *media; + + if (!This || !Buffer) + return EFI_INVALID_PARAMETER; + + if (!diskio->media) + return EFI_INVALID_PARAMETER; + media = diskio->media; + + if (media->m.MediaId != MediaId) + return EFI_MEDIA_CHANGED; + + blksz = media->m.BlockSize; + if (!blksz) + return EFI_INVALID_PARAMETER; + + if (Offset % blksz) { + ret = read_block(media, Offset / blksz, &block); + if (EFI_ERROR(ret)) + return ret; + + size = min(blksz - (Offset % blksz), BufferSize); + memcpy(buf, block + (Offset % blksz), size); + free(block); + + buf += size; + Offset += size; + BufferSize -= size; + } + + size = BufferSize / blksz; + if (size > 0) { + count = media->storage->read(media->storage, Offset / blksz, size, buf); + if (count != size) + return EFI_DEVICE_ERROR; + size *= blksz; + BufferSize -= size; + Offset += size; + buf += size; + } + if (BufferSize) { + ret = read_block(media, Offset / blksz, &block); + if (EFI_ERROR(ret)) + return ret; + memcpy(buf, block, BufferSize); + free(block); + } + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +diskio_write(EFI_DISK_IO_PROTOCOL *This, + UINT32 MediaId, + UINT64 Offset, + UINTN BufferSize, + VOID *Buffer) +{ + EFI_STATUS ret; + diskio_t *diskio = (diskio_t *)This; + UINT32 count, size; + UINT32 blksz; + unsigned char *buf = Buffer, *block; + media_t *media; + + if (!This || !Buffer) + return EFI_INVALID_PARAMETER; + + if (!diskio->media) + return EFI_INVALID_PARAMETER; + media = diskio->media; + + if (media->m.MediaId != MediaId) + return EFI_MEDIA_CHANGED; + + blksz = media->m.BlockSize; + if (!blksz) + return EFI_INVALID_PARAMETER; + + if (Offset % blksz) { + ret = read_block(media, Offset / blksz, &block); + if (EFI_ERROR(ret)) + return ret; + + size = min(blksz - (Offset % blksz), BufferSize); + memcpy(block + (Offset % blksz), buf, size); + + count = media->storage->write(media->storage, Offset / blksz, 1, block); + free(block); + if (count != 1) + return EFI_DEVICE_ERROR; + + buf += size; + Offset += size; + BufferSize -= size; + } + + size = BufferSize / blksz; + if (size > 0) { + count = media->storage->write(media->storage, Offset / blksz, size, buf); + if (count != size) + return EFI_DEVICE_ERROR; + + size *= blksz; + BufferSize -= size; + Offset += size; + buf += size; + } + if (BufferSize) { + ret = read_block(media, Offset / blksz, &block); + if (EFI_ERROR(ret)) + return ret; + + memcpy(block, buf, BufferSize); + count = media->storage->write(media->storage, Offset / blksz, 1, block); + free(block); + if (count != 1) + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +static EFI_GUID diskio_guid = DISK_IO_PROTOCOL; + +EFI_STATUS diskio_init(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle) +{ + static diskio_t diskio_default = { + .interface = { + .Revision = EFI_DISK_IO_INTERFACE_REVISION, + .ReadDisk = diskio_read, + .WriteDisk = diskio_write + } + }; + EFI_STATUS ret; + diskio_t *diskio; + + ret = interface_init(st, &diskio_guid, handle, + &diskio_default, sizeof(diskio_default), + (void **)&diskio); + if (EFI_ERROR(ret)) + return ret; + + diskio->media = media; + + return EFI_SUCCESS; +} + +EFI_STATUS diskio_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + return interface_free(st, &diskio_guid, handle); +} diff --git a/libefiwrapper/diskio.h b/libefiwrapper/diskio.h new file mode 100644 index 0000000..73749b7 --- /dev/null +++ b/libefiwrapper/diskio.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _DISKIO_H_ +#define _DISKIO_H_ + +#include +#include + +#include "media.h" + +EFI_STATUS diskio_init(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle); +EFI_STATUS diskio_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle); + +#endif /* _DISKIO_H_ */ diff --git a/libefiwrapper/eraseblk.c b/libefiwrapper/eraseblk.c new file mode 100644 index 0000000..c033456 --- /dev/null +++ b/libefiwrapper/eraseblk.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "eraseblk.h" +#include "protocol/EraseBlock.h" +#include "external.h" +#include "interface.h" + +#include + + +typedef struct eraseblock { + EFI_ERASE_BLOCK_PROTOCOL interface; + media_t *media; +} eraseblk_t; + +static EFI_STATUS storage_erase_block(EFI_ERASE_BLOCK_PROTOCOL *This, + UINT32 MediaId, EFI_LBA LBA, UINTN Size) +{ + EFI_STATUS ret; + eraseblk_t *eraseblk = (eraseblk_t *)This; + media_t *media; + + if (!This) + return EFI_INVALID_PARAMETER; + + if (!eraseblk->media) + return EFI_INVALID_PARAMETER; + + media = eraseblk->media; + if (media->m.MediaId != MediaId) + return EFI_MEDIA_CHANGED; + + if (media->storage->erase) + ret = media->storage->erase(media->storage, LBA, Size); + else + return EFI_UNSUPPORTED; + + return ret; +} + +EFI_STATUS +EFIAPI +erase_block ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + __attribute__((__unused__)) IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ) +{ + return storage_erase_block(This, MediaId, Lba, Size); +} + +static EFI_GUID erase_block_guid = EFI_ERASE_BLOCK_PROTOCOL_GUID; + +EFI_STATUS erase_block_init(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle) +{ + EFI_STATUS ret; + eraseblk_t *eraseblk; + + static eraseblk_t erase_block_default = { + .interface = { + .Revision = EFI_ERASE_BLOCK_PROTOCOL_REVISION, + .EraseLengthGranularity = 1, + .EraseBlocks = erase_block, + } + }; + + ret = interface_init(st, &erase_block_guid, handle, + &erase_block_default, sizeof(erase_block_default), + (void **)&eraseblk); + if (EFI_ERROR(ret)) + return ret; + + eraseblk->media = media; + + return EFI_SUCCESS; +} + +EFI_STATUS erase_block_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + return interface_free(st, &erase_block_guid, handle); +} diff --git a/libefiwrapper/eraseblk.h b/libefiwrapper/eraseblk.h new file mode 100644 index 0000000..5af1ef0 --- /dev/null +++ b/libefiwrapper/eraseblk.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _ERASEBLOCK_H_ +#define _ERASEBLOCK_H_ + +#include +#include +#include +#include "media.h" + +EFI_STATUS erase_block_init(EFI_SYSTEM_TABLE *st, media_t *media, EFI_HANDLE *handle); +EFI_STATUS erase_block_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle); + +#endif + diff --git a/libefiwrapper/ewacpi.c b/libefiwrapper/ewacpi.c new file mode 100644 index 0000000..ee79970 --- /dev/null +++ b/libefiwrapper/ewacpi.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "ewacpi.h" +#include "external.h" + +#define SIG_SIZE (sizeof(((struct acpi_header *)0)->signature)) + +struct rsdp_table { + CHAR8 signature[8]; /* "RSD PTR " */ + UINT8 checksum; /* RSDP Checksum (bytes 0-19) */ + CHAR8 oem_id[6]; /* OEM ID String */ + CHAR8 revision; /* ACPI Revision (0=1.0,2=2.0) */ + UINT32 rsdt_address; /* 32-bit RSDT Pointer */ + UINT32 length; /* RSDP Length */ + UINT64 xsdt_address; /* 64-bit XSDT Pointer */ + UINT8 extended_checksum; /* rsdp Checksum (full) */ + CHAR8 reserved[3]; /* Reserved */ +} __attribute__((packed)); + +struct xsdt_table { + struct acpi_header header; + UINT64 entry[1]; /* Table Entries */ +} __attribute__((packed)); + +static EFI_STATUS validate_table(struct acpi_header *table) +{ + UINT8 sum, *buf = (UINT8 *)table; + UINTN i; + + if (table->length < sizeof(*table)) + return EFI_COMPROMISED_DATA; + + for (sum = 0, i = 0; i < table->length; i++) + sum += buf[i]; + + return sum ? EFI_COMPROMISED_DATA : EFI_SUCCESS; +} + +EFI_STATUS ewacpi_get_table(EFI_SYSTEM_TABLE *st, const char *name, + struct acpi_header **table) +{ + EFI_STATUS ret; + const EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; + struct rsdp_table *rsdp = NULL; + struct xsdt_table *xsdt; + struct acpi_header *cur; + UINTN i, nb; + + if (!st || !name || !table) + return EFI_INVALID_PARAMETER; + + if (strlen(name) > SIG_SIZE) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < st->NumberOfTableEntries; i++) { + if (memcmp(&st->ConfigurationTable[i].VendorGuid, + &acpi2_guid, sizeof(acpi2_guid))) + continue; + rsdp = st->ConfigurationTable[i].VendorTable; + break; + } + + if (!rsdp) + return EFI_NOT_FOUND; + + if (!rsdp->xsdt_address) + return EFI_UNSUPPORTED; + + xsdt = (struct xsdt_table *)(UINTN)rsdp->xsdt_address; + nb = (xsdt->header.length - sizeof(xsdt->header)) / sizeof(xsdt->entry); + for (i = 0; i < nb; i++) { + cur = (struct acpi_header *)(UINTN)xsdt->entry[i]; + if (memcmp(name, cur->signature, SIG_SIZE)) + continue; + ret = validate_table(cur); + if (EFI_ERROR(ret)) + return ret; + + *table = cur; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} diff --git a/libefiwrapper/ewarg.c b/libefiwrapper/ewarg.c new file mode 100644 index 0000000..02aa1fc --- /dev/null +++ b/libefiwrapper/ewarg.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "ewarg.h" +#include "external.h" + +static size_t argc; +static char **argv; + +EFI_STATUS ewarg_init(int i_argc, char **i_argv) +{ + if (argc || argv) + return EFI_ALREADY_STARTED; + + if (i_argc < 0) + return EFI_INVALID_PARAMETER; + + argc = i_argc; + argv = i_argv; + + return EFI_SUCCESS; +} + +const char *ewarg_getval(const char *name) +{ + size_t i, len; + + if (!name || !argc || !argv) + return NULL; + + for (i = 0; i < argc; i++) { + len = strlen(name); + if (!strncmp(name, argv[i], len) + && argv[i][len] == '=' + && argv[i][len + 1] != '\0') + return &argv[i][len + 1]; + } + + return NULL; +} + +EFI_STATUS ewarg_free(void) +{ + argc = 0; + argv = NULL; + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/ewdrv.c b/libefiwrapper/ewdrv.c new file mode 100644 index 0000000..4aaa5f0 --- /dev/null +++ b/libefiwrapper/ewdrv.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include + +#include "ewdrv.h" +#include "ewlog.h" + +EFI_STATUS ewdrv_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret = EFI_SUCCESS; + size_t i, j; + + if (!ew_drivers) + return EFI_UNSUPPORTED; + + for (i = 0; ew_drivers[i]; i++) { + ret = ew_drivers[i]->init(st); + if (EFI_ERROR(ret)) + break; + ewdbg("'%s' driver succesfully initialized", + ew_drivers[i]->name); + } + + if (EFI_ERROR(ret)) { + ewerr("Failed to initialize '%s' driver", ew_drivers[i]->name); + for (j = 0; j < i; j++) { + if (!ew_drivers[j]->exit) + continue; + ew_drivers[j]->exit(st); + } + } + + return ret; +} + +EFI_STATUS ewdrv_exit(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + size_t i; + + if (!ew_drivers) + return EFI_UNSUPPORTED; + + for (i = 0; ew_drivers[i]; i++) { + if (!ew_drivers[i]->exit) + continue; + ret = ew_drivers[i]->exit(st); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/ewlib.c b/libefiwrapper/ewlib.c new file mode 100644 index 0000000..52d0958 --- /dev/null +++ b/libefiwrapper/ewlib.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "ewlib.h" + +int guidcmp(EFI_GUID *g1, EFI_GUID *g2) +{ + return memcmp(g1, g2, sizeof(*g1)); +} + +size_t str16len(const CHAR16 *str) +{ + size_t len; + + for (len = 0; *str; len++) + str++; + + return len; +} + +int str16cmp(const CHAR16 *s1, const CHAR16 *s2) +{ + for (; *s1 && *s2 && *s1 == *s2; s1++, s2++) + ; + + if (*s1 < *s2) + return -1; + if (*s1 > *s2) + return 1; + return 0; +} diff --git a/libefiwrapper/ewvar.c b/libefiwrapper/ewvar.c new file mode 100644 index 0000000..0463f5f --- /dev/null +++ b/libefiwrapper/ewvar.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "ewvar.h" +#include "lib.h" + +static ewvar_t *EFI_VARS; +static ewvar_storage_t *storage; + +ewvar_t *ewvar_new(CHAR16 *name, EFI_GUID *guid, UINT32 attr, + UINTN size, VOID *data) +{ + EFI_STATUS ret; + ewvar_t *var; + + var = calloc(1, sizeof(*var)); + if (!var) + return NULL; + + var->name = str16dup(name); + if (!var->name) + goto err; + + memcpy(&var->guid, guid, sizeof(var->guid)); + var->attributes = attr; + var->size = size; + + var->data = malloc(size); + if (!var->data) + goto err; + + memcpy(var->data, data, size); + + if (attr & EFI_VARIABLE_NON_VOLATILE && + storage && storage->save) { + ret = storage->save(var); + if (EFI_ERROR(ret)) + goto err; + } + + return var; + +err: + ewvar_free(var); + return NULL; +} + +void ewvar_free(ewvar_t *var) +{ + if (var->data) + free(var->data); + if (var->name) + free(var->name); + free(var); +} + +void ewvar_free_all(void) +{ + ewvar_t *var, *next; + + for (var = EFI_VARS; var; var = next) { + next = var->next; + ewvar_free(var); + } + EFI_VARS = NULL; +} + +void ewvar_add(ewvar_t *var) +{ + var->next = EFI_VARS; + EFI_VARS = var; +} + +ewvar_t *ewvar_get(const CHAR16 *name, EFI_GUID *guid, ewvar_t **prev_p) +{ + ewvar_t *var, *prev = NULL; + + for (var = EFI_VARS; var; var = var->next) { + if (!str16cmp(name, var->name) && !guidcmp(&var->guid, guid)) + break; + prev = var; + } + + if (var && prev_p) + *prev_p = prev; + + return var; +} + +ewvar_t *ewvar_get_first(void) +{ + return EFI_VARS; +} + +EFI_STATUS ewvar_del(ewvar_t *var, ewvar_t *prev) +{ + EFI_STATUS ret; + if (!var) + return EFI_NOT_FOUND; + + if (var->attributes & EFI_VARIABLE_NON_VOLATILE && + storage && storage->delete) { + ret = storage->delete(var); + if (EFI_ERROR(ret)) + return ret; + } + + if (prev) + prev->next = var->next; + else + EFI_VARS = var->next; + ewvar_free(var); + + return EFI_SUCCESS; +} + +EFI_STATUS ewvar_update(ewvar_t *var, UINTN size, VOID *data) +{ + if (var->attributes & EFI_VARIABLE_APPEND_WRITE) { + var->data = realloc(var->data, var->size + size); + if (!var->data) + return EFI_OUT_OF_RESOURCES; + + memcpy((char *)var->data + var->size, data, size); + var->size += size; + } else { + var->data = realloc(var->data, size); + if (!var->data) + return EFI_OUT_OF_RESOURCES; + + var->size = size; + if (!var->data) + return EFI_OUT_OF_RESOURCES; + memcpy(var->data, data, size); + } + + if (var->attributes & EFI_VARIABLE_NON_VOLATILE && + storage && storage->save) + return storage->save(var); + + return EFI_SUCCESS; +} + +EFI_STATUS ewvar_register_storage(ewvar_storage_t *s) +{ + if (!s) + return EFI_INVALID_PARAMETER; + + storage = s; + if (s->load) + return s->load(); + + return EFI_SUCCESS; +} + +EFI_STATUS ewvar_unregister_storage(void) +{ + storage = NULL; + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/interface.c b/libefiwrapper/interface.c new file mode 100644 index 0000000..5d1f43b --- /dev/null +++ b/libefiwrapper/interface.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "interface.h" +#include "external.h" + +EFI_STATUS interface_init(EFI_SYSTEM_TABLE *st, EFI_GUID *guid, + EFI_HANDLE *handle, + VOID *base, size_t base_size, + VOID **interface) +{ + EFI_STATUS ret; + + if (!st || !guid || !handle || !base || !interface) + return EFI_INVALID_PARAMETER; + + *interface = malloc(base_size); + if (!*interface) + return EFI_OUT_OF_RESOURCES; + + memcpy(*interface, base, base_size); + + ret = uefi_call_wrapper(st->BootServices->InstallProtocolInterface, 4, + handle, guid, EFI_NATIVE_INTERFACE, *interface); + if (EFI_ERROR(ret)) + free(*interface); + + return ret; +} + +EFI_STATUS interface_free(EFI_SYSTEM_TABLE *st, EFI_GUID *guid, + EFI_HANDLE handle) +{ + EFI_STATUS ret; + VOID *interface; + + if (!handle) + return EFI_INVALID_PARAMETER; + + ret = uefi_call_wrapper(st->BootServices->HandleProtocol, 3, + handle, guid, (VOID **)&interface); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(st->BootServices->UninstallProtocolInterface, 3, + handle, guid, interface); + if (EFI_ERROR(ret)) + return ret; + + free(interface); + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/lib.c b/libefiwrapper/lib.c new file mode 100644 index 0000000..36ad19b --- /dev/null +++ b/libefiwrapper/lib.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "lib.h" + +CHAR16 *str16dup(const CHAR16 *str) +{ + CHAR16 *dup; + size_t size; + + size = (str16len(str) + 1) * sizeof(*str); + dup = malloc(size); + if (!dup) + return NULL; + + memcpy(dup, str, size); + return dup; +} + +static int str2str16(const char *str, CHAR16 *str16) +{ + size_t i; + + for (i = 0; str[i] != '\0'; i++) + str16[i] = str[i]; + + str16[i] = '\0'; + return i; +} + +CHAR16 *str2str16_p(const char *str) +{ + CHAR16 *copy; + + copy = malloc((strlen(str) + 1) * sizeof(*copy)); + if (!copy) + return NULL; + + str2str16(str, copy); + + return copy; +} + +static UINT32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +EFI_STATUS crc32(const void *buf, size_t size, UINT32 *crc_p) +{ + UINT32 crc = ~0U; + const UINT8 *p; + + if (!buf || !crc_p) + return EFI_INVALID_PARAMETER; + + p = buf; + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + *crc_p = crc ^ ~0U; + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/lib.h b/libefiwrapper/lib.h new file mode 100644 index 0000000..f266448 --- /dev/null +++ b/libefiwrapper/lib.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _LIB_H_ +#define _LIB_H_ + +#include +#include + +#include "external.h" +#include "ewlib.h" + +CHAR16 *str16dup(const CHAR16 *str); +CHAR16 *str2str16_p(const char *str); + +EFI_STATUS crc32(const void *buf, size_t size, UINT32 *crc_p); + +#endif /* _LIB_H_ */ diff --git a/libefiwrapper/media.c b/libefiwrapper/media.c new file mode 100644 index 0000000..62926b1 --- /dev/null +++ b/libefiwrapper/media.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "external.h" +#include "interface.h" +#include "media.h" + +media_t *media_new(storage_t *storage) +{ + static UINT32 id; + media_t *media; + + media = calloc(1, sizeof(*media)); + if (!media) + return NULL; + + media->m.MediaId = ++id; + media->m.MediaPresent = TRUE; + media->m.BlockSize = storage->blk_sz; + media->m.LastBlock = storage->blk_cnt - 1; + + media->storage = storage; + + return media; +} + +/* Randomly generated GUID */ +static EFI_GUID media_guid = { 0xd4b39595, 0xe31b, 0x48d5, + { 0xac, 0xa3, 0x03, 0x1c, 0x61, 0x42, 0xc7, 0xab } }; + +EFI_STATUS media_register(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle) +{ + return uefi_call_wrapper(st->BootServices->InstallProtocolInterface, 4, + handle, &media_guid, + EFI_NATIVE_INTERFACE, media); +} + +EFI_STATUS media_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + return interface_free(st, &media_guid, handle); +} diff --git a/libefiwrapper/media.h b/libefiwrapper/media.h new file mode 100644 index 0000000..c5aea93 --- /dev/null +++ b/libefiwrapper/media.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _MEDIA_H_ +#define _MEDIA_H_ + +#include +#include +#include + +typedef struct media { + EFI_BLOCK_IO_MEDIA m; + storage_t *storage; +} media_t; + +media_t *media_new(storage_t *storage); +EFI_STATUS media_register(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle); +EFI_STATUS media_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle); + +#endif /* _MEDIA_H_ */ diff --git a/libefiwrapper/protocol.c b/libefiwrapper/protocol.c new file mode 100644 index 0000000..b24a6a4 --- /dev/null +++ b/libefiwrapper/protocol.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include "lib.h" +#include "protocol.h" + +static EFI_GUID dp_guid = DEVICE_PATH_PROTOCOL; + +#define MAX_INTERFACE_NUMBER 32 + +typedef struct interface { + EFI_HANDLE handle; + EFI_GUID protocol; + VOID *interface; + BOOLEAN installed; +} interface_t; + +static interface_t INTERFACES[MAX_INTERFACE_NUMBER]; + +static EFIAPI EFI_STATUS +install_protocol_interface(EFI_HANDLE *Handle, + EFI_GUID *Protocol, + EFI_INTERFACE_TYPE InterfaceType, + VOID *Interface) +{ + interface_t *inte; + unsigned int i; + + if (!Handle || !Protocol || + InterfaceType != EFI_NATIVE_INTERFACE) + return EFI_INVALID_PARAMETER; + + if (*Handle) { + for (i = 0; i < ARRAY_SIZE(INTERFACES); i++) + if (INTERFACES[i].installed && + !guidcmp(&INTERFACES[i].protocol, Protocol) && + INTERFACES[i].handle == *Handle) + return EFI_INVALID_PARAMETER; + } + + for (i = 0; i < ARRAY_SIZE(INTERFACES); i++) { + inte = &INTERFACES[i]; + if (!inte->installed) { + if (*Handle) + inte->handle = *Handle; + else { + inte->handle = inte; + *Handle = inte->handle; + } + memcpy(&inte->protocol, Protocol, sizeof(*Protocol)); + inte->interface = Interface; + inte->installed = TRUE; + + return EFI_SUCCESS; + } + } + + return EFI_OUT_OF_RESOURCES; +} + +static EFIAPI EFI_STATUS +reinstall_protocol_interface(EFI_HANDLE Handle, + EFI_GUID *Protocol, + VOID *OldInterface, + VOID *NewInterface) +{ + unsigned int i; + + if (!Handle || !Protocol) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(INTERFACES); i++) + if (INTERFACES[i].installed && + INTERFACES[i].handle == Handle && + INTERFACES[i].interface == OldInterface) { + INTERFACES[i].interface = NewInterface; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +static EFIAPI EFI_STATUS +uninstall_protocol_interface(EFI_HANDLE Handle, + EFI_GUID *Protocol, + VOID *Interface) +{ + interface_t *inte; + unsigned int i; + + if (!Handle || !Protocol) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(INTERFACES); i++) { + inte = &INTERFACES[i]; + if (inte->installed && inte->handle == Handle && + !guidcmp(&inte->protocol, Protocol) && + inte->interface == Interface) { + inte->installed = FALSE; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +static EFIAPI EFI_STATUS +handle_protocol(EFI_HANDLE Handle, + EFI_GUID *Protocol, + VOID **Interface) +{ + interface_t *inte; + unsigned int i; + + if (!Handle || !Protocol || !Interface) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(INTERFACES); i++) { + inte = &INTERFACES[i]; + if (inte->installed && + inte->handle == Handle && + !guidcmp(&inte->protocol, Protocol)) { + *Interface = inte->interface; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +static EFIAPI EFI_STATUS +locate_handle(EFI_LOCATE_SEARCH_TYPE SearchType, + EFI_GUID *Protocol, + __attribute__((__unused__)) VOID *SearchKey, + UINTN *BufferSize, + EFI_HANDLE *Buffer) +{ + interface_t *inte; + unsigned int i, nb = 0; + + if (!Protocol || !BufferSize || !Buffer) + return EFI_INVALID_PARAMETER; + + if (SearchType != AllHandles && + SearchType != ByRegisterNotify && + SearchType != ByProtocol) + return EFI_INVALID_PARAMETER; + + if (SearchType == ByRegisterNotify) + return EFI_UNSUPPORTED; + + for (i = 0; i < ARRAY_SIZE(INTERFACES); i++) { + inte = &INTERFACES[i]; + if (!inte->installed) + continue; + + if (SearchType == ByProtocol && + guidcmp(&inte->protocol, Protocol)) + continue; + + if ((nb + 1) * sizeof(*Buffer) > *BufferSize) + return EFI_BUFFER_TOO_SMALL; + + Buffer[nb++] = inte->handle; + } + + if (nb == 0) + return EFI_NOT_FOUND; + + *BufferSize = nb * sizeof(*Buffer); + + return EFI_SUCCESS; +} + +static int dpcmp(EFI_DEVICE_PATH *p1, EFI_DEVICE_PATH *p2) +{ + int cmp; + + while (!IsDevicePathEndType(p1) && !IsDevicePathEndType(p2)) { + if (DevicePathNodeLength(p1) < DevicePathNodeLength(p2)) + return -1; + if (DevicePathNodeLength(p1) > DevicePathNodeLength(p2)) + return 1; + cmp = memcmp(p1, p2, DevicePathNodeLength(p1)); + if (cmp) + return cmp; + p1 = NextDevicePathNode(p1); + p2 = NextDevicePathNode(p2); + } + + return !(IsDevicePathEndType(p1) && IsDevicePathEndType(p2)); +} + +static EFIAPI EFI_STATUS +locate_handle_buffer(EFI_LOCATE_SEARCH_TYPE SearchType, + EFI_GUID *Protocol, + VOID *SearchKey, + UINTN *NoHandles, + EFI_HANDLE **Buffer); + +static EFIAPI EFI_STATUS +locate_device_path(EFI_GUID *Protocol, + EFI_DEVICE_PATH **DevicePath, + EFI_HANDLE *Device) +{ + EFI_STATUS ret; + UINTN nb_handle, i; + EFI_HANDLE *handles; + EFI_DEVICE_PATH *path; + + if (!Protocol || !DevicePath || !*DevicePath) + return EFI_INVALID_PARAMETER; + + ret = locate_handle_buffer(ByProtocol, Protocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret) || nb_handle == 0) + return EFI_NOT_FOUND; + + for (i = 0; i < nb_handle; i++) { + ret = handle_protocol(handles[i], &dp_guid, (VOID **)&path); + if (EFI_ERROR(ret)) + continue; + + if (path == *DevicePath || !dpcmp(*DevicePath, path)) { + *Device = handles[i]; + free(handles); + return EFI_SUCCESS; + } + } + free(handles); + + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +open_protocol(EFI_HANDLE Handle, + EFI_GUID *Protocol, + VOID **Interface, + __attribute__((__unused__)) EFI_HANDLE AgentHandle, + EFI_HANDLE ControllerHandle, + __attribute__((__unused__)) UINT32 Attributes) +{ + if (ControllerHandle != NULL) + return EFI_UNSUPPORTED; + + return handle_protocol(Handle, Protocol, Interface); +} + +static EFIAPI EFI_STATUS +close_protocol(__attribute__((__unused__)) EFI_HANDLE Handle, + __attribute__((__unused__)) EFI_GUID *Protocol, + __attribute__((__unused__)) EFI_HANDLE AgentHandle, + __attribute__((__unused__)) EFI_HANDLE ControllerHandle) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +protocols_per_handle(__attribute__((__unused__)) EFI_HANDLE Handle, + __attribute__((__unused__)) EFI_GUID ***ProtocolBuffer, + __attribute__((__unused__)) UINTN *ProtocolBufferCount) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +locate_handle_buffer(EFI_LOCATE_SEARCH_TYPE SearchType, + EFI_GUID *Protocol, + __attribute__((__unused__)) VOID *SearchKey, + UINTN *NoHandles, + EFI_HANDLE **Buffer) +{ + interface_t *inte; + unsigned int i, nb, cur; + EFI_HANDLE *buf; + + if (!Protocol || !NoHandles || !Buffer) + return EFI_INVALID_PARAMETER; + + if (SearchType != AllHandles && + SearchType != ByRegisterNotify && + SearchType != ByProtocol) + return EFI_INVALID_PARAMETER; + + if (SearchType == ByRegisterNotify) + return EFI_UNSUPPORTED; + + for (i = 0, nb = 0; i < ARRAY_SIZE(INTERFACES); i++) { + inte = &INTERFACES[i]; + if (!inte->installed) + continue; + + if (SearchType == ByProtocol && + !guidcmp(&inte->protocol, Protocol)) + nb++; + } + + if (nb == 0) + return EFI_NOT_FOUND; + + buf = malloc(sizeof(EFI_HANDLE) * nb); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + for (i = 0, cur = 0; i < ARRAY_SIZE(INTERFACES); i++) { + inte = &INTERFACES[i]; + if (!inte->installed) + continue; + + if (SearchType == ByProtocol && + guidcmp(&inte->protocol, Protocol)) + continue; + + buf[cur++] = inte->handle; + } + + *NoHandles = nb; + *Buffer = buf; + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +locate_protocol(__attribute__((__unused__)) EFI_GUID *Protocol, + __attribute__((__unused__)) VOID *Registration, + __attribute__((__unused__)) VOID **Interface) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS protocol_init_bs(EFI_BOOT_SERVICES *bs) +{ + if (!bs) + return EFI_INVALID_PARAMETER; + + bs->InstallProtocolInterface = install_protocol_interface; + bs->ReinstallProtocolInterface = reinstall_protocol_interface; + bs->UninstallProtocolInterface = uninstall_protocol_interface; + bs->HandleProtocol = handle_protocol; + bs->LocateHandle = locate_handle; + bs->LocateDevicePath = locate_device_path; + bs->ProtocolsPerHandle = protocols_per_handle; + bs->LocateHandleBuffer = locate_handle_buffer; + bs->LocateProtocol = locate_protocol; + bs->OpenProtocol = open_protocol; + bs->CloseProtocol = close_protocol; + + return EFI_SUCCESS; +} diff --git a/libefiwrapper/protocol.h b/libefiwrapper/protocol.h new file mode 100644 index 0000000..0b0ed8a --- /dev/null +++ b/libefiwrapper/protocol.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _PROTOCOL_H_ +#define _PROTOCOL_H_ + +#include +#include + +EFI_STATUS protocol_init_bs(EFI_BOOT_SERVICES *bs); + +#endif /* _PROTOCOL_H_ */ diff --git a/libefiwrapper/rs.c b/libefiwrapper/rs.c new file mode 100644 index 0000000..fbe423d --- /dev/null +++ b/libefiwrapper/rs.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "ewvar.h" +#include "lib.h" +#include "rs.h" + +static EFIAPI EFI_STATUS +rs_get_variable(CHAR16 *VariableName, EFI_GUID *VendorGuid, UINT32 *Attributes, + UINTN *DataSize, VOID *Data) +{ + ewvar_t *var; + + if (!VariableName || !VendorGuid || !Attributes || !DataSize || !Data) + return EFI_INVALID_PARAMETER; + + var = ewvar_get(VariableName, VendorGuid, NULL); + if (!var) + return EFI_NOT_FOUND; + + if (var->size > *DataSize) { + *DataSize = var->size; + return EFI_BUFFER_TOO_SMALL; + } + + *Attributes = var->attributes; + *DataSize = var->size; + memcpy(Data, var->data, var->size); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +rs_get_next_variable_name(UINTN *VariableNameSize, + CHAR16 *VariableName, + EFI_GUID *VendorGuid) +{ + ewvar_t *var; + size_t name_size; + + if (!VariableNameSize || !VariableName || !VendorGuid) + return EFI_INVALID_PARAMETER; + + if (VariableName[0] == '\0') + var = ewvar_get_first(); + else { + var = ewvar_get(VariableName, VendorGuid, NULL); + var = var ? var->next : NULL; + } + + if (!var) + return EFI_NOT_FOUND; + + name_size = (str16len(var->name) + 1) * sizeof(*var->name); + if (name_size > *VariableNameSize) { + *VariableNameSize = name_size; + return EFI_BUFFER_TOO_SMALL; + } + + memcpy(VariableName, var->name, name_size); + memcpy(VendorGuid, &var->guid, sizeof(var->guid)); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +rs_set_variable(CHAR16 *VariableName, EFI_GUID *VendorGuid, UINT32 Attributes, + UINTN DataSize, VOID *Data) +{ + EFI_STATUS ret; + ewvar_t *var, *prev; + + if (!VariableName || !VendorGuid) + return EFI_INVALID_PARAMETER; + + if (Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS || + Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) + return EFI_UNSUPPORTED; + + var = ewvar_get(VariableName, VendorGuid, &prev); + + if (!Data) { + if (!var) + return EFI_NOT_FOUND; + return ewvar_del(var, prev); + } + + if (var) { + if (Attributes != var->attributes) + return EFI_INVALID_PARAMETER; + + ret = ewvar_update(var, DataSize, Data); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; + } + + var = ewvar_new(VariableName, VendorGuid, Attributes, DataSize, Data); + if (!var) + return EFI_OUT_OF_RESOURCES; + + ewvar_add(var); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +rs_get_time(__attribute__((__unused__)) EFI_TIME *Time, + __attribute__((__unused__)) EFI_TIME_CAPABILITIES *Capabilities) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_set_time(__attribute__((__unused__)) EFI_TIME *Time) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_get_wakeup_time(__attribute__((__unused__)) BOOLEAN *Enabled, + __attribute__((__unused__)) BOOLEAN *Pending, + __attribute__((__unused__)) EFI_TIME *Time) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_set_wakeup_time(__attribute__((__unused__)) BOOLEAN Enable, + __attribute__((__unused__)) EFI_TIME *Time) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_set_virtual_address_map(__attribute__((__unused__)) UINTN MemoryMapSize, + __attribute__((__unused__)) UINTN DescriptorSize, + __attribute__((__unused__)) UINT32 DescriptorVersion, + __attribute__((__unused__)) EFI_MEMORY_DESCRIPTOR *VirtualMap) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_convert_pointer(__attribute__((__unused__)) UINTN DebugDisposition, + __attribute__((__unused__)) VOID **Address) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_get_next_high_monotonic_count(__attribute__((__unused__)) UINT32 *HighCount) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_reset_system(__attribute__((__unused__)) EFI_RESET_TYPE ResetType, + __attribute__((__unused__)) EFI_STATUS ResetStatus, + __attribute__((__unused__)) UINTN DataSize, + __attribute__((__unused__)) CHAR16 *ResetData) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_update_capsule(__attribute__((__unused__)) EFI_CAPSULE_HEADER **CapsuleHeaderArray, + __attribute__((__unused__)) UINTN CapsuleCount, + __attribute__((__unused__)) EFI_PHYSICAL_ADDRESS ScatterGatherList) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_query_capsule_capabilities(__attribute__((__unused__)) EFI_CAPSULE_HEADER **CapsuleHeaderArray, + __attribute__((__unused__)) UINTN CapsuleCount, + __attribute__((__unused__)) UINT64 *MaximumCapsuleSize, + __attribute__((__unused__)) EFI_RESET_TYPE *ResetType) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +rs_query_variable_info(__attribute__((__unused__)) UINT32 Attributes, + __attribute__((__unused__)) UINT64 *MaximumVariableStorageSize, + __attribute__((__unused__)) UINT64 *RemainingVariableStorageSize, + __attribute__((__unused__)) UINT64 *MaximumVariableSize) +{ + return EFI_UNSUPPORTED; +} + +static EFI_RUNTIME_SERVICES runtime_services_default = { + .Hdr = { + .Signature = EFI_RUNTIME_SERVICES_SIGNATURE, + .Revision = EFI_RUNTIME_SERVICES_REVISION, + .HeaderSize = sizeof(EFI_TABLE_HEADER) + }, + .GetTime = rs_get_time, + .SetTime = rs_set_time, + .GetWakeupTime = rs_get_wakeup_time, + .SetWakeupTime = rs_set_wakeup_time, + .SetVirtualAddressMap = rs_set_virtual_address_map, + .ConvertPointer = rs_convert_pointer, + .GetVariable = rs_get_variable, + .GetNextVariableName = rs_get_next_variable_name, + .SetVariable = rs_set_variable, + .GetNextHighMonotonicCount = rs_get_next_high_monotonic_count, + .ResetSystem = rs_reset_system, + .UpdateCapsule = rs_update_capsule, + .QueryCapsuleCapabilities = rs_query_capsule_capabilities, + .QueryVariableInfo = rs_query_variable_info +}; + +EFI_STATUS rs_init(EFI_SYSTEM_TABLE *st) +{ + EFI_RUNTIME_SERVICES *rs; + + if (!st || !st->RuntimeServices) + return EFI_INVALID_PARAMETER; + + rs = st->RuntimeServices; + memcpy(rs, &runtime_services_default, sizeof(*rs)); + + return crc32((void *)rs, sizeof(*rs), &rs->Hdr.CRC32); +} diff --git a/libefiwrapper/rs.h b/libefiwrapper/rs.h new file mode 100644 index 0000000..9e2bddb --- /dev/null +++ b/libefiwrapper/rs.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _RS_H_ +#define _RS_H_ + +#include +#include + +EFI_STATUS rs_init(EFI_SYSTEM_TABLE *st); + +#endif /* _RS_H_ */ diff --git a/libefiwrapper/sdio.c b/libefiwrapper/sdio.c new file mode 100644 index 0000000..95d6f46 --- /dev/null +++ b/libefiwrapper/sdio.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include + +#include "protocol/SdHostIo.h" +#include "sdio.h" + +typedef struct sdio { + EFI_SD_HOST_IO_PROTOCOL interface; + storage_t *s; +} sdio_t; + +static EFIAPI EFI_STATUS +sdio_send_command(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT16 CommandIndex, + __attribute__((__unused__)) UINT32 Argument, + __attribute__((__unused__)) TRANSFER_TYPE DataType, + __attribute__((__unused__)) UINT8 *Buffer, + __attribute__((__unused__)) UINT32 BufferSize, + __attribute__((__unused__)) RESPONSE_TYPE ResponseType, + __attribute__((__unused__)) UINT32 TimeOut, + __attribute__((__unused__)) UINT32 *ResponseData) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_set_clock_frequency(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT32 MaxFrequency) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_set_bus_width(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT32 BusWidth) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_set_host_voltage(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT32 Voltage) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_set_host_speed_mode(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT32 HighSpeed) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_set_host_ddr_mode(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT32 DdrMode) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_reset_sd_host(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) RESET_TYPE ResetType) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_enable_auto_stop_cmd(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) BOOLEAN Enable) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_detect_card_and_init_host(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_set_block_length(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This, + __attribute__((__unused__)) UINT32 BlockLength) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +sdio_setup_device(__attribute__((__unused__)) EFI_SD_HOST_IO_PROTOCOL *This) +{ + return EFI_UNSUPPORTED; +} + +static EFI_GUID sdio_guid = EFI_SD_HOST_IO_PROTOCOL_GUID; + +EFI_STATUS sdio_init(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle, storage_t *s) +{ + EFI_STATUS ret; + static sdio_t sdio_default = { + .interface = { + .Revision = 0x1, + .HostCapability = { + .BoundarySize = 8 + }, + .SendCommand = sdio_send_command, + .SetClockFrequency = sdio_set_clock_frequency, + .SetBusWidth = sdio_set_bus_width, + .SetHostVoltage = sdio_set_host_voltage, + .SetHostDdrMode = sdio_set_host_ddr_mode, + .ResetSdHost = sdio_reset_sd_host, + .EnableAutoStopCmd = sdio_enable_auto_stop_cmd, + .DetectCardAndInitHost =sdio_detect_card_and_init_host, + .SetBlockLength = sdio_set_block_length, + .SetupDevice = sdio_setup_device, + .SetHostSpeedMode = sdio_set_host_speed_mode + } + }; + sdio_t *sdio; + + ret = interface_init(st, &sdio_guid, &handle, + &sdio_default, sizeof(sdio_default), + (void **)&sdio); + if (EFI_ERROR(ret)) + return ret; + + sdio->s = s; + + return EFI_SUCCESS; +} + +EFI_STATUS sdio_get_storage(EFI_SD_HOST_IO_PROTOCOL *This, storage_t **storage_p) +{ + sdio_t *sdio = (sdio_t *)This; + + if (!This) + return EFI_INVALID_PARAMETER; + + *storage_p = sdio->s; + + return EFI_SUCCESS; +} + +EFI_STATUS sdio_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + return interface_free(st, &sdio_guid, handle); +} diff --git a/libefiwrapper/serialio.c b/libefiwrapper/serialio.c new file mode 100644 index 0000000..0e206f9 --- /dev/null +++ b/libefiwrapper/serialio.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "external.h" +#include "interface.h" +#include "serialio.h" + +static EFIAPI EFI_STATUS +serialio_reset(__attribute__((__unused__)) SERIAL_IO_INTERFACE *This) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +serialio_write(__attribute__((__unused__)) SERIAL_IO_INTERFACE *This, + UINTN *BufferSize, VOID *Buffer) +{ + size_t i; + + if (!This || !BufferSize || !Buffer) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < *BufferSize; i++) + printf("%c", ((char *)Buffer)[i]); + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +serialio_set_attributes(__attribute__((__unused__)) SERIAL_IO_INTERFACE *This, + __attribute__((__unused__)) UINT64 BaudRate, + __attribute__((__unused__)) UINT32 ReceiveFifoDepth, + __attribute__((__unused__)) UINT32 Timeout, + __attribute__((__unused__)) EFI_PARITY_TYPE Parity, + __attribute__((__unused__)) UINT8 DataBits, + __attribute__((__unused__)) EFI_STOP_BITS_TYPE StopBits) +{ + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS +serialio_set_control(__attribute__((__unused__)) SERIAL_IO_INTERFACE *This, + __attribute__((__unused__)) UINT32 Control) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +serialio_get_control(__attribute__((__unused__)) SERIAL_IO_INTERFACE *This, + __attribute__((__unused__)) UINT32 *Control) +{ + return EFI_UNSUPPORTED; +} + +static EFIAPI EFI_STATUS +serialio_read(__attribute__((__unused__)) SERIAL_IO_INTERFACE *This, + __attribute__((__unused__)) UINTN *BufferSize, + __attribute__((__unused__)) VOID *Buffer) +{ + return EFI_UNSUPPORTED; +} + +static SERIAL_IO_MODE io_mode; +static EFI_GUID serialio_guid = SERIAL_IO_PROTOCOL; +static EFI_HANDLE handle; + +EFI_STATUS serialio_init(EFI_SYSTEM_TABLE *st) +{ + static SERIAL_IO_INTERFACE serialio_default = { + .Revision = SERIAL_IO_INTERFACE_REVISION, + .Reset = serialio_reset, + .SetAttributes = serialio_set_attributes, + .SetControl = serialio_set_control, + .GetControl = serialio_get_control, + .Write = serialio_write, + .Read = serialio_read, + .Mode = &io_mode + }; + SERIAL_IO_INTERFACE *serialio; + + if (!st) + return EFI_INVALID_PARAMETER; + + if (handle) + return EFI_ALREADY_STARTED; + + return interface_init(st, &serialio_guid, &handle, + &serialio_default, sizeof(serialio_default), + (void **)&serialio); +} + +EFI_STATUS serialio_free(EFI_SYSTEM_TABLE *st) +{ + if (!handle) + return EFI_INVALID_PARAMETER; + + return interface_free(st, &serialio_guid, handle); +} diff --git a/libefiwrapper/serialio.h b/libefiwrapper/serialio.h new file mode 100644 index 0000000..75f6a9e --- /dev/null +++ b/libefiwrapper/serialio.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _SERIALIO_H_ +#define _SERIALIO_H_ + +#include +#include + +EFI_STATUS serialio_init(EFI_SYSTEM_TABLE *st); +EFI_STATUS serialio_free(EFI_SYSTEM_TABLE *st); + +#endif /* _SERIALIO_H_ */ diff --git a/libefiwrapper/smbios.c b/libefiwrapper/smbios.c new file mode 100644 index 0000000..30d1c37 --- /dev/null +++ b/libefiwrapper/smbios.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#if defined(HOST) && defined(__LP64__) +#define HOST_64 +#endif + +#ifdef HOST_64 +#define _GNU_SOURCE +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "conf_table.h" +#include "external.h" +#include "lib.h" +#include "smbios.h" +#include "version.h" + +static EFI_GUID smbios_guid = SMBIOS_TABLE_GUID; + +#define MAX_SMBIOS_FIELD 32 + +static struct { + struct type0 { + SMBIOS_TYPE0 type0; + char vendor[MAX_SMBIOS_FIELD]; + char unused0[2]; + char bios_version[MAX_SMBIOS_FIELD]; + char unused1[2]; + char end; + } __attribute__((__packed__)) type0; + struct type1 { + SMBIOS_TYPE1 type1; + char serial_number[MAX_SMBIOS_FIELD]; + char unused0[2]; + char product_name[MAX_SMBIOS_FIELD]; + char unused1[2]; + char version[MAX_SMBIOS_FIELD]; + char unused2[2]; + char end; + } __attribute__((__packed__)) type1; + struct type2 { + SMBIOS_TYPE2 type2; + char manufacturer[MAX_SMBIOS_FIELD]; + char unused0[2]; + char product_name[MAX_SMBIOS_FIELD]; + char unused1[2]; + char version[MAX_SMBIOS_FIELD]; + char unused2[2]; + char end; + } __attribute__((__packed__)) type2; +} __attribute__((__packed__,aligned(4096))) smbios_table = { + { + .type0 = { + .Hdr = { + .Type = 0, + .Length = sizeof(SMBIOS_TYPE0) + }, + .Vendor = 1, + .BiosVersion = 3 + }, + .unused0 = " ", + .unused1 = " " + }, + { + .type1 = { + .Hdr = { + .Type = 1, + .Length = sizeof(SMBIOS_TYPE1) + }, + .SerialNumber = 1, + .ProductName = 3, + .Version = 5 + }, + .unused0 = " ", + .unused1 = " ", + .unused2 = " " + }, + { + .type2 = { + .Hdr = { + .Type = 2, + .Length = sizeof(SMBIOS_TYPE2) + }, + .Manufacturer = 1, + .ProductName = 3, + .Version = 5 + }, + .unused0 = " ", + .unused1 = " ", + .unused2 = " " + } +}; + +static SMBIOS_STRUCTURE_TABLE smbios = { + .AnchorString = "_SM_", + .EntryPointLength = sizeof(SMBIOS_STRUCTURE_TABLE), + .MajorVersion = 2, + .MinorVersion = 2, + .IntermediateAnchorString = "_DMI_", + .TableLength = 3 +}; + +static UINT8 checksum(UINT8 *buf, size_t size) +{ + UINT8 sum; + size_t i; + + for (sum = 0, i = 0; i < size; i++) + sum += buf[i]; + + return !sum ? 0 : 0x100 - sum; +} + +static EFI_STATUS set_field(char *field, const char *value) +{ + size_t len; + + len = strlen(value); + if (len == 0) + return EFI_INVALID_PARAMETER; + if (len > MAX_SMBIOS_FIELD - 1) + return EFI_BUFFER_TOO_SMALL; + + memcpy(field, value, len + 1); + memset(field + len + 1, ' ', MAX_SMBIOS_FIELD - len - 1); + + return EFI_SUCCESS; +} + +static const struct { + char *field; + const char *value; +} SMBIOS_DEFAULT[] = { + { smbios_table.type0.vendor, PRODUCT_MANUFACTURER }, + { smbios_table.type0.bios_version, EFIWRAPPER_VERSION }, + { smbios_table.type1.serial_number, SMBIOS_UNDEFINED }, + { smbios_table.type1.product_name, PRODUCT_NAME }, + { smbios_table.type1.version, SMBIOS_UNDEFINED }, + { smbios_table.type2.manufacturer, PRODUCT_MANUFACTURER }, + { smbios_table.type2.product_name, PRODUCT_NAME }, + { smbios_table.type2.version, SMBIOS_UNDEFINED }, +}; + +EFI_STATUS smbios_init(EFI_SYSTEM_TABLE *st) +{ + EFI_STATUS ret; + EFI_CONFIGURATION_TABLE *table; + size_t i; +#ifdef HOST_64 + void *addr; +#endif + + if (!st) + return EFI_INVALID_PARAMETER; + + ret = conf_table_new(st, &smbios_guid, &table); + if (EFI_ERROR(ret)) + return ret; + + for (i = 0; i < ARRAY_SIZE(SMBIOS_DEFAULT); i++) { + ret = set_field(SMBIOS_DEFAULT[i].field, SMBIOS_DEFAULT[i].value); + if (EFI_ERROR(ret)) + return ret; + } + + table->VendorTable = &smbios; +#ifdef HOST_64 + /* smbios.TableAddress is a UINT32 field and cannot hold a 64 + bits address, we remap the smbios_table to an arbitrary + address. */ +#define SMBIOS_ADDRESS 0x10000 + addr = mremap(&smbios_table, sizeof(smbios_table), sizeof(smbios_table), + MREMAP_MAYMOVE | MREMAP_FIXED, SMBIOS_ADDRESS); + if (addr != (void *)SMBIOS_ADDRESS) { + ewerr("remaping of SMBIOS table failed failed, %s", + strerror(errno)); + return EFI_DEVICE_ERROR; + } + smbios.TableAddress = SMBIOS_ADDRESS; +#else + smbios.TableAddress = (UINTN)&smbios_table; +#endif + smbios.EntryPointStructureChecksum = 0; + smbios.EntryPointStructureChecksum = checksum((UINT8 *)&smbios, sizeof(smbios)); + + return EFI_SUCCESS; +} + +EFI_STATUS smbios_free(EFI_SYSTEM_TABLE *st) +{ + return conf_table_free(st, &smbios_guid); +} + +static char *get_table_field(SMBIOS_HEADER *hdr, UINT8 field) +{ + UINT8 i; + char *str; + + if (field == 0 || hdr >= (SMBIOS_HEADER *)(&smbios_table + 1)) + return NULL; + + str = (char *)hdr + hdr->Length; + for (i = 1; i < field; i++) { + while (*str) + str++; + + if (*(++str)) + continue; + + return field == (UINT8)-1 ? str + 1 : NULL; + } + + return str; +} + +static char *get_field(UINT8 type, UINT8 offset) +{ + SMBIOS_HEADER *hdr; + + hdr = (SMBIOS_HEADER *)&smbios_table; + while (hdr && hdr->Type != type) + hdr = (SMBIOS_HEADER *)get_table_field(hdr, -1); + + if (!hdr) + return NULL; + + return get_table_field(hdr, ((UINT8 *)hdr)[offset]); +} + +EFI_STATUS smbios_set(UINT8 type, UINT8 offset, const char *value) +{ + char *field; + + field = get_field(type, offset); + if (!field) + return EFI_NOT_FOUND; + + return set_field(field, value); +} diff --git a/libefiwrapper/storage.c b/libefiwrapper/storage.c new file mode 100755 index 0000000..963b24c --- /dev/null +++ b/libefiwrapper/storage.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include "blockio.h" +#include "diskio.h" +#include "eraseblk.h" +#include "external.h" +#include "lib.h" +#include "media.h" +#include "interface.h" +#include "ewlog.h" +#include "ewarg.h" + +#include +#include + +#ifndef MSG_UFS_DP +#define MSG_UFS_DP 0x19 +#endif + +#ifndef MSG_NVME_DP +#define MSG_NVME_DP 0x17 +#endif + +#ifndef MSG_EMMC_DP +#define MSG_EMMC_DP 29 +#endif + +#ifndef MSG_VIRTUAL_MEDIA_DP +#define MSG_VIRTUAL_MEDIA_DP 0x20 +#endif + +static EFI_GUID dp_guid = DEVICE_PATH_PROTOCOL; + +#define ABL_BDEV "ABL.bdev" +#define ABL_DISKBUS "ABL.diskbus" +#define ABL_BDEVLIST "ABL.bootdevices" + +static boot_dev_t boot_dev = { + .type = STORAGE_EMMC, + .diskbus = 0 +}; + +/* Device path */ +struct storage_dp { + PCI_DEVICE_PATH pci; + CONTROLLER_DEVICE_PATH ctrl; + SCSI_DEVICE_PATH msg_device_path; + EFI_DEVICE_PATH end; +} __attribute__((__packed__)); + +typedef struct { + // Boot medium type, Refer OS_BOOT_MEDIUM_TYPE + UINT8 DevType; + //Zero-based hardware partition number + UINT8 HwPart; + //For PCI device, its value is 0x00BBDDFF + //For other device, its value is MMIO address. + UINT32 DevAddr; +} __attribute__((__packed__)) OS_BOOT_DEVICE; + +typedef struct { + UINT8 Revision; + UINT8 BootDeviceCount; + OS_BOOT_DEVICE BootDevice[0]; +}__attribute__((__packed__)) OS_BOOT_DEVICE_LIST; + +static EFI_STATUS dp_init(EFI_SYSTEM_TABLE *st, media_t *media, + EFI_HANDLE *handle) +{ + EFI_STATUS ret; + struct storage_dp *dp; + + dp = calloc(1, sizeof(struct storage_dp)); + if (!dp) + return EFI_OUT_OF_RESOURCES; + + dp->pci.Header.Type = HARDWARE_DEVICE_PATH; + dp->pci.Header.SubType = HW_PCI_DP; + dp->pci.Function = media->storage->pci_function; + dp->pci.Device = media->storage->pci_device; + SetDevicePathNodeLength(&dp->pci.Header, sizeof(dp->pci)); + + dp->ctrl.Header.Type = HARDWARE_DEVICE_PATH; + dp->ctrl.Header.SubType = HW_CONTROLLER_DP; + SetDevicePathNodeLength(&dp->ctrl.Header, sizeof(dp->ctrl)); + + dp->msg_device_path.Header.SubType = get_boot_media_device_path_type(); + dp->msg_device_path.Header.Type = MESSAGING_DEVICE_PATH; + dp->msg_device_path.Pun = 0; + dp->msg_device_path.Lun = 0; + SetDevicePathNodeLength(&dp->msg_device_path.Header, sizeof(dp->msg_device_path)); + + dp->end.Type = END_DEVICE_PATH_TYPE; + dp->end.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + SetDevicePathNodeLength(&dp->end, sizeof(dp->end)); + + ret = uefi_call_wrapper(st->BootServices->InstallProtocolInterface, 4, + handle, &dp_guid, + EFI_NATIVE_INTERFACE, dp); + if (EFI_ERROR(ret)) + free(dp); + + return ret; +} + +EFI_STATUS dp_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + return interface_free(st, &dp_guid, handle); +} + +static struct storage_interface { + const char *name; + EFI_STATUS (*init)(EFI_SYSTEM_TABLE *, media_t *, EFI_HANDLE *); + EFI_STATUS (*free)(EFI_SYSTEM_TABLE *, EFI_HANDLE); +} STORAGE_INTERFACES[] = { + { "media", media_register, media_free }, + { "device path", dp_init, dp_free }, + { "blockio", blockio_init, blockio_free }, + { "diskio", diskio_init, diskio_free }, + { "eraseblock", erase_block_init, erase_block_free } +}; + +EFI_STATUS storage_init(EFI_SYSTEM_TABLE *st, storage_t *storage, + EFI_HANDLE *handle) +{ + EFI_STATUS ret, tmp_ret; + size_t i, j; + media_t *media; + + if (!st || !storage || !handle) + return EFI_INVALID_PARAMETER; + + if (storage->init) { + ret = storage->init(storage); + if (EFI_ERROR(ret)) + return ret; + } + + media = media_new(storage); + if (!media) + return EFI_OUT_OF_RESOURCES; + + *handle = NULL; + for (i = 0; i < ARRAY_SIZE(STORAGE_INTERFACES); i++) { + ret = STORAGE_INTERFACES[i].init(st, media, handle); + if (EFI_ERROR(ret)) { + ewerr("Failed to register %s interface", + STORAGE_INTERFACES[i].name); + goto err; + } + } + + return EFI_SUCCESS; + +err: + for (j = 0; j < i; j++) { + tmp_ret = STORAGE_INTERFACES[i].free(st, handle); + if (EFI_ERROR(tmp_ret)) + ewerr("Failed to unregister %s interface", + STORAGE_INTERFACES[i].name); + } + free(media); + return ret; +} + +EFI_STATUS storage_free(EFI_SYSTEM_TABLE *st, EFI_HANDLE handle) +{ + EFI_STATUS ret; + size_t i; + + if (!st || !handle) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(STORAGE_INTERFACES); i++) { + ret = STORAGE_INTERFACES[i].free(st, handle); + if (EFI_ERROR(ret)) { + ewerr("Failed to unregister %s interface", + STORAGE_INTERFACES[i].name); + return ret; + } + } + + return EFI_SUCCESS; +} + +static enum storage_type convert_sbl_dev_type(SBL_OS_BOOT_MEDIUM_TYPE type) +{ + switch(type) { + case OsBootDeviceUfs: + return STORAGE_UFS; + case OsBootDeviceSata: + return STORAGE_SATA; + case OsBootDeviceNvme: + return STORAGE_NVME; + default: + return STORAGE_EMMC; + } +} + +EFI_STATUS identify_flash_media(boot_dev_t* pdev) +{ + const char *val; + OS_BOOT_DEVICE_LIST* plist; + SBL_OS_BOOT_MEDIUM_TYPE type; + + ewdbg("identify_flash_media"); + val = ewarg_getval(ABL_BDEVLIST); + if (!val) { + ewdbg("No devlist, select default"); + return EFI_SUCCESS; + } + + plist = (OS_BOOT_DEVICE_LIST*)(UINTN)strtoull(val, NULL, 16); + + if(plist->BootDeviceCount < 1) { + ewdbg("devlist count < 1, select default"); + return EFI_SUCCESS; + } + + type = (SBL_OS_BOOT_MEDIUM_TYPE)(plist->BootDevice[0].DevType); + + if ((type != OsBootDeviceSpi) && (type != OsBootDeviceMemory)) { + ewdbg("Select 1st boot dev"); + pdev->type = convert_sbl_dev_type(type); + pdev->diskbus = plist->BootDevice[0].DevAddr; + return EFI_SUCCESS; + } + + //1st boot dev is SPI, check 2nd dev instead... + + if(plist->BootDeviceCount < 2) { + ewdbg("devlist count < 2, select default"); + return EFI_SUCCESS; + } + + ewdbg("Select 2nd boot dev"); + type = (SBL_OS_BOOT_MEDIUM_TYPE)(plist->BootDevice[1].DevType); + pdev->type = convert_sbl_dev_type(type); + pdev->diskbus = plist->BootDevice[1].DevAddr; + + return EFI_SUCCESS; +} + +EFI_STATUS identify_boot_media() +{ + const char *val; + size_t len; + + val = ewarg_getval(ABL_BDEV); + if (!val) + return EFI_SUCCESS; + + len = strlen(val); + if (!strncmp(val, "MMC", len)) + boot_dev.type = STORAGE_EMMC; + else if (!strncmp(val, "UFS", len)) + boot_dev.type = STORAGE_UFS; + else if (!strncmp(val, "NVME", len)) + boot_dev.type = STORAGE_NVME; + else if (!strncmp(val, "VIRTUAL", len)) + boot_dev.type = STORAGE_VIRTUAL; + else if ((!strncmp(val, "SPI", len)) || (!strncmp(val, "MEM", len))) //Fastboot case + identify_flash_media(&boot_dev); + + //if diskbus is already get from boot option list + if (boot_dev.diskbus != 0) + return EFI_SUCCESS; + + val = ewarg_getval(ABL_DISKBUS); + if (!val) + return EFI_SUCCESS; + + boot_dev.diskbus = (UINT32)strtoull(val, NULL, 16); + + return EFI_SUCCESS; +} + +boot_dev_t* get_boot_media() +{ + return &boot_dev; +} + +UINT8 get_boot_media_device_path_type(void) +{ + switch(boot_dev.type) + { + case STORAGE_EMMC: + return MSG_EMMC_DP; + + case STORAGE_UFS: + return MSG_UFS_DP; + + case STORAGE_NVME: + return MSG_NVME_DP; + + case STORAGE_VIRTUAL: + return MSG_VIRTUAL_MEDIA_DP; + + default: + break; + } + + return MSG_EMMC_DP; +} + diff --git a/libefiwrapper/version.h b/libefiwrapper/version.h new file mode 100644 index 0000000..8b73696 --- /dev/null +++ b/libefiwrapper/version.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#if defined(USER) +#define BUILD_VARIANT "" +#elif defined(USERDEBUG) +#define BUILD_VARIANT "-userdebug" +#else +#define BUILD_VARIANT "-eng" +#endif + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define PASTE(a,b) a##b +#define HEX(a) PASTE(0x, a) + +#define _EFIWRAPPER_MAJOR 02 +#define _EFIWRAPPER_MINOR 04 + +#define EFIWRAPPER_MAJOR HEX(_EFIWRAPPER_MAJOR) +#define EFIWRAPPER_MINOR HEX(_EFIWRAPPER_MINOR) + +#define EFIWRAPPER_VERSION ("efiwrapper-" \ + TOSTRING(_EFIWRAPPER_MAJOR) "." \ + TOSTRING(_EFIWRAPPER_MINOR) BUILD_VARIANT) + +#endif /* _VERSION_H_ */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..c9f39ba --- /dev/null +++ b/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Entry point */ +int main(int argc, char **argv) +{ + EFI_HANDLE image = NULL; + EFI_SYSTEM_TABLE *st; + EFI_STATUS ret; + + ret = efiwrapper_init(argc, argv, &st, &image); + if (ret) { + ewerr("efiwrapper library initialization failed"); + return EXIT_FAILURE; + } + + ret = ewdrv_init(st); + if (ret) { + ewerr("drivers initialization failed"); + return EXIT_FAILURE; + } + + ret = efi_main(image, st); + if (EFI_ERROR(ret)) + ewerr("The EFI program exited with error code: 0x%x", ret); + + ret = ewdrv_exit(st); + if (ret) + ewerr("drivers release failed"); + + ret = efiwrapper_free(image); + if (EFI_ERROR(ret)) + ewerr("efiwrapper library exit failed"); + + return EFI_ERROR(ret) ? EXIT_FAILURE : EXIT_SUCCESS; +}