pax_global_header00006660000000000000000000000064126520443620014516gustar00rootroot0000000000000052 comment=0671bb857f442efde618f1968c30e2b4828c6924 skiboot-skiboot-5.1.13/000077500000000000000000000000001265204436200147275ustar00rootroot00000000000000skiboot-skiboot-5.1.13/.gitignore000066400000000000000000000025211265204436200167170ustar00rootroot00000000000000*~ *.o *.d *.gcda *.gcno *.rej *.swp .version skiboot-nosection.elf skiboot*.elf skiboot.lds skiboot.lid skiboot*.map skiboot.info coverage-report/ extract-gcov TAGS tags cscope.out asm/asm-offsets.s include/asm-offsets.h version.c ccan/*/test/*-gcov ccan/*/test/run ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO ccan/list/test/run-check-corrupt ccan/list/test/run-list_del_from-assert ccan/list/test/run-single-eval ccan/list/test/run-with-debug ccan/str/test/run-STR_MAX_CHARS core/test/run-console-log core/test/run-time-utils core/test/run-timer core/test/run-console-log-buf-overrun core/test/run-console-log-pr_fmt core/test/run-mem_region_next core/test/run-mem_region_reservations ccan/*/test/gmon.out core/test/run-device core/test/run-malloc core/test/run-malloc-speed core/test/run-mem_region core/test/run-mem_region_init core/test/run-mem_region_release_unused core/test/run-mem_region_release_unused_noalloc core/test/run-msg core/test/run-nvram-format core/test/run-pel core/test/run-pool core/test/run-trace core/test/*-gcov external/dump_trace external/mambo/skiboot-hello_world.dump hdata/test/hdata_to_dt hw/ipmi/test/run-fru hw/ipmi/test/*-gcov libc/test/run-time libc/test/run-time-gcov libflash/test/test-blocklevel libflash/test/test-flash libflash/test/test-ecc libflash/test/test-flash-gcov test/hello_world/hello_kernel/hello_kernel skiboot-skiboot-5.1.13/.travis.yml000066400000000000000000000034161265204436200170440ustar00rootroot00000000000000language: c before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update -qq - sudo apt-get install -y gcc-4.8 libstdc++6 valgrind expect xterm - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50 - wget https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.8.0/x86_64-gcc-4.8.0-nolibc_powerpc64-linux.tar.xz - sudo mkdir /opt/cross - sudo tar -C /opt/cross -xvf x86_64-gcc-4.8.0-nolibc_powerpc64-linux.tar.xz - wget ftp://public.dhe.ibm.com/software/server/powerfuncsim/p8/packages/v1.0-2/systemsim-p8_1.0-2_amd64.deb - sudo dpkg -i systemsim-p8_1.0-2_amd64.deb env: global: - CROSS=/opt/cross/gcc-4.8.0-nolibc/powerpc64-linux/bin/powerpc64-linux- - HOSTCC=gcc-4.8 # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "MpNEGFa1VrF/vsQq24n5UgfRbz1wVC6B8mubFnyK4gX0IuQ9xhWuTzMLUQF9UJxe5jnC2DTmVUvYTYN/hggw+PpYwbOOAE0QGR5pmPHA4PSRmc5pxt1q18/sv7EPFw66GFyWJq94nWjpigyKQ8KGtA67j1xFqrDoS43OA76WZgo=" script: - make -j4 all check ; (make clean; cd external/gard && make) ; (make clean; cd external/pflash && make) - make clean && SKIBOOT_GCOV=1 make && SKIBOOT_GCOV=1 make check - make clean && rm -rf builddir && mkdir builddir && make SRC=`pwd` -f ../Makefile -C builddir - make clean addons: coverity_scan: project: name: "open-power/skiboot" description: "Build submitted via Travis CI" notification_email: stewart@linux.vnet.ibm.com build_command_prepend: "make clean; cov-configure --comptype gcc --compiler powerpc64-linux-gcc --template" build_command: "make -j4 all check gard" branch_pattern: coverity_scan skiboot-skiboot-5.1.13/LICENCE000066400000000000000000000261361265204436200157240ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. skiboot-skiboot-5.1.13/Makefile000066400000000000000000000015741265204436200163760ustar00rootroot00000000000000# If you want to build in another directory copy this file there and # fill in the following values # # Prefix of cross toolchain, if anything # Example: CROSS= powerpc64-unknown-linux-gnu- # ARCH = $(shell uname -m) ifdef CROSS_COMPILE CROSS ?= $(CROSS_COMPILE) endif ifeq ("$(ARCH)", "ppc64") CROSS ?= else CROSS ?= powerpc64-linux- endif # # Set to enable SLW bits # PORE ?= 1 # # Optional location of embedded linux kernel file # This can be a raw vmlinux, stripped vmlinux or # zImage.epapr # KERNEL ?= # # Optional build with advanced stack checking # STACK_CHECK ?= 0 # # Where is the source directory, must be a full path (no ~) # Example: SRC= /home/me/skiboot # SRC=$(CURDIR) # # Where to get information about this machine (subdir name) # DEVSRC=hdata # # default config file, see include config_*.h for more specifics # CONFIG := config.h include $(SRC)/Makefile.main skiboot-skiboot-5.1.13/Makefile.main000066400000000000000000000147011265204436200173150ustar00rootroot00000000000000# -*-Makefile-*- # # This is the main Makefile # Target tools CC=$(CROSS)gcc$(POSTFIX) LD=$(CROSS)ld$(POSTFIX) AS=$(CROSS)as AR=$(CROSS)ar NM=$(CROSS)nm OBJCOPY=$(CROSS)objcopy OBJDUMP=$(CROSS)objdump SIZE=$(CROSS)size LD_TEXT=0x0 NM += --synthetic try = $(shell set -e; if ($(1)) >/dev/null 2>&1; \ then echo "$(2)"; \ else echo "$(3)"; fi ) try-cflag = $(call try,$(1) $(2) -x c -c /dev/null -o /dev/null,$(2)) # Base warnings CWARNS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -Werror-implicit-function-declaration -Wdeclaration-after-statement \ -Wno-pointer-sign -Wextra -Wno-sign-compare \ -Wmissing-prototypes -Wmissing-declarations \ -Wwrite-strings -Wcast-align \ -Winit-self \ -Wframe-larger-than=1024 \ -Werror # Host tools and options HOSTCC=gcc HOSTEND=$(shell uname -m | sed -e 's/^i.*86$$/LITTLE/' -e 's/^x86.*/LITTLE/' -e 's/^ppc64le/LITTLE/' -e 's/^ppc.*/BIG/') HOSTCFLAGS=-O1 $(CWARNS) -DHAVE_$(HOSTEND)_ENDIAN -MMD HOSTCFLAGS += $(call try-cflag,$(HOSTCC),-std=gnu11) HOSTCFLAGS += $(call try-cflag,$(HOSTCC),-Wjump-misses-init) \ $(call try-cflag,$(HOSTCC),-Wsuggest-attribute=const) \ $(call try-cflag,$(HOSTCC),-Wsuggest-attribute=noreturn) \ $(call try-cflag,$(HOSTCC),-Wstack-usage=1024) VALGRIND=valgrind -q --show-reachable=yes --error-exitcode=99 # Target options OPTS=-Os -ffunction-sections DBG=-g CPPFLAGS := -I$(SRC)/include -Iinclude -MMD -include $(SRC)/include/config.h CPPFLAGS += -I$(SRC)/libfdt -I$(SRC)/libflash -I$(SRC)/libc/include -I$(SRC) ifeq ($(PORE),1) CPPFLAGS += -I$(SRC)/libpore -D__HAVE_LIBPORE__ endif CPPFLAGS += -D__SKIBOOT__ -nostdinc CPPFLAGS += -isystem $(shell $(CC) -print-file-name=include) CPPFLAGS += -DBITS_PER_LONG=64 -DHAVE_BIG_ENDIAN # We might want to remove our copy of stdint.h # but that means uint64_t becomes an ulong instead of an ullong # causing all our printf's to warn CPPFLAGS += -ffreestanding CFLAGS := -fno-strict-aliasing -fstack-protector-all -pie -mbig-endian -m64 CFLAGS += -Wl,--oformat,elf64-powerpc CFLAGS += $(call try-cflag,$(CC),-mabi=elfv1) CFLAGS += $(call try-cflag,$(CC),-std=gnu11) ifeq ($(SKIBOOT_GCOV),1) CFLAGS += -fprofile-arcs -ftest-coverage -DSKIBOOT_GCOV=1 endif ifeq ($(STACK_CHECK),1) CFLAGS += -fstack-protector-all -pg CPPFLAGS += -DSTACK_CHECK_ENABLED else # XXX Add -fstack-protector-strong on gcc 4.9 CFLAGS += -fstack-protector endif CFLAGS += $(call try-cflag,$(CC),-Wjump-misses-init) \ $(call try-cflag,$(CC),-Wsuggest-attribute=const) \ $(call try-cflag,$(CC),-Wsuggest-attribute=noreturn) \ $(call try-cflag,$(CC),-Wstack-usage=1024) CFLAGS += $(CWARNS) $(OPTS) $(DBG) LDFLAGS := -m64 -static -nostdlib -pie LDFLAGS += -Wl,-Ttext-segment,$(LD_TEXT) -Wl,-N -Wl,--build-id=none LDFLAGS += -Wl,--no-multi-toc LDFLAGS += -mbig-endian -Wl,--oformat,elf64-powerpc LDRFLAGS=-melf64ppc # Debug stuff #LDFLAGS += -Wl,-v -Wl,-Map,foomap AFLAGS := -D__ASSEMBLY__ -mbig-endian -m64 # Special tool flags: # Do not use the floating point unit CFLAGS += -msoft-float # Do not use string instructions CFLAGS += -mno-string # do not use load/store multiple word instrcutions CFLAGS += -mno-multiple # do not use any automatic vector foo # While it would be safe during boot, we don't save/restore across OPAL calls CFLAGS += $(call try-cflag,$(CC),-mno-vsx) \ $(call try-cflag,$(CC),-mno-direct-move) \ $(call try-cflag,$(CC),-mno-altivec) # Do not use load/store update. You REALLY do not want to use this! # The async safety of the ABI stack depends on the atomicity # of update on store. #CFLAGS += -mno-update ifneq ($(KERNEL),) CPPFLAGS += -DBUILTIN_KERNEL="\"$(KERNEL)\"" endif CHECK = sparse CHECKFLAGS := $(CF) .SECONDARY: vpath %.c $(SRC) vpath %.C $(SRC) vpath %.S $(SRC) default: all include/asm-offsets.h: asm/asm-offsets.s @mkdir -p include $(call Q,GN, $(SRC)/make_offsets.sh $< >$@, $@) TARGET = skiboot include $(SRC)/asm/Makefile.inc include $(SRC)/core/Makefile.inc include $(SRC)/hw/Makefile.inc include $(SRC)/platforms/Makefile.inc include $(SRC)/libfdt/Makefile.inc include $(SRC)/libflash/Makefile.inc include $(SRC)/libpore/Makefile.inc include $(SRC)/libc/Makefile.inc include $(SRC)/ccan/Makefile.inc include $(SRC)/$(DEVSRC)/Makefile.inc # hack for travis-ci and coverity gard: (cd external/gard; make) all: $(SUBDIRS) $(TARGET).lid $(TARGET).map extract-gcov OBJS := $(ASM) $(CORE) $(HW) $(PLATFORMS) $(LIBFDT) $(LIBFLASH) ifeq ($(PORE),1) OBJS += $(LIBPORE) endif OBJS += $(LIBC) $(CCAN) $(DEVSRC_OBJ) OBJS_NO_VER = $(OBJS) ALL_OBJS = $(OBJS) version.o ALL_OBJS_1 = $(ALL_OBJS) asm/dummy_map.o ALL_OBJS_2 = $(ALL_OBJS) asm/real_map.o $(TARGET).lid: $(TARGET).elf $(call Q,OBJCOPY, $(OBJCOPY) -O binary -S $^ $@, $@) $(TARGET).tmp.elf: $(ALL_OBJS_1) $(TARGET).lds $(KERNEL) $(call Q,LD, $(CC) $(LDFLAGS) -T $(TARGET).lds $(ALL_OBJS_1) -o $@, $@) asm/real_map.o : $(TARGET).tmp.map $(TARGET).elf: $(ALL_OBJS_2) $(TARGET).lds $(KERNEL) $(call Q,LD, $(CC) $(LDFLAGS) -T $(TARGET).lds $(ALL_OBJS_2) -o $@, $@) $(SUBDIRS): $(call Q,MKDIR,mkdir $@, $@) -include $(wildcard *.d) -include $(wildcard $(SUBDIRS:%=%/*.d)) # Set V=1 if you want to see everything. include $(SRC)/Makefile.rules VERSION ?= $(shell cd $(SRC); GIT_DIR=$(SRC)/.git $(SRC)/make_version.sh) .PHONY: VERSION-always .version: VERSION-always @echo $(VERSION) > $@.tmp @cmp -s $@ $@.tmp || cp $@.tmp $@ @rm -f $@.tmp version.c: $(SRC)/make_version.sh $(OBJS_NO_VER) .version @(if [ "a$(VERSION)" = "a" ]; then \ echo "#error You need to set SKIBOOT_VERSION environment variable" > $@ ;\ else \ echo "const char version[] = \"$(VERSION)\";" ;\ fi) > $@ .PHONY: coverage include $(shell find $(SRC)/* -name Makefile.check) extract-gcov: extract-gcov.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) \ -DTARGET__GNUC__=`echo '__GNUC__'|$(CC) -E -|grep -v '^#'` \ -DTARGET__GNUC_MINOR__=`echo '__GNUC_MINOR__'|$(CC) -E -|grep -v '^#'` \ -Wpadded -O0 -g -I$(SRC) -o $@ $<,$<) coverage-report: skiboot.info genhtml --branch-coverage -q -o $@ $< skiboot.info: coverage lcov -q -c -d . $(LCOV_DIRS) -o $@ --rc lcov_branch_coverage=1 lcov -q -r $@ $(LCOV_EXCLUDE) -o $@ --rc lcov_branch_coverage=1 tags: find . -name '*.[chS]' | xargs ctags TAGS: find . -name '*.[chS]' | xargs etags .PHONY: tags TAGS check coverage cscope: find . -name '*.[chS]' | xargs cscope clean: $(RM) *.[odsa] $(SUBDIRS:%=%/*.[odsa]) $(RM) *.elf $(TARGET).lid *.map $(TARGET).lds $(RM) include/asm-offsets.h version.c distclean: clean $(RM) *~ $(SUBDIRS:%=%/*~) include/*~ skiboot-skiboot-5.1.13/Makefile.rules000066400000000000000000000027421265204436200175250ustar00rootroot00000000000000# # These allow for the build to be less verbose # ifdef V VERBOSE:= $(V) else VERBOSE:= 0 endif ifeq ($(VERBOSE),1) define Q $(2) endef else ifneq ($(filter s% -s%,$(MAKEFLAGS)),) define Q @$(2) endef else define Q @echo " [$1] $(3)" @$(2) endef endif endif define cook_aflags $(filter-out $(AFLAGS_SKIP_$(1)), $(CPPFLAGS) $(AFLAGS)) $(AFLAGS_$(1)) endef define cook_cflags $(filter-out $(CFLAGS_SKIP_$(1)), $(CPPFLAGS) $(CFLAGS)) $(CFLAGS_$(1)) endef ifeq ($(C),1) ifeq ($(VERBOSE),1) cmd_check = $(CHECK) $(CHECKFLAGS) $(call cook_cflags,$@) $< else cmd_check = @$(CHECK) $(CHECKFLAGS) $(call cook_cflags,$@) $< endif endif %.o : %.S include/asm-offsets.h $(call Q,AS, $(CC) $(call cook_aflags,$@) -c $< -o $@, $@) %.s : %.S include/asm-offsets.h $(call Q,CC, $(CC) $(call cook_aflags,$@) -E -c $< -o $@, $@) %.o : %.c $(call cmd_check) $(call Q,CC, $(CC) $(call cook_cflags,$@) -c $< -o $@, $@) # Force the use of the C compiler, not C++ for the .C files in libpore %.o : %.C $(call cmd_check) $(call Q,CC, $(CC) $(call cook_cflags,$@) -x c -c $< -o $@, $@) %.s : %.c $(call Q,CC, $(CC) $(call cook_cflags,$@) -S -c $< -o $@, $@) %.i : %.c $(call Q,CC, $(CC) $(call cook_cflags,$@) -E -c $< -o $@, $@) %built-in.o : $(call Q,LD, $(LD) $(LDRFLAGS) -r $^ -o $@, $@) %.lds : %.lds.S $(call Q,CC, $(CC) $(CPPFLAGS) -P -E $< -o $@, $@) %.map: %.elf $(call Q,NM, $(NM) --synthetic -n $< | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $@, $@) skiboot-skiboot-5.1.13/README000066400000000000000000000070771265204436200156220ustar00rootroot00000000000000skiboot ------- Firmware for OpenPower systems. Source: https://github.com/open-power/skiboot Mailing list: skiboot@lists.ozlabs.org Info/subscribe: https://lists.ozlabs.org/listinfo/skiboot Archives: https://lists.ozlabs.org/pipermail/skiboot/ Patchwork: http://patchwork.ozlabs.org/project/skiboot/list/ Overview -------- OPAL firmware (OpenPower Abstraction Layer) comes in several parts. A simplified flow of what happens when the power button is pressed is: 1) The baseboard management controller (BMC) powers the system on. 2) The BMC selects the master chip and releases the self-boot engines (SBEs) on the POWER8 chips, master last. 3) The BMC relinquishes control of the flexible service interface (FSI) SCAN/SCOM engines. 4) The hostboot firmware IPLs the system. It initiates a secondary power-on sequence through a digital power systems sweep (DPSS). 5) The hostboot firmware loads the OPAL image and moves all processors to their execution starting points. Here, the OPAL image is three parts: 1) skiboot (includes OPAL runtime services) 2) skiroot - the bootloader environment a) kernel b) initramfs (containing petitboot bootloader) They may be all part of one payload or three separate images (depending on platform). The bootloader will kexec a host kernel (probably linux). The host OS can make OPAL calls. The OPAL API is documented in doc/opal-api/ (there are missing parts, patches are welcome!) See doc/overview.txt for a more in depth overview of skiboot. Building -------- You can build on a linux host. Modern Debian and Ubuntu are well known to be suitable. Build and testing on x86 is fine. You do not need a POWER host to build and test skiboot. You will need a C compiler for big endian ppc64. If your distro does not provide one, crosstool built compilers work well: https://www.kernel.org/pub/tools/crosstool/ You should then be able to just (where 4=nr cpu cores of your machine) $ make -j4 $ make -j4 check If using crosstool compilers, add /opt/cross/gcc-4.8.0-nolibc/powerpc64-linux/bin/ to your PATH. If using packaged cross compilers on Ubuntu, you may need to set the following environment variable: CROSS=powerpc-linux-gnu- Testing ------- To test in a simulator, install the IBM POWER8 Functional Simulator from: http://www-304.ibm.com/support/customercare/sas/f/pwrfs/home.html Qemu (as of 2.2.0) is not suitable as it does not (yet) implement the HyperVisor mode of the POWER8 processor. See https://www.flamingspork.com/blog/2015/08/28/running-opal-in-qemu-the-powernv-platform/ for instructions on how to use a work-in-progress patchset to qemu that may be suitable for some work. To run a boot-to-bootloader test, you'll need a zImage.papr built using the mambo_defconfig config for op-build. See https://github.com/open-power/op-build/ on howto build. Drop zImage.epapr in the skiboot directory and the skiboot test suite will automatically pick it up. See opal-ci/README for further testing instructions. To test on real hardware, you will need to understand how to flash new skiboot onto your system. This will vary from platform to platform. You may want to start with external/boot-tests/boot_test.sh as it can (provided the correct usernames/passwords) automatically flash a new skiboot onto ASTBMC based OpenPower machines. Hacking ------- All patches should be sent to the mailing list with linux-kernel style 'Signed-Off-By'. The following git commands are your friends: - git commit -s - git format-patch You probably want to read the linux Documentation/SubmittingPatches as much of it applies to skiboot. License ------- See LICENSE skiboot-skiboot-5.1.13/asm/000077500000000000000000000000001265204436200155075ustar00rootroot00000000000000skiboot-skiboot-5.1.13/asm/Makefile.inc000066400000000000000000000003171265204436200177200ustar00rootroot00000000000000# -*-Makefile-*- SUBDIRS += asm ASM_OBJS = head.o lock.o misc.o kernel-wrapper.o ASM=asm/built-in.o # Add extra dependency to the kernel wrapper kernel_wrapper.o : $(KERNEL) $(ASM): $(ASM_OBJS:%=asm/%) skiboot-skiboot-5.1.13/asm/asm-offsets.c000066400000000000000000000065201265204436200201050ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "../hdata/spira.h" #include #include #include #define DEFINE(sym, val) \ asm volatile("\n#define " #sym " %0 /* " #val " */" : : "i" (val)) #define OFFSET(sym, str, mem) \ DEFINE(sym, offsetof(struct str, mem)) int main(void); int main(void) { OFFSET(SPIRA_ACTUAL_SIZE, spira, reserved); OFFSET(CPUTHREAD_PIR, cpu_thread, pir); OFFSET(CPUTHREAD_SAVE_R1, cpu_thread, save_r1); OFFSET(CPUTHREAD_STATE, cpu_thread, state); OFFSET(CPUTHREAD_CUR_TOKEN, cpu_thread, current_token); DEFINE(CPUTHREAD_GAP, sizeof(struct cpu_thread) + STACK_SAFETY_GAP); #ifdef STACK_CHECK_ENABLED OFFSET(CPUTHREAD_STACK_BOT_MARK, cpu_thread, stack_bot_mark); OFFSET(CPUTHREAD_STACK_BOT_PC, cpu_thread, stack_bot_pc); OFFSET(CPUTHREAD_STACK_BOT_TOK, cpu_thread, stack_bot_tok); #endif OFFSET(STACK_TYPE, stack_frame, type); OFFSET(STACK_LOCALS, stack_frame, locals); OFFSET(STACK_GPR0, stack_frame, gpr[0]); OFFSET(STACK_GPR1, stack_frame, gpr[1]); OFFSET(STACK_GPR2, stack_frame, gpr[2]); OFFSET(STACK_GPR3, stack_frame, gpr[3]); OFFSET(STACK_GPR4, stack_frame, gpr[4]); OFFSET(STACK_GPR5, stack_frame, gpr[5]); OFFSET(STACK_GPR6, stack_frame, gpr[6]); OFFSET(STACK_GPR7, stack_frame, gpr[7]); OFFSET(STACK_GPR8, stack_frame, gpr[8]); OFFSET(STACK_GPR9, stack_frame, gpr[9]); OFFSET(STACK_GPR10, stack_frame, gpr[10]); OFFSET(STACK_GPR11, stack_frame, gpr[11]); OFFSET(STACK_GPR12, stack_frame, gpr[12]); OFFSET(STACK_GPR13, stack_frame, gpr[13]); OFFSET(STACK_GPR14, stack_frame, gpr[14]); OFFSET(STACK_GPR15, stack_frame, gpr[15]); OFFSET(STACK_GPR16, stack_frame, gpr[16]); OFFSET(STACK_GPR17, stack_frame, gpr[17]); OFFSET(STACK_GPR18, stack_frame, gpr[18]); OFFSET(STACK_GPR19, stack_frame, gpr[19]); OFFSET(STACK_GPR20, stack_frame, gpr[20]); OFFSET(STACK_GPR21, stack_frame, gpr[21]); OFFSET(STACK_GPR22, stack_frame, gpr[22]); OFFSET(STACK_GPR23, stack_frame, gpr[23]); OFFSET(STACK_GPR24, stack_frame, gpr[24]); OFFSET(STACK_GPR25, stack_frame, gpr[25]); OFFSET(STACK_GPR26, stack_frame, gpr[26]); OFFSET(STACK_GPR27, stack_frame, gpr[27]); OFFSET(STACK_GPR28, stack_frame, gpr[28]); OFFSET(STACK_GPR29, stack_frame, gpr[29]); OFFSET(STACK_GPR30, stack_frame, gpr[30]); OFFSET(STACK_GPR31, stack_frame, gpr[31]); OFFSET(STACK_CR, stack_frame, cr); OFFSET(STACK_XER, stack_frame, xer); OFFSET(STACK_CTR, stack_frame, ctr); OFFSET(STACK_LR, stack_frame, lr); OFFSET(STACK_PC, stack_frame, pc); OFFSET(STACK_CFAR, stack_frame, cfar); OFFSET(STACK_SRR0, stack_frame, srr0); OFFSET(STACK_SRR1, stack_frame, srr1); OFFSET(STACK_HSRR0, stack_frame, hsrr0); OFFSET(STACK_HSRR1, stack_frame, hsrr1); DEFINE(STACK_FRAMESIZE, sizeof(struct stack_frame)); return 0; } skiboot-skiboot-5.1.13/asm/dummy_map.S000066400000000000000000000000431265204436200176200ustar00rootroot00000000000000 .section ".sym_map","a" .byte 0 skiboot-skiboot-5.1.13/asm/head.S000066400000000000000000000457311265204436200165460ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define EPAPR_MAGIC 0x65504150 /* Power management instructions */ #define PPC_INST_NAP .long 0x4c000364 #define PPC_INST_SLEEP .long 0x4c0003a4 #define PPC_INST_RVWINKLE .long 0x4c0003e4 #define GET_STACK(stack_reg,pir_reg) \ sldi stack_reg,pir_reg,STACK_SHIFT; \ addis stack_reg,stack_reg,CPU_STACKS_OFFSET@ha; \ addi stack_reg,stack_reg,CPU_STACKS_OFFSET@l; #define GET_CPU() \ clrrdi %r13,%r1,STACK_SHIFT #define SAVE_GPR(reg,sp) std %r##reg,STACK_GPR##reg(sp) #define REST_GPR(reg,sp) ld %r##reg,STACK_GPR##reg(sp) .section ".head","ax" . = 0 .global __head __head: /* When booted as an OPAL LID, this is a pointer to the OPAL * variant of the NACA */ .llong opal_naca /* This entry point is used when booting with a flat device-tree * pointer in r3 */ . = 0x10 .global fdt_entry fdt_entry: mr %r27,%r3 li %r25,0 b boot_entry /* This is a pointer to a descriptor used by debugging tools * on the service processor to get to various trace buffers */ . = 0x80 .llong debug_descriptor /* This is our boot semaphore used for CPUs to sync, it has to be * at an easy to locate address (without relocation) since we * need to get at it very early, before we apply our relocs */ . = 0xf0 boot_sem: .long 0 /* And this is a boot flag used to kick secondaries into the * main code. */ boot_flag: .long 0 /* This is used to trigger an assert() and in turn an ATTN * in skiboot when a special sequence is written at this * address. For testing purposes only. */ . = 0xf8 .global attn_trigger attn_trigger: .long 0 /* This is the host initiated reset trigger for test */ . = 0xfc .global hir_trigger hir_trigger: .long 0 . = 0x100 /* BML entry, load up r3 with device tree location */ li %r3, 0 oris %r3, %r3, 0xa b fdt_entry /* hack for lab boot */ /* Entry point set by the FSP */ .= 0x180 li %r27,0 li %r25,0 b boot_entry #define EXCEPTION(nr) \ .= nr ;\ mtsprg0 %r3 ;\ mfspr %r3,SPR_CFAR ;\ mtsprg1 %r4 ;\ li %r4,nr ;\ b _exception /* More exception stubs */ EXCEPTION(0x200) EXCEPTION(0x300) EXCEPTION(0x380) EXCEPTION(0x400) EXCEPTION(0x480) EXCEPTION(0x500) EXCEPTION(0x600) EXCEPTION(0x700) EXCEPTION(0x800) EXCEPTION(0x900) EXCEPTION(0x980) EXCEPTION(0xa00) EXCEPTION(0xb00) EXCEPTION(0xc00) EXCEPTION(0xd00) EXCEPTION(0xe00) EXCEPTION(0xe20) EXCEPTION(0xe40) EXCEPTION(0xe60) EXCEPTION(0xf00) EXCEPTION(0xf20) EXCEPTION(0xf40) EXCEPTION(0x1000) EXCEPTION(0x1100) EXCEPTION(0x1200) EXCEPTION(0x1300) EXCEPTION(0x1400) EXCEPTION(0x1500) EXCEPTION(0x1600) .= 0x1e00 _exception: std %r4,16(%r1) stdu %r1,-STACK_FRAMESIZE(%r1) std %r3,STACK_CFAR(%r1) std %r4,STACK_TYPE(%r1) mfsprg0 %r3 mfsprg1 %r4 SAVE_GPR(0,%r1) SAVE_GPR(1,%r1) SAVE_GPR(2,%r1) SAVE_GPR(3,%r1) SAVE_GPR(4,%r1) SAVE_GPR(5,%r1) SAVE_GPR(6,%r1) SAVE_GPR(7,%r1) SAVE_GPR(8,%r1) SAVE_GPR(9,%r1) SAVE_GPR(10,%r1) SAVE_GPR(11,%r1) SAVE_GPR(12,%r1) SAVE_GPR(13,%r1) SAVE_GPR(14,%r1) SAVE_GPR(15,%r1) SAVE_GPR(16,%r1) SAVE_GPR(17,%r1) SAVE_GPR(18,%r1) SAVE_GPR(19,%r1) SAVE_GPR(20,%r1) SAVE_GPR(21,%r1) SAVE_GPR(22,%r1) SAVE_GPR(23,%r1) SAVE_GPR(24,%r1) SAVE_GPR(25,%r1) SAVE_GPR(26,%r1) SAVE_GPR(27,%r1) SAVE_GPR(28,%r1) SAVE_GPR(29,%r1) SAVE_GPR(30,%r1) SAVE_GPR(31,%r1) mfcr %r3 mfxer %r4 mfctr %r5 mflr %r6 stw %r3,STACK_CR(%r1) stw %r4,STACK_XER(%r1) stw %r5,STACK_CTR(%r1) stw %r5,STACK_LR(%r1) mfspr %r3,SPR_SRR0 mfspr %r4,SPR_SRR1 mfspr %r5,SPR_HSRR0 mfspr %r6,SPR_HSRR1 std %r3,STACK_SRR0(%r1) std %r4,STACK_SRR1(%r1) std %r5,STACK_HSRR0(%r1) std %r6,STACK_HSRR1(%r1) mr %r3,%r1 LOAD_IMM64(%r4, SKIBOOT_BASE) LOAD_IMM32(%r5, exception_entry_foo - __head) add %r4,%r4,%r5 mtctr %r4 bctrl b . exception_entry_foo: b exception_entry .= 0x2000 /* This is the OPAL branch table. It's populated at boot time * with function pointers to the various OPAL functions from * the content of the .opal_table section, indexed by Token. */ .global opal_branch_table opal_branch_table: .space 8 * (OPAL_LAST + 1) /* Stores the offset we were started from. Used later on if we want to * read any unrelocated code/data such as the built-in kernel image */ .global boot_offset boot_offset: .llong 0 /* * * Boot time entry point from FSP * * All CPUs come here * * Boot code NV register usage: * * r31 : Boot PIR * r30 : Current running offset * r29 : Target address * r28 : PVR * r27 : DTB pointer (or NULL) * r26 : PIR thread mask * r25 : Selected master CPU (OPAL boot) */ .global boot_entry boot_entry: /* Check PVR and set some CR bits */ mfspr %r28,SPR_PVR li %r26,3 /* Default to SMT4 */ srdi %r3,%r28,16 cmpwi cr0,%r3,PVR_TYPE_P7 beq 1f cmpwi cr0,%r3,PVR_TYPE_P7P beq 1f cmpwi cr0,%r3,PVR_TYPE_P8 beq 2f cmpwi cr0,%r3,PVR_TYPE_P8E beq 2f cmpwi cr0,%r3,PVR_TYPE_P8NVL beq 2f attn /* Unsupported CPU type... what do we do ? */ /* P8 -> 8 threads */ 2: li %r26,7 /* Get our reloc offset into r30 */ 1: bcl 20,31,$+4 1: mflr %r30 subi %r30,%r30,(1b - __head) /* Store reloc offset in boot_offset */ LOAD_IMM32(%r3, boot_offset - __head) add %r3,%r3,%r30 std %r30,0(%r3) /* Get ourselves a TOC & relocate it to our target address */ LOAD_IMM32(%r2,__toc_start - __head) LOAD_IMM64(%r29, SKIBOOT_BASE) add %r2,%r2,%r29 /* Fixup our MSR (remove TA) */ LOAD_IMM64(%r3, (MSR_HV | MSR_SF)) mtmsrd %r3,0 /* Check our PIR, avoid threads */ mfspr %r31,SPR_PIR and. %r0,%r31,%r26 bne secondary_wait /* Initialize per-core SPRs */ bl init_shared_sprs /* Pick a boot CPU, cpu index in r31 */ LOAD_IMM32(%r3, boot_sem - __head) add %r3,%r3,%r30 1: lwarx %r4,0,%r3 addi %r0,%r4,1 stwcx. %r0,0,%r3 bne 1b isync cmpwi cr0,%r4,0 bne secondary_wait /* Make sure we are in SMT medium */ smt_medium /* Initialize thread SPRs */ bl init_replicated_sprs /* Check if we need to copy ourselves up and update %r30 to * be our new offset */ cmpd %r29,%r30 beq 2f LOAD_IMM32(%r3, _sbss - __head) srdi %r3,%r3,3 mtctr %r3 mr %r4,%r30 mr %r15,%r30 mr %r30,%r29 1: ld %r0,0(%r4) std %r0,0(%r29) addi %r29,%r29,8 addi %r4,%r4,8 bdnz 1b sync icbi 0,%r29 sync isync LOAD_IMM32(%r3, 2f - __head) add %r3,%r3,%r30 mtctr %r3 bctr /* Get ready for C code: get a stack */ 2: GET_STACK(%r1,%r31) /* Clear up initial frame */ li %r3,0 std %r3,0(%r1) std %r3,8(%r1) std %r3,16(%r1) /* Relocate ourselves */ bl call_relocate /* Tell secondaries to move to second stage (relocated) spin loop */ LOAD_IMM32(%r3, boot_flag - __head) add %r3,%r3,%r15 li %r0,1 stw %r0,0(%r3) /* Clear BSS */ li %r0,0 LOAD_ADDR_FROM_TOC(%r3, _sbss) LOAD_ADDR_FROM_TOC(%r4, _ebss) subf %r4,%r3,%r4 srdi %r4,%r4,3 mtctr %r4 1: std %r0,0(%r3) addi %r3,%r3,8 bdnz 1b /* Get our per-cpu pointer into r13 */ GET_CPU() #ifdef STACK_CHECK_ENABLED /* Initialize stack bottom mark to 0, it will be updated in C code */ li %r0,0 std %r0,CPUTHREAD_STACK_BOT_MARK(%r13) #endif /* Jump to C */ mr %r3,%r27 mr %r4,%r25 bl main_cpu_entry b . /* Secondary CPUs wait here r31 is PIR */ secondary_wait: /* The primary might be in the middle of relocating us, * so first we spin on the boot_flag */ LOAD_IMM32(%r3, boot_flag - __head) add %r3,%r3,%r30 1: smt_very_low lwz %r0,0(%r3) cmpdi %r0,0 beq 1b /* Init some registers */ bl init_replicated_sprs /* Switch to new runtime address */ mr %r30,%r29 LOAD_IMM32(%r3, 1f - __head) add %r3,%r3,%r30 mtctr %r3 isync bctr 1: /* Now wait for cpu_secondary_start to be set */ LOAD_ADDR_FROM_TOC(%r3, cpu_secondary_start) 1: smt_very_low ld %r0,0(%r3) cmpdi %r0,0 beq 1b smt_medium /* Check our PIR is in bound */ LOAD_ADDR_FROM_TOC(%r5, cpu_max_pir) lwz %r5,0(%r5) cmpw %r31,%r5 bgt- secondary_not_found /* Get our stack, cpu thread, and jump to C */ GET_STACK(%r1,%r31) li %r0,0 std %r0,0(%r1) std %r0,16(%r1) GET_CPU() bl secondary_cpu_entry b . /* Not found... what to do ? set some global error ? */ secondary_not_found: smt_very_low b . call_relocate: mflr %r14 LOAD_IMM32(%r4,__dynamic_start - __head) LOAD_IMM32(%r5,__rela_dyn_start - __head) add %r4,%r4,%r30 add %r5,%r5,%r30 mr %r3,%r30 bl relocate cmpwi %r3,0 bne 1f mtlr %r14 blr 1: /* Fatal relocate failure */ attn /* This is a little piece of code that is copied down to * 0x100 when doing a "fast reset" */ .global fast_reset_patch_start fast_reset_patch_start: smt_medium LOAD_IMM64(%r30, SKIBOOT_BASE) LOAD_IMM32(%r3, fast_reset_entry - __head) add %r3,%r30,%r3 mtctr %r3 bctr .global fast_reset_patch_end fast_reset_patch_end: /* Fast reset code. We clean up the TLB and a few SPRs and * return to C code. All CPUs do that, the CPU triggering the * reset does it to itself last. The C code will sort out who * the master is. We come from the trampoline above with * r30 containing SKIBOOT_BASE */ fast_reset_entry: /* Clear out SLB */ li %r6,0 slbmte %r6,%r6 slbia ptesync /* Get PIR */ mfspr %r31,SPR_PIR /* Get a stack and restore r13 */ GET_STACK(%r1,%r31) li %r3,0 std %r3,0(%r1) std %r3,8(%r1) std %r3,16(%r1) GET_CPU() /* Get our TOC */ addis %r2,%r30,(__toc_start - __head)@ha addi %r2,%r2,(__toc_start - __head)@l /* Go to C ! */ bl fast_reboot b . .global cleanup_tlb cleanup_tlb: /* Clean the TLB */ li %r3,128 mtctr %r3 li %r4,0x800 /* IS field = 0b10 */ ptesync 1: tlbiel %r4 addi %r4,%r4,0x1000 bdnz 1b ptesync #define FIXUP_ENDIAN \ tdi 0,0,0x48; /* Reverse endian of b . + 8 */ \ b $+36; /* Skip trampoline if endian is good */ \ .long 0x05009f42; /* bcl 20,31,$+4 */ \ .long 0xa602487d; /* mflr r10 */ \ .long 0x1c004a39; /* addi r10,r10,28 */ \ .long 0xa600607d; /* mfmsr r11 */ \ .long 0x01006b69; /* xori r11,r11,1 */ \ .long 0xa6035a7d; /* mtsrr0 r10 */ \ .long 0xa6037b7d; /* mtsrr1 r11 */ \ .long 0x2400004c /* rfid */ .global enter_rvwinkle enter_rvwinkle: /* Before entering rvwinkle, we create a stack frame * and save our non-volatile registers. * * We also save these SPRs: * * - HSPRG0 in GPR0 slot * - HSPRG1 in GPR1 slot * * - xxx TODO: HIDs * - TODO: Mask MSR:ME during the process */ mflr %r0 std %r0,16(%r1) stdu %r1,-STACK_FRAMESIZE(%r1) SAVE_GPR(2,%r1) SAVE_GPR(14,%r1) SAVE_GPR(15,%r1) SAVE_GPR(16,%r1) SAVE_GPR(17,%r1) SAVE_GPR(18,%r1) SAVE_GPR(19,%r1) SAVE_GPR(20,%r1) SAVE_GPR(21,%r1) SAVE_GPR(22,%r1) SAVE_GPR(23,%r1) SAVE_GPR(24,%r1) SAVE_GPR(25,%r1) SAVE_GPR(26,%r1) SAVE_GPR(27,%r1) SAVE_GPR(28,%r1) SAVE_GPR(29,%r1) SAVE_GPR(30,%r1) SAVE_GPR(31,%r1) mfcr %r3 mfxer %r4 mfspr %r5,SPR_HSPRG0 mfspr %r6,SPR_HSPRG1 stw %r3,STACK_CR(%r1) stw %r4,STACK_XER(%r1) std %r5,STACK_GPR0(%r1) std %r6,STACK_GPR1(%r1) /* Save stack pointer in struct cpu_thread */ std %r1,CPUTHREAD_SAVE_R1(%r13) /* rvwinkle sequence */ ptesync 0: ld %r0,CPUTHREAD_SAVE_R1(%r13) cmpd cr0,%r0,%r0 bne 0b PPC_INST_RVWINKLE b . /* This is a little piece of code that is copied down to * 0x100 when doing a "rvwinkle reinit" */ .global rvwinkle_patch_start rvwinkle_patch_start: FIXUP_ENDIAN smt_medium LOAD_IMM64(%r30, SKIBOOT_BASE) LOAD_IMM32(%r3, rvwinkle_restore - __head) add %r3,%r30,%r3 mtctr %r3 bctr .global rvwinkle_patch_end rvwinkle_patch_end: rvwinkle_restore: /* Get PIR */ mfspr %r31,SPR_PIR /* Initialize per-core SPRs * * XXX We do it on each thread ... oh well, improve that later */ bl init_shared_sprs /* Initialize thread SPRs */ bl init_replicated_sprs /* Get that CPU stack base and use it to restore r13 */ GET_STACK(%r1,%r31) GET_CPU() /* Restore original stack pointer */ ld %r1,CPUTHREAD_SAVE_R1(%r13) /* Restore more stuff */ lwz %r3,STACK_CR(%r1) lwz %r4,STACK_XER(%r1) ld %r5,STACK_GPR0(%r1) ld %r6,STACK_GPR1(%r1) mtcr %r3 mtxer %r4 mtspr SPR_HSPRG0,%r5 mtspr SPR_HSPRG1,%r6 REST_GPR(2,%r1) REST_GPR(14,%r1) REST_GPR(15,%r1) REST_GPR(16,%r1) REST_GPR(17,%r1) REST_GPR(18,%r1) REST_GPR(19,%r1) REST_GPR(20,%r1) REST_GPR(21,%r1) REST_GPR(22,%r1) REST_GPR(23,%r1) REST_GPR(24,%r1) REST_GPR(25,%r1) REST_GPR(26,%r1) REST_GPR(27,%r1) REST_GPR(28,%r1) REST_GPR(29,%r1) REST_GPR(30,%r1) REST_GPR(31,%r1) /* Get LR back, pop stack and return */ addi %r1,%r1,STACK_FRAMESIZE ld %r0,16(%r1) mtlr %r0 blr /* Functions to initialize replicated and shared SPRs to sane * values. This is called at boot and on soft-reset */ .global init_shared_sprs init_shared_sprs: li %r0,0 mtspr SPR_SDR1, %r0 mtspr SPR_AMOR, %r0 mfspr %r3,SPR_PVR srdi %r3,%r3,16 cmpwi cr0,%r3,PVR_TYPE_P7 beq 1f cmpwi cr0,%r3,PVR_TYPE_P7P beq 2f cmpwi cr0,%r3,PVR_TYPE_P8E beq 3f cmpwi cr0,%r3,PVR_TYPE_P8 beq 3f cmpwi cr0,%r3,PVR_TYPE_P8NVL beq 3f /* Unsupported CPU type... what do we do ? */ b 9f 1: /* P7 */ /* TSCR: Value from pHyp */ LOAD_IMM32(%r3,0x880DE880) mtspr SPR_TSCR, %r3 b 9f 2: /* P7+ */ /* TSCR: Recommended value by HW folks */ LOAD_IMM32(%r3,0x88CDE880) mtspr SPR_TSCR, %r3 b 9f 3: /* P8E/P8 */ /* TSCR: Recommended value by HW folks */ LOAD_IMM32(%r3,0x8ACC6880) mtspr SPR_TSCR, %r3 mfspr %r3,SPR_LPCR rldicr %r3,%r3,12,60 ori %r3,%r3,4 rldicr %r3,%r3,52,63 mtspr SPR_LPCR,%r3 sync isync /* HID0: Clear bit 13 (enable core recovery) */ mfspr %r3,SPR_HID0 li %r0,1 sldi %r0,%r0,(63-13) andc %r3,%r3,%r0 sync mtspr SPR_HID0,%r3 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 isync /* HMEER: Enable HMIs for core recovery and TOD errors. */ LOAD_IMM64(%r0,SPR_HMEER_HMI_ENABLE_MASK) mfspr %r3,SPR_HMEER or %r3,%r3,%r0 sync mtspr SPR_HMEER,%r3 isync /* RPR (per-LPAR but let's treat it as replicated for now) */ LOAD_IMM64(%r3,0x00000103070F1F3F) mtspr SPR_RPR,%r3 9: blr .global init_replicated_sprs init_replicated_sprs: /* LPCR: sane value */ LOAD_IMM64(%r3,0x0070000000000004) mtspr SPR_LPCR, %r3 /* XXX TODO: Add more */ blr /* * * NACA structure, accessed by the FPS to find the SPIRA * */ . = 0x4000 .global naca naca: .llong 0 /* 0x0000 : Reserved */ .llong 0 /* 0x0008 : Reserved */ .llong 0 /* 0x0010 : Reserved */ .llong hv_release_data /* 0x0018 : HV release data */ .llong 0 /* 0x0020 : Reserved */ .llong 0 /* 0x0028 : Reserved */ .llong spira /* 0x0030 : SP Interface Root */ .llong hv_lid_load_table /* 0x0038 : LID load table */ .llong 0 /* 0x0040 : Reserved */ .space 68 .long 0 /* 0x008c : Reserved */ .space 16 .long SPIRA_ACTUAL_SIZE /* 0x00a0 : Actual size of SPIRA */ .space 28 .llong 0 /* 0x00c0 : resident module loadmap */ .space 136 .llong 0 /* 0x0150 : reserved */ .space 40 .llong 0 /* 0x0180 : reserved */ .space 36 .long 0 /* 0x01ac : control flags */ .byte 0 /* 0x01b0 : reserved */ .space 4 .byte 0 /* 0x01b5 : default state for SW attn */ .space 1 .byte 0x01 /* 0x01b7 : PCIA format */ .space 0xe48 .balign 0x10 hv_release_data: .space 58 .llong 0x666 /* VRM ? */ .balign 0x10 hv_lid_load_table: .long 0x10 .long 0x10 .long 0 .long 0 /* * * OPAL variant of NACA * */ .global opal_naca opal_naca: .llong opal_boot_trampoline /* Primary entry (used ?) */ .llong opal_boot_trampoline /* Secondary entry (used ?) */ .llong spira /* Spira pointer */ .llong 0 /* Load address */ .llong opal_boot_trampoline /* 0x180 trampoline */ .llong 0 /* More stuff as seen in objdump ...*/ .llong 0 .llong 0 .llong 0 /* The FSP seems to ignore our primary/secondary entry * points and instead copy that bit down to 0x180 and * patch the first instruction to get our expected * boot CPU number. We ignore that patching for now and * got to the same entry we use for pHyp and FDT HB. */ opal_boot_trampoline: li %r25,0 li %r27,-1 ba boot_entry - __head /* * * OPAL entry point from operating system * * Register usage: * * r0: Token * r2: OPAL Base * r3..r10: Args * r12: Scratch * r13..r31: Preserved * */ .balign 0x10 .global opal_entry opal_entry: /* Get our per CPU stack */ mfspr %r12,SPR_PIR GET_STACK(%r12,%r12) stdu %r12,-STACK_FRAMESIZE(%r12) /* Save caller r1, establish new r1 */ std %r1,STACK_GPR1(%r12) mr %r1,%r12 /* May save arguments for tracing */ #ifdef OPAL_TRACE_ENTRY std %r3,STACK_GPR3(%r1) std %r4,STACK_GPR4(%r1) std %r5,STACK_GPR5(%r1) std %r6,STACK_GPR6(%r1) std %r7,STACK_GPR7(%r1) std %r8,STACK_GPR8(%r1) std %r9,STACK_GPR9(%r1) std %r10,STACK_GPR10(%r1) #endif /* Save Token (r0), LR and r13 */ mflr %r12 std %r0,STACK_GPR0(%r1) std %r13,STACK_GPR13(%r1) std %r12,STACK_LR(%r1) /* Get the CPU thread */ GET_CPU() /* Store token in CPU thread */ std %r0,CPUTHREAD_CUR_TOKEN(%r13) /* Mark the stack frame */ li %r12,STACK_ENTRY_OPAL_API std %r12,STACK_TYPE(%r1) /* Get our TOC */ addis %r2,%r2,(__toc_start - __head)@ha addi %r2,%r2,(__toc_start - __head)@l /* Check for a reboot in progress */ LOAD_ADDR_FROM_TOC(%r12, reboot_in_progress) lbz %r12,0(%r12) cmpwi %r12,0 bne 3f #ifdef OPAL_TRACE_ENTRY mr %r3,%r1 bl opal_trace_entry ld %r0,STACK_GPR0(%r1) ld %r3,STACK_GPR3(%r1) ld %r4,STACK_GPR4(%r1) ld %r5,STACK_GPR5(%r1) ld %r6,STACK_GPR6(%r1) ld %r7,STACK_GPR7(%r1) ld %r8,STACK_GPR8(%r1) ld %r9,STACK_GPR9(%r1) ld %r10,STACK_GPR10(%r1) #endif /* OPAL_TRACE_ENTRY */ /* Convert our token into a table entry and get the * function pointer. Also check the token. */ cmpldi %r0,OPAL_LAST bgt- 2f sldi %r0,%r0,3 LOAD_ADDR_FROM_TOC(%r12, opal_branch_table) ldx %r0,%r12,%r0 cmpldi %r0,0 beq- 2f mtctr %r0 /* Jump ! */ bctrl 1: ld %r12,STACK_LR(%r1) mtlr %r12 ld %r13,STACK_GPR13(%r1) ld %r1,STACK_GPR1(%r1) blr 2: /* Bad token */ ld %r3,STACK_GPR0(%r1) bl opal_bad_token b 1b 3: /* Reboot in progress, reject all calls */ li %r3,OPAL_BUSY b 1b .global start_kernel start_kernel: sync icbi 0,%r3 sync isync mtctr %r3 mr %r3,%r4 LOAD_IMM64(%r8,SKIBOOT_BASE); LOAD_IMM32(%r10, opal_entry - __head) add %r9,%r8,%r10 LOAD_IMM32(%r6, EPAPR_MAGIC) addi %r7,%r5,1 li %r4,0 li %r5,0 bctr .global start_kernel32 start_kernel32: mfmsr %r10 clrldi %r10,%r10,1 mtmsrd %r10,0 sync isync b start_kernel .global start_kernel_secondary start_kernel_secondary: sync isync mtctr %r3 mfspr %r3,SPR_PIR bctr skiboot-skiboot-5.1.13/asm/kernel-wrapper.S000066400000000000000000000012731265204436200205740ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ .section ".builtin_kernel","a" .balign 0x10000 #ifdef BUILTIN_KERNEL .incbin BUILTIN_KERNEL #endif skiboot-skiboot-5.1.13/asm/lock.S000066400000000000000000000017431265204436200165700ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include .section ".text","ax" .balign 0x10 /* bool try_lock(struct lock *lock) */ .global __try_lock __try_lock: ld %r0,0(%r3) andi. %r10,%r0,1 bne 2f lwz %r9,CPUTHREAD_PIR(%r13) 1: ldarx %r0,0,%r3 andi. %r10,%r0,1 bne- 2f ori %r0,%r0,1 rldimi %r0,%r9,32,0 stdcx. %r0,0,%r3 bne 1b sync li %r3,-1 blr 2: li %r3,0 blr skiboot-skiboot-5.1.13/asm/misc.S000066400000000000000000000031261265204436200165700ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include .section ".text","ax" .balign 0x10 /* void set_hid0(unsigned long hid0) */ .global set_hid0 set_hid0: sync mtspr SPR_HID0,%r3 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 mfspr %r3,SPR_HID0 isync blr .global trigger_attn trigger_attn: sync isync attn blr #ifdef STACK_CHECK_ENABLED .global _mcount _mcount: mr %r3,%r1 mflr %r4 b __mcount_stack_check #endif .global mambo_read mambo_read: #define SIM_READ_CONSOLE_CODE 60 li %r3,SIM_READ_CONSOLE_CODE .long 0x000eaeb0 extsw %r3,%r3 blr .global mambo_write mambo_write: #define SIM_WRITE_CONSOLE_CODE 0 li %r6,0 mr %r5,%r4 mr %r4,%r3 li %r3,SIM_WRITE_CONSOLE_CODE .long 0x000eaeb0 blr .global mambo_sim_exit mambo_sim_exit: li %r3, 31 /* aka. SimExitCode */ .long 0x000eaeb0 b . .global mambo_get_time mambo_get_time: #define SIM_GET_TIME_CODE 70 li %r3,SIM_GET_TIME_CODE .long 0x000eaeb0 blr skiboot-skiboot-5.1.13/asm/real_map.S000066400000000000000000000012131265204436200174100ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ .section ".sym_map","a" .incbin "skiboot.tmp.map" skiboot-skiboot-5.1.13/ccan/000077500000000000000000000000001265204436200156335ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/Makefile.check000066400000000000000000000023031265204436200203450ustar00rootroot00000000000000CCAN_TEST_SRC := $(wildcard ccan/*/test/run*.c) LCOV_EXCLUDE += $(CCAN_TEST_SRC) ccan/list/test/helper.c CCAN_TEST := $(CCAN_TEST_SRC:%.c=%) .PHONY: $(CCAN_TEST:%=%-gcov-run) ccan-check ccan-check: $(CCAN_TEST:%=%-check) check: ccan-check $(CCAN_TEST:%=%-gcov-run) .PHONY: ccan-coverage coverage: ccan-coverage ccan-coverage: $(CCAN_TEST:%=%-gcov-run) $(CCAN_TEST:%=%-gcov-run) : %-run: % $(eval LCOV_DIRS += -d $(dir $<) ) $(call Q, TEST-COVERAGE , (cd $(dir $<); GCOV_PREFIX_STRIP=`(c=0; while [ "\`pwd\`" != '/' ]; do cd ..; c=\`expr 1 + $$c\`; done; echo $$c)` ./$(notdir $<) ), $< ) $(CCAN_TEST:%=%-check) : %-check: % $(call Q, RUN-TEST , $(VALGRIND) $<, $<) $(CCAN_TEST) : % : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I . -Iccan/ -o $@ $<,$<) $(CCAN_TEST:%=%-gcov): %-gcov : %.c $(call Q, HOSTCC , (cd $(dir $<); $(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -O0 -g -I $(shell pwd) -I$(shell pwd)/./ccan/ -pg -o $(notdir $@) $(notdir $<) ), $<) -include $(wildcard ccan/*/test/*.d) clean: ccan-test-clean ccan-test-clean: $(RM) -f $(CCAN_TEST) \ $(CCAN_TEST:%=%-gcov) \ $(CCAN_TEST:%=%.d) \ $(CCAN_TEST:%=%.o) \ $(CCAN_TEST:%=%.gcda) \ $(CCAN_TEST:%=%.gcno) skiboot-skiboot-5.1.13/ccan/Makefile.inc000066400000000000000000000002151265204436200200410ustar00rootroot00000000000000# -*-Makefile-*- SUBDIRS += ccan ccan/list ccan/str CCAN_OBJS = list/list.o str/str.o CCAN=ccan/built-in.o $(CCAN): $(CCAN_OBJS:%=ccan/%) skiboot-skiboot-5.1.13/ccan/array_size/000077500000000000000000000000001265204436200200035ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/array_size/LICENSE000066400000000000000000000143571265204436200210220ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/array_size/_info000066400000000000000000000020061265204436200210160ustar00rootroot00000000000000#include #include #include "config.h" /** * array_size - routine for safely deriving the size of a visible array. * * This provides a simple ARRAY_SIZE() macro, which (given a good compiler) * will also break compile if you try to use it on a pointer. * * This can ensure your code is robust to changes, without needing a gratuitous * macro or constant. * * Example: * // Outputs "Initialized 32 values" * #include * #include * #include * * // We currently use 32 random values. * static unsigned int vals[32]; * * int main(void) * { * unsigned int i; * for (i = 0; i < ARRAY_SIZE(vals); i++) * vals[i] = random(); * printf("Initialized %u values\n", i); * return 0; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/build_assert\n"); return 0; } return 1; } skiboot-skiboot-5.1.13/ccan/array_size/array_size.h000066400000000000000000000015711265204436200223300ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_ARRAY_SIZE_H #define CCAN_ARRAY_SIZE_H #include "config.h" #include /** * ARRAY_SIZE - get the number of elements in a visible array * @arr: the array whose size you want. * * This does not work on pointers, or arrays declared as [], or * function parameters. With correct compiler support, such usage * will cause a build error (see build_assert). */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr)) #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF /* Two gcc extensions. * &a[0] degrades to a pointer: a different type from an array */ #define _array_size_chk(arr) \ BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(arr), \ typeof(&(arr)[0]))) #else #define _array_size_chk(arr) 0 #endif #endif /* CCAN_ALIGNOF_H */ skiboot-skiboot-5.1.13/ccan/array_size/test/000077500000000000000000000000001265204436200207625ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/array_size/test/compile_fail-function-param.c000066400000000000000000000007651265204436200265020ustar00rootroot00000000000000#include #include struct foo { unsigned int a, b; }; int check_parameter(const struct foo array[4]); int check_parameter(const struct foo array[4]) { #ifdef FAIL return (ARRAY_SIZE(array) == 4); #if !HAVE_TYPEOF || !HAVE_BUILTIN_TYPES_COMPATIBLE_P #error "Unfortunately we don't fail if _array_size_chk is a noop." #endif #else return sizeof(array) == 4 * sizeof(struct foo); #endif } int main(int argc, char *argv[]) { return check_parameter(NULL); } skiboot-skiboot-5.1.13/ccan/array_size/test/compile_fail.c000066400000000000000000000005021265204436200235460ustar00rootroot00000000000000#include int main(int argc, char *argv[8]) { char array[100]; #ifdef FAIL return ARRAY_SIZE(argv) + ARRAY_SIZE(array); #if !HAVE_TYPEOF || !HAVE_BUILTIN_TYPES_COMPATIBLE_P #error "Unfortunately we don't fail if _array_size_chk is a noop." #endif #else return ARRAY_SIZE(array); #endif } skiboot-skiboot-5.1.13/ccan/array_size/test/run.c000066400000000000000000000014241265204436200217330ustar00rootroot00000000000000#include #include static char array1[1]; static int array2[2]; static unsigned long array3[3][5]; struct foo { unsigned int a, b; char string[100]; }; static struct foo array4[4]; /* Make sure they can be used in initializers. */ static int array1_size = ARRAY_SIZE(array1); static int array2_size = ARRAY_SIZE(array2); static int array3_size = ARRAY_SIZE(array3); static int array4_size = ARRAY_SIZE(array4); int main(int argc, char *argv[]) { (void)argc; (void)argv; plan_tests(8); ok1(array1_size == 1); ok1(array2_size == 2); ok1(array3_size == 3); ok1(array4_size == 4); ok1(ARRAY_SIZE(array1) == 1); ok1(ARRAY_SIZE(array2) == 2); ok1(ARRAY_SIZE(array3) == 3); ok1(ARRAY_SIZE(array4) == 4); return exit_status(); } skiboot-skiboot-5.1.13/ccan/build_assert/000077500000000000000000000000001265204436200203135ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/build_assert/LICENSE000066400000000000000000000143571265204436200213320ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/build_assert/_info000066400000000000000000000025061265204436200213330ustar00rootroot00000000000000#include #include #include "config.h" /** * build_assert - routines for build-time assertions * * This code provides routines which will cause compilation to fail should some * assertion be untrue: such failures are preferable to run-time assertions, * but much more limited since they can only depends on compile-time constants. * * These assertions are most useful when two parts of the code must be kept in * sync: it is better to avoid such cases if possible, but seconds best is to * detect invalid changes at build time. * * For example, a tricky piece of code might rely on a certain element being at * the start of the structure. To ensure that future changes don't break it, * you would catch such changes in your code like so: * * Example: * #include * #include * * struct foo { * char string[5]; * int x; * }; * * static char *foo_string(struct foo *foo) * { * // This trick requires that the string be first in the structure * BUILD_ASSERT(offsetof(struct foo, string) == 0); * return (char *)foo; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) /* Nothing. */ return 0; return 1; } skiboot-skiboot-5.1.13/ccan/build_assert/build_assert.h000066400000000000000000000023141265204436200231440ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_BUILD_ASSERT_H #define CCAN_BUILD_ASSERT_H /** * BUILD_ASSERT - assert a build-time dependency. * @cond: the compile-time condition which must be true. * * Your compile will fail if the condition isn't true, or can't be evaluated * by the compiler. This can only be used within a function. * * Example: * #include * ... * static char *foo_to_char(struct foo *foo) * { * // This code needs string to be at start of foo. * BUILD_ASSERT(offsetof(struct foo, string) == 0); * return (char *)foo; * } */ #define BUILD_ASSERT(cond) \ do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) /** * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression. * @cond: the compile-time condition which must be true. * * Your compile will fail if the condition isn't true, or can't be evaluated * by the compiler. This can be used in an expression: its value is "0". * * Example: * #define foo_to_char(foo) \ * ((char *)(foo) \ * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0)) */ #define BUILD_ASSERT_OR_ZERO(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1) #endif /* CCAN_BUILD_ASSERT_H */ skiboot-skiboot-5.1.13/ccan/build_assert/test/000077500000000000000000000000001265204436200212725ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/build_assert/test/compile_fail-expr.c000066400000000000000000000002341265204436200250340ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL return BUILD_ASSERT_OR_ZERO(1 == 0); #else return 0; #endif } skiboot-skiboot-5.1.13/ccan/build_assert/test/compile_fail.c000066400000000000000000000002071265204436200240600ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL BUILD_ASSERT(1 == 0); #endif return 0; } skiboot-skiboot-5.1.13/ccan/build_assert/test/compile_ok.c000066400000000000000000000001641265204436200235600ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { BUILD_ASSERT(1 == 1); return 0; } skiboot-skiboot-5.1.13/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c000066400000000000000000000003271265204436200254210ustar00rootroot00000000000000#include #include int main(int argc, char *argv[]) { (void)argc; (void)argv; plan_tests(1); ok1(BUILD_ASSERT_OR_ZERO(1 == 1) == 0); return exit_status(); } skiboot-skiboot-5.1.13/ccan/check_type/000077500000000000000000000000001265204436200177515ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/check_type/LICENSE000066400000000000000000000143571265204436200207700ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/check_type/_info000066400000000000000000000015111265204436200207640ustar00rootroot00000000000000#include #include #include "config.h" /** * check_type - routines for compile time type checking * * C has fairly weak typing: ints get automatically converted to longs, signed * to unsigned, etc. There are some cases where this is best avoided, and * these macros provide methods for evoking warnings (or build errors) when * a precise type isn't used. * * On compilers which don't support typeof() these routines are less effective, * since they have to use sizeof() which can only distiguish between types of * different size. * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { #if !HAVE_TYPEOF printf("ccan/build_assert\n"); #endif return 0; } return 1; } skiboot-skiboot-5.1.13/ccan/check_type/check_type.h000066400000000000000000000045051265204436200222440ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_CHECK_TYPE_H #define CCAN_CHECK_TYPE_H #include "config.h" /** * check_type - issue a warning or build failure if type is not correct. * @expr: the expression whose type we should check (not evaluated). * @type: the exact type we expect the expression to be. * * This macro is usually used within other macros to try to ensure that a macro * argument is of the expected type. No type promotion of the expression is * done: an unsigned int is not the same as an int! * * check_type() always evaluates to 0. * * If your compiler does not support typeof, then the best we can do is fail * to compile if the sizes of the types are unequal (a less complete check). * * Example: * // They should always pass a 64-bit value to _set_some_value! * #define set_some_value(expr) \ * _set_some_value((check_type((expr), uint64_t), (expr))) */ /** * check_types_match - issue a warning or build failure if types are not same. * @expr1: the first expression (not evaluated). * @expr2: the second expression (not evaluated). * * This macro is usually used within other macros to try to ensure that * arguments are of identical types. No type promotion of the expressions is * done: an unsigned int is not the same as an int! * * check_types_match() always evaluates to 0. * * If your compiler does not support typeof, then the best we can do is fail * to compile if the sizes of the types are unequal (a less complete check). * * Example: * // Do subtraction to get to enclosing type, but make sure that * // pointer is of correct type for that member. * #define container_of(mbr_ptr, encl_type, mbr) \ * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ * ((encl_type *) \ * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) */ #if HAVE_TYPEOF #define check_type(expr, type) \ ((typeof(expr) *)0 != (type *)0) #define check_types_match(expr1, expr2) \ ((typeof(expr1) *)0 != (typeof(expr2) *)0) #else #include /* Without typeof, we can only test the sizes. */ #define check_type(expr, type) \ BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) #define check_types_match(expr1, expr2) \ BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) #endif /* HAVE_TYPEOF */ #endif /* CCAN_CHECK_TYPE_H */ skiboot-skiboot-5.1.13/ccan/check_type/test/000077500000000000000000000000001265204436200207305ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/check_type/test/compile_fail-check_type.c000066400000000000000000000002051265204436200256300ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL check_type(argc, char); #endif return 0; } skiboot-skiboot-5.1.13/ccan/check_type/test/compile_fail-check_type_unsigned.c000066400000000000000000000003751265204436200275340ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL #if HAVE_TYPEOF check_type(argc, unsigned int); #else /* This doesn't work without typeof, so just fail */ #error "Fail without typeof" #endif #endif return 0; } skiboot-skiboot-5.1.13/ccan/check_type/test/compile_fail-check_types_match.c000066400000000000000000000002421265204436200271700ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { unsigned char x = argc; #ifdef FAIL check_types_match(argc, x); #endif return x; } skiboot-skiboot-5.1.13/ccan/check_type/test/run.c000066400000000000000000000011151265204436200216760ustar00rootroot00000000000000#include #include int main(int argc, char *argv[]) { int x = 0, y = 0; (void)argc; (void)argv; plan_tests(9); ok1(check_type(argc, int) == 0); ok1(check_type(&argc, int *) == 0); ok1(check_types_match(argc, argc) == 0); ok1(check_types_match(argc, x) == 0); ok1(check_types_match(&argc, &x) == 0); ok1(check_type(x++, int) == 0); ok(x == 0, "check_type does not evaluate expression"); ok1(check_types_match(x++, y++) == 0); ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); return exit_status(); } skiboot-skiboot-5.1.13/ccan/config.h000066400000000000000000000015521265204436200172540ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Dummy config.h for CCAN test suite */ #define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 #define HAVE_TYPEOF 1 #ifndef HAVE_BIG_ENDIAN #define HAVE_BIG_ENDIAN 0 #endif #ifndef HAVE_LITTLE_ENDIAN #define HAVE_LITTLE_ENDIAN 0 #endif #define HAVE_BYTESWAP_H 0 #define HAVE_BSWAP_64 0 skiboot-skiboot-5.1.13/ccan/container_of/000077500000000000000000000000001265204436200203015ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/container_of/LICENSE000066400000000000000000000143571265204436200213200ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/container_of/_info000066400000000000000000000024741265204436200213250ustar00rootroot00000000000000#include #include #include "config.h" /** * container_of - routine for upcasting * * It is often convenient to create code where the caller registers a pointer * to a generic structure and a callback. The callback might know that the * pointer points to within a larger structure, and container_of gives a * convenient and fairly type-safe way of returning to the enclosing structure. * * This idiom is an alternative to providing a void * pointer for every * callback. * * Example: * #include * #include * * struct timer { * void *members; * }; * * struct info { * int my_stuff; * struct timer timer; * }; * * static void register_timer(struct timer *timer) * { * //... * } * * static void my_timer_callback(struct timer *timer) * { * struct info *info = container_of(timer, struct info, timer); * printf("my_stuff is %u\n", info->my_stuff); * } * * int main(void) * { * struct info info = { .my_stuff = 1 }; * * register_timer(&info.timer); * // ... * return 0; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/check_type\n"); return 0; } return 1; } skiboot-skiboot-5.1.13/ccan/container_of/container_of.h000066400000000000000000000061201265204436200231170ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_CONTAINER_OF_H #define CCAN_CONTAINER_OF_H #include #include "config.h" #include /** * container_of - get pointer to enclosing structure * @member_ptr: pointer to the structure member * @containing_type: the type this member is within * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does pointer * subtraction to return the pointer to the enclosing type. * * Example: * struct foo { * int fielda, fieldb; * // ... * }; * struct info { * int some_other_field; * struct foo my_foo; * }; * * static struct info *foo_to_info(struct foo *foo) * { * return container_of(foo, struct info, my_foo); * } */ #define container_of(member_ptr, containing_type, member) \ ((containing_type *) \ ((char *)(member_ptr) \ - container_off(containing_type, member)) \ + check_types_match(*(member_ptr), ((containing_type *)0)->member)) /** * container_off - get offset to enclosing structure * @containing_type: the type this member is within * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does * typechecking and figures out the offset to the enclosing type. * * Example: * struct foo { * int fielda, fieldb; * // ... * }; * struct info { * int some_other_field; * struct foo my_foo; * }; * * static struct info *foo_to_info(struct foo *foo) * { * size_t off = container_off(struct info, my_foo); * return (void *)((char *)foo - off); * } */ #define container_off(containing_type, member) \ offsetof(containing_type, member) /** * container_of_var - get pointer to enclosing structure using a variable * @member_ptr: pointer to the structure member * @container_var: a pointer of same type as this member's container * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does pointer * subtraction to return the pointer to the enclosing type. * * Example: * static struct info *foo_to_i(struct foo *foo) * { * struct info *i = container_of_var(foo, i, my_foo); * return i; * } */ #if HAVE_TYPEOF #define container_of_var(member_ptr, container_var, member) \ container_of(member_ptr, typeof(*container_var), member) #else #define container_of_var(member_ptr, container_var, member) \ ((void *)((char *)(member_ptr) - \ container_off_var(container_var, member))) #endif /** * container_off_var - get offset of a field in enclosing structure * @container_var: a pointer to a container structure * @member: the name of a member within the structure. * * Given (any) pointer to a structure and a its member name, this * macro does pointer subtraction to return offset of member in a * structure memory layout. * */ #if HAVE_TYPEOF #define container_off_var(var, member) \ container_off(typeof(*var), member) #else #define container_off_var(var, member) \ ((char *)&(var)->member - (char *)(var)) #endif #endif /* CCAN_CONTAINER_OF_H */ skiboot-skiboot-5.1.13/ccan/container_of/test/000077500000000000000000000000001265204436200212605ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/container_of/test/compile_fail-bad-type.c000066400000000000000000000005511265204436200255530ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }; int *intp = &foo.a; char *p; #ifdef FAIL /* p is a char *, but this gives a struct foo * */ p = container_of(intp, struct foo, a); #else p = (char *)intp; #endif return p == NULL; } skiboot-skiboot-5.1.13/ccan/container_of/test/compile_fail-types.c000066400000000000000000000006321265204436200252120ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }, *foop; int *intp = &foo.a; #ifdef FAIL /* b is a char, but intp is an int * */ foop = container_of(intp, struct foo, b); #else foop = NULL; #endif (void) foop; /* Suppress unused-but-set-variable warning. */ return intp == NULL; } skiboot-skiboot-5.1.13/ccan/container_of/test/compile_fail-var-types.c000066400000000000000000000007561265204436200260070ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }, *foop; int *intp = &foo.a; #ifdef FAIL /* b is a char, but intp is an int * */ foop = container_of_var(intp, foop, b); #if !HAVE_TYPEOF #error "Unfortunately we don't fail if we don't have typeof." #endif #else foop = NULL; #endif (void) foop; /* Suppress unused-but-set-variable warning. */ return intp == NULL; } skiboot-skiboot-5.1.13/ccan/container_of/test/run.c000066400000000000000000000011161265204436200222270ustar00rootroot00000000000000#include #include struct foo { int a; char b; }; int main(int argc, char *argv[]) { struct foo foo = { .a = 1, .b = 2 }; int *intp = &foo.a; char *charp = &foo.b; (void)argc; (void)argv; plan_tests(6); ok1(container_of(intp, struct foo, a) == &foo); ok1(container_of(charp, struct foo, b) == &foo); ok1(container_of_var(intp, &foo, a) == &foo); ok1(container_of_var(charp, &foo, b) == &foo); ok1(container_off(struct foo, a) == 0); ok1(container_off(struct foo, b) == offsetof(struct foo, b)); return exit_status(); } skiboot-skiboot-5.1.13/ccan/endian/000077500000000000000000000000001265204436200170715ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/endian/.depends000066400000000000000000000000001265204436200205020ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/endian/LICENSE000066400000000000000000000143571265204436200201100ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/endian/_info000066400000000000000000000026131265204436200201100ustar00rootroot00000000000000#include "config.h" #include #include /** * endian - endian conversion macros for simple types * * Portable protocols (such as on-disk formats, or network protocols) * are often defined to be a particular endian: little-endian (least * significant bytes first) or big-endian (most significant bytes * first). * * Similarly, some CPUs lay out values in memory in little-endian * order (most commonly, Intel's 8086 and derivatives), or big-endian * order (almost everyone else). * * This module provides conversion routines, inspired by the linux kernel. * It also provides leint32_t, beint32_t etc typedefs, which are annotated for * the sparse checker. * * Example: * #include * #include * #include * * // * int main(int argc, char *argv[]) * { * uint32_t value; * * if (argc != 2) * errx(1, "Usage: %s ", argv[0]); * * value = atoi(argv[1]); * printf("native: %08x\n", value); * printf("little-endian: %08x\n", cpu_to_le32(value)); * printf("big-endian: %08x\n", cpu_to_be32(value)); * printf("byte-reversed: %08x\n", bswap_32(value)); * exit(0); * } * * License: License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) /* Nothing */ return 0; return 1; } skiboot-skiboot-5.1.13/ccan/endian/endian.h000066400000000000000000000217201265204436200205020ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_ENDIAN_H #define CCAN_ENDIAN_H #include #include "config.h" /** * BSWAP_16 - reverse bytes in a constant uint16_t value. * @val: constant value whose bytes to swap. * * Designed to be usable in constant-requiring initializers. * * Example: * struct mystruct { * char buf[BSWAP_16(0x1234)]; * }; */ #define BSWAP_16(val) \ ((((uint16_t)(val) & 0x00ff) << 8) \ | (((uint16_t)(val) & 0xff00) >> 8)) /** * BSWAP_32 - reverse bytes in a constant uint32_t value. * @val: constant value whose bytes to swap. * * Designed to be usable in constant-requiring initializers. * * Example: * struct mystruct { * char buf[BSWAP_32(0xff000000)]; * }; */ #define BSWAP_32(val) \ ((((uint32_t)(val) & 0x000000ff) << 24) \ | (((uint32_t)(val) & 0x0000ff00) << 8) \ | (((uint32_t)(val) & 0x00ff0000) >> 8) \ | (((uint32_t)(val) & 0xff000000) >> 24)) /** * BSWAP_64 - reverse bytes in a constant uint64_t value. * @val: constantvalue whose bytes to swap. * * Designed to be usable in constant-requiring initializers. * * Example: * struct mystruct { * char buf[BSWAP_64(0xff00000000000000ULL)]; * }; */ #define BSWAP_64(val) \ ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \ | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \ | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \ | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \ | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \ | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \ | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \ | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56)) #if HAVE_BYTESWAP_H #include #else /** * bswap_16 - reverse bytes in a uint16_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 4 as two bytes reversed" * printf("1024 is %u as two bytes reversed\n", bswap_16(1024)); */ static inline uint16_t bswap_16(uint16_t val) { return BSWAP_16(val); } /** * bswap_32 - reverse bytes in a uint32_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 262144 as four bytes reversed" * printf("1024 is %u as four bytes reversed\n", bswap_32(1024)); */ static inline uint32_t bswap_32(uint32_t val) { return BSWAP_32(val); } #endif /* !HAVE_BYTESWAP_H */ #if !HAVE_BSWAP_64 /** * bswap_64 - reverse bytes in a uint64_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 1125899906842624 as eight bytes reversed" * printf("1024 is %llu as eight bytes reversed\n", * (unsigned long long)bswap_64(1024)); */ static inline uint64_t bswap_64(uint64_t val) { return BSWAP_64(val); } #endif /* Sanity check the defines. We don't handle weird endianness. */ #if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN #error "Unknown endian" #elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN #error "Can't compile for both big and little endian." #endif #ifdef __CHECKER__ /* sparse needs forcing to remove bitwise attribute from ccan/short_types */ #define ENDIAN_CAST __attribute__((force)) #define ENDIAN_TYPE __attribute__((bitwise)) #else #define ENDIAN_CAST #define ENDIAN_TYPE #endif typedef uint64_t ENDIAN_TYPE leint64_t; typedef uint64_t ENDIAN_TYPE beint64_t; typedef uint32_t ENDIAN_TYPE leint32_t; typedef uint32_t ENDIAN_TYPE beint32_t; typedef uint16_t ENDIAN_TYPE leint16_t; typedef uint16_t ENDIAN_TYPE beint16_t; #if HAVE_LITTLE_ENDIAN /** * CPU_TO_LE64 - convert a constant uint64_t value to little-endian * @native: constant to convert */ #define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)(native)) /** * CPU_TO_LE32 - convert a constant uint32_t value to little-endian * @native: constant to convert */ #define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)(native)) /** * CPU_TO_LE16 - convert a constant uint16_t value to little-endian * @native: constant to convert */ #define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)(native)) /** * LE64_TO_CPU - convert a little-endian uint64_t constant * @le_val: little-endian constant to convert */ #define LE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val)) /** * LE32_TO_CPU - convert a little-endian uint32_t constant * @le_val: little-endian constant to convert */ #define LE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val)) /** * LE16_TO_CPU - convert a little-endian uint16_t constant * @le_val: little-endian constant to convert */ #define LE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val)) #else /* ... HAVE_BIG_ENDIAN */ #define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)BSWAP_64(native)) #define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)BSWAP_32(native)) #define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)BSWAP_16(native)) #define LE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val) #define LE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val) #define LE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val) #endif /* HAVE_BIG_ENDIAN */ #if HAVE_BIG_ENDIAN /** * CPU_TO_BE64 - convert a constant uint64_t value to big-endian * @native: constant to convert */ #define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)(native)) /** * CPU_TO_BE32 - convert a constant uint32_t value to big-endian * @native: constant to convert */ #define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)(native)) /** * CPU_TO_BE16 - convert a constant uint16_t value to big-endian * @native: constant to convert */ #define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)(native)) /** * BE64_TO_CPU - convert a big-endian uint64_t constant * @le_val: big-endian constant to convert */ #define BE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val)) /** * BE32_TO_CPU - convert a big-endian uint32_t constant * @le_val: big-endian constant to convert */ #define BE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val)) /** * BE16_TO_CPU - convert a big-endian uint16_t constant * @le_val: big-endian constant to convert */ #define BE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val)) #else /* ... HAVE_LITTLE_ENDIAN */ #define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)BSWAP_64(native)) #define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)BSWAP_32(native)) #define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)BSWAP_16(native)) #define BE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val) #define BE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val) #define BE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val) #endif /* HAVE_LITTE_ENDIAN */ /** * cpu_to_le64 - convert a uint64_t value to little-endian * @native: value to convert */ static inline leint64_t cpu_to_le64(uint64_t native) { return CPU_TO_LE64(native); } /** * cpu_to_le32 - convert a uint32_t value to little-endian * @native: value to convert */ static inline leint32_t cpu_to_le32(uint32_t native) { return CPU_TO_LE32(native); } /** * cpu_to_le16 - convert a uint16_t value to little-endian * @native: value to convert */ static inline leint16_t cpu_to_le16(uint16_t native) { return CPU_TO_LE16(native); } /** * le64_to_cpu - convert a little-endian uint64_t value * @le_val: little-endian value to convert */ static inline uint64_t le64_to_cpu(leint64_t le_val) { return LE64_TO_CPU(le_val); } /** * le32_to_cpu - convert a little-endian uint32_t value * @le_val: little-endian value to convert */ static inline uint32_t le32_to_cpu(leint32_t le_val) { return LE32_TO_CPU(le_val); } /** * le16_to_cpu - convert a little-endian uint16_t value * @le_val: little-endian value to convert */ static inline uint16_t le16_to_cpu(leint16_t le_val) { return LE16_TO_CPU(le_val); } /** * cpu_to_be64 - convert a uint64_t value to big endian. * @native: value to convert */ static inline beint64_t cpu_to_be64(uint64_t native) { return CPU_TO_BE64(native); } /** * cpu_to_be32 - convert a uint32_t value to big endian. * @native: value to convert */ static inline beint32_t cpu_to_be32(uint32_t native) { return CPU_TO_BE32(native); } /** * cpu_to_be16 - convert a uint16_t value to big endian. * @native: value to convert */ static inline beint16_t cpu_to_be16(uint16_t native) { return CPU_TO_BE16(native); } /** * be64_to_cpu - convert a big-endian uint64_t value * @be_val: big-endian value to convert */ static inline uint64_t be64_to_cpu(beint64_t be_val) { return BE64_TO_CPU(be_val); } /** * be32_to_cpu - convert a big-endian uint32_t value * @be_val: big-endian value to convert */ static inline uint32_t be32_to_cpu(beint32_t be_val) { return BE32_TO_CPU(be_val); } /** * be16_to_cpu - convert a big-endian uint16_t value * @be_val: big-endian value to convert */ static inline uint16_t be16_to_cpu(beint16_t be_val) { return BE16_TO_CPU(be_val); } /* Whichever they include first, they get these definitions. */ #ifdef CCAN_SHORT_TYPES_H /** * be64/be32/be16 - 64/32/16 bit big-endian representation. */ typedef beint64_t be64; typedef beint32_t be32; typedef beint16_t be16; /** * le64/le32/le16 - 64/32/16 bit little-endian representation. */ typedef leint64_t le64; typedef leint32_t le32; typedef leint16_t le16; #endif #endif /* CCAN_ENDIAN_H */ skiboot-skiboot-5.1.13/ccan/endian/test/000077500000000000000000000000001265204436200200505ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/endian/test/compile_ok-constant.c000066400000000000000000000002741265204436200241670ustar00rootroot00000000000000#include struct foo { char one[BSWAP_16(0xFF00)]; char two[BSWAP_32(0xFF000000)]; char three[BSWAP_64(0xFF00000000000000ULL)]; }; int main(void) { return 0; } skiboot-skiboot-5.1.13/ccan/endian/test/run.c000066400000000000000000000052711265204436200210250ustar00rootroot00000000000000#include #include #include #include int main(int argc, char *argv[]) { union { uint64_t u64; unsigned char u64_bytes[8]; } u64; union { uint32_t u32; unsigned char u32_bytes[4]; } u32; union { uint16_t u16; unsigned char u16_bytes[2]; } u16; (void)argc; (void)argv; plan_tests(48); /* Straight swap tests. */ u64.u64_bytes[0] = 0x00; u64.u64_bytes[1] = 0x11; u64.u64_bytes[2] = 0x22; u64.u64_bytes[3] = 0x33; u64.u64_bytes[4] = 0x44; u64.u64_bytes[5] = 0x55; u64.u64_bytes[6] = 0x66; u64.u64_bytes[7] = 0x77; u64.u64 = bswap_64(u64.u64); ok1(u64.u64_bytes[7] == 0x00); ok1(u64.u64_bytes[6] == 0x11); ok1(u64.u64_bytes[5] == 0x22); ok1(u64.u64_bytes[4] == 0x33); ok1(u64.u64_bytes[3] == 0x44); ok1(u64.u64_bytes[2] == 0x55); ok1(u64.u64_bytes[1] == 0x66); ok1(u64.u64_bytes[0] == 0x77); u32.u32_bytes[0] = 0x00; u32.u32_bytes[1] = 0x11; u32.u32_bytes[2] = 0x22; u32.u32_bytes[3] = 0x33; u32.u32 = bswap_32(u32.u32); ok1(u32.u32_bytes[3] == 0x00); ok1(u32.u32_bytes[2] == 0x11); ok1(u32.u32_bytes[1] == 0x22); ok1(u32.u32_bytes[0] == 0x33); u16.u16_bytes[0] = 0x00; u16.u16_bytes[1] = 0x11; u16.u16 = bswap_16(u16.u16); ok1(u16.u16_bytes[1] == 0x00); ok1(u16.u16_bytes[0] == 0x11); /* Endian tests. */ u64.u64 = cpu_to_le64(0x0011223344556677ULL); ok1(u64.u64_bytes[0] == 0x77); ok1(u64.u64_bytes[1] == 0x66); ok1(u64.u64_bytes[2] == 0x55); ok1(u64.u64_bytes[3] == 0x44); ok1(u64.u64_bytes[4] == 0x33); ok1(u64.u64_bytes[5] == 0x22); ok1(u64.u64_bytes[6] == 0x11); ok1(u64.u64_bytes[7] == 0x00); ok1(le64_to_cpu(u64.u64) == 0x0011223344556677ULL); u64.u64 = cpu_to_be64(0x0011223344556677ULL); ok1(u64.u64_bytes[7] == 0x77); ok1(u64.u64_bytes[6] == 0x66); ok1(u64.u64_bytes[5] == 0x55); ok1(u64.u64_bytes[4] == 0x44); ok1(u64.u64_bytes[3] == 0x33); ok1(u64.u64_bytes[2] == 0x22); ok1(u64.u64_bytes[1] == 0x11); ok1(u64.u64_bytes[0] == 0x00); ok1(be64_to_cpu(u64.u64) == 0x0011223344556677ULL); u32.u32 = cpu_to_le32(0x00112233); ok1(u32.u32_bytes[0] == 0x33); ok1(u32.u32_bytes[1] == 0x22); ok1(u32.u32_bytes[2] == 0x11); ok1(u32.u32_bytes[3] == 0x00); ok1(le32_to_cpu(u32.u32) == 0x00112233); u32.u32 = cpu_to_be32(0x00112233); ok1(u32.u32_bytes[3] == 0x33); ok1(u32.u32_bytes[2] == 0x22); ok1(u32.u32_bytes[1] == 0x11); ok1(u32.u32_bytes[0] == 0x00); ok1(be32_to_cpu(u32.u32) == 0x00112233); u16.u16 = cpu_to_le16(0x0011); ok1(u16.u16_bytes[0] == 0x11); ok1(u16.u16_bytes[1] == 0x00); ok1(le16_to_cpu(u16.u16) == 0x0011); u16.u16 = cpu_to_be16(0x0011); ok1(u16.u16_bytes[1] == 0x11); ok1(u16.u16_bytes[0] == 0x00); ok1(be16_to_cpu(u16.u16) == 0x0011); exit(exit_status()); } skiboot-skiboot-5.1.13/ccan/list/000077500000000000000000000000001265204436200166065ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/list/LICENSE000066400000000000000000000017771265204436200176270ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. skiboot-skiboot-5.1.13/ccan/list/_info000066400000000000000000000027011265204436200176230ustar00rootroot00000000000000#include #include #include "config.h" /** * list - double linked list routines * * The list header contains routines for manipulating double linked lists. * It defines two types: struct list_head used for anchoring lists, and * struct list_node which is usually embedded in the structure which is placed * in the list. * * Example: * #include * #include * #include * #include * * struct parent { * const char *name; * struct list_head children; * unsigned int num_children; * }; * * struct child { * const char *name; * struct list_node list; * }; * * int main(int argc, char *argv[]) * { * struct parent p; * struct child *c; * unsigned int i; * * if (argc < 2) * errx(1, "Usage: %s parent children...", argv[0]); * * p.name = argv[1]; * list_head_init(&p.children); * p.num_children = 0; * for (i = 2; i < argc; i++) { * c = malloc(sizeof(*c)); * c->name = argv[i]; * list_add(&p.children, &c->list); * p.num_children++; * } * * printf("%s has %u children:", p.name, p.num_children); * list_for_each(&p.children, c, list) * printf("%s ", c->name); * printf("\n"); * return 0; * } * * License: BSD-MIT * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/container_of\n"); return 0; } return 1; } skiboot-skiboot-5.1.13/ccan/list/list.c000066400000000000000000000017501265204436200177300ustar00rootroot00000000000000/* Licensed under BSD-MIT - see LICENSE file for details */ #include #include #include "list.h" static void *corrupt(const char *abortstr, const struct list_node *head, const struct list_node *node, unsigned int count) { if (abortstr) { fprintf(stderr, "%s: prev corrupt in node %p (%u) of %p\n", abortstr, node, count, head); abort(); } return NULL; } struct list_node *list_check_node(const struct list_node *node, const char *abortstr) { const struct list_node *p, *n; int count = 0; for (p = node, n = node->next; n != node; p = n, n = n->next) { count++; if (n->prev != p) return corrupt(abortstr, node, n, count); } /* Check prev on head node. */ if (node->prev != p) return corrupt(abortstr, node, node, 0); return (struct list_node *)node; } struct list_head *list_check(const struct list_head *h, const char *abortstr) { if (!list_check_node(&h->n, abortstr)) return NULL; return (struct list_head *)h; } skiboot-skiboot-5.1.13/ccan/list/list.h000066400000000000000000000346461265204436200177470ustar00rootroot00000000000000/* Licensed under BSD-MIT - see LICENSE file for details */ #ifndef CCAN_LIST_H #define CCAN_LIST_H #include #include #include #include /** * struct list_node - an entry in a doubly-linked list * @next: next entry (self if empty) * @prev: previous entry (self if empty) * * This is used as an entry in a linked list. * Example: * struct child { * const char *name; * // Linked list of all us children. * struct list_node list; * }; */ struct list_node { struct list_node *next, *prev; }; /** * struct list_head - the head of a doubly-linked list * @h: the list_head (containing next and prev pointers) * * This is used as the head of a linked list. * Example: * struct parent { * const char *name; * struct list_head children; * unsigned int num_children; * }; */ struct list_head { struct list_node n; }; /** * list_check - check head of a list for consistency * @h: the list_head * @abortstr: the location to print on aborting, or NULL. * * Because list_nodes have redundant information, consistency checking between * the back and forward links can be done. This is useful as a debugging check. * If @abortstr is non-NULL, that will be printed in a diagnostic if the list * is inconsistent, and the function will abort. * * Returns the list head if the list is consistent, NULL if not (it * can never return NULL if @abortstr is set). * * See also: list_check_node() * * Example: * static void dump_parent(struct parent *p) * { * struct child *c; * * printf("%s (%u children):\n", p->name, p->num_children); * list_check(&p->children, "bad child list"); * list_for_each(&p->children, c, list) * printf(" -> %s\n", c->name); * } */ struct list_head *list_check(const struct list_head *h, const char *abortstr); /** * list_check_node - check node of a list for consistency * @n: the list_node * @abortstr: the location to print on aborting, or NULL. * * Check consistency of the list node is in (it must be in one). * * See also: list_check() * * Example: * static void dump_child(const struct child *c) * { * list_check_node(&c->list, "bad child list"); * printf("%s\n", c->name); * } */ struct list_node *list_check_node(const struct list_node *n, const char *abortstr); #ifdef CCAN_LIST_DEBUG #define list_debug(h) list_check((h), __func__) #define list_debug_node(n) list_check_node((n), __func__) #else #define list_debug(h) (h) #define list_debug_node(n) (n) #endif /** * LIST_HEAD_INIT - initializer for an empty list_head * @name: the name of the list. * * Explicit initializer for an empty list. * * See also: * LIST_HEAD, list_head_init() * * Example: * static struct list_head my_list = LIST_HEAD_INIT(my_list); */ #define LIST_HEAD_INIT(name) { { &name.n, &name.n } } /** * LIST_HEAD - define and initialize an empty list_head * @name: the name of the list. * * The LIST_HEAD macro defines a list_head and initializes it to an empty * list. It can be prepended by "static" to define a static list_head. * * See also: * LIST_HEAD_INIT, list_head_init() * * Example: * static LIST_HEAD(my_global_list); */ #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) /** * list_head_init - initialize a list_head * @h: the list_head to set to the empty list * * Example: * ... * struct parent *parent = malloc(sizeof(*parent)); * * list_head_init(&parent->children); * parent->num_children = 0; */ static inline void list_head_init(struct list_head *h) { h->n.next = h->n.prev = &h->n; } /** * list_add - add an entry at the start of a linked list. * @h: the list_head to add the node to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * struct child *child = malloc(sizeof(*child)); * * child->name = "marvin"; * list_add(&parent->children, &child->list); * parent->num_children++; */ static inline void list_add(struct list_head *h, struct list_node *n) { n->next = h->n.next; n->prev = &h->n; h->n.next->prev = n; h->n.next = n; (void)list_debug(h); } /** * list_add_before - add an entry before another entry. * @h: the list_head to add the node to (we use it for debug purposes, can be NULL) * @n: the list_node to add to the list. * @p: the list_node of the other entry * * The list_node does not need to be initialized; it will be overwritten. */ static inline void list_add_before(struct list_head *h, struct list_node *n, struct list_node *p) { n->next = p; n->prev = p->prev; p->prev = n; n->prev->next = n; if (h) (void)list_debug(h); } /** * list_add_tail - add an entry at the end of a linked list. * @h: the list_head to add the node to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * list_add_tail(&parent->children, &child->list); * parent->num_children++; */ static inline void list_add_tail(struct list_head *h, struct list_node *n) { n->next = &h->n; n->prev = h->n.prev; h->n.prev->next = n; h->n.prev = n; (void)list_debug(h); } /** * list_empty - is a list empty? * @h: the list_head * * If the list is empty, returns true. * * Example: * assert(list_empty(&parent->children) == (parent->num_children == 0)); */ static inline bool list_empty(const struct list_head *h) { (void)list_debug(h); return h->n.next == &h->n; } /** * list_del - delete an entry from an (unknown) linked list. * @n: the list_node to delete from the list. * * Note that this leaves @n in an undefined state; it can be added to * another list, but not deleted again. * * See also: * list_del_from() * * Example: * list_del(&child->list); * parent->num_children--; */ static inline void list_del(struct list_node *n) { (void)list_debug_node(n); n->next->prev = n->prev; n->prev->next = n->next; #ifdef CCAN_LIST_DEBUG /* Catch use-after-del. */ n->next = n->prev = NULL; #endif } /** * list_del_from - delete an entry from a known linked list. * @h: the list_head the node is in. * @n: the list_node to delete from the list. * * This explicitly indicates which list a node is expected to be in, * which is better documentation and can catch more bugs. * * See also: list_del() * * Example: * list_del_from(&parent->children, &child->list); * parent->num_children--; */ static inline void list_del_from(struct list_head *h, struct list_node *n) { #ifdef CCAN_LIST_DEBUG { /* Thorough check: make sure it was in list! */ struct list_node *i; for (i = h->n.next; i != n; i = i->next) assert(i != &h->n); } #endif /* CCAN_LIST_DEBUG */ /* Quick test that catches a surprising number of bugs. */ assert(!list_empty(h)); list_del(n); } /** * list_entry - convert a list_node back into the structure containing it. * @n: the list_node * @type: the type of the entry * @member: the list_node member of the type * * Example: * // First list entry is children.next; convert back to child. * child = list_entry(parent->children.n.next, struct child, list); * * See Also: * list_top(), list_for_each() */ #define list_entry(n, type, member) container_of(n, type, member) /** * list_top - get the first entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *first; * first = list_top(&parent->children, struct child, list); * if (!first) * printf("Empty list!\n"); */ #define list_top(h, type, member) \ ((type *)list_top_((h), list_off_(type, member))) static inline const void *list_top_(const struct list_head *h, size_t off) { if (list_empty(h)) return NULL; return (const char *)h->n.next - off; } /** * list_pop - get the first entry in a list and dequeue it * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type */ #define list_pop(h, type, member) \ ((type *)list_pop_((h), list_off_(type, member))) static inline const void *list_pop_(struct list_head *h, size_t off) { struct list_node *n; if (list_empty(h)) return NULL; n = h->n.next; list_del(n); return (const char *)n - off; } /** * list_tail - get the last entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *last; * last = list_tail(&parent->children, struct child, list); * if (!last) * printf("Empty list!\n"); */ #define list_tail(h, type, member) \ ((type *)list_tail_((h), list_off_(type, member))) static inline const void *list_tail_(const struct list_head *h, size_t off) { if (list_empty(h)) return NULL; return (const char *)h->n.prev - off; } /** * list_for_each - iterate through a list. * @h: the list_head (warning: evaluated multiple times!) * @i: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. * * Example: * list_for_each(&parent->children, child, list) * printf("Name: %s\n", child->name); */ #define list_for_each(h, i, member) \ list_for_each_off(h, i, list_off_var_(i, member)) /** * list_for_each_rev - iterate through a list backwards. * @h: the list_head * @i: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. * * Example: * list_for_each_rev(&parent->children, child, list) * printf("Name: %s\n", child->name); */ #define list_for_each_rev(h, i, member) \ for (i = container_of_var(list_debug(h)->n.prev, i, member); \ &i->member != &(h)->n; \ i = container_of_var(i->member.prev, i, member)) /** * list_for_each_safe - iterate through a list, maybe during deletion * @h: the list_head * @i: the structure containing the list_node * @nxt: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. The extra variable * @nxt is used to hold the next element, so you can delete @i from the list. * * Example: * struct child *next; * list_for_each_safe(&parent->children, child, next, list) { * list_del(&child->list); * parent->num_children--; * } */ #define list_for_each_safe(h, i, nxt, member) \ list_for_each_safe_off(h, i, nxt, list_off_var_(i, member)) /** * list_for_each_off - iterate through a list of memory regions. * @h: the list_head * @i: the pointer to a memory region wich contains list node data. * @off: offset(relative to @i) at which list node data resides. * * This is a low-level wrapper to iterate @i over the entire list, used to * implement all oher, more high-level, for-each constructs. It's a for loop, * so you can break and continue as normal. * * WARNING! Being the low-level macro that it is, this wrapper doesn't know * nor care about the type of @i. The only assumtion made is that @i points * to a chunk of memory that at some @offset, relative to @i, contains a * properly filled `struct node_list' which in turn contains pointers to * memory chunks and it's turtles all the way down. Whith all that in mind * remember that given the wrong pointer/offset couple this macro will * happilly churn all you memory untill SEGFAULT stops it, in other words * caveat emptor. * * It is worth mentioning that one of legitimate use-cases for that wrapper * is operation on opaque types with known offset for `struct list_node' * member(preferably 0), because it allows you not to disclose the type of * @i. * * Example: * list_for_each_off(&parent->children, child, * offsetof(struct child, list)) * printf("Name: %s\n", child->name); */ #define list_for_each_off(h, i, off) \ for (i = list_node_to_off_(list_debug(h)->n.next, (off)); \ list_node_from_off_((void *)i, (off)) != &(h)->n; \ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \ (off))) /** * list_for_each_safe_off - iterate through a list of memory regions, maybe * during deletion * @h: the list_head * @i: the pointer to a memory region wich contains list node data. * @nxt: the structure containing the list_node * @off: offset(relative to @i) at which list node data resides. * * For details see `list_for_each_off' and `list_for_each_safe' * descriptions. * * Example: * list_for_each_safe_off(&parent->children, child, * next, offsetof(struct child, list)) * printf("Name: %s\n", child->name); */ #define list_for_each_safe_off(h, i, nxt, off) \ for (i = list_node_to_off_(list_debug(h)->n.next, (off)), \ nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ (off)); \ list_node_from_off_(i, (off)) != &(h)->n; \ i = nxt, \ nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ (off))) /* Other -off variants. */ #define list_entry_off(n, type, off) \ ((type *)list_node_from_off_((n), (off))) #define list_head_off(h, type, off) \ ((type *)list_head_off((h), (off))) #define list_tail_off(h, type, off) \ ((type *)list_tail_((h), (off))) #define list_add_off(h, n, off) \ list_add((h), list_node_from_off_((n), (off))) #define list_del_off(n, off) \ list_del(list_node_from_off_((n), (off))) #define list_del_from_off(h, n, off) \ list_del_from(h, list_node_from_off_((n), (off))) /* Offset helper functions so we only single-evaluate. */ static inline void *list_node_to_off_(struct list_node *node, size_t off) { return (void *)((char *)node - off); } static inline struct list_node *list_node_from_off_(void *ptr, size_t off) { return (struct list_node *)((char *)ptr + off); } /* Get the offset of the member, but make sure it's a list_node. */ #define list_off_(type, member) \ (container_off(type, member) + \ check_type(((type *)0)->member, struct list_node)) #define list_off_var_(var, member) \ (container_off_var(var, member) + \ check_type(var->member, struct list_node)) #endif /* CCAN_LIST_H */ skiboot-skiboot-5.1.13/ccan/list/test/000077500000000000000000000000001265204436200175655ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/list/test/compile_ok-constant.c000066400000000000000000000015641265204436200237070ustar00rootroot00000000000000#include #include #include #include #include struct child { const char *name; struct list_node list; }; static bool children(const struct list_head *list) { return !list_empty(list); } static const struct child *first_child(const struct list_head *list) { return list_top(list, struct child, list); } static const struct child *last_child(const struct list_head *list) { return list_tail(list, struct child, list); } static void check_children(const struct list_head *list) { list_check(list, "bad child list"); } static void print_children(const struct list_head *list) { const struct child *c; list_for_each(list, c, list) printf("%s\n", c->name); } int main(void) { LIST_HEAD(h); children(&h); first_child(&h); last_child(&h); check_children(&h); print_children(&h); return 0; } skiboot-skiboot-5.1.13/ccan/list/test/helper.c000066400000000000000000000024261265204436200212140ustar00rootroot00000000000000#include #include #include #include #include "helper.h" #define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \ (42) struct opaque { struct list_node list; size_t secret_offset; char secret_drawer[42]; }; static bool not_randomized = true; struct opaque *create_opaque_blob(void) { struct opaque *blob = calloc(1, sizeof(struct opaque)); if (not_randomized) { srandom((int)time(NULL)); not_randomized = false; } blob->secret_offset = random() % (sizeof(blob->secret_drawer)); blob->secret_drawer[blob->secret_offset] = ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING; return blob; } bool if_blobs_know_the_secret(struct opaque *blob) { bool answer = true; int i; for (i = 0; i < sizeof(blob->secret_drawer) / sizeof(blob->secret_drawer[0]); i++) if (i != blob->secret_offset) answer = answer && (blob->secret_drawer[i] == 0); else answer = answer && (blob->secret_drawer[blob->secret_offset] == ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING); return answer; } void destroy_opaque_blob(struct opaque *blob) { free(blob); } skiboot-skiboot-5.1.13/ccan/list/test/helper.h000066400000000000000000000003711265204436200212160ustar00rootroot00000000000000/* These are in a separate C file so we can test undefined structures. */ struct opaque; typedef struct opaque opaque_t; opaque_t *create_opaque_blob(void); bool if_blobs_know_the_secret(opaque_t *blob); void destroy_opaque_blob(opaque_t *blob); skiboot-skiboot-5.1.13/ccan/list/test/run-check-corrupt.c000066400000000000000000000041151265204436200233050ustar00rootroot00000000000000#include #include #include #include #include #include /* We don't actually want it to exit... */ static jmp_buf aborted; #define abort() longjmp(aborted, 1) #define fprintf my_fprintf static char printf_buffer[1000]; static int my_fprintf(FILE *stream, const char *format, ...) { va_list ap; int ret; (void)stream; va_start(ap, format); ret = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap); va_end(ap); return ret; } #include #include #include int main(int argc, char *argv[]) { struct list_head list; struct list_node n1; char expect[100]; (void)argc; (void)argv; plan_tests(9); /* Empty list. */ list.n.next = &list.n; list.n.prev = &list.n; ok1(list_check(&list, NULL) == &list); /* Bad back ptr */ list.n.prev = &n1; /* Non-aborting version. */ ok1(list_check(&list, NULL) == NULL); /* Aborting version. */ sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", &list, &list); if (setjmp(aborted) == 0) { list_check(&list, "test message"); fail("list_check on empty with bad back ptr didn't fail!"); } else { ok1(strcmp(printf_buffer, expect) == 0); } /* n1 in list. */ list.n.next = &n1; list.n.prev = &n1; n1.prev = &list.n; n1.next = &list.n; ok1(list_check(&list, NULL) == &list); ok1(list_check_node(&n1, NULL) == &n1); /* Bad back ptr */ n1.prev = &n1; ok1(list_check(&list, NULL) == NULL); ok1(list_check_node(&n1, NULL) == NULL); /* Aborting version. */ sprintf(expect, "test message: prev corrupt in node %p (1) of %p\n", &n1, &list); if (setjmp(aborted) == 0) { list_check(&list, "test message"); fail("list_check on n1 bad back ptr didn't fail!"); } else { ok1(strcmp(printf_buffer, expect) == 0); } sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", &n1, &n1); if (setjmp(aborted) == 0) { list_check_node(&n1, "test message"); fail("list_check_node on n1 bad back ptr didn't fail!"); } else { ok1(strcmp(printf_buffer, expect) == 0); } return exit_status(); } skiboot-skiboot-5.1.13/ccan/list/test/run-list_del_from-assert.c000066400000000000000000000014361265204436200246600ustar00rootroot00000000000000#define CCAN_LIST_DEBUG 1 #include #include #include #include #include #include #include int main(int argc, char *argv[]) { struct list_head list1, list2; struct list_node n1, n2, n3; pid_t child; int status; (void)argc; (void)argv; plan_tests(1); list_head_init(&list1); list_head_init(&list2); list_add(&list1, &n1); list_add(&list2, &n2); list_add_tail(&list2, &n3); child = fork(); if (child) { wait(&status); } else { close(2); /* Close stderr so we don't print confusing assert */ /* This should abort. */ list_del_from(&list1, &n3); exit(0); } ok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT); list_del_from(&list2, &n3); return exit_status(); } skiboot-skiboot-5.1.13/ccan/list/test/run-single-eval.c000066400000000000000000000112041265204436200227370ustar00rootroot00000000000000/* Make sure macros only evaluate their args once. */ #include #include #include struct parent { const char *name; struct list_head children; unsigned int num_children; int eval_count; }; struct child { const char *name; struct list_node list; }; static LIST_HEAD(static_list); #define ref(obj, counter) ((counter)++, (obj)) int main(int argc, char *argv[]) { struct parent parent; struct child c1, c2, c3, *c, *n; unsigned int i; unsigned int static_count = 0, parent_count = 0, list_count = 0, node_count = 0; struct list_head list = LIST_HEAD_INIT(list); (void)argc; (void)argv; plan_tests(74); /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ ok1(list_empty(ref(&static_list, static_count))); ok1(static_count == 1); ok1(list_check(ref(&static_list, static_count), NULL)); ok1(static_count == 2); ok1(list_empty(ref(&list, list_count))); ok1(list_count == 1); ok1(list_check(ref(&list, list_count), NULL)); ok1(list_count == 2); parent.num_children = 0; list_head_init(ref(&parent.children, parent_count)); ok1(parent_count == 1); /* Test list_head_init */ ok1(list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 2); ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 3); c2.name = "c2"; list_add(ref(&parent.children, parent_count), &c2.list); ok1(parent_count == 4); /* Test list_add and !list_empty. */ ok1(!list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 5); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &parent.children.n); ok1(parent.children.n.next == &c2.list); ok1(parent.children.n.prev == &c2.list); /* Test list_check */ ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 6); c1.name = "c1"; list_add(ref(&parent.children, parent_count), &c1.list); ok1(parent_count == 7); /* Test list_add and !list_empty. */ ok1(!list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 8); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &c1.list); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c2.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); /* Test list_check */ ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 9); c3.name = "c3"; list_add_tail(ref(&parent.children, parent_count), &c3.list); ok1(parent_count == 10); /* Test list_add_tail and !list_empty. */ ok1(!list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 11); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c3.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); ok1(c2.list.next == &c3.list); ok1(c2.list.prev == &c1.list); ok1(c3.list.next == &parent.children.n); ok1(c3.list.prev == &c2.list); /* Test list_check */ ok1(list_check(ref(&parent.children, parent_count), NULL)); ok1(parent_count == 12); /* Test list_check_node */ ok1(list_check_node(&c1.list, NULL)); ok1(list_check_node(&c2.list, NULL)); ok1(list_check_node(&c3.list, NULL)); /* Test list_top */ ok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1); ok1(parent_count == 13); /* Test list_tail */ ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3); ok1(parent_count == 14); /* Test list_for_each. */ i = 0; list_for_each(&parent.children, c, list) { switch (i++) { case 0: ok1(c == &c1); break; case 1: ok1(c == &c2); break; case 2: ok1(c == &c3); break; } if (i > 2) break; } ok1(i == 3); /* Test list_for_each_safe, list_del and list_del_from. */ i = 0; list_for_each_safe(&parent.children, c, n, list) { switch (i++) { case 0: ok1(c == &c1); list_del(ref(&c->list, node_count)); ok1(node_count == 1); break; case 1: ok1(c == &c2); list_del_from(ref(&parent.children, parent_count), ref(&c->list, node_count)); ok1(node_count == 2); break; case 2: ok1(c == &c3); list_del_from(ref(&parent.children, parent_count), ref(&c->list, node_count)); ok1(node_count == 3); break; } ok1(list_check(ref(&parent.children, parent_count), NULL)); if (i > 2) break; } ok1(i == 3); ok1(parent_count == 19); ok1(list_empty(ref(&parent.children, parent_count))); ok1(parent_count == 20); /* Test list_top/list_tail on empty list. */ ok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL); ok1(parent_count == 21); ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL); ok1(parent_count == 22); return exit_status(); } skiboot-skiboot-5.1.13/ccan/list/test/run-with-debug.c000066400000000000000000000001641265204436200225730ustar00rootroot00000000000000/* Just like run.c, but with all debug checks enabled. */ #define CCAN_LIST_DEBUG 1 #include skiboot-skiboot-5.1.13/ccan/list/test/run.c000066400000000000000000000112431265204436200205360ustar00rootroot00000000000000#include #include #include #include #include "helper.h" struct parent { const char *name; struct list_head children; unsigned int num_children; }; struct child { const char *name; struct list_node list; }; static LIST_HEAD(static_list); int main(int argc, char *argv[]) { struct parent parent; struct child c1, c2, c3, *c, *n; unsigned int i; struct list_head list = LIST_HEAD_INIT(list); opaque_t *q, *nq; struct list_head opaque_list = LIST_HEAD_INIT(opaque_list); (void)argc; (void)argv; plan_tests(65); /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ ok1(list_empty(&static_list)); ok1(list_check(&static_list, NULL)); ok1(list_empty(&list)); ok1(list_check(&list, NULL)); parent.num_children = 0; list_head_init(&parent.children); /* Test list_head_init */ ok1(list_empty(&parent.children)); ok1(list_check(&parent.children, NULL)); c2.name = "c2"; list_add(&parent.children, &c2.list); /* Test list_add and !list_empty. */ ok1(!list_empty(&parent.children)); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &parent.children.n); ok1(parent.children.n.next == &c2.list); ok1(parent.children.n.prev == &c2.list); /* Test list_check */ ok1(list_check(&parent.children, NULL)); c1.name = "c1"; list_add(&parent.children, &c1.list); /* Test list_add and !list_empty. */ ok1(!list_empty(&parent.children)); ok1(c2.list.next == &parent.children.n); ok1(c2.list.prev == &c1.list); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c2.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); /* Test list_check */ ok1(list_check(&parent.children, NULL)); c3.name = "c3"; list_add_tail(&parent.children, &c3.list); /* Test list_add_tail and !list_empty. */ ok1(!list_empty(&parent.children)); ok1(parent.children.n.next == &c1.list); ok1(parent.children.n.prev == &c3.list); ok1(c1.list.next == &c2.list); ok1(c1.list.prev == &parent.children.n); ok1(c2.list.next == &c3.list); ok1(c2.list.prev == &c1.list); ok1(c3.list.next == &parent.children.n); ok1(c3.list.prev == &c2.list); /* Test list_check */ ok1(list_check(&parent.children, NULL)); /* Test list_check_node */ ok1(list_check_node(&c1.list, NULL)); ok1(list_check_node(&c2.list, NULL)); ok1(list_check_node(&c3.list, NULL)); /* Test list_top */ ok1(list_top(&parent.children, struct child, list) == &c1); /* Test list_tail */ ok1(list_tail(&parent.children, struct child, list) == &c3); /* Test list_for_each. */ i = 0; list_for_each(&parent.children, c, list) { switch (i++) { case 0: ok1(c == &c1); break; case 1: ok1(c == &c2); break; case 2: ok1(c == &c3); break; } if (i > 2) break; } ok1(i == 3); /* Test list_for_each_rev. */ i = 0; list_for_each_rev(&parent.children, c, list) { switch (i++) { case 0: ok1(c == &c3); break; case 1: ok1(c == &c2); break; case 2: ok1(c == &c1); break; } if (i > 2) break; } ok1(i == 3); /* Test list_for_each_safe, list_del and list_del_from. */ i = 0; list_for_each_safe(&parent.children, c, n, list) { switch (i++) { case 0: ok1(c == &c1); list_del(&c->list); break; case 1: ok1(c == &c2); list_del_from(&parent.children, &c->list); break; case 2: ok1(c == &c3); list_del_from(&parent.children, &c->list); break; } ok1(list_check(&parent.children, NULL)); if (i > 2) break; } ok1(i == 3); ok1(list_empty(&parent.children)); /* Test list_for_each_off. */ list_add_tail(&opaque_list, (struct list_node *)create_opaque_blob()); list_add_tail(&opaque_list, (struct list_node *)create_opaque_blob()); list_add_tail(&opaque_list, (struct list_node *)create_opaque_blob()); i = 0; list_for_each_off(&opaque_list, q, 0) { i++; ok1(if_blobs_know_the_secret(q)); } ok1(i == 3); /* Test list_for_each_safe_off, list_del_off and list_del_from_off. */ i = 0; list_for_each_safe_off(&opaque_list, q, nq, 0) { switch (i++) { case 0: ok1(if_blobs_know_the_secret(q)); list_del_off(q, 0); destroy_opaque_blob(q); break; case 1: ok1(if_blobs_know_the_secret(q)); list_del_from_off(&opaque_list, q, 0); destroy_opaque_blob(q); break; case 2: ok1(c == &c3); list_del_from_off(&opaque_list, q, 0); destroy_opaque_blob(q); break; } ok1(list_check(&opaque_list, NULL)); if (i > 2) break; } ok1(i == 3); ok1(list_empty(&opaque_list)); /* Test list_top/list_tail on empty list. */ ok1(list_top(&parent.children, struct child, list) == NULL); ok1(list_tail(&parent.children, struct child, list) == NULL); return exit_status(); } skiboot-skiboot-5.1.13/ccan/short_types/000077500000000000000000000000001265204436200202165ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/short_types/LICENSE000066400000000000000000000143571265204436200212350ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/short_types/_info000066400000000000000000000051631265204436200212400ustar00rootroot00000000000000#include "config.h" #include #include /** * short_types - shorter names for standard integer types * * "C is a Spartan language, and so should your naming be." * -- Linus Torvalds * * The short_types header provides for convenient abbreviations for the * posixly-damned uint32_t types. If ccan/endian/endian.h is included, * it also provides be32/le32 for explicitly annotating types of specific * endian. * * Include this header, if only to stop people using these identifiers * for other things! * * Example: * #include * #include * #include * #include * * // Print nonsensical numerical comparison of POSIX vs. short_types. * #define stringify_1(x) #x * #define stringify(x) stringify_1(x) * * static void evaluate(size_t size, const char *posix, const char *sht, * unsigned int *posix_total, unsigned int *sht_total, * unsigned int *size_total) * { * printf("\t%ssigned %s: POSIX %zu%%, short %zu%%\n", * sht[0] == 'u' ? "un" : "", * sht+1, * strlen(posix)*100 / size, * strlen(sht)*100 / size); * *posix_total += strlen(posix); * *sht_total += strlen(sht); * *size_total += size; * } * * #define EVALUATE(psx, short, pt, st, t) \ * evaluate(sizeof(psx), stringify(psx), stringify(sht), pt, st, t) * * int main(void) * { * unsigned int posix_total = 0, sht_total = 0, size_total = 0; * * printf("Comparing size of type vs size of name:\n"); * * EVALUATE(uint8_t, u8, &posix_total, &sht_total, &size_total); * EVALUATE(int8_t, s8, &posix_total, &sht_total, &size_total); * EVALUATE(uint16_t, u16, &posix_total, &sht_total, &size_total); * EVALUATE(int16_t, s16, &posix_total, &sht_total, &size_total); * EVALUATE(uint32_t, u32, &posix_total, &sht_total, &size_total); * EVALUATE(int32_t, s32, &posix_total, &sht_total, &size_total); * EVALUATE(uint64_t, u64, &posix_total, &sht_total, &size_total); * EVALUATE(int64_t, s64, &posix_total, &sht_total, &size_total); * * printf("Conclusion:\n" * "\tPOSIX is %u%% LESS efficient than binary.\n" * "\tshort_types.h is %u%% MORE efficient than binary.\n", * (posix_total - size_total) * 100 / size_total, * (size_total - sht_total) * 100 / size_total); * return 0; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { return 0; } if (strcmp(argv[1], "testdepends") == 0) { printf("ccan/endian\n"); return 0; } return 1; } skiboot-skiboot-5.1.13/ccan/short_types/short_types.h000066400000000000000000000014311265204436200227510ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_SHORT_TYPES_H #define CCAN_SHORT_TYPES_H #include /** * u64/s64/u32/s32/u16/s16/u8/s8 - short names for explicitly-sized types. */ typedef uint64_t u64; typedef int64_t s64; typedef uint32_t u32; typedef int32_t s32; typedef uint16_t u16; typedef int16_t s16; typedef uint8_t u8; typedef int8_t s8; /* Whichever they include first, they get these definitions. */ #ifdef CCAN_ENDIAN_H /** * be64/be32/be16 - 64/32/16 bit big-endian representation. */ typedef beint64_t be64; typedef beint32_t be32; typedef beint16_t be16; /** * le64/le32/le16 - 64/32/16 bit little-endian representation. */ typedef leint64_t le64; typedef leint32_t le32; typedef leint16_t le16; #endif #endif /* CCAN_SHORT_TYPES_H */ skiboot-skiboot-5.1.13/ccan/short_types/test/000077500000000000000000000000001265204436200211755ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/short_types/test/run-endian.c000066400000000000000000000005351265204436200234040ustar00rootroot00000000000000#include #include #include #include #include int main(void) { plan_tests(6); ok1(sizeof(be64) == 8); ok1(sizeof(be32) == 4); ok1(sizeof(be16) == 2); ok1(sizeof(le64) == 8); ok1(sizeof(le32) == 4); ok1(sizeof(le16) == 2); return exit_status(); } skiboot-skiboot-5.1.13/ccan/short_types/test/run.c000066400000000000000000000010251265204436200221430ustar00rootroot00000000000000#include #include #include #include int main(void) { plan_tests(16); ok1(sizeof(u64) == 8); ok1(sizeof(s64) == 8); ok1(sizeof(u32) == 4); ok1(sizeof(s32) == 4); ok1(sizeof(u16) == 2); ok1(sizeof(s16) == 2); ok1(sizeof(u8) == 1); ok1(sizeof(s8) == 1); /* Signedness tests. */ ok1((u64)-1 > 0); ok1((u32)-1 > 0); ok1((u16)-1 > 0); ok1((u8)-1 > 0); ok1((s64)-1 < 0); ok1((s32)-1 < 0); ok1((s16)-1 < 0); ok1((s8)-1 < 0); return exit_status(); } skiboot-skiboot-5.1.13/ccan/str/000077500000000000000000000000001265204436200164435ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/str/LICENSE000066400000000000000000000143571265204436200174620ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. skiboot-skiboot-5.1.13/ccan/str/_info000066400000000000000000000025101265204436200174560ustar00rootroot00000000000000#include #include #include "config.h" /** * str - string helper routines * * This is a grab bag of functions for string operations, designed to enhance * the standard string.h. * * Note that if you define CCAN_STR_DEBUG, you will get extra compile * checks on common misuses of the following functions (they will now * be out-of-line, so there is a runtime penalty!). * * strstr, strchr, strrchr: * Return const char * if first argument is const (gcc only). * * isalnum, isalpha, isascii, isblank, iscntrl, isdigit, isgraph, * islower, isprint, ispunct, isspace, isupper, isxdigit: * Static and runtime check that input is EOF or an *unsigned* * char, as per C standard (really!). * * Example: * #include * #include * * int main(int argc, char *argv[]) * { * if (argv[1] && streq(argv[1], "--verbose")) * printf("verbose set\n"); * if (argv[1] && strstarts(argv[1], "--")) * printf("Some option set\n"); * if (argv[1] && strends(argv[1], "cow-powers")) * printf("Magic option set\n"); * return 0; * } * * License: CC0 (Public domain) * Author: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/build_assert\n"); return 0; } return 1; } skiboot-skiboot-5.1.13/ccan/str/str.c000066400000000000000000000004331265204436200174170ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #include size_t strcount(const char *haystack, const char *needle) { size_t i = 0, nlen = strlen(needle); while ((haystack = strstr(haystack, needle)) != NULL) { i++; haystack += nlen; } return i; } skiboot-skiboot-5.1.13/ccan/str/str.h000066400000000000000000000064301265204436200174270ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_STR_H #define CCAN_STR_H #include "config.h" #include #include #include #include /** * streq - Are two strings equal? * @a: first string * @b: first string * * This macro is arguably more readable than "!strcmp(a, b)". * * Example: * if (streq(somestring, "")) * printf("String is empty!\n"); */ #define streq(a,b) (strcmp((a),(b)) == 0) /** * strstarts - Does this string start with this prefix? * @str: string to test * @prefix: prefix to look for at start of str * * Example: * if (strstarts(somestring, "foo")) * printf("String %s begins with 'foo'!\n", somestring); */ #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) /** * strends - Does this string end with this postfix? * @str: string to test * @postfix: postfix to look for at end of str * * Example: * if (strends(somestring, "foo")) * printf("String %s end with 'foo'!\n", somestring); */ static inline bool strends(const char *str, const char *postfix) { if (strlen(str) < strlen(postfix)) return false; return streq(str + strlen(str) - strlen(postfix), postfix); } /** * stringify - Turn expression into a string literal * @expr: any C expression * * Example: * #define PRINT_COND_IF_FALSE(cond) \ * ((cond) || printf("%s is false!", stringify(cond))) */ #define stringify(expr) stringify_1(expr) /* Double-indirection required to stringify expansions */ #define stringify_1(expr) #expr /** * strcount - Count number of (non-overlapping) occurrences of a substring. * @haystack: a C string * @needle: a substring * * Example: * assert(strcount("aaa aaa", "a") == 6); * assert(strcount("aaa aaa", "ab") == 0); * assert(strcount("aaa aaa", "aa") == 2); */ size_t strcount(const char *haystack, const char *needle); /** * STR_MAX_CHARS - Maximum possible size of numeric string for this type. * @type_or_expr: a pointer or integer type or expression. * * This provides enough space for a nul-terminated string which represents the * largest possible value for the type or expression. * * Note: The implementation adds extra space so hex values or negative * values will fit (eg. sprintf(... "%p"). ) * * Example: * char str[STR_MAX_CHARS(int)]; * * sprintf(str, "%i", 7); */ #define STR_MAX_CHARS(type_or_expr) \ ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ + STR_MAX_CHARS_TCHECK_(type_or_expr)) #if HAVE_TYPEOF /* Only a simple type can have 0 assigned, so test that. */ #define STR_MAX_CHARS_TCHECK_(type_or_expr) \ ({ typeof(type_or_expr) x = 0; (void)x; 0; }) #else #define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 #endif /* These checks force things out of line, hence they are under DEBUG. */ #ifdef CCAN_STR_DEBUG #if HAVE_TYPEOF /* With GNU magic, we can make const-respecting standard string functions. */ #undef strstr #undef strchr #undef strrchr /* + 0 is needed to decay array into pointer. */ #define strstr(haystack, needle) \ ((typeof((haystack) + 0))str_strstr((haystack), (needle))) #define strchr(haystack, c) \ ((typeof((haystack) + 0))str_strchr((haystack), (c))) #define strrchr(haystack, c) \ ((typeof((haystack) + 0))str_strrchr((haystack), (c))) #endif #endif /* CCAN_STR_DEBUG */ #endif /* CCAN_STR_H */ skiboot-skiboot-5.1.13/ccan/str/test/000077500000000000000000000000001265204436200174225ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-STR_MAX_CHARS.c000066400000000000000000000004561265204436200243110ustar00rootroot00000000000000#include struct s { int val; }; int main(int argc, char *argv[]) { struct s #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check STR_MAX_CHARS. #endif #else /* A pointer is OK. */ * #endif val; char str[STR_MAX_CHARS(val)]; str[0] = '\0'; return str[0] ? 0 : 1; } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isalnum.c000066400000000000000000000005611265204436200236610ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isalnum. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isalnum(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isalpha.c000066400000000000000000000005611265204436200236320ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isalpha. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isalpha(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isascii.c000066400000000000000000000005611265204436200236350ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isascii. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isascii(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isblank.c000066400000000000000000000006531265204436200236360ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF || !HAVE_ISBLANK #error We need typeof to check isblank. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif #if HAVE_ISBLANK return isblank(c); #else return c; #endif } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-iscntrl.c000066400000000000000000000005611265204436200236670ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check iscntrl. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return iscntrl(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isdigit.c000066400000000000000000000005611265204436200236450ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isdigit. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isdigit(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-islower.c000066400000000000000000000005611265204436200236750ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check islower. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return islower(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isprint.c000066400000000000000000000005611265204436200237010ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isprint. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isprint(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-ispunct.c000066400000000000000000000005611265204436200236760ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check ispunct. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return ispunct(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isspace.c000066400000000000000000000005611265204436200236400ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isspace. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isspace(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isupper.c000066400000000000000000000005611265204436200237000ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isupper. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isupper(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-isxdigit.c000066400000000000000000000005631265204436200240370ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF #error We need typeof to check isxdigit. #endif char #else unsigned char #endif c = argv[0][0]; #ifdef FAIL /* Fake fail on unsigned char platforms. */ BUILD_ASSERT((char)255 < 0); #endif return isxdigit(c); } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-strchr.c000066400000000000000000000004211265204436200235110ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check strstr. #endif #else const #endif char *ret; const char *str = "hello"; ret = strchr(str, 'l'); return ret ? 0 : 1; } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-strrchr.c000066400000000000000000000004221265204436200236740ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check strstr. #endif #else const #endif char *ret; const char *str = "hello"; ret = strrchr(str, 'l'); return ret ? 0 : 1; } skiboot-skiboot-5.1.13/ccan/str/test/compile_fail-strstr.c000066400000000000000000000004241265204436200235500ustar00rootroot00000000000000#define CCAN_STR_DEBUG 1 #include int main(int argc, char *argv[]) { #ifdef FAIL #if !HAVE_TYPEOF #error We need typeof to check strstr. #endif #else const #endif char *ret; const char *str = "hello"; ret = strstr(str, "hell"); return ret ? 0 : 1; } skiboot-skiboot-5.1.13/ccan/str/test/debug.c000066400000000000000000000003241265204436200206530ustar00rootroot00000000000000/* We can't use the normal "#include the .c file" trick, since this is contaminated by str.h's macro overrides. So we put it in all tests like this. */ #define CCAN_STR_DEBUG 1 #include skiboot-skiboot-5.1.13/ccan/str/test/run-STR_MAX_CHARS.c000066400000000000000000000034131265204436200224660ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char *argv[]) { char *str = (char*)malloc(sizeof(char)*1000); struct { uint8_t u1byte; int8_t s1byte; uint16_t u2byte; int16_t s2byte; uint32_t u4byte; int32_t s4byte; uint64_t u8byte; int64_t s8byte; void *ptr; } types; (void)argc; (void)argv; assert(str); plan_tests(13); memset(&types, 0xFF, sizeof(types)); /* Hex versions */ sprintf(str, "0x%llx", (unsigned long long)types.u1byte); ok1(strlen(str) < STR_MAX_CHARS(types.u1byte)); sprintf(str, "0x%llx", (unsigned long long)types.u2byte); ok1(strlen(str) < STR_MAX_CHARS(types.u2byte)); sprintf(str, "0x%llx", (unsigned long long)types.u4byte); ok1(strlen(str) < STR_MAX_CHARS(types.u4byte)); sprintf(str, "0x%llx", (unsigned long long)types.u8byte); ok1(strlen(str) < STR_MAX_CHARS(types.u8byte)); /* Decimal versions */ sprintf(str, "%u", types.u1byte); ok1(strlen(str) < STR_MAX_CHARS(types.u1byte)); sprintf(str, "%d", types.s1byte); ok1(strlen(str) < STR_MAX_CHARS(types.s1byte)); sprintf(str, "%u", types.u2byte); ok1(strlen(str) < STR_MAX_CHARS(types.u2byte)); sprintf(str, "%d", types.s2byte); ok1(strlen(str) < STR_MAX_CHARS(types.s2byte)); sprintf(str, "%u", types.u4byte); ok1(strlen(str) < STR_MAX_CHARS(types.u4byte)); sprintf(str, "%d", types.s4byte); ok1(strlen(str) < STR_MAX_CHARS(types.s4byte)); sprintf(str, "%llu", (unsigned long long)types.u8byte); ok1(strlen(str) < STR_MAX_CHARS(types.u8byte)); sprintf(str, "%lld", (long long)types.s8byte); ok1(strlen(str) < STR_MAX_CHARS(types.s8byte)); /* Pointer version. */ sprintf(str, "%p", types.ptr); ok1(strlen(str) < STR_MAX_CHARS(types.ptr)); free(str); return exit_status(); } skiboot-skiboot-5.1.13/ccan/str/test/run.c000066400000000000000000000051651265204436200204010ustar00rootroot00000000000000#include #include #include #include #include #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) static const char *substrings[] = { "far", "bar", "baz", "b", "ba", "z", "ar", NULL }; #define NUM_SUBSTRINGS (ARRAY_SIZE(substrings) - 1) static char *strdup_rev(const char *s) { char *ret = strdup(s); unsigned int i; for (i = 0; i < strlen(s); i++) ret[i] = s[strlen(s) - i - 1]; return ret; } int main(int argc, char *argv[]) { unsigned int i, j, n; char *strings[NUM_SUBSTRINGS * NUM_SUBSTRINGS]; (void)argc; (void)argv; n = 0; for (i = 0; i < NUM_SUBSTRINGS; i++) { for (j = 0; j < NUM_SUBSTRINGS; j++) { strings[n] = malloc(strlen(substrings[i]) + strlen(substrings[j]) + 1); sprintf(strings[n++], "%s%s", substrings[i], substrings[j]); } } plan_tests(n * n * 5 + 16); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { unsigned int k, identical = 0; char *reva, *revb; /* Find first difference. */ for (k = 0; strings[i][k]==strings[j][k]; k++) { if (k == strlen(strings[i])) { identical = 1; break; } } if (identical) ok1(streq(strings[i], strings[j])); else ok1(!streq(strings[i], strings[j])); /* Postfix test should be equivalent to prefix * test on reversed string. */ reva = strdup_rev(strings[i]); revb = strdup_rev(strings[j]); if (!strings[i][k]) { ok1(strstarts(strings[j], strings[i])); ok1(strends(revb, reva)); } else { ok1(!strstarts(strings[j], strings[i])); ok1(!strends(revb, reva)); } if (!strings[j][k]) { ok1(strstarts(strings[i], strings[j])); ok1(strends(reva, revb)); } else { ok1(!strstarts(strings[i], strings[j])); ok1(!strends(reva, revb)); } free(reva); free(revb); } } for (i = 0; i < n; i++) free(strings[i]); ok1(streq(stringify(NUM_SUBSTRINGS), "((sizeof(substrings) / sizeof(substrings[0])) - 1)")); ok1(streq(stringify(ARRAY_SIZE(substrings)), "(sizeof(substrings) / sizeof(substrings[0]))")); ok1(streq(stringify(i == 0), "i == 0")); ok1(strcount("aaaaaa", "b") == 0); ok1(strcount("aaaaaa", "a") == 6); ok1(strcount("aaaaaa", "aa") == 3); ok1(strcount("aaaaaa", "aaa") == 2); ok1(strcount("aaaaaa", "aaaa") == 1); ok1(strcount("aaaaaa", "aaaaa") == 1); ok1(strcount("aaaaaa", "aaaaaa") == 1); ok1(strcount("aaa aaa", "b") == 0); ok1(strcount("aaa aaa", "a") == 6); ok1(strcount("aaa aaa", "aa") == 2); ok1(strcount("aaa aaa", "aaa") == 2); ok1(strcount("aaa aaa", "aaaa") == 0); ok1(strcount("aaa aaa", "aaaaa") == 0); return exit_status(); } skiboot-skiboot-5.1.13/ccan/tap/000077500000000000000000000000001265204436200164175ustar00rootroot00000000000000skiboot-skiboot-5.1.13/ccan/tap/tap.h000066400000000000000000000014351265204436200173570ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Dummy tap.h for ccan tests */ #include #define plan_tests(x) do { } while(0) #define ok1(e) assert(e) #define ok(e, ...) assert(e) #define fail(...) assert(0) #define exit_status() (0) skiboot-skiboot-5.1.13/core/000077500000000000000000000000001265204436200156575ustar00rootroot00000000000000skiboot-skiboot-5.1.13/core/Makefile.inc000066400000000000000000000014331265204436200200700ustar00rootroot00000000000000# -*-Makefile-*- SUBDIRS += core CORE_OBJS = relocate.o console.o stack.o init.o chip.o mem_region.o CORE_OBJS += malloc.o lock.o cpu.o utils.o fdt.o opal.o interrupts.o CORE_OBJS += timebase.o opal-msg.o pci.o pci-opal.o fast-reboot.o CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o CORE_OBJS += hostservices.o platform.o nvram.o nvram-format.o hmi.o CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ifeq ($(SKIBOOT_GCOV),1) CORE_OBJS += gcov-profiling.o endif CORE=core/built-in.o CFLAGS_SKIP_core/relocate.o = -pg -fstack-protector-all CFLAGS_SKIP_core/relocate.o += -fstack-protector -fstack-protector-strong CFLAGS_SKIP_core/relocate.o += -fprofile-arcs -ftest-coverage $(CORE): $(CORE_OBJS:%=core/%) skiboot-skiboot-5.1.13/core/affinity.c000066400000000000000000000075341265204436200176450ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * * We currently construct our associativity properties as such: * * - For "chip" devices (bridges, memory, ...), 4 entries: * * - CCM node ID * - HW card ID * - HW module ID * - Chip ID * * The information is constructed based on the chip ID which (unlike * pHyp) is our HW chip ID (aka "XSCOM" chip ID). We use it to retrieve * the other properties from the corresponding chip/xscom node in the * device-tree. If those properties are absent, 0 is used. * * - For "core" devices, we add a 5th entry: * * - Core ID * * Here too, we do not use the "cooked" HW processor ID from HDAT but * instead use the real HW core ID which is basically the interrupt * server number of thread 0 on that core. * * * The ibm,associativity-reference-points property is currently set to * 4,4 indicating that the chip ID is our only reference point. This * should be extended to encompass the node IDs eventually. */ #include #include #include #include #include #include #include #include static uint32_t get_chip_node_id(struct proc_chip *chip) { /* If the xscom node has an ibm,ccm-node-id property, use it */ if (dt_has_node_property(chip->devnode, "ibm,ccm-node-id", NULL)) return dt_prop_get_u32(chip->devnode, "ibm,ccm-node-id"); /* * Else use the 3 top bits of the chip ID which should be * the node on both P7 and P8 */ return chip->id >> 3; } void add_associativity_ref_point(void) { int ref2 = 0x4; /* * Note about our use of reference points: * * Linux currently supports two levels of NUMA. We use the first * reference point for the node ID and the second reference point * for a second level of affinity. We always use the chip ID (4) * for the first reference point. * * Choosing the second level of affinity is model specific * unfortunately. Current POWER8E models should use the DCM * as a second level of NUMA. * * If there is a way to obtain this information from the FSP * that would be ideal, but for now hardwire our POWER8E setting. */ if (PVR_TYPE(mfspr(SPR_PVR)) == PVR_TYPE_P8E) ref2 = 0x3; dt_add_property_cells(opal_node, "ibm,associativity-reference-points", 0x4, ref2); } void add_chip_dev_associativity(struct dt_node *dev) { uint32_t chip_id = dt_get_chip_id(dev); struct proc_chip *chip = get_chip(chip_id); uint32_t hw_cid, hw_mid; if (!chip) return; hw_cid = dt_prop_get_u32_def(chip->devnode, "ibm,hw-card-id", 0); hw_mid = dt_prop_get_u32_def(chip->devnode, "ibm,hw-module-id", 0); dt_add_property_cells(dev, "ibm,associativity", 4, get_chip_node_id(chip), hw_cid, hw_mid, chip_id); } void add_core_associativity(struct cpu_thread *cpu) { struct proc_chip *chip = get_chip(cpu->chip_id); uint32_t hw_cid, hw_mid, core_id; if (!chip) return; if (proc_gen == proc_gen_p7) core_id = (cpu->pir >> 2) & 0x7; else if (proc_gen == proc_gen_p8) core_id = (cpu->pir >> 3) & 0xf; else return; hw_cid = dt_prop_get_u32_def(chip->devnode, "ibm,hw-card-id", 0); hw_mid = dt_prop_get_u32_def(chip->devnode, "ibm,hw-module-id", 0); dt_add_property_cells(cpu->node, "ibm,associativity", 5, get_chip_node_id(chip), hw_cid, hw_mid, chip->id, core_id); } skiboot-skiboot-5.1.13/core/chip.c000066400000000000000000000046671265204436200167630ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include static struct proc_chip *chips[MAX_CHIPS]; enum proc_chip_quirks proc_chip_quirks; uint32_t pir_to_chip_id(uint32_t pir) { if (proc_gen == proc_gen_p8) return P8_PIR2GCID(pir); else return P7_PIR2GCID(pir); } uint32_t pir_to_core_id(uint32_t pir) { if (proc_gen == proc_gen_p8) return P8_PIR2COREID(pir); else return P7_PIR2COREID(pir); } uint32_t pir_to_thread_id(uint32_t pir) { if (proc_gen == proc_gen_p8) return P8_PIR2THREADID(pir); else return P7_PIR2THREADID(pir); } struct proc_chip *next_chip(struct proc_chip *chip) { unsigned int i; for (i = chip ? (chip->id + 1) : 0; i < MAX_CHIPS; i++) if (chips[i]) return chips[i]; return NULL; } struct proc_chip *get_chip(uint32_t chip_id) { if (chip_id >= MAX_CHIPS) return NULL; return chips[chip_id]; } void init_chips(void) { struct proc_chip *chip; struct dt_node *xn; /* Detect mambo chip */ if (dt_find_by_path(dt_root, "/mambo")) { proc_chip_quirks |= QUIRK_NO_CHIPTOD | QUIRK_MAMBO_CALLOUTS | QUIRK_NO_F000F | QUIRK_NO_PBA | QUIRK_NO_OCC_IRQ | QUIRK_DISABLE_NAP; prlog(PR_NOTICE, "CHIP: Detected Mambo simulator\n"); } if (dt_node_is_compatible(dt_root, "qemu,powernv")) { proc_chip_quirks |= QUIRK_NO_CHIPTOD | QUIRK_NO_PBA; prlog(PR_NOTICE, "CHIP: Detected Qemu simulator\n"); } /* We walk the chips based on xscom nodes in the tree */ dt_for_each_compatible(dt_root, xn, "ibm,xscom") { uint32_t id = dt_get_chip_id(xn); assert(id < MAX_CHIPS); chip = zalloc(sizeof(struct proc_chip)); assert(chip); chip->id = id; chip->devnode = xn; chips[id] = chip; chip->dbob_id = dt_prop_get_u32_def(xn, "ibm,dbob-id", 0xffffffff); chip->pcid = dt_prop_get_u32_def(xn, "ibm,proc-chip-id", 0xffffffff); list_head_init(&chip->i2cms); list_head_init(&chip->lpc_clients); }; } skiboot-skiboot-5.1.13/core/console-log.c000066400000000000000000000042701265204436200202470ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Console Log routines * Wraps libc and console lower level functions * does fancy-schmancy things like timestamps and priorities * Doesn't make waffles. */ #include "skiboot.h" #include "unistd.h" #include "stdio.h" #include "console.h" #include "timebase.h" static int vprlog(int log_level, const char *fmt, va_list ap) { int count; char buffer[320]; bool flush_to_drivers = true; /* It's safe to return 0 when we "did" something here * as only printf cares about how much we wrote, and * if you change log_level to below PR_PRINTF then you * get everything you deserve. * By default, only PR_DEBUG and higher are stored in memory. * PR_TRACE and PR_INSANE are for those having a bad day. */ if (log_level > (debug_descriptor.console_log_levels >> 4)) return 0; count = snprintf(buffer, sizeof(buffer), "[%lu,%d] ", mftb(), log_level); count+= vsnprintf(buffer+count, sizeof(buffer)-count, fmt, ap); if (log_level > (debug_descriptor.console_log_levels & 0x0f)) flush_to_drivers = false; console_write(flush_to_drivers, buffer, count); return count; } /* we don't return anything as what on earth are we going to do * if we actually fail to print a log message? Print a log message about it? * Callers shouldn't care, prlog and friends should do something generically * sane in such crazy situations. */ void _prlog(int log_level, const char* fmt, ...) { va_list ap; va_start(ap, fmt); vprlog(log_level, fmt, ap); va_end(ap); } int _printf(const char* fmt, ...) { int count; va_list ap; va_start(ap, fmt); count = vprlog(PR_PRINTF, fmt, ap); va_end(ap); return count; } skiboot-skiboot-5.1.13/core/console.c000066400000000000000000000222001265204436200174610ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Console IO routine for use by libc * * fd is the classic posix 0,1,2 (stdin, stdout, stderr) */ #include #include #include #include #include #include #include static char *con_buf = (char *)INMEM_CON_START; static size_t con_in; static size_t con_out; static bool con_wrapped; static struct con_ops *con_driver; struct lock con_lock = LOCK_UNLOCKED; /* This is mapped via TCEs so we keep it alone in a page */ struct memcons memcons __section(".data.memcons") = { .magic = MEMCONS_MAGIC, .obuf_phys = INMEM_CON_START, .ibuf_phys = INMEM_CON_START + INMEM_CON_OUT_LEN, .obuf_size = INMEM_CON_OUT_LEN, .ibuf_size = INMEM_CON_IN_LEN, }; bool dummy_console_enabled(void) { #ifdef FORCE_DUMMY_CONSOLE return true; #else return dt_has_node_property(dt_chosen, "sapphire,enable-dummy-console", NULL); #endif } void force_dummy_console(void) { if (!dummy_console_enabled()) dt_add_property(dt_chosen, "sapphire,enable-dummy-console", NULL, 0); } static int mambo_char = -1; static bool mambo_con_poll_read(void) { if (mambo_char < 0) mambo_char = mambo_read(); return mambo_char >= 0; } static size_t mambo_con_read(char *buf, size_t len) { size_t count = 0; while(count < len) { if (!mambo_con_poll_read()) break; *(buf++) = mambo_char; mambo_char = -1; count++; } return count; } static size_t mambo_con_write(const char *buf, size_t len) { mambo_write(buf, len); return len; } static struct con_ops mambo_con_driver = { .poll_read = mambo_con_poll_read, .read = mambo_con_read, .write = mambo_con_write, }; void enable_mambo_console(void) { prlog(PR_NOTICE, "Enabling Mambo console\n"); set_console(&mambo_con_driver); } void clear_console(void) { memset(con_buf, 0, INMEM_CON_LEN); } /* * Flush the console buffer into the driver, returns true * if there is more to go. * Optionally can skip flushing to drivers, leaving messages * just in memory console. */ bool __flush_console(bool flush_to_drivers) { struct cpu_thread *cpu = this_cpu(); size_t req, len = 0; static bool in_flush, more_flush; /* Is there anything to flush ? Bail out early if not */ if (con_in == con_out || !con_driver) return false; /* * Console flushing is suspended on this CPU, typically because * some critical locks are held that would potentially cause a * flush to deadlock */ if (cpu->con_suspend) { cpu->con_need_flush = true; return false; } cpu->con_need_flush = false; /* * We must call the underlying driver with the console lock * dropped otherwise we get some deadlocks if anything down * that path tries to printf() something. * * So instead what we do is we keep a static in_flush flag * set/released with the lock held, which is used to prevent * concurrent attempts at flushing the same chunk of buffer * by other processors. */ if (in_flush) { more_flush = true; return false; } in_flush = true; do { more_flush = false; if (con_out > con_in) { req = INMEM_CON_OUT_LEN - con_out; if (!flush_to_drivers) { len = req; } else { unlock(&con_lock); len = con_driver->write(con_buf + con_out, req); lock(&con_lock); } con_out = (con_out + len) % INMEM_CON_OUT_LEN; if (len < req) goto bail; } if (con_out < con_in) { if (!flush_to_drivers) { len = con_in - con_out; } else { unlock(&con_lock); len = con_driver->write(con_buf + con_out, con_in - con_out); lock(&con_lock); } con_out = (con_out + len) % INMEM_CON_OUT_LEN; } } while(more_flush); bail: in_flush = false; return con_out != con_in; } bool flush_console(void) { bool ret; lock(&con_lock); ret = __flush_console(true); unlock(&con_lock); return ret; } static void inmem_write(char c) { uint32_t opos; if (!c) return; con_buf[con_in++] = c; if (con_in >= INMEM_CON_OUT_LEN) { con_in = 0; con_wrapped = true; } /* * We must always re-generate memcons.out_pos because * under some circumstances, the console script will * use a broken putmemproc that does RMW on the full * 8 bytes containing out_pos and in_prod, thus corrupting * out_pos */ opos = con_in; if (con_wrapped) opos |= MEMCONS_OUT_POS_WRAP; lwsync(); memcons.out_pos = opos; /* If head reaches tail, push tail around & drop chars */ if (con_in == con_out) con_out = (con_in + 1) % INMEM_CON_OUT_LEN; } static size_t inmem_read(char *buf, size_t req) { size_t read = 0; char *ibuf = (char *)memcons.ibuf_phys; while (req && memcons.in_prod != memcons.in_cons) { *(buf++) = ibuf[memcons.in_cons]; lwsync(); memcons.in_cons = (memcons.in_cons + 1) % INMEM_CON_IN_LEN; req--; read++; } return read; } static void write_char(char c) { #ifdef MAMBO_DEBUG_CONSOLE mambo_write(&c, 1); #endif inmem_write(c); } ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count) { /* We use recursive locking here as we can get called * from fairly deep debug path */ bool need_unlock = lock_recursive(&con_lock); const char *cbuf = buf; while(count--) { char c = *(cbuf++); if (c == 10) write_char(13); write_char(c); } __flush_console(flush_to_drivers); if (need_unlock) unlock(&con_lock); return count; } ssize_t write(int fd __unused, const void *buf, size_t count) { return console_write(true, buf, count); } ssize_t read(int fd __unused, void *buf, size_t req_count) { bool need_unlock = lock_recursive(&con_lock); size_t count = 0; if (con_driver && con_driver->read) count = con_driver->read(buf, req_count); if (!count) count = inmem_read(buf, req_count); if (need_unlock) unlock(&con_lock); return count; } static int64_t opal_console_flush(int64_t term_number) { if (term_number != 0) return OPAL_PARAMETER; if (con_driver == NULL || con_driver->flush == NULL) return OPAL_UNSUPPORTED; return con_driver->flush(); } opal_call(OPAL_CONSOLE_FLUSH, opal_console_flush, 1); /* Helper function to perform a full synchronous flush */ void console_complete_flush(void) { int64_t ret = opal_console_flush(0); if (ret == OPAL_UNSUPPORTED || ret == OPAL_PARAMETER) return; while (ret != OPAL_SUCCESS) { ret = opal_console_flush(0); } } void set_console(struct con_ops *driver) { con_driver = driver; if (driver) flush_console(); } void memcons_add_properties(void) { uint64_t addr = (u64)&memcons; dt_add_property_cells(opal_node, "ibm,opal-memcons", hi32(addr), lo32(addr)); } /* * Default OPAL console provided if nothing else overrides it */ static int64_t dummy_console_write(int64_t term_number, int64_t *length, const uint8_t *buffer) { if (term_number != 0) return OPAL_PARAMETER; write(0, buffer, *length); return OPAL_SUCCESS; } opal_call(OPAL_CONSOLE_WRITE, dummy_console_write, 3); static int64_t dummy_console_write_buffer_space(int64_t term_number, int64_t *length) { if (term_number != 0) return OPAL_PARAMETER; if (length) *length = INMEM_CON_OUT_LEN; return OPAL_SUCCESS; } opal_call(OPAL_CONSOLE_WRITE_BUFFER_SPACE, dummy_console_write_buffer_space, 2); static int64_t dummy_console_read(int64_t term_number, int64_t *length, uint8_t *buffer) { if (term_number != 0) return OPAL_PARAMETER; *length = read(0, buffer, *length); opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0); return OPAL_SUCCESS; } opal_call(OPAL_CONSOLE_READ, dummy_console_read, 3); static void dummy_console_poll(void *data __unused) { bool has_data = false; lock(&con_lock); if (con_driver && con_driver->poll_read) has_data = con_driver->poll_read(); if (memcons.in_prod != memcons.in_cons) has_data = true; if (has_data) opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, OPAL_EVENT_CONSOLE_INPUT); else opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0); unlock(&con_lock); } void dummy_console_add_nodes(void) { struct dt_node *con, *consoles; struct dt_property *p; consoles = dt_new(opal_node, "consoles"); assert(consoles); dt_add_property_cells(consoles, "#address-cells", 1); dt_add_property_cells(consoles, "#size-cells", 0); con = dt_new_addr(consoles, "serial", 0); assert(con); dt_add_property_string(con, "compatible", "ibm,opal-console-raw"); dt_add_property_cells(con, "#write-buffer-size", INMEM_CON_OUT_LEN); dt_add_property_cells(con, "reg", 0); dt_add_property_string(con, "device_type", "serial"); /* Mambo might have left a crap one, clear it */ p = __dt_find_property(dt_chosen, "linux,stdout-path"); if (p) dt_del_property(dt_chosen, p); dt_add_property_string(dt_chosen, "linux,stdout-path", "/ibm,opal/consoles/serial@0"); opal_add_poller(dummy_console_poll, NULL); } skiboot-skiboot-5.1.13/core/cpu.c000066400000000000000000000423701265204436200166200ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * TODO: Index array by PIR to be able to catch them easily * from assembly such as machine checks etc... */ #include #include #include #include #include #include #include #include #include #include #include #include /* The cpu_threads array is static and indexed by PIR in * order to speed up lookup from asm entry points */ struct cpu_stack { union { uint8_t stack[STACK_SIZE]; struct cpu_thread cpu; }; } __align(STACK_SIZE); static struct cpu_stack *cpu_stacks = (struct cpu_stack *)CPU_STACKS_BASE; unsigned int cpu_thread_count; unsigned int cpu_max_pir; struct cpu_thread *boot_cpu; static struct lock reinit_lock = LOCK_UNLOCKED; static bool hile_supported; unsigned long cpu_secondary_start __force_data = 0; struct cpu_job { struct list_node link; void (*func)(void *data); void *data; const char *name; bool complete; bool no_return; }; static struct lock global_job_queue_lock = LOCK_UNLOCKED; static struct list_head global_job_queue; /* attribute const as cpu_stacks is constant. */ unsigned long __attrconst cpu_stack_bottom(unsigned int pir) { return ((unsigned long)&cpu_stacks[pir]) + sizeof(struct cpu_thread) + STACK_SAFETY_GAP; } unsigned long __attrconst cpu_stack_top(unsigned int pir) { /* This is the top of the MC stack which is above the normal * stack, which means a SP between cpu_stack_bottom() and * cpu_stack_top() can either be a normal stack pointer or * a Machine Check stack pointer */ return ((unsigned long)&cpu_stacks[pir]) + NORMAL_STACK_SIZE - STACK_TOP_GAP; } struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu, const char *name, void (*func)(void *data), void *data, bool no_return) { struct cpu_job *job; if (cpu && !cpu_is_available(cpu)) { prerror("CPU: Tried to queue job on unavailable CPU 0x%04x\n", cpu->pir); return NULL; } job = zalloc(sizeof(struct cpu_job)); if (!job) return NULL; job->func = func; job->data = data; job->name = name; job->complete = false; job->no_return = no_return; if (cpu == NULL) { lock(&global_job_queue_lock); list_add_tail(&global_job_queue, &job->link); unlock(&global_job_queue_lock); } else if (cpu != this_cpu()) { lock(&cpu->job_lock); list_add_tail(&cpu->job_queue, &job->link); unlock(&cpu->job_lock); } else { func(data); job->complete = true; } /* XXX Add poking of CPU with interrupt */ return job; } bool cpu_poll_job(struct cpu_job *job) { lwsync(); return job->complete; } void cpu_wait_job(struct cpu_job *job, bool free_it) { unsigned long ticks = usecs_to_tb(5); unsigned long period = msecs_to_tb(5); unsigned long time_waited = 0; if (!job) return; while(!job->complete) { time_wait(ticks); time_waited+=ticks; if (time_waited % period == 0) opal_run_pollers(); lwsync(); } lwsync(); smt_medium(); if (time_waited > msecs_to_tb(1000)) prlog(PR_DEBUG, "cpu_wait_job(%s) for %lu\n", job->name, tb_to_msecs(time_waited)); if (free_it) free(job); } void cpu_free_job(struct cpu_job *job) { if (!job) return; assert(job->complete); free(job); } void cpu_process_jobs(void) { struct cpu_thread *cpu = this_cpu(); struct cpu_job *job = NULL; void (*func)(void *); void *data; sync(); if (list_empty(&cpu->job_queue) && list_empty(&global_job_queue)) return; lock(&cpu->job_lock); while (true) { bool no_return; if (list_empty(&cpu->job_queue)) { smt_medium(); if (list_empty(&global_job_queue)) break; lock(&global_job_queue_lock); job = list_pop(&global_job_queue, struct cpu_job, link); unlock(&global_job_queue_lock); } else { smt_medium(); job = list_pop(&cpu->job_queue, struct cpu_job, link); } if (!job) break; func = job->func; data = job->data; no_return = job->no_return; unlock(&cpu->job_lock); prlog(PR_TRACE, "running job %s on %x\n", job->name, cpu->pir); if (no_return) free(job); func(data); lock(&cpu->job_lock); if (!no_return) { lwsync(); job->complete = true; } } unlock(&cpu->job_lock); } void cpu_process_local_jobs(void) { struct cpu_thread *cpu = first_available_cpu(); while (cpu) { if (cpu != this_cpu()) return; cpu = next_available_cpu(cpu); } if (!cpu) cpu = first_available_cpu(); /* No CPU to run on, just run synchro */ if (cpu == this_cpu()) { prlog_once(PR_DEBUG, "Processing jobs synchronously\n"); cpu_process_jobs(); } } struct dt_node *get_cpu_node(u32 pir) { struct cpu_thread *t = find_cpu_by_pir(pir); return t ? t->node : NULL; } /* This only covers primary, active cpus */ struct cpu_thread *find_cpu_by_chip_id(u32 chip_id) { struct cpu_thread *t; for_each_available_cpu(t) { if (t->is_secondary) continue; if (t->chip_id == chip_id) return t; } return NULL; } struct cpu_thread *find_cpu_by_node(struct dt_node *cpu) { struct cpu_thread *t; for_each_available_cpu(t) { if (t->node == cpu) return t; } return NULL; } struct cpu_thread *find_cpu_by_pir(u32 pir) { if (pir > cpu_max_pir) return NULL; return &cpu_stacks[pir].cpu; } struct cpu_thread *find_cpu_by_server(u32 server_no) { struct cpu_thread *t; for_each_cpu(t) { if (t->server_no == server_no) return t; } return NULL; } struct cpu_thread *next_cpu(struct cpu_thread *cpu) { struct cpu_stack *s = container_of(cpu, struct cpu_stack, cpu); unsigned int index; if (cpu == NULL) index = 0; else index = s - cpu_stacks + 1; for (; index <= cpu_max_pir; index++) { cpu = &cpu_stacks[index].cpu; if (cpu->state != cpu_state_no_cpu) return cpu; } return NULL; } struct cpu_thread *first_cpu(void) { return next_cpu(NULL); } struct cpu_thread *next_available_cpu(struct cpu_thread *cpu) { do { cpu = next_cpu(cpu); } while(cpu && !cpu_is_available(cpu)); return cpu; } struct cpu_thread *first_available_cpu(void) { return next_available_cpu(NULL); } struct cpu_thread *next_available_core_in_chip(struct cpu_thread *core, u32 chip_id) { do { core = next_cpu(core); } while(core && (!cpu_is_available(core) || core->chip_id != chip_id || core->is_secondary)); return core; } struct cpu_thread *first_available_core_in_chip(u32 chip_id) { return next_available_core_in_chip(NULL, chip_id); } uint32_t cpu_get_core_index(struct cpu_thread *cpu) { return pir_to_core_id(cpu->pir); } void cpu_remove_node(const struct cpu_thread *t) { struct dt_node *i; /* Find this cpu node */ dt_for_each_node(dt_root, i) { const struct dt_property *p; if (!dt_has_node_property(i, "device_type", "cpu")) continue; p = dt_find_property(i, "ibm,pir"); if (!p) continue; if (dt_property_get_cell(p, 0) == t->pir) { dt_free(i); return; } } prerror("CPU: Could not find cpu node %i to remove!\n", t->pir); abort(); } void cpu_disable_all_threads(struct cpu_thread *cpu) { unsigned int i; for (i = 0; i <= cpu_max_pir; i++) { struct cpu_thread *t = &cpu_stacks[i].cpu; if (t->primary == cpu->primary) t->state = cpu_state_disabled; } /* XXX Do something to actually stop the core */ } static void init_cpu_thread(struct cpu_thread *t, enum cpu_thread_state state, unsigned int pir) { init_lock(&t->job_lock); list_head_init(&t->job_queue); t->state = state; t->pir = pir; #ifdef STACK_CHECK_ENABLED t->stack_bot_mark = LONG_MAX; #endif assert(pir == container_of(t, struct cpu_stack, cpu) - cpu_stacks); } void pre_init_boot_cpu(void) { struct cpu_thread *cpu = this_cpu(); memset(cpu, 0, sizeof(struct cpu_thread)); } void init_boot_cpu(void) { unsigned int i, pir, pvr; pir = mfspr(SPR_PIR); pvr = mfspr(SPR_PVR); /* Get CPU family and other flags based on PVR */ switch(PVR_TYPE(pvr)) { case PVR_TYPE_P7: case PVR_TYPE_P7P: proc_gen = proc_gen_p7; break; case PVR_TYPE_P8E: case PVR_TYPE_P8: proc_gen = proc_gen_p8; hile_supported = PVR_VERS_MAJ(mfspr(SPR_PVR)) >= 2; break; case PVR_TYPE_P8NVL: proc_gen = proc_gen_p8; hile_supported = true; break; default: proc_gen = proc_gen_unknown; } /* Get a CPU thread count and an initial max PIR based on family */ switch(proc_gen) { case proc_gen_p7: cpu_thread_count = 4; cpu_max_pir = SPR_PIR_P7_MASK; prlog(PR_INFO, "CPU: P7 generation processor" "(max %d threads/core)\n", cpu_thread_count); break; case proc_gen_p8: cpu_thread_count = 8; cpu_max_pir = SPR_PIR_P8_MASK; prlog(PR_INFO, "CPU: P8 generation processor" "(max %d threads/core)\n", cpu_thread_count); break; default: prerror("CPU: Unknown PVR, assuming 1 thread\n"); cpu_thread_count = 1; cpu_max_pir = mfspr(SPR_PIR); } prlog(PR_DEBUG, "CPU: Boot CPU PIR is 0x%04x PVR is 0x%08x\n", pir, pvr); prlog(PR_DEBUG, "CPU: Initial max PIR set to 0x%x\n", cpu_max_pir); /* Clear the CPU structs */ for (i = 0; i <= cpu_max_pir; i++) memset(&cpu_stacks[i].cpu, 0, sizeof(struct cpu_thread)); /* Setup boot CPU state */ boot_cpu = &cpu_stacks[pir].cpu; init_cpu_thread(boot_cpu, cpu_state_active, pir); init_boot_tracebuf(boot_cpu); assert(this_cpu() == boot_cpu); list_head_init(&global_job_queue); } void init_all_cpus(void) { struct dt_node *cpus, *cpu; unsigned int thread, new_max_pir = 0; cpus = dt_find_by_path(dt_root, "/cpus"); assert(cpus); /* Iterate all CPUs in the device-tree */ dt_for_each_child(cpus, cpu) { unsigned int pir, server_no, chip_id; enum cpu_thread_state state; const struct dt_property *p; struct cpu_thread *t, *pt; /* Skip cache nodes */ if (strcmp(dt_prop_get(cpu, "device_type"), "cpu")) continue; server_no = dt_prop_get_u32(cpu, "reg"); /* If PIR property is absent, assume it's the same as the * server number */ pir = dt_prop_get_u32_def(cpu, "ibm,pir", server_no); /* We should always have an ibm,chip-id property */ chip_id = dt_get_chip_id(cpu); /* Only use operational CPUs */ if (!strcmp(dt_prop_get(cpu, "status"), "okay")) state = cpu_state_present; else state = cpu_state_unavailable; prlog(PR_INFO, "CPU: CPU from DT PIR=0x%04x Server#=0x%x" " State=%d\n", pir, server_no, state); /* Setup thread 0 */ assert(pir <= cpu_max_pir); t = pt = &cpu_stacks[pir].cpu; if (t != boot_cpu) { init_cpu_thread(t, state, pir); /* Each cpu gets its own later in init_trace_buffers */ t->trace = boot_cpu->trace; } t->server_no = server_no; t->primary = t; t->node = cpu; t->chip_id = chip_id; t->icp_regs = NULL; /* Will be set later */ t->core_hmi_state = 0; t->core_hmi_state_ptr = &t->core_hmi_state; t->thread_mask = 1; /* Add associativity properties */ add_core_associativity(t); /* Adjust max PIR */ if (new_max_pir < (pir + cpu_thread_count - 1)) new_max_pir = pir + cpu_thread_count - 1; /* Iterate threads */ p = dt_find_property(cpu, "ibm,ppc-interrupt-server#s"); if (!p) continue; for (thread = 1; thread < (p->len / 4); thread++) { prlog(PR_TRACE, "CPU: secondary thread %d found\n", thread); t = &cpu_stacks[pir + thread].cpu; init_cpu_thread(t, state, pir + thread); t->trace = boot_cpu->trace; t->server_no = ((const u32 *)p->prop)[thread]; t->is_secondary = true; t->primary = pt; t->node = cpu; t->chip_id = chip_id; t->core_hmi_state_ptr = &pt->core_hmi_state; t->thread_mask = 1 << thread; } prlog(PR_INFO, "CPU: %d secondary threads\n", thread); } cpu_max_pir = new_max_pir; prlog(PR_DEBUG, "CPU: New max PIR set to 0x%x\n", new_max_pir); adjust_cpu_stacks_alloc(); } void cpu_bringup(void) { struct cpu_thread *t; prlog(PR_INFO, "CPU: Setting up secondary CPU state\n"); op_display(OP_LOG, OP_MOD_CPU, 0x0000); /* Tell everybody to chime in ! */ prlog(PR_INFO, "CPU: Calling in all processors...\n"); cpu_secondary_start = 1; sync(); op_display(OP_LOG, OP_MOD_CPU, 0x0002); for_each_cpu(t) { if (t->state != cpu_state_present && t->state != cpu_state_active) continue; /* Add a callin timeout ? If so, call cpu_remove_node(t). */ while (t->state != cpu_state_active) { smt_very_low(); sync(); } smt_medium(); } prlog(PR_INFO, "CPU: All processors called in...\n"); op_display(OP_LOG, OP_MOD_CPU, 0x0003); } void cpu_callin(struct cpu_thread *cpu) { cpu->state = cpu_state_active; } static void opal_start_thread_job(void *data) { cpu_give_self_os(); /* We do not return, so let's mark the job as * complete */ start_kernel_secondary((uint64_t)data); } static int64_t opal_start_cpu_thread(uint64_t server_no, uint64_t start_address) { struct cpu_thread *cpu; struct cpu_job *job; cpu = find_cpu_by_server(server_no); if (!cpu) { prerror("OPAL: Start invalid CPU 0x%04llx !\n", server_no); return OPAL_PARAMETER; } prlog(PR_DEBUG, "OPAL: Start CPU 0x%04llx (PIR 0x%04x) -> 0x%016llx\n", server_no, cpu->pir, start_address); lock(&reinit_lock); if (!cpu_is_available(cpu)) { unlock(&reinit_lock); prerror("OPAL: CPU not active in OPAL !\n"); return OPAL_WRONG_STATE; } job = __cpu_queue_job(cpu, "start_thread", opal_start_thread_job, (void *)start_address, true); unlock(&reinit_lock); if (!job) { prerror("OPAL: Failed to create CPU start job !\n"); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } opal_call(OPAL_START_CPU, opal_start_cpu_thread, 2); static int64_t opal_query_cpu_status(uint64_t server_no, uint8_t *thread_status) { struct cpu_thread *cpu; cpu = find_cpu_by_server(server_no); if (!cpu) { prerror("OPAL: Query invalid CPU 0x%04llx !\n", server_no); return OPAL_PARAMETER; } if (!cpu_is_available(cpu) && cpu->state != cpu_state_os) { prerror("OPAL: CPU not active in OPAL nor OS !\n"); return OPAL_PARAMETER; } switch(cpu->state) { case cpu_state_os: *thread_status = OPAL_THREAD_STARTED; break; case cpu_state_active: /* Active in skiboot -> inactive in OS */ *thread_status = OPAL_THREAD_INACTIVE; break; default: *thread_status = OPAL_THREAD_UNAVAILABLE; } return OPAL_SUCCESS; } opal_call(OPAL_QUERY_CPU_STATUS, opal_query_cpu_status, 2); static int64_t opal_return_cpu(void) { prlog(PR_DEBUG, "OPAL: Returning CPU 0x%04x\n", this_cpu()->pir); __secondary_cpu_entry(); return OPAL_HARDWARE; /* Should not happen */ } opal_call(OPAL_RETURN_CPU, opal_return_cpu, 0); static void cpu_change_hile(void *hilep) { bool hile = *(bool *)hilep; unsigned long hid0; hid0 = mfspr(SPR_HID0); if (hile) hid0 |= SPR_HID0_HILE; else hid0 &= ~SPR_HID0_HILE; prlog(PR_DEBUG, "CPU: [%08x] HID0 set to 0x%016lx\n", this_cpu()->pir, hid0); set_hid0(hid0); this_cpu()->current_hile = hile; } static int64_t cpu_change_all_hile(bool hile) { struct cpu_thread *cpu; prlog(PR_INFO, "CPU: Switching HILE on all CPUs to %d\n", hile); for_each_available_cpu(cpu) { if (cpu->current_hile == hile) continue; if (cpu == this_cpu()) { cpu_change_hile(&hile); continue; } cpu_wait_job(cpu_queue_job(cpu, "cpu_change_hile", cpu_change_hile, &hile), true); } return OPAL_SUCCESS; } static int64_t opal_reinit_cpus(uint64_t flags) { struct cpu_thread *cpu; int64_t rc = OPAL_SUCCESS; int i; prerror("OPAL: Trying a CPU re-init with flags: 0x%llx\n", flags); lock(&reinit_lock); for (cpu = first_cpu(); cpu; cpu = next_cpu(cpu)) { if (cpu == this_cpu()) continue; if (cpu->state == cpu_state_os) { /* * That might be a race with return CPU during kexec * where we are still, wait a bit and try again */ for (i = 0; (i < 1000) && (cpu->state == cpu_state_os); i++) { unlock(&reinit_lock); time_wait_ms(1); lock(&reinit_lock); } if (cpu->state == cpu_state_os) { prerror("OPAL: CPU 0x%x not in OPAL !\n", cpu->pir); rc = OPAL_WRONG_STATE; goto bail; } } } /* * Now we need to mark ourselves "active" or we'll be skipped * by the various "for_each_active_..." calls done by slw_reinit() */ this_cpu()->state = cpu_state_active; /* * If the flags affect endianness and we are on P8 DD2 or later, then * use the HID bit. We use the PVR (we could use the EC level in * the chip but the PVR is more readily available). */ if (hile_supported && (flags & (OPAL_REINIT_CPUS_HILE_BE | OPAL_REINIT_CPUS_HILE_LE))) { bool hile = !!(flags & OPAL_REINIT_CPUS_HILE_LE); flags &= ~(OPAL_REINIT_CPUS_HILE_BE | OPAL_REINIT_CPUS_HILE_LE); rc = cpu_change_all_hile(hile); } /* If we have a P7, error out for LE switch, do nothing for BE */ if (proc_gen < proc_gen_p8) { if (flags & OPAL_REINIT_CPUS_HILE_LE) rc = OPAL_UNSUPPORTED; flags &= ~(OPAL_REINIT_CPUS_HILE_BE | OPAL_REINIT_CPUS_HILE_LE); } /* Any flags left ? */ if (flags != 0) rc = slw_reinit(flags); /* And undo the above */ this_cpu()->state = cpu_state_os; bail: unlock(&reinit_lock); return rc; } opal_call(OPAL_REINIT_CPUS, opal_reinit_cpus, 1); skiboot-skiboot-5.1.13/core/device.c000066400000000000000000000460231265204436200172670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include /* Used to give unique handles. */ u32 last_phandle = 0; struct dt_node *dt_root; struct dt_node *dt_chosen; static const char *take_name(const char *name) { if (!is_rodata(name) && !(name = strdup(name))) { prerror("Failed to allocate copy of name"); abort(); } return name; } static void free_name(const char *name) { if (!is_rodata(name)) free((char *)name); } static struct dt_node *new_node(const char *name) { struct dt_node *node = malloc(sizeof *node); if (!node) { prerror("Failed to allocate node\n"); abort(); } node->name = take_name(name); node->parent = NULL; list_head_init(&node->properties); list_head_init(&node->children); /* FIXME: locking? */ node->phandle = ++last_phandle; return node; } struct dt_node *dt_new_root(const char *name) { return new_node(name); } static const char *get_unitname(const struct dt_node *node) { const char *c = strchr(node->name, '@'); if (!c) return NULL; return c + 1; } int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b) { const char *a_unit = get_unitname(a); const char *b_unit = get_unitname(b); ptrdiff_t basenamelen = a_unit - a->name; /* sort hex unit addresses by number */ if (a_unit && b_unit && !strncmp(a->name, b->name, basenamelen)) { unsigned long long a_num, b_num; char *a_end, *b_end; a_num = strtoul(a_unit, &a_end, 16); b_num = strtoul(b_unit, &b_end, 16); /* only compare if the unit addr parsed correctly */ if (*a_end == 0 && *b_end == 0) return (a_num > b_num) - (a_num < b_num); } return strcmp(a->name, b->name); } bool dt_attach_root(struct dt_node *parent, struct dt_node *root) { struct dt_node *node; assert(!root->parent); if (list_empty(&parent->children)) { list_add(&parent->children, &root->list); root->parent = parent; return true; } dt_for_each_child(parent, node) { int cmp = dt_cmp_subnodes(node, root); /* Look for duplicates */ if (cmp == 0) { prerror("DT: %s failed, duplicate %s\n", __func__, root->name); return false; } /* insert before the first node that's larger * the the node we're inserting */ if (cmp > 0) break; } list_add_before(&parent->children, &root->list, &node->list); root->parent = parent; return true; } static inline void dt_destroy(struct dt_node *dn) { if (!dn) return; free_name(dn->name); free(dn); } struct dt_node *dt_new(struct dt_node *parent, const char *name) { struct dt_node *new; assert(parent); new = new_node(name); if (!dt_attach_root(parent, new)) { dt_destroy(new); return NULL; } return new; } struct dt_node *dt_new_addr(struct dt_node *parent, const char *name, uint64_t addr) { char *lname; struct dt_node *new; size_t len; assert(parent); len = strlen(name) + STR_MAX_CHARS(addr) + 2; lname = malloc(len); if (!lname) return NULL; snprintf(lname, len, "%s@%llx", name, (long long)addr); new = new_node(lname); free(lname); if (!dt_attach_root(parent, new)) { dt_destroy(new); return NULL; } return new; } struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name, uint64_t addr0, uint64_t addr1) { char *lname; struct dt_node *new; size_t len; assert(parent); len = strlen(name) + 2*STR_MAX_CHARS(addr0) + 3; lname = malloc(len); if (!lname) return NULL; snprintf(lname, len, "%s@%llx,%llx", name, (long long)addr0, (long long)addr1); new = new_node(lname); free(lname); if (!dt_attach_root(parent, new)) { dt_destroy(new); return NULL; } return new; } static struct dt_node *__dt_copy(struct dt_node *node, struct dt_node *parent, bool root) { struct dt_property *prop, *new_prop; struct dt_node *new_node, *child; new_node = dt_new(parent, node->name); if (!new_node) return NULL; list_for_each(&node->properties, prop, list) { new_prop = dt_add_property(new_node, prop->name, prop->prop, prop->len); if (!new_prop) goto fail; } list_for_each(&node->children, child, list) { child = __dt_copy(child, new_node, false); if (!child) goto fail; } return new_node; fail: /* dt_free will recurse for us, so only free when we unwind to the * top-level failure */ if (root) dt_free(new_node); return NULL; } struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent) { return __dt_copy(node, parent, true); } char *dt_get_path(const struct dt_node *node) { unsigned int len = 0; const struct dt_node *n; char *path, *p; /* Dealing with NULL is for test/debug purposes */ if (!node) return strdup(""); for (n = node; n; n = n->parent) { len += strlen(n->name); if (n->parent || n == node) len++; } path = zalloc(len + 1); assert(path); p = path + len; for (n = node; n; n = n->parent) { len = strlen(n->name); p -= len; memcpy(p, n->name, len); if (n->parent || n == node) *(--p) = '/'; } assert(p == path); return p; } static const char *__dt_path_split(const char *p, const char **namep, unsigned int *namel, const char **addrp, unsigned int *addrl) { const char *at, *sl; *namel = *addrl = 0; /* Skip initial '/' */ while (*p == '/') p++; /* Check empty path */ if (*p == 0) return p; at = strchr(p, '@'); sl = strchr(p, '/'); if (sl == NULL) sl = p + strlen(p); if (sl < at) at = NULL; if (at) { *addrp = at + 1; *addrl = sl - at - 1; } *namep = p; *namel = at ? (at - p) : (sl - p); return sl; } struct dt_node *dt_find_by_path(struct dt_node *root, const char *path) { struct dt_node *n; const char *pn, *pa, *p = path, *nn, *na; unsigned int pnl, pal, nnl, nal; bool match; /* Walk path components */ while (*p) { /* Extract next path component */ p = __dt_path_split(p, &pn, &pnl, &pa, &pal); if (pnl == 0 && pal == 0) break; /* Compare with each child node */ match = false; list_for_each(&root->children, n, list) { match = true; __dt_path_split(n->name, &nn, &nnl, &na, &nal); if (pnl && (pnl != nnl || strncmp(pn, nn, pnl))) match = false; if (pal && (pal != nal || strncmp(pa, na, pal))) match = false; if (match) { root = n; break; } } /* No child match */ if (!match) return NULL; } return root; } struct dt_node *dt_find_by_name(struct dt_node *root, const char *name) { struct dt_node *child, *match; list_for_each(&root->children, child, list) { if (!strcmp(child->name, name)) return child; match = dt_find_by_name(child, name); if (match) return match; } return NULL; } struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle) { struct dt_node *node; dt_for_each_node(root, node) if (node->phandle == phandle) return node; return NULL; } static struct dt_property *new_property(struct dt_node *node, const char *name, size_t size) { struct dt_property *p = malloc(sizeof(*p) + size); if (!p) { prerror("Failed to allocate property \"%s\" for %s of %zu bytes\n", name, dt_get_path(node), size); abort(); } if (dt_find_property(node, name)) { prerror("Duplicate property \"%s\" in node %s\n", name, dt_get_path(node)); abort(); } p->name = take_name(name); p->len = size; list_add_tail(&node->properties, &p->list); return p; } struct dt_property *dt_add_property(struct dt_node *node, const char *name, const void *val, size_t size) { struct dt_property *p; /* * Filter out phandle properties, we re-generate them * when flattening */ if (strcmp(name, "linux,phandle") == 0 || strcmp(name, "phandle") == 0) { assert(size == 4); node->phandle = *(const u32 *)val; if (node->phandle >= last_phandle) last_phandle = node->phandle; return NULL; } p = new_property(node, name, size); if (size) memcpy(p->prop, val, size); return p; } void dt_resize_property(struct dt_property **prop, size_t len) { size_t new_len = sizeof(**prop) + len; *prop = realloc(*prop, new_len); /* Fix up linked lists in case we moved. (note: not an empty list). */ (*prop)->list.next->prev = &(*prop)->list; (*prop)->list.prev->next = &(*prop)->list; } struct dt_property *dt_add_property_string(struct dt_node *node, const char *name, const char *value) { return dt_add_property(node, name, value, strlen(value)+1); } struct dt_property *dt_add_property_nstr(struct dt_node *node, const char *name, const char *value, unsigned int vlen) { struct dt_property *p; char *tmp = zalloc(vlen + 1); if (!tmp) return NULL; strncpy(tmp, value, vlen); p = dt_add_property(node, name, tmp, strlen(tmp)+1); free(tmp); return p; } struct dt_property *__dt_add_property_cells(struct dt_node *node, const char *name, int count, ...) { struct dt_property *p; u32 *val; unsigned int i; va_list args; p = new_property(node, name, count * sizeof(u32)); val = (u32 *)p->prop; va_start(args, count); for (i = 0; i < count; i++) val[i] = cpu_to_fdt32(va_arg(args, u32)); va_end(args); return p; } struct dt_property *__dt_add_property_u64s(struct dt_node *node, const char *name, int count, ...) { struct dt_property *p; u64 *val; unsigned int i; va_list args; p = new_property(node, name, count * sizeof(u64)); val = (u64 *)p->prop; va_start(args, count); for (i = 0; i < count; i++) val[i] = cpu_to_fdt64(va_arg(args, u64)); va_end(args); return p; } struct dt_property *__dt_add_property_strings(struct dt_node *node, const char *name, int count, ...) { struct dt_property *p; unsigned int i, size; va_list args; const char *sstr; char *s; va_start(args, count); for (i = size = 0; i < count; i++) { sstr = va_arg(args, const char *); if (sstr) size += strlen(sstr) + 1; } va_end(args); if (!size) size = 1; p = new_property(node, name, size); s = (char *)p->prop; *s = 0; va_start(args, count); for (i = 0; i < count; i++) { sstr = va_arg(args, const char *); if (sstr) { strcpy(s, sstr); s = s + strlen(sstr) + 1; } } va_end(args); return p; } void dt_del_property(struct dt_node *node, struct dt_property *prop) { list_del_from(&node->properties, &prop->list); free_name(prop->name); free(prop); } u32 dt_property_get_cell(const struct dt_property *prop, u32 index) { assert(prop->len >= (index+1)*sizeof(u32)); /* Always aligned, so this works. */ return fdt32_to_cpu(((const u32 *)prop->prop)[index]); } /* First child of this node. */ struct dt_node *dt_first(const struct dt_node *root) { return list_top(&root->children, struct dt_node, list); } /* Return next node, or NULL. */ struct dt_node *dt_next(const struct dt_node *root, const struct dt_node *prev) { /* Children? */ if (!list_empty(&prev->children)) return dt_first(prev); do { /* More siblings? */ if (prev->list.next != &prev->parent->children.n) return list_entry(prev->list.next, struct dt_node,list); /* No more siblings, move up to parent. */ prev = prev->parent; } while (prev != root); return NULL; } struct dt_property *__dt_find_property(struct dt_node *node, const char *name) { struct dt_property *i; list_for_each(&node->properties, i, list) if (strcmp(i->name, name) == 0) return i; return NULL; } const struct dt_property *dt_find_property(const struct dt_node *node, const char *name) { const struct dt_property *i; list_for_each(&node->properties, i, list) if (strcmp(i->name, name) == 0) return i; return NULL; } const struct dt_property *dt_require_property(const struct dt_node *node, const char *name, int wanted_len) { const struct dt_property *p = dt_find_property(node, name); if (!p) { const char *path = dt_get_path(node); prerror("DT: Missing required property %s/%s\n", path, name); assert(false); } if (wanted_len >= 0 && p->len != wanted_len) { const char *path = dt_get_path(node); prerror("DT: Unexpected property length %s/%s\n", path, name); prerror("DT: Expected len: %d got len: %zu\n", wanted_len, p->len); assert(false); } return p; } bool dt_has_node_property(const struct dt_node *node, const char *name, const char *val) { const struct dt_property *p = dt_find_property(node, name); if (!p) return false; if (!val) return true; return p->len == strlen(val) + 1 && memcmp(p->prop, val, p->len) == 0; } bool dt_prop_find_string(const struct dt_property *p, const char *s) { const char *c, *end; if (!p) return false; c = p->prop; end = c + p->len; while(c < end) { if (!strcasecmp(s, c)) return true; c += strlen(c) + 1; } return false; } bool dt_node_is_compatible(const struct dt_node *node, const char *compat) { const struct dt_property *p = dt_find_property(node, "compatible"); return dt_prop_find_string(p, compat); } struct dt_node *dt_find_compatible_node(struct dt_node *root, struct dt_node *prev, const char *compat) { struct dt_node *node; node = prev ? dt_next(root, prev) : root; for (; node; node = dt_next(root, node)) if (dt_node_is_compatible(node, compat)) return node; return NULL; } u64 dt_prop_get_u64(const struct dt_node *node, const char *prop) { const struct dt_property *p = dt_require_property(node, prop, 8); return ((u64)dt_property_get_cell(p, 0) << 32) | dt_property_get_cell(p, 1); } u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def) { const struct dt_property *p = dt_find_property(node, prop); if (!p) return def; return ((u64)dt_property_get_cell(p, 0) << 32) | dt_property_get_cell(p, 1); } u32 dt_prop_get_u32(const struct dt_node *node, const char *prop) { const struct dt_property *p = dt_require_property(node, prop, 4); return dt_property_get_cell(p, 0); } u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def) { const struct dt_property *p = dt_find_property(node, prop); if (!p) return def; return dt_property_get_cell(p, 0); } const void *dt_prop_get(const struct dt_node *node, const char *prop) { const struct dt_property *p = dt_require_property(node, prop, -1); return p->prop; } const void *dt_prop_get_def(const struct dt_node *node, const char *prop, void *def) { const struct dt_property *p = dt_find_property(node, prop); return p ? p->prop : def; } const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop, void *def, size_t *len) { const struct dt_property *p = dt_find_property(node, prop); *len = 0; if (p) *len = p->len; return p ? p->prop : def; } u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell) { const struct dt_property *p = dt_require_property(node, prop, -1); return dt_property_get_cell(p, cell); } u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop, u32 cell, u32 def) { const struct dt_property *p = dt_find_property(node, prop); if (!p) return def; return dt_property_get_cell(p, cell); } void dt_free(struct dt_node *node) { struct dt_node *child; struct dt_property *p; while ((child = list_top(&node->children, struct dt_node, list))) dt_free(child); while ((p = list_pop(&node->properties, struct dt_property, list))) { free_name(p->name); free(p); } if (node->parent) list_del_from(&node->parent->children, &node->list); dt_destroy(node); } int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node) { const struct fdt_property *prop; int offset, nextoffset, err; struct dt_node *child; const char *name; uint32_t tag; if (((err = fdt_check_header(fdt)) != 0) || ((err = _fdt_check_node_offset(fdt, fdt_node)) < 0)) { prerror("FDT: Error %d parsing node 0x%x\n", err, fdt_node); return -1; } nextoffset = err; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_PROP: prop = _fdt_offset_ptr(fdt, offset); name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); dt_add_property(node, name, prop->data, fdt32_to_cpu(prop->len)); break; case FDT_BEGIN_NODE: name = fdt_get_name(fdt, offset, NULL); child = dt_new_root(name); assert(child); nextoffset = dt_expand_node(child, fdt, offset); /* * This may fail in case of duplicate, keep it * going for now, we may ultimately want to * assert */ (void)dt_attach_root(node, child); break; case FDT_END: return -1; } } while (tag != FDT_END_NODE); return nextoffset; } void dt_expand(const void *fdt) { printf("FDT: Parsing fdt @%p\n", fdt); dt_root = dt_new_root(""); if (dt_expand_node(dt_root, fdt, 0) < 0) abort(); } u64 dt_get_number(const void *pdata, unsigned int cells) { const u32 *p = pdata; u64 ret = 0; while(cells--) ret = (ret << 32) | be32_to_cpu(*(p++)); return ret; } u32 dt_n_address_cells(const struct dt_node *node) { if (!node->parent) return 0; return dt_prop_get_u32_def(node->parent, "#address-cells", 2); } u32 dt_n_size_cells(const struct dt_node *node) { if (!node->parent) return 0; return dt_prop_get_u32_def(node->parent, "#size-cells", 1); } u64 dt_get_address(const struct dt_node *node, unsigned int index, u64 *out_size) { const struct dt_property *p; u32 na = dt_n_address_cells(node); u32 ns = dt_n_size_cells(node); u32 pos, n; p = dt_require_property(node, "reg", -1); n = (na + ns) * sizeof(u32); pos = n * index; assert((pos + n) <= p->len); if (out_size) *out_size = dt_get_number(p->prop + pos + na * sizeof(u32), ns); return dt_get_number(p->prop + pos, na); } static u32 __dt_get_chip_id(const struct dt_node *node) { const struct dt_property *prop; for (; node; node = node->parent) { prop = dt_find_property(node, "ibm,chip-id"); if (prop) return dt_property_get_cell(prop, 0); } return 0xffffffff; } u32 dt_get_chip_id(const struct dt_node *node) { u32 id = __dt_get_chip_id(node); assert(id != 0xffffffff); return id; } struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root, struct dt_node *prev, const char *compat, uint32_t chip_id) { struct dt_node *node; node = prev ? dt_next(root, prev) : root; for (; node; node = dt_next(root, node)) { u32 cid = __dt_get_chip_id(node); if (cid == chip_id && dt_node_is_compatible(node, compat)) return node; } return NULL; } unsigned int dt_count_addresses(const struct dt_node *node) { const struct dt_property *p; u32 na = dt_n_address_cells(node); u32 ns = dt_n_size_cells(node); u32 n; p = dt_require_property(node, "reg", -1); n = (na + ns) * sizeof(u32); if (n == 0) return 0; return p->len / n; } u64 dt_translate_address(const struct dt_node *node, unsigned int index, u64 *out_size) { /* XXX TODO */ return dt_get_address(node, index, out_size); } skiboot-skiboot-5.1.13/core/errorlog.c000066400000000000000000000125521265204436200176630ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This file contains the front end for OPAL error logging. It is used * to construct a struct errorlog representing the event/error to be * logged which is then passed to the platform specific backend to log * the actual errors. */ #include #include #include #include #include /* * Maximum number buffers that are pre-allocated * to hold elogs that are reported on Sapphire and * powernv. */ #define ELOG_WRITE_MAX_RECORD 64 /* Platform Log ID as per the spec */ static uint32_t sapphire_elog_id = 0xB0000000; /* Reserved for future use */ /* static uint32_t powernv_elog_id = 0xB1000000; */ /* Pool to allocate elog messages from */ static struct pool elog_pool; static struct lock elog_lock = LOCK_UNLOCKED; static bool elog_available = false; static struct errorlog *get_write_buffer(int opal_event_severity) { struct errorlog *buf; if (!elog_available) return NULL; lock(&elog_lock); if (opal_event_severity == OPAL_ERROR_PANIC) buf = pool_get(&elog_pool, POOL_HIGH); else buf = pool_get(&elog_pool, POOL_NORMAL); unlock(&elog_lock); return buf; } /* Reporting of error via struct errorlog */ struct errorlog *opal_elog_create(struct opal_err_info *e_info, uint32_t tag) { struct errorlog *buf; buf = get_write_buffer(e_info->sev); if (buf) { buf->error_event_type = e_info->err_type; buf->component_id = e_info->cmp_id; buf->subsystem_id = e_info->subsystem; buf->event_severity = e_info->sev; buf->event_subtype = e_info->event_subtype; buf->reason_code = e_info->reason_code; buf->elog_origin = ORG_SAPPHIRE; lock(&elog_lock); buf->plid = ++sapphire_elog_id; unlock(&elog_lock); /* Initialise the first user dump section */ log_add_section(buf, tag); } return buf; } /* Add a new user data section to an existing error log */ void log_add_section(struct errorlog *buf, uint32_t tag) { size_t size = sizeof(struct elog_user_data_section) - 1; struct elog_user_data_section *tmp; if (!buf) { prerror("ELOG: Cannot add user data section. " "Buffer is invalid\n"); return; } if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) { prerror("ELOG: Size of dump data overruns buffer\n"); return; } tmp = (struct elog_user_data_section *)(buf->user_data_dump + buf->user_section_size); /* Use DESC if no other tag provided */ tmp->tag = tag ? tag : 0x44455343; tmp->size = size; buf->user_section_size += tmp->size; buf->user_section_count++; } void opal_elog_complete(struct errorlog *buf, bool success) { if (!success) printf("Unable to log error\n"); lock(&elog_lock); pool_free_object(&elog_pool, buf); unlock(&elog_lock); } void log_commit(struct errorlog *elog) { int rc; if (!elog) return; if (platform.elog_commit) { rc = platform.elog_commit(elog); if (rc) prerror("ELOG: Platform commit error %d\n", rc); return; } opal_elog_complete(elog, false); } void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size) { struct elog_user_data_section *section; uint8_t n_sections; char *buffer; if (!buf) { prerror("ELOG: Cannot update user data. Buffer is invalid\n"); return; } if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) { prerror("ELOG: Size of dump data overruns buffer\n"); return; } /* Step through user sections to find latest dump section */ buffer = buf->user_data_dump; n_sections = buf->user_section_count; if (!n_sections) { prerror("ELOG: User section invalid\n"); return; } while (--n_sections) { section = (struct elog_user_data_section *)buffer; buffer += section->size; } section = (struct elog_user_data_section *)buffer; buffer += section->size; memcpy(buffer, data, size); section->size += size; buf->user_section_size += size; } void log_append_msg(struct errorlog *buf, const char *fmt, ...) { char err_msg[250]; va_list list; if (!buf) { prerror("Tried to append log to NULL buffer\n"); return; } va_start(list, fmt); vsnprintf(err_msg, sizeof(err_msg), fmt, list); va_end(list); /* Log the error on to Sapphire console */ prerror("%s", err_msg); log_append_data(buf, err_msg, strlen(err_msg)); } void log_simple_error(struct opal_err_info *e_info, const char *fmt, ...) { struct errorlog *buf; va_list list; char err_msg[250]; va_start(list, fmt); vsnprintf(err_msg, sizeof(err_msg), fmt, list); va_end(list); /* Log the error on to Sapphire console */ prerror("%s", err_msg); buf = opal_elog_create(e_info, 0); if (buf == NULL) prerror("ELOG: Error getting buffer to log error\n"); else { log_append_data(buf, err_msg, strlen(err_msg)); log_commit(buf); } } int elog_init(void) { /* pre-allocate memory for records */ if (pool_init(&elog_pool, sizeof(struct errorlog), ELOG_WRITE_MAX_RECORD, 1)) return OPAL_RESOURCE; elog_available = true; return 0; } skiboot-skiboot-5.1.13/core/exceptions.c000066400000000000000000000034771265204436200202170ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define REG "%016llx" #define REGS_PER_LINE 4 static void dump_regs(struct stack_frame *stack) { unsigned int i; prerror("SRR0 : "REG" SRR1 : "REG"\n", stack->srr0, stack->srr1); prerror("HSRR0: "REG" HSRR1: "REG"\n", stack->hsrr0, stack->hsrr1); prerror("LR : "REG" CTR : "REG"\n", stack->lr, stack->ctr); prerror("CFAR : "REG"\n", stack->cfar); prerror("CR : %08x XER: %08x\n", stack->cr, stack->xer); for (i = 0; i < 16; i++) prerror("GPR%02d: "REG" GPR%02d: "REG"\n", i, stack->gpr[i], i + 16, stack->gpr[i + 16]); } /* Called from head.S, thus no prototype */ void exception_entry(struct stack_frame *stack) __noreturn; void exception_entry(struct stack_frame *stack) { prerror("***********************************************\n"); prerror("Unexpected exception %llx !\n", stack->type); dump_regs(stack); abort(); } static int64_t opal_register_exc_handler(uint64_t opal_exception __unused, uint64_t handler_address __unused, uint64_t glue_cache_line __unused) { /* This interface is deprecated */ return OPAL_UNSUPPORTED; } opal_call(OPAL_REGISTER_OPAL_EXCEPTION_HANDLER, opal_register_exc_handler, 3); skiboot-skiboot-5.1.13/core/fast-reboot.c000066400000000000000000000202501265204436200202470ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include /* * To get control of all threads, we sreset them via XSCOM after * patching the 0x100 vector. This will work as long as the target * HRMOR is 0. If Linux ever uses HRMOR, we'll have to consider * a more messy approach. * * The SCOM register we want is called "Core RAS Control" in the doc * and EX0.EC.PC.TCTL_GENERATE#0.TCTL.DIRECT_CONTROLS in the SCOM list * * Bits in there change from CPU rev to CPU rev but the bit we care * about, bit 60 "sreset_request" appears to have stuck to the same * place in both P7 and P7+. The register also has the same SCOM * address */ #define EX0_TCTL_DIRECT_CONTROLS0 0x08010400 #define EX0_TCTL_DIRECT_CONTROLS1 0x08010440 #define EX0_TCTL_DIRECT_CONTROLS2 0x08010480 #define EX0_TCTL_DIRECT_CONTROLS3 0x080104c0 #define TCTL_DC_SRESET_REQUEST PPC_BIT(60) /* Flag tested by the OPAL entry code */ uint8_t reboot_in_progress; static struct cpu_thread *resettor, *resettee; static void flush_caches(void) { uint64_t base = SKIBOOT_BASE; uint64_t end = base + SKIBOOT_SIZE; /* Not sure what the effect of sreset is on cores, so let's * shoot a series of dcbf's on all cachelines that make up * our core memory just in case... */ while(base < end) { asm volatile("dcbf 0,%0" : : "r" (base) : "memory"); base += 128; } sync(); } static bool do_reset_core_p7(struct cpu_thread *cpu) { uint32_t xscom_addr, chip; uint64_t ctl; int rc; /* Add the Core# */ xscom_addr = EX0_TCTL_DIRECT_CONTROLS0; xscom_addr |= ((cpu->pir >> 2) & 7) << 24; chip = pir_to_chip_id(cpu->pir); ctl = TCTL_DC_SRESET_REQUEST; rc = xscom_write(chip, xscom_addr, ctl); rc |= xscom_write(chip, xscom_addr + 0x40, ctl); rc |= xscom_write(chip, xscom_addr + 0x80, ctl); rc |= xscom_write(chip, xscom_addr + 0xc0, ctl); if (rc) { prerror("RESET: Error %d resetting CPU 0x%04x\n", rc, cpu->pir); return false; } return true; } static void fast_reset_p7(void) { struct cpu_thread *cpu; resettee = this_cpu(); resettor = NULL; /* Pick up a candidate resettor. We do that before we flush * the caches */ for_each_cpu(cpu) { /* * Some threads might still be in skiboot. * * But because we deal with entire cores and we don't want * to special case things, we are just going to reset them * too making the assumption that this is safe, they are * holding no locks. This can only be true if they don't * have jobs scheduled which is hopefully the case. */ if (cpu->state != cpu_state_os && cpu->state != cpu_state_active) continue; /* * Only hit cores and only if they aren't on the same core * as ourselves */ if (cpu_get_thread0(cpu) == cpu_get_thread0(this_cpu()) || cpu->pir & 0x3) continue; /* Pick up one of those guys as our "resettor". It will be * in charge of resetting this CPU. We avoid resetting * ourselves, not sure how well it would do with SCOM */ resettor = cpu; break; } if (!resettor) { printf("RESET: Can't find a resettor !\n"); return; } printf("RESET: Resetting from 0x%04x, resettor 0x%04x\n", this_cpu()->pir, resettor->pir); printf("RESET: Flushing caches...\n"); /* Is that necessary ? */ flush_caches(); /* Reset everybody except self and except resettor */ for_each_cpu(cpu) { if (cpu->state != cpu_state_os && cpu->state != cpu_state_active) continue; if (cpu_get_thread0(cpu) == cpu_get_thread0(this_cpu()) || cpu->pir & 0x3) continue; if (cpu_get_thread0(cpu) == cpu_get_thread0(resettor)) continue; printf("RESET: Resetting CPU 0x%04x...\n", cpu->pir); if (!do_reset_core_p7(cpu)) return; } /* Reset the resettor last because it's going to kill me ! */ printf("RESET: Resetting CPU 0x%04x...\n", resettor->pir); if (!do_reset_core_p7(resettor)) return; /* Don't return */ for (;;) ; } void fast_reset(void) { uint32_t pvr = mfspr(SPR_PVR); extern uint32_t fast_reset_patch_start; extern uint32_t fast_reset_patch_end; uint32_t *dst, *src; printf("RESET: Fast reboot request !\n"); /* XXX We need a way to ensure that no other CPU is in skiboot * holding locks (via the OPAL APIs) and if they are, we need * for them to get out */ reboot_in_progress = 1; time_wait_ms(200); /* Copy reset trampoline */ printf("RESET: Copying reset trampoline...\n"); src = &fast_reset_patch_start; dst = (uint32_t *)0x100; while(src < &fast_reset_patch_end) *(dst++) = *(src++); sync_icache(); switch(PVR_TYPE(pvr)) { case PVR_TYPE_P7: case PVR_TYPE_P7P: fast_reset_p7(); } } static void cleanup_cpu_state(void) { if (cpu_is_thread0(this_cpu())) { cleanup_tlb(); init_shared_sprs(); } init_replicated_sprs(); reset_cpu_icp(); } #ifdef FAST_REBOOT_CLEARS_MEMORY static void fast_mem_clear(uint64_t start, uint64_t end) { printf("MEMORY: Clearing %llx..%llx\n", start, end); while(start < end) { asm volatile("dcbz 0,%0" : : "r" (start) : "memory"); start += 128; } } static void memory_reset(void) { struct address_range *i; uint64_t skistart = SKIBOOT_BASE; uint64_t skiend = SKIBOOT_BASE + SKIBOOT_SIZE; printf("MEMORY: Clearing ...\n"); list_for_each(&address_ranges, i, list) { uint64_t start = cleanup_addr(i->arange->start); uint64_t end = cleanup_addr(i->arange->end); if (start >= skiend || end <= skistart) fast_mem_clear(start, end); else { if (start < skistart) fast_mem_clear(start, skistart); if (end > skiend) fast_mem_clear(skiend, end); } } } #endif /* FAST_REBOOT_CLEARS_MEMORY */ /* Entry from asm after a fast reset */ void __noreturn fast_reboot(void); void __noreturn fast_reboot(void) { static volatile bool fast_boot_release; struct cpu_thread *cpu; printf("INIT: CPU PIR 0x%04x reset in\n", this_cpu()->pir); /* If this CPU was chosen as the resettor, it must reset the * resettee (the one that initiated the whole process */ if (this_cpu() == resettor) do_reset_core_p7(resettee); /* Are we the original boot CPU ? If not, we spin waiting * for a relase signal from CPU 1, then we clean ourselves * up and go processing jobs. */ if (this_cpu() != boot_cpu) { this_cpu()->state = cpu_state_present; while (!fast_boot_release) { smt_very_low(); sync(); } smt_medium(); cleanup_cpu_state(); __secondary_cpu_entry(); } /* We are the original boot CPU, wait for secondaries to * be captured */ for_each_cpu(cpu) { if (cpu == this_cpu()) continue; /* XXX Add a callin timeout ? */ while (cpu->state != cpu_state_present) { smt_very_low(); sync(); } smt_medium(); } printf("INIT: Releasing secondaries...\n"); /* Release everybody */ fast_boot_release = true; sync(); /* Wait for them to respond */ for_each_cpu(cpu) { if (cpu == this_cpu()) continue; /* XXX Add a callin timeout ? */ while (cpu->state == cpu_state_present) { smt_very_low(); sync(); } } printf("INIT: All done, resetting everything else...\n"); /* Clear release flag for next time */ fast_boot_release = false; reboot_in_progress = 0; /* Cleanup ourselves */ cleanup_cpu_state(); /* Set our state to active */ this_cpu()->state = cpu_state_active; /* Poke the consoles (see comments in the code there) */ fsp_console_reset(); /* Reset/EOI the PSI interrupt */ psi_irq_reset(); /* Remove all PCI devices */ pci_reset(); /* Reset IO Hubs */ cec_reset(); /* Re-Initialize all discovered PCI slots */ pci_init_slots(); /* Clear memory */ #ifdef FAST_REBOOT_CLEARS_MEMORY memory_reset(); #endif load_and_boot_kernel(true); } skiboot-skiboot-5.1.13/core/fdt.c000066400000000000000000000106051265204436200166020ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include static int fdt_error; static void *fdt; #undef DEBUG_FDT static void __save_err(int err, const char *str) { #ifdef DEBUG_FDT printf("FDT: rc: %d from \"%s\"\n", err, str); #endif if (err && !fdt_error) { prerror("FDT: Error %d from \"%s\"\n", err, str); fdt_error = err; } } #define save_err(...) __save_err(__VA_ARGS__, #__VA_ARGS__) static void dt_property_cell(const char *name, u32 cell) { save_err(fdt_property_cell(fdt, name, cell)); } static void dt_begin_node(const char *name, uint32_t phandle) { save_err(fdt_begin_node(fdt, name)); /* * We add both the new style "phandle" and the legacy * "linux,phandle" properties */ dt_property_cell("linux,phandle", phandle); dt_property_cell("phandle", phandle); } static void dt_property(const char *name, const void *val, size_t size) { save_err(fdt_property(fdt, name, val, size)); } static void dt_end_node(void) { save_err(fdt_end_node(fdt)); } static void dump_fdt(void) { #ifdef DEBUG_FDT int i, off, depth, err; printf("Device tree %u@%p\n", fdt_totalsize(fdt), fdt); err = fdt_check_header(fdt); if (err) { prerror("fdt_check_header: %s\n", fdt_strerror(err)); return; } printf("fdt_check_header passed\n"); printf("fdt_num_mem_rsv = %u\n", fdt_num_mem_rsv(fdt)); for (i = 0; i < fdt_num_mem_rsv(fdt); i++) { u64 addr, size; err = fdt_get_mem_rsv(fdt, i, &addr, &size); if (err) { printf(" ERR %s\n", fdt_strerror(err)); return; } printf(" mem_rsv[%i] = %lu@%#lx\n", i, (long)addr, (long)size); } for (off = fdt_next_node(fdt, 0, &depth); off > 0; off = fdt_next_node(fdt, off, &depth)) { int len; const char *name; name = fdt_get_name(fdt, off, &len); if (!name) { prerror("fdt: offset %i no name!\n", off); return; } printf("name: %s [%u]\n", name, off); } #endif } static void flatten_dt_node(const struct dt_node *root) { const struct dt_node *i; const struct dt_property *p; #ifdef DEBUG_FDT printf("FDT: node: %s\n", root->name); #endif list_for_each(&root->properties, p, list) { if (strstarts(p->name, DT_PRIVATE)) continue; #ifdef DEBUG_FDT printf("FDT: prop: %s size: %ld\n", p->name, p->len); #endif dt_property(p->name, p->prop, p->len); } list_for_each(&root->children, i, list) { dt_begin_node(i->name, i->phandle); flatten_dt_node(i); dt_end_node(); } } static void create_dtb_reservemap(const struct dt_node *root) { uint64_t base, size; const uint64_t *ranges; const struct dt_property *prop; int i; /* Duplicate the reserved-ranges property into the fdt reservemap */ prop = dt_find_property(root, "reserved-ranges"); if (prop) { ranges = (const void *)prop->prop; for (i = 0; i < prop->len / (sizeof(uint64_t) * 2); i++) { base = *(ranges++); size = *(ranges++); save_err(fdt_add_reservemap_entry(fdt, base, size)); } } save_err(fdt_finish_reservemap(fdt)); } void *create_dtb(const struct dt_node *root) { size_t len = DEVICE_TREE_MAX_SIZE; uint32_t old_last_phandle = last_phandle; do { if (fdt) free(fdt); last_phandle = old_last_phandle; fdt_error = 0; fdt = malloc(len); if (!fdt) { prerror("dtb: could not malloc %lu\n", (long)len); return NULL; } fdt_create(fdt, len); create_dtb_reservemap(root); /* Open root node */ dt_begin_node(root->name, root->phandle); /* Unflatten our live tree */ flatten_dt_node(root); /* Close root node */ dt_end_node(); save_err(fdt_finish(fdt)); if (!fdt_error) break; len *= 2; } while (fdt_error == -FDT_ERR_NOSPACE); dump_fdt(); if (fdt_error) { prerror("dtb: error %s\n", fdt_strerror(fdt_error)); return NULL; } return fdt; } skiboot-skiboot-5.1.13/core/flash.c000066400000000000000000000376111265204436200171300ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include struct flash { bool registered; bool busy; struct blocklevel_device *bl; uint32_t size; uint32_t block_size; }; #define MAX_FLASH 1 static struct flash flashes[MAX_FLASH]; static struct flash *system_flash; /* Using a single lock as we only have one flash at present. */ static struct lock flash_lock; /* nvram-on-flash support */ static struct flash *nvram_flash; static u32 nvram_offset, nvram_size; bool flash_reserve(void) { bool rc = false; if (!try_lock(&flash_lock)) return false; if (!system_flash->busy) { system_flash->busy = true; rc = true; } unlock(&flash_lock); return rc; } void flash_release(void) { lock(&flash_lock); system_flash->busy = false; unlock(&flash_lock); } static int flash_nvram_info(uint32_t *total_size) { int rc; lock(&flash_lock); if (!nvram_flash) { rc = OPAL_HARDWARE; } else if (nvram_flash->busy) { rc = OPAL_BUSY; } else { *total_size = nvram_size; rc = OPAL_SUCCESS; } unlock(&flash_lock); return rc; } static int flash_nvram_start_read(void *dst, uint32_t src, uint32_t len) { int rc; if (!try_lock(&flash_lock)) return OPAL_BUSY; if (!nvram_flash) { rc = OPAL_HARDWARE; goto out; } if (nvram_flash->busy) { rc = OPAL_BUSY; goto out; } if ((src + len) > nvram_size) { prerror("FLASH_NVRAM: read out of bound (0x%x,0x%x)\n", src, len); rc = OPAL_PARAMETER; goto out; } rc = blocklevel_read(nvram_flash->bl, nvram_offset + src, dst, len); out: unlock(&flash_lock); if (!rc) nvram_read_complete(true); return rc; } static int flash_nvram_write(uint32_t dst, void *src, uint32_t len) { int rc; if (!try_lock(&flash_lock)) return OPAL_BUSY; if (nvram_flash->busy) { rc = OPAL_BUSY; goto out; } /* TODO: When we have async jobs for PRD, turn this into one */ if ((dst + len) > nvram_size) { prerror("FLASH_NVRAM: write out of bound (0x%x,0x%x)\n", dst, len); rc = OPAL_PARAMETER; goto out; } rc = blocklevel_write(nvram_flash->bl, nvram_offset + dst, src, len); out: unlock(&flash_lock); return rc; } static int flash_nvram_probe(struct flash *flash, struct ffs_handle *ffs) { uint32_t start, size, part; int rc; prlog(PR_INFO, "FLASH: probing for NVRAM\n"); rc = ffs_lookup_part(ffs, "NVRAM", &part); if (rc) { prlog(PR_WARNING, "FLASH: no NVRAM partition found\n"); return OPAL_HARDWARE; } rc = ffs_part_info(ffs, part, NULL, &start, &size, NULL, NULL); if (rc) { prlog(PR_ERR, "FLASH: Can't parse ffs info for NVRAM\n"); return OPAL_HARDWARE; } nvram_flash = flash; nvram_offset = start; nvram_size = size; platform.nvram_info = flash_nvram_info; platform.nvram_start_read = flash_nvram_start_read; platform.nvram_write = flash_nvram_write; return 0; } /* core flash support */ static struct dt_node *flash_add_dt_node(struct flash *flash, int id) { struct dt_node *flash_node; flash_node = dt_new_addr(opal_node, "flash", id); dt_add_property_strings(flash_node, "compatible", "ibm,opal-flash"); dt_add_property_cells(flash_node, "ibm,opal-id", id); dt_add_property_cells(flash_node, "reg", 0, flash->size); dt_add_property_cells(flash_node, "ibm,flash-block-size", flash->block_size); /* we fix to 32-bits */ dt_add_property_cells(flash_node, "#address-cells", 1); dt_add_property_cells(flash_node, "#size-cells", 1); return flash_node; } static void setup_system_flash(struct flash *flash, struct dt_node *node, const char *name, struct ffs_handle *ffs) { char *path; if (system_flash) { prlog(PR_WARNING, "FLASH: attempted to register a second " "system flash device %s\n", name); return; } if (!ffs) { prlog(PR_WARNING, "FLASH: attempted to register system flash " "%s, wwhich has no partition info\n", name); return; } system_flash = flash; path = dt_get_path(node); dt_add_property_string(dt_chosen, "ibm,system-flash", path); free(path); prlog(PR_INFO, "FLASH: registered system flash device %s\n", name); flash_nvram_probe(flash, ffs); } int flash_register(struct blocklevel_device *bl, bool is_system_flash) { uint32_t size, block_size; struct ffs_handle *ffs; struct dt_node *node; struct flash *flash = NULL; const char *name; unsigned int i; int rc; rc = blocklevel_get_info(bl, &name, &size, &block_size); if (rc) return rc; prlog(PR_INFO, "FLASH: registering flash device %s " "(size 0x%x, blocksize 0x%x)\n", name ?: "(unnamed)", size, block_size); lock(&flash_lock); for (i = 0; i < ARRAY_SIZE(flashes); i++) { if (flashes[i].registered) continue; flash = &flashes[i]; flash->registered = true; flash->busy = false; flash->bl = bl; flash->size = size; flash->block_size = block_size; break; } if (!flash) { unlock(&flash_lock); prlog(PR_ERR, "FLASH: No flash slots available\n"); return OPAL_RESOURCE; } rc = ffs_init(0, flash->size, bl, &ffs, 0); if (rc) { prlog(PR_WARNING, "FLASH: No ffs info; " "using raw device only\n"); ffs = NULL; } node = flash_add_dt_node(flash, i); if (is_system_flash) setup_system_flash(flash, node, name, ffs); if (ffs) ffs_close(ffs); unlock(&flash_lock); return OPAL_SUCCESS; } enum flash_op { FLASH_OP_READ, FLASH_OP_WRITE, FLASH_OP_ERASE, }; static int64_t opal_flash_op(enum flash_op op, uint64_t id, uint64_t offset, uint64_t buf, uint64_t size, uint64_t token) { struct flash *flash; int rc; if (id >= ARRAY_SIZE(flashes)) return OPAL_PARAMETER; if (!try_lock(&flash_lock)) return OPAL_BUSY; flash = &flashes[id]; if (flash->busy) { rc = OPAL_BUSY; goto err; } if (!flash->registered) { rc = OPAL_PARAMETER; goto err; } if (size >= flash->size || offset >= flash->size || offset + size > flash->size) { rc = OPAL_PARAMETER; goto err; } switch (op) { case FLASH_OP_READ: rc = blocklevel_read(flash->bl, offset, (void *)buf, size); break; case FLASH_OP_WRITE: /* * Note: blocklevel_write() uses flash_smart_write(), this call used to * be flash_write() */ rc = blocklevel_write(flash->bl, offset, (void *)buf, size); break; case FLASH_OP_ERASE: rc = blocklevel_erase(flash->bl, offset, size); break; default: assert(0); } if (rc) { rc = OPAL_HARDWARE; goto err; } unlock(&flash_lock); opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc); return OPAL_ASYNC_COMPLETION; err: unlock(&flash_lock); return rc; } static int64_t opal_flash_read(uint64_t id, uint64_t offset, uint64_t buf, uint64_t size, uint64_t token) { return opal_flash_op(FLASH_OP_READ, id, offset, buf, size, token); } static int64_t opal_flash_write(uint64_t id, uint64_t offset, uint64_t buf, uint64_t size, uint64_t token) { return opal_flash_op(FLASH_OP_WRITE, id, offset, buf, size, token); } static int64_t opal_flash_erase(uint64_t id, uint64_t offset, uint64_t size, uint64_t token) { return opal_flash_op(FLASH_OP_ERASE, id, offset, 0L, size, token); } opal_call(OPAL_FLASH_READ, opal_flash_read, 5); opal_call(OPAL_FLASH_WRITE, opal_flash_write, 5); opal_call(OPAL_FLASH_ERASE, opal_flash_erase, 4); /* flash resource API */ static struct { enum resource_id id; uint32_t subid; char name[PART_NAME_MAX+1]; } part_name_map[] = { { RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE, "BOOTKERNEL" }, { RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE, "ROOTFS" }, { RESOURCE_ID_CAPP, RESOURCE_SUBID_SUPPORTED, "CAPP" }, }; /* This mimics the hostboot SBE format */ #define FLASH_SUBPART_ALIGNMENT 0x1000 #define FLASH_SUBPART_HEADER_SIZE FLASH_SUBPART_ALIGNMENT struct flash_hostboot_toc { be32 ec; be32 offset; /* From start of header. 4K aligned */ be32 size; }; #define FLASH_HOSTBOOT_TOC_MAX_ENTRIES ((FLASH_SUBPART_HEADER_SIZE - 8) \ /sizeof(struct flash_hostboot_toc)) struct flash_hostboot_header { char eyecatcher[4]; be32 version; struct flash_hostboot_toc toc[FLASH_HOSTBOOT_TOC_MAX_ENTRIES]; }; /* start and total size include ECC */ static int flash_find_subpartition(struct blocklevel_device *bl, uint32_t subid, uint32_t *start, uint32_t *total_size, bool *ecc) { struct flash_hostboot_header *header; char eyecatcher[5]; uint32_t i, partsize; int rc; header = malloc(FLASH_SUBPART_HEADER_SIZE); if (!header) return false; /* Get raw partition size without ECC */ partsize = *total_size; if (ecc) partsize = ecc_buffer_size_minus_ecc(*total_size); /* Get the TOC */ rc = flash_read_corrected(bl, *start, header, FLASH_SUBPART_HEADER_SIZE, ecc); if (rc) { prerror("FLASH: flash subpartition TOC read failed %i\n", rc); goto end; } /* Perform sanity */ i = be32_to_cpu(header->version); if (i != 1) { prerror("FLASH: flash subpartition TOC version unknown %i\n", i); rc = OPAL_RESOURCE; goto end; } /* NULL terminate eyecatcher */ strncpy(eyecatcher, header->eyecatcher, 4); eyecatcher[4] = 0; prlog(PR_DEBUG, "FLASH: flash subpartition eyecatcher %s\n", eyecatcher); rc = OPAL_RESOURCE; for (i = 0; i< FLASH_HOSTBOOT_TOC_MAX_ENTRIES; i++) { uint32_t ec, offset, size; ec = be32_to_cpu(header->toc[i].ec); offset = be32_to_cpu(header->toc[i].offset); size = be32_to_cpu(header->toc[i].size); /* Check for null terminating entry */ if (!ec && !offset && !size) { prerror("FLASH: flash subpartition not found.\n"); goto end; } if (ec != subid) continue; /* Sanity check the offset and size. */ if (offset + size > partsize) { prerror("FLASH: flash subpartition too big: %i\n", i); goto end; } if (!size) { prerror("FLASH: flash subpartition zero size: %i\n", i); goto end; } if (offset < FLASH_SUBPART_HEADER_SIZE) { prerror("FLASH: flash subpartition " "offset too small: %i\n", i); goto end; } prlog(PR_DEBUG, "FLASH: flash found subpartition: " "%i size: %i offset %i\n", i, size, offset); /* * Adjust the start and size. The start location in the needs * to account for ecc but the size doesn't. */ *start += offset; *total_size = size; if (ecc) { *start += ecc_size(offset); *total_size += ecc_size(size); } rc = 0; goto end; } end: free(header); return rc; } /* * load a resource from FLASH * buf and len shouldn't account for ECC even if partition is ECCed. */ static int flash_load_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len) { int i, rc, part_num, part_size, part_start, size; struct ffs_handle *ffs; struct flash *flash; const char *name; bool status, ecc; rc = OPAL_RESOURCE; status = false; lock(&flash_lock); if (!system_flash) goto out_unlock; flash = system_flash; if (flash->busy) goto out_unlock; for (i = 0, name = NULL; i < ARRAY_SIZE(part_name_map); i++) { if (part_name_map[i].id == id) { name = part_name_map[i].name; break; } } if (!name) { prerror("FLASH: Couldn't find partition for id %d\n", id); goto out_unlock; } /* * If partition doesn't have a subindex but the caller specifies one, * we fail. eg. kernel partition doesn't have a subindex */ if ((part_name_map[i].subid == RESOURCE_SUBID_NONE) && (subid != RESOURCE_SUBID_NONE)) { prerror("PLAT: Partition %s doesn't have subindex\n", name); goto out_unlock; } rc = ffs_init(0, flash->size, flash->bl, &ffs, 0); if (rc) { prerror("FLASH: Can't open ffs handle\n"); goto out_unlock; } rc = ffs_lookup_part(ffs, name, &part_num); if (rc) { prerror("FLASH: No %s partition\n", name); goto out_free_ffs; } rc = ffs_part_info(ffs, part_num, NULL, &part_start, &part_size, NULL, &ecc); if (rc) { prerror("FLASH: Failed to get %s partition info\n", name); goto out_free_ffs; } prlog(PR_DEBUG,"FLASH: %s partition %s ECC\n", name, ecc ? "has" : "doesn't have"); /* * part_start/size are raw pointers into the partition. * ie. they will account for ECC if included. */ /* Find the sub partition if required */ if (subid != RESOURCE_SUBID_NONE) { rc = flash_find_subpartition(flash->bl, subid, &part_start, &part_size, &ecc); if (rc) goto out_free_ffs; } /* Work out what the final size of buffer will be without ECC */ size = part_size; if (ecc) { if (ecc_buffer_size_check(part_size)) { prerror("FLASH: %s image invalid size for ECC %d\n", name, part_size); goto out_free_ffs; } size = ecc_buffer_size_minus_ecc(part_size); } if (size > *len) { prerror("FLASH: %s image too large (%d > %zd)\n", name, part_size, *len); goto out_free_ffs; } rc = flash_read_corrected(flash->bl, part_start, buf, size, ecc); if (rc) { prerror("FLASH: failed to read %s partition\n", name); goto out_free_ffs; } *len = size; status = true; out_free_ffs: ffs_close(ffs); out_unlock: unlock(&flash_lock); return status ? OPAL_SUCCESS : rc; } struct flash_load_resource_item { enum resource_id id; uint32_t subid; int result; void *buf; size_t *len; struct list_node link; }; static LIST_HEAD(flash_load_resource_queue); static LIST_HEAD(flash_loaded_resources); static struct lock flash_load_resource_lock = LOCK_UNLOCKED; static struct cpu_job *flash_load_job = NULL; int flash_resource_loaded(enum resource_id id, uint32_t subid) { struct flash_load_resource_item *resource = NULL; struct flash_load_resource_item *r; int rc = OPAL_BUSY; lock(&flash_load_resource_lock); list_for_each(&flash_loaded_resources, r, link) { if (r->id == id && r->subid == subid) { resource = r; break; } } if (resource) { rc = resource->result; list_del(&resource->link); free(resource); } if (list_empty(&flash_load_resource_queue) && flash_load_job) { cpu_wait_job(flash_load_job, true); flash_load_job = NULL; } unlock(&flash_load_resource_lock); return rc; } static void flash_load_resources(void *data __unused) { struct flash_load_resource_item *r; int result; lock(&flash_load_resource_lock); do { if (list_empty(&flash_load_resource_queue)) { break; } r = list_top(&flash_load_resource_queue, struct flash_load_resource_item, link); if (r->result != OPAL_EMPTY) prerror("flash_load_resources() list_top unexpected " " result %d\n", r->result); r->result = OPAL_BUSY; unlock(&flash_load_resource_lock); result = flash_load_resource(r->id, r->subid, r->buf, r->len); lock(&flash_load_resource_lock); r = list_pop(&flash_load_resource_queue, struct flash_load_resource_item, link); r->result = result; list_add_tail(&flash_loaded_resources, &r->link); } while(true); unlock(&flash_load_resource_lock); } static void start_flash_load_resource_job(void) { if (flash_load_job) cpu_wait_job(flash_load_job, true); flash_load_job = cpu_queue_job(NULL, "flash_load_resources", flash_load_resources, NULL); cpu_process_local_jobs(); } int flash_start_preload_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len) { struct flash_load_resource_item *r; bool start_thread = false; r = malloc(sizeof(struct flash_load_resource_item)); assert(r != NULL); r->id = id; r->subid = subid; r->buf = buf; r->len = len; r->result = OPAL_EMPTY; printf("FLASH: Queueing preload of %x/%x\n", r->id, r->subid); lock(&flash_load_resource_lock); if (list_empty(&flash_load_resource_queue)) { start_thread = true; } list_add_tail(&flash_load_resource_queue, &r->link); unlock(&flash_load_resource_lock); if (start_thread) start_flash_load_resource_job(); return OPAL_SUCCESS; } skiboot-skiboot-5.1.13/core/gcov-profiling.c000066400000000000000000000061101265204436200207460ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include typedef long gcov_type; /* * This is GCC internal data structure. See GCC libgcc/libgcov.h for * details. * * If gcc changes this, we have to change it. */ typedef unsigned int gcov_unsigned_int; #if __GNUC__ == 4 && __GNUC_MINOR__ >= 9 #define GCOV_COUNTERS 9 #else #define GCOV_COUNTERS 8 #endif struct gcov_info { gcov_unsigned_int version; struct gcov_info *next; gcov_unsigned_int stamp; const char *filename; void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int); unsigned int n_functions; struct gcov_fn_info **functions; }; /* We have a list of all gcov info set up at startup */ struct gcov_info *gcov_info_list; void __gcov_init(struct gcov_info* f); void skiboot_gcov_done(void); void __gcov_flush(void) __attrconst; void __gcov_merge_add(gcov_type *counters, unsigned int n_counters) __attrconst; void __gcov_merge_single(gcov_type *counters, unsigned int n_counters) __attrconst; void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters) __attrconst; void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters) __attrconst; void __gcov_merge_time_profile(gcov_type *counters, unsigned int n_counters) __attrconst; void __gcov_init(struct gcov_info* f) { static gcov_unsigned_int version = 0; if (version == 0) { printf("GCOV version: %u\n", f->version); version = f->version; } if (gcov_info_list) f->next = gcov_info_list; gcov_info_list = f; return; } void skiboot_gcov_done(void) { struct gcov_info *i = gcov_info_list; if (i->filename) printf("GCOV: gcov_info_list looks sane (first file: %s)\n", i->filename); else prlog(PR_WARNING, "GCOV: gcov_info_list doesn't look sane. " "i->filename == NULL."); printf("GCOV: gcov_info_list at 0x%p\n", gcov_info_list); } void __gcov_merge_add(gcov_type *counters, unsigned int n_counters) { (void)counters; (void)n_counters; return; } void __gcov_flush(void) { return; } void __gcov_merge_single(gcov_type *counters, unsigned int n_counters) { (void)counters; (void)n_counters; return; } void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters) { (void)counters; (void)n_counters; return; } void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters) { (void)counters; (void)n_counters; return; } void __gcov_merge_time_profile(gcov_type *counters, unsigned int n_counters) { (void)counters; (void)n_counters; } skiboot-skiboot-5.1.13/core/hmi.c000066400000000000000000000656461265204436200166210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include /* * HMER register layout: * +===+==========+============================+========+===================+ * |Bit|Name |Description |PowerKVM|Action | * | | | |HMI | | * | | | |enabled | | * | | | |for this| | * | | | |bit ? | | * +===+==========+============================+========+===================+ * |0 |malfunctio|A processor core in the |Yes |Raise attn from | * | |n_allert |system has checkstopped | |sapphire resulting | * | | |(failed recovery) and has | |xstop | * | | |requested a CP Sparing | | | * | | |to occur. This is | | | * | | |broadcasted to every | | | * | | |processor in the system | | | * |---+----------+----------------------------+--------+-------------------| * |1 |Reserved |reserved |n/a | | * |---+----------+----------------------------+--------+-------------------| * |2 |proc_recv_|Processor recovery occurred |Yes |Log message and | * | |done |error-bit in fir not masked | |continue working. | * | | |(see bit 11) | | | * |---+----------+----------------------------+--------+-------------------| * |3 |proc_recv_|Processor went through |Yes |Log message and | * | |error_mask|recovery for an error which | |continue working. | * | |ed |is actually masked for | | | * | | |reporting | | | * |---+----------+----------------------------+--------+-------------------| * |4 | |Timer facility experienced |Yes |Raise attn from | * | |tfac_error|an error. | |sapphire resulting | * | | |TB, DEC, HDEC, PURR or SPURR| |xstop | * | | |may be corrupted (details in| | | * | | |TFMR) | | | * |---+----------+----------------------------+--------+-------------------| * |5 | |TFMR SPR itself is |Yes |Raise attn from | * | |tfmr_parit|corrupted. | |sapphire resulting | * | |y_error |Entire timing facility may | |xstop | * | | |be compromised. | | | * |---+----------+----------------------------+--------+-------------------| * |6 |ha_overflo| UPS (Uniterrupted Power |No |N/A | * | |w_warning |System) Overflow indication | | | * | | |indicating that the UPS | | | * | | |DirtyAddrTable has | | | * | | |reached a limit where it | | | * | | |requires PHYP unload support| | | * |---+----------+----------------------------+--------+-------------------| * |7 |reserved |reserved |n/a |n/a | * |---+----------+----------------------------+--------+-------------------| * |8 |xscom_fail|An XSCOM operation caused by|No |We handle it by | * | | |a cache inhibited load/store| |manually reading | * | | |from this thread failed. A | |HMER register. | * | | |trap register is | | | * | | |available. | | | * | | | | | | * |---+----------+----------------------------+--------+-------------------| * |9 |xscom_done|An XSCOM operation caused by|No |We handle it by | * | | |a cache inhibited load/store| |manually reading | * | | |from this thread completed. | |HMER register. | * | | |If hypervisor | | | * | | |intends to use this bit, it | | | * | | |is responsible for clearing | | | * | | |it before performing the | | | * | | |xscom operation. | | | * | | |NOTE: this bit should always| | | * | | |be masked in HMEER | | | * |---+----------+----------------------------+--------+-------------------| * |10 |reserved |reserved |n/a |n/a | * |---+----------+----------------------------+--------+-------------------| * |11 |proc_recv_|Processor recovery occurred |y |Log message and | * | |again |again before bit2 or bit3 | |continue working. | * | | |was cleared | | | * |---+----------+----------------------------+--------+-------------------| * |12-|reserved |was temperature sensor |n/a |n/a | * |15 | |passed the critical point on| | | * | | |the way up | | | * |---+----------+----------------------------+--------+-------------------| * |16 | |SCOM has set a reserved FIR |No |n/a | * | |scom_fir_h|bit to cause recovery | | | * | |m | | | | * |---+----------+----------------------------+--------+-------------------| * |17 |trig_fir_h|Debug trigger has set a |No |n/a | * | |mi |reserved FIR bit to cause | | | * | | |recovery | | | * |---+----------+----------------------------+--------+-------------------| * |18 |reserved |reserved |n/a |n/a | * |---+----------+----------------------------+--------+-------------------| * |19 |reserved |reserved |n/a |n/a | * |---+----------+----------------------------+--------+-------------------| * |20 |hyp_resour|A hypervisor resource error |y |Raise attn from | * | |ce_err |occurred: data parity error | |sapphire resulting | * | | |on, SPRC0:3; SPR_Modereg or | |xstop. | * | | |HMEER. | | | * | | |Note: this bit will cause an| | | * | | |check_stop when (HV=1, PR=0 | | | * | | |and EE=0) | | | * |---+----------+----------------------------+--------+-------------------| * |21-| |if bit 8 is active, the |No |We handle it by | * |23 |xscom_stat|reason will be detailed in | |Manually reading | * | |us |these bits. see chapter 11.1| |HMER register. | * | | |This bits are information | | | * | | |only and always masked | | | * | | |(mask = '0') | | | * | | |If hypervisor intends to use| | | * | | |this bit, it is responsible | | | * | | |for clearing it before | | | * | | |performing the xscom | | | * | | |operation. | | | * |---+----------+----------------------------+--------+-------------------| * |24-|Not |Not implemented |n/a |n/a | * |63 |implemente| | | | * | |d | | | | * +-- +----------+----------------------------+--------+-------------------+ * * Above HMER bits can be enabled/disabled by modifying * SPR_HMEER_HMI_ENABLE_MASK #define in include/processor.h * If you modify support for any of the bits listed above, please make sure * you change the above table to refelct that. * * NOTE: Per Dave Larson, never enable 8,9,21-23 */ /* Used for tracking cpu threads inside hmi handling. */ #define HMI_STATE_CLEANUP_DONE 0x100 #define CORE_THREAD_MASK 0x0ff #define SUBCORE_THREAD_MASK(s_id, t_count) \ ((((1UL) << (t_count)) - 1) << ((s_id) * (t_count))) /* xscom addresses for core FIR (Fault Isolation Register) */ #define CORE_FIR 0x10013100 #define NX_STATUS_REG 0x02013040 /* NX status register */ #define NX_DMA_ENGINE_FIR 0x02013100 /* DMA & Engine FIR Data Register */ #define NX_PBI_FIR 0x02013080 /* PowerBus Interface FIR Register */ /* * Bit 54 from NX status register is set to 1 when HMI interrupt is triggered * due to NX checksop. */ #define NX_HMI_ACTIVE PPC_BIT(54) /* Number of iterations for the various timeouts */ #define TIMEOUT_LOOPS 20000000 static const struct core_xstop_bit_info { uint8_t bit; /* CORE FIR bit number */ enum OpalHMI_CoreXstopReason reason; } xstop_bits[] = { { 3, CORE_CHECKSTOP_IFU_REGFILE }, { 5, CORE_CHECKSTOP_IFU_LOGIC }, { 8, CORE_CHECKSTOP_PC_DURING_RECOV }, { 10, CORE_CHECKSTOP_ISU_REGFILE }, { 12, CORE_CHECKSTOP_ISU_LOGIC }, { 21, CORE_CHECKSTOP_FXU_LOGIC }, { 25, CORE_CHECKSTOP_VSU_LOGIC }, { 26, CORE_CHECKSTOP_PC_RECOV_IN_MAINT_MODE }, { 32, CORE_CHECKSTOP_LSU_REGFILE }, { 36, CORE_CHECKSTOP_PC_FWD_PROGRESS }, { 38, CORE_CHECKSTOP_LSU_LOGIC }, { 45, CORE_CHECKSTOP_PC_LOGIC }, { 48, CORE_CHECKSTOP_PC_HYP_RESOURCE }, { 52, CORE_CHECKSTOP_PC_HANG_RECOV_FAILED }, { 54, CORE_CHECKSTOP_PC_AMBI_HANG_DETECTED }, { 60, CORE_CHECKSTOP_PC_DEBUG_TRIG_ERR_INJ }, { 63, CORE_CHECKSTOP_PC_SPRD_HYP_ERR_INJ }, }; static const struct nx_xstop_bit_info { uint8_t bit; /* NX FIR bit number */ enum OpalHMI_NestAccelXstopReason reason; } nx_dma_xstop_bits[] = { { 1, NX_CHECKSTOP_SHM_INVAL_STATE_ERR }, { 15, NX_CHECKSTOP_DMA_INVAL_STATE_ERR_1 }, { 16, NX_CHECKSTOP_DMA_INVAL_STATE_ERR_2 }, { 20, NX_CHECKSTOP_DMA_CH0_INVAL_STATE_ERR }, { 21, NX_CHECKSTOP_DMA_CH1_INVAL_STATE_ERR }, { 22, NX_CHECKSTOP_DMA_CH2_INVAL_STATE_ERR }, { 23, NX_CHECKSTOP_DMA_CH3_INVAL_STATE_ERR }, { 24, NX_CHECKSTOP_DMA_CH4_INVAL_STATE_ERR }, { 25, NX_CHECKSTOP_DMA_CH5_INVAL_STATE_ERR }, { 26, NX_CHECKSTOP_DMA_CH6_INVAL_STATE_ERR }, { 27, NX_CHECKSTOP_DMA_CH7_INVAL_STATE_ERR }, { 31, NX_CHECKSTOP_DMA_CRB_UE }, { 32, NX_CHECKSTOP_DMA_CRB_SUE }, }; static const struct nx_xstop_bit_info nx_pbi_xstop_bits[] = { { 12, NX_CHECKSTOP_PBI_ISN_UE }, }; static struct lock hmi_lock = LOCK_UNLOCKED; static int queue_hmi_event(struct OpalHMIEvent *hmi_evt, int recover) { uint64_t *hmi_data; /* Don't queue up event if recover == -1 */ if (recover == -1) return 0; /* set disposition */ if (recover == 1) hmi_evt->disposition = OpalHMI_DISPOSITION_RECOVERED; else if (recover == 0) hmi_evt->disposition = OpalHMI_DISPOSITION_NOT_RECOVERED; /* * V2 of struct OpalHMIEvent is of (4 * 64 bits) size and well packed * structure. Hence use uint64_t pointer to pass entire structure * using 4 params in generic message format. */ hmi_data = (uint64_t *)hmi_evt; /* queue up for delivery to host. */ return opal_queue_msg(OPAL_MSG_HMI_EVT, NULL, NULL, hmi_data[0], hmi_data[1], hmi_data[2], hmi_data[3]); } static int is_capp_recoverable(int chip_id) { uint64_t reg; xscom_read(chip_id, CAPP_ERR_STATUS_CTRL, ®); return (reg & PPC_BIT(0)) != 0; } static int handle_capp_recoverable(int chip_id) { struct dt_node *np; u64 phb_id; u32 dt_chip_id; struct phb *phb; u32 phb_index; struct proc_chip *chip = get_chip(chip_id); u8 mask = chip->capp_phb3_attached_mask; dt_for_each_compatible(dt_root, np, "ibm,power8-pciex") { dt_chip_id = dt_prop_get_u32(np, "ibm,chip-id"); phb_index = dt_prop_get_u32(np, "ibm,phb-index"); phb_id = dt_prop_get_u64(np, "ibm,opal-phbid"); if ((mask & (1 << phb_index)) && (chip_id == dt_chip_id)) { phb = pci_get_phb(phb_id); phb->ops->lock(phb); phb->ops->set_capp_recovery(phb); phb->ops->unlock(phb); return 1; } } return 0; } static int decode_one_malfunction(int flat_chip_id, struct OpalHMIEvent *hmi_evt) { hmi_evt->severity = OpalHMI_SEV_FATAL; hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT; if (is_capp_recoverable(flat_chip_id)) { if (handle_capp_recoverable(flat_chip_id) == 0) return 0; hmi_evt->severity = OpalHMI_SEV_NO_ERROR; hmi_evt->type = OpalHMI_ERROR_CAPP_RECOVERY; return 1; } /* TODO check other FIRs */ return 0; } static bool decode_core_fir(struct cpu_thread *cpu, struct OpalHMIEvent *hmi_evt) { uint64_t core_fir; uint32_t core_id; int i; bool found = false; /* Sanity check */ if (!cpu || !hmi_evt) return false; core_id = pir_to_core_id(cpu->pir); /* Get CORE FIR register value. */ if (xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX(core_id, CORE_FIR), &core_fir) != 0) { prerror("HMI: XSCOM error reading CORE FIR\n"); return false; } prlog(PR_INFO, "HMI: CHIP ID: %x, CORE ID: %x, FIR: %016llx\n", cpu->chip_id, core_id, core_fir); /* Check CORE FIR bits and populate HMI event with error info. */ for (i = 0; i < ARRAY_SIZE(xstop_bits); i++) { if (core_fir & PPC_BIT(xstop_bits[i].bit)) { found = true; hmi_evt->u.xstop_error.xstop_reason |= xstop_bits[i].reason; } } return found; } static void find_core_checkstop_reason(struct OpalHMIEvent *hmi_evt, int *event_generated) { struct cpu_thread *cpu; /* Initialize HMI event */ hmi_evt->severity = OpalHMI_SEV_FATAL; hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT; hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_CORE; /* * Check CORE FIRs and find the reason for core checkstop. * Send a separate HMI event for each core that has checkstopped. */ for_each_cpu(cpu) { /* GARDed CPUs are marked unavailable. Skip them. */ if (cpu->state == cpu_state_unavailable) continue; /* Only check on primaries (ie. core), not threads */ if (cpu->is_secondary) continue; /* Initialize xstop_error fields. */ hmi_evt->u.xstop_error.xstop_reason = 0; hmi_evt->u.xstop_error.u.pir = cpu->pir; if (decode_core_fir(cpu, hmi_evt)) { queue_hmi_event(hmi_evt, 0); *event_generated = 1; } } } static void find_nx_checkstop_reason(int flat_chip_id, struct OpalHMIEvent *hmi_evt, int *event_generated) { uint64_t nx_status; uint64_t nx_dma_fir; uint64_t nx_pbi_fir; int i; /* Get NX status register value. */ if (xscom_read(flat_chip_id, NX_STATUS_REG, &nx_status) != 0) { prerror("HMI: XSCOM error reading NX_STATUS_REG\n"); return; } /* Check if NX has driven an HMI interrupt. */ if (!(nx_status & NX_HMI_ACTIVE)) return; /* Initialize HMI event */ hmi_evt->severity = OpalHMI_SEV_FATAL; hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT; hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_NX; hmi_evt->u.xstop_error.u.chip_id = flat_chip_id; /* Get DMA & Engine FIR data register value. */ if (xscom_read(flat_chip_id, NX_DMA_ENGINE_FIR, &nx_dma_fir) != 0) { prerror("HMI: XSCOM error reading NX_DMA_ENGINE_FIR\n"); return; } /* Get PowerBus Interface FIR data register value. */ if (xscom_read(flat_chip_id, NX_PBI_FIR, &nx_pbi_fir) != 0) { prerror("HMI: XSCOM error reading NX_DMA_ENGINE_FIR\n"); return; } /* Find NX checkstop reason and populate HMI event with error info. */ for (i = 0; i < ARRAY_SIZE(nx_dma_xstop_bits); i++) if (nx_dma_fir & PPC_BIT(nx_dma_xstop_bits[i].bit)) hmi_evt->u.xstop_error.xstop_reason |= nx_dma_xstop_bits[i].reason; for (i = 0; i < ARRAY_SIZE(nx_pbi_xstop_bits); i++) if (nx_pbi_fir & PPC_BIT(nx_pbi_xstop_bits[i].bit)) hmi_evt->u.xstop_error.xstop_reason |= nx_pbi_xstop_bits[i].reason; /* * Set NXDMAENGFIR[38] to signal PRD that service action is required. * Without this inject, PRD will not be able to do NX unit checkstop * error analysis. NXDMAENGFIR[38] is a spare bit and used to report * a software initiated attention. * * The behavior of this bit and all FIR bits are documented in * RAS spreadsheet. */ xscom_write(flat_chip_id, NX_DMA_ENGINE_FIR, PPC_BIT(38)); /* Send an HMI event. */ queue_hmi_event(hmi_evt, 0); *event_generated = 1; } static int decode_malfunction(struct OpalHMIEvent *hmi_evt) { int i; int recover = -1; uint64_t malf_alert; int event_generated = 0; xscom_read(this_cpu()->chip_id, 0x2020011, &malf_alert); for (i = 0; i < 64; i++) if (malf_alert & PPC_BIT(i)) { recover = decode_one_malfunction(i, hmi_evt); xscom_write(this_cpu()->chip_id, 0x02020011, ~PPC_BIT(i)); if (recover) { queue_hmi_event(hmi_evt, recover); event_generated = 1; } find_nx_checkstop_reason(i, hmi_evt, &event_generated); } if (recover != -1) { find_core_checkstop_reason(hmi_evt, &event_generated); /* * In case, if we fail to find checkstop reason send an * unknown HMI event. */ if (!event_generated) { hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_UNKNOWN; hmi_evt->u.xstop_error.xstop_reason = 0; } } return recover; } static void wait_for_subcore_threads(void) { uint64_t timeout = 0; while (!(*(this_cpu()->core_hmi_state_ptr) & HMI_STATE_CLEANUP_DONE)) { /* * We use a fixed number of TIMEOUT_LOOPS rather * than using the timebase to do a pseudo-wall time * timeout due to the fact that timebase may not actually * work at this point in time. */ if (++timeout >= (TIMEOUT_LOOPS*3)) { /* * Break out the loop here and fall through * recovery code. If recovery fails, kernel will get * informed about the failure. This way we can avoid * looping here if other threads are stuck. */ prlog(PR_DEBUG, "HMI: TB pre-recovery timeout\n"); break; } cpu_relax(); } } /* * For successful recovery of TB residue error, remove dirty data * from TB/HDEC register in each active partition (subcore). Writing * zero's to TB/HDEC will achieve the same. */ static void timer_facility_do_cleanup(uint64_t tfmr) { if (tfmr & SPR_TFMR_TB_RESIDUE_ERR) { /* Reset the TB register to clear the dirty data. */ mtspr(SPR_TBWU, 0); mtspr(SPR_TBWL, 0); } if (tfmr & SPR_TFMR_HDEC_PARITY_ERROR) { /* Reset HDEC register */ mtspr(SPR_HDEC, 0); } } static int get_split_core_mode(void) { uint64_t hid0; hid0 = mfspr(SPR_HID0); if (hid0 & SPR_HID0_POWER8_2LPARMODE) return 2; else if (hid0 & SPR_HID0_POWER8_4LPARMODE) return 4; return 1; } /* * Certain TB/HDEC errors leaves dirty data in timebase and hdec register * which need to cleared before we initiate clear_tb_errors through TFMR[24]. * The cleanup has to be done by once by any one thread from core or subcore. * * In split core mode, it is required to clear the dirty data from TB/HDEC * register by all subcores (active partitions) before we clear tb errors * through TFMR[24]. The HMI recovery would fail even if one subcore do * not cleanup the respective TB/HDEC register. * * For un-split core, any one thread can do the cleanup. * For split core, any one thread from each subcore can do the cleanup. * * Errors that required pre-recovery cleanup: * - SPR_TFMR_TB_RESIDUE_ERR * - SPR_TFMR_HDEC_PARITY_ERROR */ static void pre_recovery_cleanup(void) { uint64_t hmer; uint64_t tfmr; uint32_t sibling_thread_mask; int split_core_mode, subcore_id, thread_id, threads_per_core; int i; hmer = mfspr(SPR_HMER); /* exit if it is not Time facility error. */ if (!(hmer & SPR_HMER_TFAC_ERROR)) return; /* * Exit if it is not the error that leaves dirty data in timebase * or HDEC register. OR this may be the thread which came in very * late and recovery is been already done. * * TFMR is per [sub]core register. If any one thread on the [sub]core * does the recovery it reflects in TFMR register and applicable to * all threads in that [sub]core. Hence take a lock before checking * TFMR errors. Once a thread from a [sub]core completes the * recovery, all other threads on that [sub]core will return from * here. * * If TFMR does not show error that we are looking for, return * from here. We would just fall through recovery code which would * check for other errors on TFMR and fix them. */ lock(&hmi_lock); tfmr = mfspr(SPR_TFMR); if (!(tfmr & (SPR_TFMR_TB_RESIDUE_ERR | SPR_TFMR_HDEC_PARITY_ERROR))) { unlock(&hmi_lock); return; } /* Gather split core information. */ split_core_mode = get_split_core_mode(); threads_per_core = cpu_thread_count / split_core_mode; /* Prepare core/subcore sibling mask */ thread_id = cpu_get_thread_index(this_cpu()); subcore_id = thread_id / threads_per_core; sibling_thread_mask = SUBCORE_THREAD_MASK(subcore_id, threads_per_core); /* * First thread on the core ? * if yes, setup the hmi cleanup state to !DONE */ if ((*(this_cpu()->core_hmi_state_ptr) & CORE_THREAD_MASK) == 0) *(this_cpu()->core_hmi_state_ptr) &= ~HMI_STATE_CLEANUP_DONE; /* * First thread on subcore ? * if yes, do cleanup. * * Clear TB and wait for other threads (one from each subcore) to * finish its cleanup work. */ if ((*(this_cpu()->core_hmi_state_ptr) & sibling_thread_mask) == 0) timer_facility_do_cleanup(tfmr); /* * Mark this thread bit. This bit will stay on until this thread * exit from handle_hmi_exception(). */ *(this_cpu()->core_hmi_state_ptr) |= this_cpu()->thread_mask; /* * Check if each subcore has completed the cleanup work. * if yes, then notify all the threads that we are done with cleanup. */ for (i = 0; i < split_core_mode; i++) { uint32_t subcore_thread_mask = SUBCORE_THREAD_MASK(i, threads_per_core); if (!(*(this_cpu()->core_hmi_state_ptr) & subcore_thread_mask)) break; } if (i == split_core_mode) *(this_cpu()->core_hmi_state_ptr) |= HMI_STATE_CLEANUP_DONE; unlock(&hmi_lock); /* Wait for other subcore to complete the cleanup. */ wait_for_subcore_threads(); } static void hmi_exit(void) { /* unconditionally unset the thread bit */ *(this_cpu()->core_hmi_state_ptr) &= ~(this_cpu()->thread_mask); } int handle_hmi_exception(uint64_t hmer, struct OpalHMIEvent *hmi_evt) { int recover = 1; uint64_t tfmr; /* * In case of split core, some of the Timer facility errors need * cleanup to be done before we proceed with the error recovery. */ pre_recovery_cleanup(); lock(&hmi_lock); /* * Not all HMIs would move TB into invalid state. Set the TB state * looking at TFMR register. TFMR will tell us correct state of * TB register. */ this_cpu()->tb_invalid = !(mfspr(SPR_TFMR) & SPR_TFMR_TB_VALID); prlog(PR_DEBUG, "HMI: Received HMI interrupt: HMER = 0x%016llx\n", hmer); if (hmi_evt) hmi_evt->hmer = hmer; if (hmer & SPR_HMER_PROC_RECV_DONE) { hmer &= ~SPR_HMER_PROC_RECV_DONE; if (hmi_evt) { hmi_evt->severity = OpalHMI_SEV_NO_ERROR; hmi_evt->type = OpalHMI_ERROR_PROC_RECOV_DONE; queue_hmi_event(hmi_evt, recover); } prlog(PR_DEBUG, "HMI: Processor recovery Done.\n"); } if (hmer & SPR_HMER_PROC_RECV_ERROR_MASKED) { hmer &= ~SPR_HMER_PROC_RECV_ERROR_MASKED; if (hmi_evt) { hmi_evt->severity = OpalHMI_SEV_NO_ERROR; hmi_evt->type = OpalHMI_ERROR_PROC_RECOV_MASKED; queue_hmi_event(hmi_evt, recover); } prlog(PR_DEBUG, "HMI: Processor recovery Done (masked).\n"); } if (hmer & SPR_HMER_PROC_RECV_AGAIN) { hmer &= ~SPR_HMER_PROC_RECV_AGAIN; if (hmi_evt) { hmi_evt->severity = OpalHMI_SEV_NO_ERROR; hmi_evt->type = OpalHMI_ERROR_PROC_RECOV_DONE_AGAIN; queue_hmi_event(hmi_evt, recover); } prlog(PR_DEBUG, "HMI: Processor recovery occurred again before" "bit2 was cleared\n"); } /* Assert if we see malfunction alert, we can not continue. */ if (hmer & SPR_HMER_MALFUNCTION_ALERT) { hmer &= ~SPR_HMER_MALFUNCTION_ALERT; recover = 0; if (hmi_evt) { recover = decode_malfunction(hmi_evt); queue_hmi_event(hmi_evt, recover); } } /* Assert if we see Hypervisor resource error, we can not continue. */ if (hmer & SPR_HMER_HYP_RESOURCE_ERR) { hmer &= ~SPR_HMER_HYP_RESOURCE_ERR; recover = 0; if (hmi_evt) { hmi_evt->severity = OpalHMI_SEV_FATAL; hmi_evt->type = OpalHMI_ERROR_HYP_RESOURCE; queue_hmi_event(hmi_evt, recover); } } /* * Assert for now for all TOD errors. In future we need to decode * TFMR and take corrective action wherever required. */ if (hmer & SPR_HMER_TFAC_ERROR) { tfmr = mfspr(SPR_TFMR); /* save original TFMR */ hmer &= ~SPR_HMER_TFAC_ERROR; recover = chiptod_recover_tb_errors(); if (hmi_evt) { hmi_evt->severity = OpalHMI_SEV_ERROR_SYNC; hmi_evt->type = OpalHMI_ERROR_TFAC; hmi_evt->tfmr = tfmr; queue_hmi_event(hmi_evt, recover); } } if (hmer & SPR_HMER_TFMR_PARITY_ERROR) { tfmr = mfspr(SPR_TFMR); /* save original TFMR */ hmer &= ~SPR_HMER_TFMR_PARITY_ERROR; recover = chiptod_recover_tb_errors(); if (hmi_evt) { hmi_evt->severity = OpalHMI_SEV_FATAL; hmi_evt->type = OpalHMI_ERROR_TFMR_PARITY; hmi_evt->tfmr = tfmr; queue_hmi_event(hmi_evt, recover); } } /* * HMER bits are sticky, once set to 1 they remain set to 1 until * they are set to 0. Reset the error source bit to 0, otherwise * we keep getting HMI interrupt again and again. */ mtspr(SPR_HMER, hmer); hmi_exit(); /* Set the TB state looking at TFMR register before we head out. */ this_cpu()->tb_invalid = !(mfspr(SPR_TFMR) & SPR_TFMR_TB_VALID); unlock(&hmi_lock); return recover; } static int64_t opal_handle_hmi(void) { uint64_t hmer; struct OpalHMIEvent hmi_evt; /* * Compiled time check to see size of OpalHMIEvent do not exceed * that of struct opal_msg. */ BUILD_ASSERT(sizeof(struct opal_msg) >= sizeof(struct OpalHMIEvent)); memset(&hmi_evt, 0, sizeof(struct OpalHMIEvent)); hmi_evt.version = OpalHMIEvt_V2; hmer = mfspr(SPR_HMER); /* Get HMER register value */ handle_hmi_exception(hmer, &hmi_evt); return OPAL_SUCCESS; } opal_call(OPAL_HANDLE_HMI, opal_handle_hmi, 0); skiboot-skiboot-5.1.13/core/hostservices.c000066400000000000000000000553231265204436200205540ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HOSTBOOT_RUNTIME_INTERFACE_VERSION 1 struct host_interfaces { /** Interface version. */ uint64_t interface_version; /** Put a string to the console. */ void (*puts)(const char*); /** Critical failure in runtime execution. */ void (*assert)(void); /** OPTIONAL. Hint to environment that the page may be executed. */ int (*set_page_execute)(void*); /** malloc */ void *(*malloc)(size_t); /** free */ void (*free)(void*); /** realloc */ void *(*realloc)(void*, size_t); /** sendErrorLog * @param[in] plid Platform Log identifier * @param[in] data size in bytes * @param[in] pointer to data * @return 0 on success else error code */ int (*send_error_log)(uint32_t,uint32_t,void *); /** Scan communication read * @param[in] chip_id (based on devtree defn) * @param[in] address * @param[in] pointer to 8-byte data buffer * @return 0 on success else return code */ int (*scom_read)(uint64_t, uint64_t, void*); /** Scan communication write * @param[in] chip_id (based on devtree defn) * @param[in] address * @param[in] pointer to 8-byte data buffer * @return 0 on success else return code */ int (*scom_write)(uint64_t, uint64_t, const void *); /** lid_load * Load a LID from PNOR, FSP, etc. * * @param[in] LID number. * @param[out] Allocated buffer for LID. * @param[out] Size of LID (in bytes). * * @return 0 on success, else RC. */ int (*lid_load)(uint32_t lid, void **buf, size_t *len); /** lid_unload * Release memory from previously loaded LID. * * @param[in] Allocated buffer for LID to release. * * @return 0 on success, else RC. */ int (*lid_unload)(void *buf); /** Get the address of a reserved memory region by its devtree name. * * @param[in] Devtree name (ex. "ibm,hbrt-vpd-image") * @return physical address of region (or NULL). **/ uint64_t (*get_reserved_mem)(const char*); /** * @brief Force a core to be awake, or clear the force * @param[in] i_core Core to wake up (pid) * @param[in] i_mode 0=force awake * 1=clear force * 2=clear all previous forces * @return rc non-zero on error */ int (*wakeup)( uint32_t i_core, uint32_t i_mode ); /** * @brief Delay/sleep for at least the time given * @param[in] seconds * @param[in] nano seconds */ void (*nanosleep)(uint64_t i_seconds, uint64_t i_nano_seconds); // Reserve some space for future growth. void (*reserved[32])(void); }; struct runtime_interfaces { /** Interface version. */ uint64_t interface_version; /** Execute CxxTests that may be contained in the image. * * @param[in] - Pointer to CxxTestStats structure for results reporting. */ void (*cxxtestExecute)(void *); /** Get a list of lids numbers of the lids known to HostBoot * * @param[out] o_num - the number of lids in the list * @return a pointer to the list */ const uint32_t * (*get_lid_list)(size_t * o_num); /** Load OCC Image and common data into mainstore, also setup OCC BARSs * * @param[in] i_homer_addr_phys - The physical mainstore address of the * start of the HOMER image * @param[in] i_homer_addr_va - Virtual memory address of the HOMER image * @param[in] i_common_addr_phys - The physical mainstore address of the * OCC common area. * @param[in] i_common_addr_va - Virtual memory address of the common area * @param[in] i_chip - The HW chip id (XSCOM chip ID) * @return 0 on success else return code */ int(*loadOCC)(uint64_t i_homer_addr_phys, uint64_t i_homer_addr_va, uint64_t i_common_addr_phys, uint64_t i_common_addr_va, uint64_t i_chip); /** Start OCC on all chips, by module * * @param[in] i_chip - Array of functional HW chip ids * @Note The caller must include a complete modules worth of chips * @param[in] i_num_chips - Number of chips in the array * @return 0 on success else return code */ int (*startOCCs)(uint64_t* i_chip, size_t i_num_chips); /** Stop OCC hold OCCs in reset * * @param[in] i_chip - Array of functional HW chip ids * @Note The caller must include a complete modules worth of chips * @param[in] i_num_chips - Number of chips in the array * @return 0 on success else return code */ int (*stopOCCs)(uint64_t* i_chip, size_t i_num_chips); /* Reserve some space for future growth. */ void (*reserved[32])(void); }; static struct runtime_interfaces *hservice_runtime; static char *hbrt_con_buf = (char *)HBRT_CON_START; static size_t hbrt_con_pos; static bool hbrt_con_wrapped; #define HBRT_CON_IN_LEN 0 #define HBRT_CON_OUT_LEN (HBRT_CON_LEN - HBRT_CON_IN_LEN) static struct memcons hbrt_memcons __section(".data.memcons") = { .magic = MEMCONS_MAGIC, .obuf_phys = HBRT_CON_START, .ibuf_phys = HBRT_CON_START + HBRT_CON_OUT_LEN, .obuf_size = HBRT_CON_OUT_LEN, .ibuf_size = HBRT_CON_IN_LEN, }; static void hservice_putc(char c) { uint32_t opos; hbrt_con_buf[hbrt_con_pos++] = c; if (hbrt_con_pos >= HBRT_CON_OUT_LEN) { hbrt_con_pos = 0; hbrt_con_wrapped = true; } /* * We must always re-generate memcons.out_pos because * under some circumstances, the console script will * use a broken putmemproc that does RMW on the full * 8 bytes containing out_pos and in_prod, thus corrupting * out_pos */ opos = hbrt_con_pos; if (hbrt_con_wrapped) opos |= MEMCONS_OUT_POS_WRAP; lwsync(); hbrt_memcons.out_pos = opos; } static void hservice_puts(const char *str) { char c; while((c = *(str++)) != 0) hservice_putc(c); hservice_putc(10); } static void hservice_mark(void) { hservice_puts("--------------------------------------------------" "--------------------------------------------------\n"); } static void hservice_assert(void) { prlog(PR_EMERG, "HBRT: Assertion from hostservices\n"); abort(); } static void *hservice_malloc(size_t size) { return malloc(size); } static void hservice_free(void *ptr) { free(ptr); } static void *hservice_realloc(void *ptr, size_t size) { return realloc(ptr, size); } struct hbrt_elog_ent { void *buf; unsigned int size; unsigned int plid; struct list_node link; }; static LIST_HEAD(hbrt_elogs); static struct lock hbrt_elog_lock = LOCK_UNLOCKED; static bool hbrt_elog_sending; static void hservice_start_elog_send(void); static void hservice_elog_write_complete(struct fsp_msg *msg) { struct hbrt_elog_ent *ent = msg->user_data; lock(&hbrt_elog_lock); prlog(PR_DEBUG, "HBRT: Completed send of PLID 0x%08x\n", ent->plid); hbrt_elog_sending = false; fsp_tce_unmap(PSI_DMA_HBRT_LOG_WRITE_BUF, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ); free(ent->buf); free(ent); fsp_freemsg(msg); hservice_start_elog_send(); unlock(&hbrt_elog_lock); } static void hservice_start_elog_send(void) { struct fsp_msg *msg; struct hbrt_elog_ent *ent; again: if (list_empty(&hbrt_elogs)) return; ent = list_pop(&hbrt_elogs, struct hbrt_elog_ent, link); hbrt_elog_sending = true; prlog(PR_DEBUG, "HBRT: Starting send of PLID 0x%08x\n", ent->plid); fsp_tce_map(PSI_DMA_HBRT_LOG_WRITE_BUF, ent->buf, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ); msg = fsp_mkmsg(FSP_CMD_WRITE_SP_DATA, 6, FSP_DATASET_HBRT_BLOB, 0, 0, 0, PSI_DMA_HBRT_LOG_WRITE_BUF, ent->size); if (!msg) { prerror("HBRT: Failed to create error msg log to FSP\n"); goto error; } msg->user_data = ent; if (!fsp_queue_msg(msg, hservice_elog_write_complete)) return; prerror("FSP: Error queueing elog update\n"); error: if (msg) fsp_freemsg(msg); fsp_tce_unmap(PSI_DMA_HBRT_LOG_WRITE_BUF, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ); free(ent->buf); free(ent); hbrt_elog_sending = false; goto again; } static int hservice_send_error_log(uint32_t plid, uint32_t dsize, void *data) { struct hbrt_elog_ent *ent; void *abuf; prlog(PR_ERR, "HBRT: Error log generated with plid 0x%08x\n", plid); /* We only know how to send error logs to FSP */ if (!fsp_present()) { prerror("HBRT: Warning, error log from HBRT discarded !\n"); return OPAL_UNSUPPORTED; } if (dsize > PSI_DMA_HBRT_LOG_WRITE_BUF_SZ) { prerror("HBRT: Warning, error log from HBRT too big (%d) !\n", dsize); dsize = PSI_DMA_HBRT_LOG_WRITE_BUF_SZ; } lock(&hbrt_elog_lock); /* Create and populate a tracking structure */ ent = zalloc(sizeof(struct hbrt_elog_ent)); if (!ent) { unlock(&hbrt_elog_lock); return OPAL_NO_MEM; } /* Grab a 4k aligned page */ abuf = memalign(0x1000, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ); if (!abuf) { free(ent); unlock(&hbrt_elog_lock); return OPAL_NO_MEM; } memset(abuf, 0, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ); memcpy(abuf, data, dsize); ent->buf = abuf; ent->size = dsize; ent->plid = plid; list_add_tail(&hbrt_elogs, &ent->link); if (!hbrt_elog_sending) hservice_start_elog_send(); unlock(&hbrt_elog_lock); return 0; } static int hservice_scom_read(uint64_t chip_id, uint64_t addr, void *buf) { return xscom_read(chip_id, addr, buf); } static int hservice_scom_write(uint64_t chip_id, uint64_t addr, const void *buf) { uint64_t val; memcpy(&val, buf, sizeof(val)); return xscom_write(chip_id, addr, val); } struct hbrt_lid { void *load_addr; size_t len; uint32_t id; struct list_node link; }; static LIST_HEAD(hbrt_lid_list); static bool hbrt_lid_preload_complete = false; bool hservices_lid_preload_complete(void) { return hbrt_lid_preload_complete; } /* TODO: Few of the following routines can be generalized */ static int __hservice_lid_load(uint32_t lid, void **buf, size_t *len) { int rc; /* Adjust LID side first or we get a cache mismatch */ lid = fsp_adjust_lid_side(lid); /* * Allocate a new buffer and load the LID into it * XXX: We currently use the same size for each HBRT lid. */ *buf = malloc(HBRT_LOAD_LID_SIZE); *len = HBRT_LOAD_LID_SIZE; rc = fsp_preload_lid(lid, *buf, len); rc = fsp_wait_lid_loaded(lid); if (rc != 0) /* Take advantage of realloc corner case here. */ *len = 0; *buf = realloc(*buf, *len); prlog(PR_DEBUG, "HBRT: LID 0x%08x successfully loaded, len=0x%lx\n", lid, (unsigned long)len); return rc; } static int __hservice_lid_preload(const uint32_t lid) { struct hbrt_lid *hlid; void *buf; size_t len; int rc; hlid = zalloc(sizeof(struct hbrt_lid)); if (!hlid) { prerror("HBRT: Could not allocate struct hbrt_lid\n"); return OPAL_NO_MEM; } rc = __hservice_lid_load(lid, &buf, &len); if (rc) { free(hlid); return rc; } hlid->load_addr = buf; hlid->len = len; hlid->id = lid; list_add_tail(&hbrt_lid_list, &hlid->link); return 0; } /* Find and preload all lids needed by hostservices */ void hservices_lid_preload(void) { const uint32_t *lid_list = NULL; size_t num_lids; int i; if (!hservice_runtime) return; lid_list = (const uint32_t *)hservice_runtime->get_lid_list(&num_lids); if (!lid_list) { prerror("HBRT: get_lid_list() returned NULL\n"); return; } prlog(PR_INFO, "HBRT: %d lids to load\n", (int)num_lids); /* Currently HBRT needs only one (OCC) lid */ for (i = 0; i < num_lids; i++) __hservice_lid_preload(lid_list[i]); hbrt_lid_preload_complete = true; occ_poke_load_queue(); } static int hservice_lid_load(uint32_t lid, void **buf, size_t *len) { struct hbrt_lid *hlid; prlog(PR_INFO, "HBRT: Lid load request for 0x%08x\n", lid); if (list_empty(&hbrt_lid_list)) { /* Should not happen */ prlog(PR_CRIT, "HBRT: LID Load failed\n"); abort(); } list_for_each(&hbrt_lid_list, hlid, link) { if (hlid->id == lid) { *buf = hlid->load_addr; *len = hlid->len; prlog(PR_DEBUG, "HBRT: LID Serviced from cache," " %x, len=0x%lx\n", hlid->id, hlid->len); return 0; } } return -ENOENT; } static int hservice_lid_unload(void *buf __unused) { /* We do nothing as the LID is held in cache */ return 0; } static uint64_t hservice_get_reserved_mem(const char *name) { struct mem_region *region; uint64_t ret; /* We assume it doesn't change after we've unlocked it, but * lock ensures list is safe to walk. */ lock(&mem_region_lock); region = find_mem_region(name); ret = region ? region->start : 0; unlock(&mem_region_lock); if (!ret) prerror("HBRT: Mem region '%s' not found !\n", name); return ret; } static void hservice_nanosleep(uint64_t i_seconds, uint64_t i_nano_seconds) { struct timespec ts; ts.tv_sec = i_seconds; ts.tv_nsec = i_nano_seconds; nanosleep_nopoll(&ts, NULL); } static int hservice_set_special_wakeup(struct cpu_thread *cpu) { uint64_t val, core_id, poll_target, stamp; int rc; /* * Note: HWP checks for checkstops, but I assume we don't need to * as we wouldn't be running if one was present */ /* Grab core ID once */ core_id = pir_to_core_id(cpu->pir); /* * The original HWp reads the XSCOM first but ignores the result * and error, let's do the same until I know for sure that is * not necessary */ xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), &val); /* Then we write special wakeup */ rc = xscom_write(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), PPC_BIT(0)); if (rc) { prerror("HBRT: XSCOM error %d asserting special" " wakeup on 0x%x\n", rc, cpu->pir); return rc; } /* * HWP uses the history for Perf register here, dunno why it uses * that one instead of the pHyp one, maybe to avoid clobbering it... * * In any case, it does that to check for run/nap vs.sleep/winkle/other * to decide whether to poll on checkstop or not. Since we don't deal * with checkstop conditions here, we ignore that part. */ /* * Now poll for completion of special wakeup. The HWP is nasty here, * it will poll at 5ms intervals for up to 200ms. This is not quite * acceptable for us at runtime, at least not until we have the * ability to "context switch" HBRT. In practice, because we don't * winkle, it will never take that long, so we increase the polling * frequency to 1us per poll. However we do have to keep the same * timeout. * * We don't use time_wait_ms() either for now as we don't want to * poll the FSP here. */ stamp = mftb(); poll_target = stamp + msecs_to_tb(200); val = 0; while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) { /* Wait 1 us */ hservice_nanosleep(0, 1000); /* Read PM state */ rc = xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0), &val); if (rc) { prerror("HBRT: XSCOM error %d reading PM state on" " 0x%x\n", rc, cpu->pir); return rc; } /* Check timeout */ if (mftb() > poll_target) break; } /* Success ? */ if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) { uint64_t now = mftb(); prlog(PR_TRACE, "HBRT: Special wakeup complete after %ld us\n", tb_to_usecs(now - stamp)); return 0; } /* * We timed out ... * * HWP has a complex workaround for HW255321 which affects * Murano DD1 and Venice DD1. Ignore that for now * * Instead we just dump some XSCOMs for error logging */ prerror("HBRT: Timeout on special wakeup of 0x%0x\n", cpu->pir); prerror("HBRT: PM0 = 0x%016llx\n", val); val = -1; xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), &val); prerror("HBRT: SPC_WKUP = 0x%016llx\n", val); val = -1; xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_IDLE_STATE_HISTORY_PHYP), &val); prerror("HBRT: HISTORY = 0x%016llx\n", val); return OPAL_HARDWARE; } static int hservice_clr_special_wakeup(struct cpu_thread *cpu) { uint64_t val, core_id; int rc; /* * Note: HWP checks for checkstops, but I assume we don't need to * as we wouldn't be running if one was present */ /* Grab core ID once */ core_id = pir_to_core_id(cpu->pir); /* * The original HWp reads the XSCOM first but ignores the result * and error, let's do the same until I know for sure that is * not necessary */ xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), &val); /* Then we write special wakeup */ rc = xscom_write(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), 0); if (rc) { prerror("HBRT: XSCOM error %d deasserting" " special wakeup on 0x%x\n", rc, cpu->pir); return rc; } /* * The original HWp reads the XSCOM again with the comment * "This puts an inherent delay in the propagation of the reset * transition" */ xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), &val); return 0; } static int hservice_wakeup(uint32_t i_core, uint32_t i_mode) { struct cpu_thread *cpu; int rc = OPAL_SUCCESS; /* * Mask out the top nibble of i_core since it may contain * 0x4 (which we use for XSCOM targeting) */ i_core &= 0x0fffffff; /* What do we need to do ? */ switch(i_mode) { case 0: /* Assert special wakeup */ /* XXX Assume P8 */ cpu = find_cpu_by_pir(i_core << 3); if (!cpu) return OPAL_PARAMETER; prlog(PR_DEBUG, "HBRT: Special wakeup assert for core 0x%x," " count=%d\n", i_core, cpu->hbrt_spec_wakeup); if (cpu->hbrt_spec_wakeup == 0) rc = hservice_set_special_wakeup(cpu); if (rc == 0) cpu->hbrt_spec_wakeup++; return rc; case 1: /* Deassert special wakeup */ /* XXX Assume P8 */ cpu = find_cpu_by_pir(i_core << 3); if (!cpu) return OPAL_PARAMETER; prlog(PR_DEBUG, "HBRT: Special wakeup release for core" " 0x%x, count=%d\n", i_core, cpu->hbrt_spec_wakeup); if (cpu->hbrt_spec_wakeup == 0) { prerror("HBRT: Special wakeup clear" " on core 0x%x with count=0\n", i_core); return OPAL_WRONG_STATE; } /* What to do with count on errors ? */ cpu->hbrt_spec_wakeup--; if (cpu->hbrt_spec_wakeup == 0) rc = hservice_clr_special_wakeup(cpu); return rc; case 2: /* Clear all special wakeups */ prlog(PR_DEBUG, "HBRT: Special wakeup release for all cores\n"); for_each_cpu(cpu) { if (cpu->hbrt_spec_wakeup) { cpu->hbrt_spec_wakeup = 0; /* What to do on errors ? */ hservice_clr_special_wakeup(cpu); } } return OPAL_SUCCESS; default: return OPAL_PARAMETER; } } static struct host_interfaces hinterface = { .interface_version = HOSTBOOT_RUNTIME_INTERFACE_VERSION, .puts = hservice_puts, .assert = hservice_assert, .malloc = hservice_malloc, .free = hservice_free, .realloc = hservice_realloc, .send_error_log = hservice_send_error_log, .scom_read = hservice_scom_read, .scom_write = hservice_scom_write, .lid_load = hservice_lid_load, .lid_unload = hservice_lid_unload, .get_reserved_mem = hservice_get_reserved_mem, .wakeup = hservice_wakeup, .nanosleep = hservice_nanosleep, }; int host_services_occ_load(void) { struct proc_chip *chip; int rc = 0; prlog(PR_DEBUG, "HBRT: OCC Load requested\n"); if (!(hservice_runtime && hservice_runtime->loadOCC)) { prerror("HBRT: No hservice_runtime->loadOCC\n"); return -ENOENT; } for_each_chip(chip) { prlog(PR_DEBUG, "HBRT: Calling loadOCC() homer" " %016llx, occ_common_area %016llx, chip %04x\n", chip->homer_base, chip->occ_common_base, chip->id); rc = hservice_runtime->loadOCC(chip->homer_base, chip->homer_base, chip->occ_common_base, chip->occ_common_base, chip->id); hservice_mark(); prlog(PR_DEBUG, "HBRT: -> rc = %d\n", rc); } return rc; } int host_services_occ_start(void) { struct proc_chip *chip; int i, rc = 0, nr_chips=0; uint64_t chipids[MAX_CHIPS]; prlog(PR_INFO, "HBRT: OCC Start requested\n"); if (!(hservice_runtime && hservice_runtime->startOCCs)) { prerror("HBRT: No hservice_runtime->startOCCs\n"); return -ENOENT; } for_each_chip(chip) { chipids[nr_chips++] = chip->id; } for (i = 0; i < nr_chips; i++) prlog(PR_TRACE, "HBRT: Calling startOCC() for %04llx\n", chipids[i]); /* Lets start all OCC */ rc = hservice_runtime->startOCCs(chipids, nr_chips); hservice_mark(); prlog(PR_DEBUG, "HBRT: startOCCs() rc = %d\n", rc); return rc; } int host_services_occ_stop(void) { struct proc_chip *chip; int i, rc = 0, nr_chips=0; uint64_t chipids[MAX_CHIPS]; prlog(PR_INFO, "HBRT: OCC Stop requested\n"); if (!(hservice_runtime && hservice_runtime->stopOCCs)) { prerror("HBRT: No hservice_runtime->stopOCCs\n"); return -ENOENT; } for_each_chip(chip) { chipids[nr_chips++] = chip->id; } for (i = 0; i < nr_chips; i++) prlog(PR_TRACE, "HBRT: Calling stopOCC() for %04llx ", chipids[i]); /* Lets STOP all OCC */ rc = hservice_runtime->stopOCCs(chipids, nr_chips); hservice_mark(); prlog(PR_DEBUG, "HBRT: stopOCCs() rc = %d\n", rc); return rc; } void host_services_occ_base_setup(void) { struct proc_chip *chip; uint64_t occ_common; chip = next_chip(NULL); /* Frist chip */ occ_common = (uint64_t) local_alloc(chip->id, OCC_COMMON_SIZE, OCC_COMMON_SIZE); for_each_chip(chip) { chip->occ_common_base = occ_common; chip->occ_common_size = OCC_COMMON_SIZE; chip->homer_base = (uint64_t) local_alloc(chip->id, HOMER_IMAGE_SIZE, HOMER_IMAGE_SIZE); chip->homer_size = HOMER_IMAGE_SIZE; memset((void *)chip->homer_base, 0, chip->homer_size); prlog(PR_DEBUG, "HBRT: Chip %d HOMER base %016llx : %08llx\n", chip->id, chip->homer_base, chip->homer_size); prlog(PR_DEBUG, "HBRT: OCC common base %016llx : %08llx\n", chip->occ_common_base, chip->occ_common_size); } } bool hservices_init(void) { void *code = NULL; struct runtime_interfaces *(*hbrt_init)(struct host_interfaces *); struct function_descriptor { void *addr; void *toc; } fdesc; code = (void *)hservice_get_reserved_mem("ibm,hbrt-code-image"); if (!code) { prerror("HBRT: No ibm,hbrt-code-image found.\n"); return false; } if (memcmp(code, "HBRTVERS", 8) != 0) { prerror("HBRT: Bad eyecatcher for ibm,hbrt-code-image!\n"); return false; } prlog(PR_INFO, "HBRT: Found HostBoot Runtime version %llu\n", ((u64 *)code)[1]); /* We enter at 0x100 into the image. */ fdesc.addr = code + 0x100; /* It doesn't care about TOC */ fdesc.toc = NULL; hbrt_init = (void *)&fdesc; hservice_runtime = hbrt_init(&hinterface); hservice_mark(); if (!hservice_runtime) { prerror("HBRT: Host services init failed\n"); return false; } prlog(PR_INFO, "HBRT: Interface version %llu\n", hservice_runtime->interface_version); return true; } skiboot-skiboot-5.1.13/core/i2c.c000066400000000000000000000053131265204436200165020ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include static LIST_HEAD(i2c_bus_list); /* Used to assign OPAL IDs */ static uint32_t i2c_next_bus; void i2c_add_bus(struct i2c_bus *bus) { bus->opal_id = ++i2c_next_bus; dt_add_property_cells(bus->dt_node, "ibm,opal-id", bus->opal_id); list_add_tail(&i2c_bus_list, &bus->link); } struct i2c_bus *i2c_find_bus_by_id(uint32_t opal_id) { struct i2c_bus *bus; list_for_each(&i2c_bus_list, bus, link) { if (bus->opal_id == opal_id) return bus; } return NULL; } static void opal_i2c_request_complete(int rc, struct i2c_request *req) { uint64_t token = (uint64_t)(unsigned long)req->user_data; opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc); i2c_free_req(req); } static int opal_i2c_request(uint64_t async_token, uint32_t bus_id, struct opal_i2c_request *oreq) { struct i2c_bus *bus = NULL; struct i2c_request *req; int rc; if (oreq->flags & OPAL_I2C_ADDR_10) return OPAL_UNSUPPORTED; bus = i2c_find_bus_by_id(bus_id); if (!bus) { prlog(PR_ERR, "I2C: Invalid 'bus_id' passed to the OPAL\n"); return OPAL_PARAMETER; } req = i2c_alloc_req(bus); if (!req) { prlog(PR_ERR, "I2C: Failed to allocate 'i2c_request'\n"); return OPAL_NO_MEM; } switch(oreq->type) { case OPAL_I2C_RAW_READ: req->op = I2C_READ; break; case OPAL_I2C_RAW_WRITE: req->op = I2C_WRITE; break; case OPAL_I2C_SM_READ: req->op = SMBUS_READ; req->offset = oreq->subaddr; req->offset_bytes = oreq->subaddr_sz; break; case OPAL_I2C_SM_WRITE: req->op = SMBUS_WRITE; req->offset = oreq->subaddr; req->offset_bytes = oreq->subaddr_sz; break; default: bus->free_req(req); return OPAL_PARAMETER; } req->dev_addr = oreq->addr; req->rw_len = oreq->size; req->rw_buf = (void *)oreq->buffer_ra; req->completion = opal_i2c_request_complete; req->user_data = (void *)(unsigned long)async_token; req->bus = bus; /* Finally, queue the OPAL i2c request and return */ rc = i2c_queue_req(req); if (rc) { i2c_free_req(req); return rc; } return OPAL_ASYNC_COMPLETION; } opal_call(OPAL_I2C_REQUEST, opal_i2c_request, 3); skiboot-skiboot-5.1.13/core/init.c000066400000000000000000000514411265204436200167730ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum proc_gen proc_gen; static uint64_t kernel_entry; static bool kernel_32bit; #ifdef SKIBOOT_GCOV void skiboot_gcov_done(void); #endif struct debug_descriptor debug_descriptor = { .eye_catcher = "OPALdbug", .version = DEBUG_DESC_VERSION, .state_flags = 0, .memcons_phys = (uint64_t)&memcons, .trace_mask = 0, /* All traces disabled by default */ .console_log_levels = (PR_DEBUG << 4) | PR_NOTICE, }; static bool try_load_elf64_le(struct elf_hdr *header) { struct elf64_hdr *kh = (struct elf64_hdr *)header; uint64_t load_base = (uint64_t)kh; struct elf64_phdr *ph; unsigned int i; printf("INIT: 64-bit LE kernel discovered\n"); /* Look for a loadable program header that has our entry in it * * Note that we execute the kernel in-place, we don't actually * obey the load informations in the headers. This is expected * to work for the Linux Kernel because it's a fairly dumb ELF * but it will not work for any ELF binary. */ ph = (struct elf64_phdr *)(load_base + le64_to_cpu(kh->e_phoff)); for (i = 0; i < le16_to_cpu(kh->e_phnum); i++, ph++) { if (le32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD) continue; if (le64_to_cpu(ph->p_vaddr) > le64_to_cpu(kh->e_entry) || (le64_to_cpu(ph->p_vaddr) + le64_to_cpu(ph->p_memsz)) < le64_to_cpu(kh->e_entry)) continue; /* Get our entry */ kernel_entry = le64_to_cpu(kh->e_entry) - le64_to_cpu(ph->p_vaddr) + le64_to_cpu(ph->p_offset); break; } if (!kernel_entry) { prerror("INIT: Failed to find kernel entry !\n"); return false; } kernel_entry += load_base; kernel_32bit = false; printf("INIT: 64-bit kernel entry at 0x%llx\n", kernel_entry); return true; } static bool try_load_elf64(struct elf_hdr *header) { struct elf64_hdr *kh = (struct elf64_hdr *)header; uint64_t load_base = (uint64_t)kh; struct elf64_phdr *ph; struct elf64_shdr *sh; unsigned int i; /* Check it's a ppc64 LE ELF */ if (kh->ei_ident == ELF_IDENT && kh->ei_data == ELF_DATA_LSB && kh->e_machine == le16_to_cpu(ELF_MACH_PPC64)) { return try_load_elf64_le(header); } /* Check it's a ppc64 ELF */ if (kh->ei_ident != ELF_IDENT || kh->ei_data != ELF_DATA_MSB || kh->e_machine != ELF_MACH_PPC64) { prerror("INIT: Kernel doesn't look like an ppc64 ELF\n"); return false; } /* Look for a loadable program header that has our entry in it * * Note that we execute the kernel in-place, we don't actually * obey the load informations in the headers. This is expected * to work for the Linux Kernel because it's a fairly dumb ELF * but it will not work for any ELF binary. */ ph = (struct elf64_phdr *)(load_base + kh->e_phoff); for (i = 0; i < kh->e_phnum; i++, ph++) { if (ph->p_type != ELF_PTYPE_LOAD) continue; if (ph->p_vaddr > kh->e_entry || (ph->p_vaddr + ph->p_memsz) < kh->e_entry) continue; /* Get our entry */ kernel_entry = kh->e_entry - ph->p_vaddr + ph->p_offset; break; } if (!kernel_entry) { prerror("INIT: Failed to find kernel entry !\n"); return false; } /* For the normal big-endian ELF ABI, the kernel entry points * to a function descriptor in the data section. Linux instead * has it point directly to code. Test whether it is pointing * into an executable section or not to figure this out. Default * to assuming it obeys the ABI. */ sh = (struct elf64_shdr *)(load_base + kh->e_shoff); for (i = 0; i < kh->e_shnum; i++, sh++) { if (sh->sh_addr <= kh->e_entry && (sh->sh_addr + sh->sh_size) > kh->e_entry) break; } if (i == kh->e_shnum || !(sh->sh_flags & ELF_SFLAGS_X)) { kernel_entry = *(uint64_t *)(kernel_entry + load_base); kernel_entry = kernel_entry - ph->p_vaddr + ph->p_offset; } kernel_entry += load_base; kernel_32bit = false; printf("INIT: 64-bit kernel entry at 0x%llx\n", kernel_entry); return true; } static bool try_load_elf32_le(struct elf_hdr *header) { struct elf32_hdr *kh = (struct elf32_hdr *)header; uint64_t load_base = (uint64_t)kh; struct elf32_phdr *ph; unsigned int i; printf("INIT: 32-bit LE kernel discovered\n"); /* Look for a loadable program header that has our entry in it * * Note that we execute the kernel in-place, we don't actually * obey the load informations in the headers. This is expected * to work for the Linux Kernel because it's a fairly dumb ELF * but it will not work for any ELF binary. */ ph = (struct elf32_phdr *)(load_base + le32_to_cpu(kh->e_phoff)); for (i = 0; i < le16_to_cpu(kh->e_phnum); i++, ph++) { if (le32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD) continue; if (le32_to_cpu(ph->p_vaddr) > le32_to_cpu(kh->e_entry) || (le32_to_cpu(ph->p_vaddr) + le32_to_cpu(ph->p_memsz)) < le32_to_cpu(kh->e_entry)) continue; /* Get our entry */ kernel_entry = le32_to_cpu(kh->e_entry) - le32_to_cpu(ph->p_vaddr) + le32_to_cpu(ph->p_offset); break; } if (!kernel_entry) { prerror("INIT: Failed to find kernel entry !\n"); return false; } kernel_entry += load_base; kernel_32bit = true; printf("INIT: 32-bit kernel entry at 0x%llx\n", kernel_entry); return true; } static bool try_load_elf32(struct elf_hdr *header) { struct elf32_hdr *kh = (struct elf32_hdr *)header; uint64_t load_base = (uint64_t)kh; struct elf32_phdr *ph; unsigned int i; /* Check it's a ppc32 LE ELF */ if (header->ei_ident == ELF_IDENT && header->ei_data == ELF_DATA_LSB && header->e_machine == le16_to_cpu(ELF_MACH_PPC32)) { return try_load_elf32_le(header); } /* Check it's a ppc32 ELF */ if (header->ei_ident != ELF_IDENT || header->ei_data != ELF_DATA_MSB || header->e_machine != ELF_MACH_PPC32) { prerror("INIT: Kernel doesn't look like an ppc32 ELF\n"); return false; } /* Look for a loadable program header that has our entry in it * * Note that we execute the kernel in-place, we don't actually * obey the load informations in the headers. This is expected * to work for the Linux Kernel because it's a fairly dumb ELF * but it will not work for any ELF binary. */ ph = (struct elf32_phdr *)(load_base + kh->e_phoff); for (i = 0; i < kh->e_phnum; i++, ph++) { if (ph->p_type != ELF_PTYPE_LOAD) continue; if (ph->p_vaddr > kh->e_entry || (ph->p_vaddr + ph->p_memsz) < kh->e_entry) continue; /* Get our entry */ kernel_entry = kh->e_entry - ph->p_vaddr + ph->p_offset; break; } if (!kernel_entry) { prerror("INIT: Failed to find kernel entry !\n"); return false; } kernel_entry += load_base; kernel_32bit = true; printf("INIT: 32-bit kernel entry at 0x%llx\n", kernel_entry); return true; } extern char __builtin_kernel_start[]; extern char __builtin_kernel_end[]; extern uint64_t boot_offset; static size_t kernel_size; static size_t initramfs_size; static bool start_preload_kernel(void) { int loaded; /* Try to load an external kernel payload through the platform hooks */ kernel_size = KERNEL_LOAD_SIZE; loaded = start_preload_resource(RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE, KERNEL_LOAD_BASE, &kernel_size); if (loaded != OPAL_SUCCESS) { printf("INIT: platform start load kernel failed\n"); kernel_size = 0; return false; } initramfs_size = INITRAMFS_LOAD_SIZE; loaded = start_preload_resource(RESOURCE_ID_INITRAMFS, RESOURCE_SUBID_NONE, INITRAMFS_LOAD_BASE, &initramfs_size); if (loaded != OPAL_SUCCESS) { printf("INIT: platform start load initramfs failed\n"); initramfs_size = 0; return false; } return true; } static bool load_kernel(void) { struct elf_hdr *kh; int loaded; prlog(PR_NOTICE, "INIT: Waiting for kernel...\n"); loaded = wait_for_resource_loaded(RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE); if (loaded != OPAL_SUCCESS) { printf("INIT: platform wait for kernel load failed\n"); kernel_size = 0; } /* Try embedded kernel payload */ if (!kernel_size) { kernel_size = __builtin_kernel_end - __builtin_kernel_start; if (kernel_size) { /* Move the built-in kernel up */ uint64_t builtin_base = ((uint64_t)__builtin_kernel_start) - SKIBOOT_BASE + boot_offset; printf("Using built-in kernel\n"); memmove(KERNEL_LOAD_BASE, (void*)builtin_base, kernel_size); } } if (!kernel_size) printf("Assuming kernel at %p\n", KERNEL_LOAD_BASE); printf("INIT: Kernel loaded, size: %zu bytes (0 = unknown preload)\n", kernel_size); kh = (struct elf_hdr *)KERNEL_LOAD_BASE; if (kh->ei_class == ELF_CLASS_64) return try_load_elf64(kh); else if (kh->ei_class == ELF_CLASS_32) return try_load_elf32(kh); printf("INIT: Neither ELF32 not ELF64 ?\n"); return false; } static void load_initramfs(void) { int loaded; loaded = wait_for_resource_loaded(RESOURCE_ID_INITRAMFS, RESOURCE_SUBID_NONE); if (loaded != OPAL_SUCCESS || !initramfs_size) return; printf("INIT: Initramfs loaded, size: %zu bytes\n", initramfs_size); dt_add_property_u64(dt_chosen, "linux,initrd-start", (uint64_t)INITRAMFS_LOAD_BASE); dt_add_property_u64(dt_chosen, "linux,initrd-end", (uint64_t)INITRAMFS_LOAD_BASE + initramfs_size); } int64_t mem_dump_free(void); void __noreturn load_and_boot_kernel(bool is_reboot) { const struct dt_property *memprop; uint64_t mem_top; void *fdt; memprop = dt_find_property(dt_root, DT_PRIVATE "maxmem"); if (memprop) mem_top = (u64)dt_property_get_cell(memprop, 0) << 32 | dt_property_get_cell(memprop, 1); else /* XXX HB hack, might want to calc it */ mem_top = 0x40000000; op_display(OP_LOG, OP_MOD_INIT, 0x000A); if (platform.exit) platform.exit(); /* Load kernel LID */ if (!load_kernel()) { op_display(OP_FATAL, OP_MOD_INIT, 1); abort(); } load_initramfs(); ipmi_set_fw_progress_sensor(IPMI_FW_OS_BOOT); if (!is_reboot) { /* We wait for the nvram read to complete here so we can * grab stuff from there such as the kernel arguments */ fsp_nvram_wait_open(); /* Wait for FW VPD data read to complete */ fsp_code_update_wait_vpd(true); } fsp_console_select_stdout(); /* * OCC takes few secs to boot. Call this as late as * as possible to avoid delay. */ occ_pstates_init(); /* Set kernel command line argument if specified */ #ifdef KERNEL_COMMAND_LINE dt_add_property_string(dt_chosen, "bootargs", KERNEL_COMMAND_LINE); #endif op_display(OP_LOG, OP_MOD_INIT, 0x000B); /* Create the device tree blob to boot OS. */ fdt = create_dtb(dt_root); if (!fdt) { op_display(OP_FATAL, OP_MOD_INIT, 2); abort(); } op_display(OP_LOG, OP_MOD_INIT, 0x000C); /* Start the kernel */ if (!is_reboot) op_panel_disable_src_echo(); /* Clear SRCs on the op-panel when Linux starts */ op_panel_clear_src(); cpu_give_self_os(); mem_dump_free(); printf("INIT: Starting kernel at 0x%llx, fdt at %p (size 0x%x)\n", kernel_entry, fdt, fdt_totalsize(fdt)); debug_descriptor.state_flags |= OPAL_BOOT_COMPLETE; fdt_set_boot_cpuid_phys(fdt, this_cpu()->pir); if (kernel_32bit) start_kernel32(kernel_entry, fdt, mem_top); start_kernel(kernel_entry, fdt, mem_top); } static void dt_fixups(void) { struct dt_node *n; struct dt_node *primary_lpc = NULL; /* lpc node missing #address/size cells. Also pick one as * primary for now (TBD: How to convey that from HB) */ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") { if (!primary_lpc || dt_has_node_property(n, "primary", NULL)) primary_lpc = n; if (dt_has_node_property(n, "#address-cells", NULL)) break; dt_add_property_cells(n, "#address-cells", 2); dt_add_property_cells(n, "#size-cells", 1); dt_add_property_strings(n, "status", "ok"); } /* Missing "primary" property in LPC bus */ if (primary_lpc && !dt_has_node_property(primary_lpc, "primary", NULL)) dt_add_property(primary_lpc, "primary", NULL, 0); /* Missing "scom-controller" */ dt_for_each_compatible(dt_root, n, "ibm,xscom") { if (!dt_has_node_property(n, "scom-controller", NULL)) dt_add_property(n, "scom-controller", NULL, 0); } } static void add_arch_vector(void) { /** * vec5 = a PVR-list : Number-of-option-vectors : * option-vectors[Number-of-option-vectors + 1] */ uint8_t vec5[] = {0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00}; if (dt_has_node_property(dt_chosen, "ibm,architecture-vec-5", NULL)) return; dt_add_property(dt_chosen, "ibm,architecture-vec-5", vec5, sizeof(vec5)); } static void dt_init_misc(void) { /* Check if there's a /chosen node, if not, add one */ dt_chosen = dt_find_by_path(dt_root, "/chosen"); if (!dt_chosen) dt_chosen = dt_new(dt_root, "chosen"); assert(dt_chosen); /* Add IBM architecture vectors if needed */ add_arch_vector(); /* Add the "OPAL virtual ICS*/ add_ics_node(); /* Additional fixups. TODO: Move into platform */ dt_fixups(); } static void branch_null(void) { assert_fail("Branch to NULL !"); } static void setup_branch_null_catcher(void) { void (*bn)(void) = branch_null; memcpy(0, bn, 16); } /* Called from head.S, thus no prototype. */ void __noreturn main_cpu_entry(const void *fdt, u32 master_cpu); typedef void (*ctorcall_t)(void); static void do_ctors(void) { extern ctorcall_t __ctors_start[], __ctors_end[]; ctorcall_t *call; for (call = __ctors_start; call < __ctors_end; call++) (*call)(); } void __noreturn main_cpu_entry(const void *fdt, u32 master_cpu) { /* * WARNING: At this point. the timebases have * *not* been synchronized yet. Do not use any timebase * related functions for timeouts etc... unless you can cope * with the speed being some random core clock divider and * the value jumping backward when the synchronization actually * happens (in chiptod_init() below). * * Also the current cpu_thread() struct is not initialized * either so we need to clear it out first thing first (without * putting any other useful info in there jus yet) otherwise * printf an locks are going to play funny games with "con_suspend" */ pre_init_boot_cpu(); /* * Before first printk, ensure console buffer is clear or * reading tools might think it has wrapped */ clear_console(); /* Put at 0 an OPD to a warning function in case we branch through * a NULL function pointer */ setup_branch_null_catcher(); do_ctors(); printf("SkiBoot %s starting...\n", version); printf("initial console log level: memory %d, driver %d\n", (debug_descriptor.console_log_levels >> 4), (debug_descriptor.console_log_levels & 0x0f)); prlog(PR_TRACE, "You will not see this\n"); #ifdef SKIBOOT_GCOV skiboot_gcov_done(); #endif /* Initialize boot cpu's cpu_thread struct */ init_boot_cpu(); /* Now locks can be used */ init_locks(); /* Create the OPAL call table early on, entries can be overridden * later on (FSP console code for example) */ opal_table_init(); /* * If we are coming in with a flat device-tree, we expand it * now. Else look for HDAT and create a device-tree from them * * Hack alert: When entering via the OPAL entry point, fdt * is set to -1, we record that and pass it to parse_hdat */ if (fdt == (void *)-1ul) parse_hdat(true, master_cpu); else if (fdt == NULL) parse_hdat(false, master_cpu); else { dt_expand(fdt); } /* * From there, we follow a fairly strict initialization order. * * First we need to build up our chip data structures and initialize * XSCOM which will be needed for a number of susbequent things. * * We want XSCOM available as early as the platform probe in case the * probe requires some HW accesses. * * We also initialize the FSI master at that point in case we need * to access chips via that path early on. */ init_chips(); if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) enable_mambo_console(); xscom_init(); mfsi_init(); /* * Put various bits & pieces in device-tree that might not * already be there such as the /chosen node if not there yet, * the ICS node, etc... This can potentially use XSCOM */ dt_init_misc(); /* * Initialize LPC (P8 only) so we can get to UART, BMC and * other system controller. This is done before probe_platform * so that the platform probing code can access an external * BMC if needed. */ lpc_init(); /* * Now, we init our memory map from the device-tree, and immediately * reserve areas which we know might contain data coming from * HostBoot. We need to do these things before we start doing * allocations outside of our heap, such as chip local allocs, * otherwise we might clobber those data. */ mem_region_init(); /* Reserve HOMER and OCC area */ homer_init(); /* Add the /opal node to the device-tree */ add_opal_node(); /* * We probe the platform now. This means the platform probe gets * the opportunity to reserve additional areas of memory if needed. * * Note: Timebases still not synchronized. */ probe_platform(); /* Initialize the rest of the cpu thread structs */ init_all_cpus(); /* Allocate our split trace buffers now. Depends add_opal_node() */ init_trace_buffers(); /* Get the ICPs and make sure they are in a sane state */ init_interrupts(); /* Grab centaurs from device-tree if present (only on FSP-less) */ centaur_init(); /* Initialize PSI (depends on probe_platform being called) */ psi_init(); /* Call in secondary CPUs */ cpu_bringup(); /* * Sycnhronize time bases. Thi resets all the TB values to a small * value (so they appear to go backward at this point), and synchronize * all core timebases to the global ChipTOD network */ chiptod_init(); /* Initialize i2c */ p8_i2c_init(); /* Register routine to dispatch and read sensors */ sensor_init(); /* * We have initialized the basic HW, we can now call into the * platform to perform subsequent inits, such as establishing * communication with the FSP or starting IPMI. */ if (platform.init) platform.init(); /* Setup dummy console nodes if it's enabled */ if (dummy_console_enabled()) dummy_console_add_nodes(); /* Init SLW related stuff, including fastsleep */ slw_init(); op_display(OP_LOG, OP_MOD_INIT, 0x0002); /* Read in NVRAM and set it up */ nvram_init(); phb3_preload_vpd(); phb3_preload_capp_ucode(); start_preload_kernel(); /* NX init */ nx_init(); /* Initialize the opal messaging */ opal_init_msg(); /* Probe IO hubs */ probe_p5ioc2(); probe_p7ioc(); /* Probe PHB3 on P8 */ probe_phb3(); /* Initialize PCI */ pci_init_slots(); /* Add OPAL timer related properties */ late_init_timers(); ipmi_set_fw_progress_sensor(IPMI_FW_PCI_INIT); /* * These last few things must be done as late as possible * because they rely on various other things having been setup, * for example, add_opal_interrupts() will add all the interrupt * sources that are going to the firmware. We can't add a new one * after that call. Similarly, the mem_region calls will construct * the reserve maps in the DT so we shouldn't affect the memory * regions after that */ /* Add the list of interrupts going to OPAL */ add_opal_interrupts(); /* Now release parts of memory nodes we haven't used ourselves... */ mem_region_release_unused(); /* ... and add remaining reservations to the DT */ mem_region_add_dt_reserved(); prd_register_reserved_memory(); load_and_boot_kernel(false); } void __noreturn __secondary_cpu_entry(void) { struct cpu_thread *cpu = this_cpu(); /* Secondary CPU called in */ cpu_callin(cpu); /* Wait for work to do */ while(true) { int i; /* Process pending jobs on this processor */ cpu_process_jobs(); /* Relax a bit to give the simulator some breathing space */ i = 1000; smt_very_low(); asm volatile("mtctr %0;\n" "1: nop; nop; nop; nop;\n" " nop; nop; nop; nop;\n" " nop; nop; nop; nop;\n" " nop; nop; nop; nop;\n" " bdnz 1b" : : "r" (i) : "memory", "ctr"); smt_medium(); } } /* Called from head.S, thus no prototype. */ void __noreturn secondary_cpu_entry(void); void __noreturn secondary_cpu_entry(void) { struct cpu_thread *cpu = this_cpu(); prlog(PR_DEBUG, "INIT: CPU PIR 0x%04x called in\n", cpu->pir); __secondary_cpu_entry(); } skiboot-skiboot-5.1.13/core/interrupts.c000066400000000000000000000177431265204436200202560ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include /* ICP registers */ #define ICP_XIRR 0x4 /* 32-bit access */ #define ICP_CPPR 0x4 /* 8-bit access */ #define ICP_MFRR 0xc /* 8-bit access */ struct irq_source { uint32_t start; uint32_t end; const struct irq_source_ops *ops; void *data; struct list_node link; }; static LIST_HEAD(irq_sources); static struct lock irq_lock = LOCK_UNLOCKED; void register_irq_source(const struct irq_source_ops *ops, void *data, uint32_t start, uint32_t count) { struct irq_source *is, *is1; is = zalloc(sizeof(struct irq_source)); assert(is); is->start = start; is->end = start + count; is->ops = ops; is->data = data; prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p) %s\n", start, start + count - 1, ops, data, ops->interrupt ? "[Internal]" : "[OS]"); lock(&irq_lock); list_for_each(&irq_sources, is1, link) { if (is->end > is1->start && is->start < is1->end) { prerror("register IRQ source overlap !\n"); prerror(" new: %x..%x old: %x..%x\n", is->start, is->end - 1, is1->start, is1->end - 1); assert(0); } } list_add_tail(&irq_sources, &is->link); unlock(&irq_lock); } void unregister_irq_source(uint32_t start, uint32_t count) { struct irq_source *is; lock(&irq_lock); list_for_each(&irq_sources, is, link) { if (start >= is->start && start < is->end) { if (start != is->start || count != (is->end - is->start)) { prerror("unregister IRQ source mismatch !\n"); prerror("start:%x, count: %x match: %x..%x\n", start, count, is->start, is->end); assert(0); } list_del(&is->link); unlock(&irq_lock); /* XXX Add synchronize / RCU */ free(is); return; } } unlock(&irq_lock); prerror("unregister IRQ source not found !\n"); prerror("start:%x, count: %x\n", start, count); assert(0); } /* * This takes a 6-bit chip id and returns a 20 bit value representing * the PSI interrupt. This includes all the fields above, ie, is a * global interrupt number. * * For P8, this returns the base of the 8-interrupts block for PSI */ uint32_t get_psi_interrupt(uint32_t chip_id) { uint32_t irq; switch(proc_gen) { case proc_gen_p7: /* Get the chip ID into position, it already has * the T bit so all we need is room for the GX * bit, 9 bit BUID and 4 bit level */ irq = chip_id << (1 + 9 + 4); /* Add in the BUID */ irq |= P7_PSI_IRQ_BUID << 4; break; case proc_gen_p8: irq = P8_CHIP_IRQ_BLOCK_BASE(chip_id, P8_IRQ_BLOCK_MISC); irq += P8_IRQ_MISC_PSI_BASE; break; default: assert(false); }; return irq; } struct dt_node *add_ics_node(void) { struct dt_node *ics = dt_new_addr(dt_root, "interrupt-controller", 0); if (!ics) return NULL; dt_add_property_cells(ics, "reg", 0, 0, 0, 0); dt_add_property_strings(ics, "compatible", "IBM,ppc-xics", "IBM,opal-xics"); dt_add_property_cells(ics, "#address-cells", 0); dt_add_property_cells(ics, "#interrupt-cells", 1); dt_add_property_string(ics, "device_type", "PowerPC-Interrupt-Source-Controller"); dt_add_property(ics, "interrupt-controller", NULL, 0); return ics; } uint32_t get_ics_phandle(void) { struct dt_node *i; for (i = dt_first(dt_root); i; i = dt_next(dt_root, i)) { if (streq(i->name, "interrupt-controller@0")) { return i->phandle; } } abort(); } void add_opal_interrupts(void) { struct irq_source *is; unsigned int i, count = 0; uint32_t *irqs = NULL, isn; lock(&irq_lock); list_for_each(&irq_sources, is, link) { /* * Add a source to opal-interrupts if it has an * ->interrupt callback */ if (!is->ops->interrupt) continue; for (isn = is->start; isn < is->end; isn++) { i = count++; irqs = realloc(irqs, 4 * count); irqs[i] = isn; } } unlock(&irq_lock); /* The opal-interrupts property has one cell per interrupt, * it is not a standard interrupt property. * * Note: Even if empty, create it, otherwise some bogus error * handling in Linux can cause problems. */ dt_add_property(opal_node, "opal-interrupts", irqs, count * 4); free(irqs); } /* * This is called at init time (and one fast reboot) to sanitize the * ICP. We set our priority to 0 to mask all interrupts and make sure * no IPI is on the way. */ void reset_cpu_icp(void) { void *icp = this_cpu()->icp_regs; assert(icp); /* Clear pending IPIs */ out_8(icp + ICP_MFRR, 0xff); /* Set priority to max, ignore all incoming interrupts, EOI IPIs */ out_be32(icp + ICP_XIRR, 2); } /* Used by the PSI code to send an EOI during reset. This will also * set the CPPR to 0 which should already be the case anyway */ void icp_send_eoi(uint32_t interrupt) { void *icp = this_cpu()->icp_regs; assert(icp); /* Set priority to max, ignore all incoming interrupts */ out_be32(icp + ICP_XIRR, interrupt & 0xffffff); } /* This is called before winkle, we clear pending IPIs and set our priority * to 1 to mask all but the IPI */ void icp_prep_for_rvwinkle(void) { void *icp = this_cpu()->icp_regs; assert(icp); /* Clear pending IPIs */ out_8(icp + ICP_MFRR, 0xff); /* Set priority to 1, ignore all incoming interrupts, EOI IPIs */ out_be32(icp + ICP_XIRR, 0x01000002); } /* This is called to wakeup somebody from winkle */ void icp_kick_cpu(struct cpu_thread *cpu) { void *icp = cpu->icp_regs; assert(icp); /* Send high priority IPI */ out_8(icp + ICP_MFRR, 0); } static struct irq_source *irq_find_source(uint32_t isn) { struct irq_source *is; lock(&irq_lock); list_for_each(&irq_sources, is, link) { if (isn >= is->start && isn < is->end) { unlock(&irq_lock); return is; } } unlock(&irq_lock); return NULL; } static int64_t opal_set_xive(uint32_t isn, uint16_t server, uint8_t priority) { struct irq_source *is = irq_find_source(isn); if (!is || !is->ops->set_xive) return OPAL_PARAMETER; return is->ops->set_xive(is->data, isn, server, priority); } opal_call(OPAL_SET_XIVE, opal_set_xive, 3); static int64_t opal_get_xive(uint32_t isn, uint16_t *server, uint8_t *priority) { struct irq_source *is = irq_find_source(isn); if (!is || !is->ops->get_xive) return OPAL_PARAMETER; return is->ops->get_xive(is->data, isn, server, priority); } opal_call(OPAL_GET_XIVE, opal_get_xive, 3); static int64_t opal_handle_interrupt(uint32_t isn, uint64_t *outstanding_event_mask) { struct irq_source *is = irq_find_source(isn); int64_t rc = OPAL_SUCCESS; /* No source ? return */ if (!is || !is->ops->interrupt) { rc = OPAL_PARAMETER; goto bail; } /* Run it */ is->ops->interrupt(is->data, isn); /* Check timers if SLW timer isn't working */ if (!slw_timer_ok()) check_timers(true); /* Update output events */ bail: if (outstanding_event_mask) *outstanding_event_mask = opal_pending_events; return rc; } opal_call(OPAL_HANDLE_INTERRUPT, opal_handle_interrupt, 2); void init_interrupts(void) { struct dt_node *icp; const struct dt_property *sranges; struct cpu_thread *cpu; u32 base, count, i; u64 addr, size; dt_for_each_compatible(dt_root, icp, "ibm,ppc-xicp") { sranges = dt_require_property(icp, "ibm,interrupt-server-ranges", -1); base = dt_get_number(sranges->prop, 1); count = dt_get_number(sranges->prop + 4, 1); for (i = 0; i < count; i++) { addr = dt_get_address(icp, i, &size); cpu = find_cpu_by_server(base + i); if (cpu) cpu->icp_regs = (void *)addr; } } } skiboot-skiboot-5.1.13/core/ipmi.c000066400000000000000000000142571265204436200167720ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include struct ipmi_backend *ipmi_backend = NULL; static struct lock sync_lock = LOCK_UNLOCKED; static struct ipmi_msg *sync_msg = NULL; void ipmi_free_msg(struct ipmi_msg *msg) { /* ipmi_free_msg frees messages allocated by the * backend. Without a backend we couldn't have allocated * messages to free (we don't support removing backends * yet). */ if (!ipmi_present()) { prerror("IPMI: Trying to free message without backend\n"); return; } msg->backend->free_msg(msg); } void ipmi_init_msg(struct ipmi_msg *msg, int interface, uint32_t code, void (*complete)(struct ipmi_msg *), void *user_data, size_t req_size, size_t resp_size) { /* We don't actually support multiple interfaces at the moment. */ assert(interface == IPMI_DEFAULT_INTERFACE); msg->backend = ipmi_backend; msg->cmd = IPMI_CMD(code); msg->netfn = IPMI_NETFN(code) << 2; msg->req_size = req_size; msg->resp_size = resp_size; msg->complete = complete; msg->user_data = user_data; } struct ipmi_msg *ipmi_mkmsg_simple(uint32_t code, void *req_data, size_t req_size) { return ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, code, ipmi_free_msg, NULL, req_data, req_size, 0); } struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code, void (*complete)(struct ipmi_msg *), void *user_data, void *req_data, size_t req_size, size_t resp_size) { struct ipmi_msg *msg; if (!ipmi_present()) return NULL; msg = ipmi_backend->alloc_msg(req_size, resp_size); if (!msg) return NULL; ipmi_init_msg(msg, interface, code, complete, user_data, req_size, resp_size); /* Commands are free to over ride this if they want to handle errors */ msg->error = ipmi_free_msg; if (req_data) memcpy(msg->data, req_data, req_size); return msg; } int ipmi_queue_msg_head(struct ipmi_msg *msg) { if (!ipmi_present()) return OPAL_HARDWARE; if (!msg) { prerror("%s: Attempting to queue NULL message\n", __func__); return OPAL_PARAMETER; } return msg->backend->queue_msg_head(msg); } int ipmi_queue_msg(struct ipmi_msg *msg) { /* Here we could choose which interface to use if we want to support multiple interfaces. */ if (!ipmi_present()) return OPAL_HARDWARE; if (!msg) { prerror("%s: Attempting to queue NULL message\n", __func__); return OPAL_PARAMETER; } return msg->backend->queue_msg(msg); } int ipmi_dequeue_msg(struct ipmi_msg *msg) { if (!ipmi_present()) return OPAL_HARDWARE; if (!msg) { prerror("%s: Attempting to dequeue NULL message\n", __func__); return OPAL_PARAMETER; } return msg->backend->dequeue_msg(msg); } void ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc, struct ipmi_msg *msg) { msg->cc = cc; if (msg->cmd != cmd) { prerror("IPMI: Incorrect cmd 0x%02x in response\n", cmd); cc = IPMI_ERR_UNSPECIFIED; } if ((msg->netfn >> 2) + 1 != (netfn >> 2)) { prerror("IPMI: Incorrect netfn 0x%02x in response\n", netfn); cc = IPMI_ERR_UNSPECIFIED; } msg->netfn = netfn; if (cc != IPMI_CC_NO_ERROR) { prlog(PR_DEBUG, "IPMI: Got error response 0x%02x\n", msg->cc); assert(msg->error); msg->error(msg); } else if (msg->complete) msg->complete(msg); /* At this point the message has should have been freed by the completion functions. */ /* If this is a synchronous message flag that we are done */ if (msg == sync_msg) sync_msg = NULL; } void ipmi_queue_msg_sync(struct ipmi_msg *msg) { if (!ipmi_present()) return; if (!msg) { prerror("%s: Attempting to queue NULL message\n", __func__); return; } lock(&sync_lock); while (sync_msg); sync_msg = msg; ipmi_queue_msg(msg); unlock(&sync_lock); while (sync_msg == msg) time_wait_ms(100); } static void ipmi_read_event_complete(struct ipmi_msg *msg) { prlog(PR_DEBUG, "IPMI read event %02x complete: %d bytes. cc: %02x\n", msg->cmd, msg->resp_size, msg->cc); /* Handle power control & PNOR handshake events */ ipmi_parse_sel(msg); ipmi_free_msg(msg); } static void ipmi_get_message_flags_complete(struct ipmi_msg *msg) { uint8_t flags = msg->data[0]; ipmi_free_msg(msg); prlog(PR_DEBUG, "IPMI Get Message Flags: %02x\n", flags); /* Once we see an interrupt we assume the payload has * booted. We disable the wdt and let the OS setup its own * wdt. * * This is also where we consider the OS to be booted, so we set * the boot count sensor */ if (flags & IPMI_MESSAGE_FLAGS_WATCHDOG_PRE_TIMEOUT) { ipmi_wdt_stop(); ipmi_set_boot_count(); } /* Message available in the event buffer? Queue a Read Event command * to retrieve it. The flag is cleared by performing a read */ if (flags & IPMI_MESSAGE_FLAGS_EVENT_BUFFER) { msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_READ_EVENT, ipmi_read_event_complete, NULL, NULL, 0, 16); ipmi_queue_msg(msg); } } void ipmi_sms_attention(void) { struct ipmi_msg *msg; if (!ipmi_present()) return; /* todo: when we handle multiple IPMI interfaces, we'll need to * ensure that this message is associated with the appropriate * backend. */ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_GET_MESSAGE_FLAGS, ipmi_get_message_flags_complete, NULL, NULL, 0, 1); ipmi_queue_msg(msg); } void ipmi_register_backend(struct ipmi_backend *backend) { /* We only support one backend at the moment */ assert(backend->alloc_msg); assert(backend->free_msg); assert(backend->queue_msg); assert(backend->dequeue_msg); ipmi_backend = backend; ipmi_backend->opal_event_ipmi_recv = opal_dynamic_event_alloc(); } bool ipmi_present(void) { return ipmi_backend != NULL; } skiboot-skiboot-5.1.13/core/lock.c000066400000000000000000000053341265204436200167600ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* Set to bust locks. Note, this is initialized to true because our * lock debugging code is not going to work until we have the per * CPU data initialized */ bool bust_locks = true; #ifdef DEBUG_LOCKS static void lock_error(struct lock *l, const char *reason, uint16_t err) { bust_locks = true; fprintf(stderr, "LOCK ERROR: %s @%p (state: 0x%016lx)\n", reason, l, l->lock_val); op_display(OP_FATAL, OP_MOD_LOCK, err); abort(); } static void lock_check(struct lock *l) { if ((l->lock_val & 1) && (l->lock_val >> 32) == this_cpu()->pir) lock_error(l, "Invalid recursive lock", 0); } static void unlock_check(struct lock *l) { if (!(l->lock_val & 1)) lock_error(l, "Unlocking unlocked lock", 1); if ((l->lock_val >> 32) != this_cpu()->pir) lock_error(l, "Unlocked non-owned lock", 2); if (l->in_con_path && this_cpu()->con_suspend == 0) lock_error(l, "Unlock con lock with console not suspended", 3); if (this_cpu()->lock_depth == 0) lock_error(l, "Releasing lock with 0 depth", 4); } #else static inline void lock_check(struct lock *l) { }; static inline void unlock_check(struct lock *l) { }; #endif /* DEBUG_LOCKS */ bool lock_held_by_me(struct lock *l) { uint64_t pir64 = this_cpu()->pir; return l->lock_val == ((pir64 << 32) | 1); } bool try_lock(struct lock *l) { if (__try_lock(l)) { if (l->in_con_path) this_cpu()->con_suspend++; this_cpu()->lock_depth++; return true; } return false; } void lock(struct lock *l) { if (bust_locks) return; lock_check(l); for (;;) { if (try_lock(l)) break; cpu_relax(); } } void unlock(struct lock *l) { struct cpu_thread *cpu = this_cpu(); if (bust_locks) return; unlock_check(l); lwsync(); this_cpu()->lock_depth--; l->lock_val = 0; if (l->in_con_path) { cpu->con_suspend--; if (cpu->con_suspend == 0 && cpu->con_need_flush) flush_console(); } } bool lock_recursive(struct lock *l) { if (bust_locks) return false; if (lock_held_by_me(l)) return false; lock(l); return true; } void init_locks(void) { bust_locks = false; } skiboot-skiboot-5.1.13/core/malloc.c000066400000000000000000000040101265204436200172650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Wrappers for malloc, et. al. */ #include #include #include #include #define DEFAULT_ALIGN __alignof__(long) void *__memalign(size_t blocksize, size_t bytes, const char *location) { void *p; lock(&skiboot_heap.free_list_lock); p = mem_alloc(&skiboot_heap, bytes, blocksize, location); unlock(&skiboot_heap.free_list_lock); return p; } void *__malloc(size_t bytes, const char *location) { return __memalign(DEFAULT_ALIGN, bytes, location); } void __free(void *p, const char *location) { lock(&skiboot_heap.free_list_lock); mem_free(&skiboot_heap, p, location); unlock(&skiboot_heap.free_list_lock); } void *__realloc(void *ptr, size_t size, const char *location) { void *newptr; /* Two classic malloc corner cases. */ if (!size) { __free(ptr, location); return NULL; } if (!ptr) return __malloc(size, location); lock(&skiboot_heap.free_list_lock); if (mem_resize(&skiboot_heap, ptr, size, location)) { newptr = ptr; } else { newptr = mem_alloc(&skiboot_heap, size, DEFAULT_ALIGN, location); if (newptr) { size_t copy = mem_allocated_size(ptr); if (copy > size) copy = size; memcpy(newptr, ptr, copy); mem_free(&skiboot_heap, ptr, location); } } unlock(&skiboot_heap.free_list_lock); return newptr; } void *__zalloc(size_t bytes, const char *location) { void *p = __malloc(bytes, location); if (p) memset(p, 0, bytes); return p; } skiboot-skiboot-5.1.13/core/mem_region.c000066400000000000000000000726571265204436200201650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include int64_t mem_dump_free(void); void mem_dump_allocs(void); /* Memory poisoning on free (if POISON_MEM_REGION set to 1) */ #define POISON_MEM_REGION 0 #define POISON_MEM_REGION_WITH 0x99 #define POISON_MEM_REGION_LIMIT 1*1024*1024*1024 /* Locking: The mem_region_lock protects the regions list from concurrent * updates. Additions to, or removals from, the region list must be done * with this lock held. This is typically done when we're establishing * the memory & reserved regions. * * Each region has a lock (region->free_list_lock) to protect the free list * from concurrent modification. This lock is used when we're allocating * memory out of a specific region. * * If both locks are needed (eg, __local_alloc, where we need to find a region, * then allocate from it), the mem_region_lock must be acquired before (and * released after) the per-region lock. */ struct lock mem_region_lock = LOCK_UNLOCKED; static struct list_head regions = LIST_HEAD_INIT(regions); static bool mem_regions_finalised = false; unsigned long top_of_ram = SKIBOOT_BASE + SKIBOOT_SIZE; static struct mem_region skiboot_os_reserve = { .name = "ibm,os-reserve", .start = 0, .len = SKIBOOT_BASE, .type = REGION_OS, }; struct mem_region skiboot_heap = { .name = "ibm,firmware-heap", .start = HEAP_BASE, .len = HEAP_SIZE, .type = REGION_SKIBOOT_HEAP, }; static struct mem_region skiboot_code_and_text = { .name = "ibm,firmware-code", .start = SKIBOOT_BASE, .len = HEAP_BASE - SKIBOOT_BASE, .type = REGION_SKIBOOT_FIRMWARE, }; static struct mem_region skiboot_after_heap = { .name = "ibm,firmware-data", .start = HEAP_BASE + HEAP_SIZE, .len = SKIBOOT_BASE + SKIBOOT_SIZE - (HEAP_BASE + HEAP_SIZE), .type = REGION_SKIBOOT_FIRMWARE, }; static struct mem_region skiboot_cpu_stacks = { .name = "ibm,firmware-stacks", .start = CPU_STACKS_BASE, .len = 0, /* TBA */ .type = REGION_SKIBOOT_FIRMWARE, }; struct alloc_hdr { bool free : 1; bool prev_free : 1; unsigned long num_longs : BITS_PER_LONG-2; /* Including header. */ const char *location; }; struct free_hdr { struct alloc_hdr hdr; struct list_node list; /* ... unsigned long tailer; */ }; #define ALLOC_HDR_LONGS (sizeof(struct alloc_hdr) / sizeof(long)) #define ALLOC_MIN_LONGS (sizeof(struct free_hdr) / sizeof(long) + 1) /* Avoid ugly casts. */ static void *region_start(const struct mem_region *region) { return (void *)(unsigned long)region->start; } /* Each free block has a tailer, so we can walk backwards. */ static unsigned long *tailer(struct free_hdr *f) { return (unsigned long *)f + f->hdr.num_longs - 1; } /* This walks forward to the next hdr (or NULL if at the end). */ static struct alloc_hdr *next_hdr(const struct mem_region *region, const struct alloc_hdr *hdr) { void *next; next = ((unsigned long *)hdr + hdr->num_longs); if (next >= region_start(region) + region->len) next = NULL; return next; } /* Creates free block covering entire region. */ static void init_allocatable_region(struct mem_region *region) { struct free_hdr *f = region_start(region); assert(region->type == REGION_SKIBOOT_HEAP || region->type == REGION_MEMORY); f->hdr.num_longs = region->len / sizeof(long); f->hdr.free = true; f->hdr.prev_free = false; *tailer(f) = f->hdr.num_longs; list_head_init(®ion->free_list); list_add(®ion->free_list, &f->list); } static void make_free(struct mem_region *region, struct free_hdr *f, const char *location) { struct alloc_hdr *next; #if POISON_MEM_REGION == 1 size_t poison_size= (void*)tailer(f) - (void*)(f+1); /* We only poison up to a limit, as otherwise boot is kinda slow */ if (poison_size > POISON_MEM_REGION_LIMIT) { poison_size = POISON_MEM_REGION_LIMIT; } memset(f+1, POISON_MEM_REGION_WITH, poison_size); #endif if (f->hdr.prev_free) { struct free_hdr *prev; unsigned long *prev_tailer = (unsigned long *)f - 1; assert(*prev_tailer); prev = (void *)((unsigned long *)f - *prev_tailer); assert(prev->hdr.free); assert(!prev->hdr.prev_free); /* Expand to cover the one we just freed. */ prev->hdr.num_longs += f->hdr.num_longs; f = prev; } else { f->hdr.free = true; f->hdr.location = location; list_add(®ion->free_list, &f->list); } /* Fix up tailer. */ *tailer(f) = f->hdr.num_longs; /* If next is free, coalesce it */ next = next_hdr(region, &f->hdr); if (next) { next->prev_free = true; if (next->free) { struct free_hdr *next_free = (void *)next; list_del_from(®ion->free_list, &next_free->list); /* Maximum of one level of recursion */ make_free(region, next_free, location); } } } /* Can we fit this many longs with this alignment in this free block? */ static bool fits(struct free_hdr *f, size_t longs, size_t align, size_t *offset) { *offset = 0; while (f->hdr.num_longs >= *offset + longs) { size_t addr; addr = (unsigned long)f + (*offset + ALLOC_HDR_LONGS) * sizeof(long); if ((addr & (align - 1)) == 0) return true; /* Don't make tiny chunks! */ if (*offset == 0) *offset = ALLOC_MIN_LONGS; else (*offset)++; } return false; } static void discard_excess(struct mem_region *region, struct alloc_hdr *hdr, size_t alloc_longs, const char *location) { /* Do we have excess? */ if (hdr->num_longs > alloc_longs + ALLOC_MIN_LONGS) { struct free_hdr *post; /* Set up post block. */ post = (void *)hdr + alloc_longs * sizeof(long); post->hdr.num_longs = hdr->num_longs - alloc_longs; post->hdr.prev_free = false; /* Trim our block. */ hdr->num_longs = alloc_longs; /* This coalesces as required. */ make_free(region, post, location); } } static const char *hdr_location(const struct alloc_hdr *hdr) { /* Corrupt: step carefully! */ if (is_rodata(hdr->location)) return hdr->location; return "*CORRUPT*"; } static void bad_header(const struct mem_region *region, const struct alloc_hdr *hdr, const char *during, const char *location) { /* Corrupt: step carefully! */ if (is_rodata(hdr->location)) prerror("%p (in %s) %s at %s, previously %s\n", hdr-1, region->name, during, location, hdr->location); else prerror("%p (in %s) %s at %s, previously %p\n", hdr-1, region->name, during, location, hdr->location); abort(); } static bool region_is_reservable(struct mem_region *region) { return region->type != REGION_OS; } static bool region_is_reserved(struct mem_region *region) { return region->type != REGION_OS && region->type != REGION_MEMORY; } void mem_dump_allocs(void) { struct mem_region *region; struct alloc_hdr *hdr; /* Second pass: populate property data */ printf("Memory regions:\n"); list_for_each(®ions, region, list) { if (!(region->type == REGION_SKIBOOT_HEAP || region->type == REGION_MEMORY)) continue; printf(" 0x%012llx..%012llx : %s\n", (long long)region->start, (long long)(region->start + region->len - 1), region->name); if (region->free_list.n.next == NULL) { printf(" no allocs\n"); continue; } for (hdr = region_start(region); hdr; hdr = next_hdr(region, hdr)) { if (hdr->free) continue; printf(" 0x%.8lx %s\n", hdr->num_longs * sizeof(long), hdr_location(hdr)); } } } int64_t mem_dump_free(void) { struct mem_region *region; struct alloc_hdr *hdr; int64_t total_free; int64_t region_free; total_free = 0; printf("Free space in HEAP memory regions:\n"); list_for_each(®ions, region, list) { if (!(region->type == REGION_SKIBOOT_HEAP || region->type == REGION_MEMORY)) continue; region_free = 0; if (region->free_list.n.next == NULL) { continue; } for (hdr = region_start(region); hdr; hdr = next_hdr(region, hdr)) { if (!hdr->free) continue; region_free+= hdr->num_longs * sizeof(long); } printf("Region %s free: %"PRIx64"\n", region->name, region_free); total_free += region_free; } printf("Total free: %"PRIu64"\n", total_free); return total_free; } static void *__mem_alloc(struct mem_region *region, size_t size, size_t align, const char *location) { size_t alloc_longs, offset; struct free_hdr *f; struct alloc_hdr *next; /* Align must be power of 2. */ assert(!((align - 1) & align)); /* This should be a constant. */ assert(is_rodata(location)); /* Unallocatable region? */ if (!(region->type == REGION_SKIBOOT_HEAP || region->type == REGION_MEMORY)) return NULL; /* First allocation? */ if (region->free_list.n.next == NULL) init_allocatable_region(region); /* Don't do screwy sizes. */ if (size > region->len) return NULL; /* Don't do tiny alignments, we deal in long increments. */ if (align < sizeof(long)) align = sizeof(long); /* Convert size to number of longs, too. */ alloc_longs = (size + sizeof(long)-1) / sizeof(long) + ALLOC_HDR_LONGS; /* Can't be too small for when we free it, either. */ if (alloc_longs < ALLOC_MIN_LONGS) alloc_longs = ALLOC_MIN_LONGS; /* Walk free list. */ list_for_each(®ion->free_list, f, list) { /* We may have to skip some to meet alignment. */ if (fits(f, alloc_longs, align, &offset)) goto found; } return NULL; found: assert(f->hdr.free); assert(!f->hdr.prev_free); /* This block is no longer free. */ list_del_from(®ion->free_list, &f->list); f->hdr.free = false; f->hdr.location = location; next = next_hdr(region, &f->hdr); if (next) { assert(next->prev_free); next->prev_free = false; } if (offset != 0) { struct free_hdr *pre = f; f = (void *)f + offset * sizeof(long); assert(f >= pre + 1); /* Set up new header. */ f->hdr.num_longs = pre->hdr.num_longs - offset; /* f->hdr.prev_free will be set by make_free below. */ f->hdr.free = false; f->hdr.location = location; /* Fix up old header. */ pre->hdr.num_longs = offset; pre->hdr.prev_free = false; /* This coalesces as required. */ make_free(region, pre, location); } /* We might be too long; put the rest back. */ discard_excess(region, &f->hdr, alloc_longs, location); /* Clear tailer for debugging */ *tailer(f) = 0; /* Their pointer is immediately after header. */ return &f->hdr + 1; } void *mem_alloc(struct mem_region *region, size_t size, size_t align, const char *location) { void *r; assert(lock_held_by_me(®ion->free_list_lock)); r = __mem_alloc(region, size, align, location); if (r) return r; prerror("mem_alloc(0x%lx, 0x%lx, \"%s\") failed !\n", size, align, location); mem_dump_allocs(); return NULL; } void mem_free(struct mem_region *region, void *mem, const char *location) { struct alloc_hdr *hdr; /* This should be a constant. */ assert(is_rodata(location)); assert(lock_held_by_me(®ion->free_list_lock)); /* Freeing NULL is always a noop. */ if (!mem) return; /* Your memory is in the region, right? */ assert(mem >= region_start(region) + sizeof(*hdr)); assert(mem < region_start(region) + region->len); /* Grab header. */ hdr = mem - sizeof(*hdr); if (hdr->free) bad_header(region, hdr, "re-freed", location); make_free(region, (struct free_hdr *)hdr, location); } size_t mem_allocated_size(const void *ptr) { const struct alloc_hdr *hdr = ptr - sizeof(*hdr); return hdr->num_longs * sizeof(long) - sizeof(struct alloc_hdr); } bool mem_resize(struct mem_region *region, void *mem, size_t len, const char *location) { struct alloc_hdr *hdr, *next; struct free_hdr *f; /* This should be a constant. */ assert(is_rodata(location)); assert(lock_held_by_me(®ion->free_list_lock)); /* Get header. */ hdr = mem - sizeof(*hdr); if (hdr->free) bad_header(region, hdr, "resize", location); /* Round up size to multiple of longs. */ len = (sizeof(*hdr) + len + sizeof(long) - 1) / sizeof(long); /* Can't be too small for when we free it, either. */ if (len < ALLOC_MIN_LONGS) len = ALLOC_MIN_LONGS; /* Shrinking is simple. */ if (len <= hdr->num_longs) { hdr->location = location; discard_excess(region, hdr, len, location); return true; } /* Check if we can expand. */ next = next_hdr(region, hdr); if (!next || !next->free || hdr->num_longs + next->num_longs < len) return false; /* OK, it's free and big enough, absorb it. */ f = (struct free_hdr *)next; list_del_from(®ion->free_list, &f->list); hdr->num_longs += next->num_longs; hdr->location = location; /* Update next prev_free */ next = next_hdr(region, &f->hdr); if (next) { assert(next->prev_free); next->prev_free = false; } /* Clear tailer for debugging */ *tailer(f) = 0; /* Now we might have *too* much. */ discard_excess(region, hdr, len, location); return true; } bool mem_check(const struct mem_region *region) { size_t frees = 0; struct alloc_hdr *hdr, *prev_free = NULL; struct free_hdr *f; /* Check it's sanely aligned. */ if (region->start % sizeof(struct alloc_hdr)) { prerror("Region '%s' not sanely aligned (%llx)\n", region->name, (unsigned long long)region->start); return false; } if ((long)region->len % sizeof(struct alloc_hdr)) { prerror("Region '%s' not sane length (%llu)\n", region->name, (unsigned long long)region->len); return false; } /* Not ours to play with, or empty? Don't do anything. */ if (!(region->type == REGION_MEMORY || region->type == REGION_SKIBOOT_HEAP) || region->free_list.n.next == NULL) return true; /* Walk linearly. */ for (hdr = region_start(region); hdr; hdr = next_hdr(region, hdr)) { if (hdr->num_longs < ALLOC_MIN_LONGS) { prerror("Region '%s' %s %p (%s) size %zu\n", region->name, hdr->free ? "free" : "alloc", hdr, hdr_location(hdr), hdr->num_longs * sizeof(long)); return false; } if ((unsigned long)hdr + hdr->num_longs * sizeof(long) > region->start + region->len) { prerror("Region '%s' %s %p (%s) oversize %zu\n", region->name, hdr->free ? "free" : "alloc", hdr, hdr_location(hdr), hdr->num_longs * sizeof(long)); return false; } if (hdr->free) { if (hdr->prev_free || prev_free) { prerror("Region '%s' free %p (%s) has prev_free" " %p (%s) %sset?\n", region->name, hdr, hdr_location(hdr), prev_free, prev_free ? hdr_location(prev_free) : "NULL", hdr->prev_free ? "" : "un"); return false; } prev_free = hdr; frees ^= (unsigned long)hdr - region->start; } else { if (hdr->prev_free != (bool)prev_free) { prerror("Region '%s' alloc %p (%s) has" " prev_free %p %sset?\n", region->name, hdr, hdr_location(hdr), prev_free, hdr->prev_free ? "" : "un"); return false; } prev_free = NULL; } } /* Now walk free list. */ list_for_each(®ion->free_list, f, list) frees ^= (unsigned long)f - region->start; if (frees) { prerror("Region '%s' free list and walk do not match!\n", region->name); return false; } return true; } static struct mem_region *new_region(const char *name, uint64_t start, uint64_t len, struct dt_node *node, enum mem_region_type type) { struct mem_region *region; region = malloc(sizeof(*region)); if (!region) return NULL; region->name = name; region->start = start; region->len = len; region->node = node; region->type = type; region->free_list.n.next = NULL; init_lock(®ion->free_list_lock); return region; } /* We always split regions, so we only have to replace one. */ static struct mem_region *split_region(struct mem_region *head, uint64_t split_at, enum mem_region_type type) { struct mem_region *tail; uint64_t end = head->start + head->len; tail = new_region(head->name, split_at, end - split_at, head->node, type); /* Original region becomes head. */ if (tail) head->len -= tail->len; return tail; } static bool intersects(const struct mem_region *region, uint64_t addr) { return addr > region->start && addr < region->start + region->len; } static bool maybe_split(struct mem_region *r, uint64_t split_at) { struct mem_region *tail; if (!intersects(r, split_at)) return true; tail = split_region(r, split_at, r->type); if (!tail) return false; /* Tail add is important: we may need to split again! */ list_add_tail(®ions, &tail->list); return true; } static bool overlaps(const struct mem_region *r1, const struct mem_region *r2) { return (r1->start + r1->len > r2->start && r1->start < r2->start + r2->len); } static struct mem_region *get_overlap(const struct mem_region *region) { struct mem_region *i; list_for_each(®ions, i, list) { if (overlaps(region, i)) return i; } return NULL; } static bool add_region(struct mem_region *region) { struct mem_region *r; if (mem_regions_finalised) { prerror("MEM: add_region(%s@0x%"PRIx64") called after finalise!\n", region->name, region->start); return false; } /* First split any regions which intersect. */ list_for_each(®ions, r, list) if (!maybe_split(r, region->start) || !maybe_split(r, region->start + region->len)) return false; /* Now we have only whole overlaps, if any. */ while ((r = get_overlap(region)) != NULL) { assert(r->start == region->start); assert(r->len == region->len); list_del_from(®ions, &r->list); free(r); } /* Finally, add in our own region. */ list_add(®ions, ®ion->list); return true; } void mem_reserve_hw(const char *name, uint64_t start, uint64_t len) { struct mem_region *region; bool added; lock(&mem_region_lock); region = new_region(name, start, len, NULL, REGION_HW_RESERVED); assert(region); added = add_region(region); assert(added); unlock(&mem_region_lock); } static bool matches_chip_id(const __be32 ids[], size_t num, u32 chip_id) { size_t i; for (i = 0; i < num; i++) if (be32_to_cpu(ids[i]) == chip_id) return true; return false; } void *__local_alloc(unsigned int chip_id, size_t size, size_t align, const char *location) { struct mem_region *region; void *p = NULL; bool use_local = true; lock(&mem_region_lock); restart: list_for_each(®ions, region, list) { const struct dt_property *prop; const __be32 *ids; if (!(region->type == REGION_SKIBOOT_HEAP || region->type == REGION_MEMORY)) continue; /* Don't allocate from normal heap. */ if (region == &skiboot_heap) continue; /* First pass, only match node local regions */ if (use_local) { if (!region->node) continue; prop = dt_find_property(region->node, "ibm,chip-id"); ids = (const __be32 *)prop->prop; if (!matches_chip_id(ids, prop->len/sizeof(u32), chip_id)) continue; } /* Second pass, match anything */ lock(®ion->free_list_lock); p = mem_alloc(region, size, align, location); unlock(®ion->free_list_lock); if (p) break; } /* * If we can't allocate the memory block from the expected * node, we bail to any one that can accommodate our request. */ if (!p && use_local) { use_local = false; goto restart; } unlock(&mem_region_lock); return p; } struct mem_region *find_mem_region(const char *name) { struct mem_region *region; list_for_each(®ions, region, list) { if (streq(region->name, name)) return region; } return NULL; } bool mem_range_is_reserved(uint64_t start, uint64_t size) { uint64_t end = start + size; struct mem_region *region; /* We may have the range covered by a number of regions, which could * appear in any order. So, we look for a region that covers the * start address, and bump start up to the end of that region. * * We repeat until we've either bumped past the end of the range, * or we didn't find a matching region. * * This has a worst-case of O(n^2), but n is well bounded by the * small number of reservations. */ for (;;) { bool found = false; list_for_each(®ions, region, list) { if (!region_is_reserved(region)) continue; /* does this region overlap the start address, and * have a non-zero size? */ if (region->start <= start && region->start + region->len > start && region->len) { start = region->start + region->len; found = true; } } /* 'end' is the first byte outside of the range */ if (start >= end) return true; if (!found) break; } return false; } void adjust_cpu_stacks_alloc(void) { /* CPU stacks start at 0, then when we know max possible PIR, * we adjust, then when we bring all CPUs online we know the * runtime max PIR, so we adjust this a few times during boot. */ skiboot_cpu_stacks.len = (cpu_max_pir + 1) * STACK_SIZE; } static void mem_region_parse_reserved_properties(void) { const struct dt_property *names, *ranges; struct mem_region *region; prlog(PR_INFO, "MEM: parsing reserved memory from " "reserved-names/-ranges properties\n"); names = dt_find_property(dt_root, "reserved-names"); ranges = dt_find_property(dt_root, "reserved-ranges"); if (names && ranges) { const uint64_t *range; int n, len; range = (const void *)ranges->prop; for (n = 0; n < names->len; n += len, range += 2) { char *name; len = strlen(names->prop + n) + 1; name = strdup(names->prop + n); region = new_region(name, dt_get_number(range, 2), dt_get_number(range + 1, 2), NULL, REGION_HW_RESERVED); list_add(®ions, ®ion->list); } } else if (names || ranges) { prerror("Invalid properties: reserved-names=%p " "with reserved-ranges=%p\n", names, ranges); abort(); } else { return; } } static bool mem_region_parse_reserved_nodes(const char *path) { struct dt_node *parent, *node; parent = dt_find_by_path(dt_root, path); if (!parent) return false; prlog(PR_INFO, "MEM: parsing reserved memory from node %s\n", path); dt_for_each_child(parent, node) { const struct dt_property *reg; struct mem_region *region; reg = dt_find_property(node, "reg"); if (!reg) { char *nodepath = dt_get_path(node); prerror("node %s has no reg property, ignoring\n", nodepath); free(nodepath); continue; } region = new_region(strdup(node->name), dt_get_number(reg->prop, 2), dt_get_number(reg->prop + sizeof(u64), 2), node, REGION_HW_RESERVED); list_add(®ions, ®ion->list); } return true; } /* Trawl through device tree, create memory regions from nodes. */ void mem_region_init(void) { struct mem_region *region; struct dt_node *i; bool rc; /* Ensure we have no collision between skiboot core and our heap */ extern char _end[]; BUILD_ASSERT(HEAP_BASE >= (uint64_t)_end); /* * Add associativity properties outside of the lock * to avoid recursive locking caused by allocations * done by add_chip_dev_associativity() */ dt_for_each_node(dt_root, i) { if (!dt_has_node_property(i, "device_type", "memory")) continue; /* Add associativity properties */ add_chip_dev_associativity(i); } /* Add each memory node. */ dt_for_each_node(dt_root, i) { uint64_t start, len; char *rname; #define NODE_REGION_PREFIX "ibm,firmware-allocs-" if (!dt_has_node_property(i, "device_type", "memory")) continue; rname = zalloc(strlen(i->name) + strlen(NODE_REGION_PREFIX) + 1); assert(rname); strcat(rname, NODE_REGION_PREFIX); strcat(rname, i->name); start = dt_get_address(i, 0, &len); lock(&mem_region_lock); region = new_region(rname, start, len, i, REGION_MEMORY); if (!region) { prerror("MEM: Could not add mem region %s!\n", i->name); abort(); } list_add(®ions, ®ion->list); if ((start + len) > top_of_ram) top_of_ram = start + len; unlock(&mem_region_lock); } adjust_cpu_stacks_alloc(); lock(&mem_region_lock); /* Now carve out our own reserved areas. */ if (!add_region(&skiboot_os_reserve) || !add_region(&skiboot_code_and_text) || !add_region(&skiboot_heap) || !add_region(&skiboot_after_heap) || !add_region(&skiboot_cpu_stacks)) { prerror("Out of memory adding skiboot reserved areas\n"); abort(); } /* Add reserved ranges from the DT */ rc = mem_region_parse_reserved_nodes("/reserved-memory"); if (!rc) rc = mem_region_parse_reserved_nodes( "/ibm,hostboot/reserved-memory"); if (!rc) mem_region_parse_reserved_properties(); unlock(&mem_region_lock); } static uint64_t allocated_length(const struct mem_region *r) { struct free_hdr *f, *last = NULL; /* No allocations at all? */ if (r->free_list.n.next == NULL) return 0; /* Find last free block. */ list_for_each(&r->free_list, f, list) if (f > last) last = f; /* No free blocks? */ if (!last) return r->len; /* Last free block isn't at end? */ if (next_hdr(r, &last->hdr)) return r->len; return (unsigned long)last - r->start; } /* Separate out allocated sections into their own region. */ void mem_region_release_unused(void) { struct mem_region *r; lock(&mem_region_lock); assert(!mem_regions_finalised); printf("Releasing unused memory:\n"); list_for_each(®ions, r, list) { uint64_t used_len; /* If it's not allocatable, ignore it. */ if (!(r->type == REGION_SKIBOOT_HEAP || r->type == REGION_MEMORY)) continue; used_len = allocated_length(r); printf(" %s: %llu/%llu used\n", r->name, (long long)used_len, (long long)r->len); /* We keep the skiboot heap. */ if (r == &skiboot_heap) continue; /* Nothing used? Whole thing is for Linux. */ if (used_len == 0) r->type = REGION_OS; /* Partially used? Split region. */ else if (used_len != r->len) { struct mem_region *for_linux; struct free_hdr *last = region_start(r) + used_len; /* Remove the final free block. */ list_del_from(&r->free_list, &last->list); for_linux = split_region(r, r->start + used_len, REGION_OS); if (!for_linux) { prerror("OOM splitting mem node %s for linux\n", r->name); abort(); } list_add(®ions, &for_linux->list); } } unlock(&mem_region_lock); } static void mem_region_add_dt_reserved_node(struct dt_node *parent, struct mem_region *region) { char *name, *p; /* If a reserved region was established before skiboot, it may be * referenced by a device-tree node with extra data. In that case, * copy the node to /reserved-memory/, unless it's already there. * * We update region->node to the new copy here, as the prd code may * update regions' device-tree nodes, and we want those updates to * apply to the nodes in /reserved-memory/. */ if (region->type == REGION_HW_RESERVED && region->node) { if (region->node->parent != parent) region->node = dt_copy(region->node, parent); return; } name = strdup(region->name); assert(name); /* remove any cell addresses in the region name; we have our own cell * addresses here */ p = strchr(name, '@'); if (p) *p = '\0'; region->node = dt_new_addr(parent, name, region->start); assert(region->node); dt_add_property_u64s(region->node, "reg", region->start, region->len); free(name); } void mem_region_add_dt_reserved(void) { int names_len, ranges_len, len; const struct dt_property *prop; struct mem_region *region; void *names, *ranges; struct dt_node *node; uint64_t *range; char *name; names_len = 0; ranges_len = 0; /* Finalise the region list, so we know that the regions list won't be * altered after this point. The regions' free lists may change after * we drop the lock, but we don't access those. */ lock(&mem_region_lock); mem_regions_finalised = true; /* establish top-level reservation node */ node = dt_find_by_path(dt_root, "reserved-memory"); if (!node) { node = dt_new(dt_root, "reserved-memory"); dt_add_property_cells(node, "#address-cells", 2); dt_add_property_cells(node, "#size-cells", 2); dt_add_property(node, "ranges", NULL, 0); } /* First pass: calculate length of property data */ list_for_each(®ions, region, list) { if (!region_is_reservable(region)) continue; names_len += strlen(region->name) + 1; ranges_len += 2 * sizeof(uint64_t); } name = names = malloc(names_len); range = ranges = malloc(ranges_len); printf("Reserved regions:\n"); /* Second pass: populate property data */ list_for_each(®ions, region, list) { if (!region_is_reservable(region)) continue; len = strlen(region->name) + 1; memcpy(name, region->name, len); name += len; printf(" 0x%012llx..%012llx : %s\n", (long long)region->start, (long long)(region->start + region->len - 1), region->name); mem_region_add_dt_reserved_node(node, region); range[0] = cpu_to_fdt64(region->start); range[1] = cpu_to_fdt64(region->len); range += 2; } unlock(&mem_region_lock); prop = dt_find_property(dt_root, "reserved-names"); if (prop) dt_del_property(dt_root, (struct dt_property *)prop); prop = dt_find_property(dt_root, "reserved-ranges"); if (prop) dt_del_property(dt_root, (struct dt_property *)prop); dt_add_property(dt_root, "reserved-names", names, names_len); dt_add_property(dt_root, "reserved-ranges", ranges, ranges_len); free(names); free(ranges); } struct mem_region *mem_region_next(struct mem_region *region) { struct list_node *node; assert(lock_held_by_me(&mem_region_lock)); node = region ? ®ion->list : ®ions.n; if (node->next == ®ions.n) return NULL; return list_entry(node->next, struct mem_region, list); } skiboot-skiboot-5.1.13/core/nvram-format.c000066400000000000000000000075451265204436200204470ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include /* * NVRAM Format as specified in PAPR */ struct chrp_nvram_hdr { uint8_t sig; uint8_t cksum; uint16_t len; char name[12]; }; #define NVRAM_SIG_FW_PRIV 0x51 #define NVRAM_SIG_SYSTEM 0x70 #define NVRAM_SIG_FREE 0x7f #define NVRAM_NAME_COMMON "common" #define NVRAM_NAME_FW_PRIV "ibm,skiboot" #define NVRAM_NAME_FREE "wwwwwwwwwwww" /* 64k should be enough, famous last words... */ #define NVRAM_SIZE_COMMON 0x10000 /* 4k should be enough, famous last words... */ #define NVRAM_SIZE_FW_PRIV 0x1000 static uint8_t chrp_nv_cksum(struct chrp_nvram_hdr *hdr) { struct chrp_nvram_hdr h_copy = *hdr; uint8_t b_data, i_sum, c_sum; uint8_t *p = (uint8_t *)&h_copy; unsigned int nbytes = sizeof(h_copy); h_copy.cksum = 0; for (c_sum = 0; nbytes; nbytes--) { b_data = *(p++); i_sum = c_sum + b_data; if (i_sum < c_sum) i_sum++; c_sum = i_sum; } return c_sum; } int nvram_format(void *nvram_image, uint32_t nvram_size) { struct chrp_nvram_hdr *h; unsigned int offset = 0; prerror("NVRAM: Re-initializing\n"); memset(nvram_image, 0, nvram_size); /* Create private partition */ if (nvram_size - offset < NVRAM_SIZE_FW_PRIV) return -1; h = nvram_image + offset; h->sig = NVRAM_SIG_FW_PRIV; h->len = NVRAM_SIZE_FW_PRIV >> 4; strcpy(h->name, NVRAM_NAME_FW_PRIV); h->cksum = chrp_nv_cksum(h); offset += NVRAM_SIZE_FW_PRIV; /* Create common partition */ if (nvram_size - offset < NVRAM_SIZE_COMMON) return -1; h = nvram_image + offset; h->sig = NVRAM_SIG_SYSTEM; h->len = NVRAM_SIZE_COMMON >> 4; strcpy(h->name, NVRAM_NAME_COMMON); h->cksum = chrp_nv_cksum(h); offset += NVRAM_SIZE_COMMON; /* Create free space partition */ if (nvram_size - offset < sizeof(struct chrp_nvram_hdr)) return -1; h = nvram_image + offset; h->sig = NVRAM_SIG_FREE; h->len = (nvram_size - offset) >> 4; /* We have the full 12 bytes here */ memcpy(h->name, NVRAM_NAME_FREE, 12); h->cksum = chrp_nv_cksum(h); return 0; } /* * Check that the nvram partition layout is sane and that it * contains our required partitions. If not, we re-format the * lot of it */ int nvram_check(void *nvram_image, const uint32_t nvram_size) { unsigned int offset = 0; bool found_common = false; bool found_skiboot = false; while (offset + sizeof(struct chrp_nvram_hdr) < nvram_size) { struct chrp_nvram_hdr *h = nvram_image + offset; if (chrp_nv_cksum(h) != h->cksum) { prerror("NVRAM: Partition at offset 0x%x" " has bad checksum\n", offset); goto failed; } if (h->len < 1) { prerror("NVRAM: Partition at offset 0x%x" " has incorrect 0 length\n", offset); goto failed; } if (h->sig == NVRAM_SIG_SYSTEM && strcmp(h->name, NVRAM_NAME_COMMON) == 0) found_common = true; if (h->sig == NVRAM_SIG_FW_PRIV && strcmp(h->name, NVRAM_NAME_FW_PRIV) == 0) found_skiboot = true; offset += h->len << 4; if (offset > nvram_size) { prerror("NVRAM: Partition at offset 0x%x" " extends beyond end of nvram !\n", offset); goto failed; } } if (!found_common) { prerror("NVRAM: Common partition not found !\n"); goto failed; } if (!found_skiboot) { prerror("NVRAM: Skiboot private partition " "not found !\n"); goto failed; } prerror("NVRAM: Layout appears sane\n"); return 0; failed: return -1; } skiboot-skiboot-5.1.13/core/nvram.c000066400000000000000000000060721265204436200171530ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include static void *nvram_image; static uint32_t nvram_size; static bool nvram_ready; static int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset) { if (!nvram_ready) return OPAL_HARDWARE; if (offset >= nvram_size || (offset + size) > nvram_size) return OPAL_PARAMETER; memcpy((void *)buffer, nvram_image + offset, size); return OPAL_SUCCESS; } opal_call(OPAL_READ_NVRAM, opal_read_nvram, 3); static int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset) { if (!nvram_ready) return OPAL_HARDWARE; if (offset >= nvram_size || (offset + size) > nvram_size) return OPAL_PARAMETER; memcpy(nvram_image + offset, (void *)buffer, size); if (platform.nvram_write) platform.nvram_write(offset, nvram_image + offset, size); return OPAL_SUCCESS; } opal_call(OPAL_WRITE_NVRAM, opal_write_nvram, 3); void nvram_read_complete(bool success) { struct dt_node *np; /* Read not successful, error out and free the buffer */ if (!success) { free(nvram_image); nvram_size = 0; return; } /* Check and maybe format nvram */ if (nvram_check(nvram_image, nvram_size)) { nvram_format(nvram_image, nvram_size); /* Write the whole thing back */ if (platform.nvram_write) platform.nvram_write(0, nvram_image, nvram_size); } /* Add nvram node */ np = dt_new(opal_node, "nvram"); dt_add_property_cells(np, "#bytes", nvram_size); dt_add_property_string(np, "compatible", "ibm,opal-nvram"); /* Mark ready */ nvram_ready = true; } void nvram_init(void) { int rc; if (!platform.nvram_info) return; rc = platform.nvram_info(&nvram_size); if (rc) { prerror("NVRAM: Error %d retrieving nvram info\n", rc); return; } printf("NVRAM: Size is %d KB\n", nvram_size >> 10); if (nvram_size > 0x100000) { printf("NVRAM: Cropping to 1MB !\n"); nvram_size = 0x100000; } /* * We allocate the nvram image with 4k alignment to make the * FSP backend job's easier */ nvram_image = memalign(0x1000, nvram_size); if (!nvram_image) { prerror("NVRAM: Failed to allocate nvram image\n"); nvram_size = 0; return; } /* Read it in */ rc = platform.nvram_start_read(nvram_image, 0, nvram_size); if (rc) { prerror("NVRAM: Failed to read NVRAM from FSP !\n"); nvram_size = 0; free(nvram_image); return; } /* * We'll get called back later (or recursively from * nvram_start_read) in nvram_read_complete() */ } skiboot-skiboot-5.1.13/core/opal-msg.c000066400000000000000000000100331265204436200175370ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define pr_fmt(fmt) "opalmsg: " fmt #include #include #include #include #define OPAL_MAX_MSGS (OPAL_MSG_TYPE_MAX + OPAL_MAX_ASYNC_COMP - 1) struct opal_msg_entry { struct list_node link; void (*consumed)(void *data); void *data; struct opal_msg msg; }; static LIST_HEAD(msg_free_list); static LIST_HEAD(msg_pending_list); static struct lock opal_msg_lock = LOCK_UNLOCKED; int _opal_queue_msg(enum opal_msg_type msg_type, void *data, void (*consumed)(void *data), size_t num_params, const u64 *params) { struct opal_msg_entry *entry; lock(&opal_msg_lock); entry = list_pop(&msg_free_list, struct opal_msg_entry, link); if (!entry) { prerror("No available node in the free list, allocating\n"); entry = zalloc(sizeof(struct opal_msg_entry)); if (!entry) { prerror("Allocation failed\n"); unlock(&opal_msg_lock); return OPAL_RESOURCE; } } entry->consumed = consumed; entry->data = data; entry->msg.msg_type = msg_type; if (num_params > ARRAY_SIZE(entry->msg.params)) { prerror("Discarding extra parameters\n"); num_params = ARRAY_SIZE(entry->msg.params); } memcpy(entry->msg.params, params, num_params*sizeof(u64)); list_add_tail(&msg_pending_list, &entry->link); opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, OPAL_EVENT_MSG_PENDING); unlock(&opal_msg_lock); return 0; } static int64_t opal_get_msg(uint64_t *buffer, uint64_t size) { struct opal_msg_entry *entry; void (*callback)(void *data); void *data; if (size < sizeof(struct opal_msg) || !buffer) return OPAL_PARAMETER; lock(&opal_msg_lock); entry = list_pop(&msg_pending_list, struct opal_msg_entry, link); if (!entry) { unlock(&opal_msg_lock); return OPAL_RESOURCE; } memcpy(buffer, &entry->msg, sizeof(entry->msg)); callback = entry->consumed; data = entry->data; list_add(&msg_free_list, &entry->link); if (list_empty(&msg_pending_list)) opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, 0); unlock(&opal_msg_lock); if (callback) callback(data); return OPAL_SUCCESS; } opal_call(OPAL_GET_MSG, opal_get_msg, 2); static int64_t opal_check_completion(uint64_t *buffer, uint64_t size, uint64_t token) { struct opal_msg_entry *entry, *next_entry; void (*callback)(void *data) = NULL; int rc = OPAL_BUSY; void *data = NULL; lock(&opal_msg_lock); list_for_each_safe(&msg_pending_list, entry, next_entry, link) { if (entry->msg.msg_type == OPAL_MSG_ASYNC_COMP && entry->msg.params[0] == token) { list_del(&entry->link); callback = entry->consumed; data = entry->data; list_add(&msg_free_list, &entry->link); if (list_empty(&msg_pending_list)) opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, 0); rc = OPAL_SUCCESS; break; } } if (rc == OPAL_SUCCESS && size >= sizeof(struct opal_msg)) memcpy(buffer, &entry->msg, sizeof(entry->msg)); unlock(&opal_msg_lock); if (callback) callback(data); return rc; } opal_call(OPAL_CHECK_ASYNC_COMPLETION, opal_check_completion, 3); void opal_init_msg(void) { struct opal_msg_entry *entry; int i; for (i = 0; i < OPAL_MAX_MSGS; i++, entry++) { entry = zalloc(sizeof(*entry)); if (!entry) goto err; list_add_tail(&msg_free_list, &entry->link); } return; err: for (; i > 0; i--) { entry = list_pop(&msg_free_list, struct opal_msg_entry, link); if (entry) free(entry); } } skiboot-skiboot-5.1.13/core/opal.c000066400000000000000000000242471265204436200167670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Pending events to signal via opal_poll_events */ uint64_t opal_pending_events; /* OPAL dispatch table defined in head.S */ extern uint64_t opal_branch_table[]; /* Number of args expected for each call. */ static u8 opal_num_args[OPAL_LAST+1]; /* OPAL anchor node */ struct dt_node *opal_node; /* mask of dynamic vs fixed events; opal_allocate_dynamic_event will * only allocate from this range */ static const uint64_t opal_dynamic_events_mask = 0xffffffff00000000ul; static uint64_t opal_dynamic_events; extern uint32_t attn_trigger; extern uint32_t hir_trigger; void opal_table_init(void) { struct opal_table_entry *s = __opal_table_start; struct opal_table_entry *e = __opal_table_end; printf("OPAL table: %p .. %p, branch table: %p\n", s, e, opal_branch_table); while(s < e) { uint64_t *func = s->func; opal_branch_table[s->token] = *func; opal_num_args[s->token] = s->nargs; s++; } } /* Called from head.S, thus no prototype */ long opal_bad_token(uint64_t token); long opal_bad_token(uint64_t token) { prerror("OPAL: Called with bad token %lld !\n", token); return OPAL_PARAMETER; } /* Called from head.S, thus no prototype */ void opal_trace_entry(struct stack_frame *eframe); void opal_trace_entry(struct stack_frame *eframe) { union trace t; unsigned nargs; if (this_cpu()->pir != mfspr(SPR_PIR)) { printf("CPU MISMATCH ! PIR=%04lx cpu @%p -> pir=%04x\n", mfspr(SPR_PIR), this_cpu(), this_cpu()->pir); abort(); } if (eframe->gpr[0] > OPAL_LAST) nargs = 0; else nargs = opal_num_args[eframe->gpr[0]]; t.opal.token = eframe->gpr[0]; t.opal.lr = eframe->lr; t.opal.sp = eframe->gpr[1]; memcpy(t.opal.r3_to_11, &eframe->gpr[3], nargs*sizeof(u64)); trace_add(&t, TRACE_OPAL, offsetof(struct trace_opal, r3_to_11[nargs])); } void __opal_register(uint64_t token, void *func, unsigned int nargs) { uint64_t *opd = func; assert(token <= OPAL_LAST); opal_branch_table[token] = *opd; opal_num_args[token] = nargs; } static void add_opal_firmware_node(void) { struct dt_node *firmware = dt_new(opal_node, "firmware"); uint64_t sym_start = (uint64_t)__sym_map_start; uint64_t sym_size = (uint64_t)__sym_map_end - sym_start; dt_add_property_string(firmware, "compatible", "ibm,opal-firmware"); dt_add_property_string(firmware, "name", "firmware"); dt_add_property_string(firmware, "version", version); dt_add_property_cells(firmware, "symbol-map", hi32(sym_start), lo32(sym_start), hi32(sym_size), lo32(sym_size)); } void add_opal_node(void) { uint64_t base, entry, size; extern uint32_t opal_entry; /* XXX TODO: Reorg this. We should create the base OPAL * node early on, and have the various sub modules populate * their own entries (console etc...) * * The logic of which console backend to use should be * extracted */ entry = (uint64_t)&opal_entry; base = SKIBOOT_BASE; size = (CPU_STACKS_BASE + (uint64_t)(cpu_max_pir + 1) * STACK_SIZE) - SKIBOOT_BASE; if (!opal_node) { opal_node = dt_new(dt_root, "ibm,opal"); assert(opal_node); } dt_add_property_cells(opal_node, "#address-cells", 0); dt_add_property_cells(opal_node, "#size-cells", 0); dt_add_property_strings(opal_node, "compatible", "ibm,opal-v2", "ibm,opal-v3"); dt_add_property_cells(opal_node, "opal-msg-async-num", OPAL_MAX_ASYNC_COMP); dt_add_property_cells(opal_node, "opal-msg-size", sizeof(struct opal_msg)); dt_add_property_u64(opal_node, "opal-base-address", base); dt_add_property_u64(opal_node, "opal-entry-address", entry); dt_add_property_u64(opal_node, "opal-runtime-size", size); add_opal_firmware_node(); add_associativity_ref_point(); memcons_add_properties(); } static struct lock evt_lock = LOCK_UNLOCKED; void opal_update_pending_evt(uint64_t evt_mask, uint64_t evt_values) { uint64_t new_evts; lock(&evt_lock); new_evts = (opal_pending_events & ~evt_mask) | evt_values; if (opal_pending_events != new_evts) { uint64_t tok; #ifdef OPAL_TRACE_EVT_CHG printf("OPAL: Evt change: 0x%016llx -> 0x%016llx\n", opal_pending_events, new_evts); #endif /* * If an event gets *set* while we are in a different call chain * than opal_handle_interrupt() or opal_handle_hmi(), then we * artificially generate an interrupt (OCC interrupt specifically) * to ensure that Linux properly broadcast the event change internally */ if ((new_evts & ~opal_pending_events) != 0) { tok = this_cpu()->current_token; if (tok != OPAL_HANDLE_INTERRUPT && tok != OPAL_HANDLE_HMI) occ_send_dummy_interrupt(); } opal_pending_events = new_evts; } unlock(&evt_lock); } uint64_t opal_dynamic_event_alloc(void) { uint64_t new_event; int n; lock(&evt_lock); /* Create the event mask. This set-bit will be within the event mask * iff there are free events, or out of the mask if there are no free * events. If opal_dynamic_events is all ones (ie, all events are * dynamic, and allocated), then ilog2 will return -1, and we'll have a * zero mask. */ n = ilog2(~opal_dynamic_events); new_event = 1ull << n; /* Ensure we're still within the allocatable dynamic events range */ if (new_event & opal_dynamic_events_mask) opal_dynamic_events |= new_event; else new_event = 0; unlock(&evt_lock); return new_event; } void opal_dynamic_event_free(uint64_t event) { lock(&evt_lock); opal_dynamic_events &= ~event; unlock(&evt_lock); } static uint64_t opal_test_func(uint64_t arg) { printf("OPAL: Test function called with arg 0x%llx\n", arg); return 0xfeedf00d; } opal_call(OPAL_TEST, opal_test_func, 1); struct opal_poll_entry { struct list_node link; void (*poller)(void *data); void *data; }; static struct list_head opal_pollers = LIST_HEAD_INIT(opal_pollers); static struct lock opal_poll_lock = LOCK_UNLOCKED; void opal_add_poller(void (*poller)(void *data), void *data) { struct opal_poll_entry *ent; ent = zalloc(sizeof(struct opal_poll_entry)); assert(ent); ent->poller = poller; ent->data = data; lock(&opal_poll_lock); list_add_tail(&opal_pollers, &ent->link); unlock(&opal_poll_lock); } void opal_del_poller(void (*poller)(void *data)) { struct opal_poll_entry *ent; /* XXX This is currently unused. To solve various "interesting" * locking issues, the pollers are run locklessly, so if we were * to free them, we would have to be careful, using something * akin to RCU to synchronize with other OPAL entries. For now * if anybody uses it, print a warning and leak the entry, don't * free it. */ prlog(PR_ALERT, "WARNING: Unsupported opal_del_poller." " Interesting locking issues, don't call this.\n"); lock(&opal_poll_lock); list_for_each(&opal_pollers, ent, link) { if (ent->poller == poller) { list_del(&ent->link); /* free(ent); */ break; } } unlock(&opal_poll_lock); } void opal_run_pollers(void) { struct opal_poll_entry *poll_ent; static int pollers_with_lock_warnings = 0; /* Don't re-enter on this CPU */ if (this_cpu()->in_poller) { prlog(PR_ERR, "OPAL: Poller recursion detected.\n"); backtrace(); return; } this_cpu()->in_poller = true; if (this_cpu()->lock_depth && pollers_with_lock_warnings < 64) { prlog(PR_ERR, "Running pollers with lock held !\n"); backtrace(); pollers_with_lock_warnings++; if (pollers_with_lock_warnings == 64) prlog(PR_ERR, "opal_run_pollers with lock run 64 " "times, disabling warning.\n"); } /* We run the timers first */ check_timers(false); /* The pollers are run lokelessly, see comment in opal_del_poller */ list_for_each(&opal_pollers, poll_ent, link) poll_ent->poller(poll_ent->data); /* Disable poller flag */ this_cpu()->in_poller = false; /* On debug builds, print max stack usage */ check_stacks(); } static int64_t opal_poll_events(uint64_t *outstanding_event_mask) { /* Check if we need to trigger an attn for test use */ if (attn_trigger == 0xdeadbeef) { prlog(PR_EMERG, "Triggering attn\n"); assert(false); } /* Test the host initiated reset */ if (hir_trigger == 0xdeadbeef) { fsp_trigger_reset(); hir_trigger = 0; } opal_run_pollers(); if (outstanding_event_mask) *outstanding_event_mask = opal_pending_events; return OPAL_SUCCESS; } opal_call(OPAL_POLL_EVENTS, opal_poll_events, 1); static int64_t opal_check_token(uint64_t token) { if (token > OPAL_LAST) return OPAL_TOKEN_ABSENT; if (opal_branch_table[token]) return OPAL_TOKEN_PRESENT; return OPAL_TOKEN_ABSENT; } opal_call(OPAL_CHECK_TOKEN, opal_check_token, 1); struct opal_sync_entry { struct list_node link; bool (*notify)(void *data); void *data; }; static struct list_head opal_syncers = LIST_HEAD_INIT(opal_syncers); void opal_add_host_sync_notifier(bool (*notify)(void *data), void *data) { struct opal_sync_entry *ent; ent = zalloc(sizeof(struct opal_sync_entry)); assert(ent); ent->notify = notify; ent->data = data; list_add_tail(&opal_syncers, &ent->link); } void opal_del_host_sync_notifier(bool (*notify)(void *data)) { struct opal_sync_entry *ent; list_for_each(&opal_syncers, ent, link) { if (ent->notify == notify) { list_del(&ent->link); free(ent); return; } } } /* * OPAL call to handle host kexec'ing scenario */ static int64_t opal_sync_host_reboot(void) { struct opal_sync_entry *ent; bool ret = true; list_for_each(&opal_syncers, ent, link) ret &= ent->notify(ent->data); if (ret) return OPAL_SUCCESS; else return OPAL_BUSY_EVENT; } opal_call(OPAL_SYNC_HOST_REBOOT, opal_sync_host_reboot, 0); skiboot-skiboot-5.1.13/core/pci-opal.c000066400000000000000000000445071265204436200175410ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define OPAL_PCICFG_ACCESS(op, cb, type) \ static int64_t opal_pci_config_##op(uint64_t phb_id, \ uint64_t bus_dev_func, \ uint64_t offset, type data) \ { \ struct phb *phb = pci_get_phb(phb_id); \ int64_t rc; \ \ if (!phb) \ return OPAL_PARAMETER; \ phb->ops->lock(phb); \ rc = phb->ops->cfg_##cb(phb, bus_dev_func, offset, data); \ phb->ops->unlock(phb); \ pci_put_phb(phb); \ \ return rc; \ } OPAL_PCICFG_ACCESS(read_byte, read8, uint8_t *) OPAL_PCICFG_ACCESS(read_half_word, read16, uint16_t *) OPAL_PCICFG_ACCESS(read_word, read32, uint32_t *) OPAL_PCICFG_ACCESS(write_byte, write8, uint8_t) OPAL_PCICFG_ACCESS(write_half_word, write16, uint16_t) OPAL_PCICFG_ACCESS(write_word, write32, uint32_t) opal_call(OPAL_PCI_CONFIG_READ_BYTE, opal_pci_config_read_byte, 4); opal_call(OPAL_PCI_CONFIG_READ_HALF_WORD, opal_pci_config_read_half_word, 4); opal_call(OPAL_PCI_CONFIG_READ_WORD, opal_pci_config_read_word, 4); opal_call(OPAL_PCI_CONFIG_WRITE_BYTE, opal_pci_config_write_byte, 4); opal_call(OPAL_PCI_CONFIG_WRITE_HALF_WORD, opal_pci_config_write_half_word, 4); opal_call(OPAL_PCI_CONFIG_WRITE_WORD, opal_pci_config_write_word, 4); static struct lock opal_eeh_evt_lock = LOCK_UNLOCKED; static uint64_t opal_eeh_evt = 0; void opal_pci_eeh_set_evt(uint64_t phb_id) { lock(&opal_eeh_evt_lock); opal_eeh_evt |= 1ULL << phb_id; opal_update_pending_evt(OPAL_EVENT_PCI_ERROR, OPAL_EVENT_PCI_ERROR); unlock(&opal_eeh_evt_lock); } void opal_pci_eeh_clear_evt(uint64_t phb_id) { lock(&opal_eeh_evt_lock); opal_eeh_evt &= ~(1ULL << phb_id); if (!opal_eeh_evt) opal_update_pending_evt(OPAL_EVENT_PCI_ERROR, 0); unlock(&opal_eeh_evt_lock); } static int64_t opal_pci_eeh_freeze_status(uint64_t phb_id, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint64_t *phb_status) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->eeh_freeze_status) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->eeh_freeze_status(phb, pe_number, freeze_state, pci_error_type, NULL, phb_status); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_EEH_FREEZE_STATUS, opal_pci_eeh_freeze_status, 5); static int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number, uint64_t eeh_action_token) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->eeh_freeze_clear) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->eeh_freeze_clear(phb, pe_number, eeh_action_token); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_EEH_FREEZE_CLEAR, opal_pci_eeh_freeze_clear, 3); static int64_t opal_pci_eeh_freeze_set(uint64_t phb_id, uint64_t pe_number, uint64_t eeh_action_token) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->eeh_freeze_set) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->eeh_freeze_set(phb, pe_number, eeh_action_token); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_EEH_FREEZE_SET, opal_pci_eeh_freeze_set, 3); static int64_t opal_pci_err_inject(uint64_t phb_id, uint32_t pe_no, uint32_t type, uint32_t func, uint64_t addr, uint64_t mask) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops || !phb->ops->err_inject) return OPAL_UNSUPPORTED; if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR && type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) return OPAL_PARAMETER; phb->ops->lock(phb); rc = phb->ops->err_inject(phb, pe_no, type, func, addr, mask); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_ERR_INJECT, opal_pci_err_inject, 6); static int64_t opal_pci_phb_mmio_enable(uint64_t phb_id, uint16_t window_type, uint16_t window_num, uint16_t enable) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->phb_mmio_enable) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->phb_mmio_enable(phb, window_type, window_num, enable); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_PHB_MMIO_ENABLE, opal_pci_phb_mmio_enable, 4); static int64_t opal_pci_set_phb_mem_window(uint64_t phb_id, uint16_t window_type, uint16_t window_num, uint64_t addr, uint64_t pci_addr, uint64_t size) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_phb_mem_window) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_phb_mem_window(phb, window_type, window_num, addr, pci_addr, size); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_PHB_MEM_WINDOW, opal_pci_set_phb_mem_window, 6); static int64_t opal_pci_map_pe_mmio_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_type, uint16_t window_num, uint16_t segment_num) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->map_pe_mmio_window) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->map_pe_mmio_window(phb, pe_number, window_type, window_num, segment_num); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_MAP_PE_MMIO_WINDOW, opal_pci_map_pe_mmio_window, 5); static int64_t opal_pci_set_phb_table_memory(uint64_t phb_id __unused, uint64_t rtt_addr __unused, uint64_t ivt_addr __unused, uint64_t ivt_len __unused, uint64_t rej_array_addr __unused, uint64_t peltv_addr __unused) { /* IODA2 (P8) stuff, TODO */ return OPAL_UNSUPPORTED; } opal_call(OPAL_PCI_SET_PHB_TABLE_MEMORY, opal_pci_set_phb_table_memory, 6); static int64_t opal_pci_set_pe(uint64_t phb_id, uint64_t pe_number, uint64_t bus_dev_func, uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, uint8_t pe_action) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_pe) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_pe(phb, pe_number, bus_dev_func, bus_compare, dev_compare, func_compare, pe_action); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_PE, opal_pci_set_pe, 7); static int64_t opal_pci_set_peltv(uint64_t phb_id, uint32_t parent_pe, uint32_t child_pe, uint8_t state) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_peltv) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_peltv(phb, parent_pe, child_pe, state); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_PELTV, opal_pci_set_peltv, 4); static int64_t opal_pci_set_mve(uint64_t phb_id, uint32_t mve_number, uint32_t pe_number) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_mve) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_mve(phb, mve_number, pe_number); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_MVE, opal_pci_set_mve, 3); static int64_t opal_pci_set_mve_enable(uint64_t phb_id, uint32_t mve_number, uint32_t state) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_mve_enable) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_mve_enable(phb, mve_number, state); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_MVE_ENABLE, opal_pci_set_mve_enable, 3); static int64_t opal_pci_get_xive_reissue(uint64_t phb_id __unused, uint32_t xive_number __unused, uint8_t *p_bit __unused, uint8_t *q_bit __unused) { /* IODA2 (P8) stuff, TODO */ return OPAL_UNSUPPORTED; } opal_call(OPAL_PCI_GET_XIVE_REISSUE, opal_pci_get_xive_reissue, 4); static int64_t opal_pci_set_xive_reissue(uint64_t phb_id __unused, uint32_t xive_number __unused, uint8_t p_bit __unused, uint8_t q_bit __unused) { /* IODA2 (P8) stuff, TODO */ return OPAL_UNSUPPORTED; } opal_call(OPAL_PCI_SET_XIVE_REISSUE, opal_pci_set_xive_reissue, 4); static int64_t opal_pci_msi_eoi(uint64_t phb_id, uint32_t hwirq) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->pci_msi_eoi) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->pci_msi_eoi(phb, hwirq); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_MSI_EOI, opal_pci_msi_eoi, 2); static int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number, uint32_t xive_num) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_xive_pe) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_xive_pe(phb, pe_number, xive_num); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_XIVE_PE, opal_pci_set_xive_pe, 3); static int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num, int32_t *interrupt_source_number) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->get_xive_source) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->get_xive_source(phb, xive_num, interrupt_source_number); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_GET_XIVE_SOURCE, opal_get_xive_source, 3); static int64_t opal_get_msi_32(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint32_t *msi_address, uint32_t *message_data) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->get_msi_32) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->get_msi_32(phb, mve_number, xive_num, msi_range, msi_address, message_data); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_GET_MSI_32, opal_get_msi_32, 6); static int64_t opal_get_msi_64(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint64_t *msi_address, uint32_t *message_data) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->get_msi_64) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->get_msi_64(phb, mve_number, xive_num, msi_range, msi_address, message_data); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_GET_MSI_64, opal_get_msi_64, 6); static int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_id, uint16_t tce_levels, uint64_t tce_table_addr, uint64_t tce_table_size, uint64_t tce_page_size) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->map_pe_dma_window) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->map_pe_dma_window(phb, pe_number, window_id, tce_levels, tce_table_addr, tce_table_size, tce_page_size); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW, opal_pci_map_pe_dma_window, 7); static int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id, uint16_t pe_number, uint16_t window_id, uint64_t pci_start_addr, uint64_t pci_mem_size) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->map_pe_dma_window_real) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->map_pe_dma_window_real(phb, pe_number, window_id, pci_start_addr, pci_mem_size); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW_REAL, opal_pci_map_pe_dma_window_real, 5); static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope, uint8_t assert_state) { struct phb *phb = pci_get_phb(phb_id); int64_t rc = OPAL_SUCCESS; if (!phb) return OPAL_PARAMETER; if (!phb->ops) return OPAL_UNSUPPORTED; if (assert_state != OPAL_ASSERT_RESET && assert_state != OPAL_DEASSERT_RESET) return OPAL_PARAMETER; phb->ops->lock(phb); switch(reset_scope) { case OPAL_RESET_PHB_COMPLETE: if (!phb->ops->complete_reset) { rc = OPAL_UNSUPPORTED; break; } rc = phb->ops->complete_reset(phb, assert_state); if (rc < 0) prerror("PHB#%d: Failure on complete reset, rc=%lld\n", phb->opal_id, rc); break; case OPAL_RESET_PCI_FUNDAMENTAL: if (!phb->ops->fundamental_reset) { rc = OPAL_UNSUPPORTED; break; } /* We need do nothing on deassert time */ if (assert_state != OPAL_ASSERT_RESET) break; rc = phb->ops->fundamental_reset(phb); if (rc < 0) prerror("PHB#%d: Failure on fundamental reset, rc=%lld\n", phb->opal_id, rc); break; case OPAL_RESET_PCI_HOT: if (!phb->ops->hot_reset) { rc = OPAL_UNSUPPORTED; break; } /* We need do nothing on deassert time */ if (assert_state != OPAL_ASSERT_RESET) break; rc = phb->ops->hot_reset(phb); if (rc < 0) prerror("PHB#%d: Failure on hot reset, rc=%lld\n", phb->opal_id, rc); break; case OPAL_RESET_PCI_IODA_TABLE: if (assert_state != OPAL_ASSERT_RESET) break; if (phb->ops->ioda_reset) phb->ops->ioda_reset(phb, true); break; case OPAL_RESET_PHB_ERROR: if (assert_state != OPAL_ASSERT_RESET) break; if (phb->ops->papr_errinjct_reset) phb->ops->papr_errinjct_reset(phb); break; default: rc = OPAL_UNSUPPORTED; } phb->ops->unlock(phb); pci_put_phb(phb); return (rc > 0) ? tb_to_msecs(rc) : rc; } opal_call(OPAL_PCI_RESET, opal_pci_reset, 3); static int64_t opal_pci_reinit(uint64_t phb_id, uint64_t reinit_scope, uint64_t data) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops || !phb->ops->pci_reinit) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->pci_reinit(phb, reinit_scope, data); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_REINIT, opal_pci_reinit, 3); static int64_t opal_pci_poll(uint64_t phb_id) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops || !phb->ops->poll) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->poll(phb); phb->ops->unlock(phb); pci_put_phb(phb); /* Return milliseconds for caller to sleep: round up */ if (rc > 0) { rc = tb_to_msecs(rc); if (rc == 0) rc = 1; } return rc; } opal_call(OPAL_PCI_POLL, opal_pci_poll, 1); static int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id, uint64_t tce_mem_addr, uint64_t tce_mem_size) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_phb_tce_memory) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_phb_tce_memory(phb, tce_mem_addr, tce_mem_size); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_SET_PHB_TCE_MEMORY, opal_pci_set_phb_tce_memory, 3); static int64_t opal_pci_get_phb_diag_data(uint64_t phb_id, void *diag_buffer, uint64_t diag_buffer_len) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->get_diag_data) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->get_diag_data(phb, diag_buffer, diag_buffer_len); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_GET_PHB_DIAG_DATA, opal_pci_get_phb_diag_data, 3); static int64_t opal_pci_get_phb_diag_data2(uint64_t phb_id, void *diag_buffer, uint64_t diag_buffer_len) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->get_diag_data2) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->get_diag_data2(phb, diag_buffer, diag_buffer_len); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_GET_PHB_DIAG_DATA2, opal_pci_get_phb_diag_data2, 3); static int64_t opal_pci_next_error(uint64_t phb_id, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->next_error) return OPAL_UNSUPPORTED; phb->ops->lock(phb); opal_pci_eeh_clear_evt(phb_id); rc = phb->ops->next_error(phb, first_frozen_pe, pci_error_type, severity); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_NEXT_ERROR, opal_pci_next_error, 4); static int64_t opal_pci_eeh_freeze_status2(uint64_t phb_id, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint16_t *severity, uint64_t *phb_status) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->eeh_freeze_status) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->eeh_freeze_status(phb, pe_number, freeze_state, pci_error_type, severity, phb_status); phb->ops->unlock(phb); pci_put_phb(phb); return rc; } opal_call(OPAL_PCI_EEH_FREEZE_STATUS2, opal_pci_eeh_freeze_status2, 6); static int64_t opal_pci_set_phb_capi_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number) { struct phb *phb = pci_get_phb(phb_id); int64_t rc; if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_capi_mode) return OPAL_UNSUPPORTED; phb->ops->lock(phb); rc = phb->ops->set_capi_mode(phb, mode, pe_number); phb->ops->unlock(phb); return rc; } opal_call(OPAL_PCI_SET_PHB_CAPI_MODE, opal_pci_set_phb_capi_mode, 3); skiboot-skiboot-5.1.13/core/pci.c000066400000000000000000001243341265204436200166050ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include /* The eeh event code will need updating if this is ever increased to * support more than 64 phbs */ static struct phb *phbs[64]; #define PCITRACE(_p, _bdfn, fmt, a...) \ prlog(PR_TRACE, "PHB%d:%02x:%02x.%x " fmt, \ (_p)->opal_id, \ ((_bdfn) >> 8) & 0xff, \ ((_bdfn) >> 3) & 0x1f, (_bdfn) & 0x7, ## a) #define PCIDBG(_p, _bdfn, fmt, a...) \ prlog(PR_DEBUG, "PHB%d:%02x:%02x.%x " fmt, \ (_p)->opal_id, \ ((_bdfn) >> 8) & 0xff, \ ((_bdfn) >> 3) & 0x1f, (_bdfn) & 0x7, ## a) #define PCINOTICE(_p, _bdfn, fmt, a...) \ prlog(PR_NOTICE, "PHB%d:%02x:%02x.%x " fmt, \ (_p)->opal_id, \ ((_bdfn) >> 8) & 0xff, \ ((_bdfn) >> 3) & 0x1f, (_bdfn) & 0x7, ## a) #define PCIERR(_p, _bdfn, fmt, a...) \ prlog(PR_ERR, "PHB%d:%02x:%02x.%x " fmt, \ (_p)->opal_id, \ ((_bdfn) >> 8) & 0xff, \ ((_bdfn) >> 3) & 0x1f, (_bdfn) & 0x7, ## a) /* * Generic PCI utilities */ static int64_t __pci_find_cap(struct phb *phb, uint16_t bdfn, uint8_t want, bool check_cap_indicator) { int64_t rc; uint16_t stat, cap; uint8_t pos, next; rc = pci_cfg_read16(phb, bdfn, PCI_CFG_STAT, &stat); if (rc) return rc; if (check_cap_indicator && !(stat & PCI_CFG_STAT_CAP)) return OPAL_UNSUPPORTED; rc = pci_cfg_read8(phb, bdfn, PCI_CFG_CAP, &pos); if (rc) return rc; pos &= 0xfc; while(pos) { rc = pci_cfg_read16(phb, bdfn, pos, &cap); if (rc) return rc; if ((cap & 0xff) == want) return pos; next = (cap >> 8) & 0xfc; if (next == pos) { PCIERR(phb, bdfn, "pci_find_cap hit a loop !\n"); break; } pos = next; } return OPAL_UNSUPPORTED; } /* pci_find_cap - Find a PCI capability in a device config space * * This will return a config space offset (positive) or a negative * error (OPAL error codes). * * OPAL_UNSUPPORTED is returned if the capability doesn't exist */ int64_t pci_find_cap(struct phb *phb, uint16_t bdfn, uint8_t want) { return __pci_find_cap(phb, bdfn, want, true); } /* pci_find_ecap - Find a PCIe extended capability in a device * config space * * This will return a config space offset (positive) or a negative * error (OPAL error code). Additionally, if the "version" argument * is non-NULL, the capability version will be returned there. * * OPAL_UNSUPPORTED is returned if the capability doesn't exist */ int64_t pci_find_ecap(struct phb *phb, uint16_t bdfn, uint16_t want, uint8_t *version) { int64_t rc; uint32_t cap; uint16_t off, prev = 0; for (off = 0x100; off && off < 0x1000; off = (cap >> 20) & 0xffc ) { if (off == prev) { PCIERR(phb, bdfn, "pci_find_ecap hit a loop !\n"); break; } prev = off; rc = pci_cfg_read32(phb, bdfn, off, &cap); if (rc) return rc; if ((cap & 0xffff) == want) { if (version) *version = (cap >> 16) & 0xf; return off; } } return OPAL_UNSUPPORTED; } static struct pci_device *pci_scan_one(struct phb *phb, struct pci_device *parent, uint16_t bdfn) { struct pci_device *pd = NULL; uint32_t retries, vdid, val; int64_t rc, ecap; uint8_t htype; uint16_t capreg; bool had_crs = false; for (retries = 40; retries; retries--) { rc = pci_cfg_read32(phb, bdfn, 0, &vdid); if (rc) return NULL; if (vdid == 0xffffffff || vdid == 0x00000000) return NULL; if (vdid != 0xffff0001) break; had_crs = true; time_wait_ms(100); } if (vdid == 0xffff0001) { PCIERR(phb, bdfn, "CRS timeout !\n"); return NULL; } if (had_crs) PCIDBG(phb, bdfn, "Probe success after CRS\n"); pd = zalloc(sizeof(struct pci_device)); if (!pd) { PCIERR(phb, bdfn,"Failed to allocate structure pci_device !\n"); goto fail; } pd->bdfn = bdfn; pd->parent = parent; list_head_init(&pd->children); rc = pci_cfg_read8(phb, bdfn, PCI_CFG_HDR_TYPE, &htype); if (rc) { PCIERR(phb, bdfn, "Failed to read header type !\n"); goto fail; } pd->is_multifunction = !!(htype & 0x80); pd->is_bridge = (htype & 0x7f) != 0; pd->scan_map = 0xffffffff; /* Default */ pd->primary_bus = (bdfn >> 8); /* On the upstream port of PLX bridge 8724 (rev ba), PCI_STATUS * register doesn't have capability indicator though it support * various PCI capabilities. So we need ignore that bit when * looking for PCI capabilities on the upstream port, which is * limited to one that seats directly under root port. */ if (vdid == 0x872410b5 && parent && !parent->parent) { uint8_t rev; pci_cfg_read8(phb, bdfn, PCI_CFG_REV_ID, &rev); if (rev == 0xba) ecap = __pci_find_cap(phb, bdfn, PCI_CFG_CAP_ID_EXP, false); else ecap = pci_find_cap(phb, bdfn, PCI_CFG_CAP_ID_EXP); } else { ecap = pci_find_cap(phb, bdfn, PCI_CFG_CAP_ID_EXP); } if (ecap > 0) { pci_set_cap(pd, PCI_CFG_CAP_ID_EXP, ecap, false); pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_CAPABILITY_REG, &capreg); pd->dev_type = GETFIELD(PCICAP_EXP_CAP_TYPE, capreg); /* * XXX We observe a problem on some PLX switches where one * of the downstream ports appears as an upstream port, we * fix that up here otherwise, other code will misbehave */ if (pd->parent && pd->dev_type == PCIE_TYPE_SWITCH_UPPORT && pd->parent->dev_type == PCIE_TYPE_SWITCH_UPPORT && vdid == 0x874810b5) { PCIDBG(phb, bdfn, "Fixing up bad PLX downstream port !\n"); pd->dev_type = PCIE_TYPE_SWITCH_DNPORT; } /* XXX Handle ARI */ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT || pd->dev_type == PCIE_TYPE_ROOT_PORT) pd->scan_map = 0x1; /* Read MPS capability, whose maximal size is 4096 */ pci_cfg_read32(phb, bdfn, ecap + PCICAP_EXP_DEVCAP, &val); pd->mps = (128 << GETFIELD(PCICAP_EXP_DEVCAP_MPSS, val)); if (pd->mps > 4096) pd->mps = 4096; } else { pd->dev_type = PCIE_TYPE_LEGACY; } /* If it's a bridge, sanitize the bus numbers to avoid forwarding * * This will help when walking down those bridges later on */ if (pd->is_bridge) { pci_cfg_write8(phb, bdfn, PCI_CFG_PRIMARY_BUS, pd->primary_bus); pci_cfg_write8(phb, bdfn, PCI_CFG_SECONDARY_BUS, 0); pci_cfg_write8(phb, bdfn, PCI_CFG_SUBORDINATE_BUS, 0); } /* XXX Need to do some basic setups, such as MPSS, MRS, * RCB, etc... */ PCIDBG(phb, bdfn, "Found VID:%04x DEV:%04x TYP:%d MF%s BR%s EX%s\n", vdid & 0xffff, vdid >> 16, pd->dev_type, pd->is_multifunction ? "+" : "-", pd->is_bridge ? "+" : "-", pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false) ? "+" : "-"); /* * Call PHB hook */ if (phb->ops->device_init) phb->ops->device_init(phb, pd); return pd; fail: if (pd) free(pd); return NULL; } /* pci_check_clear_freeze - Probing empty slot will result in an EEH * freeze. Currently we have a single PE mapping * everything (default state of our backend) so * we just check and clear the state of PE#0 * * NOTE: We currently only handle simple PE freeze, not PHB fencing * (or rather our backend does) */ static void pci_check_clear_freeze(struct phb *phb) { int64_t rc; uint8_t freeze_state; uint16_t pci_error_type, sev; rc = phb->ops->eeh_freeze_status(phb, 0, &freeze_state, &pci_error_type, &sev, NULL); if (rc) return; if (freeze_state == OPAL_EEH_STOPPED_NOT_FROZEN) return; /* We can't handle anything worse than an ER here */ if (sev > OPAL_EEH_SEV_NO_ERROR && sev < OPAL_EEH_SEV_PE_ER) { PCIERR(phb, 0, "Fatal probe in %s error !\n", __func__); return; } phb->ops->eeh_freeze_clear(phb, 0, OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); } /* pci_enable_bridge - Called before scanning a bridge * * Ensures error flags are clean, disable master abort, and * check if the subordinate bus isn't reset, the slot is enabled * on PCIe, etc... */ static bool pci_enable_bridge(struct phb *phb, struct pci_device *pd) { uint16_t bctl; bool was_reset = false; int64_t ecap = 0; /* Disable master aborts, clear errors */ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_BRCTL, &bctl); bctl &= ~PCI_CFG_BRCTL_MABORT_REPORT; pci_cfg_write16(phb, pd->bdfn, PCI_CFG_BRCTL, bctl); /* PCI-E bridge, check the slot state. We don't do that on the * root complex as this is handled separately and not all our * RCs implement the standard register set. */ if ((pd->dev_type == PCIE_TYPE_ROOT_PORT && pd->primary_bus > 0) || pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) { uint16_t slctl, slcap, slsta, lctl; ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); /* Read the slot status & check for presence detect */ pci_cfg_read16(phb, pd->bdfn, ecap+PCICAP_EXP_SLOTSTAT, &slsta); PCITRACE(phb, pd->bdfn, "slstat=%04x\n", slsta); if (!(slsta & PCICAP_EXP_SLOTSTAT_PDETECTST)) { PCIDBG(phb, pd->bdfn, "No card in slot\n"); return false; } /* Read the slot capabilities */ pci_cfg_read16(phb, pd->bdfn, ecap+PCICAP_EXP_SLOTCAP, &slcap); PCITRACE(phb, pd->bdfn, "slcap=%04x\n", slcap); if (!(slcap & PCICAP_EXP_SLOTCAP_PWCTRL)) goto power_is_on; /* Read the slot control register, check if the slot is off */ pci_cfg_read16(phb, pd->bdfn, ecap+PCICAP_EXP_SLOTCTL, &slctl); PCITRACE(phb, pd->bdfn, "slctl=%04x\n", slctl); if (!(slctl & PCICAP_EXP_SLOTCTL_PWRCTLR)) goto power_is_on; /* Turn power on * * XXX This is a "command", we should wait for it to complete * etc... but just waiting 2s will do for now */ PCIDBG(phb, pd->bdfn, "Bridge power is off, turning on ...\n"); slctl &= ~PCICAP_EXP_SLOTCTL_PWRCTLR; slctl |= SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, 0, PCIE_INDIC_ON); pci_cfg_write16(phb, pd->bdfn, ecap+PCICAP_EXP_SLOTCTL, slctl); /* Wait a couple of seconds */ time_wait_ms(2000); power_is_on: /* Enable link */ pci_cfg_read16(phb, pd->bdfn, ecap+PCICAP_EXP_LCTL, &lctl); PCITRACE(phb, pd->bdfn, " lctl=%04x\n", lctl); lctl &= ~PCICAP_EXP_LCTL_LINK_DIS; pci_cfg_write16(phb, pd->bdfn, ecap+PCICAP_EXP_LCTL, lctl); } /* Clear secondary reset */ if (bctl & PCI_CFG_BRCTL_SECONDARY_RESET) { PCIDBG(phb, pd->bdfn, "Bridge secondary reset is on, clearing it ...\n"); bctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; pci_cfg_write16(phb, pd->bdfn, PCI_CFG_BRCTL, bctl); time_wait_ms(1000); was_reset = true; } /* PCI-E bridge, wait for link */ if (pd->dev_type == PCIE_TYPE_ROOT_PORT || pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) { uint32_t lcap; /* Read link caps */ pci_cfg_read32(phb, pd->bdfn, ecap+PCICAP_EXP_LCAP, &lcap); /* Did link capability say we got reporting ? * * If yes, wait up to 10s, if not, wait 1s if we didn't already */ if (lcap & PCICAP_EXP_LCAP_DL_ACT_REP) { uint32_t retries = 100; uint16_t lstat; PCIDBG(phb, pd->bdfn, "waiting for link... \n"); while(retries--) { pci_cfg_read16(phb, pd->bdfn, ecap+PCICAP_EXP_LSTAT, &lstat); if (lstat & PCICAP_EXP_LSTAT_DLLL_ACT) break; time_wait_ms(100); } PCIDBG(phb, pd->bdfn, "end wait for link...\n"); if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) { PCIERR(phb, pd->bdfn, "Timeout waiting" " for downstream link\n"); return false; } /* Need to wait another 100ms before touching * the config space */ time_wait_ms(100); } else if (!was_reset) time_wait_ms(1000); } /* Clear error status */ pci_cfg_write16(phb, pd->bdfn, PCI_CFG_STAT, 0xffff); return true; } /* Clear up bridge resources */ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd) { uint16_t cmd; pci_cfg_write16(phb, pd->bdfn, PCI_CFG_IO_BASE_U16, 0xffff); pci_cfg_write8(phb, pd->bdfn, PCI_CFG_IO_BASE, 0xf0); pci_cfg_write16(phb, pd->bdfn, PCI_CFG_IO_LIMIT_U16, 0); pci_cfg_write8(phb, pd->bdfn, PCI_CFG_IO_LIMIT, 0); pci_cfg_write16(phb, pd->bdfn, PCI_CFG_MEM_BASE, 0xfff0); pci_cfg_write16(phb, pd->bdfn, PCI_CFG_MEM_LIMIT, 0); pci_cfg_write32(phb, pd->bdfn, PCI_CFG_PREF_MEM_BASE_U32, 0xffffffff); pci_cfg_write16(phb, pd->bdfn, PCI_CFG_PREF_MEM_BASE, 0xfff0); pci_cfg_write32(phb, pd->bdfn, PCI_CFG_PREF_MEM_LIMIT_U32, 0); pci_cfg_write16(phb, pd->bdfn, PCI_CFG_PREF_MEM_LIMIT, 0); /* Note: This is a bit fishy but since we have closed all the * bridge windows above, it shouldn't be a problem. Basically * we enable Memory, IO and Bus Master on the bridge because * some versions of Linux will fail to do it themselves. */ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_CMD, &cmd); cmd |= PCI_CFG_CMD_IO_EN | PCI_CFG_CMD_MEM_EN; cmd |= PCI_CFG_CMD_BUS_MASTER_EN; pci_cfg_write16(phb, pd->bdfn, PCI_CFG_CMD, cmd); } /* pci_scan - Perform a recursive scan of the bus at bus_number * populating the list passed as an argument. This also * performs the bus numbering, so it returns the largest * bus number that was assigned. * * Note: Eventually this might want to access some VPD information * in order to know what slots to scan and what not etc.. * * XXX NOTE: We might want to enable ARI along the way... * * XXX NOTE: We might also want to setup the PCIe MPS/MRSS properly * here as Linux may or may not do it */ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus, struct list_head *list, struct pci_device *parent, bool scan_downstream) { struct pci_device *pd = NULL; uint8_t dev, fn, next_bus, max_sub, save_max; uint32_t scan_map; /* Decide what to scan */ scan_map = parent ? parent->scan_map : phb->scan_map; /* Do scan */ for (dev = 0; dev < 32; dev++) { if (!(scan_map & (1ul << dev))) continue; /* Scan the device */ pd = pci_scan_one(phb, parent, (bus << 8) | (dev << 3)); pci_check_clear_freeze(phb); if (!pd) continue; /* Get slot info if any */ if (platform.pci_get_slot_info) platform.pci_get_slot_info(phb, pd); /* Link it up */ list_add_tail(list, &pd->link); /* XXX Handle ARI */ if (!pd->is_multifunction) continue; for (fn = 1; fn < 8; fn++) { pd = pci_scan_one(phb, parent, ((uint16_t)bus << 8) | (dev << 3) | fn); pci_check_clear_freeze(phb); if (pd) { if (platform.pci_get_slot_info) platform.pci_get_slot_info(phb, pd); list_add_tail(list, &pd->link); } } } /* * We only scan downstream if instructed to do so by the * caller. Typically we avoid the scan when we know the * link is down already, which happens for the top level * root complex, and avoids a long secondary timeout */ if (!scan_downstream) return bus; next_bus = bus + 1; max_sub = bus; save_max = max_bus; /* Scan down bridges */ list_for_each(list, pd, link) { bool use_max, do_scan; if (!pd->is_bridge) continue; /* We need to figure out a new bus number to start from. * * This can be tricky due to our HW constraints which differ * from bridge to bridge so we are going to let the phb * driver decide what to do. This can return us a maximum * bus number to assign as well * * This function will: * * - Return the bus number to use as secondary for the * bridge or 0 for a failure * * - "max_bus" will be adjusted to represent the max * subordinate that can be associated with the downstream * device * * - "use_max" will be set to true if the returned max_bus * *must* be used as the subordinate bus number of that * bridge (when we need to give aligned powers of two's * on P7IOC). If is is set to false, we just adjust the * subordinate bus number based on what we probed. * */ max_bus = save_max; next_bus = phb->ops->choose_bus(phb, pd, next_bus, &max_bus, &use_max); /* Configure the bridge with the returned values */ if (next_bus <= bus) { PCIERR(phb, pd->bdfn, "Out of bus numbers !\n"); max_bus = next_bus = 0; /* Failure case */ } pd->secondary_bus = next_bus; pd->subordinate_bus = max_bus; pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SECONDARY_BUS, next_bus); pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SUBORDINATE_BUS, max_bus); if (!next_bus) break; PCIDBG(phb, pd->bdfn, "Bus %02x..%02x %s scanning...\n", next_bus, max_bus, use_max ? "[use max]" : ""); /* Clear up bridge resources */ pci_cleanup_bridge(phb, pd); /* Configure the bridge. This will enable power to the slot * if it's currently disabled, lift reset, etc... * * Return false if we know there's nothing behind the bridge */ do_scan = pci_enable_bridge(phb, pd); /* Perform recursive scan */ if (do_scan) { max_sub = pci_scan(phb, next_bus, max_bus, &pd->children, pd, true); } else if (!use_max) { /* XXX Empty bridge... we leave room for hotplug * slots etc.. but we should be smarter at figuring * out if this is actually a hotpluggable one */ max_sub = next_bus + 4; if (max_sub > max_bus) max_sub = max_bus; } /* Update the max subordinate as described previously */ if (use_max) max_sub = max_bus; pd->subordinate_bus = max_sub; pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SUBORDINATE_BUS, max_sub); next_bus = max_sub + 1; } return max_sub; } static int pci_get_mps(struct phb *phb, struct pci_device *pd, void *userdata) { uint32_t *mps = (uint32_t *)userdata; /* Only check PCI device that had MPS capacity */ if (phb && pd && pd->mps && *mps > pd->mps) *mps = pd->mps; return 0; } static int pci_configure_mps(struct phb *phb, struct pci_device *pd, void *userdata __unused) { uint32_t ecap, aercap, mps; uint16_t val; assert(phb); assert(pd); /* If the MPS isn't acceptable one, bail immediately */ mps = phb->mps; if (mps < 128 || mps > 4096) return 1; /* Retrieve PCIe and AER capability */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); aercap = pci_cap(pd, PCIECAP_ID_AER, true); /* PCIe device always has MPS capacity */ if (pd->mps) { mps = ilog2(mps) - 7; pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_DEVCTL, &val); val = SETFIELD(PCICAP_EXP_DEVCTL_MPS, val, mps); pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_DEVCTL, val); } /* Changing MPS on upstream PCI bridge might cause some error * bits in PCIe and AER capability. To clear them to avoid * confusion. */ if (aercap) { pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_STATUS, 0xffffffff); pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_CE_STATUS, 0xffffffff); } if (ecap) pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_DEVSTAT, 0xf); return 0; } static void pci_disable_completion_timeout(struct phb *phb, struct pci_device *pd) { uint32_t ecap; uint32_t val; /* PCIE capability required */ if (!pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false)) return; /* Check if it has capability to disable completion timeout */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read32(phb, pd->bdfn, ecap + PCIECAP_EXP_DCAP2, &val); if (!(val & PCICAP_EXP_DCAP2_CMPTOUT_DIS)) return; /* Disable completion timeout without more check */ pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_DCTL2, &val); val |= PCICAP_EXP_DCTL2_CMPTOUT_DIS; pci_cfg_write32(phb, pd->bdfn, ecap + PCICAP_EXP_DCTL2, val); } void pci_device_init(struct phb *phb, struct pci_device *pd) { pci_configure_mps(phb, pd, NULL); pci_disable_completion_timeout(phb, pd); } /* * The power state would be checked. If the power has * been on, we will issue fundamental reset. Otherwise, * we will power it on before issuing fundamental reset. */ static int64_t pci_phb_reset(struct phb *phb) { const char *desc; int64_t rc; rc = phb->ops->power_state(phb); if (rc < 0) { PCIERR(phb, 0, "Failed to get power state, rc=%lld\n", rc); return rc; } if (rc == OPAL_SHPC_POWER_ON) { desc = "fundamental reset"; rc = phb->ops->fundamental_reset(phb); } else { desc = "power on"; rc = phb->ops->slot_power_on(phb); } if (rc < 0) { /* Don't warn if it's just an empty slot */ if (rc != OPAL_CLOSED) goto warn_err; return rc; } /* Wait the internal state machine */ while (rc > 0) { time_wait(rc); rc = phb->ops->poll(phb); } warn_err: if (rc < 0) PCIERR(phb, 0, "Failed to %s, rc=%lld\n", desc, rc); return rc; } static void pci_reset_phb(void *data) { struct phb *phb = data; int64_t rc; PCIDBG(phb, 0, "Init slot...\n"); /* * For PCI/PCI-X, we get the slot info and we also * check if the PHB has anything connected to it */ if (phb->phb_type < phb_type_pcie_v1) { if (platform.pci_get_slot_info) platform.pci_get_slot_info(phb, NULL); rc = phb->ops->presence_detect(phb); if (rc != OPAL_SHPC_DEV_PRESENT) { PCIDBG(phb, 0, "Slot empty\n"); return; } } /* * Power on the PHB, the PHB should be reset in * fundamental way while powering on. The reset * state machine is going to wait for the link */ pci_phb_reset(phb); } static void pci_scan_phb(void *data) { struct phb *phb = data; uint32_t mps = 0xffffffff; bool has_link = false; int64_t rc; rc = phb->ops->link_state(phb); if (rc < 0) { PCIERR(phb, 0, "Failed to query link state, rc=%lld\n", rc); return; } /* * We will probe the root port. If the PHB has trained * link, we will probe the downstream port as well. */ if (rc != OPAL_SHPC_LINK_DOWN) has_link = true; if (has_link && phb->phb_type >= phb_type_pcie_v1) PCIDBG(phb, 0, "Link up at x%lld width\n", rc); else if (has_link) PCIDBG(phb, 0, "Link up\n"); else PCIDBG(phb, 0, "Link down\n"); /* Scan root port and downstream ports if applicable */ PCIDBG(phb, 0, "Scanning (upstream%s)...\n", has_link ? "+downsteam" : " only"); pci_scan(phb, 0, 0xff, &phb->devices, NULL, has_link); /* Configure MPS (Max Payload Size) for PCIe domain */ pci_walk_dev(phb, pci_get_mps, &mps); phb->mps = mps; pci_walk_dev(phb, pci_configure_mps, NULL); } int64_t pci_register_phb(struct phb *phb) { int64_t rc = OPAL_SUCCESS; unsigned int i; /* This is called at init time in non-concurrent way, so no lock needed */ for (i = 0; i < ARRAY_SIZE(phbs); i++) if (!phbs[i]) break; if (i >= ARRAY_SIZE(phbs)) { prerror("PHB: Failed to find a free ID slot\n"); rc = OPAL_RESOURCE; } else { phbs[i] = phb; phb->opal_id = i; dt_add_property_cells(phb->dt_node, "ibm,opal-phbid", 0, phb->opal_id); PCIDBG(phb, 0, "PCI: Registered PHB\n"); } list_head_init(&phb->devices); return rc; } int64_t pci_unregister_phb(struct phb *phb) { /* XXX We want some kind of RCU or RWlock to make things * like that happen while no OPAL callback is in progress, * that way we avoid taking a lock in each of them. * * Right now we don't unregister so we are fine */ phbs[phb->opal_id] = phb; return OPAL_SUCCESS; } struct phb *pci_get_phb(uint64_t phb_id) { if (phb_id >= ARRAY_SIZE(phbs)) return NULL; /* XXX See comment in pci_unregister_phb() about locking etc... */ return phbs[phb_id]; } static const char *pci_class_name(uint32_t class_code) { uint8_t class = class_code >> 16; uint8_t sub = (class_code >> 8) & 0xff; uint8_t pif = class_code & 0xff; switch(class) { case 0x00: switch(sub) { case 0x00: return "device"; case 0x01: return "vga"; } break; case 0x01: switch(sub) { case 0x00: return "scsi"; case 0x01: return "ide"; case 0x02: return "fdc"; case 0x03: return "ipi"; case 0x04: return "raid"; case 0x05: return "ata"; case 0x06: return "sata"; case 0x07: return "sas"; default: return "mass-storage"; } case 0x02: switch(sub) { case 0x00: return "ethernet"; case 0x01: return "token-ring"; case 0x02: return "fddi"; case 0x03: return "atm"; case 0x04: return "isdn"; case 0x05: return "worldfip"; case 0x06: return "picmg"; default: return "network"; } case 0x03: switch(sub) { case 0x00: return "vga"; case 0x01: return "xga"; case 0x02: return "3d-controller"; default: return "display"; } case 0x04: switch(sub) { case 0x00: return "video"; case 0x01: return "sound"; case 0x02: return "telephony"; default: return "multimedia-device"; } case 0x05: switch(sub) { case 0x00: return "memory"; case 0x01: return "flash"; default: return "memory-controller"; } case 0x06: switch(sub) { case 0x00: return "host"; case 0x01: return "isa"; case 0x02: return "eisa"; case 0x03: return "mca"; case 0x04: return "pci"; case 0x05: return "pcmcia"; case 0x06: return "nubus"; case 0x07: return "cardbus"; case 0x08: return "raceway"; case 0x09: return "semi-transparent-pci"; case 0x0a: return "infiniband"; default: return "unknown-bridge"; } case 0x07: switch(sub) { case 0x00: switch(pif) { case 0x01: return "16450-serial"; case 0x02: return "16550-serial"; case 0x03: return "16650-serial"; case 0x04: return "16750-serial"; case 0x05: return "16850-serial"; case 0x06: return "16950-serial"; default: return "serial"; } case 0x01: switch(pif) { case 0x01: return "bi-directional-parallel"; case 0x02: return "ecp-1.x-parallel"; case 0x03: return "ieee1284-controller"; case 0xfe: return "ieee1284-device"; default: return "parallel"; } case 0x02: return "multiport-serial"; case 0x03: switch(pif) { case 0x01: return "16450-modem"; case 0x02: return "16550-modem"; case 0x03: return "16650-modem"; case 0x04: return "16750-modem"; default: return "modem"; } case 0x04: return "gpib"; case 0x05: return "smart-card"; default: return "communication-controller"; } case 0x08: switch(sub) { case 0x00: switch(pif) { case 0x01: return "isa-pic"; case 0x02: return "eisa-pic"; case 0x10: return "io-apic"; case 0x20: return "iox-apic"; default: return "interrupt-controller"; } case 0x01: switch(pif) { case 0x01: return "isa-dma"; case 0x02: return "eisa-dma"; default: return "dma-controller"; } case 0x02: switch(pif) { case 0x01: return "isa-system-timer"; case 0x02: return "eisa-system-timer"; default: return "timer"; } case 0x03: switch(pif) { case 0x01: return "isa-rtc"; default: return "rtc"; } case 0x04: return "hotplug-controller"; case 0x05: return "sd-host-controller"; default: return "system-peripheral"; } case 0x09: switch(sub) { case 0x00: return "keyboard"; case 0x01: return "pen"; case 0x02: return "mouse"; case 0x03: return "scanner"; case 0x04: return "gameport"; default: return "input-controller"; } case 0x0a: switch(sub) { case 0x00: return "clock"; default: return "docking-station"; } case 0x0b: switch(sub) { case 0x00: return "386"; case 0x01: return "486"; case 0x02: return "pentium"; case 0x10: return "alpha"; case 0x20: return "powerpc"; case 0x30: return "mips"; case 0x40: return "co-processor"; default: return "cpu"; } case 0x0c: switch(sub) { case 0x00: return "firewire"; case 0x01: return "access-bus"; case 0x02: return "ssa"; case 0x03: switch(pif) { case 0x00: return "usb-uhci"; case 0x10: return "usb-ohci"; case 0x20: return "usb-ehci"; case 0x30: return "usb-xhci"; case 0xfe: return "usb-device"; default: return "usb"; } case 0x04: return "fibre-channel"; case 0x05: return "smb"; case 0x06: return "infiniband"; case 0x07: switch(pif) { case 0x00: return "impi-smic"; case 0x01: return "impi-kbrd"; case 0x02: return "impi-bltr"; default: return "impi"; } case 0x08: return "secos"; case 0x09: return "canbus"; default: return "serial-bus"; } case 0x0d: switch(sub) { case 0x00: return "irda"; case 0x01: return "consumer-ir"; case 0x10: return "rf-controller"; case 0x11: return "bluetooth"; case 0x12: return "broadband"; case 0x20: return "enet-802.11a"; case 0x21: return "enet-802.11b"; default: return "wireless-controller"; } case 0x0e: return "intelligent-controller"; case 0x0f: switch(sub) { case 0x01: return "satellite-tv"; case 0x02: return "satellite-audio"; case 0x03: return "satellite-voice"; case 0x04: return "satellite-data"; default: return "satellite-device"; } case 0x10: switch(sub) { case 0x00: return "network-encryption"; case 0x01: return "entertainment-encryption"; default: return "encryption"; } case 0x011: switch(sub) { case 0x00: return "dpio"; case 0x01: return "counter"; case 0x10: return "measurement"; case 0x20: return "management-card"; default: return "data-processing"; } } return "device"; } void pci_std_swizzle_irq_map(struct dt_node *np, struct pci_device *pd, struct pci_lsi_state *lstate, uint8_t swizzle) { uint32_t *map, *p; int dev, irq; size_t map_size; /* Size in bytes of a target interrupt */ size_t isize = lstate->int_size * sizeof(uint32_t); /* Calculate the size of a map entry: * * 3 cells : PCI Address * 1 cell : PCI IRQ * 1 cell : PIC phandle * n cells : PIC irq (n = lstate->int_size) * * Assumption: PIC address is 0-size */ int esize = 3 + 1 + 1 + lstate->int_size; /* Number of map "device" entries * * A PCI Express root or downstream port needs only one * entry for device 0. Anything else will get a full map * for all possible 32 child device numbers * * If we have been passed a host bridge (pd == NULL) we also * do a simple per-pin map */ int edevcount; if (!pd || (pd->dev_type == PCIE_TYPE_ROOT_PORT || pd->dev_type == PCIE_TYPE_SWITCH_DNPORT)) { edevcount = 1; dt_add_property_cells(np, "interrupt-map-mask", 0, 0, 0, 7); } else { edevcount = 32; dt_add_property_cells(np, "interrupt-map-mask", 0xf800, 0, 0, 7); } map_size = esize * edevcount * 4 * sizeof(uint32_t); map = p = zalloc(map_size); if (!map) { prerror("Failed to allocate interrupt-map-mask !\n"); return; } for (dev = 0; dev < edevcount; dev++) { for (irq = 0; irq < 4; irq++) { /* Calculate pin */ uint32_t new_irq = (irq + dev + swizzle) % 4; /* PCI address portion */ *(p++) = dev << (8 + 3); *(p++) = 0; *(p++) = 0; /* PCI interrupt portion */ *(p++) = irq + 1; /* Parent phandle */ *(p++) = lstate->int_parent[new_irq]; /* Parent desc */ memcpy(p, lstate->int_val[new_irq], isize); p += lstate->int_size; } } dt_add_property(np, "interrupt-map", map, map_size); free(map); } static void pci_add_slot_properties(struct phb *phb, struct pci_slot_info *info, struct dt_node *np) { char loc_code[LOC_CODE_SIZE]; size_t base_loc_code_len = 0, slot_label_len = 0; if (phb->base_loc_code) { base_loc_code_len = strlen(phb->base_loc_code); strcpy(loc_code, phb->base_loc_code); } slot_label_len = strlen(info->label); if (slot_label_len) { if ((base_loc_code_len + slot_label_len + 1) < LOC_CODE_SIZE) { if (base_loc_code_len) strcat(loc_code, "-"); strcat(loc_code, info->label); dt_add_property(np, "ibm,slot-location-code", loc_code, strlen(loc_code) + 1); } else { PCIERR(phb, 0, "Loc Code too long - %zu + %zu + 1\n", base_loc_code_len, slot_label_len); } } /* Add other slot information */ dt_add_property_cells(np, "ibm,slot-pluggable", info->pluggable); dt_add_property_cells(np, "ibm,slot-power-ctl", info->power_ctl); if (info->wired_lanes >= 0) dt_add_property_cells(np, "ibm,slot-wired-lanes", info->wired_lanes); /*dt_add_property(np, "ibm,slot-bus-clock", &pd->slot_info->bus_clock, sizeof(uint8_t));*/ if (info->connector_type >= 0) dt_add_property_cells(np, "ibm,slot-connector-type", info->connector_type); if (info->card_desc >= 0) dt_add_property_cells(np, "ibm,slot-card-desc", info->card_desc); if (info->card_mech >= 0) dt_add_property_cells(np, "ibm,slot-card-mech", info->card_mech); if (info->pwr_led_ctl >= 0) dt_add_property_cells(np, "ibm,slot-pwr-led-ctl", info->pwr_led_ctl); if (info->attn_led_ctl >= 0) dt_add_property_cells(np, "ibm,slot-attn-led-ctl", info->attn_led_ctl); if (strlen(info->label) > 0) dt_add_property_string(np, "ibm,slot-label", info->label); } static void pci_add_loc_code(struct dt_node *np, struct pci_device *pd) { struct dt_node *p = np->parent; const char *blcode = NULL; char *lcode; uint32_t class_code; uint8_t class, sub; uint8_t pos, len; /* Look for a parent with a slot-location-code */ while (p && !blcode) { blcode = dt_prop_get_def(p, "ibm,slot-location-code", NULL); p = p->parent; } if (!blcode) return; /* ethernet devices get port codes */ class_code = dt_prop_get_u32(np, "class-code"); class = class_code >> 16; sub = (class_code >> 8) & 0xff; /* XXX Don't do that on openpower for now, we will need to sort things * out later, otherwise the mezzanine slot on Habanero gets weird results */ if (class == 0x02 && sub == 0x00 && fsp_present()) { /* There's usually several spaces at the end of the property. Test for, but don't rely on, that being the case */ len = strlen(blcode); for (pos = 0; pos < len; pos++) if (blcode[pos] == ' ') break; if (pos + 3 < len) lcode = strdup(blcode); else { lcode = malloc(pos + 3); memcpy(lcode, blcode, len); } lcode[pos++] = '-'; lcode[pos++] = 'T'; lcode[pos++] = (char)(pd->bdfn & 0x7) + '1'; lcode[pos++] = '\0'; dt_add_property_string(np, "ibm,loc-code", lcode); free(lcode); } else dt_add_property_string(np, "ibm,loc-code", blcode); } static void pci_print_summary_line(struct phb *phb, struct pci_device *pd, struct dt_node *np, u32 rev_class, const char *cname) { const char *label, *dtype, *s; u32 vdid; #define MAX_SLOTSTR 32 char slotstr[MAX_SLOTSTR + 1] = { 0, }; pci_cfg_read32(phb, pd->bdfn, 0, &vdid); /* If it's a slot, it has a slot-label */ label = dt_prop_get_def(np, "ibm,slot-label", NULL); if (label) { u32 lanes = dt_prop_get_u32_def(np, "ibm,slot-wired-lanes", 0); static const char *lanestrs[] = { "", " x1", " x2", " x4", " x8", "x16", "x32", "32b", "64b" }; const char *lstr = lanes > PCI_SLOT_WIRED_LANES_PCIX_64 ? "" : lanestrs[lanes]; snprintf(slotstr, MAX_SLOTSTR, "SLOT=%3s %s", label, lstr); /* XXX Add more slot info */ } else { /* * No label, ignore downstream switch legs and root complex, * Those would essentially be non-populated */ if (pd->dev_type != PCIE_TYPE_ROOT_PORT && pd->dev_type != PCIE_TYPE_SWITCH_DNPORT) { /* It's a mere device, get loc code */ s = dt_prop_get_def(np, "ibm,loc-code", NULL); if (s) snprintf(slotstr, MAX_SLOTSTR, "LOC_CODE=%s", s); } } if (pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false)) { static const char *pcie_types[] = { "EP ", "LGCY", "????", "????", "ROOT", "SWUP", "SWDN", "ETOX", "XTOE", "RINT", "EVTC" }; if (pd->dev_type >= ARRAY_SIZE(pcie_types)) dtype = "????"; else dtype = pcie_types[pd->dev_type]; } else dtype = pd->is_bridge ? "PCIB" : "PCID"; if (pd->is_bridge) PCINOTICE(phb, pd->bdfn, "[%s] %04x %04x R:%02x C:%06x B:%02x..%02x %s\n", dtype, vdid & 0xffff, vdid >> 16, rev_class & 0xff, rev_class >> 8, pd->secondary_bus, pd->subordinate_bus, slotstr); else PCINOTICE(phb, pd->bdfn, "[%s] %04x %04x R:%02x C:%06x (%14s) %s\n", dtype, vdid & 0xffff, vdid >> 16, rev_class & 0xff, rev_class >> 8, cname, slotstr); } static void pci_add_one_node(struct phb *phb, struct pci_device *pd, struct dt_node *parent_node, struct pci_lsi_state *lstate, uint8_t swizzle) { struct pci_device *child; struct dt_node *np; const char *cname; #define MAX_NAME 256 char name[MAX_NAME]; char compat[MAX_NAME]; uint32_t rev_class, vdid; uint32_t reg[5]; uint8_t intpin; pci_cfg_read32(phb, pd->bdfn, 0, &vdid); pci_cfg_read32(phb, pd->bdfn, PCI_CFG_REV_ID, &rev_class); pci_cfg_read8(phb, pd->bdfn, PCI_CFG_INT_PIN, &intpin); /* * Quirk for IBM bridge bogus class on PCIe root complex. * Without it, the PCI DN won't be created for its downstream * devices in Linux. */ if (pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false) && parent_node == phb->dt_node) rev_class = (rev_class & 0xff) | 0x6040000; cname = pci_class_name(rev_class >> 8); if (pd->bdfn & 0x7) snprintf(name, MAX_NAME - 1, "%s@%x,%x", cname, (pd->bdfn >> 3) & 0x1f, pd->bdfn & 0x7); else snprintf(name, MAX_NAME - 1, "%s@%x", cname, (pd->bdfn >> 3) & 0x1f); np = dt_new(parent_node, name); /* XXX FIXME: make proper "compatible" properties */ if (pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false)) { snprintf(compat, MAX_NAME, "pciex%x,%x", vdid & 0xffff, vdid >> 16); dt_add_property_cells(np, "ibm,pci-config-space-type", 1); } else { snprintf(compat, MAX_NAME, "pci%x,%x", vdid & 0xffff, vdid >> 16); dt_add_property_cells(np, "ibm,pci-config-space-type", 0); } dt_add_property_cells(np, "class-code", rev_class >> 8); dt_add_property_cells(np, "revision-id", rev_class & 0xff); dt_add_property_cells(np, "vendor-id", vdid & 0xffff); dt_add_property_cells(np, "device-id", vdid >> 16); if (intpin) dt_add_property_cells(np, "interrupts", intpin); /* XXX FIXME: Add a few missing ones such as * * - devsel-speed (!express) * - max-latency * - min-grant * - subsystem-id * - subsystem-vendor-id * - ... */ /* Add slot properties if needed */ if (pd->slot_info) pci_add_slot_properties(phb, pd->slot_info, np); /* Make up location code */ pci_add_loc_code(np, pd); /* XXX FIXME: We don't look for BARs, we only put the config space * entry in the "reg" property. That's enough for Linux and we might * even want to make this legit in future ePAPR */ reg[0] = pd->bdfn << 8; reg[1] = reg[2] = reg[3] = reg[4] = 0; dt_add_property(np, "reg", reg, sizeof(reg)); /* Print summary info about the device */ pci_print_summary_line(phb, pd, np, rev_class, cname); if (!pd->is_bridge) return; dt_add_property_cells(np, "#address-cells", 3); dt_add_property_cells(np, "#size-cells", 2); dt_add_property_cells(np, "#interrupt-cells", 1); /* We want "device_type" for bridges */ if (pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false)) dt_add_property_string(np, "device_type", "pciex"); else dt_add_property_string(np, "device_type", "pci"); /* Update the current interrupt swizzling level based on our own * device number */ swizzle = (swizzle + ((pd->bdfn >> 3) & 0x1f)) & 3; /* We generate a standard-swizzling interrupt map. This is pretty * big, we *could* try to be smarter for things that aren't hotplug * slots at least and only populate those entries for which there's * an actual children (especially on PCI Express), but for now that * will do */ pci_std_swizzle_irq_map(np, pd, lstate, swizzle); /* We do an empty ranges property for now, we haven't setup any * bridge windows, the kernel will deal with that * * XXX The kernel should probably fix that up */ dt_add_property(np, "ranges", NULL, 0); list_for_each(&pd->children, child, link) pci_add_one_node(phb, child, np, lstate, swizzle); } static void pci_add_nodes(struct phb *phb) { struct pci_lsi_state *lstate = &phb->lstate; struct pci_device *pd; /* If the PHB has its own slot info, add them */ if (phb->slot_info) pci_add_slot_properties(phb, phb->slot_info, NULL); /* Add all child devices */ list_for_each(&phb->devices, pd, link) pci_add_one_node(phb, pd, phb->dt_node, lstate, 0); } static void __pci_reset(struct list_head *list) { struct pci_device *pd; while ((pd = list_pop(list, struct pci_device, link)) != NULL) { __pci_reset(&pd->children); free(pd); } } void pci_reset(void) { unsigned int i; prlog(PR_NOTICE, "PCI: Clearing all devices...\n"); /* This is a remnant of fast-reboot, not currently used */ /* XXX Do those in parallel (at least the power up * state machine could be done in parallel) */ for (i = 0; i < ARRAY_SIZE(phbs); i++) { if (!phbs[i]) continue; __pci_reset(&phbs[i]->devices); } } static void pci_do_jobs(void (*fn)(void *)) { void *jobs[ARRAY_SIZE(phbs)]; int i; for (i = 0; i < ARRAY_SIZE(phbs); i++) { if (!phbs[i]) { jobs[i] = NULL; continue; } jobs[i] = __cpu_queue_job(NULL, phbs[i]->dt_node->name, fn, phbs[i], false); assert(jobs[i]); } /* If no secondary CPUs, do everything sync */ cpu_process_local_jobs(); /* Wait until all tasks are done */ for (i = 0; i < ARRAY_SIZE(phbs); i++) { if (!jobs[i]) continue; cpu_wait_job(jobs[i], true); } } void pci_init_slots(void) { unsigned int i; prlog(PR_NOTICE, "PCI: Resetting PHBs...\n"); pci_do_jobs(pci_reset_phb); prlog(PR_NOTICE, "PCI: Probing slots...\n"); pci_do_jobs(pci_scan_phb); if (platform.pci_probe_complete) platform.pci_probe_complete(); prlog(PR_DEBUG, "PCI Summary:\n"); for (i = 0; i < ARRAY_SIZE(phbs); i++) { if (!phbs[i]) continue; pci_add_nodes(phbs[i]); } } /* * Complete iteration on current level before switching to * child level, which is the proper order for restoring * PCI bus range on bridges. */ static struct pci_device *__pci_walk_dev(struct phb *phb, struct list_head *l, int (*cb)(struct phb *, struct pci_device *, void *), void *userdata) { struct pci_device *pd, *child; if (list_empty(l)) return NULL; list_for_each(l, pd, link) { if (cb && cb(phb, pd, userdata)) return pd; } list_for_each(l, pd, link) { child = __pci_walk_dev(phb, &pd->children, cb, userdata); if (child) return child; } return NULL; } struct pci_device *pci_walk_dev(struct phb *phb, int (*cb)(struct phb *, struct pci_device *, void *), void *userdata) { return __pci_walk_dev(phb, &phb->devices, cb, userdata); } static int __pci_find_dev(struct phb *phb, struct pci_device *pd, void *userdata) { uint16_t bdfn = *((uint16_t *)userdata); if (!phb || !pd) return 0; if (pd->bdfn == bdfn) return 1; return 0; } struct pci_device *pci_find_dev(struct phb *phb, uint16_t bdfn) { return pci_walk_dev(phb, __pci_find_dev, &bdfn); } static int __pci_restore_bridge_buses(struct phb *phb, struct pci_device *pd, void *data __unused) { if (!pd->is_bridge) return 0; pci_cfg_write8(phb, pd->bdfn, PCI_CFG_PRIMARY_BUS, pd->primary_bus); pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SECONDARY_BUS, pd->secondary_bus); pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SUBORDINATE_BUS, pd->subordinate_bus); return 0; } void pci_restore_bridge_buses(struct phb *phb) { pci_walk_dev(phb, __pci_restore_bridge_buses, NULL); } skiboot-skiboot-5.1.13/core/pel.c000066400000000000000000000175611265204436200166150ustar00rootroot00000000000000#include #include #include #include #include #include /* Create MTMS section for sapphire log */ static void create_mtms_section(struct errorlog *elog_data, char *pel_buffer, int *pel_offset) { struct opal_mtms_section *mtms = (struct opal_mtms_section *) (pel_buffer + *pel_offset); mtms->v6header.id = ELOG_SID_MACHINE_TYPE; mtms->v6header.length = MTMS_SECTION_SIZE; mtms->v6header.version = OPAL_EXT_HRD_VER; mtms->v6header.subtype = 0; mtms->v6header.component_id = elog_data->component_id; memset(mtms->model, 0x00, sizeof(mtms->model)); memcpy(mtms->model, dt_prop_get(dt_root, "model"), OPAL_SYS_MODEL_LEN); memset(mtms->serial_no, 0x00, sizeof(mtms->serial_no)); memcpy(mtms->serial_no, dt_prop_get(dt_root, "system-id"), OPAL_SYS_SERIAL_LEN); *pel_offset += MTMS_SECTION_SIZE; } /* Create extended header section */ static void create_extended_header_section(struct errorlog *elog_data, char *pel_buffer, int *pel_offset) { const char *opalmodel = NULL; uint64_t extd_time; struct opal_extended_header_section *extdhdr = (struct opal_extended_header_section *) (pel_buffer + *pel_offset); extdhdr->v6header.id = ELOG_SID_EXTENDED_HEADER; extdhdr->v6header.length = EXTENDED_HEADER_SECTION_SIZE; extdhdr->v6header.version = OPAL_EXT_HRD_VER; extdhdr->v6header.subtype = 0; extdhdr->v6header.component_id = elog_data->component_id; memset(extdhdr->model, 0x00, sizeof(extdhdr->model)); opalmodel = dt_prop_get(dt_root, "model"); memcpy(extdhdr->model, opalmodel, OPAL_SYS_MODEL_LEN); memset(extdhdr->serial_no, 0x00, sizeof(extdhdr->serial_no)); memcpy(extdhdr->serial_no, dt_prop_get(dt_root, "system-id"), OPAL_SYS_SERIAL_LEN); memset(extdhdr->opal_release_version, 0x00, sizeof(extdhdr->opal_release_version)); memset(extdhdr->opal_subsys_version, 0x00, sizeof(extdhdr->opal_subsys_version)); rtc_cache_get_datetime(&extdhdr->extended_header_date, &extd_time); extdhdr->extended_header_time = extd_time >> 32; extdhdr->opal_symid_len = 0; *pel_offset += EXTENDED_HEADER_SECTION_SIZE; } /* set src type */ static void settype(struct opal_src_section *src, uint8_t src_type) { char type[4]; snprintf(type, sizeof(type), "%02X", src_type); memcpy(src->srcstring, type, 2); } /* set SRC subsystem type */ static void setsubsys(struct opal_src_section *src, uint8_t src_subsys) { char subsys[4]; snprintf(subsys, sizeof(subsys), "%02X", src_subsys); memcpy(src->srcstring+2, subsys, 2); } /* Ser reason code of SRC */ static void setrefcode(struct opal_src_section *src, uint16_t src_refcode) { char refcode[8]; snprintf(refcode, sizeof(refcode), "%04X", src_refcode); memcpy(src->srcstring+4, refcode, 4); } /* Create SRC section of OPAL log */ static void create_src_section(struct errorlog *elog_data, char *pel_buffer, int *pel_offset) { struct opal_src_section *src = (struct opal_src_section *) (pel_buffer + *pel_offset); src->v6header.id = ELOG_SID_PRIMARY_SRC; src->v6header.length = SRC_SECTION_SIZE; src->v6header.version = OPAL_ELOG_VERSION; src->v6header.subtype = OPAL_ELOG_SST; src->v6header.component_id = elog_data->component_id; src->version = OPAL_SRC_SEC_VER; src->flags = 0; src->wordcount = OPAL_SRC_MAX_WORD_COUNT; src->srclength = SRC_LENGTH; settype(src, OPAL_SRC_TYPE_ERROR); setsubsys(src, OPAL_FAILING_SUBSYSTEM); setrefcode(src, elog_data->reason_code); memset(src->hexwords, 0 , (8 * 4)); src->hexwords[0] = OPAL_SRC_FORMAT; src->hexwords[4] = elog_data->additional_info[0]; src->hexwords[5] = elog_data->additional_info[1]; src->hexwords[6] = elog_data->additional_info[2]; src->hexwords[7] = elog_data->additional_info[3]; *pel_offset += SRC_SECTION_SIZE; } /* Create user header section */ static void create_user_header_section(struct errorlog *elog_data, char *pel_buffer, int *pel_offset) { struct opal_user_header_section *usrhdr = (struct opal_user_header_section *) (pel_buffer + *pel_offset); usrhdr->v6header.id = ELOG_SID_USER_HEADER; usrhdr->v6header.length = USER_HEADER_SECTION_SIZE; usrhdr->v6header.version = OPAL_ELOG_VERSION; usrhdr->v6header.subtype = OPAL_ELOG_SST; usrhdr->v6header.component_id = elog_data->component_id; usrhdr->subsystem_id = elog_data->subsystem_id; usrhdr->event_scope = 0; usrhdr->event_severity = elog_data->event_severity; usrhdr->event_type = elog_data->event_subtype; if (elog_data->elog_origin == ORG_SAPPHIRE) usrhdr->action_flags = ERRL_ACTION_REPORT; else usrhdr->action_flags = ERRL_ACTION_NONE; *pel_offset += USER_HEADER_SECTION_SIZE; } /* Create private header section */ static void create_private_header_section(struct errorlog *elog_data, char *pel_buffer, int *pel_offset) { uint64_t ctime; struct opal_private_header_section *privhdr = (struct opal_private_header_section *) pel_buffer; privhdr->v6header.id = ELOG_SID_PRIVATE_HEADER; privhdr->v6header.length = PRIVATE_HEADER_SECTION_SIZE; privhdr->v6header.version = OPAL_ELOG_VERSION; privhdr->v6header.subtype = OPAL_ELOG_SST; privhdr->v6header.component_id = elog_data->component_id; privhdr->plid = elog_data->plid; rtc_cache_get_datetime(&privhdr->create_date, &ctime); privhdr->create_time = ctime >> 32; privhdr->section_count = 5; privhdr->creator_subid_hi = 0x00; privhdr->creator_subid_lo = 0x00; if (elog_data->elog_origin == ORG_SAPPHIRE) privhdr->creator_id = OPAL_CID_SAPPHIRE; else privhdr->creator_id = OPAL_CID_POWERNV; privhdr->log_entry_id = elog_data->plid; /*entry id is updated by FSP*/ *pel_offset += PRIVATE_HEADER_SECTION_SIZE; } static void create_user_defined_section(struct errorlog *elog_data, char *pel_buffer, int *pel_offset) { char *dump = (char *)pel_buffer + *pel_offset; char *opal_buf = (char *)elog_data->user_data_dump; struct opal_user_section *usrhdr; struct elog_user_data_section *opal_usr_data; struct opal_private_header_section *privhdr = (struct opal_private_header_section *)pel_buffer; int i; for (i = 0; i < elog_data->user_section_count; i++) { usrhdr = (struct opal_user_section *)dump; opal_usr_data = (struct elog_user_data_section *)opal_buf; usrhdr->v6header.id = ELOG_SID_USER_DEFINED; usrhdr->v6header.version = OPAL_ELOG_VERSION; usrhdr->v6header.length = sizeof(struct opal_v6_header) + opal_usr_data->size; usrhdr->v6header.subtype = OPAL_ELOG_SST; usrhdr->v6header.component_id = elog_data->component_id; memcpy(usrhdr->dump, opal_buf, opal_usr_data->size); *pel_offset += usrhdr->v6header.length; dump += usrhdr->v6header.length; opal_buf += opal_usr_data->size; privhdr->section_count++; } } static size_t pel_user_section_size(struct errorlog *elog_data) { int i; size_t total = 0; char *opal_buf = (char *)elog_data->user_data_dump; struct elog_user_data_section *opal_usr_data; for (i = 0; i < elog_data->user_section_count; i++) { opal_usr_data = (struct elog_user_data_section *)opal_buf; total += sizeof(struct opal_v6_header) + opal_usr_data->size; opal_buf += opal_usr_data->size; } return total; } size_t pel_size(struct errorlog *elog_data) { return PEL_MIN_SIZE + pel_user_section_size(elog_data); } /* Converts an OPAL errorlog into a PEL formatted log */ int create_pel_log(struct errorlog *elog_data, char *pel_buffer, size_t pel_buffer_size) { int pel_offset = 0; if (pel_buffer_size < pel_size(elog_data)) { prerror("PEL buffer too small to create record\n"); return 0; } memset(pel_buffer, 0, pel_buffer_size); create_private_header_section(elog_data, pel_buffer, &pel_offset); create_user_header_section(elog_data, pel_buffer, &pel_offset); create_src_section(elog_data, pel_buffer, &pel_offset); create_extended_header_section(elog_data, pel_buffer, &pel_offset); create_mtms_section(elog_data, pel_buffer, &pel_offset); if (elog_data->user_section_count) create_user_defined_section(elog_data, pel_buffer, &pel_offset); return pel_offset; } skiboot-skiboot-5.1.13/core/platform.c000066400000000000000000000075551265204436200176630ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include struct platform platform; DEFINE_LOG_ENTRY(OPAL_RC_ABNORMAL_REBOOT, OPAL_PLATFORM_ERR_EVT, OPAL_CEC, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_ABNORMAL_POWER_OFF); /* * Various wrappers for platform functions */ static int64_t opal_cec_power_down(uint64_t request) { printf("OPAL: Shutdown request type 0x%llx...\n", request); console_complete_flush(); if (platform.cec_power_down) return platform.cec_power_down(request); return OPAL_SUCCESS; } opal_call(OPAL_CEC_POWER_DOWN, opal_cec_power_down, 1); static int64_t opal_cec_reboot(void) { printf("OPAL: Reboot request...\n"); console_complete_flush(); #ifdef ENABLE_FAST_RESET /* Try a fast reset first */ fast_reset(); #endif if (platform.cec_reboot) return platform.cec_reboot(); return OPAL_SUCCESS; } opal_call(OPAL_CEC_REBOOT, opal_cec_reboot, 0); static int64_t opal_cec_reboot2(uint32_t reboot_type, char *diag) { struct errorlog *buf; switch (reboot_type) { case OPAL_REBOOT_NORMAL: return opal_cec_reboot(); case OPAL_REBOOT_PLATFORM_ERROR: prlog(PR_EMERG, "OPAL: Reboot requested due to Platform error."); buf = opal_elog_create(&e_info(OPAL_RC_ABNORMAL_REBOOT), 0); if (buf) { log_append_msg(buf, "OPAL: Reboot requested due to Platform error."); if (diag) { /* Add user section "DESC" */ log_add_section(buf, 0x44455350); log_append_data(buf, diag, strlen(diag)); log_commit(buf); } } else { prerror("OPAL: failed to log an error\n"); } return xscom_trigger_xstop(); default: printf("OPAL: Unsupported reboot request %d\n", reboot_type); return OPAL_UNSUPPORTED; break; } return OPAL_SUCCESS; } opal_call(OPAL_CEC_REBOOT2, opal_cec_reboot2, 2); static void generic_platform_init(void) { force_dummy_console(); fake_rtc_init(); } static int64_t generic_cec_power_down(uint64_t request __unused) { if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) mambo_sim_exit(); return OPAL_UNSUPPORTED; } static struct platform generic_platform = { .name = "generic", .init = generic_platform_init, .cec_power_down = generic_cec_power_down, }; void probe_platform(void) { struct platform *platforms = &__platforms_start; unsigned int i; platform = generic_platform; for (i = 0; &platforms[i] < &__platforms_end; i++) { if (platforms[i].probe && platforms[i].probe()) { platform = platforms[i]; break; } } printf("PLAT: Detected %s platform\n", platform.name); } int start_preload_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len) { if (!platform.start_preload_resource) return OPAL_UNSUPPORTED; return platform.start_preload_resource(id, subid, buf, len); } int resource_loaded(enum resource_id id, uint32_t idx) { if (!platform.resource_loaded) return OPAL_SUCCESS; return platform.resource_loaded(id, idx); } int wait_for_resource_loaded(enum resource_id id, uint32_t idx) { int r = resource_loaded(id, idx); int waited = 0; while(r == OPAL_BUSY) { opal_run_pollers(); time_wait_ms_nopoll(5); waited+=5; r = resource_loaded(id, idx); } prlog(PR_TRACE, "PLATFORM: wait_for_resource_loaded %x/%x %u ms\n", id, idx, waited); return r; } skiboot-skiboot-5.1.13/core/pool.c000066400000000000000000000043761265204436200170060ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This file provides some functions to manage a pool of pre-allocated * objects. It also provides a method to reserve a pre-defined number * of objects for higher priorty requests. The allocations follow the * following rules: * * 1. An allocation will succeed at any priority if there is more than * the reserved number of objects free. * 2. Only high priority allocations will succeed when there are less * than the reserved number of objects free. * 3. When an allocation is freed it is always added to the high priority * pool if there are less than the reserved number of allocations * available. */ #include #include #include #include void* pool_get(struct pool *pool, enum pool_priority priority) { void *obj; if (!pool->free_count || ((pool->free_count <= pool->reserved) && priority == POOL_NORMAL)) return NULL; pool->free_count--; obj = (void *) list_pop_(&pool->free_list, 0); assert(obj); memset(obj, 0, pool->obj_size); return obj; } void pool_free_object(struct pool *pool, void *obj) { pool->free_count++; list_add_tail(&pool->free_list, (struct list_node *) (obj)); } int pool_init(struct pool *pool, size_t obj_size, int count, int reserved) { int i; if (obj_size < sizeof(struct list_node)) obj_size = sizeof(struct list_node); assert(count >= reserved); pool->buf = malloc(obj_size*count); if (!pool->buf) return -1; pool->obj_size = obj_size; pool->free_count = count; pool->reserved = reserved; list_head_init(&pool->free_list); for(i = 0; i < count; i++) list_add_tail(&pool->free_list, (struct list_node *) (pool->buf + obj_size*i)); return 0; } skiboot-skiboot-5.1.13/core/relocate.c000066400000000000000000000035111265204436200176210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include /* WARNING: This code is used to self-relocate, it cannot have any * global reference nor TOC reference. It's also called before BSS * is cleared. */ /* Called from head.S, thus no header. */ int relocate(uint64_t offset, struct elf64_dyn *dyn, struct elf64_rela *rela); /* Note: This code is simplified according to the assumptions * that our link address is 0 and we are running at the * target address already. */ int relocate(uint64_t offset, struct elf64_dyn *dyn, struct elf64_rela *rela) { uint64_t dt_rela = 0; uint64_t dt_relacount = 0; unsigned int i; /* Look for relocation table */ for (; dyn->d_tag != DT_NULL; dyn++) { if (dyn->d_tag == DT_RELA) dt_rela = dyn->d_val; else if (dyn->d_tag == DT_RELACOUNT) dt_relacount = dyn->d_val; } /* If we miss either rela or relacount, bail */ if (!dt_rela || !dt_relacount) return -1; /* Check if the offset is consistent */ if ((offset + dt_rela) != (uint64_t)rela) return -2; /* Perform relocations */ for (i = 0; i < dt_relacount; i++, rela++) { uint64_t *t; if (ELF64_R_TYPE(rela->r_info) != R_PPC64_RELATIVE) return -3; t = (uint64_t *)(rela->r_offset + offset); *t = rela->r_addend + offset; } return 0; } skiboot-skiboot-5.1.13/core/rtc.c000066400000000000000000000030201265204436200166060ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include static struct lock rtc_tod_lock = LOCK_UNLOCKED; static struct { struct tm tm; unsigned long tb; bool valid; } rtc_tod_cache; void rtc_cache_update(struct tm *tm) { lock(&rtc_tod_lock); rtc_tod_cache.tb = mftb(); rtc_tod_cache.tm = *tm; rtc_tod_cache.valid = true; unlock(&rtc_tod_lock); } int rtc_cache_get(struct tm *tm) { unsigned long cache_age_sec; lock(&rtc_tod_lock); if (!rtc_tod_cache.valid) { unlock(&rtc_tod_lock); return -1; } cache_age_sec = tb_to_msecs(mftb() - rtc_tod_cache.tb) / 1000; *tm = rtc_tod_cache.tm; unlock(&rtc_tod_lock); tm->tm_sec += cache_age_sec; mktime(tm); return 0; } int rtc_cache_get_datetime(uint32_t *year_month_day, uint64_t *hour_minute_second_millisecond) { struct tm tm; if (rtc_cache_get(&tm) < 0) return -1; tm_to_datetime(&tm, year_month_day, hour_minute_second_millisecond); return 0; } skiboot-skiboot-5.1.13/core/sensor.c000066400000000000000000000024141265204436200173350ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include struct dt_node *sensor_node; static int64_t opal_sensor_read(uint32_t sensor_hndl, int token, uint32_t *sensor_data) { if (sensor_is_dts(sensor_hndl)) return dts_sensor_read(sensor_hndl, sensor_data); if (platform.sensor_read) return platform.sensor_read(sensor_hndl, token, sensor_data); return OPAL_UNSUPPORTED; } void sensor_init(void) { sensor_node = dt_new(opal_node, "sensors"); dt_add_property_string(sensor_node, "compatible", "ibm,opal-sensor"); dts_sensor_create_nodes(sensor_node); /* Register OPAL interface */ opal_register(OPAL_SENSOR_READ, opal_sensor_read, 3); } skiboot-skiboot-5.1.13/core/stack.c000066400000000000000000000123431265204436200171330ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define STACK_BUF_ENTRIES 60 static struct bt_entry bt_buf[STACK_BUF_ENTRIES]; extern uint32_t _stext, _etext; /* Dumps backtrace to buffer */ void __nomcount __backtrace(struct bt_entry *entries, unsigned int *count) { unsigned int room = *count; unsigned long *fp = __builtin_frame_address(1); *count = 0; while(room) { if (!fp || (unsigned long)fp > top_of_ram) break; entries->sp = (unsigned long)fp; entries->pc = fp[2]; entries++; *count = (*count) + 1; room--; fp = (unsigned long *)fp[0]; } } void __print_backtrace(unsigned int pir, struct bt_entry *entries, unsigned int count, char *out_buf, unsigned int *len, bool symbols) { static char bt_text_buf[4096]; int i, l = 0, max; char *buf = out_buf; unsigned long bottom, top, tbot, ttop, saddr = 0; char *sym = NULL, *sym_end = NULL; char mark; if (!out_buf) { buf = bt_text_buf; max = sizeof(bt_text_buf) - 16; } else max = *len - 1; bottom = cpu_stack_bottom(pir); top = cpu_stack_top(pir); tbot = SKIBOOT_BASE; ttop = (unsigned long)&_etext; l += snprintf(buf, max, "CPU %04x Backtrace:\n", pir); for (i = 0; i < count && l < max; i++) { if (entries->sp < bottom || entries->sp > top) mark = '!'; else if (entries->pc < tbot || entries->pc > ttop) mark = '*'; else mark = ' '; if (symbols) saddr = get_symbol(entries->pc, &sym, &sym_end); else saddr = 0; l += snprintf(buf + l, max - l, " S: %016lx R: %016lx %c ", entries->sp, entries->pc, mark); while(saddr && sym < sym_end && l < max) buf[l++] = *(sym++); if (sym && l < max) l += snprintf(buf + l, max - l, "+0x%lx\n", entries->pc - saddr); else l += snprintf(buf + l, max - l, "\n"); entries++; } if (!out_buf) write(stdout->fd, bt_text_buf, l); buf[l++] = 0; if (len) *len = l; } void backtrace(void) { unsigned int ents = STACK_BUF_ENTRIES; __backtrace(bt_buf, &ents); __print_backtrace(mfspr(SPR_PIR), bt_buf, ents, NULL, NULL, true); } void __noreturn __nomcount __stack_chk_fail(void); void __noreturn __nomcount __stack_chk_fail(void) { prlog(PR_EMERG, "Stack corruption detected !\n"); abort(); } #ifdef STACK_CHECK_ENABLED static int64_t lowest_stack_mark = LONG_MAX; static struct lock stack_check_lock = LOCK_UNLOCKED; void __nomcount __mcount_stack_check(uint64_t sp, uint64_t lr); void __nomcount __mcount_stack_check(uint64_t sp, uint64_t lr) { struct cpu_thread *c = this_cpu(); uint64_t base = (uint64_t)c; uint64_t bot = base + sizeof(struct cpu_thread); int64_t mark = sp - bot; uint64_t top = base + NORMAL_STACK_SIZE; /* * Don't re-enter on this CPU or don't enter at all if somebody * has spotted an overflow */ if (c->in_mcount) return; c->in_mcount = true; /* Capture lowest stack for this thread */ if (mark < c->stack_bot_mark) { unsigned int count = CPU_BACKTRACE_SIZE; lock(&stack_check_lock); c->stack_bot_mark = mark; c->stack_bot_pc = lr; c->stack_bot_tok = c->current_token; __backtrace(c->stack_bot_bt, &count); c->stack_bot_bt_count = count; unlock(&stack_check_lock); } /* Stack is within bounds ? check for warning and bail */ if (sp >= (bot + STACK_SAFETY_GAP) && sp < top) { if (mark < STACK_WARNING_GAP) { prlog(PR_EMERG, "CPU %04x Stack usage danger !" " pc=%08llx sp=%08llx (gap=%lld) token=%lld\n", c->pir, lr, sp, mark, c->current_token); backtrace(); } c->in_mcount = false; return; } prlog(PR_EMERG, "CPU %04x Stack overflow detected !" " pc=%08llx sp=%08llx (gap=%lld) token=%lld\n", c->pir, lr, sp, mark, c->current_token); abort(); } void check_stacks(void) { struct cpu_thread *c, *lowest = NULL; /* We should never call that from mcount */ assert(!this_cpu()->in_mcount); /* Mark ourselves "in_mcount" to avoid deadlock on stack * check lock */ this_cpu()->in_mcount = true; for_each_cpu(c) { if (!c->stack_bot_mark || c->stack_bot_mark >= lowest_stack_mark) continue; lock(&stack_check_lock); if (c->stack_bot_mark < lowest_stack_mark) { lowest = c; lowest_stack_mark = c->stack_bot_mark; } unlock(&stack_check_lock); } if (lowest) { lock(&stack_check_lock); prlog(PR_NOTICE, "CPU %04x lowest stack mark %lld bytes left" " pc=%08llx token=%lld\n", lowest->pir, lowest->stack_bot_mark, lowest->stack_bot_pc, lowest->stack_bot_tok); __print_backtrace(lowest->pir, lowest->stack_bot_bt, lowest->stack_bot_bt_count, NULL, NULL, true); unlock(&stack_check_lock); } this_cpu()->in_mcount = false; } #endif /* STACK_CHECK_ENABLED */ skiboot-skiboot-5.1.13/core/test/000077500000000000000000000000001265204436200166365ustar00rootroot00000000000000skiboot-skiboot-5.1.13/core/test/Makefile.check000066400000000000000000000044511265204436200213560ustar00rootroot00000000000000# -*-Makefile-*- CORE_TEST := core/test/run-device \ core/test/run-mem_region \ core/test/run-malloc \ core/test/run-malloc-speed \ core/test/run-mem_region_init \ core/test/run-mem_region_next \ core/test/run-mem_region_release_unused \ core/test/run-mem_region_release_unused_noalloc \ core/test/run-mem_region_reservations \ core/test/run-mem_range_is_reserved \ core/test/run-nvram-format \ core/test/run-trace core/test/run-msg \ core/test/run-pel \ core/test/run-pool \ core/test/run-time-utils \ core/test/run-timer CORE_TEST_NOSTUB := core/test/run-console-log CORE_TEST_NOSTUB += core/test/run-console-log-buf-overrun CORE_TEST_NOSTUB += core/test/run-console-log-pr_fmt LCOV_EXCLUDE += $(CORE_TEST:%=%.c) core/test/stubs.c LCOV_EXCLUDE += $(CORE_TEST_NOSTUB:%=%.c) /usr/include/* check: $(CORE_TEST:%=%-check) $(CORE_TEST:%=%-gcov-run) check: $(CORE_TEST_NOSTUB:%=%-check) $(CORE_TEST_NOSTUB:%=%-gcov-run) coverage: $(CORE_TEST:%=%-gcov-run) coverage: $(CORE_TEST_NOSTUB:%=%-gcov-run) $(CORE_TEST:%=%-gcov-run) : %-run: % $(call Q, TEST-COVERAGE ,$< , $<) $(CORE_TEST_NOSTUB:%=%-gcov-run) : %-run: % $(call Q, TEST-COVERAGE ,$< , $<) $(CORE_TEST:%=%-check) : %-check: % $(call Q, RUN-TEST ,$(VALGRIND) $<, $<) $(CORE_TEST_NOSTUB:%=%-check) : %-check: % $(call Q, RUN-TEST ,$(VALGRIND) $<, $<) core/test/stubs.o: core/test/stubs.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -g -c -o $@ $<, $<) $(CORE_TEST) : core/test/stubs.o $(CORE_TEST) : % : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -I libfdt -o $@ $< core/test/stubs.o, $<) $(CORE_TEST_NOSTUB) : % : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -I libfdt -o $@ $< , $<) $(CORE_TEST:%=%-gcov): %-gcov : %.c % $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -O0 -g -I include -I . -I libfdt -lgcov -o $@ $< core/test/stubs.o, $<) $(CORE_TEST_NOSTUB:%=%-gcov) : %-gcov : %.c % $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -O0 -g -I include -I . -I libfdt -lgcov -o $@ $< , $<) -include $(wildcard core/test/*.d) clean: core-test-clean core-test-clean: $(RM) -f core/test/*.[od] $(CORE_TEST) $(CORE_TEST:%=%-gcov) $(RM) -f $(CORE_TEST_NOSTUB) $(CORE_TEST_NOSTUB:%=%-gcov) $(RM) -f *.gcda *.gcno skiboot.info $(RM) -rf coverage-report skiboot-skiboot-5.1.13/core/test/run-console-log-buf-overrun.c000066400000000000000000000054261265204436200243040ustar00rootroot00000000000000/* Copyright 2014-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define __TEST__ #define CHECK_BUF_ASSERT(buf, str) \ assert(memcmp(buf, str, strlen(str)) == 0) #define CHECK_ASSERT(str) \ CHECK_BUF_ASSERT(console_buffer, str) int huge_tb; static inline unsigned long mftb(void) { /* * return huge value for TB that overrun tmp[16] buffer defined * in print_itoa(). */ if (huge_tb) return 1223372515963611388; else return 42; } #include "../../libc/include/stdio.h" #include "../console-log.c" #include "../../libc/stdio/snprintf.c" #include "../../libc/stdio/vsnprintf.c" char console_buffer[4096]; struct debug_descriptor debug_descriptor; bool flushed_to_drivers; ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count) { flushed_to_drivers = flush_to_drivers; memcpy(console_buffer, buf, count); return count; } int main(void) { unsigned long value = 0xffffffffffffffff; char *ptr = console_buffer; debug_descriptor.console_log_levels = 0x75; /* Test for huge TB value. */ huge_tb = 1; prlog(PR_EMERG, "Hello World"); CHECK_ASSERT("[1223372515963611388,0] Hello World"); memset(console_buffer, 0, sizeof(console_buffer)); /* Test for normal TB with huge unsigned long value */ huge_tb = 0; prlog(PR_EMERG, "Hello World %lu", value); CHECK_ASSERT("[42,0] Hello World 18446744073709551615"); printf("Hello World %lu", value); CHECK_ASSERT("[42,5] Hello World 18446744073709551615"); /* * Test string of size > 320 * * core/console-log.c:vprlog() uses buffer[320] to print message * Try printing more than 320 bytes to test stack corruption. * You would see Segmentation fault on stack corruption. */ prlog(PR_EMERG, "%330s", "Hello World"); memset(console_buffer, 0, sizeof(console_buffer)); /* * Test boundary condition. * * Print string of exact size 320. We should see string truncated * with console_buffer[319] == '\0'. */ memset(console_buffer, 0, sizeof(console_buffer)); prlog(PR_EMERG, "%313s", "Hello World"); assert(console_buffer[319] == 0); /* compare truncated string */ ptr += 320 - strlen("Hello World"); CHECK_BUF_ASSERT(ptr, "Hello Worl"); return 0; } skiboot-skiboot-5.1.13/core/test/run-console-log-pr_fmt.c000066400000000000000000000037031265204436200233150ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define __TEST__ static inline unsigned long mftb(void) { return 42; } #define pr_fmt(f) "PREFIX: " f #include "../../libc/include/stdio.h" #include "../console-log.c" #include "../../libc/stdio/snprintf.c" #include "../../libc/stdio/vsnprintf.c" struct debug_descriptor debug_descriptor; bool flushed_to_drivers; char console_buffer[4096]; ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count) { flushed_to_drivers = flush_to_drivers; memcpy(console_buffer, buf, count); return count; } int main(void) { debug_descriptor.console_log_levels = 0x75; prlog(PR_EMERG, "Hello World"); assert(memcmp(console_buffer, "[42,0] PREFIX: Hello World", strlen("[42,0] PREFIX: Hello World")) == 0); assert(flushed_to_drivers==true); memset(console_buffer, 0, sizeof(console_buffer)); // Below log level prlog(PR_TRACE, "Hello World"); assert(console_buffer[0] == 0); // Should not be flushed to console prlog(PR_DEBUG, "Hello World"); assert(memcmp(console_buffer, "[42,7] PREFIX: Hello World", strlen("[42,7] PREFIX: Hello World")) == 0); assert(flushed_to_drivers==false); printf("Hello World"); assert(memcmp(console_buffer, "[42,5] PREFIX: Hello World", strlen("[42,5] PREFIX: Hello World")) == 0); assert(flushed_to_drivers==true); return 0; } skiboot-skiboot-5.1.13/core/test/run-console-log.c000066400000000000000000000034731265204436200220340ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define __TEST__ #define _printf printf static inline unsigned long mftb(void) { return 42; } int _printf(const char* fmt, ...); #include "../console-log.c" struct debug_descriptor debug_descriptor; bool flushed_to_drivers; char console_buffer[4096]; ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count) { flushed_to_drivers = flush_to_drivers; memcpy(console_buffer, buf, count); return count; } int main(void) { debug_descriptor.console_log_levels = 0x75; prlog(PR_EMERG, "Hello World"); assert(memcmp(console_buffer, "[42,0] Hello World", strlen("[42,0] Hello World")) == 0); assert(flushed_to_drivers==true); memset(console_buffer, 0, sizeof(console_buffer)); // Below log level prlog(PR_TRACE, "Hello World"); assert(console_buffer[0] == 0); // Should not be flushed to console prlog(PR_DEBUG, "Hello World"); assert(memcmp(console_buffer, "[42,7] Hello World", strlen("[42,7] Hello World")) == 0); assert(flushed_to_drivers==false); printf("Hello World"); assert(memcmp(console_buffer, "[42,5] Hello World", strlen("[42,5] Hello World")) == 0); assert(flushed_to_drivers==true); return 0; } skiboot-skiboot-5.1.13/core/test/run-device.c000066400000000000000000000265401265204436200210520ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include /* Override this for testing. */ #define is_rodata(p) fake_is_rodata(p) char __rodata_start[16]; #define __rodata_end (__rodata_start + sizeof(__rodata_start)) static inline bool fake_is_rodata(const void *p) { return ((char *)p >= __rodata_start && (char *)p < __rodata_end); } #define zalloc(bytes) calloc((bytes), 1) #include "../device.c" #include "../../ccan/list/list.c" /* For list_check */ #include #include "../../test/dt_common.c" static void check_path(const struct dt_node *node, const char * expected_path) { char * path; path = dt_get_path(node); if (strcmp(path, expected_path) != 0) { printf("check_path: expected %s, got %s\n", expected_path, path); } assert(strcmp(path, expected_path) == 0); free(path); } /* constructs a random nodes only device tree */ static void build_tree(int max_depth, int min_depth, struct dt_node *parent) { char name[64]; int i; for (i = 0; i < max_depth; i++) { struct dt_node *new; snprintf(name, sizeof name, "prefix@%.8x", rand()); new = dt_new(parent, name); if(max_depth > min_depth) build_tree(max_depth - 1, min_depth, new); } } static bool is_sorted(const struct dt_node *root) { struct dt_node *end = list_tail(&root->children, struct dt_node, list); struct dt_node *node; dt_for_each_child(root, node) { struct dt_node *next = list_entry(node->list.next, struct dt_node, list); /* current node must be "less than" the next node */ if (node != end && dt_cmp_subnodes(node, next) != -1) { printf("nodes '%s' and '%s' out of order\n", node->name, next->name); return false; } if (!is_sorted(node)) return false; } return true; } int main(void) { struct dt_node *root, *c1, *c2, *gc1, *gc2, *gc3, *ggc1; struct dt_node *addrs, *addr1, *addr2; struct dt_node *i; const struct dt_property *p; struct dt_property *p2; unsigned int n; char *s; size_t sz; u32 phandle; root = dt_new_root(""); assert(!list_top(&root->properties, struct dt_property, list)); check_path(root, "/"); c1 = dt_new(root, "c1"); assert(!list_top(&c1->properties, struct dt_property, list)); check_path(c1, "/c1"); assert(dt_find_by_name(root, "c1") == c1); assert(dt_find_by_path(root, "/c1") == c1); c2 = dt_new(root, "c2"); assert(!list_top(&c2->properties, struct dt_property, list)); check_path(c2, "/c2"); assert(dt_find_by_name(root, "c2") == c2); assert(dt_find_by_path(root, "/c2") == c2); gc1 = dt_new(c1, "gc1"); assert(!list_top(&gc1->properties, struct dt_property, list)); check_path(gc1, "/c1/gc1"); assert(dt_find_by_name(root, "gc1") == gc1); assert(dt_find_by_path(root, "/c1/gc1") == gc1); gc2 = dt_new(c1, "gc2"); assert(!list_top(&gc2->properties, struct dt_property, list)); check_path(gc2, "/c1/gc2"); assert(dt_find_by_name(root, "gc2") == gc2); assert(dt_find_by_path(root, "/c1/gc2") == gc2); gc3 = dt_new(c1, "gc3"); assert(!list_top(&gc3->properties, struct dt_property, list)); check_path(gc3, "/c1/gc3"); assert(dt_find_by_name(root, "gc3") == gc3); assert(dt_find_by_path(root, "/c1/gc3") == gc3); ggc1 = dt_new(gc1, "ggc1"); assert(!list_top(&ggc1->properties, struct dt_property, list)); check_path(ggc1, "/c1/gc1/ggc1"); assert(dt_find_by_name(root, "ggc1") == ggc1); assert(dt_find_by_path(root, "/c1/gc1/ggc1") == ggc1); addrs = dt_new(root, "addrs"); assert(!list_top(&addrs->properties, struct dt_property, list)); check_path(addrs, "/addrs"); assert(dt_find_by_name(root, "addrs") == addrs); assert(dt_find_by_path(root, "/addrs") == addrs); addr1 = dt_new_addr(addrs, "addr", 0x1337); assert(!list_top(&addr1->properties, struct dt_property, list)); check_path(addr1, "/addrs/addr@1337"); assert(dt_find_by_name(root, "addr@1337") == addr1); assert(dt_find_by_path(root, "/addrs/addr@1337") == addr1); addr2 = dt_new_2addr(addrs, "2addr", 0xdead, 0xbeef); assert(!list_top(&addr2->properties, struct dt_property, list)); check_path(addr2, "/addrs/2addr@dead,beef"); assert(dt_find_by_name(root, "2addr@dead,beef") == addr2); assert(dt_find_by_path(root, "/addrs/2addr@dead,beef") == addr2); /* Test walking the tree, checking and setting values */ for (n = 0, i = dt_first(root); i; i = dt_next(root, i), n++) { assert(!list_top(&i->properties, struct dt_property, list)); dt_add_property_cells(i, "visited", 1); } assert(n == 9); for (n = 0, i = dt_first(root); i; i = dt_next(root, i), n++) { p = list_top(&i->properties, struct dt_property, list); assert(strcmp(p->name, "visited") == 0); assert(p->len == sizeof(u32)); assert(fdt32_to_cpu(*(u32 *)p->prop) == 1); } assert(n == 9); /* Test cells */ dt_add_property_cells(c1, "some-property", 1, 2, 3); p = dt_find_property(c1, "some-property"); assert(p); assert(strcmp(p->name, "some-property") == 0); assert(p->len == sizeof(u32) * 3); assert(fdt32_to_cpu(*(u32 *)p->prop) == 1); assert(dt_prop_get_cell(c1, "some-property", 0) == 1); assert(fdt32_to_cpu(*((u32 *)p->prop + 1)) == 2); assert(dt_prop_get_cell(c1, "some-property", 1) == 2); assert(fdt32_to_cpu(*((u32 *)p->prop + 2)) == 3); assert(dt_prop_get_cell_def(c1, "some-property", 2, 42) == 3); assert(dt_prop_get_cell_def(c1, "not-a-property", 2, 42) == 42); /* Test u64s */ dt_add_property_u64s(c2, "some-property", (2LL << 33), (3LL << 33), (4LL << 33)); p = dt_find_property(c2, "some-property"); assert(p); assert(p->len == sizeof(u64) * 3); assert(fdt64_to_cpu(*(u64 *)p->prop) == (2LL << 33)); assert(fdt64_to_cpu(*((u64 *)p->prop + 1)) == (3LL << 33)); assert(fdt64_to_cpu(*((u64 *)p->prop + 2)) == (4LL << 33)); /* Test u32/u64 get defaults */ assert(dt_prop_get_u32_def(c1, "u32", 42) == 42); dt_add_property_cells(c1, "u32", 1337); assert(dt_prop_get_u32_def(c1, "u32", 42) == 1337); assert(dt_prop_get_u32(c1, "u32") == 1337); assert(dt_prop_get_u64_def(c1, "u64", (42LL << 42)) == (42LL << 42)); dt_add_property_u64s(c1, "u64", (1337LL << 42)); assert(dt_prop_get_u64_def(c1, "u64", (42LL << 42)) == (1337LL << 42)); assert(dt_prop_get_u64(c1, "u64") == (1337LL << 42)); /* Test freeing a single node */ assert(!list_empty(&gc1->children)); dt_free(ggc1); assert(list_empty(&gc1->children)); /* Test rodata logic. */ assert(!is_rodata("hello")); assert(is_rodata(__rodata_start)); strcpy(__rodata_start, "name"); ggc1 = dt_new(root, __rodata_start); assert(ggc1->name == __rodata_start); /* Test string node. */ dt_add_property_string(ggc1, "somestring", "someval"); assert(dt_has_node_property(ggc1, "somestring", "someval")); assert(!dt_has_node_property(ggc1, "somestrin", "someval")); assert(!dt_has_node_property(ggc1, "somestring", "someva")); assert(!dt_has_node_property(ggc1, "somestring", "somevale")); /* Test nstr, which allows for non-null-terminated inputs */ dt_add_property_nstr(ggc1, "nstring", "somevalue_long", 7); assert(dt_has_node_property(ggc1, "nstring", "someval")); assert(!dt_has_node_property(ggc1, "nstring", "someva")); assert(!dt_has_node_property(ggc1, "nstring", "somevalue_long")); /* Test multiple strings */ dt_add_property_strings(ggc1, "somestrings", "These", "are", "strings!"); p = dt_find_property(ggc1, "somestrings"); assert(p); assert(p->len == sizeof(char) * (6 + 4 + 9)); s = (char *)p->prop; assert(strcmp(s, "These") == 0); assert(strlen(s) == 5); s += 6; assert(strcmp(s, "are") == 0); assert(strlen(s) == 3); s += 4; assert(strcmp(s, "strings!") == 0); assert(strlen(s) == 8); s += 9; assert(s == (char *)p->prop + p->len); assert(dt_prop_find_string(p, "These")); /* dt_prop_find_string is case insensitve */ assert(dt_prop_find_string(p, "ARE")); assert(!dt_prop_find_string(p, "integers!")); /* And always returns false for NULL properties */ assert(!dt_prop_find_string(NULL, "anything!")); /* Test more get/get_def varieties */ assert(dt_prop_get_def(c1, "does-not-exist", NULL) == NULL); sz = 0xbad; assert(dt_prop_get_def_size(c1, "does-not-exist", NULL, &sz) == NULL); assert(sz == 0); dt_add_property_string(c1, "another-property", "xyzzy"); assert(dt_prop_get_def(c1, "another-property", NULL) != NULL); assert(strcmp(dt_prop_get(c1, "another-property"), "xyzzy") == 0); n = 0xbad; assert(dt_prop_get_def_size(c1, "another-property", NULL, &sz) != NULL); assert(sz == strlen("xyzzy") + 1); /* Test resizing property. */ p = p2 = __dt_find_property(c1, "some-property"); assert(p); n = p2->len; while (p2 == p) { n *= 2; dt_resize_property(&p2, n); } assert(dt_find_property(c1, "some-property") == p2); list_check(&c1->properties, "properties after resizing"); dt_del_property(c1, p2); list_check(&c1->properties, "properties after delete"); /* No leaks for valgrind! */ dt_free(root); /* Test compatible and chip id. */ root = dt_new_root(""); c1 = dt_new(root, "chip1"); dt_add_property_cells(c1, "ibm,chip-id", 0xcafe); assert(dt_get_chip_id(c1) == 0xcafe); dt_add_property_strings(c1, "compatible", "specific-fake-chip", "generic-fake-chip"); assert(dt_node_is_compatible(c1, "specific-fake-chip")); assert(dt_node_is_compatible(c1, "generic-fake-chip")); c2 = dt_new(root, "chip2"); dt_add_property_cells(c2, "ibm,chip-id", 0xbeef); assert(dt_get_chip_id(c2) == 0xbeef); dt_add_property_strings(c2, "compatible", "specific-fake-bus", "generic-fake-bus"); gc1 = dt_new(c1, "coprocessor1"); dt_add_property_strings(gc1, "compatible", "specific-fake-coprocessor"); gc2 = dt_new(c1, "node-without-compatible"); assert(__dt_find_property(gc2, "compatible") == NULL); assert(!dt_node_is_compatible(gc2, "any-property")); assert(dt_find_compatible_node(root, NULL, "generic-fake-bus") == c2); assert(dt_find_compatible_node(root, c2, "generic-fake-bus") == NULL); /* we can find the coprocessor once on the cpu */ assert(dt_find_compatible_node_on_chip(root, NULL, "specific-fake-coprocessor", 0xcafe) == gc1); assert(dt_find_compatible_node_on_chip(root, gc1, "specific-fake-coprocessor", 0xcafe) == NULL); /* we can't find the coprocessor on the bus */ assert(dt_find_compatible_node_on_chip(root, NULL, "specific-fake-coprocessor", 0xbeef) == NULL); /* Test phandles. We override the automatically generated one. */ phandle = 0xf00; dt_add_property(gc2, "phandle", (const void *)&phandle, 4); assert(last_phandle == 0xf00); assert(dt_find_by_phandle(root, 0xf00) == gc2); assert(dt_find_by_phandle(root, 0xf0f) == NULL); dt_free(root); /* basic sorting */ root = dt_new_root("rewt"); dt_new(root, "a@1"); dt_new(root, "a@2"); dt_new(root, "a@3"); dt_new(root, "a@4"); dt_new(root, "b@4"); dt_new(root, "c@4"); assert(is_sorted(root)); dt_free(root); /* Test child node sorting */ root = dt_new_root("test root"); build_tree(5, 3, root); if (!is_sorted(root)) { dump_dt(root, 1, false); } assert(is_sorted(root)); dt_free(root); return 0; } skiboot-skiboot-5.1.13/core/test/run-malloc-speed.c000066400000000000000000000043531265204436200221560ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include /* Use these before we undefine them below. */ static inline void *real_malloc(size_t size) { return malloc(size); } static inline void real_free(void *p) { return free(p); } #include /* We need mem_region to accept __location__ */ #define is_rodata(p) true #include "../malloc.c" #include "../mem_region.c" #include "../device.c" #undef malloc #undef free #undef realloc #include #include char __rodata_start[1], __rodata_end[1]; struct dt_node *dt_root; void lock(struct lock *l) { assert(!l->lock_val); l->lock_val = 1; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val = 0; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 27 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) #define NUM_ALLOCS 4096 int main(void) { uint64_t i, len; void **p = real_malloc(sizeof(void*)*NUM_ALLOCS); assert(p); /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (unsigned long)real_malloc(skiboot_heap.len); len = skiboot_heap.len / NUM_ALLOCS - sizeof(struct alloc_hdr); for (i = 0; i < NUM_ALLOCS; i++) { p[i] = __malloc(len, __location__); assert(p[i] > region_start(&skiboot_heap)); assert(p[i] + len <= region_start(&skiboot_heap) + skiboot_heap.len); } assert(mem_check(&skiboot_heap)); assert(skiboot_heap.free_list_lock.lock_val == 0); free(region_start(&skiboot_heap)); real_free(p); return 0; } skiboot-skiboot-5.1.13/core/test/run-malloc.c000066400000000000000000000102351265204436200210540ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include /* Use these before we undefine them below. */ static inline void *real_malloc(size_t size) { return malloc(size); } static inline void real_free(void *p) { return free(p); } #undef malloc #undef free #undef realloc #include #define is_rodata(p) true #include "../mem_region.c" #include "../malloc.c" #include "../device.c" #include "mem_region-malloc.h" #define TEST_HEAP_ORDER 12 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) struct dt_node *dt_root; void lock(struct lock *l) { assert(!l->lock_val); l->lock_val = 1; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val = 0; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } static bool heap_empty(void) { const struct alloc_hdr *h = region_start(&skiboot_heap); return h->num_longs == skiboot_heap.len / sizeof(long); } int main(void) { char *test_heap = real_malloc(TEST_HEAP_SIZE); char *p, *p2, *p3, *p4; char *pr; size_t i; /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (unsigned long)test_heap; skiboot_heap.len = TEST_HEAP_SIZE; /* Allocations of various sizes. */ for (i = 0; i < TEST_HEAP_ORDER; i++) { p = malloc(1ULL << i); assert(p); assert(p > (char *)test_heap); assert(p + (1ULL << i) <= (char *)test_heap + TEST_HEAP_SIZE); assert(!skiboot_heap.free_list_lock.lock_val); free(p); assert(!skiboot_heap.free_list_lock.lock_val); assert(heap_empty()); } /* Realloc as malloc. */ skiboot_heap.free_list_lock.lock_val = 0; p = realloc(NULL, 100); assert(p); assert(!skiboot_heap.free_list_lock.lock_val); /* Realloc as free. */ p = realloc(p, 0); assert(!p); assert(!skiboot_heap.free_list_lock.lock_val); assert(heap_empty()); /* Realloc longer. */ p = realloc(NULL, 100); assert(p); assert(!skiboot_heap.free_list_lock.lock_val); p2 = realloc(p, 200); assert(p2 == p); assert(!skiboot_heap.free_list_lock.lock_val); free(p2); assert(!skiboot_heap.free_list_lock.lock_val); assert(heap_empty()); /* Realloc shorter. */ skiboot_heap.free_list_lock.lock_val = 0; p = realloc(NULL, 100); assert(!skiboot_heap.free_list_lock.lock_val); assert(p); p2 = realloc(p, 1); assert(!skiboot_heap.free_list_lock.lock_val); assert(p2 == p); free(p2); assert(!skiboot_heap.free_list_lock.lock_val); assert(heap_empty()); /* zalloc failure */ p2 = zalloc(TEST_HEAP_SIZE * 2); assert(p2 == NULL); /* Realloc with move. */ p2 = malloc(TEST_HEAP_SIZE - 64 - sizeof(struct alloc_hdr)*2); memset(p2, 'a', TEST_HEAP_SIZE - 64 - sizeof(struct alloc_hdr)*2); assert(p2); p = malloc(64); memset(p, 'b', 64); p[63] = 'c'; assert(p); free(p2); p2 = realloc(p, 128); assert(p2 != p); assert(p2[63] == 'c'); free(p2); assert(heap_empty()); assert(!skiboot_heap.free_list_lock.lock_val); /* Realloc with failure to allocate new size */ p2 = malloc(TEST_HEAP_SIZE - sizeof(struct alloc_hdr)*2); assert(p2); memset(p2, 'a', TEST_HEAP_SIZE - sizeof(struct alloc_hdr)*2); p = p2; p2 = realloc(p, TEST_HEAP_SIZE*2); assert(p2==NULL); memset(p, 'b', TEST_HEAP_SIZE - sizeof(struct alloc_hdr)*2); free(p); /* Reproduce bug BZ109128/SW257364 */ p = malloc(100); p2 = malloc(100); p3 = malloc(100); p4 = malloc(100); free(p2); pr = realloc(p,216); assert(pr); free(p3); free(pr); free(p4); assert(heap_empty()); assert(!skiboot_heap.free_list_lock.lock_val); real_free(test_heap); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_range_is_reserved.c000066400000000000000000000116771265204436200241440ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include static void *real_malloc(size_t size) { return malloc(size); } static void real_free(void *p) { return free(p); } #undef malloc #undef free #undef realloc #include #include /* We need mem_region to accept __location__ */ #define is_rodata(p) true #include "../mem_region.c" #include "../malloc.c" /* But we need device tree to make copies of names. */ #undef is_rodata #define is_rodata(p) false #include "../../libc/string/strdup.c" #include "../device.c" #include #include void lock(struct lock *l) { assert(!l->lock_val); l->lock_val++; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val--; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 14 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) static void add_mem_node(uint64_t start, uint64_t len) { struct dt_node *mem; u64 reg[2]; char *name; name = (char*)malloc(sizeof("memory@") + STR_MAX_CHARS(reg[0])); assert(name); /* reg contains start and length */ reg[0] = cpu_to_be64(start); reg[1] = cpu_to_be64(len); sprintf(name, "memory@%llx", (long long)start); mem = dt_new(dt_root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property(mem, "reg", reg, sizeof(reg)); free(name); } void add_chip_dev_associativity(struct dt_node *dev __attribute__((unused))) { } struct test_region { uint64_t start; uint64_t end; }; static struct test { struct test_region regions[3]; bool reserved; } tests[] = { /* empty region set */ { { { 0 } }, false }, /* single exact match */ { { { 0x1000, 0x2000 }, }, true }, /* overlap downwards */ { { { 0x0fff, 0x2000 }, }, true }, /* overlap upwards */ { { { 0x1000, 0x2001 }, }, true }, /* missing first byte */ { { { 0x1001, 0x2000 }, }, false }, /* missing last byte */ { { { 0x1000, 0x1fff }, }, false }, /* two regions, full coverage, split before start of range */ { { { 0x0500, 0x1000 }, { 0x1000, 0x2500 } }, true }, /* two regions, full coverage, split after start of range */ { { { 0x0500, 0x1001 }, { 0x1001, 0x2500 } }, true }, /* two regions, full coverage, split at middle of range */ { { { 0x0500, 0x1500 }, { 0x1500, 0x2500 } }, true }, /* two regions, full coverage, split before end of range */ { { { 0x0500, 0x1fff }, { 0x1fff, 0x2500 } }, true }, /* two regions, full coverage, split after end of range */ { { { 0x0500, 0x2000 }, { 0x2000, 0x2500 } }, true }, /* two regions, missing byte in middle of range */ { { { 0x0500, 0x14ff }, { 0x1500, 0x2500 } }, false }, /* two regions, missing byte after start of range */ { { { 0x0500, 0x1000 }, { 0x1001, 0x2500 } }, false }, /* two regions, missing byte before end of range */ { { { 0x0500, 0x1fff }, { 0x2000, 0x2500 } }, false }, }; static void run_test(struct test *test) { struct test_region *r; bool reserved; list_head_init(®ions); mem_region_init(); /* create our reservations */ for (r = test->regions; r->start; r++) mem_reserve_hw("r", r->start, r->end - r->start); reserved = mem_range_is_reserved(0x1000, 0x1000); if (reserved != test->reserved) { struct mem_region *r; fprintf(stderr, "test failed; got %s, expected %s\n", reserved ? "reserved" : "unreserved", test->reserved ? "reserved" : "unreserved"); fprintf(stderr, "reserved regions:\n"); list_for_each(®ions, r, list) { fprintf(stderr, "\t: %08"PRIx64"[%08"PRIx64"] %s\n", r->start, r->len, r->name); } exit(EXIT_FAILURE); } } int main(void) { unsigned int i; void *buf; /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (long)real_malloc(TEST_HEAP_SIZE); skiboot_heap.len = TEST_HEAP_SIZE; /* shift the OS reserve area out of the way of our playground */ skiboot_os_reserve.start = 0x100000; skiboot_os_reserve.len = 0x1000; dt_root = dt_new_root(""); dt_add_property_cells(dt_root, "#address-cells", 2); dt_add_property_cells(dt_root, "#size-cells", 2); buf = real_malloc(1024*1024); add_mem_node((unsigned long)buf, 1024*1024); for (i = 0; i < ARRAY_SIZE(tests); i++) run_test(&tests[i]); dt_free(dt_root); real_free(buf); real_free((void *)(long)skiboot_heap.start); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_region.c000066400000000000000000000163721265204436200217360ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include #include /* Use these before we override definitions below. */ static void *real_malloc(size_t size) { return malloc(size); } static inline void real_free(void *p) { return free(p); } #undef malloc #undef free #undef realloc #include #define is_rodata(p) true #include "../mem_region.c" #include "../malloc.c" #include "../device.c" #include #include struct dt_node *dt_root; void lock(struct lock *l) { assert(!l->lock_val); l->lock_val++; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val--; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 12 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) static bool heap_empty(void) { const struct alloc_hdr *h = region_start(&skiboot_heap); return h->num_longs == skiboot_heap.len / sizeof(long); } int main(void) { char *test_heap; void *p, *ptrs[100]; size_t i; struct mem_region *r; /* Use malloc for the heap, so valgrind can find issues. */ test_heap = real_malloc(TEST_HEAP_SIZE); skiboot_heap.start = (unsigned long)test_heap; skiboot_heap.len = TEST_HEAP_SIZE; lock(&skiboot_heap.free_list_lock); /* Allocations of various sizes. */ for (i = 0; i < TEST_HEAP_ORDER; i++) { p = mem_alloc(&skiboot_heap, 1ULL << i, 1, "here"); assert(p); assert(mem_check(&skiboot_heap)); assert(!strcmp(((struct alloc_hdr *)p)[-1].location, "here")); assert(p > (void *)test_heap); assert(p + (1ULL << i) <= (void *)test_heap + TEST_HEAP_SIZE); assert(mem_allocated_size(p) >= 1ULL << i); mem_free(&skiboot_heap, p, "freed"); assert(heap_empty()); assert(mem_check(&skiboot_heap)); assert(!strcmp(((struct alloc_hdr *)p)[-1].location, "freed")); } p = mem_alloc(&skiboot_heap, 1ULL << i, 1, "here"); assert(!p); mem_free(&skiboot_heap, p, "freed"); assert(heap_empty()); assert(mem_check(&skiboot_heap)); /* Allocations of various alignments: use small alloc first. */ ptrs[0] = mem_alloc(&skiboot_heap, 1, 1, "small"); for (i = 0; ; i++) { p = mem_alloc(&skiboot_heap, 1, 1ULL << i, "here"); assert(mem_check(&skiboot_heap)); /* We will eventually fail... */ if (!p) { assert(i >= TEST_HEAP_ORDER); break; } assert(p); assert((long)p % (1ULL << i) == 0); assert(p > (void *)test_heap); assert(p + 1 <= (void *)test_heap + TEST_HEAP_SIZE); mem_free(&skiboot_heap, p, "freed"); assert(mem_check(&skiboot_heap)); } mem_free(&skiboot_heap, ptrs[0], "small freed"); assert(heap_empty()); assert(mem_check(&skiboot_heap)); /* Many little allocations, freed in reverse order. */ for (i = 0; i < 100; i++) { ptrs[i] = mem_alloc(&skiboot_heap, sizeof(long), 1, "here"); assert(ptrs[i]); assert(ptrs[i] > (void *)test_heap); assert(ptrs[i] + sizeof(long) <= (void *)test_heap + TEST_HEAP_SIZE); assert(mem_check(&skiboot_heap)); } mem_dump_free(); for (i = 0; i < 100; i++) mem_free(&skiboot_heap, ptrs[100 - 1 - i], "freed"); assert(heap_empty()); assert(mem_check(&skiboot_heap)); /* Check the prev_free gets updated properly. */ ptrs[0] = mem_alloc(&skiboot_heap, sizeof(long), 1, "ptrs[0]"); ptrs[1] = mem_alloc(&skiboot_heap, sizeof(long), 1, "ptrs[1]"); assert(ptrs[1] > ptrs[0]); mem_free(&skiboot_heap, ptrs[0], "ptrs[0] free"); assert(mem_check(&skiboot_heap)); ptrs[0] = mem_alloc(&skiboot_heap, sizeof(long), 1, "ptrs[0] again"); assert(mem_check(&skiboot_heap)); mem_free(&skiboot_heap, ptrs[1], "ptrs[1] free"); mem_free(&skiboot_heap, ptrs[0], "ptrs[0] free"); assert(mem_check(&skiboot_heap)); assert(heap_empty()); #if 0 printf("Heap map:\n"); for (i = 0; i < TEST_HEAP_SIZE / sizeof(long); i++) { printf("%u", test_bit(skiboot_heap.bitmap, i)); if (i % 64 == 63) printf("\n"); else if (i % 8 == 7) printf(" "); } #endif /* Simple enlargement, then free */ p = mem_alloc(&skiboot_heap, 1, 1, "one byte"); assert(p); assert(mem_resize(&skiboot_heap, p, 100, "hundred bytes")); assert(mem_allocated_size(p) >= 100); assert(mem_check(&skiboot_heap)); assert(!strcmp(((struct alloc_hdr *)p)[-1].location, "hundred bytes")); mem_free(&skiboot_heap, p, "freed"); /* Simple shrink, then free */ p = mem_alloc(&skiboot_heap, 100, 1, "100 bytes"); assert(p); assert(mem_resize(&skiboot_heap, p, 1, "1 byte")); assert(mem_allocated_size(p) < 100); assert(mem_check(&skiboot_heap)); assert(!strcmp(((struct alloc_hdr *)p)[-1].location, "1 byte")); mem_free(&skiboot_heap, p, "freed"); /* Lots of resizing (enlarge). */ p = mem_alloc(&skiboot_heap, 1, 1, "one byte"); assert(p); for (i = 1; i <= TEST_HEAP_SIZE - sizeof(struct alloc_hdr); i++) { assert(mem_resize(&skiboot_heap, p, i, "enlarge")); assert(mem_allocated_size(p) >= i); assert(mem_check(&skiboot_heap)); } /* Can't make it larger though. */ assert(!mem_resize(&skiboot_heap, p, i, "enlarge")); for (i = TEST_HEAP_SIZE - sizeof(struct alloc_hdr); i > 0; i--) { assert(mem_resize(&skiboot_heap, p, i, "shrink")); assert(mem_check(&skiboot_heap)); } mem_free(&skiboot_heap, p, "freed"); assert(mem_check(&skiboot_heap)); unlock(&skiboot_heap.free_list_lock); /* lock the regions list */ lock(&mem_region_lock); /* Test splitting of a region. */ r = new_region("base", (unsigned long)test_heap, TEST_HEAP_SIZE, NULL, REGION_SKIBOOT_HEAP); assert(add_region(r)); r = new_region("splitter", (unsigned long)test_heap + TEST_HEAP_SIZE/4, TEST_HEAP_SIZE/2, NULL, REGION_RESERVED); assert(add_region(r)); /* Now we should have *three* regions. */ i = 0; list_for_each(®ions, r, list) { if (region_start(r) == test_heap) { assert(r->len == TEST_HEAP_SIZE/4); assert(strcmp(r->name, "base") == 0); assert(r->type == REGION_SKIBOOT_HEAP); } else if (region_start(r) == test_heap + TEST_HEAP_SIZE / 4) { assert(r->len == TEST_HEAP_SIZE/2); assert(strcmp(r->name, "splitter") == 0); assert(r->type == REGION_RESERVED); assert(!r->free_list.n.next); } else if (region_start(r) == test_heap + TEST_HEAP_SIZE/4*3) { assert(r->len == TEST_HEAP_SIZE/4); assert(strcmp(r->name, "base") == 0); assert(r->type == REGION_SKIBOOT_HEAP); } else abort(); assert(mem_check(r)); i++; } mem_dump_free(); assert(i == 3); while ((r = list_pop(®ions, struct mem_region, list)) != NULL) { list_del(&r->list); lock(&skiboot_heap.free_list_lock); mem_free(&skiboot_heap, r, __location__); unlock(&skiboot_heap.free_list_lock); } unlock(&mem_region_lock); assert(skiboot_heap.free_list_lock.lock_val == 0); real_free(test_heap); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_region_init.c000066400000000000000000000104771265204436200227610ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include /* Use these before we undefine them below. */ static inline void *real_malloc(size_t size) { return malloc(size); } static inline void real_free(void *p) { return free(p); } #include "../malloc.c" #include /* We need mem_region to accept __location__ */ #define is_rodata(p) true #include "../mem_region.c" /* But we need device tree to make copies of names. */ #undef is_rodata #define is_rodata(p) false static inline char *skiboot_strdup(const char *str) { char *ret = __malloc(strlen(str) + 1, ""); return memcpy(ret, str, strlen(str) + 1); } #undef strdup #define strdup skiboot_strdup #include "../device.c" #include #include #include void lock(struct lock *l) { assert(!l->lock_val); l->lock_val = 1; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val = 0; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } /* We actually need a lot of room for the bitmaps! */ #define TEST_HEAP_ORDER 27 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) static void add_mem_node(uint64_t start, uint64_t len) { struct dt_node *mem; u64 reg[2]; char *name= (char*)malloc(sizeof("memory@") + STR_MAX_CHARS(reg[0])); assert(name); /* reg contains start and length */ reg[0] = cpu_to_be64(start); reg[1] = cpu_to_be64(len); sprintf(name, "memory@%llx", (unsigned long long)start); mem = dt_new(dt_root, name); assert(mem); dt_add_property_string(mem, "device_type", "memory"); dt_add_property(mem, "reg", reg, sizeof(reg)); free(name); } void add_chip_dev_associativity(struct dt_node *dev __attribute__((unused))) { } int main(void) { uint64_t end; int builtins; struct mem_region *r; char *heap = real_malloc(TEST_HEAP_SIZE); /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (unsigned long)heap; skiboot_heap.len = TEST_HEAP_SIZE; skiboot_os_reserve.len = 16384; dt_root = dt_new_root(""); dt_add_property_cells(dt_root, "#address-cells", 2); dt_add_property_cells(dt_root, "#size-cells", 2); /* Make sure we overlap the heap, at least. */ add_mem_node(0, (uint64_t)(heap + 0x100000000ULL)); add_mem_node((uint64_t)heap+0x100000000ULL , 0x100000000ULL); end = (uint64_t)(heap+ 0x100000000ULL + 0x100000000ULL); /* Now convert. */ mem_region_init(); mem_dump_allocs(); assert(mem_check(&skiboot_heap)); builtins = 0; list_for_each(®ions, r, list) { /* Regions must not overlap. */ struct mem_region *r2, *pre = NULL, *post = NULL; list_for_each(®ions, r2, list) { if (r == r2) continue; assert(!overlaps(r, r2)); } /* But should have exact neighbours. */ list_for_each(®ions, r2, list) { if (r == r2) continue; if (r2->start == r->start + r->len) post = r2; if (r2->start + r2->len == r->start) pre = r2; } assert(r->start == 0 || pre); assert(r->start + r->len == end || post); if (r == &skiboot_code_and_text || r == &skiboot_heap || r == &skiboot_after_heap || r == &skiboot_cpu_stacks || r == &skiboot_os_reserve) builtins++; else assert(r->type == REGION_MEMORY); assert(mem_check(r)); } assert(builtins == 5); dt_free(dt_root); while ((r = list_pop(®ions, struct mem_region, list)) != NULL) { list_del(&r->list); if (r != &skiboot_code_and_text && r != &skiboot_heap && r != &skiboot_after_heap && r != &skiboot_os_reserve && r != &skiboot_cpu_stacks) { free(r); } assert(mem_check(&skiboot_heap)); } assert(skiboot_heap.free_list_lock.lock_val == 0); real_free(heap); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_region_next.c000066400000000000000000000045451265204436200227730ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include #include /* Use these before we override definitions below. */ static void *real_malloc(size_t size) { return malloc(size); } static void real_free(void *p) { return free(p); } #undef malloc #undef free #include #define is_rodata(p) true #include "../mem_region.c" #include "../malloc.c" #include "../device.c" #include #include void lock(struct lock *l) { assert(!l->lock_val); l->lock_val++; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val--; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 12 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) int main(void) { struct mem_region *r; char *test_heap; /* Use malloc for the heap, so valgrind can find issues. */ test_heap = real_malloc(TEST_HEAP_SIZE); skiboot_heap.start = (unsigned long)test_heap; skiboot_heap.len = TEST_HEAP_SIZE; lock(&mem_region_lock); /* empty regions */ r = mem_region_next(NULL); assert(!r); r = new_region("test.1", 0x1000, 0x1000, NULL, REGION_RESERVED); assert(add_region(r)); r = new_region("test.2", 0x2000, 0x1000, NULL, REGION_RESERVED); assert(add_region(r)); mem_regions_finalised = true; r = mem_region_next(NULL); assert(r); assert(r->start == 0x2000); assert(r->len == 0x1000); assert(r->type == REGION_RESERVED); r = mem_region_next(r); assert(r); assert(r->start == 0x1000); assert(r->len == 0x1000); assert(r->type == REGION_RESERVED); r = mem_region_next(r); assert(!r); unlock(&mem_region_lock); real_free(test_heap); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_region_release_unused.c000066400000000000000000000104341265204436200250120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include static void *__malloc(size_t size, const char *location __attribute__((unused))) { return malloc(size); } static void *__realloc(void *ptr, size_t size, const char *location __attribute__((unused))) { return realloc(ptr, size); } static void *__zalloc(size_t size, const char *location __attribute__((unused))) { return calloc(size, 1); } static inline void __free(void *p, const char *location __attribute__((unused))) { return free(p); } #include /* We need mem_region to accept __location__ */ #define is_rodata(p) true #include "../mem_region.c" /* But we need device tree to make copies of names. */ #undef is_rodata #define is_rodata(p) false #include "../device.c" #include #include void lock(struct lock *l) { l->lock_val++; } void unlock(struct lock *l) { l->lock_val--; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 12 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) static void add_mem_node(uint64_t start, uint64_t len) { struct dt_node *mem; u64 reg[2]; char *name; name = (char*)malloc(sizeof("memory@") + STR_MAX_CHARS(reg[0])); assert(name); /* reg contains start and length */ reg[0] = cpu_to_be64(start); reg[1] = cpu_to_be64(len); sprintf(name, "memory@%llx", (long long)start); mem = dt_new(dt_root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property(mem, "reg", reg, sizeof(reg)); free(name); } void add_chip_dev_associativity(struct dt_node *dev __attribute__((unused))) { } int main(void) { uint64_t i; struct mem_region *r, *other = NULL; void *other_mem; const char *last; /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (unsigned long)malloc(TEST_HEAP_SIZE); skiboot_heap.len = TEST_HEAP_SIZE; skiboot_os_reserve.len = skiboot_heap.start; dt_root = dt_new_root(""); dt_add_property_cells(dt_root, "#address-cells", 2); dt_add_property_cells(dt_root, "#size-cells", 2); other_mem = malloc(1024*1024); add_mem_node((unsigned long)other_mem, 1024*1024); /* Now convert. */ mem_region_init(); /* Find our node to allocate from */ list_for_each(®ions, r, list) { if (region_start(r) == other_mem) other = r; } /* This could happen if skiboot addresses clashed with our alloc. */ assert(other); assert(mem_check(other)); /* Allocate 1k from other region. */ lock(&other->free_list_lock); mem_alloc(other, 1024, 1, "1k"); unlock(&other->free_list_lock); mem_region_release_unused(); assert(mem_check(&skiboot_heap)); /* Now we expect it to be split. */ i = 0; list_for_each(®ions, r, list) { assert(mem_check(r)); i++; if (r == &skiboot_os_reserve) continue; if (r == &skiboot_code_and_text) continue; if (r == &skiboot_heap) continue; if (r == &skiboot_after_heap) continue; if (r == &skiboot_cpu_stacks) continue; if (r == other) { assert(r->type == REGION_MEMORY); assert(r->len < 1024 * 1024); } else { assert(r->type == REGION_OS); assert(r->start == other->start + other->len); assert(r->start + r->len == other->start + 1024*1024); } } assert(i == 7); last = NULL; list_for_each(®ions, r, list) { if (last != r->name && strncmp(r->name, NODE_REGION_PREFIX, strlen(NODE_REGION_PREFIX)) == 0) { /* It's safe to cast away const as this is * only going to happen in test code */ free((void*)r->name); break; } last = r->name; } dt_free(dt_root); free((void *)(long)skiboot_heap.start); free(other_mem); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_region_release_unused_noalloc.c000066400000000000000000000073361265204436200265300ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include static void *__malloc(size_t size, const char *location __attribute__((unused))) { return malloc(size); } static void *__realloc(void *ptr, size_t size, const char *location __attribute__((unused))) { return realloc(ptr, size); } static void *__zalloc(size_t size, const char *location __attribute__((unused))) { return calloc(size, 1); } static inline void __free(void *p, const char *location __attribute__((unused))) { return free(p); } #include /* We need mem_region to accept __location__ */ #define is_rodata(p) true #include "../mem_region.c" /* But we need device tree to make copies of names. */ #undef is_rodata #define is_rodata(p) false #include "../device.c" #include #include void lock(struct lock *l) { l->lock_val++; } void unlock(struct lock *l) { l->lock_val--; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 12 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) static void add_mem_node(uint64_t start, uint64_t len) { struct dt_node *mem; u64 reg[2]; char *name; name = (char*)malloc(sizeof("memory@") + STR_MAX_CHARS(reg[0])); assert(name); /* reg contains start and length */ reg[0] = cpu_to_be64(start); reg[1] = cpu_to_be64(len); sprintf(name, "memory@%llx", (long long)start); mem = dt_new(dt_root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property(mem, "reg", reg, sizeof(reg)); free(name); } void add_chip_dev_associativity(struct dt_node *dev __attribute__((unused))) { } int main(void) { uint64_t i; struct mem_region *r; const char *last; /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (unsigned long)malloc(TEST_HEAP_SIZE); skiboot_heap.len = TEST_HEAP_SIZE; skiboot_os_reserve.len = skiboot_heap.start; dt_root = dt_new_root(""); dt_add_property_cells(dt_root, "#address-cells", 2); dt_add_property_cells(dt_root, "#size-cells", 2); add_mem_node(0, 0x100000000ULL); add_mem_node(0x100000000ULL, 0x100000000ULL); mem_region_init(); mem_region_release_unused(); assert(mem_check(&skiboot_heap)); /* Now we expect it to be split. */ i = 0; list_for_each(®ions, r, list) { assert(mem_check(r)); i++; if (r == &skiboot_os_reserve) continue; if (r == &skiboot_code_and_text) continue; if (r == &skiboot_heap) continue; if (r == &skiboot_after_heap) continue; if (r == &skiboot_cpu_stacks) continue; /* the memory nodes should all be available to the OS now */ assert(r->type == REGION_OS); } assert(i == 9); last = NULL; list_for_each(®ions, r, list) { if (last != r->name && strncmp(r->name, NODE_REGION_PREFIX, strlen(NODE_REGION_PREFIX)) == 0) { /* It's safe to cast away the const as * this never happens at runtime, * only in test and only for valgrind */ free((void*)r->name); } last = r->name; } dt_free(dt_root); free((void *)(long)skiboot_heap.start); return 0; } skiboot-skiboot-5.1.13/core/test/run-mem_region_reservations.c000066400000000000000000000124031265204436200245310ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define BITS_PER_LONG (sizeof(long) * 8) /* Don't include this, it's PPC-specific */ #define __CPU_H static unsigned int cpu_max_pir = 1; struct cpu_thread { unsigned int chip_id; }; #include static void *real_malloc(size_t size) { return malloc(size); } static void real_free(void *p) { return free(p); } #undef malloc #undef free #undef realloc #include #include /* We need mem_region to accept __location__ */ #define is_rodata(p) true #include "../mem_region.c" #include "../malloc.c" /* But we need device tree to make copies of names. */ #undef is_rodata #define is_rodata(p) false #include "../../libc/string/strdup.c" #include "../device.c" #include #include void lock(struct lock *l) { assert(!l->lock_val); l->lock_val++; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val--; } bool lock_held_by_me(struct lock *l) { return l->lock_val; } #define TEST_HEAP_ORDER 12 #define TEST_HEAP_SIZE (1ULL << TEST_HEAP_ORDER) static void add_mem_node(uint64_t start, uint64_t len) { struct dt_node *mem; u64 reg[2]; char *name; name = (char*)malloc(sizeof("memory@") + STR_MAX_CHARS(reg[0])); assert(name); /* reg contains start and length */ reg[0] = cpu_to_be64(start); reg[1] = cpu_to_be64(len); sprintf(name, "memory@%llx", (long long)start); mem = dt_new(dt_root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property(mem, "reg", reg, sizeof(reg)); free(name); } void add_chip_dev_associativity(struct dt_node *dev __attribute__((unused))) { } static struct { const char *name; uint64_t addr; bool found; } test_regions[] = { { "test.1", 0x1000, false }, { "test.2", 0x2000, false }, { "test.3", 0x4000, false }, }; static void check_property_reservations(void) { const struct dt_property *names, *ranges; unsigned int i, l; const char *name; uint64_t *rangep; /* check dt properties */ names = dt_find_property(dt_root, "reserved-names"); ranges = dt_find_property(dt_root, "reserved-ranges"); assert(names && ranges); /* walk through names & ranges properies, ensuring that the test * regions are all present */ for (name = names->prop, rangep = (uint64_t *)ranges->prop; name < names->prop + names->len; name += l, rangep += 2) { uint64_t addr; addr = dt_get_number(rangep, 2); l = strlen(name) + 1; for (i = 0; i < ARRAY_SIZE(test_regions); i++) { if (strcmp(test_regions[i].name, name)) continue; assert(test_regions[i].addr == addr); assert(!test_regions[i].found); test_regions[i].found = true; } } for (i = 0; i < ARRAY_SIZE(test_regions); i++) { assert(test_regions[i].found); test_regions[i].found = false; } } static void check_node_reservations(void) { struct dt_node *parent, *node; unsigned int i; parent = dt_find_by_name(dt_root, "reserved-memory"); assert(parent); assert(dt_prop_get_cell(parent, "#address-cells", 0) == 2); assert(dt_prop_get_cell(parent, "#size-cells", 0) == 2); dt_require_property(parent, "ranges", 0); dt_for_each_child(parent, node) { uint64_t addr, size; addr = dt_get_address(node, 0, &size); for (i = 0; i < ARRAY_SIZE(test_regions); i++) { if (strncmp(test_regions[i].name, node->name, strlen(test_regions[i].name))) continue; assert(!test_regions[i].found); assert(test_regions[i].addr == addr); assert(size == 0x1000); test_regions[i].found = true; } } for (i = 0; i < ARRAY_SIZE(test_regions); i++) { assert(test_regions[i].found); test_regions[i].found = false; } } int main(void) { struct mem_region *r; unsigned int i; void *buf; /* Use malloc for the heap, so valgrind can find issues. */ skiboot_heap.start = (long)real_malloc(TEST_HEAP_SIZE); skiboot_heap.len = TEST_HEAP_SIZE; skiboot_os_reserve.len = skiboot_heap.start; dt_root = dt_new_root(""); dt_add_property_cells(dt_root, "#address-cells", 2); dt_add_property_cells(dt_root, "#size-cells", 2); buf = real_malloc(1024*1024); add_mem_node((unsigned long)buf, 1024*1024); /* Now convert. */ mem_region_init(); /* create our reservations */ for (i = 0; i < ARRAY_SIZE(test_regions); i++) mem_reserve_hw(test_regions[i].name, test_regions[i].addr, 0x1000); /* release unused */ mem_region_release_unused(); /* and create reservations */ mem_region_add_dt_reserved(); /* ensure we can't create further reservations */ r = new_region("test.4", 0x5000, 0x1000, NULL, REGION_RESERVED); assert(!add_region(r)); /* check old property-style reservations */ check_property_reservations(); /* and new node-style reservations */ check_node_reservations(); dt_free(dt_root); real_free(buf); real_free((void *)(long)skiboot_heap.start); return 0; } skiboot-skiboot-5.1.13/core/test/run-msg.c000066400000000000000000000200021265204436200203640ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include static bool zalloc_should_fail = false; static int zalloc_should_fail_after = 0; static void *zalloc(size_t size) { if (zalloc_should_fail && zalloc_should_fail_after == 0) { errno = ENOMEM; return NULL; } if (zalloc_should_fail_after > 0) zalloc_should_fail_after--; return calloc(size, 1); } #include "../opal-msg.c" #include void lock(struct lock *l) { assert(!l->lock_val); l->lock_val = 1; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val = 0; } void opal_update_pending_evt(uint64_t evt_mask, uint64_t evt_values) { (void)evt_mask; (void)evt_values; } static long magic = 8097883813087437089UL; static void callback(void *data) { assert(*(uint64_t *)data == magic); } static size_t list_count(struct list_head *list) { size_t count = 0; struct opal_msg_entry *dummy; list_for_each(list, dummy, link) count++; return count; } int main(void) { struct opal_msg_entry* entry; int free_size = OPAL_MAX_MSGS; int nfree = free_size; int npending = 0; int r; static struct opal_msg m; uint64_t *m_ptr = (uint64_t *)&m; zalloc_should_fail = true; zalloc_should_fail_after = 3; opal_init_msg(); zalloc_should_fail = false; opal_init_msg(); assert(list_count(&msg_pending_list) == npending); assert(list_count(&msg_free_list) == nfree); /* Callback. */ r = opal_queue_msg(0, &magic, callback, (u64)0, (u64)1, (u64)2); assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == --nfree); r = opal_get_msg(m_ptr, sizeof(m)); assert(r == 0); assert(m.params[0] == 0); assert(m.params[1] == 1); assert(m.params[2] == 2); assert(list_count(&msg_pending_list) == --npending); assert(list_count(&msg_free_list) == ++nfree); /* No params. */ r = opal_queue_msg(0, NULL, NULL); assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == --nfree); r = opal_get_msg(m_ptr, sizeof(m)); assert(r == 0); assert(list_count(&msg_pending_list) == --npending); assert(list_count(&msg_free_list) == ++nfree); /* > 8 params (ARRAY_SIZE(entry->msg.params) */ r = opal_queue_msg(0, NULL, NULL, 0, 1, 2, 3, 4, 5, 6, 7, 0xBADDA7A); assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == --nfree); r = opal_get_msg(m_ptr, sizeof(m)); assert(r == 0); assert(list_count(&msg_pending_list) == --npending); assert(list_count(&msg_free_list) == ++nfree); assert(m.params[0] == 0); assert(m.params[1] == 1); assert(m.params[2] == 2); assert(m.params[3] == 3); assert(m.params[4] == 4); assert(m.params[5] == 5); assert(m.params[6] == 6); assert(m.params[7] == 7); /* 8 params (ARRAY_SIZE(entry->msg.params) */ r = opal_queue_msg(0, NULL, NULL, 0, 10, 20, 30, 40, 50, 60, 70); assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == --nfree); r = opal_get_msg(m_ptr, sizeof(m)); assert(r == 0); assert(list_count(&msg_pending_list) == --npending); assert(list_count(&msg_free_list) == ++nfree); assert(m.params[0] == 0); assert(m.params[1] == 10); assert(m.params[2] == 20); assert(m.params[3] == 30); assert(m.params[4] == 40); assert(m.params[5] == 50); assert(m.params[6] == 60); assert(m.params[7] == 70); /* Full list (no free nodes in pending). */ while (nfree > 0) { r = opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL); assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == --nfree); } assert(list_count(&msg_free_list) == 0); assert(nfree == 0); assert(npending == OPAL_MAX_MSGS); r = opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL); assert(r == 0); assert(list_count(&msg_pending_list) == OPAL_MAX_MSGS+1); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == nfree); /* Make zalloc fail to test error handling. */ zalloc_should_fail = true; r = opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL); assert(r == OPAL_RESOURCE); assert(list_count(&msg_pending_list) == OPAL_MAX_MSGS+1); assert(list_count(&msg_pending_list) == npending); assert(list_count(&msg_free_list) == nfree); /* Empty list (no nodes). */ while(!list_empty(&msg_pending_list)) { r = opal_get_msg(m_ptr, sizeof(m)); assert(r == 0); npending--; nfree++; } assert(list_count(&msg_pending_list) == npending); assert(list_count(&msg_free_list) == nfree); assert(npending == 0); assert(nfree == OPAL_MAX_MSGS+1); r = opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL); assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); assert(list_count(&msg_free_list) == --nfree); /* Request invalid size. */ r = opal_get_msg(m_ptr, sizeof(m) - 1); assert(r == OPAL_PARAMETER); /* Pass null buffer. */ r = opal_get_msg(NULL, sizeof(m)); assert(r == OPAL_PARAMETER); /* Get msg when none are pending. */ r = opal_get_msg(m_ptr, sizeof(m)); assert(r == 0); r = opal_get_msg(m_ptr, sizeof(m)); assert(r == OPAL_RESOURCE); #define test_queue_num(type, val) \ r = opal_queue_msg(0, NULL, NULL, \ (type)val, (type)val, (type)val, (type)val, \ (type)val, (type)val, (type)val, (type)val); \ assert(r == 0); \ opal_get_msg(m_ptr, sizeof(m)); \ assert(r == OPAL_SUCCESS); \ assert(m.params[0] == (type)val); \ assert(m.params[1] == (type)val); \ assert(m.params[2] == (type)val); \ assert(m.params[3] == (type)val); \ assert(m.params[4] == (type)val); \ assert(m.params[5] == (type)val); \ assert(m.params[6] == (type)val); \ assert(m.params[7] == (type)val) /* Test types of various widths */ test_queue_num(u64, -1); test_queue_num(s64, -1); test_queue_num(u32, -1); test_queue_num(s32, -1); test_queue_num(u16, -1); test_queue_num(s16, -1); test_queue_num(u8, -1); test_queue_num(s8, -1); /* Clean up the list to keep valgrind happy. */ while(!list_empty(&msg_free_list)) { entry = list_pop(&msg_free_list, struct opal_msg_entry, link); assert(entry); free(entry); } while(!list_empty(&msg_pending_list)) { entry = list_pop(&msg_pending_list, struct opal_msg_entry, link); assert(entry); free(entry); } return 0; } skiboot-skiboot-5.1.13/core/test/run-nvram-format.c000066400000000000000000000064361265204436200222260ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "../nvram-format.c" int main(void) { char *nvram_image; size_t sz; struct chrp_nvram_hdr *h; /* 1024 bytes is too small for our NVRAM */ nvram_image = malloc(1024); assert(nvram_format(nvram_image, 1024)!=0); free(nvram_image); /* 4096 bytes is too small for our NVRAM */ nvram_image = malloc(4096); assert(nvram_format(nvram_image, 4096)!=0); free(nvram_image); /* 64k is too small for our NVRAM */ nvram_image = malloc(0x10000); assert(nvram_format(nvram_image, 0x10000)!=0); free(nvram_image); /* 68k is too small for our NVRAM */ nvram_image = malloc(68*1024); assert(nvram_format(nvram_image, 68*1024)!=0); free(nvram_image); /* 68k+16 bytes (nvram header) should generate empty free space */ sz = NVRAM_SIZE_COMMON + NVRAM_SIZE_FW_PRIV + sizeof(struct chrp_nvram_hdr); nvram_image = malloc(sz); assert(nvram_format(nvram_image, sz)==0); assert(nvram_check(nvram_image, sz)==0); assert(nvram_image[sz-13]==0); assert(nvram_image[sz-14]==1); h = (struct chrp_nvram_hdr*)(&nvram_image[NVRAM_SIZE_COMMON + NVRAM_SIZE_FW_PRIV]); assert(memcmp(h->name, "wwwwwwwwwwww", 12)==0); free(nvram_image); /* 128k NVRAM check */ nvram_image = malloc(128*1024); assert(nvram_format(nvram_image, 128*1024)==0); assert(nvram_check(nvram_image,128*1024)==0); /* Now, we corrupt it */ nvram_image[0] = 0; assert(nvram_check(nvram_image,128*1024) != 0); assert(nvram_format(nvram_image, 128*1024)==0); /* corrupt the length of the partition */ nvram_image[2] = 0; nvram_image[3] = 0; assert(nvram_check(nvram_image,128*1024) != 0); assert(nvram_format(nvram_image, 128*1024)==0); /* corrupt the length of the partition */ nvram_image[2] = 0; nvram_image[3] = 0; /* but reset checksum! */ h = (struct chrp_nvram_hdr*)nvram_image; h->cksum = chrp_nv_cksum(h); assert(nvram_check(nvram_image,128*1024) != 0); assert(nvram_format(nvram_image, 128*1024)==0); /* make the length insanely beyond end of nvram */ nvram_image[2] = 42; nvram_image[3] = 32; /* but reset checksum! */ h = (struct chrp_nvram_hdr*)nvram_image; h->cksum = chrp_nv_cksum(h); assert(nvram_check(nvram_image,128*1024) != 0); assert(nvram_format(nvram_image, 128*1024)==0); /* remove skiboot partition */ nvram_image[12] = '\0'; /* but reset checksum! */ h = (struct chrp_nvram_hdr*)nvram_image; h->cksum = chrp_nv_cksum(h); assert(nvram_check(nvram_image,128*1024) != 0); assert(nvram_format(nvram_image, 128*1024)==0); /* remove common partition */ nvram_image[NVRAM_SIZE_FW_PRIV+5] = '\0'; /* but reset checksum! */ h = (struct chrp_nvram_hdr*)(&nvram_image[NVRAM_SIZE_FW_PRIV]); h->cksum = chrp_nv_cksum(h); assert(nvram_check(nvram_image,128*1024) != 0); free(nvram_image); return 0; } skiboot-skiboot-5.1.13/core/test/run-pel.c000066400000000000000000000063361265204436200203740ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Test for our PEL record generation. Currently this doesn't actually * test that the records we generate are correct, but it at least lets * us run valgrind over the generation routines to check for buffer * overflows, etc. */ #include #include #include #include #include #define TEST_ERROR 0x1234 #define TEST_SUBSYS 0x5678 DEFINE_LOG_ENTRY(TEST_ERROR, OPAL_PLATFORM_ERR_EVT, TEST_SUBSYS, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); #include "../pel.c" struct dt_node *dt_root = NULL; char dt_prop[] = "DUMMY DT PROP"; const void *dt_prop_get(const struct dt_node *node __unused, const char *prop __unused) { return dt_prop; } int rtc_cache_get_datetime(uint32_t *year_month_day, uint64_t *hour_minute_second_millisecond) { *year_month_day = 0; *hour_minute_second_millisecond = 0; return 0; } int main(void) { char *pel_buf; size_t size; struct errorlog *elog; struct opal_err_info *opal_err_info = &err_TEST_ERROR; char *buffer; struct elog_user_data_section *tmp; elog = malloc(sizeof(struct errorlog)); pel_buf = malloc(PEL_MIN_SIZE + 4); assert(elog); assert(pel_buf); memset(elog, 0, sizeof(struct errorlog)); elog->error_event_type = opal_err_info->err_type; elog->component_id = opal_err_info->cmp_id; elog->subsystem_id = opal_err_info->subsystem; elog->event_severity = opal_err_info->sev; elog->event_subtype = opal_err_info->event_subtype; elog->reason_code = opal_err_info->reason_code; elog->elog_origin = ORG_SAPPHIRE; size = pel_size(elog); printf("Test buffer too small: "); assert(0 == create_pel_log(elog, NULL, size - 1)); assert(size <= PEL_MIN_SIZE + 4); assert(size == create_pel_log(elog, pel_buf, size)); memset(elog, 0, sizeof(struct errorlog)); elog->error_event_type = opal_err_info->err_type; elog->component_id = opal_err_info->cmp_id; elog->subsystem_id = opal_err_info->subsystem; elog->event_severity = opal_err_info->sev; elog->event_subtype = opal_err_info->event_subtype; elog->reason_code = opal_err_info->reason_code; elog->elog_origin = ORG_SAPPHIRE; size = pel_size(elog); pel_buf = realloc(pel_buf, size); assert(pel_buf); buffer = elog->user_data_dump + elog->user_section_size; tmp = (struct elog_user_data_section *)buffer; tmp->tag = 0x44455343; /* ASCII of DESC */ tmp->size = size + sizeof(struct elog_user_data_section) - 1; strcpy(tmp->data_dump, "Hello World!"); elog->user_section_size += tmp->size; elog->user_section_count++; size = pel_size(elog); pel_buf = realloc(pel_buf, size); assert(pel_buf); assert(size == create_pel_log(elog, pel_buf, size)); free(pel_buf); free(elog); return 0; } skiboot-skiboot-5.1.13/core/test/run-pool.c000066400000000000000000000021651265204436200205610ustar00rootroot00000000000000#include #include "../pool.c" #define POOL_OBJ_COUNT 10 #define POOL_RESERVED_COUNT 2 #define POOL_NORMAL_COUNT (POOL_OBJ_COUNT - POOL_RESERVED_COUNT) struct test_object { int a; int b; int c; }; int main(void) { int i, count = 0; struct pool pool; struct test_object *a[POOL_OBJ_COUNT]; assert(!pool_init(&pool, sizeof(struct test_object), POOL_OBJ_COUNT, POOL_RESERVED_COUNT)); a[0] = pool_get(&pool, POOL_NORMAL); assert(a[0]); pool_free_object(&pool, a[0]); for(i = 0; i < POOL_NORMAL_COUNT; i++) { a[i] = pool_get(&pool, POOL_NORMAL); if (a[i]) count++; } assert(count == POOL_NORMAL_COUNT); /* Normal pool should be exhausted */ assert(!pool_get(&pool, POOL_NORMAL)); /* Reserved pool should still be available */ a[POOL_NORMAL_COUNT] = pool_get(&pool, POOL_HIGH); assert(a[POOL_NORMAL_COUNT]); a[POOL_NORMAL_COUNT + 1] = pool_get(&pool, POOL_HIGH); assert(a[POOL_NORMAL_COUNT + 1]); pool_free_object(&pool, a[3]); /* Should be a free object to get now */ a[3] = pool_get(&pool, POOL_HIGH); assert(a[3]); /* This exits depending on whether all tests passed */ return 0; } skiboot-skiboot-5.1.13/core/test/run-time-utils.c000066400000000000000000000025421265204436200217030ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define __TEST__ #include "../time-utils.c" int main(void) { struct tm *t = malloc(sizeof(struct tm)); uint32_t *ymd = malloc(sizeof(uint32_t)); uint64_t *hms = malloc(sizeof(uint64_t)); t->tm_year = 1982; t->tm_mon = 0; t->tm_mday = 29; t->tm_hour = 7; t->tm_min = 42; t->tm_sec = 24; tm_to_datetime(t, ymd, hms); assert(*ymd == 0x19820129); assert(*hms == 0x742240000000000ULL); memset(t, 0, sizeof(struct tm)); *ymd = 0x19760412; datetime_to_tm(*ymd, *hms, t); assert(t->tm_year == 1976); assert(t->tm_mon == 03); assert(t->tm_mday == 12); assert(t->tm_hour == 7); assert(t->tm_min == 42); assert(t->tm_sec == 24); return 0; } skiboot-skiboot-5.1.13/core/test/run-timer.c000066400000000000000000000021001265204436200207150ustar00rootroot00000000000000#include #include #include #define __TEST__ #include #define mftb() (stamp) #define sync() static uint64_t stamp, last; struct lock; static inline void lock(struct lock *l) { (void)l; } static inline void unlock(struct lock *l) { (void)l; } #include "../timer.c" #define NUM_TIMERS 100 static struct timer timers[NUM_TIMERS]; static unsigned int rand_shift, count; static void init_rand(void) { unsigned long max = RAND_MAX; /* Get something reasonably small */ while(max > 0x10000) { rand_shift++; max >>= 1; } } static void expiry(struct timer *t, void *data, uint64_t now) { (void)data; (void)now; assert(t->target >= last); count--; } void slw_update_timer_expiry(uint64_t new_target) { (void)new_target; /* FIXME: do intersting SLW timer sim */ } int main(void) { unsigned int i; init_rand(); for (i = 0; i < NUM_TIMERS; i++) { init_timer(&timers[i], expiry, NULL); schedule_timer(&timers[i], random() >> rand_shift); } count = NUM_TIMERS; while(count) { check_timers(false); stamp++; } return 0; } skiboot-skiboot-5.1.13/core/test/run-trace.c000066400000000000000000000244741265204436200207150ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include /* Don't include these: PPC-specific */ #define __CPU_H #define __TIME_H #define __PROCESSOR_H #if defined(__i386__) || defined(__x86_64__) /* This is more than a lwsync, but it'll work */ static void full_barrier(void) { asm volatile("mfence" : : : "memory"); } #define lwsync full_barrier #elif defined(__powerpc__) || defined(__powerpc64__) static inline void lwsync(void) { asm volatile("lwsync" : : : "memory"); } #else #error "Define lwsync for this arch" #endif #define zalloc(size) calloc((size), 1) struct cpu_thread { uint32_t pir; uint32_t chip_id; struct trace_info *trace; int server_no; bool is_secondary; struct cpu_thread *primary; }; static struct cpu_thread *this_cpu(void); #define CPUS 4 static struct cpu_thread fake_cpus[CPUS]; static inline struct cpu_thread *next_cpu(struct cpu_thread *cpu) { if (cpu == NULL) return &fake_cpus[0]; cpu++; if (cpu == &fake_cpus[CPUS]) return NULL; return cpu; } #define first_cpu() next_cpu(NULL) #define for_each_cpu(cpu) \ for (cpu = first_cpu(); cpu; cpu = next_cpu(cpu)) static unsigned long timestamp; static unsigned long mftb(void) { return timestamp; } static void *local_alloc(unsigned int chip_id, size_t size, size_t align) { void *p; (void)chip_id; if (posix_memalign(&p, align, size)) p = NULL; return p; } struct dt_node; extern struct dt_node *opal_node; #include "../trace.c" #define rmb() lwsync() #include "../external/trace/trace.c" #include "../device.c" char __rodata_start[1], __rodata_end[1]; struct dt_node *opal_node; struct debug_descriptor debug_descriptor = { .trace_mask = -1 }; void lock(struct lock *l) { assert(!l->lock_val); l->lock_val = 1; } void unlock(struct lock *l) { assert(l->lock_val); l->lock_val = 0; } struct cpu_thread *my_fake_cpu; static struct cpu_thread *this_cpu(void) { return my_fake_cpu; } #include #define PER_CHILD_TRACES (1024*1024) static void write_trace_entries(int id) { void exit(int); unsigned int i; union trace trace; timestamp = id; for (i = 0; i < PER_CHILD_TRACES; i++) { timestamp = i * CPUS + id; assert(sizeof(trace.hdr) % 8 == 0); /* First child never repeats, second repeats once, etc. */ trace_add(&trace, 3 + ((i / (id + 1)) % 0x40), sizeof(trace.hdr)); } /* Final entry has special type, so parent knows it's over. */ trace_add(&trace, 0x70, sizeof(trace.hdr)); exit(0); } static bool all_done(const bool done[]) { unsigned int i; for (i = 0; i < CPUS; i++) if (!done[i]) return false; return true; } static void test_parallel(void) { void *p; unsigned int i, counts[CPUS] = { 0 }, overflows[CPUS] = { 0 }; unsigned int repeats[CPUS] = { 0 }, num_overflows[CPUS] = { 0 }; bool done[CPUS] = { false }; size_t len = sizeof(struct trace_info) + TBUF_SZ + sizeof(union trace); int last = 0; /* Use a shared mmap to test actual parallel buffers. */ i = (CPUS*len + getpagesize()-1)&~(getpagesize()-1); p = mmap(NULL, i, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); for (i = 0; i < CPUS; i++) { fake_cpus[i].trace = p + i * len; fake_cpus[i].trace->tb.mask = cpu_to_be64(TBUF_SZ - 1); fake_cpus[i].trace->tb.max_size = cpu_to_be32(sizeof(union trace)); fake_cpus[i].is_secondary = false; } for (i = 0; i < CPUS; i++) { if (!fork()) { /* Child. */ my_fake_cpu = &fake_cpus[i]; write_trace_entries(i); } } while (!all_done(done)) { union trace t; for (i = 0; i < CPUS; i++) { if (trace_get(&t, &fake_cpus[(i+last) % CPUS].trace->tb)) break; } if (i == CPUS) { sched_yield(); continue; } i = (i + last) % CPUS; last = i; assert(be16_to_cpu(t.hdr.cpu) < CPUS); assert(!done[be16_to_cpu(t.hdr.cpu)]); if (t.hdr.type == TRACE_OVERFLOW) { /* Conveniently, each record is 16 bytes here. */ assert(be64_to_cpu(t.overflow.bytes_missed) % 16 == 0); overflows[i] += be64_to_cpu(t.overflow.bytes_missed) / 16; num_overflows[i]++; continue; } assert(be64_to_cpu(t.hdr.timestamp) % CPUS == be16_to_cpu(t.hdr.cpu)); if (t.hdr.type == TRACE_REPEAT) { assert(t.hdr.len_div_8 * 8 == sizeof(t.repeat)); assert(be16_to_cpu(t.repeat.num) != 0); assert(be16_to_cpu(t.repeat.num) <= be16_to_cpu(t.hdr.cpu)); repeats[be16_to_cpu(t.hdr.cpu)] += be16_to_cpu(t.repeat.num); } else if (t.hdr.type == 0x70) { done[be16_to_cpu(t.hdr.cpu)] = true; } else { counts[be16_to_cpu(t.hdr.cpu)]++; } } /* Gather children. */ for (i = 0; i < CPUS; i++) { int status; wait(&status); } for (i = 0; i < CPUS; i++) { printf("Child %i: %u produced, %u overflows, %llu total\n", i, counts[i], overflows[i], (long long)be64_to_cpu(fake_cpus[i].trace->tb.end)); assert(counts[i] + repeats[i] <= PER_CHILD_TRACES); } /* Child 0 never repeats. */ assert(repeats[0] == 0); assert(counts[0] + overflows[0] == PER_CHILD_TRACES); /* * FIXME: Other children have some fuzz, since overflows may * include repeat record we already read. And odd-numbered * overflows may include more repeat records than normal * records (they alternate). */ } int main(void) { union trace minimal; union trace large; union trace trace; unsigned int i, j; opal_node = dt_new_root("opal"); for (i = 0; i < CPUS; i++) { fake_cpus[i].server_no = i; fake_cpus[i].is_secondary = (i & 0x1); fake_cpus[i].primary = &fake_cpus[i & ~0x1]; } init_trace_buffers(); my_fake_cpu = &fake_cpus[0]; for (i = 0; i < CPUS; i++) { assert(trace_empty(&fake_cpus[i].trace->tb)); assert(!trace_get(&trace, &fake_cpus[i].trace->tb)); } assert(sizeof(trace.hdr) % 8 == 0); timestamp = 1; trace_add(&minimal, 100, sizeof(trace.hdr)); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); assert(be64_to_cpu(trace.hdr.timestamp) == timestamp); /* Make it wrap once. */ for (i = 0; i < TBUF_SZ / (minimal.hdr.len_div_8 * 8) + 1; i++) { timestamp = i; trace_add(&minimal, 99 + (i%2), sizeof(trace.hdr)); } assert(trace_get(&trace, &my_fake_cpu->trace->tb)); /* First one must be overflow marker. */ assert(trace.hdr.type == TRACE_OVERFLOW); assert(trace.hdr.len_div_8 * 8 == sizeof(trace.overflow)); assert(be64_to_cpu(trace.overflow.bytes_missed) == minimal.hdr.len_div_8 * 8); for (i = 0; i < TBUF_SZ / (minimal.hdr.len_div_8 * 8); i++) { assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); assert(be64_to_cpu(trace.hdr.timestamp) == i+1); assert(trace.hdr.type == 99 + ((i+1)%2)); } assert(!trace_get(&trace, &my_fake_cpu->trace->tb)); /* Now put in some weird-length ones, to test overlap. * Last power of 2, minus 8. */ for (j = 0; (1 << j) < sizeof(large); j++); for (i = 0; i < TBUF_SZ; i++) { timestamp = i; trace_add(&large, 100 + (i%2), (1 << (j-1))); } assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.type == TRACE_OVERFLOW); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.len_div_8 == large.hdr.len_div_8); i = be64_to_cpu(trace.hdr.timestamp); while (trace_get(&trace, &my_fake_cpu->trace->tb)) assert(be64_to_cpu(trace.hdr.timestamp) == ++i); /* Test repeats. */ for (i = 0; i < 65538; i++) { timestamp = i; trace_add(&minimal, 100, sizeof(trace.hdr)); } timestamp = i; trace_add(&minimal, 101, sizeof(trace.hdr)); timestamp = i+1; trace_add(&minimal, 101, sizeof(trace.hdr)); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.timestamp == 0); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); assert(trace.hdr.type == 100); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.type == TRACE_REPEAT); assert(trace.hdr.len_div_8 * 8 == sizeof(trace.repeat)); assert(be16_to_cpu(trace.repeat.num) == 65535); assert(be64_to_cpu(trace.repeat.timestamp) == 65535); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(be64_to_cpu(trace.hdr.timestamp) == 65536); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); assert(trace.hdr.type == 100); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.type == TRACE_REPEAT); assert(trace.hdr.len_div_8 * 8 == sizeof(trace.repeat)); assert(be16_to_cpu(trace.repeat.num) == 1); assert(be64_to_cpu(trace.repeat.timestamp) == 65537); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(be64_to_cpu(trace.hdr.timestamp) == 65538); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); assert(trace.hdr.type == 101); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(trace.hdr.type == TRACE_REPEAT); assert(trace.hdr.len_div_8 * 8 == sizeof(trace.repeat)); assert(be16_to_cpu(trace.repeat.num) == 1); assert(be64_to_cpu(trace.repeat.timestamp) == 65539); /* Now, test adding repeat while we're reading... */ timestamp = 0; trace_add(&minimal, 100, sizeof(trace.hdr)); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); assert(be64_to_cpu(trace.hdr.timestamp) == 0); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); assert(trace.hdr.type == 100); for (i = 1; i < TBUF_SZ; i++) { timestamp = i; trace_add(&minimal, 100, sizeof(trace.hdr)); assert(trace_get(&trace, &my_fake_cpu->trace->tb)); if (i % 65536 == 0) { assert(trace.hdr.type == 100); assert(trace.hdr.len_div_8 == minimal.hdr.len_div_8); } else { assert(trace.hdr.type == TRACE_REPEAT); assert(trace.hdr.len_div_8 * 8 == sizeof(trace.repeat)); assert(be16_to_cpu(trace.repeat.num) == 1); } assert(be64_to_cpu(trace.repeat.timestamp) == i); assert(!trace_get(&trace, &my_fake_cpu->trace->tb)); } for (i = 0; i < CPUS; i++) if (!fake_cpus[i].is_secondary) free(fake_cpus[i].trace); test_parallel(); return 0; } skiboot-skiboot-5.1.13/core/test/stubs.c000066400000000000000000000031571265204436200201500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include void _prlog(int log_level __attribute__((unused)), const char* fmt, ...) __attribute__((format (printf, 2, 3))); #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif #define prlog(l, f, ...) do { _prlog(l, pr_fmt(f), ##__VA_ARGS__); } while(0) void _prlog(int log_level __attribute__((unused)), const char* fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } /* Add any stub functions required for linking here. */ static void stub_function(void) { abort(); } #define STUB(fnname) \ void fnname(void) __attribute__((weak, alias ("stub_function"))) STUB(fdt_begin_node); STUB(fdt_property); STUB(fdt_end_node); STUB(fdt_create); STUB(fdt_add_reservemap_entry); STUB(fdt_finish_reservemap); STUB(fdt_strerror); STUB(fdt_check_header); STUB(_fdt_check_node_offset); STUB(fdt_next_tag); STUB(fdt_string); STUB(fdt_get_name); STUB(dt_first); STUB(dt_next); STUB(dt_has_node_property); STUB(dt_get_address); STUB(add_chip_dev_associativity); skiboot-skiboot-5.1.13/core/time-utils.c000066400000000000000000000044441265204436200201250ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include /* * Converts an OPAL formatted datetime into a struct tm. We ignore microseconds * as Linux doesn't use them anyway. * * | year | month | mday | * +------------------------------------+ * | hour | minute | secs | reserved | * +------------------------------------+ * | microseconds | */ void datetime_to_tm(uint32_t y_m_d, uint64_t h_m_s_m, struct tm *tm) { uint32_t x; tm->tm_year = bcd_byte(y_m_d, 3) * 100 + bcd_byte(y_m_d, 2); tm->tm_mon = bcd_byte(y_m_d, 1) - 1; tm->tm_mday = bcd_byte(y_m_d, 0); x = h_m_s_m >> 32; tm->tm_hour = bcd_byte(x, 3); tm->tm_min = bcd_byte(x, 2); tm->tm_sec = bcd_byte(x, 1); } /* * The OPAL API is defined as returned a u64 of a similar * format to the FSP message; the 32-bit date field is * in the format: * * | year | month | mday | * * ... and the 64-bit time field is in the format * * | hour | minutes | secs | millisec | * | ------------------------------------- * | millisec | reserved | * * We simply ignore the microseconds/milliseconds for now * as I don't quite understand why the OPAL API defines that * it needs 6 digits for the milliseconds :-) I suspect the * doc got that wrong and it's supposed to be micro but * let's ignore it. * * Note that Linux doesn't use nor set the ms field anyway. */ void tm_to_datetime(struct tm *tm, uint32_t *y_m_d, uint64_t *h_m_s_m) { uint64_t h_m_s; *y_m_d = int_to_bcd4(tm->tm_year) << 16 | int_to_bcd2(tm->tm_mon + 1) << 8 | int_to_bcd2(tm->tm_mday); h_m_s = int_to_bcd2(tm->tm_hour) << 24 | int_to_bcd2(tm->tm_min) << 16 | int_to_bcd2(tm->tm_sec) << 8; *h_m_s_m = h_m_s << 32; } skiboot-skiboot-5.1.13/core/timebase.c000066400000000000000000000054111265204436200176150ustar00rootroot00000000000000 /* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include static void time_wait_poll(unsigned long duration) { unsigned long remaining = duration; unsigned long end = mftb() + duration; unsigned long period = msecs_to_tb(5); if (this_cpu()->tb_invalid) { cpu_relax(); return; } while (tb_compare(mftb(), end) != TB_AAFTERB) { /* Call pollers periodically but not continually to avoid * bouncing cachelines due to lock contention. */ if (remaining >= period) { opal_run_pollers(); time_wait_nopoll(period); remaining -= period; } cpu_relax(); } } void time_wait(unsigned long duration) { struct cpu_thread *c = this_cpu(); if (this_cpu()->lock_depth) { time_wait_nopoll(duration); return; } if (c != boot_cpu) time_wait_nopoll(duration); else time_wait_poll(duration); } void time_wait_nopoll(unsigned long duration) { unsigned long end = mftb() + duration; if (this_cpu()->tb_invalid) { cpu_relax(); return; } while(tb_compare(mftb(), end) != TB_AAFTERB) cpu_relax(); } void time_wait_ms(unsigned long ms) { time_wait(msecs_to_tb(ms)); } void time_wait_ms_nopoll(unsigned long ms) { time_wait_nopoll(msecs_to_tb(ms)); } void time_wait_us(unsigned long us) { time_wait(usecs_to_tb(us)); } void time_wait_us_nopoll(unsigned long us) { time_wait_nopoll(usecs_to_tb(us)); } unsigned long timespec_to_tb(const struct timespec *ts) { unsigned long ns; /* First convert to ns */ ns = ts->tv_sec * 1000000000ul; ns += ts->tv_nsec; /* * This is a very rough approximation, it works provided * we never try to pass too long delays here and the TB * frequency isn't significantly lower than 512Mhz. * * We could improve the precision by shifting less bits * at the expense of capacity or do 128 bit math which * I'm not eager to do :-) */ return (ns * (tb_hz >> 24)) / (1000000000ul >> 24); } int nanosleep(const struct timespec *req, struct timespec *rem) { time_wait(timespec_to_tb(req)); if (rem) { rem->tv_sec = 0; rem->tv_nsec = 0; } return 0; } int nanosleep_nopoll(const struct timespec *req, struct timespec *rem) { time_wait_nopoll(timespec_to_tb(req)); if (rem) { rem->tv_sec = 0; rem->tv_nsec = 0; } return 0; } skiboot-skiboot-5.1.13/core/timer.c000066400000000000000000000136351265204436200171530ustar00rootroot00000000000000#include #include #include #include #include #include #ifdef __TEST__ #define this_cpu() ((void *)-1) #define cpu_relax() #else #include #endif /* Heartbeat requested from Linux */ #define HEARTBEAT_DEFAULT_MS 2000 static struct lock timer_lock = LOCK_UNLOCKED; static LIST_HEAD(timer_list); static LIST_HEAD(timer_poll_list); static bool timer_in_poll; static uint64_t timer_poll_gen; void init_timer(struct timer *t, timer_func_t expiry, void *data) { t->link.next = t->link.prev = NULL; t->target = 0; t->expiry = expiry; t->user_data = data; t->running = NULL; } static void __remove_timer(struct timer *t) { list_del(&t->link); t->link.next = t->link.prev = NULL; } static void __sync_timer(struct timer *t) { sync(); /* Guard against re-entrancy */ assert(t->running != this_cpu()); while (t->running) { unlock(&timer_lock); cpu_relax(); /* Should we call the pollers here ? */ lock(&timer_lock); } } void sync_timer(struct timer *t) { lock(&timer_lock); __sync_timer(t); unlock(&timer_lock); } void cancel_timer(struct timer *t) { lock(&timer_lock); __sync_timer(t); if (t->link.next) __remove_timer(t); unlock(&timer_lock); } void cancel_timer_async(struct timer *t) { lock(&timer_lock); if (t->link.next) __remove_timer(t); unlock(&timer_lock); } static void __schedule_timer_at(struct timer *t, uint64_t when) { struct timer *lt; /* If the timer is already scheduled, take it out */ if (t->link.next) __remove_timer(t); /* Update target */ t->target = when; if (when == TIMER_POLL) { /* It's a poller, add it to the poller list */ t->gen = timer_poll_gen; list_add_tail(&timer_poll_list, &t->link); } else { /* It's a real timer, add it in the right spot in the * ordered timer list */ list_for_each(&timer_list, lt, link) { if (when >= lt->target) continue; list_add_before(&timer_list, &t->link, <->link); goto bail; } list_add_tail(&timer_list, &t->link); } bail: /* Pick up the next timer and upddate the SBE HW timer */ lt = list_top(&timer_list, struct timer, link); if (lt) slw_update_timer_expiry(lt->target); } void schedule_timer_at(struct timer *t, uint64_t when) { lock(&timer_lock); __schedule_timer_at(t, when); unlock(&timer_lock); } uint64_t schedule_timer(struct timer *t, uint64_t how_long) { uint64_t now = mftb(); if (how_long == TIMER_POLL) schedule_timer_at(t, TIMER_POLL); else schedule_timer_at(t, now + how_long); return now; } static void __check_poll_timers(uint64_t now) { struct timer *t; /* Don't call this from multiple CPUs at once */ if (timer_in_poll) return; timer_in_poll = true; /* * Poll timers might re-enqueue themselves and don't have an * expiry so we can't do like normal timers and just run until * we hit a wall. Instead, each timer has a generation count, * which we set to the current global gen count when we schedule * it and update when we run it. It will only be considered if * the generation count is different than the current one. We * don't try to compare generations being larger or smaller * because at boot, this can be called quite quickly and I want * to be safe vs. wraps. */ timer_poll_gen++; for (;;) { t = list_top(&timer_poll_list, struct timer, link); /* Top timer has a different generation than current ? Must * be older, we are done. */ if (!t || t->gen == timer_poll_gen) break; /* Top of list still running, we have to delay handling it, * let's reprogram the SLW with a small delay. We chose * arbitrarily 1us. */ if (t->running) { slw_update_timer_expiry(now + usecs_to_tb(1)); break; } /* Allright, first remove it and mark it running */ __remove_timer(t); t->running = this_cpu(); /* Now we can unlock and call it's expiry */ unlock(&timer_lock); t->expiry(t, t->user_data, now); /* Re-lock and mark not running */ lock(&timer_lock); t->running = NULL; } timer_in_poll = false; } static void __check_timers(uint64_t now) { struct timer *t; for (;;) { t = list_top(&timer_list, struct timer, link); /* Top of list not expired ? that's it ... */ if (!t || t->target > now) break; /* Top of list still running, we have to delay handling * it. For now just skip until the next poll, when we have * SLW interrupts, we'll probably want to trip another one * ASAP */ if (t->running) break; /* Allright, first remove it and mark it running */ __remove_timer(t); t->running = this_cpu(); /* Now we can unlock and call it's expiry */ unlock(&timer_lock); t->expiry(t, t->user_data, now); /* Re-lock and mark not running */ lock(&timer_lock); t->running = NULL; /* Update time stamp */ now = mftb(); } } void check_timers(bool from_interrupt) { struct timer *t; uint64_t now = mftb(); /* This is the polling variant, the SLW interrupt path, when it * exists, will use a slight variant of this that doesn't call * the pollers */ /* Lockless "peek", a bit racy but shouldn't be a problem */ t = list_top(&timer_list, struct timer, link); if (list_empty(&timer_poll_list) && (!t || t->target > now)) return; /* Take lock and try again */ lock(&timer_lock); if (!from_interrupt) __check_poll_timers(now); __check_timers(now); unlock(&timer_lock); } #ifndef __TEST__ void late_init_timers(void) { /* Add a property requesting the OS to call opal_poll_event() at * a specified interval in order for us to run our background * low priority pollers. * * If we have an SLW timer facility, we run this 10 times slower, * we could possibly completely get rid of it. * * We use a value in milliseconds, we don't want this to ever be * faster than that. */ if (slw_timer_ok() || fsp_present()) { dt_add_property_cells(opal_node, "ibm,heartbeat-ms", HEARTBEAT_DEFAULT_MS); } else { dt_add_property_cells(opal_node, "ibm,heartbeat-ms", HEARTBEAT_DEFAULT_MS / 10); } } #endif skiboot-skiboot-5.1.13/core/trace.c000066400000000000000000000155021265204436200171240ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_TRACES #define MAX_SIZE (sizeof(union trace) + 7) /* Smaller trace buffer for early booting */ #define BOOT_TBUF_SZ 65536 static struct { struct trace_info trace_info; char buf[BOOT_TBUF_SZ + MAX_SIZE]; } boot_tracebuf; void init_boot_tracebuf(struct cpu_thread *boot_cpu) { init_lock(&boot_tracebuf.trace_info.lock); boot_tracebuf.trace_info.tb.mask = BOOT_TBUF_SZ - 1; boot_tracebuf.trace_info.tb.max_size = MAX_SIZE; boot_cpu->trace = &boot_tracebuf.trace_info; } static size_t tracebuf_extra(void) { /* We make room for the largest possible record */ return TBUF_SZ + MAX_SIZE; } /* To avoid bloating each entry, repeats are actually specific entries. * tb->last points to the last (non-repeat) entry. */ static bool handle_repeat(struct tracebuf *tb, const union trace *trace) { struct trace_hdr *prev; struct trace_repeat *rpt; u32 len; prev = (void *)tb->buf + be64_to_cpu(tb->last & tb->mask); if (prev->type != trace->hdr.type || prev->len_div_8 != trace->hdr.len_div_8 || prev->cpu != trace->hdr.cpu) return false; len = prev->len_div_8 << 3; if (memcmp(prev + 1, &trace->hdr + 1, len - sizeof(*prev)) != 0) return false; /* If they've consumed prev entry, don't repeat. */ if (be64_to_cpu(tb->last) < be64_to_cpu(tb->start)) return false; /* OK, it's a duplicate. Do we already have repeat? */ if (be64_to_cpu(tb->last) + len != be64_to_cpu(tb->end)) { u64 pos = be64_to_cpu(tb->last) + len; /* FIXME: Reader is not protected from seeing this! */ rpt = (void *)tb->buf + (pos & be64_to_cpu(tb->mask)); assert(pos + rpt->len_div_8*8 == be64_to_cpu(tb->end)); assert(rpt->type == TRACE_REPEAT); /* If this repeat entry is full, don't repeat. */ if (be16_to_cpu(rpt->num) == 0xFFFF) return false; rpt->num = cpu_to_be16(be16_to_cpu(rpt->num) + 1); rpt->timestamp = trace->hdr.timestamp; return true; } /* * Generate repeat entry: it's the smallest possible entry, so we * must have eliminated old entries. */ assert(trace->hdr.len_div_8 * 8 >= sizeof(*rpt)); rpt = (void *)tb->buf + be64_to_cpu(tb->end & tb->mask); rpt->timestamp = trace->hdr.timestamp; rpt->type = TRACE_REPEAT; rpt->len_div_8 = sizeof(*rpt) >> 3; rpt->cpu = trace->hdr.cpu; rpt->prev_len = trace->hdr.len_div_8 << 3; rpt->num = cpu_to_be16(1); lwsync(); /* write barrier: complete repeat record before exposing */ tb->end = cpu_to_be64(be64_to_cpu(tb->end) + sizeof(*rpt)); return true; } void trace_add(union trace *trace, u8 type, u16 len) { struct trace_info *ti = this_cpu()->trace; unsigned int tsz; trace->hdr.type = type; trace->hdr.len_div_8 = (len + 7) >> 3; tsz = trace->hdr.len_div_8 << 3; #ifdef DEBUG_TRACES assert(tsz >= sizeof(trace->hdr)); assert(tsz <= sizeof(*trace)); assert(trace->hdr.type != TRACE_REPEAT); assert(trace->hdr.type != TRACE_OVERFLOW); #endif /* Skip traces not enabled in the debug descriptor */ if (!((1ul << trace->hdr.type) & debug_descriptor.trace_mask)) return; trace->hdr.timestamp = cpu_to_be64(mftb()); trace->hdr.cpu = cpu_to_be16(this_cpu()->server_no); lock(&ti->lock); /* Throw away old entries before we overwrite them. */ while ((be64_to_cpu(ti->tb.start) + be64_to_cpu(ti->tb.mask) + 1) < (be64_to_cpu(ti->tb.end) + tsz)) { struct trace_hdr *hdr; hdr = (void *)ti->tb.buf + be64_to_cpu(ti->tb.start & ti->tb.mask); ti->tb.start = cpu_to_be64(be64_to_cpu(ti->tb.start) + (hdr->len_div_8 << 3)); } /* Must update ->start before we rewrite new entries. */ lwsync(); /* write barrier */ /* Check for duplicates... */ if (!handle_repeat(&ti->tb, trace)) { /* This may go off end, and that's why ti->tb.buf is oversize */ memcpy(ti->tb.buf + be64_to_cpu(ti->tb.end & ti->tb.mask), trace, tsz); ti->tb.last = ti->tb.end; lwsync(); /* write barrier: write entry before exposing */ ti->tb.end = cpu_to_be64(be64_to_cpu(ti->tb.end) + tsz); } unlock(&ti->lock); } static void trace_add_dt_props(void) { unsigned int i; u64 *prop, tmask; prop = malloc(sizeof(u64) * 2 * debug_descriptor.num_traces); for (i = 0; i < debug_descriptor.num_traces; i++) { prop[i * 2] = cpu_to_fdt64(debug_descriptor.trace_phys[i]); prop[i * 2 + 1] = cpu_to_fdt64(debug_descriptor.trace_size[i]); } dt_add_property(opal_node, "ibm,opal-traces", prop, sizeof(u64) * 2 * i); free(prop); tmask = (uint64_t)&debug_descriptor.trace_mask; dt_add_property_cells(opal_node, "ibm,opal-trace-mask", hi32(tmask), lo32(tmask)); } static void trace_add_desc(struct trace_info *t, uint64_t size) { unsigned int i = debug_descriptor.num_traces; if (i >= DEBUG_DESC_MAX_TRACES) { prerror("TRACE: Debug descriptor trace list full !\n"); return; } debug_descriptor.num_traces++; debug_descriptor.trace_phys[i] = (uint64_t)&t->tb; debug_descriptor.trace_tce[i] = 0; /* populated later */ debug_descriptor.trace_size[i] = size; } /* Allocate trace buffers once we know memory topology */ void init_trace_buffers(void) { struct cpu_thread *t; struct trace_info *any = &boot_tracebuf.trace_info; uint64_t size; /* Boot the boot trace in the debug descriptor */ trace_add_desc(any, sizeof(boot_tracebuf.buf)); /* Allocate a trace buffer for each primary cpu. */ for_each_cpu(t) { if (t->is_secondary) continue; /* Use a 4K alignment for TCE mapping */ size = ALIGN_UP(sizeof(*t->trace) + tracebuf_extra(), 0x1000); t->trace = local_alloc(t->chip_id, size, 0x1000); if (t->trace) { any = t->trace; memset(t->trace, 0, size); init_lock(&t->trace->lock); t->trace->tb.mask = cpu_to_be64(TBUF_SZ - 1); t->trace->tb.max_size = cpu_to_be32(MAX_SIZE); trace_add_desc(any, sizeof(t->trace->tb) + tracebuf_extra()); } else prerror("TRACE: cpu 0x%x allocation failed\n", t->pir); } /* In case any allocations failed, share trace buffers. */ for_each_cpu(t) { if (!t->is_secondary && !t->trace) t->trace = any; } /* And copy those to the secondaries. */ for_each_cpu(t) { if (!t->is_secondary) continue; t->trace = t->primary->trace; } /* Trace node in DT. */ trace_add_dt_props(); } skiboot-skiboot-5.1.13/core/utils.c000066400000000000000000000035351265204436200171710ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include unsigned long __stack_chk_guard = 0xdeadf00dbaad300d; void __noreturn assert_fail(const char *msg) { prlog(PR_EMERG, "Assert fail: %s\n", msg); _abort(msg); } void __noreturn _abort(const char *msg) { static bool in_abort = false; if (in_abort) for (;;) ; in_abort = true; prlog(PR_EMERG, "Aborting!\n"); backtrace(); if (platform.terminate) platform.terminate(msg); for (;;) ; } char __attrconst tohex(uint8_t nibble) { static const char __tohex[] = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'}; if (nibble > 0xf) return '?'; return __tohex[nibble]; } unsigned long get_symbol(unsigned long addr, char **sym, char **sym_end) { unsigned long prev = 0, next; char *psym = NULL, *p = __sym_map_start; *sym = *sym_end = NULL; while(p < __sym_map_end) { next = strtoul(p, &p, 16) | SKIBOOT_BASE; if (next > addr && prev <= addr) { p = psym + 3;; if (p >= __sym_map_end) return 0; *sym = p; while(p < __sym_map_end && *p != 10) p++; *sym_end = p; return prev; } prev = next; psym = p; while(p < __sym_map_end && *p != 10) p++; p++; } return 0; } skiboot-skiboot-5.1.13/core/vpd.c000066400000000000000000000131211265204436200166120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define CHECK_SPACE(_p, _n, _e) (((_e) - (_p)) >= (_n)) /* Low level keyword search in a record. Can be used when we * need to find the next keyword of a given type, for example * when having multiple MF/SM keyword pairs */ const void *vpd_find_keyword(const void *rec, size_t rec_sz, const char *kw, uint8_t *kw_size) { const uint8_t *p = rec, *end = rec + rec_sz; while (CHECK_SPACE(p, 3, end)) { uint8_t k1 = *(p++); uint8_t k2 = *(p++); uint8_t sz = *(p++); if (k1 == kw[0] && k2 == kw[1]) { if (kw_size) *kw_size = sz; return p; } p += sz; } return NULL; } /* Locate a record in a VPD blob * * Note: This works with VPD LIDs. It will scan until it finds * the first 0x84, so it will skip all those 0's that the VPD * LIDs seem to contain */ const void *vpd_find_record(const void *vpd, size_t vpd_size, const char *record, size_t *sz) { const uint8_t *p = vpd, *end = vpd + vpd_size; bool first_start = true; size_t rec_sz; uint8_t namesz = 0; const char *rec_name; while (CHECK_SPACE(p, 4, end)) { /* Get header byte */ if (*(p++) != 0x84) { /* Skip initial crap in VPD LIDs */ if (first_start) continue; break; } first_start = false; rec_sz = *(p++); rec_sz |= *(p++) << 8; if (!CHECK_SPACE(p, rec_sz, end)) { prerror("VPD: Malformed or truncated VPD," " record size doesn't fit\n"); return NULL; } /* Find record name */ rec_name = vpd_find_keyword(p, rec_sz, "RT", &namesz); if (rec_name && strncmp(record, rec_name, namesz) == 0) { *sz = rec_sz; return p; } p += rec_sz; if (*(p++) != 0x78) { prerror("VPD: Malformed or truncated VPD," " missing final 0x78 in record %.4s\n", rec_name ? rec_name : "????"); return NULL; } } return NULL; } /* Locate a keyword in a record in a VPD blob * * Note: This works with VPD LIDs. It will scan until it finds * the first 0x84, so it will skip all those 0's that the VPD * LIDs seem to contain */ const void *vpd_find(const void *vpd, size_t vpd_size, const char *record, const char *keyword, uint8_t *sz) { size_t rec_sz; const uint8_t *p; p = vpd_find_record(vpd, vpd_size, record, &rec_sz); if (p) p = vpd_find_keyword(p, rec_sz, keyword, sz); return p; } static void *vpd; static size_t vpd_size; static uint32_t vpd_lid_no; /* Helper to load a VPD LID. Pass a ptr to the corresponding LX keyword */ static void *vpd_lid_preload(const uint8_t *lx) { int rc; /* Now this is a guess game as we don't have the info from the * pHyp folks. But basically, it seems to boil down to loading * a LID whose name is 0x80e000yy where yy is the last 2 digits * of the LX record in hex. * * [ Correction: After a chat with some folks, it looks like it's * actually 4 digits, though the lid number is limited to fff * so we weren't far off. ] * * For safety, we look for a matching LX record in an LXRn * (n = lxrn argument) or in VINI if lxrn=0xff */ vpd_lid_no = 0x80e00000 | ((lx[6] & 0xf) << 8) | lx[7]; /* We don't quite know how to get to the LID directory so * we don't know the size. Let's allocate 16K. All the VPD LIDs * I've seen so far are much smaller. */ #define VPD_LID_MAX_SIZE 0x4000 vpd = malloc(VPD_LID_MAX_SIZE); if (!vpd) { prerror("VPD: Failed to allocate memory for LID\n"); return NULL; } /* Adjust LID number for flash side */ vpd_lid_no = fsp_adjust_lid_side(vpd_lid_no); printf("VPD: Trying to load VPD LID 0x%08x...\n", vpd_lid_no); vpd_size = VPD_LID_MAX_SIZE; /* Load it from the FSP */ rc = fsp_preload_lid(vpd_lid_no, vpd, &vpd_size); if (rc) { prerror("VPD: Error %d loading VPD LID\n", rc); goto fail; } return vpd; fail: free(vpd); return NULL; } void vpd_iohub_load(struct dt_node *hub_node) { char record[4] = "LXR0"; const void *valid_lx; uint8_t lx_size; int r; const uint32_t *p; const uint8_t *lx; unsigned int lxrn; p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL); if (!p) return; lxrn = p[0]; lx = (const char *)&p[1]; assert(vpd); assert(vpd_lid_no); r = fsp_wait_lid_loaded(vpd_lid_no); if (r) goto fail; /* Validate it */ if (lxrn < 9) record[3] = '0' + lxrn; else memcpy(record, "VINI", 4); valid_lx = vpd_find(vpd, vpd_size, record, "LX", &lx_size); if (!valid_lx || lx_size != 8) { prerror("VPD: Cannot find validation LX record\n"); goto fail; } if (memcmp(valid_lx, lx, 8) != 0) { prerror("VPD: LX record mismatch !\n"); goto fail; } printf("VPD: Loaded %zu bytes\n", vpd_size); /* Got it ! */ vpd = realloc(vpd, vpd_size); if (!vpd) goto fail; dt_add_property(hub_node, "ibm,io-vpd", vpd, vpd_size); free(vpd); return; fail: free(vpd); vpd = NULL; prerror("VPD: Failed to load VPD LID\n"); return; } void vpd_preload(struct dt_node *hub_node) { const uint32_t *p; const char *lxr; p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL); if (!p) return; lxr = (const char *)&p[1]; vpd = vpd_lid_preload(lxr); } skiboot-skiboot-5.1.13/doc/000077500000000000000000000000001265204436200154745ustar00rootroot00000000000000skiboot-skiboot-5.1.13/doc/console-log.txt000066400000000000000000000037171265204436200204660ustar00rootroot00000000000000SkiBoot Console Log ------------------- Skiboot maintains a circular textual log buffer in memory. It can be accessed using any debugging method that can peek at memory contents. While the debug_descriptor does hold the location of the memory console, we're pretty keen on keeping its location static. Events are logged in the following format: [timebase,log_level] message You should use the new prlog() call for any log message and set the log level/priority appropriately. printf() is mapped to PR_PRINTF and should be phased out and replaced with prlog() calls. See timebase.h for full timebase explanation. Log level from skiboot.h: #define PR_EMERG 0 #define PR_ALERT 1 #define PR_CRIT 2 #define PR_ERR 3 #define PR_WARNING 4 #define PR_NOTICE 5 #define PR_PRINTF PR_NOTICE #define PR_INFO 6 #define PR_DEBUG 7 #define PR_TRACE 8 #define PR_INSANE 9 The console_log_levels byte in the debug_descriptor controls what messages are written to any console drivers (e.g. fsp, uart) and what level is just written to the in memory console (or not at all). This enables (advanced) users to vary what level of output they want at runtime in the memory console and through console drivers (fsp/uart) You can vary two things by poking in the debug descriptor: a) what log level is printed at all e.g. only turn on PR_TRACE at specific points during runtime b) what log level goes out the fsp/uart console defaults to PR_PRINTF We use two 4bit numbers (1 byte) for this in debug descriptor (saving some space, not needlessly wasting space that we may want in future). The default is 0x75 (7=PR_DEBUG to in memory console, 5=PR_PRINTF to drivers If you write 0x77 you will get debug info on uart/fsp console as well as in memory. If you write 0x95 you get PR_INSANE in memory but still only PR_NOTICE through drivers. People who write something like 0x1f will get a very quiet boot indeed. skiboot-skiboot-5.1.13/doc/device-tree.txt000066400000000000000000000404251265204436200204360ustar00rootroot00000000000000/* * Sapphire device-tree requirements * * Version: 0.2.1 * * This documents the generated device-tree requirements, this is based on * a commented device-tree dump obtained from a DT generated by Sapphire * itself from an HDAT. */ /* * General comments: * * - skiboot does not require nodes to have phandle properties, but * if you have them then *all* nodes must have them including the * root of the device-tree (currently a HB bug !). It is recommended * to have them since they are needed to represent the cache levels. * * NOTE: The example tree below only has phandle properties for * nodes that are referenced by other nodes. This is *not* correct * and is purely done for keeping this document smaller, make sure * to follow the rule above. * * - Only the "phandle" property is required. Sapphire also generates * a "linux,phandle" for backward compatibility but doesn't require * it as an input * * - Any property not specifically documented must be put in "as is" * * - All ibm,chip-id properties contain a HW chip ID which correspond * on P8 to the PIR value shifted right by 7 bits, ie. it's a 6-bit * value made of a 3-bit node number and a 3-bit chip number. * * - Unit addresses (@xxxx part of node names) should if possible use * lower case hexadecimal to be consistent with what skiboot does * and to help some stupid parsers out there... */ /* * Version history * * 2013/10/08 : Version 0.1 * * 2013/10/09 : Version 0.2 * * - Add comment about case of unit addresses * - Add missing lpc node definition * - Add example UART node on LPC * - Remove "status" property from PSI xsco nodes * * 2014/03/26 : Version 0.2.1 * * - Fix cpus/xxx/ibm,pa-features to be a byte array */ /dts-v1/; /* * Here are the reserve map entries. They should exactly match the * reserved-ranges property of the root node (see documentation * of that property) */ /memreserve/ 0x00000007fe600000 0x0000000000100000; /memreserve/ 0x00000007fe200000 0x0000000000100000; /memreserve/ 0x0000000031e00000 0x00000000003e0000; /memreserve/ 0x0000000031000000 0x0000000000e00000; /memreserve/ 0x0000000030400000 0x0000000000c00000; /memreserve/ 0x0000000030000000 0x0000000000400000; /memreserve/ 0x0000000400000000 0x0000000000600450; /* Root node */ / { /* * "compatible" properties are string lists (ASCII strings separated by * \0 characters) indicating the overall compatibility from the more * specific to the least specific. * * The root node compatible property *must* contain "ibm,powernv" for * Linux to have the powernv platform match the machine. It is recommended * to add a slightly more precise property (first in order) indicating more * precisely the board type. We don't currently do that in HDAT based * setups but will. * * The standard naming is "vendor,name" so in your case, something like * * compatible = "goog,rhesus","ibm,powernv"; * * would work. Or even better: * * compatible = "goog,rhesus-v1","goog,rhesus","ibm,powernv"; */ compatible = "ibm,powernv"; /* mandatory */ #address-cells = <0x2>; #size-cells = <0x2>; /* User visible board name (will be shown in /proc/cpuinfo) */ model = "Machine Name"; /* * The reserved-names and reserve-names properties work hand in hand. The first one * is a list of strings providing a "name" for each entry in the second one using * the traditional "vendor,name" format. * * The reserved-ranges property contains a list of ranges, each in the form of 2 cells * of address and 2 cells of size (64-bit x2 so each entry is 4 cells) indicating * regions of memory that are reserved and must not be overwritten by skiboot or * subsequently by the Linux Kernel. * * Corresponding entries must also be created in the "reserved map" part of the flat * device-tree (which is a binary list in the header of the fdt). * * Unless a component (skiboot or Linux) specifically knows about a region (usually * based on its name) and decides to change or remove it, all these regions are * passed as-is to Linux and to subsequent kernels accross kexec and are kept * preserved. * * NOTE: Do *NOT* copy the entries below, they are just an example and are actually * created by skiboot itself. They represent the SLW image as "detected" by reading * the PBA BARs and skiboot own memory allocations. * * I would recommend that you put in there the SLW and OCC (or HOMER as one block * if that's how you use it) and any additional memory you want to preserve such * as FW log buffers etc... */ reserved-names = "ibm,slw-image", "ibm,slw-image", "ibm,firmware-stacks", "ibm,firmware-data", "ibm,firmware-heap", "ibm,firmware-code", "memory@400000000"; reserved-ranges = <0x7 0xfe600000 0x0 0x100000 0x7 0xfe200000 0x0 0x100000 0x0 0x31e00000 0x0 0x3e0000 0x0 0x31000000 0x0 0xe00000 0x0 0x30400000 0x0 0xc00000 0x0 0x30000000 0x0 0x400000 0x4 0x0 0x0 0x600450>; /* Mandatory */ cpus { #address-cells = <0x1>; #size-cells = <0x0>; /* * The following node must exist for each *core* in the system. The unit * address (number after the @) is the hexadecimal HW CPU number (PIR value) * of thread 0 of that core. */ PowerPC,POWER8@20 { /* mandatory/standard properties */ device_type = "cpu"; 64-bit; 32-64-bridge; graphics; general-purpose; /* * The "status" property indicate whether the core is functional. It's * a string containing "okay" for a good core or "bad" for a non-functional * one. You can also just ommit the non-functional ones from the DT */ status = "okay"; /* * This is the same value as the PIR of thread 0 of that core * (ie same as the @xx part of the node name) */ reg = <0x20>; /* same as above */ ibm,pir = <0x20>; /* chip ID of this core */ ibm,chip-id = <0x0>; /* * interrupt server numbers (aka HW processor numbers) of all threads * on that core. This should have 8 numbers and the first one should * have the same value as the above ibm,pir and reg properties */ ibm,ppc-interrupt-server#s = <0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27>; /* * This is the "architected processor version" as defined in PAPR. Just * stick to 0x0f000004 for P8 and things will be fine */ cpu-version = <0x0f000004>; /* * These are various definitions of the page sizes and segment sizes * supported by the MMU, those values are fine for P8 for now */ ibm,processor-segment-sizes = <0x1c 0x28 0xffffffff 0xffffffff>; ibm,processor-page-sizes = <0xc 0x10 0x18 0x22>; ibm,segment-page-sizes = <0xc 0x0 0x3 0xc 0x0 0x10 0x7 0x18 0x38 0x10 0x110 0x2 0x10 0x1 0x18 0x8 0x18 0x100 0x1 0x18 0x0 0x22 0x120 0x1 0x22 0x3>; /* * Similarily that might need to be reviewed later but will do for now... */ ibm,pa-features = [0x6 0x0 0xf6 0x3f 0xc7 0x0 0x80 0xc0]; /* SLB size, use as-is */ ibm,slb-size = <0x20>; /* VSX support, use as-is */ ibm,vmx = <0x2>; /* DFP support, use as-is */ ibm,dfp = <0x2>; /* PURR/SPURR support, use as-is */ ibm,purr = <0x1>; ibm,spurr = <0x1>; /* * Old-style core clock frequency. Only create this property if the frequency fits * in a 32-bit number. Do not create it if it doesn't */ clock-frequency = <0xf5552d00>; /* * mandatory: 64-bit version of the core clock frequency, always create this * property. */ ibm,extended-clock-frequency = <0x0 0xf5552d00>; /* Timebase freq has a fixed value, always use that */ timebase-frequency = <0x1e848000>; /* Same */ ibm,extended-timebase-frequency = <0x0 0x1e848000>; /* Use as-is, values might need to be adjusted but that will do for now */ reservation-granule-size = <0x80>; d-tlb-size = <0x800>; i-tlb-size = <0x0>; tlb-size = <0x800>; d-tlb-sets = <0x4>; i-tlb-sets = <0x0>; tlb-sets = <0x4>; d-cache-block-size = <0x80>; i-cache-block-size = <0x80>; d-cache-size = <0x10000>; i-cache-size = <0x8000>; i-cache-sets = <0x4>; d-cache-sets = <0x8>; performance-monitor = <0x0 0x1>; /* * optional: phandle of the node representing the L2 cache for this core, * note: it can also be named "next-level-cache", Linux will support both * and Sapphire doesn't currently use those properties, just passes them * along to Linux */ l2-cache = < 0x4 >; }; /* * Cache nodes. Those are siblings of the processor nodes under /cpus and * represent the various level of caches. * * The unit address (and reg property) is mostly free-for-all as long as * there is no collisions. On HDAT machines we use the following encoding * which I encourage you to also follow to limit surprises: * * L2 : (0x20 << 24) | PIR (PIR is PIR value of thread 0 of core) * L3 : (0x30 << 24) | PIR * L3.5 : (0x35 << 24) | PIR * * In addition, each cache points to the next level cache via its * own "l2-cache" (or "next-level-cache") property, so the core node * points to the L2, the L2 points to the L3 etc... */ l2-cache@20000020 { phandle = <0x4>; device_type = "cache"; reg = <0x20000020>; status = "okay"; cache-unified; d-cache-sets = <0x8>; i-cache-sets = <0x8>; d-cache-size = <0x80000>; i-cache-size = <0x80000>; l2-cache = <0x5>; }; l3-cache@30000020 { phandle = <0x5>; device_type = "cache"; reg = <0x30000020>; status = "bad"; cache-unified; d-cache-sets = <0x8>; i-cache-sets = <0x8>; d-cache-size = <0x800000>; i-cache-size = <0x800000>; }; }; /* * Interrupt presentation controller (ICP) nodes * * There is some flexibility as to how many of these are presents since * a given node can represent multiple ICPs. When generating from HDAT we * chose to create one per core */ interrupt-controller@3ffff80020000 { /* Mandatory */ compatible = "IBM,ppc-xicp", "IBM,power8-icp"; interrupt-controller; #address-cells = <0x0>; #interrupt-cells = <0x1>; device_type = "PowerPC-External-Interrupt-Presentation"; /* * Range of HW CPU IDs represented by that node. In this example * the core starting at PIR 0x20 and 8 threads, which corresponds * to the CPU node of the example above. The property in theory * supports multiple ranges but Linux doesn't. */ ibm,interrupt-server-ranges = <0x20 0x8>; /* * For each server in the above range, the physical address of the * ICP register block and its size. Since the root node #address-cells * and #size-cells properties are both "2", each entry is thus * 2 cells address and 2 cells size (64-bit each). */ reg = <0x3ffff 0x80020000 0x0 0x1000 0x3ffff 0x80021000 0x0 0x1000 0x3ffff 0x80022000 0x0 0x1000 0x3ffff 0x80023000 0x0 0x1000 0x3ffff 0x80024000 0x0 0x1000 0x3ffff 0x80025000 0x0 0x1000 0x3ffff 0x80026000 0x0 0x1000 0x3ffff 0x80027000 0x0 0x1000>; }; /* * The "memory" nodes represent physical memory in the system. They * do not represent DIMMs, memory controllers or Centaurs, thus will * be expressed separately. * * In order to be able to handle affinity propertly, we require that * a memory node is created for each range of memory that has a different * "affinity", which in practice means for each chip since we don't * support memory interleaved accross multiple chips on P8. * * Additionally, it is *not* required that one chip = one memory node, * it is perfectly acceptable to break down the memory of one chip into * multiple memory nodes (typically skiboot does that if the two MCs * are not interlaved). */ memory@0 { device_type = "memory"; /* * We support multiple entries in the ibm,chip-id property for * memory nodes in case the memory is interleaved accross multiple * chips but that shouldn't happen on P8 */ ibm,chip-id = <0x0>; /* The "reg" property is 4 cells, as usual for a child of * the root node, 2 cells of address and 2 cells of size */ reg = <0x0 0x0 0x4 0x0>; }; /* * The XSCOM node. This is the closest thing to a "chip" node we have. * there must be one per chip in the system (thus a DCM has two) and * while it represents the "parent" of various devices on the PIB/PCB * that we want to expose, it is also used to store all sort of * miscellaneous per-chip information on HDAT based systems (such * as VPDs). */ xscom@3fc0000000000 { /* standard & mandatory */ #address-cells = <0x1>; #size-cells = <0x1>; scom-controller; compatible = "ibm,xscom", "ibm,power8-xscom"; /* The chip ID as usual ... */ ibm,chip-id = <0x0>; /* The base address of xscom for that chip */ reg = <0x3fc00 0x0 0x8 0x0>; /* * This comes from HDAT and I *think* is the raw content of the * module VPD eeprom (and thus doesn't have a standard ASCII keyword * VPD format). We don't currently use it though ... */ ibm,module-vpd = < ... big pile of binary data ... >; /* PSI host bridge XSCOM register set */ psihb@2010900 { reg = <0x2010900 0x20>; compatible = "ibm,power8-psihb-x", "ibm,psihb-x"; }; /* Chip TOD XSCOM register set */ chiptod@40000 { reg = <0x40000 0x34>; compatible = "ibm,power-chiptod", "ibm,power8-chiptod"; /* * Create that property with no value if this chip has * the Primary TOD in the topology. If it has the secondary * one (backup master ?) use "secondary". */ primary; }; /* NX XSCOM register set */ nx@2010000 { reg = <0x2010000 0x4000>; compatible = "ibm,power-nx", "ibm,power8-nx"; }; /* * PCI "PE Master" XSCOM register set for each active PHB * * For now, do *not* create these if the PHB isn't connected, * clocked, or the PHY/HSS not configured. */ pbcq@2012000 { reg = <0x2012000 0x20 0x9012000 0x5 0x9013c00 0x15>; compatible = "ibm,power8-pbcq"; /* Indicate the PHB index on the chip, ie, 0,1 or 2 */ ibm,phb-index = <0x0>; /* Create that property to use the IBM-style "A/B" dual input * slot presence detect mechanism. */ ibm,use-ab-detect; /* * TBD: Lane equalization values. Not currently used by * skiboot but will have to be sorted out */ ibm,lane_eq = <0x0>; }; pbcq@2012400 { reg = <0x2012400 0x20 0x9012400 0x5 0x9013c40 0x15>; compatible = "ibm,power8-pbcq"; ibm,phb-index = <0x1>; ibm,use-ab-detect; ibm,lane_eq = <0x0>; }; /* * Here's the LPC bus. Ideally each chip has one but in * practice it's ok to only populate the ones actually * used for something. This is not an exact representation * of HW, in that case we would have eccb -> opb -> lpc, * but instead we just have an lpc node and the address is * the base of the ECCB register set for it * * Devices on the LPC are represented as children nodes, * see example below for a standard UART. */ lpc@b0020 { /* * Empty property indicating this is the primary * LPC bus. It will be used for the default UART * if any and this is the bus that will be used * by Linux as the virtual 64k of IO ports */ primary; /* * 2 cells of address, the first one indicates the * address type, see below */ #address-cells = <0x2>; #size-cells = <0x1>; reg = <0xb0020 0x4>; compatible = "ibm,power8-lpc"; /* * Example device: a UART on IO ports. * * LPC address have 2 cells. The first cell is the * address type as follow: * * 0 : LPC memory space * 1 : LPC IO space * 2: LPC FW space * * (This corresponds to the OPAL_LPC_* arguments * passed to the opal_lpc_read/write functions) * * The unit address follows the old ISA convention * for open firmware which prefixes IO ports with "i". * * (This is not critical and can be 1,3f8 if that's * problematic to generate) */ serial@i3f8 { reg = <0x1 0x3f8 8>; compatible = "ns16550", "pnpPNP,501"; /* Baud rate generator base frequency */ clock-frequency = < 1843200 >; /* Default speed to use */ current-speed = < 115200 >; /* Historical, helps Linux */ device_type = "serial"; /* * Indicate which chip ID the interrupt * is routed to (we assume it will always * be the "host error interrupt" (aka * "TPM interrupt" of that chip). */ ibm,irq-chip-id = <0x0>; } }; }; }; skiboot-skiboot-5.1.13/doc/device-tree/000077500000000000000000000000001265204436200176705ustar00rootroot00000000000000skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal.txt000066400000000000000000000031251265204436200221310ustar00rootroot00000000000000 ibm,opal { #address-cells = <0x0>; #size-cells = <0x0>; compatible = "ibm,opal-v2", "ibm,opal-v3"; ; v2 is maintained for possible compatibilty with very, very old kernels ; it will go away at some point in the future. Detect and rely on ibm,opal-v3 ibm,associativity-reference-points = <0x4 0x3>; ibm,heartbeat-ms = <0x7d0>; ; how often any OPAL call needs to be made to avoid a watchdog timer on BMC ; from kicking in ibm,opal-memcons = <0x0 0x3007a000>; ; location of in memory OPAL console buffer. ibm,opal-trace-mask = <0x0 0x3008c3f0>; ibm,opal-traces = <0x0 0x3007b010 0x0 0x10077 0x0 0x3b001010 0x0 0x1000a7 0x0 0x3b103010 0x0 0x1000a7 0x0 0x3b205010 0x0 0x1000a7 0x0 0x3b307010 0x0 0x1000a7 0x0 0x3b409010 0x0 0x1000a7 0x10 0x1801010 0x0 0x1000a7 0x10 0x1903010 0x0 0x1000a7 0x10 0x1a05010 0x0 0x1000a7 0x10 0x1b07010 0x0 0x1000a7 0x10 0x1c09010 0x0 0x1000a7 0x10 0x1d0b010 0x0 0x1000a7 0x10 0x1e0d010 0x0 0x1000a7 0x10 0x1f0f010 0x0 0x1000a7 0x10 0x2011010 0x0 0x1000a7 0x10 0x2113010 0x0 0x1000a7 0x10 0x2215010 0x0 0x1000a7 0x10 0x2317010 0x0 0x1000a7 0x10 0x2419010 0x0 0x1000a7 0x10 0x251b010 0x0 0x1000a7 0x10 0x261d010 0x0 0x1000a7>; ; see docs on tracing linux,phandle = <0x10000003>; opal-base-address = <0x0 0x30000000>; opal-entry-address = <0x0 0x300050c0>; opal-interrupts = <0x10 0x11 0x12 0x13 0x14 0x20010 0x20011 0x20012 0x20013 0x20014 0xffe 0xfff 0x17fe 0x17ff 0x2ffe 0x2fff 0x37fe 0x37ff 0x20ffe 0x20fff 0x22ffe 0x22fff 0x237fe 0x237ff>; opal-msg-async-num = <0x8>; opal-msg-size = <0x48>; opal-runtime-size = <0x0 0x9a00000>; phandle = <0x10000003>; skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/000077500000000000000000000000001265204436200213675ustar00rootroot00000000000000skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/diagnostics.txt000066400000000000000000000004411265204436200244360ustar00rootroot00000000000000 ibm,opal/diagnostics device tree entries ---------------------------------- The diagnostics node under ibm,opal describes a userspace-to-firmware interface, supporting the runtime processor recovery diagnostics functions. The properties of a prd node are: compatible = "ibm,opal-prd" skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/firmware.txt000066400000000000000000000016331265204436200237470ustar00rootroot00000000000000System Firmware --------------- The 'firmware' node under 'ibm,opal' lists system and OPAL firmware version. firmware { symbol-map = <0x0 0x300ac650 0x0 0x1b3f5>; compatible = "ibm,opal-firmware"; ml-version = [4d 4c 20 46 57 37 37 30 2e 32 30 20 46 57 37 37 30 2e 32 30 20 46 57 37 37 30 2e 32 30]; mi-version = <0x4d49205a 0x4c373730 0x5f303735 0x205a4c37 0x37305f30 0x3735205a 0x4c373730 0x5f303735>; version = "skiboot-5.0-rc2"; phandle = <0x8e>; linux,phandle = <0x8e>; }; 'compatible' property describes OPAL compatibility. 'symbol-map' property describes OPAL symbol start address and size. 'version' property describes OPAL version. Replaces 'git-id', so may not be present. 'mi-version' property describes Microcode Image. Only on IBM FSP systems. 'ml-version' property describes Microcode Level. Only on IBM FSP systems. MI/ML format:

skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/flash.txt000066400000000000000000000020071265204436200232240ustar00rootroot00000000000000ibm,opal/flash device tree entries ---------------------------------- The flash@ nodes under ibm,opal describe flash devices that can be accessed through the OPAL_FLASH_{READ,ERASE,WRITE} interface. These interfaces take an 'id' parameter, which corresponds to the ibm,opal-id property of the node. The properties under a flash node are: compatible = "ibm,opal-flash" ibm,opal-id = - provides the index used for the OPAL_FLASH_ calls to reference this flash device reg = <0 size> - the offset and size of the flash device ibm,flash-block-size - the read/write/erase block size for the flash interface. Calls to read/write/erase must be aligned to the block size. #address-cells = <1> #size-cells = <1> - flash devices are currently 32-bit addressable If valid partitions are found on the flash device, then partition@ sub-nodes are added to the flash node. These match the Linux binding for flash partitions; the reg parameter contains the offset and size of the partition. skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/led.txt000066400000000000000000000015501265204436200226750ustar00rootroot00000000000000Service Indicators (LEDS) ------------------------- The 'leds' node under 'ibm,opal' lists service indicators available in the system and their capabilities. leds { compatible = "ibm,opal-v3-led"; phandle = <0x1000006b>; linux,phandle = <0x1000006b>; led-mode = "lightpath"; U78C9.001.RST0027-P1-C1 { led-types = "identify", "fault"; phandle = <0x1000006f>; linux,phandle = <0x1000006f>; }; ... ... }; 'compatible' property describes LEDs compatibility. 'led-mode' property describes service indicator mode (lightpath/guidinglight). Each node under 'leds' node describes location code of FRU/Enclosure. The properties under each node: led-types : Supported indicators (attention/identify/fault). These LEDs can be accessed through OPAL_LEDS_{GET/SET}_INDICATOR interfaces. Refer to doc/opal-api/opal-led-get-set-114-115.txt for interface details. skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/oppanel.txt000066400000000000000000000012671265204436200235740ustar00rootroot00000000000000Operator Panel (oppanel) ------------------------ oppanel { compatible = "ibm,opal-oppanel"; #lines = <0x2>; #length = <0x10>; }; The Operator Panel is a device for displaying small amounts of textual data to an administrator. On IBM POWER8 systems with an FSP, this is a small 16x2 LCD panel that can be viewed either from the Web UI of the FSP (known as ASM) or by physically going to the machine and looking at the panel. The operator panel does not have to be present. If it is, there are OPAL calls to read and write to it. The device tree entry is so that the host OS knows the size of the panel and can pass buffers of the appropriate size to the OPAL calls. skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/power-mgt.txt000066400000000000000000000043161265204436200240550ustar00rootroot00000000000000ibm,opal/power-mgt device tree entries -------------------------------------- All available CPU idle states are listed in ibm,cpu-idle-state-names For example: ibm,cpu-idle-state-names = "nap", "fastsleep_", "winkle"; The idle states are characterized by latency and residency numbers which determine the breakeven point for entry into them. The latency is a measure of the exit overhead from the idle state and residency is the minimum amount of time that a CPU must be predicted to be idle so as to reap the powersavings from entering into that idle state. These numbers are made use of by the cpuidle governors in the kernel to arrive at the appropriate idle state that a CPU must enter into when there is no work to be done. The values in ibm,cpu-idle-state-latencies-ns are the the measured latency numbers for the idle states. The residency numbers have been arrived at experimentally after ensuring that the performance of latency sensitive workloads do not regress while allowing deeper idle states to be entered into during low load situations. The kernel is expected to use these values for optimal power efficiency. ibm,cpu-idle-state-residency-ns = <0x1 0x2 0x3> ibm,cpu-idle-state-latencies-ns = <0x1 0x2 0x3> ibm,pstate-ids -------------- This property lists the available pstate identifiers, as signed 32-bit big-endian values. While the identifiers are somewhat arbitrary, these define the order of the pstates in other ibm,pstate-* properties. ibm,pstate-frequencies-mhz -------------------------- This property lists the frequency, in MHz, of each of the pstates listed in the ibm,pstate-ids file. Each frequency is a 32-bit big-endian word. ibm,pstate-max ibm,pstate-min ibm,pstate-nominal ------------------------------------------------ These properties give the maximum, minimum and nominal pstate values, as an id specified in the ibm,pstate-ids file. ibm,pstate-vcss ibm,pstate-vdds ------------------------------- These properties list a voltage-identifier of each of the pstates listed in ibm,pstate-ids for the Vcs and Vdd values used for that pstate. Each VID is a single byte. FIXME: document these: ibm,cpu-idle-state-flags ibm,cpu-idle-state-names ibm,cpu-idle-state-pmicr ibm,cpu-idle-state-pmicr-mask skiboot-skiboot-5.1.13/doc/device-tree/ibm,opal/sensors.txt000066400000000000000000000043561265204436200236340ustar00rootroot00000000000000ibm,opal/sensors/ device tree nodes -------------------------------------- All sensors of a POWER8 system are made available to the OS in the ibm,opal/sensors/ directory. Each sensor is identified with a node which name follows this pattern : @/ For example : core-temp@20/ Each node has a minimum set of properties describing the sensor : - a "compatible" property which should be "ibm,opal-sensor" - a "sensor-type" property, which can be "temp", "fan", "power". More will be added when new resources are supported. This type is used "as is" by the Linux driver to map sensors in the sysfs interface of the hwmon framework of Linux. - a "sensor-data" property giving a unique handler for the OPAL_SENSOR_READ call to be used by Linux to get the value of a sensor attribute. A sensor handler has the following encoding : | Attr. | Res. | Resource | | Number | Class | Id | |--------|--------|----------------| - a "sensor-status" property giving the state of the sensor. The status bits have the slightly meanings depending on the resource type but testing against 0x6 should raise an alarm. - an optional "label" property Each node can have some extra properties depending on the resource they represent. See the tree below for more information. ibm,opal/sensors/ { /* * Core temperatures (DTS) nodes. * * We use the PIR of the core as a resource identifier. */ core-temp@20 { compatible = "ibm,opal-sensor"; name = "core-temp"; sensor-type = "temp"; /* Status bits : * * 0x0003 FATAL * 0x0002 CRITICAL * 0x0001 WARNING */ sensor-data = <0x00800020>; /* * These are extra properties to help Linux output. */ ibm,pir = <0x20>; label = "Core"; }; /* * Centaur temperatures (DTS) nodes. Open Power only. * * We use the PIR of the core as a resource identifier. */ mem-temp@1 { compatible = "ibm,opal-sensor"; name = "mem-temp"; sensor-type = "temp"; /* Status bits : * * 0x0003 FATAL * 0x0002 CRITICAL * 0x0001 WARNING */ sensor-data = <0x00810001>; /* * These are extra properties to help Linux output. */ ibm,chip-id = <0x80000001>; label = "Centaur"; }; }; skiboot-skiboot-5.1.13/doc/device-tree/nx.txt000066400000000000000000000031621265204436200210600ustar00rootroot00000000000000Nest (NX) Accelerator Coprocessor --------------------------------- The NX coprocessor is present in P7+ or later processors. Each NX node represents a unique NX coprocessor. The nodes are located under an xscom node, as: /xscom@/nx@ With unique xscom and nx addresses. Their compatible node contains "ibm,power-nx". NX 842 Coprocessor ------------------ This is the memory compression coprocessor, which uses the IBM proprietary 842 compression algorithm and format. Each nx node contains an 842 engine. ibm,842-coprocessor-type : CT value common to all 842 coprocessors ibm,842-coprocessor-instance : CI value unique to all 842 coprocessors Access to the coprocessor requires using the ICSWX instruction, which uses a specific format including a Coprocessor Type (CT) and Coprocessor Instance (CI) value to address each request to the right coprocessor. The driver should use the CT and CI values for a particular node to communicate with it. For all 842 coprocessors in the system, the CT value will (should) be the same, while each will have a different CI value. The driver can use CI 0 to allow the hardware to automatically select which coprocessor instance to use. NX RNG Coprocessor ------------------ This is the Random Number Generator (RNG) coprocessor, which is a part of each NX coprocessor. Each node represents a unique RNG coprocessor. Its nodes are not under the main nx node, they are located at: /hwrng@ : RNG at address ibm,chip-id : chip id where the RNG is reg : address of the register to read from Each read from the RNG register will provide a new random number. skiboot-skiboot-5.1.13/doc/device-tree/reserved-memory.txt000066400000000000000000000017351265204436200235640ustar00rootroot00000000000000reserved-memory device tree nodes OPAL exposes reserved memory through a top-level reserved-memory node, containing subnodes that represent each reserved memory region. This follows the Linux specification for the /reserved-memory node, described in the kernel source tree, in: Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt The top-level /reserved-memory node contains: #size-cells = <2> #address-cells = <2> - addresses and sizes are all 64-bits ranges; - the empty ranges node indicates no translation of physical addresses in the subnodes. The sub-nodes under the /reserved-memory node contain: reg =

- the address and size of the reserved memory region. The address and size values are two cells each, as signified by the top-level #{address,size}-cells ibm,prd-label = "string" - a string token for use by the prd system. Specific ranges may be used by prd - those will be referenced by this label. skiboot-skiboot-5.1.13/doc/device-tree/vpd.txt000066400000000000000000000052471265204436200212320ustar00rootroot00000000000000VPD (Vital Product Data) ------------------------ VPD provides the information about the FRUs (Field Replaceable Unit) present in the system and each vpd node in the device tree represents a FRU. These node and their properties are specific to the FSP-based systems, passed to the skiboot in the form of FSP-defined HDAT structures. skiboot parses these structures and add respective nodes in the device tree. /vpd : VPD root node @ : Node name ibm,vpd : VPD data binary blob ccin : Customer Card Identification Number fru-type : FRU type label (2 bytes ASCII character) fru-number : FRU stocking part number ibm,loc-code : Location code part-number : Part number serial-number : Serial number ibm,chip-id : Processor Id size : DIMM size (applicable for DIMM VPD only) ibm,memory-bus-frequency: DIMM frequency (applicable for DIMM VPD only) The VPD tree in the device tree depicts the hierarchial structure of the FRUs having parent-child relationship. root-node-vpd@a000 |-- enclosure@1e00 | |-- air-mover@3a00 | |-- air-mover@3a01 | |-- backplane@800 | | |-- anchor-card@500 | | |-- backplane-extender@900 | | | |-- serial-connector@2a00 | | | |-- usb-connector@2900 | | | `-- usb-connector@2901 | | |-- ethernet-connector@2800 | | |-- ethernet-connector@2801 | | |-- ms-dimm@d002 | | |-- ms-dimm@d003 | | |-- processor@1000 | | |-- processor@1001 | | |-- usb-connector@2902 | | |-- usb-connector@2903 | | |-- usb-connector@2904 | | `-- usb-connector@2905 | |-- dasd-backplane@2400 | |-- dasd-backplane@2401 | |-- power-supply@3103 | `-- service-processor@200 |-- enclosure-fault-led@a300 |-- enclosure-led@a200 |-- root-node-vpd@a001 `-- system-vpd@1c00 Example vpd node: anchor-card@500 { ccin = "52FE"; fru-number = "00E2147"; description = "System Anchor Card - IBM Power 824"; ibm,loc-code = "U78C9.001.WZS007X-P1-C13"; serial-number = "YL10113BJ001"; ibm,vpd = <0x84cc0052 0x54045649 0x4e494452 0x10414e43 0x484f5220 0x20202020 0x20202020 0x20434501 0x31565a02 0x3031464e 0x7303045 0x32313437 0x504e0730 0x30453231 0x3438534e 0xc594c31 0x30313133 0x424a3030 0x31434304 0x35324645 0x50520881 0x300000 0x48 0x45043030 0x31304354 0x440b400 0x485702 0x14233 0x6000000 0x142 0x34010042 0x370c0000 0x0 0x0 0x4239 0x3c435333 0x22071917 0xd1569c53 0x50973c87 0x71f9c40 0x1d4d3142 0x985e80f1 0x5cb3614d 0x32a902cb 0xd9d714ab 0x164d3322 0xdda4f986 0x5a618f4d 0x340b157c 0x2cac0a94 0x6504603 0x78 0x0>; fru-type = [41 56]; part-number = "00E2148"; phandle = <0x8d>; linux,phandle = <0x8d>; }; skiboot-skiboot-5.1.13/doc/error-logging.txt000066400000000000000000000505761265204436200210270ustar00rootroot00000000000000How to log errors on Sapphire and POWERNV: ========================================= Currently the errors reported by POWERNV/Sapphire (OPAL) interfaces are in free form, where as errors reported by FSP is in standard Platform Error Log (PEL) format. For out-of band management via IPMI interfaces, it is necessary to push down the errors to FSP via mailbox (reported by POWERNV/Sapphire) in PEL format. PEL size can vary from 2K-16K bytes, fields of which needs to populated based on the kind of event and error that needs to be reported. All the information needed to be reported as part of the error, is passed by user using the error-logging interfaces outlined below. Following which, PEL structure is generated based on the input and then passed on to FSP. Error logging interfaces in Sapphire: ==================================== Interfaces are provided for the user to log/report an error in Sapphire. Using these interfaces relevant error information is collected and later converted to PEL format and then pushed to FSP. Step 1: To report an error, invoke opal_elog_create() with required argument. struct errorlog *opal_elog_create(struct opal_err_info *e_info, uint32_t tag); Parameters: struct opal_err_info *e_info: Struct to hold information identifying error/event source. uint32_t tag: Unique value to identify the data. Ideal to have ASCII value for 4-byte string. The opal_err_info struct holds several pieces of information to help identify the error/event. The struct can be obtained via the DEFINE_LOG_ENTRY macro as below - it only needs to be called once. DEFINE_LOG_ENTRY(OPAL_RC_ATTN, OPAL_PLATFORM_ERR_EVT, OPAL_CHIP, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); The various attributes set by this macro are described below. uint8_t opal_error_event_type: Classification of error/events type reported on OPAL /* Platform Events/Errors: Report Machine Check Interrupt */ #define OPAL_PLATFORM_ERR_EVT 0x01 /* INPUT_OUTPUT: Report all I/O related events/errors */ #define OPAL_INPUT_OUTPUT_ERR_EVT 0x02 /* RESOURCE_DEALLOC: Hotplug events and errors */ #define OPAL_RESOURCE_DEALLOC_ERR_EVT 0x03 /* MISC: Miscellanous error */ #define OPAL_MISC_ERR_EVT 0x04 uint16_t component_id: Component ID of Sapphire component as listed in include/errorlog.h uint8_t subsystem_id: ID of the sub-system reporting error. /* OPAL Subsystem IDs listed for reporting events/errors */ #define OPAL_PROCESSOR_SUBSYSTEM 0x10 #define OPAL_MEMORY_SUBSYSTEM 0x20 #define OPAL_IO_SUBSYSTEM 0x30 #define OPAL_IO_DEVICES 0x40 #define OPAL_CEC_HARDWARE 0x50 #define OPAL_POWER_COOLING 0x60 #define OPAL_MISC 0x70 #define OPAL_SURVEILLANCE_ERR 0x7A #define OPAL_PLATFORM_FIRMWARE 0x80 #define OPAL_SOFTWARE 0x90 #define OPAL_EXTERNAL_ENV 0xA0 uint8_t event_severity: Severity of the event/error to be reported #define OPAL_INFO 0x00 #define OPAL_RECOVERED_ERR_GENERAL 0x10 /* 0x2X series is to denote set of Predictive Error */ /* 0x20 Generic predictive error */ #define OPAL_PREDICTIVE_ERR_GENERAL 0x20 /* 0x21 Predictive error, degraded performance */ #define OPAL_PREDICTIVE_ERR_DEGRADED_PERF 0x21 /* 0x22 Predictive error, fault may be corrected after reboot */ #define OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT 0x22 /* * 0x23 Predictive error, fault may be corrected after reboot, * degraded performance */ #define OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_BOOT_DEGRADE_PERF 0x23 /* 0x24 Predictive error, loss of redundancy */ #define OPAL_PREDICTIVE_ERR_LOSS_OF_REDUNDANCY 0x24 /* 0x4X series for Unrecoverable Error */ /* 0x40 Generic Unrecoverable error */ #define OPAL_UNRECOVERABLE_ERR_GENERAL 0x40 /* 0x41 Unrecoverable error bypassed with degraded performance */ #define OPAL_UNRECOVERABLE_ERR_DEGRADE_PERF 0x41 /* 0x44 Unrecoverable error bypassed with loss of redundancy */ #define OPAL_UNRECOVERABLE_ERR_LOSS_REDUNDANCY 0x44 /* 0x45 Unrecoverable error bypassed with loss of redundancy and performance */ #define OPAL_UNRECOVERABLE_ERR_LOSS_REDUNDANCY_PERF 0x45 /* 0x48 Unrecoverable error bypassed with loss of function */ #define OPAL_UNRECOVERABLE_ERR_LOSS_OF_FUNCTION 0x48 #define OPAL_ERROR_PANIC 0x50 uint8_t event_subtype: Event Sub-type #define OPAL_NA 0x00 #define OPAL_MISCELLANEOUS_INFO_ONLY 0x01 #define OPAL_PREV_REPORTED_ERR_RECTIFIED 0x10 #define OPAL_SYS_RESOURCES_DECONFIG_BY_USER 0x20 #define OPAL_SYS_RESOURCE_DECONFIG_PRIOR_ERR 0x21 #define OPAL_RESOURCE_DEALLOC_EVENT_NOTIFY 0x22 #define OPAL_CONCURRENT_MAINTENANCE_EVENT 0x40 #define OPAL_CAPACITY_UPGRADE_EVENT 0x60 #define OPAL_RESOURCE_SPARING_EVENT 0x70 #define OPAL_DYNAMIC_RECONFIG_EVENT 0x80 #define OPAL_NORMAL_SYS_PLATFORM_SHUTDOWN 0xD0 #define OPAL_ABNORMAL_POWER_OFF 0xE0 uint8_t opal_srctype: SRC type, value should be OPAL_SRC_TYPE_ERROR. SRC refers to System Reference Code. It is 4 byte hexa-decimal number that reflects the current system state. Eg: BB821010, 1st byte -> BB -> SRC Type 2nd byte -> 82 -> Subsystem 3rd, 4th byte -> Component ID and Reason Code SRC needs to be generated on the fly depending on the state of the system. All the parameters needed to generate a SRC should be provided during reporting of an event/error. uint32_t reason_code: Reason for failure as stated in include/errorlog.h for Sapphire Eg: Reason code for code-update failures can be OPAL_RC_CU_INIT -> Initialisation failure OPAL_RC_CU_FLASH -> Flash failure Step 2: Data can be appended to the user data section using the either of the below two interfaces: void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size) Parameters: struct opal_errorlog *buf: struct opal_errorlog *buf: struct opal_errorlog pointer returned by opal_elog_create() call. unsigned char *data: Pointer to the dump data uint16_t size: Size of the dump data. void log_append_msg(struct errorlog *buf, const char *fmt, ...) Parameters: struct opal_errorlog *buf: struct opal_errorlog *buf: struct opal_errorlog pointer returned by opal_elog_create() call. const char *fmt: Formatted error log string. Additional user data sections can be added to the error log to separate data (eg. readable text vs binary data) by calling log_add_section(). The interfaces in Step 2 operate on the 'last' user data section of the error log. void log_add_section(struct errorlog *buf, uint32_t tag); Parameters: struct opal_errorlog *buf: struct opal_errorlog *buf: struct opal_errorlog pointer returned by opal_elog_create() call. uint32_t tag: Unique value to identify the data. Ideal to have ASCII value for 4-byte string. Step 3: Once all the data for an error is logged in, the error needs to be committed in FSP. rc = elog_fsp_commit(buf); Value of 0 is returned on success. In the process of committing an error to FSP, log info is first internally converted to PEL format and then pushed to the FSP. All the errors logged in Sapphire are again pushed up to POWERNV platform by the FSP and all the errors reported by Sapphire and POWERNV are logged in FSP. If the user does not intend to dump various user data sections, but just log the error with some amount of description around that error, they can do so using just the simple error logging interface log_simple_error(uint32_t reason_code, char *fmt, ...); Eg: log_simple_error(OPAL_RC_SURVE_STATUS, "SURV: Error retreiving surveillance status: %d\n", err_len); Using the reason code, an error log is generated with the information derived from the look-up table, populated and committed to FSP. All of it is done with just one call. Note: ==== * For more information regarding error logging and PEL format refer to PAPR doc and P7 PEL and SRC PLDD document. * Refer to include/opal.h for all the error logging interface parameters and include/fsp-pel.h for PEL structures. Sample error logging: =================== DEFINE_LOG_ENTRY(OPAL_RC_ATTN, OPAL_PLATFORM_ERR_EVT, OPAL_ATTN, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); void report_error(int index) { struct errorlog *buf; char data1[] = "This is a sample user defined data section1"; char data2[] = "Error logging sample. These are dummy errors. Section 2"; char data3[] = "Sample error Sample error Sample error Sample error \ Sample error abcdefghijklmnopqrstuvwxyz"; int tag; printf("ELOG: In machine check report error index: %d\n", index); /* To report an error, create an error log with relevant information * opal_elog_create(). Call returns a pre-allocated buffer of type * 'struct errorlog' buffer with relevant fields updated. */ /* tag -> unqiue ascii tag to identify a particular data dump section */ tag = 0x4b4b4b4b; buf = opal_elog_create(&e_info(OPAL_RC_ATTN), tag); if (buf == NULL) { printf("ELOG: Error getting buffer.\n"); return; } /* Append data or text with log_append_data() or log_append_msg() */ log_append_data(buf, data1, sizeof(data1)); /* In case of user wanting to add multiple sections of various dump data * for better debug, data sections can be added using this interface * void log_add_section(struct errorlog *buf, uint32_t tag); */ tag = 0x4c4c4c4c; log_add_section(buf, tag); log_append_data(buf, data2, sizeof(data2)); log_append_data(buf, data3, sizeof(data3)); /* Once all info is updated, ready to be sent to FSP */ printf("ELOG:commit to FSP\n"); log_commit(buf); } Sample output PEL dump got from FSP: =================================== $ errl -d -x 0x533C9B37 | 00000000 50480030 01004154 20150728 02000500 PH.0..AT ..(.... | | 00000010 20150728 02000566 4B000107 00000000 ..(...fK....... | | 00000020 00000000 00000000 B0000002 533C9B37 ............S..7 | | 00000030 55480018 01004154 80002000 00000000 UH....AT.. ..... | | 00000040 00002000 01005300 50530050 01004154 .. ...S.PS.P..AT | | 00000050 02000008 00000048 00000080 00000000 .......H........ | | 00000060 00000000 00000000 00000000 00000000 ................ | | 00000070 00000000 00000000 42423832 31343130 ........BB821410 | | 00000080 20202020 20202020 20202020 20202020 | | 00000090 20202020 20202020 4548004C 01004154 EH.L..AT | | 000000A0 38323836 2D343241 31303738 34415400 8286-42A10784AT. | | 000000B0 00000000 00000000 00000000 00000000 ................ | | 000000C0 00000000 00000000 00000000 00000000 ................ | | 000000D0 00000000 00000000 20150728 02000500 ........ ..(.... | | 000000E0 00000000 4D54001C 01004154 38323836 ....MT....AT8286 | | 000000F0 2D343241 31303738 34415400 00000000 -42A10784AT..... | | 00000100 5544003C 01004154 4B4B4B4B 00340000 UD....ATKKKK.4.. | | 00000110 54686973 20697320 61207361 6D706C65 This is a sample | | 00000120 20757365 72206465 66696E65 64206461 user defined da | | 00000130 74612073 65637469 6F6E3100 554400A7 ta section1.UD.. | | 00000140 01004154 4C4C4C4C 009F0000 4572726F ..ATLLLL....Erro | | 00000150 72206C6F 6767696E 67207361 6D706C65 r logging sample | | 00000160 2E205468 65736520 61726520 64756D6D . These are dumm | | 00000170 79206572 726F7273 2E205365 6374696F y errors. Sectio | | 00000180 6E203200 53616D70 6C652065 72726F72 n 2.Sample error | | 00000190 2053616D 706C6520 6572726F 72205361 Sample error Sa | | 000001A0 6D706C65 20657272 6F722053 616D706C mple error Sampl | | 000001B0 65206572 726F7220 09090953 616D706C e error ...Sampl | | 000001C0 65206572 726F7220 61626364 65666768 e error abcdefgh | | 000001D0 696A6B6C 6D6E6F70 71727374 75767778 ijklmnopqrstuvwx | | 000001E0 797A00 yz. | |------------------------------------------------------------------------------| | Platform Event Log - 0x533C9B37 | |------------------------------------------------------------------------------| | Private Header | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Created by : 4154 | | Created at : 07/28/2015 02:00:05 | | Committed at : 07/28/2015 02:00:05 | | Creator Subsystem : OPAL | | CSSVER : | | Platform Log Id : 0xB0000002 | | Entry Id : 0x533C9B37 | | Total Log Size : 483 | |------------------------------------------------------------------------------| | User Header | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Log Committed by : 4154 | | Subsystem : Platform Firmware | | Event Scope : Unknown - 0x00000000 | | Event Severity : Predictive Error | | Event Type : Not Applicable | | Return Code : 0x00000000 | | Action Flags : Report Externally | | Action Status : Sent to Hypervisor | |------------------------------------------------------------------------------| | Primary System Reference Code | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Created by : 4154 | | SRC Format : 0x80 | | SRC Version : 0x02 | | Virtual Progress SRC : False | | I5/OS Service Event Bit : False | | Hypervisor Dump Initiated: False | | Power Control Net Fault : False | | | | Valid Word Count : 0x08 | | Reference Code : BB821410 | | Hex Words 2 - 5 : 00000080 00000000 00000000 00000000 | | Hex Words 6 - 9 : 00000000 00000000 00000000 00000000 | | | |------------------------------------------------------------------------------| | Extended User Header | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Created by : 4154 | | Reporting Machine Type : 8286-42A | | Reporting Serial Number : 10784AT | | FW Released Ver : | | FW SubSys Version : | | Common Ref Time : 07/28/2015 02:00:05 | | Symptom Id Len : 0 | | Symptom Id : | |------------------------------------------------------------------------------| | Machine Type/Model & Serial Number | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Created by : 4154 | | Machine Type Model : 8286-42A | | Serial Number : 10784AT | |------------------------------------------------------------------------------| | User Defined Data | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Created by : 4154 | | | | 00000000 4B4B4B4B 00340000 54686973 20697320 KKKK.4..This is | | 00000010 61207361 6D706C65 20757365 72206465 a sample user de | | 00000020 66696E65 64206461 74612073 65637469 fined data secti | | 00000030 6F6E3100 on1. | | | |------------------------------------------------------------------------------| | User Defined Data | |------------------------------------------------------------------------------| | Section Version : 1 | | Sub-section type : 0 | | Created by : 4154 | | | | 00000000 4C4C4C4C 009F0000 4572726F 72206C6F LLLL....Error lo | | 00000010 6767696E 67207361 6D706C65 2E205468 gging sample. Th | | 00000020 65736520 61726520 64756D6D 79206572 ese are dummy er | | 00000030 726F7273 2E205365 6374696F 6E203200 rors. Section 2. | | 00000040 53616D70 6C652065 72726F72 2053616D Sample error Sam | | 00000050 706C6520 6572726F 72205361 6D706C65 ple error Sample | | 00000060 20657272 6F722053 616D706C 65206572 error Sample er | | 00000070 726F7220 09090953 616D706C 65206572 ror ...Sample er | | 00000080 726F7220 61626364 65666768 696A6B6C ror abcdefghijkl | | 00000090 6D6E6F70 71727374 75767778 797A00 mnopqrstuvwxyz. | | | |------------------------------------------------------------------------------| skiboot-skiboot-5.1.13/doc/gcov.txt000066400000000000000000000033771265204436200172050ustar00rootroot00000000000000GCOV for skiboot ---------------- Unit tests ---------- All unit tests are built+run with gcov enabled. make coverage-report will generate a unit test coverage report like: http://open-power.github.io/skiboot/coverage-report/ Skiboot ------- You can now build Skiboot itself with gcov support, boot it on a machine, do things, and then extract out gcda files to generate coverage reports from real hardware (or a simulator). Building Skiboot with GCOV -------------------------- SKIBOOT_GCOV=1 make You may need to "make clean" first. This will build a skiboot lid roughly *twice* the size. Flash/Install the skiboot.lid and boot. Extracting GCOV data -------------------- The way we extract the gcov data from a system is by dumping the contents of skiboot memory and then parsing the data structures in user space with the extract-gcov utility in the skiboot repo. mambo: mysim memory fwrite 0x30000000 0x240000 skiboot.dump FSP: getmemproc 30000000 3407872 -fb skiboot.dump linux (e.g. petitboot environment): dd if=/proc/kcore skip=1572864 count=6656 of=skiboot.dump You basically need to dump out the first 3MB of skiboot memory. Then you need to find out where the gcov data structures are: perl -e "printf '0x%x', 0x30000000 + 0x`grep gcov_info_list skiboot.map|cut -f 1 -d ' '`" That address needs to be supplied to the extract-gcov utility: ./extract-gcov skiboot.dump 0x3023ec40 Once you've run extract-gcov, it will have extracted the gcda files from the skiboot memory image. You can then run lcov: lcov -b . -q -c -d . -o skiboot-boot.info \ --gcov-tool /opt/cross/gcc-4.8.0-nolibc/powerpc64-linux/bin/powerpc64-linux-gcov *IMPORTANT* you should point lcov to the gcov for the compiler you used to build skiboot, otherwise you're likely to get errors. skiboot-skiboot-5.1.13/doc/memory.txt000066400000000000000000000027561265204436200175570ustar00rootroot00000000000000Memory in skiboot ----------------- There are regions of memory we statically allocate for firmware as well as a HEAP region for boot and runtime allocations. A design principle of skiboot is to attempt not to allocate memory at runtime, or at least keep it to a minimum, and not do so in any critical code path for the system to remain running. At no point during runtime should a skiboot memory allocation failure cause the system to stop functioning. HEAP ---- Dynamic memory allocations go in a single heap. This is identified as Region ibm,firmware-heap and appears as a reserved section in the device tree. Originally, it was 12582912 bytes in size (declared in mem_map.h). Now, it is 13631488 bytes after being bumped as part of the GCOV work. We increased heap size as on larger systems, we were getting close to using all the heap once skiboot became 2MB with GCOV. Heap usage is printed before running the payload. For example, as of writing, on a dual socket Tuleta: [45215870591,5] SkiBoot skiboot-5.0.1-94-gb759ce2 starting... [3680939340,5] CUPD: T side MI Keyword = SV830_027 [3680942658,5] CUPD: T side ML Keyword = FW830.00 [15404383291,5] Region ibm,firmware-heap free: 5378072 and on a palmetto: [24748502575,5] SkiBoot skiboot-5.0.1-94-gb759ce2 starting... [9870429550,5] Region ibm,firmware-heap free: 10814856 Our memory allocator is simple, a use pattern of: A = malloc(); B = malloc(); free(A); is likely to generate fragmentation, so it should generally be avoided where possible. skiboot-skiboot-5.1.13/doc/opal-api/000077500000000000000000000000001265204436200171765ustar00rootroot00000000000000skiboot-skiboot-5.1.13/doc/opal-api/opal-cec-power-down-5.txt000066400000000000000000000014341265204436200236650ustar00rootroot00000000000000OPAL_CEC_POWER_DOWN ------------------- #define OPAL_CEC_POWER_DOWN 5 int64 opal_cec_power_down(uint64 request) Arguments: uint64 request values as follows: 0 - Power down normally 1 - Power down immediately This OPAL call requests OPAL to power down the system. The exact difference between a normal and immediate shutdown is platform specific. Current Linux kernels just use power down normally (0). It is valid for a platform to only support some types of power down operations. Return Values: OPAL_SUCCESS: the power down was updated successful OPAL_BUSY: unable to power down, try again later OPAL_PARAMETER: a parameter was incorrect OPAL_INTERNAL_ERROR: hal code sent incorrect data to hardware device OPAL_UNSUPPORTED: this platform does not support being powered off. skiboot-skiboot-5.1.13/doc/opal-api/opal-cec-reboot-6-116.txt000066400000000000000000000031421265204436200233620ustar00rootroot00000000000000OPAL_CEC_REBOOT and OPAL_CEC_REBOOT2 ------------------------------------ #define OPAL_CEC_REBOOT 6 #define OPAL_CEC_REBOOT2 116 There are two opal calls to invoke system reboot. OPAL_CEC_REBOOT: Used for normal reboot by Linux host. OPAL_CEC_REBOOT2: Newly introduced to handle abnormal system reboots. The Linux kernel will make this OPAL call when it has to terminate abruptly due to an anomalous condition. The kernel will push some system state context to OPAL, which will in turn push it down to the BMC for further analysis. OPAL_CEC_REBOOT --------------- Syntax: int64_t opal_cec_reboot(void) Input parameters: None. System reboots normally. OPAL_CEC_REBOOT2 ---------------- Syntax: int64_t opal_cec_reboot2(uint32_t reboot_type, char *diag) Input parameters: @reboot_type Type of reboot. (see below) @diag Null-terminated string. Depending on reboot type, this call will carry out additional steps before triggering reboot. Supported reboot types: ---------------------- OPAL_REBOOT_NORMAL = 0 Behavior is as similar to that of opal_cec_reboot() OPAL_REBOOT_PLATFORM_ERROR = 1 Log an error to the BMC and then trigger a system checkstop, using the information provided by 'ibm,sw-checkstop-fir' property in the device-tree. Post the checkstop trigger, OCC/BMC will collect relevant data for error analysis and trigger a reboot. In absence of 'ibm,sw-checkstop-fir' device property, this function will return with OPAL_UNSUPPORTED and no reboot will be triggered. Unsupported Reboot type For unsupported reboot type, this function will return with OPAL_UNSUPPORTED and no reboot will be triggered. skiboot-skiboot-5.1.13/doc/opal-api/opal-check-token-80.txt000066400000000000000000000013541265204436200233130ustar00rootroot00000000000000OPAL_CHECK_TOKEN ---------------- This OPAL call allows the host OS to determine if a particular OPAL call is present on a system. This allows for simple compatibility between OPAL versions and different OPAL implementations/platforms. One parameter is accepted: the OPAL token number. OPAL_CHECK_TOKEN will return: enum OpalCheckTokenStatus { OPAL_TOKEN_ABSENT = 0, OPAL_TOKEN_PRESENT = 1 }; indicating the presence/absence of the particular OPAL_CALL. OPAL_CHECK_TOKEN is REQUIRED to be implemented by a conformant OPAL implementation. For skiboot, only positively ancient internal-to-IBM versions were missing OPAL_CHECK_TOKEN. In this case, OPAL_PARAMETER would be returned. There is no reason for a host OS to support this behaviour. skiboot-skiboot-5.1.13/doc/opal-api/opal-code-update-76-77-78.txt000066400000000000000000000043531265204436200240100ustar00rootroot00000000000000Code Update on FSP based machine ================================ There are three OPAL calls for code update on FSP based machine: #define OPAL_FLASH_VALIDATE 76 #define OPAL_FLASH_MANAGE 77 #define OPAL_FLASH_UPDATE 78 OPAL_FLASH_VALIDATE ------------------- Validate new image is valid for this platform or not. We do below validation in OPAL: - We do below sys parameters validation to confirm inband update is allowed. - Platform is managed by HMC or not?. - Code update policy (inband code update allowed?). - We parse candidate image header (first 4k bytes) to perform below validations. - Image magic number. - Image version to confirm image is valid for this platform. Input: buffer : First 4k bytes of new image size : Input buffer size Output: buffer : Output result (current and new image version details) size : Output buffer size result : Token to identify what will happen if update is attempted See hw/fsp/fsp-codeupdate.h for token values. Return value: Validation status OPAL_FLASH_MANAGE ----------------- Commit/Reject image. - We can commit new image (T -> P), if system is running with T side image. - We can reject T side image, if system is running with P side image. Note: If a platform is running from a T side image when an update is to be applied, then the platform may automatically commit the current T side image to the P side to allow the new image to be updated to the temporary image area. Input op : Operation (1 : Commit /0 : Reject) Return value: Commit operation status (0 : Success) OPAL_FLASH_UPDATE ------------------ Update new image. It only sets the flag, actual update happens during system reboot/shutdown. Host splits FW image to scatter/gather list and sends it to OPAL. OPAL parse the image to get indivisual LID and passes it to FSP via MBOX command. FW update flow : - if (running side == T) Swap P & T side - Start code update - Delete T side LIDs - Write LIDs - Code update complete - Deep IPL Input list : Real address of image scatter/gather list of the FW image Return value: Update operation status (0: update requested) skiboot-skiboot-5.1.13/doc/opal-api/opal-console-read-write-1-2.txt000066400000000000000000000036311265204436200246730ustar00rootroot00000000000000OPAL Console calls ------------------ There are four OPAL calls relating to the OPAL console: #define OPAL_CONSOLE_WRITE 1 #define OPAL_CONSOLE_READ 2 #define OPAL_CONSOLE_WRITE_BUFFER_SPACE 25 #define OPAL_CONSOLE_FLUSH 117 The OPAL console calls can support multiple consoles. Each console MUST be represented in the device tree. A conforming implementation SHOULD have at least one console. It is valid for it to simply be an in-memory buffer and only support writing. [TODO: details on device tree specs for console] OPAL_CONSOLE_WRITE ------------------ Parameters: int64_t term_number int64_t *length, const uint8_t *buffer Returns: OPAL_SUCCESS OPAL_PARAMETER - invalid term_number OPAL_CLOSED - console device closed OPAL_BUSY_EVENT - unable to write any of buffer term_number is the terminal number as represented in the device tree. length is a pointer to the length of buffer. A conformining implementation SHOULD try to NOT do partial writes, although partial writes and not writing anything are valid. OPAL_CONSOLE_WRITE_BUFFER_SPACE ------------------------------- Parameters: int64_t term_number int64_t *length Returns: OPAL_SUCCESS OPAL_PARAMETER - invalid term_number Returns the available buffer length for OPAL_CONSOLE_WRITE in *length. This call can be used to help work out if there is sufficient buffer space to write your full message to the console with OPAL_CONSOLE_WRITE. OPAL_CONSOLE_READ ----------------- Parameters: int64_t term_number int64_t *length uint8_t *buffer Returns: OPAL_SUCCESS OPAL_PARAMETER - invalid term_number OPAL_CLOSED Use OPAL_POLL_EVENTS for how to determine OPAL_CONSOLE_FLUSH ------------------ Parameters: int64_t term_number Returns: OPAL_SUCCESS OPAL_UNSUPPORTED - the console does not implement a flush call OPAL_PARAMETER - invalid term_number OPAL_PARTIAL - more to flush, call again OPAL_BUSY - nothing was flushed this call skiboot-skiboot-5.1.13/doc/opal-api/opal-flash-110-111-112.txt000066400000000000000000000036661265204436200231000ustar00rootroot00000000000000 OPAL Flash calls ---------------- There are three OPAL calls for interacting with flash devices: #define OPAL_FLASH_READ 110 #define OPAL_FLASH_WRITE 111 #define OPAL_FLASH_ERASE 112 Multiple flash devices are supported by OPAL - each of these calls takes an id parameter, which much match an ID found in the corresponding ibm,opal/flash@n device tree node. See doc/device-tree/ibm,opal/flash.txt for details of the device tree bindings. All operations on the flash device must be aligned to the block size of the flash. This applies to both offset and size arguments. This interface is asynchronous; all calls require a 'token' argument. On success, the calls will return OPAL_ASYNC_COMPLETION, and an opal_async_completion message will be sent (with the appropriate token argument) when the operation completes. All calls share the same return values: OPAL_ASYNC_COMPLETION - operation started, an async completion will be triggered with the @token argument OPAL_PARAMETER - invalid flash id OPAL_PARAMETER - invalid size or offset (alignment, or access beyond end of device) OPAL_BUSY - flash in use OPAL_HARDWARE - error accessing flash device OPAL_FLASH_READ --------------- Parameters: uint64_t id uint64_t offset uint64_t buffer uint64_t size uint64_t token Reads from the specified flash id, at the specified offset, into the buffer. Will trigger an async completion with token when completed. OPAL_FLASH_ERASE --------------- Parameters: uint64_t id uint64_t offset uint64_t size uint64_t token Erases the specified flash id, at the specified offset and size. Will trigger an async completion with token when completed. OPAL_FLASH_WRITE --------------- Parameters: uint64_t id uint64_t offset uint64_t buffer uint64_t size uint64_t token Writes buffer to the specified flash id, at the specified offset and size. The flash must be erased before being written. Will trigger an async completion with token when completed. skiboot-skiboot-5.1.13/doc/opal-api/opal-get-msg-85.txt000066400000000000000000000026061265204436200224710ustar00rootroot00000000000000OPAL_GET_MSG ------------ OPAL_GET_MSG will get the next pending OPAL Message (see opal-messages.txt). Parameters: buffer to copy message into sizeof buffer to copy message into The maximum size of an opal message is specified in the device tree passed to the host OS: ibm,opal { opal-msg-size = <0x48>; } It is ALWAYS at least 72 bytes. In the future, OPAL may have messages larger than 72 bytes. Naturally, a HOST OS will only be able to interpret these if it correctly uses opal-msg-size. Any OPAL message > 72 bytes, a host OS may safely ignore. A host OS *SHOULD* always supply a buffer to OPAL_GET_MSG of either 72 bytes or opal-msg-size. It MUST NOT supply a buffer of < 72 bytes. Return values: OPAL_RESOURCE - no available message. OPAL_PARAMETER - buffer is NULL or size is < 72 bytes. If buffer size < 72 bytes, the message will NOT be discarded by OPAL. OPAL_PARTIAL - If pending opal message is greater than supplied buffer. In this case the message is *DISCARDED* by OPAL. This is to keep compatibility with host Operating Systems with a hard coded opal-msg-size of 72 bytes. NOT CURRENTLY IMPLEMENTED. Specified so that host OS can prepare for the possible future with either a sensible error message or by gracefully ignoring such OPAL messages. OPAL_SUCCESS - message successfully copied to buffer. skiboot-skiboot-5.1.13/doc/opal-api/opal-get-msi-39-40.txt000066400000000000000000000046311265204436200227130ustar00rootroot00000000000000OPAL_GET_MSI_32 and OPAL_GET_MSI_64 ----------------------------------- #define OPAL_GET_MSI_32 39 #define OPAL_GET_MSI_64 40 WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. OPAL PHBs encode MVE and XIVE specifiers in MSI DMA and message data values. The host calls these functions to determine the PHB MSI DMA address and message data to program into a PE PCIE function for a particular MVE and XIVE. The msi_address parameter returns the MSI DMA address and the msi_data parameter returns the MSI DMA message data value the PE uses to signal that interrupt. The phb_id parameter is the value from the PHB node ibm,opal-phbid property. The mve_number is the index of an MVE used to authorize this PE to this MSI. For ibm,opal-ioda2 PHBs, the MVE number argument is ignored. The xive_number is the index of an XIVE that corresponds to a particular DMA address and message data value this PE will signal as an MSI ro MSI-X. The msi_range parameter specifies the number of MSIs associated with the in put MVE and XIVE, primarily for MSI-conventional Multiple Message Enable > 1 MSI. MSI requires consecutive MSIs per MSI address, and each MSI DMA address must be unique for any given consecutive power of 2 set of 32 message data values,. which in turn select particular PHB XIVEs. This value must be a power of 2 value in the range of 0 to 32. OPAL returns opal_parameter for values outside of this range. For MSI conventional, the MSI address and message data returned apply to a power of 2 sequential set of XIVRs starting from the xive_number for the power of 2 msi_range input argument. The message data returned represents the power of 2 aligned starting message data value of the first interrupt number in that sequential range. Valid msi_range input values are from 1 to 32. Non-power of 2 values result in a return code of opal_PARAMETER . An msi_range value of 0 or 1 signifies that OPAL should return the message data and message address for exactly one MSI specified by the input XIVE number. For MSI conventional, the host should specify either a value of 0 or 1, for an MSI Capability MME value of 1 MSI. For MSI-X XIVRs, the host should specify a value of '1' for the msi_range argument and call this function for each MSI-X uniquely. skiboot-skiboot-5.1.13/doc/opal-api/opal-get-xive-20.txt000066400000000000000000000014471265204436200226450ustar00rootroot00000000000000OPAL_GET_XIVE ------------- #define OPAL_GET_XIVE 20 WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to return the POWER XIVE server and priority values currently set in a PHB XIVE. The phb_id parameter is the value from the PHB node ibm,opal-phbid property. The xive_number is the index of an XIVE that corresponds to a particular interrupt the server_number returns the server (processor) that is set in this XIVE the priority returns the interrupt priority value that is set in this XIVE This call returns the server and priority numbers from within the XIVE specified by the XIVE_number. skiboot-skiboot-5.1.13/doc/opal-api/opal-handle-interrupt.txt000066400000000000000000000014571265204436200241640ustar00rootroot00000000000000OPAL_HANDLE_INTERRUPT --------------------- The host OS must pass all interrupts in "ibm,opal/opal-interrupts" in the device tree to OPAL. An example dt snippet is: ibm,opal { ... opal-interrupts = <0x10 0x11 0x12 0x13 0x14 0x20010 0x20011 0x20012 0x20013 0x20014 0xffe 0xfff 0x17fe 0x17ff 0x2ffe 0x2fff 0x37fe 0x37ff 0x20ffe 0x20fff 0x217fe 0x217ff 0x22ffe 0x22fff 0x237fe 0x237ff>; } When the host OS gets any of these interrupts, it must call OPAL_HANDLE_INTERRUPT. The OPAL_HANDLE_INTERRUPT call takes two parameters, one input and one output. uint32_t isn - the interrupt uint64_t *outstanding_event_mask - returns outstanding events for host OS to handle The host OS should then handle any outstanding events. See opal-poll-events.txt for documentation on events. skiboot-skiboot-5.1.13/doc/opal-api/opal-invalid-call--1.txt000066400000000000000000000002171265204436200234420ustar00rootroot00000000000000OPAL_INVALID_CALL ----------------- An OPAL call of -1 will always return OPAL_PARAMETER. It is always ivalid. It exists purely for testing. skiboot-skiboot-5.1.13/doc/opal-api/opal-led-get-set-114-115.txt000066400000000000000000000041451265204436200236150ustar00rootroot00000000000000Service Indicators (LEDS) ------------------------- The service indicator is one element of an overall hardware service strategy where end user simplicity is a high priority. The goal is system firmware or operating system code to isolate hardware failures to the failing FRU and automatically activate the fault indicator associated with the failing FRU. The end user then needs only to look for the FRU with the active fault indicator to know which part to replace. Different types of indicators handled by LED code: - System attention indicator (Check log indicator) Indicates there is a problem with the system that needs attention. - Identify Helps the user locate/identify a particular FRU or resource in the system. - Fault Indicates there is a problem with the FRU or resource at the location with which the indicator is associated. LED Design: ----------- When it comes to implementation we can classify LEDs into two categories: 1 - Hypervisor (OPAL) controlled LEDs (All identify & fault indicators) During boot, we read/cache these LED details in OPAL (location code, state, etc). We use cached data to serve read request from FSP/Host. And we use SPCN passthrough MBOX command to update these LED state. 2 - Service processor (FSP) controlled LEDs (System Attention Indicator) During boot, we read/cache this LED info using MBOX command. Later anytime FSP updates this LED, it sends update system parameter notification MBOX command. We use that data to update cached data. LED update request is sent via set/reset attn MBOX command. LED update request: Both FSP and Host will send LED update requests. We have to serialize SPCN passthrough command. Hence we maintain local queue. Note: - For more information regarding service indicator refer to PAPR spec (Service Indicators chapter). There are two OPAL calls relating to LED operations. OPAL_LEDS_GET_INDICATOR ----------------------- Returns LED state for the given location code. OPAL_LEDS_SET_INDICATOR ----------------------- Sets LED state for the given location code. See hw/fsp/fsp-leds.c for more deatails. skiboot-skiboot-5.1.13/doc/opal-api/opal-messages.txt000066400000000000000000000145211265204436200225020ustar00rootroot00000000000000OAPL_MESSAGE ============ The host OS can use OPAL_GET_MSG to retrive messages queued by OPAL. The messages are defined by enum opal_msg_type. The host is notified of there being messages to be consumed by the OPAL_EVENT_MSG_PENDING bit being set. An opal_msg is: struct opal_msg { __be32 msg_type; __be32 reserved; __be64 params[8]; }; The data structure is ALWAYS at least this size (4+4+8*8 = 72 bytes). Some messages define fewer than eight parameters. For messages that do not define all eight parameters, the value in the undefined parameters is undefined, although can safely be memcpy()d or otherwise moved. In the device tree, there's an opal-msg-size property of the OPAL node that says the size of a struct opal-msg. In the future, OPAL may support larger messages. See OPAL_GET_MESSAGE documentation for details. ibm,opal { opal-msg-size = <0x48>; } OPAL_MSG_ASYNC_COMP ------------------- params[0] = token params[1] = rc Additional parameters are function-specific. OPAL_MSG_MEM_ERR ---------------- OPAL_MSG_EPOW ------------- Used by OPAL to issue environmental and power warnings to host OS for conditions requiring an earlier poweroff. A few examples of these are high ambient temperature or system running on UPS power with low UPS battery. Host OS can query OPAL via GET_EPOW_STATUS API to obtain information about EPOW conditions present. Refer include/opal-api.h for description of all supported EPOW events. OPAL_SYSPOWER_CHNG, OPAL_SYSPOWER_FAIL and OPAL_SYSPOWER_INC events don't require system poweroff. Host OS should look for 'ibm,opal-v3-epow' string as compatible property for 'epow' node under OPAL device-tree to determine epow support. OPAL_MSG_SHUTDOWN ----------------- Used by OPAL to inform the host OS it must imitate a graceful shutdown. Uses the first parameter to indicate weather the system is going down for shutdown or a reboot. params[0] = 0x01 reboot, 0x00 shutdown OPAL_MSG_HMI_EVT ---------------- Used by OPAL to sends the OPAL HMI Event to the host OS that reports a summary of HMI error and whether it was successfully recovered or not. HMI is a Hypervisor Maintenance Interrupt usually reports error related to processor recovery/checkstop, NX checkstop and Timer facility. Hypervisor then takes this opportunity to analyze and recover from some of these errors. Hypervisor takes assistance from OPAL layer to handle and recover from HMI. After handling HMI, OPAL layer sends the summary of error report and status of recovery action using HMI event structure shown below. The HMI event structure uses version numbering to allow future enhancement to accommodate additional members. The version start from V1 onward. Version 0 is invalid version and unsupported. The current version of HMI event structure V2 and is backward compatible to V1 version. Notes: - When adding new structure to the union in future, the version number must be bumped. - All future versions must be backward compatible to all its older versions. - Size of this structure should not exceed that of struct opal_msg. struct OpalHMIEvent { uint8_t version; /* 0x00 */ uint8_t severity; /* 0x01 */ uint8_t type; /* 0x02 */ uint8_t disposition; /* 0x03 */ uint8_t reserved_1[4]; /* 0x04 */ __be64 hmer; /* TFMR register. Valid only for TFAC and TFMR_PARITY error type. */ __be64 tfmr; /* version 2 and later */ union { /* * checkstop info (Core/NX). * Valid for OpalHMI_ERROR_MALFUNC_ALERT. */ struct { uint8_t xstop_type; /* enum OpalHMI_XstopType */ uint8_t reserved_1[3]; __be32 xstop_reason; union { __be32 pir; /* for CHECKSTOP_TYPE_CORE */ __be32 chip_id; /* for CHECKSTOP_TYPE_NX */ } u; } xstop_error; } u; }; OPAL_MSG_DPO ------------ Delayed poweroff where OPAL informs host OS that a poweroff has been requested and a forced shutdown will happen in future. Host OS can use OPAL_GET_DPO_STATUS API to query OPAL the number of seconds remaining before a forced poweroff will occur. OPAL_MSG_PRD ------------ This message is a OPAL-to-HBRT notification, and contains a struct opal_prd_msg: enum opal_prd_msg_type { OPAL_PRD_MSG_TYPE_INIT = 0, /* HBRT --> OPAL */ OPAL_PRD_MSG_TYPE_FINI, /* HBRT --> OPAL */ OPAL_PRD_MSG_TYPE_ATTN, /* HBRT <-- OPAL */ OPAL_PRD_MSG_TYPE_ATTN_ACK, /* HBRT --> OPAL */ OPAL_PRD_MSG_TYPE_OCC_ERROR, /* HBRT <-- OPAL */ OPAL_PRD_MSG_TYPE_OCC_RESET, /* HBRT <-- OPAL */ }; struct opal_prd_msg { uint8_t type; uint8_t pad[3]; __be32 token; union { struct { __be64 version; __be64 ipoll; } init; struct { __be64 proc; __be64 ipoll_status; __be64 ipoll_mask; } attn; struct { __be64 proc; __be64 ipoll_ack; } attn_ack; struct { __be64 chip; } occ_error; struct { __be64 chip; } occ_reset; }; }; Responses from the kernel use the same message format, but are passed through the opal_prd_msg call. OPAL_MSG_OCC ------------ This is used by OPAL to inform host about OCC events like OCC reset, OCC load and throttle status change by OCC which can indicate the host the reason for frequency throttling/unthrottling. #define OCC_RESET 0 #define OCC_LOAD 1 #define OCC_THROTTLE 2 #define OCC_MAX_THROTTLE_STATUS 5 /* * struct opal_occ_msg: * type: OCC_RESET, OCC_LOAD, OCC_THROTTLE * chip: chip id * throttle status: Indicates the reason why OCC may have limited * the max Pstate of the chip. * 0x00 = No throttle * 0x01 = Power Cap * 0x02 = Processor Over Temperature * 0x03 = Power Supply Failure (currently not used) * 0x04 = Over current (currently not used) * 0x05 = OCC Reset (not reliable as some failures will not allow for * OCC to update throttle status) */ struct opal_occ_msg { __be64 type; __be64 chip; __be64 throttle_status; }; Host should read opal_occ_msg.chip and opal_occ_msg.throttle_status only when opal_occ_msg.type = OCC_THROTTLE. If host receives OCC_THROTTLE after an OCC_RESET then this throttle message will have a special meaning which indicates that all the OCCs have become active after a reset. In such cases opal_occ_msg.chip and opal_occ_msg.throttle_status will be set to 0 and host should not use these values. If opal_occ_msg.type > 2 then host should ignore the message for now, new events can be defined for opal_occ_msg.type in the future versions of OPAL. skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-get-set-xive-reissue-35-36.txt000066400000000000000000000010631265204436200261120ustar00rootroot00000000000000OPAL_PCI_GET_XIVE_REISSUE and OPAL_PCI_SET_XIVE_REISSUE ------------------------------------------------------- static int64_t opal_pci_get_xive_reissue(uint64_t phb_id __unused, uint32_t xive_number __unused, uint8_t *p_bit __unused, uint8_t *q_bit __unused) static int64_t opal_pci_set_xive_reissue(uint64_t phb_id __unused, uint32_t xive_number __unused, uint8_t p_bit __unused, uint8_t q_bit __unused) Both of these calls are remnants from previous OPAL versions, calling either of them shall return OPAL_UNSUPPORTED. skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-map-pe-dma-window-44.txt000066400000000000000000000101131265204436200251050ustar00rootroot00000000000000OPAL_PCI_MAP_PE_DMA_WINDOW -------------------------- #define OPAL_PCI_MAP_PE_DMA_WINDOW 44 static int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_id, uint16_t tce_levels, uint64_t tce_table_addr, uint64_t tce_table_size, uint64_t tce_page_size) WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to create a DMA window and map it to a PE. This call returns the address in PCI memory that corresponds to the specified DMA window, which in part may depend on the particular PHB DMA window used. An address that is all zeros in the upper 32 bits reflects a DMA window enabled for 32-bit DMA addresses. The overall size of the DMA window in PCI memory is determined by the number of tce_levels times the tce_table_size times the tce_page_size. phb_id is the value from the PHB node ibm,opal-phbid property. dma_window_number specifies the DMA window For ibm,opal-ioda PHBs the dma_window_number is an index from 0 to the PHB total number of windows minus 1. For ibm,opal-ioda2 PHBs the DMA window_number is an index from 0 to n-1, where n is the number of windows per window set, within the window set associated with the specified PE number. pe_number is the index of the PE that is authorized to DMA to this window address space in PCI memory, tce_levels is the number of TCE table levels in the translation hiearchy, from 1 to ibm,opal-dmawins property . tce_table_addr is the 64-bit system real address of the first level (root, for mult-level) TCE table in the translation hiearchy. tce_table_size is the size, in bytes, of each TCE table in the translation hierarchy. A value of '0' indicates to disable this DMA window. For ibm,opal-ioda, this must be a value in the range from 128MB / tce_page_size to 256TB / tce_page_size, and must be in the format and matching a value in the tce_table ranges property that is minimally 256KB for 4K pages. A particular PE may be mapped to multiple DMA windows, each spanning a DMA window size corresponding to the win_size32 or win_size_64 specified in the ibm,opal-dmawins<> property. However, the TCE table base address must be unique for each window unless it is intended that the same page address in each DMA window is mapped through the same TCE table entry. Generally, when mapping the same PE to multiple DMA windows, so as to create a larger overall DMA window, it is recommended to use consecutive DMA windows and each DMA window should use a TCE table address that is offset by the win_size value of predecessor DMA window. tce_page_size is the size of PCI memory pages mapped to system real pages through all TCE tables in the translation hierarchy. This must be the same format as and match a value from the ibm,opal-dmawins property . This page size applies to all TCE tables in the translation hierarchy. pci_start_addr returns the starting address in PCI memory that corresponds to this DMA window based on the input translation parameter values. pci_mem_type selects whether this DMA window should be created in 32-bit or 64-bit PCI memory. The input values correspond to the same PCI memory space locators as MMIO spaces in the ranges<> property -- 0x2 indicated 32-bit PCI memory and 0x3 indicates 64-bit memory. Window 0 for both ibm,opal-ioda and ibm,opal-ioda2 PHBs must be within 32-bit PCI memory and this call return opal_parameter for calls that specify window 0 in 64-bit PCI memory. The DMA win_size property for 32 bit DMA windows limits the number of ibm,opal-ioda PHB windows that can map32-bit address space. For example, with a win_size_32 = 256MB, only 16 DMA windows (and therefore no more than 16 distinct PEs) can map the 4GB of 32-bit PCI memory for DMA. OPAL does not police this limitation. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->map_pe_dma_window) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-map-pe-dma-window-real-45.txt000066400000000000000000000035331265204436200260370ustar00rootroot00000000000000OPAL_PCI_MAP_PE_DMA_WINDOW_REAL ------------------------------- #define OPAL_PCI_MAP_PE_DMA_WINDOW_REAL 45 WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to initialize the specified DMA window for untranslated DMA addresses. This allows a PE to DMA directly to system memory without TCE translation. The DMA window PCI memory address is equal to the system memory real address. The PHB passes PCI address bits 04:63 directly to system real address bits 04:63 when PCI address bits 04:39 are within the region specified by mem_addr t0 mem_addr + window_size. The addresses must be 16MB aligned and a multiple of 16MB in size. phb_id is the value from the PHB node ibm,opal-phbid property. dma_window_number specifies the DMA window For ibm,opal-ioda PHBs the dma_window_number is an index from 0 to the PHB total number of windows minus 1. For ibm,opal-ioda2 PHBs the DMA window_number is an index from 0 to n-1, where n is the number of windows per window set, within the window set associated with the specified PE number. pe_number is the index of the PE that is authorized to DMA to this window address space in PCI memory, mem_addr is the starting 64-bit system real address mapped directly to the starting address in PCI memory. Addresses below 4GB are zero in bits above bit 32. This value must be aligned on a 16MB boundary; OPAL returns OPAL_PARAMETER for any value that is not a multiple of 16MB. window_size is the size, in bytes, of the address range defined by this window. This value must be a multiple of 16MB; OPAL returns OPAL_PARAMETER for any value that is not a multiple of 16MB. A value of '0' indicates to disable this DMA window. skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-map-pe-mmio-window-29.txt000066400000000000000000000023451265204436200253200ustar00rootroot00000000000000OPAL_PCI_MAP_PE_MMIO_WINDOW --------------------------- #define OPAL_PCI_MAP_PE_MMIO_WINDOW 29 static int64_t opal_pci_map_pe_mmio_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_type, uint16_t window_num, uint16_t segment_num) WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to map a segment of MMIO address space to a PE. phb_id is the value from the PHB node ibm,opal-phbid property. window_type specifies 32-bit or 64-bit PCI memory '0' selects PCI IO Space. ibm,opal-ioda2 PHBs do not support IO space, and OPAL returns opal_unsupported if called for IO windows. '1' selects 32-bit PCI memory space '2' selects 64 bit PCI memory space window_num is the MMIO window number within the specified PCI memory space segment_num is an index from 0 to the number of segments minus 1 defined or this window, and selects a particular segment within the specified window. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->map_pe_mmio_window) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-phb-mmio-enable-27.txt000066400000000000000000000033241265204436200246250ustar00rootroot00000000000000OPAL_PCI_PHB_MMIO_ENABLE ------------------------ #define OPAL_PCI_PHB_MMIO_ENABLE 27 static int64_t opal_pci_phb_mmio_enable(uint64_t phb_id, uint16_t window_type, uint16_t window_num, uint16_t enable) WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to enable or disable PHB decode of the PCI IO and Memory address spaces below that PHB. Window_num selects an mmio window within that addres space. Enable set to '1' enables the PHB to decode and forward system real addresses to PCI memory, while enable set to '0' disables PHB decode and forwarding for the address range defined in a particular MMIO window. Not all PHB hardware may support disabling some or all MMIO windows. OPAL returns OPAL_UNSUPPORTED if called to disable an MMIO window for which hardware does not support disable. KVM may call this function for all MMIO windows and ignore the opal_unsuppsorted return code so long as KVM has disabled MMIO to all downstream PCI devices and assured that KVM and OS guest partitions cannot issue CI loads/stores to these address spaces from the processor (e.g.,via HPT). OPAL returns OPAL_SUCCESS for calls to OPAL to enable them for PHBs that do not support disable. phb_id is the value from the PHB node ibm,opal-phbid property. window_type specifies 32-bit or 64-bit PCI memory '0' selects PCI IO Space '1' selects 32-bit PCI memory space '2' selects 64 bit PCI memory space window_num is the MMIO window number within the specified PCI memory space enable specifies to enable or disable this MMIO window. skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-set-mve-33.txt000066400000000000000000000022771265204436200232540ustar00rootroot00000000000000OPAL_PCI_SET_MVE ---------------- #define OPAL_PCI_SET_MVE 33 static int64_t opal_pci_set_mve(uint64_t phb_id, uint32_t mve_number, uint32_t pe_number) WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to bind a PE to an MSI Validation Table Entry (MVE) in the PHB. The MVE compares the MSI requester (RID) to a PE RID, including within the XIVE, to validate that the requester is authorized to signal an interrupt to the associated DMA address for a message value that selects a particular XIVE. The phb_id parameter is the value from the PHB node ibm,opal-phbid property. The mve_number is the index, from 0 to ibm,opal,ibm-num-msi-ports minus1 the pe_number is the index of a PE, from 0 to ibm,opal-num-pes minus 1. This call maps an MVE to a PE and PE RID domain. OPAL uses the PELT to determine the PE domain. OPAL treats this call as a NOP for IODA2 PHBs and returns a status of OPAL_SUCCESS. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_mve) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-set-mve-enable-34.txt000066400000000000000000000017651265204436200245020ustar00rootroot00000000000000OPAL_PCI_SET_MVE_ENABLE ----------------------- #define OPAL_PCI_SET_MVE_ENABLE 34 static int64_t opal_pci_set_mve_enable(uint64_t phb_id, uint32_t mve_number, uint32_t state) enum OpalMveEnableAction { OPAL_DISABLE_MVE = 0, OPAL_ENABLE_MVE = 1 }; WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to enable or disable an MVE to respond to an MSI DMA address and message data value. The phb_id parameter is the value from the PHB node ibm,opal-phbid property. The mve_number is the index, from 0 to ibm,opal,ibm-num-msi-ports minus1 A '1' value of the state parameter indicates to enable the MVE and a '0' value indicates to disable the MVE. This call sets the MVE to an enabled (1) or disabled (0) state. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_mve_enable) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-set-pe-31.txt000066400000000000000000000062401265204436200230610ustar00rootroot00000000000000OPAL_PCI_SET_PE --------------- #define OPAL_PCI_SET_PE 31 NOTE: The following two paragraphs come from some old documentation and have not been checked for accuracy. Same goes for bus_compare, dev_compare and func_compare documentation. Do *NOT* assume this documentation is correct without checking the source. A host OS calls this function to map a PCIE function (RID), or range of function bus/dev/funcs (RIDs), to a PHB PE. The bus, device, func, and compare parameters define a range of bus, device, or function numbers to define a range of RIDs within this domain. A value of "7" for the bus_compare, and non-zero for the dev_compare and func_compare, define exactly one function RID to be a PE (within a PE number domain). This must be called prior to ALL other OPAL calls that take a PE number argument, for OPAL to correlate the RID (bus/dev/func) domain of the PE. If a PE domain is changed, the host must call this to reset the PE bus/dev/func domain and then call all other OPAL calls that map PHB IODA resources to update those domains within PHB facilities. static int64_t opal_pci_set_pe(uint64_t phb_id, uint64_t pe_number, uint64_t bus_dev_func, uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, uint8_t pe_action) The phb_id parameter is the value from the PHB node ibm,opal-phbid property. the pe_number is the index of a PE, from 0 to ibm,opal-num-pes minus 1. the bus_compare parameter is a value from 0 to 7 indicating which bus number bits define the range of buses in a PE domain: 0 = do not validate against RID bus number (PE = all bus numbers) 2 = compare high order 3 bits of RID bus number to high order 3 bits of PE bus number 3 = compare high order 4 bits of RID bus number to high order 4 bits of PE bus number : 6 = compare high order 7 bits of RID bus number to high order 7 bits of PE bus number 7 = compare all bits of RID bus number to all bits of PE bus number the dev_compare parameter indicates to compare the RID device number to the PE device number or not. '0' signifies that the RID device number is not compared -- essentially all device numbers within the bus and function number range of this PE are also within this PE. Non-zero signifies to compare the RID device number to the PE device number, such that only that device number is in the PE domain, for all buses and function numbers in the PE domain. the func_compare parameter indicates to compare the RID function number to the PE function number or not. '0' signifies that the RID function number is not compared -- essentially all function numbers within the bus and device number range of this PE are also within this PE. Non-zero signifies to compare the RID function number to the PE function number, such that only that function number is in the PE domain, for all buses and device numbers in the PE domain. pe_action is one of: enum OpalPeAction { OPAL_UNMAP_PE = 0, OPAL_MAP_PE = 1 }; Return value: - OPAL_PARAMETER if: - invalid phb - invalid pe_action - invalid bus_dev_func - invalid bus_compare - if PHB does not support set_pe operation, OPAL_UNSUPPORTED - OPAL_SUCCESS if opreation was successful skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-set-peltv-32.txt000066400000000000000000000041541265204436200236120ustar00rootroot00000000000000OPAL_PCI_SET_PELTV ------------------ #define OPAL_PCI_SET_PELTV 32 WARNING: This documentation comes from an old source and is possibly not up to date with OPALv3. Rely on this documentation only as a starting point, use the source (and update the docs). static int64_t opal_pci_set_peltv(uint64_t phb_id, uint32_t parent_pe, uint32_t child_pe, uint8_t state) This call sets the PELTV of a parent PE to add or remove a PE number as a PE within that parent PE domain. The host must call this function for each child of a parent PE. The phb_id parameter is the value from the PHB node ibm,opal-phbid property the parent_pe is the PE number of a PE that is higher in the PCI hierarchy to other PEs, such that an error involving this parent PE should cause a collateral PE freeze for PEs below this PE in the PCI hierarchy. For example a switch upstream bridge is a PE that is parent to PEs reached through that upstream bridge such that an error involving the upstream bridge (e.g, ERR_FATAL) should cause the PHB to freeze all other PEs below that upstream bridge (e.g., a downstream bridge, or devices below a downstream bridge). the child_pe is the PE number of a PE that is lower in the PCI hierarchy than another PE, such that an error involving that other PE should cause a collateral PE freeze for this child PE. For example a device below a downstream bridge of a PCIE switch is a child PE that downstream bridge PE and the upstream bridge PE of that switch -- an ERR_Fatal from either bridge should result in a collateral freeze of that device PE. enum OpalPeltvAction { OPAL_REMOVE_PE_FROM_DOMAIN = 0, OPAL_ADD_PE_TO_DOMAIN = 1 }; OPAL Implementation Note: WARNING TODO: CHECK IF THIS IS CORRECT FOR skiboot: For ibm,opal-ioda2, OPAL sets the PELTV bit in all RTT entries for the parent PE when the state argument is '1'. OPAL clears the PELTV bit in all RTT entries for the parent PE when the state argument is '0' and setting the child PE bit in the parent PELTV results in an all-zeros value for that PELTV. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_peltv) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-set-phb-mem-window-28.txt000066400000000000000000000060341265204436200253160ustar00rootroot00000000000000OPAL_PCI_SET_PHB_MEM_WINDOW --------------------------- #define OPAL_PCI_SET_PHB_MEM_WINDOW 28 static int64_t opal_pci_set_phb_mem_window(uint64_t phb_id, uint16_t window_type, uint16_t window_num, uint64_t addr, uint64_t pci_addr, uint64_t size) WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to set the PHB PCI memory window parameters for PHBs. OPAL sets IO space for P7IOC and KVM cannot relocate this. KVM should changes these windows only while all devices below the PHB are disabled for PCI memory ops, and with the target window in disabled state (where supported by PHB hardware). phb_id is the value from the PHB node ibm,opal-phbid property. window_type specifies 32-bit or 64-bit PCI memory '0' selects IO space, and is not supported for relocation. OPAL returns OPAL_UNSUPPORTED for this value. '1' selects 32-bit PCI memory space '2' selects 64 bit PCI memory space window_num is the MMIO window number within the specified PCI memory space starting_real_address specifies the location within sytsem (processor)real address space this MMIO window starts. This must be a location within the IO Hub or PHB node ibm,opal-mmio-real property. starting_pci_address specifies the location within PCI 32 or 64-bit address space that this MMIO window starts. For 64-bit PCI memory, this must be within the low order 60 bit (1 Exabyte) region of PCI memory. Addresses above 1EB are reserved to IODA definitions. segment_size defines the segment size of this window, in the same format as and a matching value from the ibm,opal-memwin32/64 property. The window total size, in bytes, is the segment_size times the ibm,opal-memwin32/64 property and must not extend beyond the ibm,opal-mmio-real property range within system real address space. The total MMIO window size is the segment_size times the num_segments supported for the specifice window. The host must assure that the cumulative address space for all enabled windows does not exceed the total PHB 32-bit or 64-bit real address window space, or extend outside these address ranges, and that no windows overlap each other in real or PCI address space. OPAL does not validate those conditions. A segment size of '0' indicates to disable this MMIO window. If the PHB hardware does not support disabling a window, OPAL returns OPAL_UNSUPPORTED status. The size of the system real and PCI memory spaces are equal and defined by segment_size times the number of segments within this MMIO window. The host must set PHB memory windows to be within the system real address ranges indicated in the PHB parent HDT hub node ibm,opal-mmio-real property. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_phb_mem_window) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-pci-set-xive-pe-37.txt000066400000000000000000000017211265204436200240370ustar00rootroot00000000000000OPAL_PCI_SET_XIVE_PE -------------------- static int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number, uint32_t xive_num) WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to bind a PE to an XIVE. Only that PE may then signal an MSI that selects this XIVE. The phb_id parameter is the value from the PHB node ibm,opal-phbid property. the pe_number is the index of a PE, from 0 to ibm,opal-num-pes minus 1. The xive_number is the index, from 0 to ibm,opal,ibm-num-msis minus (num_lsis+1) This call maps the XIVR indexed by xive_num to the PE specified by pe_number. For ibm,opal-ioda HW, the pe_number must match the pe_number set in the MVE. Return value: if (!phb) return OPAL_PARAMETER; if (!phb->ops->set_xive_pe) return OPAL_UNSUPPORTED; skiboot-skiboot-5.1.13/doc/opal-api/opal-poll-events.txt000066400000000000000000000042651265204436200231470ustar00rootroot00000000000000OPAL_POLL_EVENTS ---------------- Poll for outstanding events. Fills in a bitmask of pending events. Current events are: OPAL_EVENT_OPAL_INTERNAL = 0x1 ------------------------------ Currently unused. OPAL_EVENT_NVRAM = 0x2 ---------------------- Unused OPAL_EVENT_RTC = 0x4 -------------------- TODO: clean this up, this is just copied from hw/fsp/fsp-rtc.c: * Because the RTC calls can be pretty slow, these functions will shoot * an asynchronous request to the FSP (if none is already pending) * * The requests will return OPAL_BUSY_EVENT as long as the event has * not been completed. * * WARNING: An attempt at doing an RTC write while one is already pending * will simply ignore the new arguments and continue returning * OPAL_BUSY_EVENT. This is to be compatible with existing Linux code. * * Completion of the request will result in an event OPAL_EVENT_RTC * being signaled, which will remain raised until a corresponding call * to opal_rtc_read() or opal_rtc_write() finally returns OPAL_SUCCESS, * at which point the operation is complete and the event cleared. * * If we end up taking longer than rtc_read_timeout_ms millieconds waiting * for the response from a read request, we simply return a cached value (plus * an offset calculated from the timebase. When the read request finally * returns, we update our cache value accordingly. * * There is two separate set of state for reads and writes. If both are * attempted at the same time, the event bit will remain set as long as either * of the two has a pending event to signal. OPAL_EVENT_CONSOLE_OUTPUT = 0x8 ------------------------------- TODO OPAL_EVENT_CONSOLE_INPUT = 0x10 ------------------------------- TODO OPAL_EVENT_ERROR_LOG_AVAIL = 0x20 --------------------------------- TODO OPAL_EVENT_ERROR_LOG = 0x40 --------------------------- TODO OPAL_EVENT_EPOW = 0x80 ---------------------- TODO OPAL_EVENT_LED_STATUS = 0x100 ----------------------------- TODO OPAL_EVENT_PCI_ERROR = 0x200 ---------------------------- TODO OPAL_EVENT_DUMP_AVAIL = 0x400 ----------------------------- Signifies that there is a pending system dump available. See OPAL_DUMP suite of calls for details. OPAL_EVENT_MSG_PENDING = 0x800, skiboot-skiboot-5.1.13/doc/opal-api/opal-prd-msg-113.txt000066400000000000000000000004211265204436200225400ustar00rootroot00000000000000 OPAL_PRD_MSG call ------------- The OPAL_PRD_MSG call is used to pass a struct opal_prd_msg from the HBRT code into opal, and is paired with the OPAL_PRD_MSG message type. Parameters: struct opal_msg *msg Passes an opal_msg, of type OPAL_PRD_MSG, from the OS to OPAL. skiboot-skiboot-5.1.13/doc/opal-api/opal-read-write-tpo-103-104.txt000066400000000000000000000006471265204436200243450ustar00rootroot00000000000000OPAL_READ_TPO and OPAL_WRITE_TPO -------------------------------- TPO is a Timed Power On facility. It is an OPTIONAL part of the OPAL spec. If a platform supports Timed Power On (TPO), the RTC node in the device tree (itself under the "ibm,opal" node will have the has-tpo property: rtc { compatible = "ibm,opal-rtc"; has-tpo; }; If the "has-tpo" proprety is *NOT* present then OPAL does *NOT* support TPO. skiboot-skiboot-5.1.13/doc/opal-api/opal-register-dump-region-101.txt000066400000000000000000000031101265204436200252320ustar00rootroot00000000000000OPAL_REGISTER_DUMP_REGION ------------------------- This call is used to register regions of memory for a service processor to capture when the host crashes. e.g. if an assert is hit in OPAL, a service processor will copy This is an OPTIONAL feature that may be unsupported, the host OS should use an OPAL_CHECK_TOKEN call to find out if OPAL_REGISTER_DUMP_REGION is supported. OPAL_REGISTER_DUMP_REGION accepts 3 parameters: - region ID - address - length There is a range of region IDs that can be used by the host OS. A host OS should start from OPAL_DUMP_REGION_HOST_END and work down if it wants to add a not well defined region to dump. Currently the only well defined region is for the host OS log buffer (e.g. dmesg on linux). /* * Dump region ID range usable by the OS */ #define OPAL_DUMP_REGION_HOST_START 0x80 #define OPAL_DUMP_REGION_LOG_BUF 0x80 #define OPAL_DUMP_REGION_HOST_END 0xFF OPAL_REGISTER_DUMP_REGION will return OPAL_UNSUPPORTED if the call is present but the system doesn't support registering regions to be dumped. In the event of being passed an invalid region ID, OPAL_REGISTER_DUMP_REGION will return OPAL_PARAMETER. Systems likely have a limit as to how many regions they can support being dumped. If this limit is reached, OPAL_REGISTER_DUMP_REGION will return OPAL_INTERNAL_ERROR. BUGS: Some skiboot versions incorrectly returned OPAL_SUCCESS in the case of OPAL_REGISTER_DUMP_REGION being supported on a platform (so the call was present) but the call being unsupported for some reason (e.g. on an IBM POWER7 machine). See also: OPAL_UNREGISTER_DUMP_REGION skiboot-skiboot-5.1.13/doc/opal-api/opal-rtc-read-3.txt000066400000000000000000000037601265204436200225370ustar00rootroot00000000000000OPAL_RTC_READ ------------- Read the Real Time Clock. Parameters: uint32_t* year_month_day: the year, month and day formatted as follows: - bits 0-15 is bcd formatted year (0100-9999) - bits 16-23 is bcd formatted month (01-12) - bits 24-31 is bcd formatted day (01-31) uint64_t* hour_minute_second_millisecond: the hour, minute, second and millisecond formatted as follows: - bits 0-16 is reserved - bits 17-24 is bcd formatted hour (00-23) - bits 25-31 is bcd formatted minute (00-59) - bits 32-39 is bcd formatted second (00-60) - bits 40-63 is bcd formatted milliseconds (000000-999999) Calling: Since RTC calls can be pretty slow, OPAL_RTC_READ is likely to first return OPAL_BUSY_EVENT, requiring the caller to wait until the OPAL_EVENT_RTC event has been signaled. Once the event has been signalled, a subsequent OPAL_RTC_READ call will retreive the time. Since the OPAL_EVENT_RTC event is used for both reading and writing the RTC, callers must be able to handle the event being signalled for a concurrent in flight OPAL_RTC_WRITE rather than this read request. The following code is one way to correctly issue and then wait for a response: int rc = OPAL_BUSY_EVENT; while (rc == OPAL_BUSY_EVENT) { rc = opal_rtc_read(&y_m_d, &h_m_s_ms); if (rc == OPAL_BUSY_EVENT) opal_poll_events(NULL); } Although as of writing all OPAL_RTC_READ backends are asynchronous, there is no requirement for them to be - it is valid for OPAL_RTC_READ to immediately return the retreived value rather than OPAL_BUSY_EVENT. TODO: describe/document format of arguments. Return codes: OPAL_SUCCESS: - parameters now contain the current time, or one read from cache. OPAL_HARDWARE: - error in retrieving the time. May be transient error, may be permanent. OPAL_PARAMETER: - year_month_day or hour_minute_second_millisecond parameters are NULL OPAL_INTERNAL_ERROR: - something went wrong, Possibly reported in error log. OPAL_BUSY_EVENT: - request is in flight skiboot-skiboot-5.1.13/doc/opal-api/opal-rtc-write-4.txt000066400000000000000000000004641265204436200227550ustar00rootroot00000000000000OPAL_RTC_WRITE -------------- OPAL_RTC_WRITE is much like OPAL_RTC_READ in that it can be asynchronous. If multiple WRITES are issued before the first one completes, subsequent writes are ignored. There can only be one write in flight at any one time. Format of the time is the same as for OPAL_RTC_READ. skiboot-skiboot-5.1.13/doc/opal-api/opal-sensor-read-88.txt000066400000000000000000000017541265204436200233560ustar00rootroot00000000000000OPAL_SENSOR_READ ---------------- The OPAL sensor call reads a sensor data using a unique handler to identity the targeted sensor. This call can be asynchronous, when a message needs to be sent to a service processor for example. In this case, the call will return OPAL_ASYNC_COMPLETION and the token parameter will be used to wait for the completion of the request. Parameters: uint32_t sensor_handler int token uint32_t *sensor_data Return values: OPAL_SUCCESS OPAL_PARAMETER - invalid sensor handler OPAL_UNSUPPORTED - plateform does not support reading sensors. in case of communication with the FSP on IBM systems OPAL_ASYNC_COMPLETION - a request was sent and an async completion will be triggered with the @token argument OPAL_PARTIAL - the request completed but the data returned is invalid OPAL_BUSY_EVENT - a previous request is still pending OPAL_NO_MEM - allocation failed OPAL_INTERNAL_ERROR - communication failure with the FSP OPAL_HARDWARE - FSP is not available skiboot-skiboot-5.1.13/doc/opal-api/opal-set-xive-19.txt000066400000000000000000000013441265204436200226650ustar00rootroot00000000000000OPAL_SET_XIVE ------------- #define OPAL_SET_XIVE 19 WARNING: following documentation is from old sources, and is possibly not representative of OPALv3 as implemented by skiboot. This should be used as a starting point for full documentation. The host calls this function to set the POWER XIVE server and priority parameters into the PHB XIVE. The phb_id parameter is the value from the PHB node ibm,opal-phbid property. The xive_number is the index of an XIVE that corresponds to a particular interrupt the service_number is the server (processor) that is to receive the interrupt request the priority is the interrupt priority value applied to the interrupt (0=highest, 0xFF = lowest/disabled). skiboot-skiboot-5.1.13/doc/opal-api/opal-test-0.txt000066400000000000000000000010451265204436200220040ustar00rootroot00000000000000OPAL_TEST --------- OPAL_TEST is a REQUIRED call for OPAL and conforming implementations MUST have it. It is designed to test basic OPAL call functionality. Token: #define OPAL_TEST 0 Arguments: uint64_t arg Returns: 0xfeedf00d Function: OPAL_TEST MAY print a string to the OPAL log with the value of argument. For example, the reference implementation (skiboot) implements OPAL_TEST as: static uint64_t opal_test_func(uint64_t arg) { printf("OPAL: Test function called with arg 0x%llx\n", arg); return 0xfeedf00d; } skiboot-skiboot-5.1.13/doc/opal-api/opal-unregister-dump-region-102.txt000066400000000000000000000013111265204436200255770ustar00rootroot00000000000000OPAL_UNREGISTER_DUMP_REGION --------------------------- While OPAL_REGISTER_DUMP_REGION registers a region, OPAL_UNREGISTER_DUMP_REGION will unregister a region by region ID. OPAL_UNREGISTER_DUMP_REGION takes one argument: the region ID. A host OS should check OPAL_UNREGISTER_DUMP_REGION is supported through a call to OPAL_CHECK_TOKEN. If OPAL_UNREGISTER_DUMP_REGION is called on a system where the call is present but unsupported, it will return OPAL_UNSUPPORTED. BUGS: Some skiboot versions incorrectly returned OPAL_SUCCESS in the case of OPAL_UNREGISTER_DUMP_REGION being supported on a platform (so the call was present) but the call being unsupported for some reason (e.g. on an IBM POWER7 machine). skiboot-skiboot-5.1.13/doc/opal-api/opal-xscom-read-write-65-66.txt000066400000000000000000000006471265204436200245520ustar00rootroot00000000000000OPAL_XSCOM_READ and OPAL_XSCOM_WRITE ------------------------------------ These low level calls will read/write XSCOM values directly. They should only be used by low level manufacturing/debug tools. "Normal" host OS kernel code should not know about XSCOM. each takes three parameters: int xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val) int xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val) skiboot-skiboot-5.1.13/doc/opal-api/return-codes.txt000066400000000000000000000044211265204436200223520ustar00rootroot00000000000000OPAL API Return Codes --------------------- All OPAL calls return an integer relaying the success/failure of the OPAL call. Success is typically indicated by OPAL_SUCCESS. Failure is always indicated by a negative return code. Conforming host Operating Systems MUST handle return codes other than those listed here. In future OPAL versions, additional return codes may be added. In the reference implementation (skiboot) these are all in include/opal.h. The core set of return codes are: #define OPAL_SUCCESS 0 Success! #define OPAL_PARAMETER -1 A parameter was invalid. This will also be returned if you call an invalid OPAL call. To determine if a specific OPAL call is supported or not, OPAL_CHECK_TOKEN should be called rather than relying on OPAL_PARAMETER being returned for an invalid token. #define OPAL_BUSY -2 Try again later. #define OPAL_PARTIAL -3 The operation partially succeeded. #define OPAL_CONSTRAINED -4 #define OPAL_CLOSED -5 #define OPAL_HARDWARE -6 #define OPAL_UNSUPPORTED -7 Unsupported operation. Non-fatal. #define OPAL_PERMISSION -8 Inadequate permission to perform the operation. #define OPAL_NO_MEM -9 Indicates a temporary or permanent lack of adequate memory to perform the operation. Ideally, this should never happen. Skiboot reserves a small amount of memory for its heap and some operations (such as I2C requests) are allocated from this heap. If this is ever hit, you should likely file a bug. #define OPAL_RESOURCE -10 #define OPAL_INTERNAL_ERROR -11 #define OPAL_BUSY_EVENT -12 #define OPAL_HARDWARE_FROZEN -13 #define OPAL_WRONG_STATE -14 #define OPAL_ASYNC_COMPLETION -15 For asynchronous calls, successfully queueing/starting executing the command is indicated by the OPAL_ASYNC_COMPLETION return code. pseudo-code for an async call: token = opal_async_get_token(); rc = opal_async_example(foo, token); if (rc != OPAL_ASYNC_COMPLETION) handle_error(rc); rc = opal_async_wait(token); // handle result here #define OPAL_EMPTY -16 Added for I2C, only applicable to I2C calls: #define OPAL_I2C_TIMEOUT -17 #define OPAL_I2C_INVALID_CMD -18 #define OPAL_I2C_LBUS_PARITY -19 #define OPAL_I2C_BKEND_OVERRUN -20 #define OPAL_I2C_BKEND_ACCESS -21 #define OPAL_I2C_ARBT_LOST -22 #define OPAL_I2C_NACK_RCVD -23 #define OPAL_I2C_STOP_ERR -24 skiboot-skiboot-5.1.13/doc/opal-spec.txt000066400000000000000000000166531265204436200201330ustar00rootroot00000000000000OPAL Specification ================== DRAFT - VERSION 0.0.1 AT BEST. COMMENTS ARE WELCOME - and indeed, needed. If you are reading this, congratulations: you're now reviewing it! This document aims to define what it means to be OPAL compliant. While skiboot is the reference implementation, this documentation should be complete enough that (given hardware documentation) create another implementation. It is not recommended that you do this though. Authors ------- Stewart Smith : OPAL Architect, IBM Definitions ----------- Host processor - the main POWER CPU (e.g. the POWER8 CPU) Host OS - the operating system running on the host processor. OPAL - OpenPOWER Abstraction Layer. What is OPAL? ------------- The OpenPower Abstraction Layer (OPAL) is boot and runtime firmware for POWER systems. There are several components to what makes up a firmware image for OpenPower machines. For example, there may be: - BMC firmware - Firmware that runs purely on the BMC. - On IBM systems that have an FSP rather than a BMC, there is FSP firmware - While essential to having the machine function, this firmware is not part of the OPAL Specification. - HostBoot - HostBoot ( https://github.com/open-power/hostboot ) performs all processor, bus and memory initialization within IBM POWER based systems. - OCC Firmware - On Chip Controller ( Firmware for OCC - a PPC405 core inside the IBM POWER8 in charge of keeping the system thermally and power safe ). - SkiBoot - Boot and runtime services. - A linux kernel and initramfs incorporating petitboot - The bootloader. This is where a user chooses what OS to boot, and petitboot will use kexec to switch to the host Operating System (for example, PowerKVM). While all of these components may be absolutely essential to power on, boot and operate a specific OpenPower POWER8 system, the majority of the code mentioned above can be thought of as implementation details and not something that should form part of an OPAL Specification. For an OPAL system, we assume that the hardware is functioning and any hardware management that is specific to a platform is performed by OPAL firmware transparently to the host OS. The OPAL Specification focus on the interface between firmware and the Operating System. It does not dictate that any specific pieces of firmware code be used, although re-inventing the wheel is strongly discouraged. The OPAL Specification explicitly allows for: - A conforming implementation to not use any of the reference implementation code. - A conforming implementation to use any 64bit POWER ISA conforming processor, and not be limited to the IBM POWER8. - A conforming implementation to be a simulator, emulator or virtual environment - A host OS other than Linux Explicitly not covered in this specification: - A 32bit OPAL Specification There is no reason this couldn't exist but the current specification is for 64bit POWER systems only. Boot Services ------------- An OPAL compliant firmware implementation will load and execute a payload capable of booting a Host Operating System. The reference implementation loads a Linux kernel with an initramfs with a minimal userspace and the petitboot boot loader - collectively referred to as skiroot. The OPAL Specification explicitly allows variation in this payload. A requirement of the payload is that it MUST support loading and booting an uncomppressed vmlinux Linux kernel. [TODO: expand on what this actually means] An OPAL system MUST pass a device tree to the host kernel. [TODO: expand the details, add device-tree section and spec] An OPAL system MUST provide the host kernel with enough information to know how to call OPAL runtime services. [TODO: expand on this. ] Explicitly not covered by the OPAL Specification: - Kernel module ABI for skiroot kernel - Userspace environment of skiroot - That skiroot is Linux. Explicitly allowed: - Replacing the payload with something of equal/similar functionality (weather replacing skiroot with an implementation of Zork would be compliant is left as an exercise for the reader) Payload Environment ------------------- The payload is started with: r3 = address of flattened device-tree (fdt) r8 = OPAL base r9 = OPAL entry Runtime Services ---------------- An OPAL Specification compliant system provides runtime services to the host Operating System via a standard interface. An OPAL call is made by calling opal_entry with: * r0: OPAL Token * r2: OPAL Base * r3..r10: Args (up to 8) The OPAL API is defined in skiboot/doc/opal-api/ Not all OPAL APIs must be supported for a system to be compliant. When called with an unsupported token, a compliant firmware implementation MUST fail gracefully and not crash. Reporting a warning that an unsupported token was called is okay, as compliant host Operating Systems should use OPAL_CHECK_TOKEN to test for optional functionality. All parameters to OPAL calls are big endian. Little endian hosts MUST appropriately convert parameters before passing them to OPAL. Machine state across OPAL calls: - r1 is preserved - r12 is scratch - r13 - 31 preserved - 64bit HV real mode - big endian - external interrupts disabled Detecting OPAL Support ---------------------- A Host OS may need to detect the presence of OPAL as it may support booting under other platforms. For example, a single Linux kernel can be built to boot under OPAL and under PowerVM or qemu pseries machine type. The root node of the device tree MUST have compatible = "ibm,powernv". See doc/device-tree.txt for more details [TODO: make doc/device-tree.txt better] The presence of the "/ibm,opal" entry in the device tree signifies running under OPAL. Additionally, the "/ibm,opal" node MUST have a compatibile property listing "ibm,opal-v3". The "/ibm,opal" node MUST have the following properties: ibm,opal { compatible = "ibm,opal-v3"; opal-base-address = <>; opal-entry-address = <>; opal-runtime-size = <>; } The compatible property MAY have other strings, such as a future "ibm,opal-v4". These are reserved for future use. Some releases of the reference implementation (skiboot) have had compatible contain "ibm,opal-v2" as well as "ibm,opal-v3". Host operating systems MUST NOT rely on "ibm,opal-v2", this is a relic from early OPAL history. The "ibm,opal" node MUST have a child node named "firmware". It MUST contain the following: firmware { compatible = "ibm,opal-firmware"; } It MUST contain one of the following two properties: git-id, version. The git-id property is deprecated, and version SHOULD be used. These are informative and MUST NOT be used by the host OS to determine anything about the firmware environment. The version property is a textual representation of the OPAL version. For example, it may be "skiboot-4.1" or other versioning described in more detail in doc/versioning.txt OPAL log -------- OPAL implementations SHOULD have an in memory log where informational and error messages are stored. If present it MUST be human readable and text based. There is a separate facility (Platform Error Logs) for machine readable errors. A conforming implementation MAY also output the log to a serial port or similar. An implementation MAY choose to only output certain log messages to a serial port. For example, the reference implementation (skiboot) by default filters log messages so that only higher priority log messages go over the serial port while more messages go to the in memory buffer. [TODO: add device-tree bits here] skiboot-skiboot-5.1.13/doc/overview.txt000066400000000000000000000072571265204436200201160ustar00rootroot00000000000000skiboot overview ================ skiboot is firmware, loaded by the FSP. Along with loading the bootloader, it provides some runtime services to the OS (typically Linux). Source layout ------------- asm/ small amount, mainly entry points ccan/ bits from CCAN core/ common code among machines. doc/ not enough here external/ tools to run external of sapphire. hdata/ all stuff going to/from FSP hw/ drivers for things & fsp things. include/ headers! libc/ tiny libc, from SLOF libfdt/ straight device tree lib libpore/ to manipulate PORE engine. We have a spinlock implementation in asm/lock.S Entry points are detailed in asm/head.S The main C entry point is in core/init.c: main_cpu_entry() Binaries -------- The following binaries are built: skiboot.lid: is the actual lid. objdump out skiboot.elf: is the elf binary of it, lid comes from this skiboot.map: plain map of symbols Booting ------- On boot, every thread of execution jumps to a single entry point in skiboot so we need to do some magic to ensure we init things properly and don't stomp on each other. We choose a master thread, putting everybody else into a spinloop. Essentially, we do this by doing an atomic fetch and inc and whoever gets 0 gets to be the master. When we enter skiboot we also get a memory location in a register which is the location of a device tree for the system. We fatten out the device tree, turning offsets into real pointers and manipulating it where needed. We re-flatten the device tree before booting the OS (Linux). The main entry point is main_cpu_entry() in core/init.c, this is a carefully ordered init of things. The sequence is relatively well documented there. OS interface ------------ Skiboot maintains its own stack for each CPU. We do not have an ABI like "may use X stack on OS stack", we entirely keep to our own stack space. The OS (Linux) calling skiboot will never use any OS stack space and the OS does not need to call skiboot with a valid stack. We define an array of stacks, one for each CPU. On entry to skiboot, we can find out stack by multiplying our CPU number by the stack size and adding that to the address of the stack area. At the bottom of each stack area is a per CPU data structure, which we can get to by chopping off the LSBs of the stack pointer. The OPAL interface is a generic message queue. The Linux side of things can be found in linux/arch/powerpc/platform/powernv/opal-*.c Interrupts ---------- We don't handle interrupts in skiboot. In the future we may have to change to process machine check interrupts during boot. We do not have timer interrupts. Memory ------ We initially occupy a chunk of memory, "heap". We pass to the OS (Linux) a reservation of what we occupy (including stacks). In the source file include/config.h we include a memory map. This is manually generated, not automatically generated. We use CCAN for a bunch of helper code, turning on things like DEBUG_LOCKS and DEBUG_MALLOC as these are not a performance issue for us, and we like to be careful. In include/config.h there are defines for turning on extra tracing. OPAL is what we name the interface from skiboot to OS (Linux). Each CPU gets a 16k stack, which is probably more than enough. Stack should be used sparingly though. Important memory locations: SKIBOOT_BASE - where we sit HEAP_BASE, HEAP_SIZE - the location and size for heap. We reserve 4MB for initial allocations. There is also SKIBOOT_SIZE (manually calculated) and DEVICE_TREE_MAX_SIZE, which is largely historical. Skiboot log ----------- There is a circular log buffer that skiboot maintains. This can be accessed either from the FSP or through /dev/mem or through a debugfs patch that's currently floating around. skiboot-skiboot-5.1.13/doc/pci-slot-properties.txt000066400000000000000000000015501265204436200221620ustar00rootroot00000000000000 PCI Slot Properties Description =============================== The following properties have been added to the PCI Device Tree Node for the PCI Slot: ibm,slot-location-code System location code string for the slot connector ibm,slot-pluggable Boolean indicating whether the slot is pluggable ibm,slot-power-ctl Boolean indicating whether the slot has power control ibm,slot-wired-lanes The number of hardware lanes that are wired (optional) ibm,slot-connector-type The type of connector present (optional) ibm,slot-card-desc The height/length of the slot (optional) ibm,slot-card-mech Value indicating slot mechanicals and orientation (optional) ibm,slot-pwr-led-ctl Presence of slot power led, and controlling entity (optional) ibm,slot-attn-led-ctl Presence of slot ATTN led, and controlling entity (optional) skiboot-skiboot-5.1.13/doc/pci.txt000066400000000000000000000104101265204436200170040ustar00rootroot00000000000000IODA PE Setup Sequences ----------------------- (WARNING: this was rescued from old internal documentation. Needs verification) To setup basic PE mappings, the host performs this basic sequence: For ibm,opal-ioda2, prior to allocating PHB resources to PEs, the host must allocate memory for PE structures and then calls opal_pci_set_phb_table_memory( phb_id, rtt_addr, ivt_addr, ivt_len, rrba_addr, peltv_addr) to define them to the PHB. OPAL returns OPAL_UNSUPPORTED status for ibm,opal-ioda PHBs. The host calls opal_pci_set_pe( phb_id, pe_number, bus, dev, func, validate_mask, bus_mask, dev_mask, func mask) to map a PE to a PCI RID or range of RIDs in the same PE domain. The host calls opal_pci_set_peltv(phb_id, parent_pe, child_pe, state) to set a parent PELT vector bit for the child PE argument to 1 (a child of the parent) or 0 (not in the parent PE domain). IODA MMIO Setup Sequences ------------------------- (WARNING: this was rescued from old internal documentation. Needs verification) The host calls opal_pci_phb_mmio_enable( phb_id, window_type, window_num, 0x0) to disable the MMIO window. The host calls opal_pci_set_phb_mmio_window( phb_id, mmio_window, starting_real_address, starting_pci_address, segment_size) to change the MMIO window location in PCI and/or processor real address space, or to change the size -- and corresponding window size -- of a particular MMIO window. The host calls opal_pci_map_pe_mmio_window( pe_number, mmio_window, segment_number) to map PEs to window segments, for each segment mapped to each PE. The host calls opal_pci_phb_mmio_enable( phb_id, window_type, window_num, 0x1) to enable the MMIO window. IODA MSI Setup Sequences ------------------------ (WARNING: this was rescued from old internal documentation. Needs verification) To setup MSIs: 1. For ibm,opal-ioda PHBs, the host chooses an MVE for a PE to use and calls opal_pci_set_mve( phb_id, mve_number, pe_number,) to setup the MVE for the PE number. HAL treats this call as a NOP and returns hal_success status for ibm,opal-ioda2 PHBs. 2. the host chooses an XIVE to use with a PE and calls a. opal_pci_set_xive_pe( phb_id, xive_number, pe_number) to authorize that PE to signal that XIVE as an interrupt. The host must call this function for each XIVE assigned to a particular PE, but may use this call for all XIVEs prior to calling opel_pci_set_mve() to bind the PE XIVEs to an MVE.For MSI conventional, the host must bind a unique MVE for each sequential set of 32 XIVEs. b. The host forms the interrupt_source_number from the combination of the device tree MSI property base BUID and XIVE number, as an input to opal_set_xive(interrupt_source_number, server_number, priority) and opal_get_xive(interrupt_source_number, server_number, priority) to set or return the server and priority numbers within an XIVE. c. opal_get_msi_64[32](phb_id, mve_number, xive_num, msi_range, msi_address, message_data) to determine the MSI DMA address (32 or 64 bit) and message data value for that xive. For MSI conventional, the host uses this for each sequential power of 2 set of 1 to 32 MSIs, to determine the MSI DMA address and starting message data value for that MSI range. For MSI-X, the host calls this uniquely for each MSI interrupt with an msi_range input value of 1. 3. For ibm,opal-ioda PHBs, once the MVE and XIVRs are setup for a PE, the host calls opal_pci_set_mve_enable( phb_id, mve_number, state)to enable that MVE to be a valid target of MSI DMAs. The host may also call this function to disable an MVE when changing PE domains or states. IODA DMA Setup Sequences ------------------------ (WARNING: this was rescued from old internal documentation. Needs verification) To Manage DMA Windows : 1. The host calls opal_pci_map_pe_dma_window( phb_id, dma_window_number, pe_number, tce_levels, tce_table_addr, tce_table_size, tce_page_size, utin64_t* pci_start_addr ) to setup a DMA window for a PE to translate through a TCE table structure in KVM memory. 2. The host calls opal_pci_map_pe_dma_window_real( phb_id, dma_window_number, pe_number, mem_low_addr, mem_high_addr) to setup a DMA window for a PE that is translated (but validated by the PHB as an untranlsated address space authorized to this PE). skiboot-skiboot-5.1.13/doc/release-notes/000077500000000000000000000000001265204436200202425ustar00rootroot00000000000000skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.0-beta1.txt000066400000000000000000000177241265204436200241210ustar00rootroot00000000000000skiboot-5.1-beta1 ----------------- skiboot-5.1.0-beta1 was released on July 21st, 2015. skiboot-5.1.0-beta1 is the first beta release of skiboot 5.1, which will become a new stable release, replacing skiboot-5.0 (released April 14th 2015) Skiboot 5.1-beta1 contains all fixes from skiboot-5.0 stable branch up to skiboot-5.0.5. Over skiboot-5.0, the following features have been added: - Centaur i2c support - Add Naples chip (CPU, PHB, LPC serial interrupts) support - Added qemu platform - improvements to FSI error handling - improvements in chip TOD failover (some only on FSP systems) - Set Relative Priority Register (RPR) to recommended value - this affects thread priority in SMT modes - greatly reduce memory consumption by CPU stacks for non-present CPUs - Previously we would reserve enough memory for max PIR for each CPU type. - This fix frees up 77MB of RAM on a typical P8 system. - increased OPAL API documentation - Asynchronous preloading of resources from FSP/flash - improves boot time on some systems - Basic Garrison platform support - Add Mambo platform (P8 Functional Simulator, systemsim) - includes fake NVRAM, RTC - Support building with GCOV, increasing memory for skiboot binary to 2MB - includes boot code coverage testing - Increased skiboot HEAP size. - We are not aware of any system where you would run out, but on large systems it was getting closer than we liked. - add boot_tests.sh for helping automate boot testing on FSP and BMC machines - Versioning of pflash and gard utilities to help Linux (or other OS) distributions with packaging. - OCC throttle status messages to host - CAPP timebase sync ("ibm,capp-timebase-sync" in DT to indicate CAPP timebase was synced by OPAL) New features for FSP based machines: - in-band IPMI support - ethernet adaptor location codes - add DIMM frequency information to device tree - improvements in FSP error log code paths - fix some boot time memory leaks - harmless to end user New features for AMI BMC based machines: - PCIe power workaround for K80 - Added support for Macronix 128Mbit flash chips - Initial PRD support for Firestone platform - improved reliability when BMC reboots The following bugs have been fixed: - Increase PHB3 timeout for electrical links coming up to 2 seconds. - fixes issues with some Mellanox cards - Hang in opal_reinit_cpus() that could prevent kdump from functioning - PHB3: fix crash in phb3_init - PHB3: fix crash with fenced PHB in phb3_init_hw() - Fix bugs in hw/bt.c (interface for IPMI on BMC machines) that could possibly lead to a crash (dereferencing invalid address, deadlock) - ipmi/sel: fix use-after-free - Bug fixes in EEH handling - opal_pci_next_error() cleared OPAL_EVENT_PCI_ERROR unconditionally, possibly leading to missed errors. FSP-specific bugs fixed: - (also fixed in skiboot-5.0.2) Fix race in firenze_get_slot_info() leading to assert() with many PCI cards With many PCI cards, we'd hit a race where calls to firenze_add_pcidev_to_fsp_inventory would step on each other leading to memory corruption and finally an assert() in the allocator being hit during boot. - PCIe power workaround for K80 cards - /ibm,opal/led renamed to /ibm,opal/leds in Device Tree - compatible change as no FSP based systems shipped with skiboot-5.0 General improvements: - don't run pollers on non-boot CPUs in time_wait - improvements to opal-prd, pflash, libflash - including new blocklevel interface in libflash - many minor fixes to issues found by static analysis - improvements in FSP error log code paths - code cleanup in memory allocator - Don't expose individual nvram partitions in the device tree, just the whole flash device. - build improvements for building on ppc64el host - improvements in cpu_relax() for idle threads, needed for GCOV on large machines. - Optimized memset() for POWER8, greatly reducing number of instructions executed for boot, which helps boot time in simulators. - Major improvements in hello_world kernel - Bloat of huge 17 instruction test case reduced to 10. - Disable bust_locks for general calls of abort() - Should enable better error messages during abort() when other users of LPC bus exist (e.g. flash) Contributors ------------ Thanks to everyone who has made skiboot-5.1.0-beta1 happen! Processed 321 csets from 25 developers 3 employers found A total of 13696 lines added, 2754 removed (delta 10942) Developers with the most changesets Stewart Smith 101 (31.5%) Benjamin Herrenschmidt 32 (10.0%) Cyril Bur 31 (9.7%) Vasant Hegde 28 (8.7%) Jeremy Kerr 27 (8.4%) Kamalesh Babulal 19 (5.9%) Alistair Popple 12 (3.7%) Mahesh Salgaonkar 12 (3.7%) Neelesh Gupta 8 (2.5%) Cédric Le Goater 8 (2.5%) Joel Stanley 8 (2.5%) Ananth N Mavinakayanahalli 8 (2.5%) Gavin Shan 6 (1.9%) Michael Neuling 6 (1.9%) Frederic Bonnard 3 (0.9%) Vipin K Parashar 2 (0.6%) Vaidyanathan Srinivasan 2 (0.6%) Philippe Bergheaud 1 (0.3%) Shilpasri G Bhat 1 (0.3%) Daniel Axtens 1 (0.3%) Hari Bathini 1 (0.3%) Michael Ellerman 1 (0.3%) Andrei Warkentin 1 (0.3%) Dan Horák 1 (0.3%) Anton Blanchard 1 (0.3%) Developers with the most changed lines Stewart Smith 3987 (27.9%) Benjamin Herrenschmidt 3811 (26.6%) Cyril Bur 1918 (13.4%) Jeremy Kerr 1307 (9.1%) Mahesh Salgaonkar 886 (6.2%) Vasant Hegde 764 (5.3%) Neelesh Gupta 473 (3.3%) Vipin K Parashar 176 (1.2%) Alistair Popple 175 (1.2%) Philippe Bergheaud 171 (1.2%) Shilpasri G Bhat 165 (1.2%) Cédric Le Goater 89 (0.6%) Frederic Bonnard 78 (0.5%) Gavin Shan 73 (0.5%) Joel Stanley 65 (0.5%) Kamalesh Babulal 63 (0.4%) Michael Neuling 47 (0.3%) Daniel Axtens 31 (0.2%) Ananth N Mavinakayanahalli 22 (0.2%) Anton Blanchard 3 (0.0%) Vaidyanathan Srinivasan 2 (0.0%) Hari Bathini 2 (0.0%) Michael Ellerman 1 (0.0%) Andrei Warkentin 1 (0.0%) Dan Horák 1 (0.0%) Developers with the most lines removed Vipin K Parashar 105 (3.8%) Michael Neuling 24 (0.9%) Hari Bathini 1 (0.0%) Developers with the most signoffs (total 214) Stewart Smith 214 (100.0%) Developers with the most reviews (total 21) Vasant Hegde 7 (33.3%) Joel Stanley 3 (14.3%) Gavin Shan 2 (9.5%) Kamalesh Babulal 2 (9.5%) Alistair Popple 2 (9.5%) Stewart Smith 1 (4.8%) Andrei Warkentin 1 (4.8%) Preeti U Murthy 1 (4.8%) Samuel Mendoza-Jonas 1 (4.8%) Ananth N Mavinakayanahalli 1 (4.8%) Developers with the most test credits (total 1) Chad Larson 1 (100.0%) Developers who gave the most tested-by credits (total 1) Gavin Shan 1 (100.0%) Developers with the most report credits (total 4) Benjamin Herrenschmidt 2 (50.0%) Chad Larson 1 (25.0%) Andrei Warkentin 1 (25.0%) Developers who gave the most report credits (total 4) Stewart Smith 3 (75.0%) Gavin Shan 1 (25.0%) Top changeset contributors by employer IBM 319 (99.4%) dan@danny.cz 1 (0.3%) andrey.warkentin@gmail.com 1 (0.3%) Top lines changed by employer IBM 14309 (100.0%) dan@danny.cz 1 (0.0%) andrey.warkentin@gmail.com 1 (0.0%) Employers with the most signoffs (total 214) IBM 214 (100.0%) Employers with the most hackers (total 25) IBM 23 (92.0%) dan@danny.cz 1 (4.0%) andrey.warkentin@gmail.com 1 (4.0%) skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.0-beta2.txt000066400000000000000000000046061265204436200241150ustar00rootroot00000000000000skiboot-5.1-beta2 ----------------- skiboot-5.1.0-beta2 was released on August 14th, 2015. skiboot-5.1.0-beta2 is the second beta release of skiboot 5.1, which will become a new stable release, replacing skiboot-5.0 (released April 14th 2015) Skiboot 5.1.0-beta2 contains all fixes from skiboot-5.0 stable branch up to skiboot-5.0.5 and everything from 5.1.0-beta1. Over skiboot-5.1.0-beta1, the following features have been added: - opal-api: Add OPAL call to handle abnormal reboots. OPAL_CEC_REBOOT2 Currently it will support two reboot types (0). normal reboot, that will behave similar to that of opal_cec_reboot() call, and (1). platform error reboot. Long term, this is designed to replace OPAL_CEC_REBOOT. Over skiboot-5.1.0-beta1, the following bugs have been fixed: - external/opal-prd: Only map each PRD range once - could eventually lead to failing to map PRD ranges - On skiboot crash, don't try to print symbol when we didn't find one - makes backtrace prettier - On skiboot crash, dump hssr0 and hsrr1 registers correctly. - Better support old and biarch compilers - test "new" compiler flags before using them - Specify -mabi=elfv1 if supported (which means it's needed) - fix boot-coverage-report makefile target - ipmi: Fix the opal_ipmi_recv() call to handle the error path - Could make kernel a sad panda when in continues with other IPMI commands - IPMI: truncate SELs at 2kb - it's the limit of the astbmc. We think. - IPMI/SEL/PEL: - As per PEL spec, we should log events with severity >= 0x22 and "service action flag" is "on". But in our case, all logs OPAL originagted logs are makred as report externally. We now only report logs with severity >= 0x22 - IPMI: fixes to eSEL logging - hw/phb3: Change reserved PE to 255 - Currently, we have reserved PE#0 to which all RIDs are mapped prior to PE assignment request from kernel. The last M64 BAR is configured to have shared mode. So we have to cut off the first M64 segment, which corresponds to reserved PE#0 in kernel. If the first BAR (for example PF's IOV BAR) requires huge alignment in kernel, we have to waste huge M64 space to accomodate the alignment. If we have reserved PE#256, the waste of M64 space will be avoided. Other changes: - unified version numbers for bundled utilities - external/boot_test/boot_test.sh - better usable for automated boot testing skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.0.txt000066400000000000000000000243361265204436200231240ustar00rootroot00000000000000skiboot-5.1.0 ------------- skiboot-5.1.0 was released on August 17th, 2015. skiboot-5.1.0 is the first stable release of 5.1.0 following two beta releases. This new stable release replaces skiboot-5.0 as the current stable skiboot release (5.0 was released April 14th 2015). Skiboot 5.1.0 contains all fixes from skiboot-5.0 stable branch up to skiboot-5.0.5 and everything from 5.1.0-beta1 and 5.1.0-beta2. Over skiboot-5.1.0-beta2, we have the following changes: - opal_prd now supports multiple socket systems - fix compiler warnings in gard and libflash Below are the changes introduced in previous skiboot-5.1.0 releases over the previous stable release, skiboot-5.0: New features: - Add Naples chip (CPU, PHB, LPC serial interrupts) support - Added qemu platform - improvements to FSI error handling - improvements in chip TOD failover (some only on FSP systems) - Set Relative Priority Register (RPR) to recommended value - this affects thread priority in SMT modes - greatly reduce memory consumption by CPU stacks for non-present CPUs - Previously we would reserve enough memory for max PIR for each CPU type. - This fix frees up 77MB of RAM on a typical P8 system. - increased OPAL API documentation - Asynchronous preloading of resources from FSP/flash - improves boot time on some systems - Basic Garrison platform support - Add Mambo platform (P8 Functional Simulator, systemsim) - includes fake NVRAM, RTC - Support building with GCOV, increasing memory for skiboot binary to 2MB - includes boot code coverage testing - Increased skiboot HEAP size. - We are not aware of any system where you would run out, but on large systems it was getting closer than we liked. - add boot_tests.sh for helping automate boot testing on FSP and BMC machines - Versioning of pflash and gard utilities to help Linux (or other OS) distributions with packaging. - OCC throttle status messages to host - CAPP timebase sync ("ibm,capp-timebase-sync" in DT to indicate CAPP timebase was synced by OPAL) - opal-api: Add OPAL call to handle abnormal reboots. OPAL_CEC_REBOOT2 Currently it will support two reboot types (0). normal reboot, that will behave similar to that of opal_cec_reboot() call, and (1). platform error reboot. Long term, this is designed to replace OPAL_CEC_REBOOT. New features for FSP based machines: - in-band IPMI support - ethernet adaptor location codes - add DIMM frequency information to device tree - improvements in FSP error log code paths - fix some boot time memory leaks - harmless to end user New features for AMI BMC based machines: - PCIe power workaround for K80 - Added support for Macronix 128Mbit flash chips - Initial PRD support for Firestone platform - improved reliability when BMC reboots The following bugs have been fixed: - Increase PHB3 timeout for electrical links coming up to 2 seconds. - fixes issues with some Mellanox cards - Hang in opal_reinit_cpus() that could prevent kdump from functioning - PHB3: fix crash in phb3_init - PHB3: fix crash with fenced PHB in phb3_init_hw() - Fix bugs in hw/bt.c (interface for IPMI on BMC machines) that could possibly lead to a crash (dereferencing invalid address, deadlock) - ipmi/sel: fix use-after-free - Bug fixes in EEH handling - opal_pci_next_error() cleared OPAL_EVENT_PCI_ERROR unconditionally, possibly leading to missed errors. - external/opal-prd: Only map each PRD range once - could eventually lead to failing to map PRD ranges - On skiboot crash, don't try to print symbol when we didn't find one - makes backtrace prettier - On skiboot crash, dump hssr0 and hsrr1 registers correctly. - Better support old and biarch compilers - test "new" compiler flags before using them - Specify -mabi=elfv1 if supported (which means it's needed) - fix boot-coverage-report makefile target - ipmi: Fix the opal_ipmi_recv() call to handle the error path - Could make kernel a sad panda when in continues with other IPMI commands - IPMI: truncate SELs at 2kb - it's the limit of the astbmc. We think. - IPMI/SEL/PEL: - As per PEL spec, we should log events with severity >= 0x22 and "service action flag" is "on". But in our case, all logs OPAL originagted logs are makred as report externally. We now only report logs with severity >= 0x22 - IPMI: fixes to eSEL logging - hw/phb3: Change reserved PE to 255 - Currently, we have reserved PE#0 to which all RIDs are mapped prior to PE assignment request from kernel. The last M64 BAR is configured to have shared mode. So we have to cut off the first M64 segment, which corresponds to reserved PE#0 in kernel. If the first BAR (for example PF's IOV BAR) requires huge alignment in kernel, we have to waste huge M64 space to accomodate the alignment. If we have reserved PE#256, the waste of M64 space will be avoided. FSP-specific bugs fixed: - (also fixed in skiboot-5.0.2) Fix race in firenze_get_slot_info() leading to assert() with many PCI cards With many PCI cards, we'd hit a race where calls to firenze_add_pcidev_to_fsp_inventory would step on each other leading to memory corruption and finally an assert() in the allocator being hit during boot. - PCIe power workaround for K80 cards - /ibm,opal/led renamed to /ibm,opal/leds in Device Tree - compatible change as no FSP based systems shipped with skiboot-5.0 General improvements: - Preliminary Centaur i2c support - lays framework for supporting Centaur i2c - don't run pollers on non-boot CPUs in time_wait - improvements to opal-prd, pflash, libflash - including new blocklevel interface in libflash - many minor fixes to issues found by static analysis - improvements in FSP error log code paths - code cleanup in memory allocator - Don't expose individual nvram partitions in the device tree, just the whole flash device. - build improvements for building on ppc64el host - improvements in cpu_relax() for idle threads, needed for GCOV on large machines. - Optimized memset() for POWER8, greatly reducing number of instructions executed for boot, which helps boot time in simulators. - Major improvements in hello_world kernel - Bloat of huge 17 instruction test case reduced to 10. - Disable bust_locks for general calls of abort() - Should enable better error messages during abort() when other users of LPC bus exist (e.g. flash) - unified version numbers for bundled utilities - external/boot_test/boot_test.sh - better usable for automated boot testing Contributors ------------ Since skiboot-5.0, we've had the following changesets: Processed 372 csets from 27 developers 2 employers found A total of 15868 lines added, 3359 removed (delta 12509) Developers with the most changesets Stewart Smith 117 (31.5%) Jeremy Kerr 37 (9.9%) Cyril Bur 33 (8.9%) Vasant Hegde 32 (8.6%) Benjamin Herrenschmidt 32 (8.6%) Kamalesh Babulal 22 (5.9%) Joel Stanley 12 (3.2%) Mahesh Salgaonkar 12 (3.2%) Alistair Popple 12 (3.2%) Neelesh Gupta 9 (2.4%) Gavin Shan 8 (2.2%) Cédric Le Goater 8 (2.2%) Ananth N Mavinakayanahalli 8 (2.2%) Vipin K Parashar 6 (1.6%) Michael Neuling 6 (1.6%) Samuel Mendoza-Jonas 3 (0.8%) Frederic Bonnard 3 (0.8%) Andrew Donnellan 2 (0.5%) Vaidyanathan Srinivasan 2 (0.5%) Philippe Bergheaud 1 (0.3%) Shilpasri G Bhat 1 (0.3%) Daniel Axtens 1 (0.3%) Hari Bathini 1 (0.3%) Michael Ellerman 1 (0.3%) Andrei Warkentin 1 (0.3%) Dan Horák 1 (0.3%) Anton Blanchard 1 (0.3%) Developers with the most changed lines Stewart Smith 4499 (27.3%) Benjamin Herrenschmidt 3782 (22.9%) Jeremy Kerr 1887 (11.4%) Cyril Bur 1654 (10.0%) Vasant Hegde 959 (5.8%) Mahesh Salgaonkar 886 (5.4%) Neelesh Gupta 473 (2.9%) Samuel Mendoza-Jonas 387 (2.3%) Vipin K Parashar 332 (2.0%) Philippe Bergheaud 171 (1.0%) Shilpasri G Bhat 165 (1.0%) Alistair Popple 151 (0.9%) Joel Stanley 105 (0.6%) Cédric Le Goater 89 (0.5%) Gavin Shan 83 (0.5%) Frederic Bonnard 76 (0.5%) Kamalesh Babulal 65 (0.4%) Michael Neuling 46 (0.3%) Daniel Axtens 31 (0.2%) Andrew Donnellan 22 (0.1%) Ananth N Mavinakayanahalli 20 (0.1%) Anton Blanchard 3 (0.0%) Vaidyanathan Srinivasan 2 (0.0%) Hari Bathini 2 (0.0%) Michael Ellerman 1 (0.0%) Andrei Warkentin 1 (0.0%) Dan Horák 1 (0.0%) Developers with the most lines removed Michael Neuling 24 (0.7%) Hari Bathini 1 (0.0%) Developers with the most signoffs (total 253) Stewart Smith 249 (98.4%) Mahesh Salgaonkar 4 (1.6%) Developers with the most reviews (total 24) Vasant Hegde 9 (37.5%) Joel Stanley 3 (12.5%) Gavin Shan 2 (8.3%) Kamalesh Babulal 2 (8.3%) Samuel Mendoza-Jonas 2 (8.3%) Alistair Popple 2 (8.3%) Stewart Smith 1 (4.2%) Andrei Warkentin 1 (4.2%) Preeti U Murthy 1 (4.2%) Ananth N Mavinakayanahalli 1 (4.2%) Developers with the most test credits (total 1) Chad Larson 1 (100.0%) Developers who gave the most tested-by credits (total 1) Gavin Shan 1 (100.0%) Developers with the most report credits (total 4) Benjamin Herrenschmidt 2 (50.0%) Chad Larson 1 (25.0%) Andrei Warkentin 1 (25.0%) Developers who gave the most report credits (total 4) Stewart Smith 3 (75.0%) Gavin Shan 1 (25.0%) Top changeset contributors by employer IBM 369 (99.2%) (Unknown) 3 (0.8%) Top lines changed by employer IBM 16497 (100.0%) (Unknown) 3 (0.0%) Employers with the most signoffs (total 253) IBM 253 (100.0%) Employers with the most hackers (total 27) IBM 24 (88.9%) (Unknown) 3 (11.1%) skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.1.txt000066400000000000000000000032341265204436200231170ustar00rootroot00000000000000skiboot-5.1.1 ------------- skiboot-5.1.1 was released on August 18th, 2015. skiboot-5.1.1 is the send stable release of 5.1, it follows skiboot-5.1.0. Skiboot 5.1.1 contains all fixes from skiboot-5.1.0 and is a minor bugfix release. Over skiboot-5.1.0, we have the following changes: - Fix detection of compiler options on ancient GCC (e.g. gcc 4.4, shipped with RHEL6) - ensure the GNUC version defines for GCOV are coming from target CC rather than host CC for extract-gcov - phb3: Continue CAPP setup even if PHB is already in CAPP mode This fixes a critical bug in CAPI support. CAPI requires that all faults are escalated into a fence, not a freeze. This is done by setting bits in a number of MMIO registers. phb3_set_capi_mode() calls phb3_init_capp_errors() to do this. However, if the PHB is already in CAPP mode - for example in the recovery case - phb3_set_capi_mode() will bail out early, and those registers will not be set. This is quite easy to verify. PCI config space access errors, for example, normally cause a freeze. On a CAPI-mode PHB, they should cause a fence. Say we have a CAPI card on PHB 0, and we inject a PCI config space error: echo 0x8000000000000000 > /sys/kernel/debug/powerpc/PCI0000/err_injct_inboundA; lspci; The first time we inject this, the PHB will fence and recover, but won't reset the registers. Therefore, the second time we inject it, we will incorrectly freeze, not fence. Worse, the recovery for the resultant EEH freeze event interacts poorly with the CAPP, triggering an EEH recovery of the PHB. The combination of the two attempted recoveries will get the PHB into an inoperable state. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.10.txt000066400000000000000000000026331265204436200232010ustar00rootroot00000000000000skiboot-5.1.10 -------------- skiboot-5.1.10 was released on Friday November 13th, 2015. skiboot-5.1.10 is the 11th stable release of 5.1, it follows skiboot-5.1.9 (which was released October 30th, 2015). Skiboot 5.1.10 contains all fixes from skiboot-5.1.9 and is a minor bug fix release. Over skiboot-5.1.9, we have the following change: IBM FSP machines: - FSP: Handle Delayed Power Off initiated CEC shutdown with FSP in Reset/Reload In a scenario where the DPO has been initiated, but the FSP then went into reset before the CEC power down came in, OPAL may not give up the link since it may never see the PSI interrupt. So, if we are in dpo_pending and an FSP reset is detected via the DISR, give up the PSI link voluntarily. Generic: - sensor: add a compatible property OPAL needs an extra compatible property "ibm,opal-sensor" to make module autoload work smoothly in Linux for ibmpowernv driver. - console: Completely flush output buffer before power down and reboot Completely flush the output buffer of the console driver before power down and reboot. Implements the flushing function for uart consoles, which includes the astbmc and rhesus platforms. This fixes an issue where some console output is sometimes lost before power down or reboot in uart consoles. If this issue is also prevalent in other console types then it can be fixed later by adding a .flush to that driver's con_ops. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.11.txt000066400000000000000000000010321265204436200231720ustar00rootroot00000000000000skiboot-5.1.11 -------------- skiboot-5.1.11 was released on Friday November 13th, 2015. Since it was Friday 13th, we had to find a bug right after we tagged and released skiboot-5.1.10. skiboot-5.1.11 is the 12th stable release of 5.1, it follows skiboot-5.1.10 (which was released November 13th, 2015). Skiboot 5.1.11 contains one additional bug fix over skiboot-5.1.10. It is: - On IBM FSP machines, if IPMI/Serial console is not connected during shutdown or reboot, machine would enter termination state rather than shut down. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.12.txt000066400000000000000000000031321265204436200231760ustar00rootroot00000000000000skiboot-5.1.12 -------------- skiboot-5.1.12 was released on Friday December 4th, 2015. skiboot-5.1.12 is the 13th stable release of 5.1, it follows skiboot-5.1.11 (which was released November 13th, 2015). Skiboot 5.1.12 contains bug fixes and a performance improvement. opal-prd: - Display an explict and obvious message if running on a system that does not support opal-prd, such as an IBM FSP based POWER system, where the FSP takes on the role of opal-prd. pflash: - Fix a missing (C) header - cherry-picked from master. General: - Don't link with libgcc - On some toolchains, we don't have libgcc available. POWER8 PHB (PCIe) specific: - hw/phb3: Flush cache line after updating P/Q bits When doing an MSI EOI, we update the P and Q bits in the IVE. That causes the corresponding cache line to be dirty in the L3 which will cause a subsequent update by the PHB (upon recieving the next MSI) to get a few retries until it gets flushed. We improve the situation (and thus performance) by doing a dcbf instruction to force a flush of the update we do in SW. This improves interrupt performance, reducing latency per interrupt. The improvement will vary by workload. IBM FSP based machines: - FSP: Give up PSI link on shutdown This clears up some erroneous SRCs (error logs) in some situations. - Correctly report back Real Time Clock errors to host Under certain rare error conditions, we could return an error code to the host OS that would cause current Linux kernels to get stuck in an infinite loop during boot. This was introduced in skiboot-5.0-rc1. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.13.txt000066400000000000000000000032361265204436200232040ustar00rootroot00000000000000skiboot-5.1.13 -------------- skiboot-5.1.13 was released on Wed January 27th, 2016. skiboot-5.1.13 is the 14th stable release of 5.1, it follows skiboot-5.1.12 (which was released December 4th, 2015). This release contains bug fixes. General: - core/device.c: Sort nodes with name@unit names by unit - This gives predictable device tree ordering to the payload (usually petitboot) - This means that utilities such as "lspci" will always return the same ordering. - Add OPAL_CONSOLE_FLUSH to the OPAL API uart consoles only flush output when polled. The Linux kernel calls these pollers frequently, except when in a panic state. As such, panic messages are not fully printed unless the system is configured to reboot after panic. This patch adds a new call to the OPAL API to flush the buffer. If the system has a uart console (i.e. BMC machines), it will incrementally flush the buffer, returning if there is more to be flushed or not. If the system has a different console, the function will have no effect. This will allow the Linux kernel to ensure that panic message have been fully printed out. CAPI: - hmi: Identify the phb upon CAPI malfunction alert Previously, any error on a CAPI adapter would assume PHB0. This could cause issues on Firestone machines. gard utility: - Fix displaying 'cleared' gard records When a garded component is replaced hostboot detects this and updates the gard partition. Previously, there was ambiguity on if the gard record ID or the whole gard record needed to be erased. This fix makes gard and hostboot agree. firestone platform: - fix spacing in slot name The other SlotN names have no space. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.2.txt000066400000000000000000000107321265204436200231210ustar00rootroot00000000000000skiboot-5.1.2 ------------- skiboot-5.1.2 was released on September 9th, 2015. skiboot-5.1.2 is the third stable release of 5.1, it follows skiboot-5.1.1 (which was released August 18th, 2015). Skiboot 5.1.2 contains all fixes from skiboot-5.1.1 and is a minor bugfix release. Over skiboot-5.1.1, we have the following changes: - phb3: Handle fence in phb3_pci_msi_check_q to fix hang If the PHB is fenced during phb3_pci_msi_check_q, it can get stuck in an infinite loop waiting to lock the FFI. Further, as the phb lock is held during this function it will prevent any other CPUs from dealing with the fence, leading to the entire system hanging. If the PHB_FFI_LOCK returns all Fs, return immediately to allow the fence to be dealt with. - phb3: Continue CAPP setup even if PHB is already in CAPP mode This fixes a critical bug in CAPI support. - Platform hook for terminate call - on assert() or other firmware failure, we will make a SEL callout on ASTBMC platforms - (slight) refactor of code for IBM-FSP platforms - refactor slot naming code - Slot names for Habanero platform - misc improvements in userspace utilities (incl pflash, gard) - build improvements - fixes for two compiler warnings were squashed in 5.1.1 commit, re-introduce the fixes. - misc complier/static analysis warning fixes - gard utility: - If gard tool detects the GUARD PNOR partition is corrupted, it will pro-actively re-initialize it. Modern Hostboot is more sensitive to the content of the GUARD partition in order to boot. - Update record clearing to match Hostboots expectations We now write ECC bytes throughout the whole partition. Without this fix, hostboot may not bring up the machine. - In the event of a corrupted GUARD partition so that even the first entry cannot be read, the gard utility now provides the user with the option to wipe the entirety of the GUARD partition to attempt recovery. - opal_prd utility: - Add run command to pass through commands to HostBoot RunTime (HBRT) - this is for OpenPower firmware developers only. - Add htmght-passthru command. - this is for OpenPower firmware developers only. - Add override interface to pass attribute-override information to HBRT. - Server sends response in error path, so that client doesn't block forever - external/mambo tcl scripts - Running little-endian kernels in mambo requires HILE to be set properly, which requires a bump in the machine's pvr value to a DD2.x chip. Stats ----- For skiboot-5.1.0 to 5.1.2: Processed 67 csets from 11 developers 1 employers found A total of 2258 lines added, 784 removed (delta 1474) Developers with the most changesets Stewart Smith 24 (35.8%) Cyril Bur 18 (26.9%) Vasant Hegde 8 (11.9%) Neelesh Gupta 5 (7.5%) Benjamin Herrenschmidt 5 (7.5%) Daniel Axtens 2 (3.0%) Samuel Mendoza-Jonas 1 (1.5%) Vaidyanathan Srinivasan 1 (1.5%) Vipin K Parashar 1 (1.5%) Ian Munsie 1 (1.5%) Michael Neuling 1 (1.5%) Developers with the most changed lines Cyril Bur 969 (42.5%) Neelesh Gupta 433 (19.0%) Benjamin Herrenschmidt 304 (13.3%) Vasant Hegde 236 (10.3%) Stewart Smith 163 (7.1%) Vaidyanathan Srinivasan 135 (5.9%) Vipin K Parashar 8 (0.4%) Ian Munsie 8 (0.4%) Daniel Axtens 2 (0.1%) Michael Neuling 2 (0.1%) Samuel Mendoza-Jonas 1 (0.0%) Developers with the most lines removed Daniel Axtens 2 (0.3%) Michael Neuling 1 (0.1%) Developers with the most signoffs (total 44) Stewart Smith 43 (97.7%) Neelesh Gupta 1 (2.3%) Developers with the most reviews (total 8) Patrick Williams 5 (62.5%) Samuel Mendoza-Jonas 3 (37.5%) Developers with the most test credits (total 0) Developers who gave the most tested-by credits (total 0) Developers with the most report credits (total 1) Benjamin Herrenschmidt 1 (100.0%) Developers who gave the most report credits (total 1) Samuel Mendoza-Jonas 1 (100.0%) Top changeset contributors by employer IBM 67 (100.0%) Top lines changed by employer IBM 2281 (100.0%) Employers with the most signoffs (total 44) IBM 44 (100.0%) Employers with the most hackers (total 11) IBM 11 (100.0%) skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.3.txt000066400000000000000000000061421265204436200231220ustar00rootroot00000000000000skiboot-5.1.3 ------------- skiboot-5.1.3 was released on September 15th, 2015. skiboot-5.1.3 is the 4th stable release of 5.1, it follows skiboot-5.1.2 (which was released September 9th, 2015). Skiboot 5.1.3 contains all fixes from skiboot-5.1.2 and is a minor bugfix release. Over skiboot-5.1.2, we have the following changes: - slot names for firestone platform - fix display of LPC errors - SBE based timer support - on supported platforms limits reliance on Linux heartbeat - fix use-after-free in fsp/ipmi - fix hang on TOD/TB errors (time-of-day/timebase) on OpenPower systems - On getting a Hypervizor Maintenance Interrupt to get the timebase back into a running state, we would call prlog which would use the LPC UART console driver on OpenPower systems, which depends on a working timebase, leading to a hang. We now don't depend on a working timebase in this recovery codepath. - enable prd for garrison platform - PCI: Clear error bits after changing MPS Chaning MPS on PCI upstream bridge might cause error bits set on downstream endpoints when system boots into Linux as below case shows: host# lspci -vvs 0001:06:00.0 0001:06:00.0 Ethernet controller: Broadcom Corporation \ NetXtreme II BCM57810 10 Gigabit Ethernet (rev 10) DevSta: CorrErr+ UncorrErr- FatalErr- UnsuppReq+ AuxPwr- TransPend- CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+ This clears those error bits in AER and PCIe capability after MPS is changed. With the patch applied, no more error bits are seen. Contributors ------------ Processed 14 csets from 6 developers 1 employers found A total of 462 lines added, 163 removed (delta 299) Developers with the most changesets Benjamin Herrenschmidt 5 (35.7%) Stewart Smith 4 (28.6%) Mahesh Salgaonkar 2 (14.3%) Gavin Shan 1 (7.1%) Jeremy Kerr 1 (7.1%) Neelesh Gupta 1 (7.1%) Developers with the most changed lines Benjamin Herrenschmidt 407 (80.8%) Mahesh Salgaonkar 23 (4.6%) Gavin Shan 19 (3.8%) Stewart Smith 18 (3.6%) Jeremy Kerr 5 (1.0%) Neelesh Gupta 2 (0.4%) Developers with the most lines removed Stewart Smith 8 (4.9%) Jeremy Kerr 3 (1.8%) Neelesh Gupta 1 (0.6%) Developers with the most signoffs (total 10) Stewart Smith 10 (100.0%) Developers with the most reviews (total 1) Joel Stanley 1 (100.0%) Developers with the most test credits (total 0) Developers who gave the most tested-by credits (total 0) Developers with the most report credits (total 1) John Walthour 1 (100.0%) Developers who gave the most report credits (total 1) Gavin Shan 1 (100.0%) Top changeset contributors by employer IBM 14 (100.0%) Top lines changed by employer IBM 504 (100.0%) Employers with the most signoffs (total 10) IBM 10 (100.0%) Employers with the most hackers (total 6) IBM 6 (100.0%) skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.4.txt000066400000000000000000000023611265204436200231220ustar00rootroot00000000000000skiboot-5.1.4 ------------- skiboot-5.1.4 was released on September 26th, 2015. skiboot-5.1.4 is the 5th stable release of 5.1, it follows skiboot-5.1.3 (which was released September 15th, 2015). Skiboot 5.1.4 contains all fixes from skiboot-5.1.3 and is an important bug fix release and a strongly recommended update from any prior skiboot-5.1.x release. Over skiboot-5.1.3, we have the following changes: - Rate limit OPAL_MSG_OCC to only one outstanding message to host In the event of a lot of OCC events (or many CPU cores), we could send many OCC messages to the host, which if it wasn't calling opal_get_msg really often, would cause skiboot to malloc() additional messages until we ran out of skiboot heap and things didn't end up being much fun. When running certain hardware exercisers, they seem to steal all time from Linux being able to call opal_get_msg, causing these to queue up and get "opalmsg: No available node in the free list, allocating" warnings followed by tonnes of backtraces of failing memory allocations. - Ensure reserved memory ranges are exposed correctly to host (fix corrupted SLW image) We seem to have not hit this on ASTBMC based OpenPower machines, but was certainly hit on FSP based machines skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.5.txt000066400000000000000000000027271265204436200231310ustar00rootroot00000000000000skiboot-5.1.5 ------------- skiboot-5.1.5 was released on October 1st, 2015. skiboot-5.1.5 is the 6th stable release of 5.1, it follows skiboot-5.1.4 (which was released September 26th, 2015). Skiboot 5.1.5 contains all fixes from skiboot-5.1.4 and is a minor bug fix release. Over skiboot-5.1.4, we have the following changes: Generic: - centaur: Add indirect XSCOM support Fixes a bug where opal-prd would not be able to recover from a bunch of errors as the indirect XSCOMs to centaurs would fail. - xscom: Fix logging of indirect XSCOM errors Better logging of error messages. - PHB3: Fix wrong PE number in error injection - Improvement in boot_test.sh utility to support copying a pflash binary to BMCs. AST BMC machines: - ipmi-sel: Run power action immediately if host not up Our normal sequence for a soft power action (IPMI 'power soft' or 'power cycle') involve receiving a SEL from the BMC, sending a message to Linux's opal platform support which instructs the host OS to shut down, and finally the host will request OPAL to cut power. When the host is not yet up we will send the message to /dev/null, and no action will be taken. This patches changes that behaviour to perform the action immediately if we know how. OpenPower machines: - opal-prd: Increase IPMI timeout to a slightly better value Proactively bump the timeout to 5seconds to match current value in petitboot Observed in the wild that this fixes bugs for petitboot. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.6.txt000066400000000000000000000016241265204436200231250ustar00rootroot00000000000000skiboot-5.1.6 ------------- skiboot-5.1.6 was released on October 8th, 2015. skiboot-5.1.6 is the 7th stable release of 5.1, it follows skiboot-5.1.5 (which was released October 1st, 2015). Skiboot 5.1.6 contains all fixes from skiboot-5.1.5 and is a minor bug fix release. Over skiboot-5.1.5, we have the following changes: Generic: - Ensure we run pollers in cpu_wait_job() In root causing a bug on AST BMC Alistair found that pollers weren't being run for around 3800ms. This could show as not resetting the boot count sensor on successful boot. AST BMC Machines: - hw/bt.c: Check for timeout after checking for message response When deciding if a BT message has timed out we should first check for a message response. This will ensure that messages will not time out if there was a delay calling the pollers. This could show as not resetting the boot count sensor on successful boot. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.7.txt000066400000000000000000000022271265204436200231260ustar00rootroot00000000000000skiboot-5.1.7 ------------- skiboot-5.1.7 was released on October 13th, 2015. skiboot-5.1.7 is the 8th stable release of 5.1, it follows skiboot-5.1.6 (which was released October 8th, 2015). Skiboot 5.1.7 contains all fixes from skiboot-5.1.6 and is a minor bug fix release with one important bug fix for FSP systems. Over skiboot-5.1.6, we have the following changes: Generic: - PHB3: Retry fundamental reset This introduces another PHB3 state (PHB3_STATE_FRESET_START) allowing to redo fundamental reset if the link doesn't come up in time at the first attempt, to improve the robustness of PHB's fundamental reset. If the link comes up after the first reset, the 2nd reset won't be issued at all. FSP based systems: - hw/fsp/fsp-leds.c: use allocated buffer for FSP_CMD_GET_LED_LIST response This fixes a bug where we would overwrite roughly 4kb of memory belonging to Linux when the FSP would ask firmware for a list of LEDs in the system. This wouldn't happen often (once before Linux was running and possibly only once during runtime, and *early* runtime at that) but it was possible for this corruption to show up and be detected. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.8.txt000066400000000000000000000013201265204436200231200ustar00rootroot00000000000000skiboot-5.1.8 ------------- skiboot-5.1.8 was released on October 19th, 2015. skiboot-5.1.8 is the 9th stable release of 5.1, it follows skiboot-5.1.7 (which was released October 13th, 2015). Skiboot 5.1.8 contains all fixes from skiboot-5.1.7 and is a minor bug fix release, with a single fix for recovery from a (rare) error. Over skiboot-5.1.7, we have the following change: - opal/hmi: Fix a soft lockup issue on Hypervisor Maintenance Interrupt for certain timebase errors. We also introduce a timeout to handle the worst situation where all other threads are badly stuck without setting a cleanup done bit. Under such situation timeout will help to avoid soft lockups and report failure to kernel. skiboot-skiboot-5.1.13/doc/release-notes/skiboot-5.1.9.txt000066400000000000000000000011131265204436200231210ustar00rootroot00000000000000skiboot-5.1.9 ------------- skiboot-5.1.9 was released on October 30th, 2015. skiboot-5.1.9 is the 10th stable release of 5.1, it follows skiboot-5.1.8 (which was released October 19th, 2015). Skiboot 5.1.9 contains all fixes from skiboot-5.1.8 and is a minor bug fix release, with a single fix to help diagnosis after a rare error condition. Over skiboot-5.1.8, we have the following change: - opal/hmi: Signal PRD about NX unit checkstop. We now signal Processor Recovery & Diagnostics (PRD) correctly following an NX unit checkstop - minor fix to the boot_test.sh test script skiboot-skiboot-5.1.13/doc/versioning.txt000066400000000000000000000101441265204436200204200ustar00rootroot00000000000000Versioning Scheme of skiboot ============================ History ------- For roughly the first six months of public life, skiboot just presented a git SHA1 as a version "number". This was "user visible" in two places: 1) /sys/firmware/opal/msglog the familiar "SkiBoot 71664fd-dirty starting..." message 2) device tree: /proc/device-tree/ibm,opal/firmware/git-id Builds were also referred to by date and by corresponding PowerKVM release. Clearly, this was unlikely to be good practice going forward. As of skiboot-4.0, this scheme has changed and we now present a version string instead. This better addresses the needs of everybody who is building OpenPower systems. Current practice ---------------- The version string is constructed from a few places and is designed to be *highly* informative about what you're running. For the most part, it should be automatically constructed by the skiboot build system. The only times you need to do something is if you are a) making an upstream skiboot release or b) building firmware to release for your platform(s). OPAL/skiboot has several consumers, for example: - IBM shipping POWER8 systems with an FSP (FW810.XX and future) - OpenPower - OpenPower partners manufacturing OpenPower systems - developers, test and support needing to understand what code a system is running and there are going to be several concurrent maintained releases in the wild, likely build by different teams of people at different companies. tl;dr; is you're likely going to see version numbers like this (for the hypothetical platforms 'ketchup' and 'mustard'): skiboot-4.0-ketchup-0 skiboot-4.0-ketchup-1 skiboot-4.1-mustard-4 skiboot-4.1-ketchup-0 If you see *extra* things on the end of the version, then you're running a custom build from a developer (e.g. 'skiboot-4.0-1-g23f147e-stewart-dirty-f42fc40' means something to us - explained below). If you see less, for example 'skiboot-4.0', then you're running a build directly out of the main git tree. Those producing OPAL builds for users must *not* ship like this, even if the tree is identical. Here are the components of the version string from master: skiboot-4.0-1-g23f147e-debug-occ-stewart-dirty-f42fc40 ^ ^^^ ^ ^^^^^^^ ^-------^ ^ ^ ^^^^^^^ | | | | | | | | | | | | | \ / - 'git diff|sha1sum' | | | | | \ / | | | | | - built from a dirty tree of $USER | | | | | | | | | - $EXTRA_VERSION (optional) | | | | | | | - git SHA1 of commit built | | | | | - commits head of skiboot-4.0 tag | | | - skiboot version number ---\ | >-- from the 'skiboot-4.0' git tag - product name (always skiboot) ---/ When doing a release for a particular platform, you are expected to create and tag a branch from master. For the (hypothetical) ketchup platform which is going to do a release based on skiboot-4.0, you would create a tag 'skiboot-4.0-ketchup-0' pointing to the same revision as the 'skiboot-4.0' tag and then make any additional modifications to skiboot that were not in the 4.0 release. So, you could ship a skiboot with the following version string: skiboot-4.0-ketchup-1 ^ ^^^ ^ ^ | | | | | | | - revision for this platform | | | | | | | | - Platform name/version | | | - skiboot version number | - product name (always skiboot) This version string tells your users to expect what is in skiboot-4.0 plus some revisions for your platform. Practical Considerations ------------------------ You MUST correctly tag your git tree for sensible version numbers to be generated. Look at the (generated) version.c file to confirm you're building the correct version number. You will need annotated tags (git tag -a). If your build infrastructure does *not* build skiboot from a git tree, you should specify SKIBOOT_VERSION as an environment variable (following this versioning scheme), otherwise the build will fail. skiboot-skiboot-5.1.13/doc/xscom-node-bindings.txt000066400000000000000000000033241265204436200221060ustar00rootroot00000000000000XSCOM regions ============= The top-level xscom nodes specify the mapping range from the 64-bit address space into the PCB address space. There's one mapping range per chip xscom, therefore one node per mapping range. / /xscom@/ /xscom@/ … /xscom@/ - where is the xscom base address with the gcid-specific bits (for chip n) OR-ed in. Each xscom node has the following properties: * #address-cells = 1 * #size-cells = 1 * reg = * ibm,chip-id = gcid * compatible = "ibm,xscom", "ibm,power8-scom" / "ibm,power7-xscom" Chiplet endpoints ================= One sub-node per endpoint. Endpoints are defined by their (port, endpoint-address) data on the PCB, and are named according to their endpoint types: /xscom@/ /xscom@/chiptod@ /xscom@/lpc@ - where the is a single address (as distinct from the current (gcid,base) format), consisting of the SCOM port and SCOM endpoint bits in their 31-bit address format. Each endpoint node has the following properties: * reg = * compatible - depends on endpoint type, eg "ibm,power8-chiptod" The endpoint address specifies the address on the PCB. So, to calculate the MMIO address for a PCB register: mmio_addr = | (pcb_addr[1:27] << 4) | (pcb_addr[28:31] << 3) Where: - xscom-base-addr is the address from the first two cells of the parent node's reg property - pcb_addr is the first cell of the endpoint's reg property skiboot-skiboot-5.1.13/external/000077500000000000000000000000001265204436200165515ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/boot-tests/000077500000000000000000000000001265204436200206545ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/boot-tests/bmc_support.sh000066400000000000000000000056511265204436200235540ustar00rootroot00000000000000#Number of times to sleep BOOT_TIMEOUT="5"; #Path to memboot binary #MEMBOOT=${MEMBOOT:-memboot}; #Username/password for ssh to BMC machines SSHUSER=${SSHUSER:-sysadmin}; export SSHPASS=${SSHPASS:-superuser}; #Username/password for IPMI IPMI_AUTH="-U ${IPMI_USER:-admin} -P ${IPMI_PASS:-admin}" PFLASH_TO_COPY=${PFLASH_TO_COPY:-} PFLASH_BINARY=/usr/local/bin/pflash # Strip control characters from IPMI before grepping? STRIP_CONTROL=0 # How do we SSH/SCP in? SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target"; # remotecp file target target_location function remotecp { sshpass -e ssh -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 dd of=$3 < $1; } function is_off { return $([ "$($IPMI_COMMAND chassis power status)" = "Chassis Power is off" ]); } function poweroff { $IPMI_COMMAND chassis power off # give it some time sleep 10 } function flash { if [ ! -z "$PFLASH_TO_COPY" ]; then remotecp $PFLASH_TO_COPY $target /tmp/pflash $SSHCMD chmod +x /tmp/pflash PFLASH_BINARY=/tmp/pflash fi if [ ! -z "$PNOR" ]; then remotecp $PNOR $target /tmp/image.pnor; fi if [ "${LID[0]}" != "" ]; then remotecp ${LID[0]} $target /tmp/skiboot.lid; fi if [ "${LID[1]}" != "" ]; then remotecp ${LID[1]} $target /tmp/bootkernel fi if [ "$?" -ne "0" ] ; then error "Couldn't copy firmware image"; fi # Habenaro doesn't have md5sum #flash_md5=$(md5sum "$1" | cut -f 1 -d ' '); #$SSHCMD "flash_md5r=\$(md5sum /tmp/image.pnor | cut -f 1 -d ' '); # if [ \"$flash_md5\" != \"\$flash_md5r\" ] ; then # exit 1; # fi"; #if [ "$?" -ne "0" ] ; then # error "Firmware MD5s don't match"; #fi # flash it if [ ! -z "$PNOR" ]; then msg "Flashing full PNOR" $SSHCMD "$PFLASH_BINARY -E -f -p /tmp/image.pnor" if [ "$?" -ne "0" ] ; then error "An unexpected pflash error has occured"; fi fi if [ ! -z "${LID[0]}" ] ; then msg "Flashing PAYLOAD PNOR partition" $SSHCMD "$PFLASH_BINARY -e -f -P PAYLOAD -p /tmp/skiboot.lid" if [ "$?" -ne "0" ] ; then error "An unexpected pflash error has occured"; fi fi if [ ! -z "${LID[1]}" ] ; then msg "Flashing BOOTKERNEL PNOR partition" $SSHCMD "$PFLASH_BINARY -e -f -P BOOTKERNEL -p /tmp/bootkernel" if [ "$?" -ne "0" ] ; then error "An unexpected pflash error has occured"; fi fi } function boot_firmware { $IPMI_COMMAND chassis power on > /dev/null; i=0; while [ "$($IPMI_COMMAND chassis power status)" = "Chassis Power is off" -a \( "$i" -lt "$BOOT_TIMEOUT" \) ] ; do msg -n "."; sleep $BOOT_SLEEP_PERIOD; i=$(expr $i + 1); done if [ "$i" -eq "$BOOT_TIMEOUT" ] ; then error "Couldn't power on $target"; fi } function machine_sanity_test { # No further sanity tests for BMC machines. true } skiboot-skiboot-5.1.13/external/boot-tests/boot_test.sh000077500000000000000000000130251265204436200232160ustar00rootroot00000000000000#!/bin/bash # Lets try for /bin/sh but bashisms will sneak in. # partial bash strict mode set -uo pipefail V=0; target="" if [ -f ~/.skiboot_boot_tests ]; then source ~/.skiboot_boot_tests fi # Utility functions function error { unset SSHPASS if [ ! -z "$target" ]; then echo "$target: $*" >&2 else echo "$0: $*" >&2 fi exit 1 } function msg { if [ $V -ne 0 ]; then if [ ! -z "$target" ]; then echo "$target: $*" else echo "$0: $*" fi fi } # Generic conf BOOT_SLEEP_PERIOD=10 FUNCTIONS_NEEDED="sshpass ssh ipmitool md5sum rsync expect"; function linux_boot { if [ $STRIP_CONTROL -eq 1 ]; then STRIPCOMMAND="col -b -l 1" else STRIPCOMMAND="cat" fi #Everyone is going to forget to disconnect - force them off ipmiresult=$($IPMI_COMMAND sol deactivate 2>&1); retval=$? if [ $retval -ne 0 -a "$ipmiresult" != "Info: SOL payload already de-activated" ]; then msg "IPMI sol deactivate failed; IPMI may have stalled, may just be IPMI. Good luck." fi LINUXBOOT_LOG=$(mktemp --tmpdir builder-2.XXXXXX); cat < $LINUXBOOT_LOG set timeout 300 spawn $IPMI_COMMAND sol activate expect { timeout { send_user "\nTimeout waiting for petitboot\n"; exit 1 } eof { send_user "\nUnexpected EOF\n;" exit 1 } "Welcome to Petitboot" } close exit 0 EOF retval=$? $IPMI_COMMAND sol deactivate > /dev/null; if [ $retval -ne 0 ]; then msg "Waiting for linux has timed out" msg "Boot log follows:" cat $LINUXBOOT_LOG rm -f $LINUXBOOT_LOG return 1 else rm -f $LINUXBOOT_LOG return 0 fi } function boot_test { # The functions called (e.g. flash, boot) are from the *_support files if [ $bootonly -ne 1 ]; then msg "Flashing ${target}..." flash $@; fi msg "Booting $target..." boot_firmware; msg "firmware looks good, waiting for linux"; linux_boot; if [ $? -ne 0 ] ; then error "Couldn't reach petitboot on $target"; fi msg "$target has booted"; unset SSHPASS; } function sanity_test { $SSHCMD true; if [ $? -ne 0 ]; then echo "$target: Failed to SSH to $target..." echo "$target: Command was: $SSHCMD true" error "Try connecting manually to diagnose the issue." fi $IPMI_COMMAND chassis power status > /dev/null; if [ $? -ne 0 ]; then echo "$target: Failed to connect to $target with IPMI..." echo "$target: Command was: $IPMI_COMMAND chassis power status" error "Try connecting manually to diagnose the issue." fi # do further machine-type specific tests machine_sanity_test } function usage { cat < /dev/null ; then error "I require command $func but it is not in \$PATH ($PATH)"; fi done # Parse options V=0; bootonly=0; powerdown=0; firmware_supplied=0; target="" method="" PNOR="" LID[0]="" LID[1]="" LID[2]="" while getopts "hvdpB1:2:3:P:t:b:" OPT; do case "$OPT" in v) V=1; ;; h) usage; ;; d) set -vx; ;; B) bootonly=1; if [ $firmware_supplied -eq 1 ]; then usage fi ;; p) powerdown=1; ;; b) method=$OPTARG; ;; 1|2|3) firmware_supplied=1; if [ ! -e "$OPTARG" ] ; then error "Couldn't stat $OPTARG"; fi LID[$(expr ${OPT} - 1)]="$OPTARG" ;; P) firmware_supplied=1; if [ ! -e "$OPTARG" ] ; then error "Couldn't stat $OPTARG"; fi PNOR="$OPTARG" ;; t) target=$OPTARG; ;; \?) usage; ;; esac done shift $(expr $OPTIND - 1); # Pull out the target and test if [ "$target" = "" ]; then usage; fi if ! ping -c 1 "$target" &> /dev/null ; then error "Couldn't ping $target"; fi if [ "$#" -ne 0 ]; then usage fi # pull in the relevant config file and set things up source $(dirname $0)/${method}_support.sh IPMI_COMMAND="ipmitool -I lanplus -H $target $IPMI_AUTH" msg "Running sanity test" sanity_test msg "Passed." # check the target is down # (pulls in is_off from ${method}_support.sh) if ! is_off; then if [ $powerdown -eq 1 ]; then poweroff else error "$target is not turned off"; fi fi # run the boot test echo "$target: Boot testing $target"; begin_t=$(date +%s); boot_test echo "$target: Done in $(expr $(date +%s) - $begin_t ) seconds"; skiboot-skiboot-5.1.13/external/boot-tests/extract_gcov.sh000077500000000000000000000017301265204436200237040ustar00rootroot00000000000000#!/bin/bash if [ "$SKIBOOT_GCOV" != 1 ]; then echo "Skipping GCOV test on physical hardware. Enable with SKIBOOT_GCOV=1" exit 0; fi if [ ! -f ~/.skiboot_boot_tests ]; then if [ -z $FSPSSHUSER ] || [ -z $FSPSSHPASS ] ; then echo "Skipping extract gcov due to missing ~/.skiboot_boot_tests" echo "Set FSPSSHUSER and FSPSSHPASS in ~/.skiboot_boot_tests" exit 0; fi fi source ~/.skiboot_boot_tests target=$1 SSHUSER=$FSPSSHUSER SSHPASS=$FSPSSHPASS export SSHUSER SSHPASS SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target"; REMOTECPCMD="sshpass -e scp -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "; $SSHCMD rm -f skiboot-$target.dump echo "Dumping skiboot memory from host: $target... (takes time)" $SSHCMD sh --login -c \"getmemproc 30000000 3145728 -fb skiboot-$target.dump\" $REMOTECPCMD $target:skiboot-$target.dump skiboot-$target.dump skiboot-skiboot-5.1.13/external/boot-tests/fsp_support.sh000066400000000000000000000112671265204436200236030ustar00rootroot00000000000000#Number of times to sleep BOOT_TIMEOUT="20"; #Username/password for for ssh to FSP machines SSHUSER=${FSPSSHUSER:-} SSHPASS=${FSPSSHPASS:-} if [ -z $SSHUSER ] || [ -z $SSHPASS ] ; then msg "Set FSPSSHUSER and FSPSSHPASS in ENV or ~/.skiboot_boot_tests" exit 1; fi export SSHUSER SSHPASS #IPMI IPMI_AUTH="-P ${IPMI_PASS:-foo}"; # Strip control characters from IPMI before grepping? STRIP_CONTROL=1 # How do we SSH in, cp files across? SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target"; REMOTECPCMD="sshpass -e scp -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "; GET_PROFILE='. /etc/profile; test -e /home/dev/.profile && . /home/dev/.profile'; function is_off { state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); return $([ "$state" = "standby" ]); } function poweroff { i=0; state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); if [ "$state" = "standby" ]; then # already off return 0 fi $SSHCMD "$GET_PROFILE; panlexec -f 8"; msg "Waiting 30 seconds..." sleep 30 state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); while [ "$state" != "standby" -a "$i" -lt "$BOOT_TIMEOUT" ] ; do msg "Waiting $BOOT_SLEEP_PERIOD more seconds..." sleep $BOOT_SLEEP_PERIOD; i=$(expr $i + 1); state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); done; # sleep a little bit longer --- p81 was getting a bit confused. sleep 10 msg "Finishing with state '$state'." } function flash { #Make a backup of the current lids $REMOTECPCMD $target:/opt/extucode/80f00100.lid 80f00100.lid.bak && $REMOTECPCMD $target:/opt/extucode/80f00101.lid 80f00101.lid.bak && $REMOTECPCMD $target:/opt/extucode/80f00102.lid 80f00102.lid.bak; if [ $? -ne 0 ] ; then error "Couldn't make backup of currently installed lids"; fi if [ "${LID[0]}" != "" ]; then $REMOTECPCMD ${LID[0]} $target:/opt/extucode/80f00100.lid || error "Error copying lid ${LID[0]}"; sum=$(md5sum ${LID[0]} | cut -f 1 -d ' '); $SSHCMD "$GET_PROFILE; sumr=\$(md5sum /opt/extucode/80f00100.lid | cut -f 1 -d ' '); if [ \"$sum\" != \"\$sumr\" ] ; then exit 1; fi;" || error "MD5sum doesn't match for ${LID[0]}" fi if [ "${LID[1]}" != "" ]; then $REMOTECPCMD ${LID[1]} $target:/opt/extucode/80f00101.lid || error "Error copying lid"; sum=$(md5sum ${LID[1]} | cut -f 1 -d ' '); $SSHCMD "$GET_PROFILE; sumr=\$(md5sum /opt/extucode/80f00101.lid | cut -f 1 -d ' '); if [ \"$sum\" != \"\$sumr\" ] ; then exit 1; fi;" || error "MD5sum doesn't match for ${LID[1]}" fi if [ "${LID[2]}" != "" ]; then $REMOTECPCMD ${LID[2]} $target:/opt/extucode/80f00102.lid || error "Error copying lid"; sum=$(md5sum ${LID[2]} | cut -f 1 -d ' '); $SSHCMD "$GET_PROFILE; sumr=\$(md5sum /opt/extucode/80f00102.lid | cut -f 1 -d ' '); if [ \"$sum\" != \"\$sumr\" ] ; then exit 1; fi;" || error "MD5sum doesn't match for ${LID[2]}" fi $SSHCMD "$GET_PROFILE; if [ \$(smgr mfgState) != 'standby' ] ; then exit 1; fi cupdmfg -opt | grep '80f0010'"; if [ $? -ne 0 ] ; then error "Could not install lids on the FSP"; fi sleep 2; #Don't rush the fsp } function boot_firmware { ISTEP_LOG=$(mktemp --tmpdir builder-1.XXXXXX); $SSHCMD "$GET_PROFILE; istep" &> $ISTEP_LOG & msg "Waiting 90 seconds for $target to boot"; sleep 90; i=0; state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); while [ \( "$state" != "runtime" \) -a \( "$i" -lt "$BOOT_TIMEOUT" \) ] ; do msg "Waiting $BOOT_SLEEP_PERIOD more seconds (istep: `grep iStep $ISTEP_LOG|tail -n 1`)"; sleep "$BOOT_SLEEP_PERIOD"; i=$(expr $i + 1); state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); done; if [ "$i" -eq "$BOOT_TIMEOUT" ] ; then state=$($SSHCMD "$GET_PROFILE; smgr mfgState"); case "$state" in "ipling") echo "$target: still hasn't come up but firmware hasn't specifically crashed"; ;; "dumping") echo "$target: has crashed"; ;; "runtime") echo "$target: Oops, looks like system has managed to come up..."; ;; "standby") echo "$target: System is powered off? How can this be?"; ;; *) echo "$target: is an unknown state '$state'"; ;; esac echo "$target: istep log"; cat $ISTEP_LOG; rm -rf $ISTEP_LOG error "Boot test on $target failed"; fi rm -rf $ISTEP_LOG; } function machine_sanity_test { $SSHCMD "$GET_PROFILE; test -d /nfs/bin" if [ $? -ne 0 ]; then echo "$target: Failed to read /nfs/bin" error "Is /nfs mounted on the FSP?" fi $SSHCMD "$GET_PROFILE; which md5sum > /dev/null && which cupdmfg > /dev/null" if [ $? -ne 0 ]; then echo "$target: Missing md5sum or cupdmfg on the FSP?" error "Is /nfs mounted on the FSP?" fi } skiboot-skiboot-5.1.13/external/common/000077500000000000000000000000001265204436200200415ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/common/arch_flash.h000066400000000000000000000026031265204436200223050ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __EXTERNAL_ARCH_FLASH_H #define __EXTERNAL_ARCH_FLASH_H #include #include int arch_flash_init(struct blocklevel_device **bl, const char *file); void arch_flash_close(struct blocklevel_device *bl, const char *file); /* Low level functions that an architecture may support */ /* * If called BEFORE init, then the behaviour is to set that on init the BMC * flash will be opened. * If called AFTER init, then the behaviour is to return wether or not BMC * flash has been opened */ int arch_flash_bmc(struct blocklevel_device *bl, int bmc); int arch_flash_erase_chip(struct blocklevel_device *bl); int arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b); int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set); #endif /* __EXTERNAL_ARCH_FLASH_H */ skiboot-skiboot-5.1.13/external/common/arch_flash_arm.c000066400000000000000000000152461265204436200231460ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ast.h" #include "arch_flash.h" struct flash_chip; static struct arch_arm_data { int fd; void *ahb_reg_map; void *gpio_ctrl; size_t ahb_flash_base; size_t ahb_flash_size; void *ahb_flash_map; int bmc; struct flash_chip *flash_chip; struct blocklevel_device *init_bl; } arch_data; uint32_t ast_ahb_readl(uint32_t offset) { assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0); return readl(arch_data.ahb_reg_map + (offset - AHB_REGS_BASE)); } void ast_ahb_writel(uint32_t val, uint32_t offset) { assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0); writel(val, arch_data.ahb_reg_map + (offset - AHB_REGS_BASE)); } int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len) { if (reg < arch_data.ahb_flash_base || (reg + len) > (arch_data.ahb_flash_base + arch_data.ahb_flash_size)) return -1; reg -= arch_data.ahb_flash_base; if (((reg | (unsigned long)src | len) & 3) == 0) { while(len > 3) { uint32_t val = *(uint32_t *)src; writel(val, arch_data.ahb_flash_map + reg); src += 4; reg += 4; len -= 4; } } while(len--) { uint8_t val = *(uint8_t *)src; writeb(val, arch_data.ahb_flash_map + reg++); src += 1; } return 0; } /* * GPIO stuff to be replaced by higher level accessors for * controlling the flash write lock via sysfs */ static inline uint32_t gpio_ctl_readl(uint32_t offset) { return readl(arch_data.gpio_ctrl + offset); } static inline void gpio_ctl_writel(uint32_t val, uint32_t offset) { writel(val, arch_data.gpio_ctrl + offset); } static bool set_wrprotect(bool protect) { uint32_t reg; bool was_protected; reg = gpio_ctl_readl(0x20); was_protected = !!(reg & 0x00004000); if (protect) reg |= 0x00004000; /* GPIOF[6] value */ else reg &= ~0x00004000; /* GPIOF[6] value */ gpio_ctl_writel(reg, 0x20); reg = gpio_ctl_readl(0x24); reg |= 0x00004000; /* GPIOF[6] direction */ gpio_ctl_writel(reg, 0x24); return was_protected; } int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len) { if (reg < arch_data.ahb_flash_base || (reg + len) > (arch_data.ahb_flash_base + arch_data.ahb_flash_size)) return -1; reg -= arch_data.ahb_flash_base; if (((reg | (unsigned long)dst | len) & 3) == 0) { while(len > 3) { *(uint32_t *)dst = readl(arch_data.ahb_flash_map + reg); dst += 4; reg += 4; len -= 4; } } while(len--) { *(uint8_t *)dst = readb(arch_data.ahb_flash_map + reg++); dst += 1; } return 0; } static void close_devs(void) { /* * Old code doesn't do this, not sure why not * * munmap(arch_data.ahb_flash_map, arch_data.ahb_flash_size); * munmap(arch_data.gpio_ctrl, GPIO_CTRL_SIZE); * munmap(arch_data.ahb_reg_map, AHB_REGS_SIZE); * close(arch_data.fd); */ } static int open_devs(int bmc) { arch_data.fd = open("/dev/mem", O_RDWR | O_SYNC); if (arch_data.fd < 0) { perror("can't open /dev/mem"); return -1; } arch_data.ahb_reg_map = mmap(0, AHB_REGS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, arch_data.fd, AHB_REGS_BASE); if (arch_data.ahb_reg_map == MAP_FAILED) { perror("can't map AHB registers /dev/mem"); return -1; } arch_data.gpio_ctrl = mmap(0, GPIO_CTRL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, arch_data.fd, GPIO_CTRL_BASE); if (arch_data.gpio_ctrl == MAP_FAILED) { perror("can't map GPIO control via /dev/mem"); return -1; } arch_data.ahb_flash_base = bmc ? BMC_FLASH_BASE : PNOR_FLASH_BASE; arch_data.ahb_flash_size = bmc ? BMC_FLASH_SIZE : PNOR_FLASH_SIZE; arch_data.ahb_flash_map = mmap(0, arch_data.ahb_flash_size, PROT_READ | PROT_WRITE, MAP_SHARED, arch_data.fd, arch_data.ahb_flash_base); if (arch_data.ahb_flash_map == MAP_FAILED) { perror("can't map flash via /dev/mem"); return -1; } return 0; } static struct blocklevel_device *flash_setup(int bmc) { int rc; struct blocklevel_device *bl; struct spi_flash_ctrl *fl; /* Open and map devices */ open_devs(bmc); /* Create the AST flash controller */ rc = ast_sf_open(bmc ? AST_SF_TYPE_BMC : AST_SF_TYPE_PNOR, &fl); if (rc) { fprintf(stderr, "Failed to open controller\n"); return NULL; } /* Open flash chip */ rc = flash_init(fl, &bl, &arch_data.flash_chip); if (rc) { fprintf(stderr, "Failed to open flash chip\n"); return NULL; } return bl; } int arch_flash_bmc(struct blocklevel_device *bl, int bmc) { if (!arch_data.init_bl) { arch_data.bmc = bmc; return 0; } /* Called with a BL not inited here, bail */ if (arch_data.init_bl != bl) return -1; return arch_data.flash_chip ? arch_data.bmc : -1; } int arch_flash_erase_chip(struct blocklevel_device *bl) { /* Called with a BL not inited here, bail */ if (!arch_data.init_bl || arch_data.init_bl != bl) return -1; if (!arch_data.flash_chip) return -1; return flash_erase_chip(arch_data.flash_chip); } int arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b) { /* Called with a BL not inited here, bail */ if (!arch_data.init_bl || arch_data.init_bl != bl) return -1; if (!arch_data.flash_chip) return -1; return flash_force_4b_mode(arch_data.flash_chip, set_4b); } int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set) { /* Called with a BL not inited here, bail */ if (!arch_data.init_bl || arch_data.init_bl != bl) return -1; if (!arch_data.flash_chip) return -1; return set_wrprotect(set); } int arch_flash_init(struct blocklevel_device **r_bl, const char *file) { struct blocklevel_device *new_bl; /* Check we haven't already inited */ if (arch_data.init_bl) return -1; if (file) { file_init_path(file, NULL, &new_bl); } else { new_bl = flash_setup(arch_data.bmc); } if (!new_bl) return -1; arch_data.init_bl = new_bl; *r_bl = new_bl; return 0; } void arch_flash_close(struct blocklevel_device *bl, const char *file) { if (file) { file_exit_close(bl); } else { flash_exit_close(bl, &ast_sf_close); close_devs(); } } skiboot-skiboot-5.1.13/external/common/arch_flash_arm_io.h000066400000000000000000000033671265204436200236430ustar00rootroot00000000000000#ifndef __IO_H #define __IO_H #include #include #include #include /* AST AHB register base */ #define AHB_REGS_BASE 0x1E600000 #define AHB_REGS_SIZE 0x00200000 /* AST GPIO control regs */ #define GPIO_CTRL_BASE 0x1E780000 #define GPIO_CTRL_SIZE 0x1000 /* AST AHB mapping of PNOR */ #define PNOR_FLASH_BASE 0x30000000 #define PNOR_FLASH_SIZE 0x04000000 /* AST AHB mapping of BMC flash */ #define BMC_FLASH_BASE 0x20000000 #define BMC_FLASH_SIZE 0x04000000 /* Address of flash mapping on LPC FW space */ #define LPC_FLASH_BASE 0x0e000000 #define LPC_CTRL_BASE 0x1e789000 static inline uint8_t readb(void *addr) { asm volatile("" : : : "memory"); return *(volatile uint8_t *)addr; } static inline uint16_t readw(void *addr) { asm volatile("" : : : "memory"); return *(volatile uint16_t *)addr; } static inline uint32_t readl(void *addr) { asm volatile("" : : : "memory"); return *(volatile uint32_t *)addr; } static inline void writeb(uint8_t val, void *addr) { asm volatile("" : : : "memory"); *(volatile uint8_t *)addr = val; } static inline void writew(uint16_t val, void *addr) { asm volatile("" : : : "memory"); *(volatile uint16_t *)addr = val; } static inline void writel(uint32_t val, void *addr) { asm volatile("" : : : "memory"); *(volatile uint32_t *)addr = val; } /* * AHB register and flash access */ extern uint32_t ast_ahb_readl(uint32_t offset); extern void ast_ahb_writel(uint32_t val, uint32_t offset); extern int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len); extern int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len); static inline void check_platform(bool *has_sfc, bool *has_ast) { *has_sfc = false; *has_ast = true; } #endif /* __IO_H */ skiboot-skiboot-5.1.13/external/common/arch_flash_common.c000066400000000000000000000020401265204436200236430ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include /* Default implmentations */ int __attribute__((weak)) arch_flash_erase_chip(struct blocklevel_device *bl) { return -1; } int __attribute__((weak)) arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b) { return -1; } int __attribute__((weak)) arch_flash_bmc(struct blocklevel_device *bl, int bmc) { return -1; } int __attribute__((weak)) arch_flash_set_wrprotect(struct blocklevel_device *bl, int set) { return -1; } skiboot-skiboot-5.1.13/external/common/arch_flash_powerpc.c000066400000000000000000000122571265204436200240450ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "arch_flash.h" #define FDT_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash" #define SYSFS_MTD_PATH "/sys/class/mtd" static inline void hint_root(void) { fprintf(stderr, "Do you have permission? Are you root?\n"); } static int get_dev_attr(const char *dev, const char *attr_file, uint32_t *attr) { char *dev_path = NULL; int fd, rc; /* * Needs to be large enough to hold at most uint32_t represented as a * string in hex with leading 0x */ char attr_buf[10]; rc = asprintf(&dev_path, "%s/%s/%s", SYSFS_MTD_PATH, dev, attr_file); if (rc < 0) { dev_path = NULL; goto out; } fd = open(dev_path, O_RDONLY); if (fd == -1) goto out; rc = read(fd, attr_buf, sizeof(attr_buf)); close(fd); if (rc == -1) goto out; if (attr) *attr = strtol(attr_buf, NULL, 0); free(dev_path); return 0; out: free(dev_path); fprintf(stderr, "Couldn't get MTD attribute '%s' from '%s'\n", dev, attr_file); return -1; } static int get_dev_mtd(const char *fdt_flash_path, char **mtd_path) { struct dirent **namelist; char fdt_node_path[PATH_MAX]; int count, i, rc, fd; bool done; if (!fdt_flash_path) return -1; fd = open(fdt_flash_path, O_RDONLY); if (fd == -1) { fprintf(stderr, "Couldn't open '%s' FDT attribute to determine which flash device to use\n", fdt_flash_path); fprintf(stderr, "Is your skiboot new enough to expose the flash through the device tree?\n"); hint_root(); return -1; } rc = read(fd, fdt_node_path, sizeof(fdt_node_path)); close(fd); if (rc == -1) { fprintf(stderr, "Couldn't read flash FDT node from '%s'\n", fdt_flash_path); hint_root(); return -1; } count = scandir(SYSFS_MTD_PATH, &namelist, NULL, alphasort); if (count == -1) { fprintf(stderr, "Couldn't scan '%s' for MTD\n", SYSFS_MTD_PATH); hint_root(); return -1; } rc = 0; done = false; for (i = 0; i < count; i++) { struct dirent *dirent; char *dev_path; char fdt_node_path_tmp[PATH_MAX]; dirent = namelist[i]; /* * The call to asprintf must happen last as when it succeeds it * will allocate dev_path */ if (dirent->d_name[0] == '.' || rc || done || asprintf(&dev_path, "%s/%s/device/of_node", SYSFS_MTD_PATH, dirent->d_name) < 0) { free(namelist[i]); continue; } rc = readlink(dev_path, fdt_node_path_tmp, sizeof(fdt_node_path_tmp) - 1); free(dev_path); if (rc == -1) { /* * This might fail because it could not exist if the system has flash * devices that present as mtd but don't have corresponding FDT * nodes, just continue silently. */ free(namelist[i]); /* Should still try the next dir so reset rc */ rc = 0; continue; } fdt_node_path_tmp[rc] = '\0'; if (strstr(fdt_node_path_tmp, fdt_node_path)) { uint32_t flags, size; /* * size and flags could perhaps have be gotten another way but this * method is super unlikely to fail so it will do. */ /* Check to see if device is writeable */ rc = get_dev_attr(dirent->d_name, "flags", &flags); if (rc) { free(namelist[i]); continue; } /* Get the size of the mtd device while we're at it */ rc = get_dev_attr(dirent->d_name, "size", &size); if (rc) { free(namelist[i]); continue; } rc = asprintf(&dev_path, "/dev/%s", dirent->d_name); if (rc < 0) { free(namelist[i]); continue; } rc = 0; *mtd_path = dev_path; done = true; } free(namelist[i]); } free(namelist); if (!done) { fprintf(stderr, "Couldn't find '%s' corresponding MTD\n", fdt_flash_path); fprintf(stderr, "Is your kernel new enough to expose MTD?\n"); } /* explicit negative value so as to not return a libflash code */ return done ? rc : -1; } static struct blocklevel_device *arch_init_blocklevel(const char *file) { int rc; struct blocklevel_device *new_bl = NULL; char *real_file; if (!file) { rc = get_dev_mtd(FDT_FLASH_PATH, &real_file); if (rc) return NULL; } file_init_path(file ? file : real_file, NULL, &new_bl); return new_bl; } /* Skiboot will worry about this for us */ int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set) { return 0; } int arch_flash_init(struct blocklevel_device **r_bl, const char *file) { struct blocklevel_device *new_bl; new_bl = arch_init_blocklevel(file); if (!new_bl) return -1; *r_bl = new_bl; return 0; } void arch_flash_close(struct blocklevel_device *bl, const char *file) { file_exit_close(bl); } skiboot-skiboot-5.1.13/external/common/arch_flash_powerpc_io.h000066400000000000000000000000001265204436200245200ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/common/arch_flash_x86.c000066400000000000000000000023711265204436200230070ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "arch_flash.h" int arch_flash_init(struct blocklevel_device **r_bl, const char *file) { struct blocklevel_device *new_bl; /* Must have passed through a file to operate on */ if (!file) { fprintf(stderr, "Cannot operate without a file\n"); return -1; } file_init_path(file, NULL, &new_bl); if (!new_bl) return -1; *r_bl = new_bl; return 0; } void arch_flash_close(struct blocklevel_device *bl, const char *file) { file_exit_close(bl); } skiboot-skiboot-5.1.13/external/common/arch_flash_x86_io.h000066400000000000000000000000001265204436200234660ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/common/get_arch.sh000077500000000000000000000003271265204436200221560ustar00rootroot00000000000000#!/bin/sh echo "#if defined(__powerpc__) echo -n ARCH_POWERPC #elif defined(__x86_64__) || defined(__i386__) echo -n ARCH_X86 #elif defined(__arm__) echo -n ARCH_ARM #else echo -n ARCH_UNKNOWN #endif" | $1cpp | sh skiboot-skiboot-5.1.13/external/common/rules.mk000066400000000000000000000016011265204436200215220ustar00rootroot00000000000000ARCH = $(shell $(GET_ARCH) "$(CROSS_COMPILE)") ifeq ($(ARCH),ARCH_ARM) arch = arm ARCH_OBJS = common/arch_flash_common.o common/arch_flash_arm.o common/ast-sf-ctrl.o else ifeq ($(ARCH),ARCH_POWERPC) arch = powerpc ARCH_OBJS = common/arch_flash_common.o common/arch_flash_powerpc.o else ifeq ($(ARCH),ARCH_X86) arch = x86 ARCH_OBJS = common/arch_flash_common.o common/arch_flash_x86.o else $(error Unsupported architecture $(ARCH)) endif endif endif .PHONY: arch_links arch_links: ln -sf ../../hw/ast-bmc/ast-sf-ctrl.c common/ast-sf-ctrl.c ln -sf arch_flash_$(arch)_io.h common/io.h .PHONY: arch_clean arch_clean: rm -rf $(ARCH_OBJS) #If arch changes make won't realise it needs to rebuild... .PHONY: .FORCE common/arch_flash_common.o: common/arch_flash_common.c .FORCE $(CROSS_COMPILE)gcc $(CFLAGS) -c $< -o $@ common/arch_flash.o: $(ARCH_OBJS) $(CROSS_COMPILE)ld -r $(ARCH_OBJS) -o $@ skiboot-skiboot-5.1.13/external/gard/000077500000000000000000000000001265204436200174665ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/gard/.gitignore000066400000000000000000000000151265204436200214520ustar00rootroot00000000000000*.o *.d gard skiboot-skiboot-5.1.13/external/gard/Makefile000066400000000000000000000012501265204436200211240ustar00rootroot00000000000000# This tool is a linux userland tool and should be completely stand alone prefix = /usr/local/ sbindir = $(prefix)/sbin datadir = $(prefix)/share mandir = $(datadir)/man CC = $(CROSS_COMPILE)gcc CFLAGS += -m64 -Werror -Wall -g2 -ggdb LDFLAGS += -m64 ASFLAGS = -m64 CPPFLAGS += -I. -I../../ OBJS = file.o gard.o libflash.o libffs.o ecc.o blocklevel.o EXE = gard all: $(EXE) %.o: %.c $(COMPILE.c) $< -o $@ %.o: ../../libflash/%.c $(COMPILE.c) $< -o $@ $(EXE): $(OBJS) $(LINK.o) -o $@ $^ install: all install -D gard $(DESTDIR)$(sbindir)/opal-gard install -D -m 0644 opal-gard.1 $(DESTDIR)$(mandir)/man1/opal-gard.1 clean: rm -f $(OBJS) $(EXE) *.d distclean: clean skiboot-skiboot-5.1.13/external/gard/config.h000066400000000000000000000005531265204436200211070ustar00rootroot00000000000000/* For CCAN */ #include #include #define HAVE_TYPEOF 1 #define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 #if __BYTE_ORDER == __LITTLE_ENDIAN #define HAVE_BIG_ENDIAN 0 #define HAVE_LITTLE_ENDIAN 1 #else #define HAVE_BIG_ENDIAN 1 #define HAVE_LITTLE_ENDIAN 0 #endif #define HAVE_BYTESWAP_H 1 #define HAVE_BSWAP_64 1 skiboot-skiboot-5.1.13/external/gard/gard.c000066400000000000000000000474631265204436200205650ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gard.h" #define CLEARED_RECORD_ID 0xFFFFFFFF #define FDT_ACTIVE_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash" #define SYSFS_MTD_PATH "/sys/class/mtd/" #define FLASH_GARD_PART "GUARD" struct gard_ctx { bool ecc; uint32_t f_size; uint32_t f_pos; uint32_t gard_part_idx; uint32_t gard_data_pos; uint32_t gard_data_len; struct blocklevel_device *bl; struct ffs_handle *ffs; }; /* * Return the size of a struct gard_ctx depending on if the buffer contains * ECC bits */ static inline size_t sizeof_gard(struct gard_ctx *ctx) { return ctx->ecc ? ecc_buffer_size(sizeof(struct gard_record)) : sizeof(struct gard_record); } static void show_flash_err(int rc) { switch (rc) { case FFS_ERR_BAD_MAGIC: fprintf(stderr, "libffs bad magic\n"); break; case FFS_ERR_BAD_VERSION: fprintf(stderr, "libffs bad version\n"); break; case FFS_ERR_BAD_CKSUM: fprintf(stderr, "libffs bad check sum\n"); break; case FFS_ERR_PART_NOT_FOUND: fprintf(stderr, "libffs flash partition not found\n"); break; /* ------- */ case FLASH_ERR_MALLOC_FAILED: fprintf(stderr, "libflash malloc failed\n"); break; case FLASH_ERR_CHIP_UNKNOWN: fprintf(stderr, "libflash unknown flash chip\n"); break; case FLASH_ERR_PARM_ERROR: fprintf(stderr, "libflash parameter error\n"); break; case FLASH_ERR_ERASE_BOUNDARY: fprintf(stderr, "libflash erase boundary error\n"); break; case FLASH_ERR_WREN_TIMEOUT: fprintf(stderr, "libflash WREN timeout\n"); break; case FLASH_ERR_WIP_TIMEOUT: fprintf(stderr, "libflash WIP timeout\n"); break; case FLASH_ERR_VERIFY_FAILURE: fprintf(stderr, "libflash verification failure\n"); break; case FLASH_ERR_4B_NOT_SUPPORTED: fprintf(stderr, "libflash 4byte mode not supported\n"); break; case FLASH_ERR_CTRL_CONFIG_MISMATCH: fprintf(stderr, "libflash control config mismatch\n"); break; case FLASH_ERR_CHIP_ER_NOT_SUPPORTED: fprintf(stderr, "libflash chip not supported\n"); break; case FLASH_ERR_CTRL_CMD_UNSUPPORTED: fprintf(stderr, "libflash unsupported control command\n"); break; case FLASH_ERR_CTRL_TIMEOUT: fprintf(stderr, "libflash control timeout\n"); break; case FLASH_ERR_ECC_INVALID: fprintf(stderr, "libflash ecc invalid\n"); break; default: fprintf(stderr, "A libflash/libffs error has occured %d\n", rc); } } static const char *target_type_to_str(enum target_type t) { switch (t) { case TYPE_NA: return "Not applicable"; case TYPE_SYS: return "System"; case TYPE_NODE: return "Node"; case TYPE_DIMM: return "Dimm"; case TYPE_MEMBUF: return "Memory Buffer"; case TYPE_PROC: return "Processor"; case TYPE_EX: return "EX"; case TYPE_CORE: return "Core"; case TYPE_L2: return "L2 cache"; case TYPE_L3: return "L3 cache"; case TYPE_L4: return "L4 cache"; case TYPE_MCS: return "MSC"; case TYPE_MBA: return "MBA"; case TYPE_XBUS: return "XBUS"; case TYPE_ABUS: return "ABUS"; case TYPE_PCI: return "PCI"; case TYPE_DPSS: return "DPSS"; case TYPE_APSS: return "APSS"; case TYPE_OCC: return "OCC"; case TYPE_PSI: return "PSI"; case TYPE_FSP: return "FSP"; case TYPE_PNOR: return "PNOR"; case TYPE_OSC: return "OSC"; case TYPE_TODCLK: return "Time of day clock"; case TYPE_CONTROL_NODE: return "Control Node"; case TYPE_OSCREFCLK: return "OSC Ref Clock"; case TYPE_OSCPCICLK: return "OSC PCI Clock"; case TYPE_REFCLKENDPT: return "Ref Clock"; case TYPE_PCICLKENDPT: return "PCI Clock"; case TYPE_NX: return "NX"; case TYPE_PORE: return "PORE"; case TYPE_PCIESWITCH: return "PCIE Switch"; case TYPE_CAPP: return "CAPP"; case TYPE_FSI: return "FSI"; case TYPE_TEST_FAIL: return "Test Fail"; case TYPE_LAST_IN_RANGE: return "Last"; } return "Unknown"; } static const char *path_type_to_str(enum path_type t) { switch (t) { case PATH_NA: return "not applicable"; case PATH_AFFINITY: return "affinity"; case PATH_PHYSICAL: return "physical"; case PATH_DEVICE: return "device"; case PATH_POWER: return "power"; } return "Unknown"; } static bool get_dev_attr(const char *dev, const char *attr_file, uint32_t *attr) { char dev_path[PATH_MAX] = SYSFS_MTD_PATH; /* * Needs to be large enough to hold at most uint32_t represented as a * string in hex with leading 0x */ char attr_buf[10]; int fd, rc; /* * sizeof(dev_path) - (strlen(dev_path) + 1) is the remaining space in * dev_path, + 1 to account for the '\0'. As strncat could write n+1 bytes * to dev_path the correct calulcation for n is: * (sizeof(dev_path) - (strlen(dev_path) + 1) - 1) */ strncat(dev_path, dev, (sizeof(dev_path) - (strlen(dev_path) + 1) - 1)); strncat(dev_path, "/", (sizeof(dev_path) - (strlen(dev_path) + 1) - 1)); strncat(dev_path, attr_file, (sizeof(dev_path) - (strlen(dev_path) + 1) - 1)); fd = open(dev_path, O_RDONLY); if (fd == -1) goto out; rc = read(fd, attr_buf, sizeof(attr_buf)); close(fd); if (rc == -1) goto out; if (attr) *attr = strtol(attr_buf, NULL, 0); return 0; out: fprintf(stderr, "Couldn't get MTD device attribute '%s' from '%s'\n", dev, attr_file); return -1; } static int get_dev_mtd(const char *fdt_flash_path, char **r_path) { struct dirent **namelist; char fdt_node_path[PATH_MAX]; int count, i, rc, fd; bool done; if (!fdt_flash_path) return -1; fd = open(fdt_flash_path, O_RDONLY); if (fd == -1) { fprintf(stderr, "Couldn't open '%s' FDT attribute to determine which flash device to use\n", fdt_flash_path); return -1; } rc = read(fd, fdt_node_path, sizeof(fdt_node_path) - 1); close(fd); if (rc == -1) { fprintf(stderr, "Couldn't read flash FDT node from '%s'\n", fdt_flash_path); return -1; } fdt_node_path[rc] = '\0'; count = scandir(SYSFS_MTD_PATH, &namelist, NULL, alphasort); if (count == -1) { fprintf(stderr, "Couldn't scan '%s' for MTD devices\n", SYSFS_MTD_PATH); return -1; } rc = 0; done = false; for (i = 0; i < count; i++) { struct dirent *dirent; char dev_path[PATH_MAX] = SYSFS_MTD_PATH; char fdt_node_path_tmp[PATH_MAX]; dirent = namelist[i]; if (dirent->d_name[0] == '.' || rc || done) { free(namelist[i]); continue; } strncat(dev_path, dirent->d_name, sizeof(dev_path) - strlen(dev_path) - 2); strncat(dev_path, "/device/of_node", sizeof(dev_path) - strlen(dev_path) - 2); rc = readlink(dev_path, fdt_node_path_tmp, sizeof(fdt_node_path_tmp) - 1); if (rc == -1) { /* * This might fail because it could not exist if the system has flash * devices that present as mtd but don't have corresponding FDT * nodes, just continue silently. */ free(namelist[i]); /* Should still try the next dir so reset rc */ rc = 0; continue; } fdt_node_path_tmp[rc] = '\0'; if (strstr(fdt_node_path_tmp, fdt_node_path)) { uint32_t flags, size; /* * size and flags could perhaps have be gotten another way but this * method is super unlikely to fail so it will do. */ /* Check to see if device is writeable */ rc = get_dev_attr(dirent->d_name, "flags", &flags); if (rc) { free(namelist[i]); continue; } /* Get the size of the mtd device while we're at it */ rc = get_dev_attr(dirent->d_name, "size", &size); if (rc) { free(namelist[i]); continue; } strcpy(dev_path, "/dev/"); strncat(dev_path, dirent->d_name, sizeof(dev_path) - strlen(dev_path) - 2); *r_path = strdup(dev_path); done = true; } free(namelist[i]); } free(namelist); if (!done) fprintf(stderr, "Couldn't find '%s' corresponding MTD\n", fdt_flash_path); /* explicit negative value so as to not return a libflash code */ return done ? rc : -1; } static bool is_valid_id(uint32_t record_id) { return record_id != CLEARED_RECORD_ID; } static int do_iterate(struct gard_ctx *ctx, int (*func)(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv), void *priv) { int rc = 0; unsigned int i; struct gard_record gard, null_gard; memset(&null_gard, UINT_MAX, sizeof(gard)); for (i = 0; i * sizeof_gard(ctx) < ctx->gard_data_len && rc == 0; i++) { memset(&gard, 0, sizeof(gard)); rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + (i * sizeof_gard(ctx)), &gard, sizeof(gard)); /* It isn't super clear what constitutes the end, this should do */ if (rc || memcmp(&gard, &null_gard, sizeof(gard)) == 0) break; rc = func(ctx, i, &gard, priv); } return rc; } static int get_largest_pos_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv) { if (!priv) return -1; *(int *)priv = pos; return 0; } static int get_largest_pos(struct gard_ctx *ctx) { int rc, largest = -1; rc = do_iterate(ctx, &get_largest_pos_i, &largest); if (rc) return -1; return largest; } static int count_valid_records_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv) { if (!gard || !priv) return -1; if (is_valid_id(be32toh(gard->record_id))) (*(int *)priv)++; return 0; } static int count_valid_records(struct gard_ctx *ctx) { int rc, count = 0; rc = do_iterate(ctx, &count_valid_records_i, &count); if (rc) return 0; return count; } static int do_list_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv) { if (!gard) return -1; if (is_valid_id(be32toh(gard->record_id))) printf("| %08x | %08x | %-15s |\n", be32toh(gard->record_id), be32toh(gard->errlog_eid), path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT)); return 0; } static int do_list(struct gard_ctx *ctx, int argc, char **argv) { int rc; /* No entries */ if (count_valid_records(ctx) == 0) { printf("No GARD entries to display\n"); rc = 0; } else { printf("| ID | Error | Type |\n"); printf("+---------------------------------------+\n"); rc = do_iterate(ctx, &do_list_i, NULL); printf("+=======================================+\n"); } return rc; } static int do_show_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv) { uint32_t id; if (!priv || !gard) return -1; id = *(uint32_t *)priv; if (be32toh(gard->record_id) == id) { unsigned int count, i; printf("Record ID: 0x%08x\n", id); printf("========================\n"); printf("Error ID: 0x%08x\n", be32toh(gard->errlog_eid)); printf("Error Type: 0x%02x\n", gard->error_type); printf("Res Recovery: 0x%02x\n", gard->resource_recovery); printf("Path Type: %s\n", path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT)); count = gard->target_id.type_size & PATH_ELEMENTS_MASK; for (i = 0; i < count && i < MAX_PATH_ELEMENTS; i++) printf("%*c%s, Instance #%d\n", i + 1, '>', target_type_to_str(gard->target_id.path_elements[i].target_type), gard->target_id.path_elements[i].instance); } return 0; } static int do_show(struct gard_ctx *ctx, int argc, char **argv) { uint32_t id; int rc; if (argc != 2) { fprintf(stderr, "%s option requires a GARD record\n", argv[0]); return -1; } id = strtoul(argv[1], NULL, 16); rc = do_iterate(ctx, &do_show_i, &id); return rc; } static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv) { int largest, rc = 0; char *buf; struct gard_record null_gard; if (!gard || !ctx || !priv) return -1; /* Not this one */ if (be32toh(gard->record_id) != *(uint32_t *)priv) return 0; memset(&null_gard, 0xFF, sizeof(null_gard)); largest = get_largest_pos(ctx); printf("Clearing gard record 0x%08x...", be32toh(gard->record_id)); if (largest < 0 || pos > largest) { /* Something went horribly wrong */ fprintf(stderr, "largest index out of range %d\n", largest); return -1; } if (pos < largest) { /* We're not clearing the last record, shift all the records up */ int buf_len = ((largest - pos) * sizeof(struct gard_record)); int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof_gard(ctx)); buf = malloc(buf_len); if (!buf) return -ENOMEM; rc = blocklevel_read(ctx->bl, buf_pos, buf, buf_len); if (rc) { free(buf); fprintf(stderr, "Couldn't read from flash at 0x%08x for len 0x%08x\n", buf_pos, buf_len); return rc; } rc = blocklevel_smart_write(ctx->bl, buf_pos - sizeof_gard(ctx), buf, buf_len); free(buf); if (rc) { fprintf(stderr, "Couldn't write to flash at 0x%08lx for len 0x%08x\n", buf_pos - sizeof_gard(ctx), buf_len); return rc; } } /* Now wipe the last record */ rc = blocklevel_smart_write(ctx->bl, ctx->gard_data_pos + (largest * sizeof_gard(ctx)), &null_gard, sizeof(null_gard)); printf("done\n"); return rc; } static int reset_partition(struct gard_ctx *ctx) { int i, rc; struct gard_record gard; memset(&gard, 0xFF, sizeof(gard)); rc = blocklevel_erase(ctx->bl, ctx->gard_data_pos, ctx->gard_data_len); if (rc) { fprintf(stderr, "Couldn't erase the gard partition. Bailing out\n"); return rc; } for (i = 0; i + sizeof_gard(ctx) < ctx->gard_data_len; i += sizeof_gard(ctx)) { rc = blocklevel_write(ctx->bl, ctx->gard_data_pos + i, &gard, sizeof(gard)); if (rc) { fprintf(stderr, "Couldn't reset the entire gard partition. Bailing out\n"); return rc; } } return 0; } static int do_clear(struct gard_ctx *ctx, int argc, char **argv) { int rc; uint32_t id; if (argc != 2) { fprintf(stderr, "%s option requires a GARD record or 'all'\n", argv[0]); return -1; } if (strncmp(argv[1], "all", strlen("all")) == 0) { printf("Clearing the entire gard partition..."); fflush(stdout); rc = reset_partition(ctx); printf("done\n"); } else { id = strtoul(argv[1], NULL, 16); rc = do_iterate(ctx, do_clear_i, &id); } return rc; } int check_gard_partition(struct gard_ctx *ctx) { int rc; struct gard_record gard; char msg[2]; if (ctx->gard_data_len == 0 || ctx->gard_data_len % sizeof(struct gard_record) != 0) /* Just warn for now */ fprintf(stderr, "The %s partition doesn't appear to be an exact multiple of" "gard records in size: %lu vs %u (or partition is zero in length)\n", FLASH_GARD_PART, sizeof(struct gard_record), ctx->gard_data_len); /* * Attempt to read the first record, nothing can really operate if the * first record is dead. There (currently) isn't a way to validate more * than ECC correctness. */ rc = blocklevel_read(ctx->bl, ctx->gard_data_pos, &gard, sizeof(gard)); if (rc == FLASH_ERR_ECC_INVALID) { fprintf(stderr, "The data at the GUARD partition does not appear to be valid gard data\n"); fprintf(stderr, "Clear the entire GUARD partition? [y/N]\n"); if (fgets(msg, sizeof(msg), stdin) == NULL) { fprintf(stderr, "Couldn't read from standard input\n"); return -1; } if (msg[0] == 'y') { rc = reset_partition(ctx); if (rc) { fprintf(stderr, "Couldn't reset the GUARD partition. Bailing out\n"); return rc; } } /* * else leave rc as is so that the main bails out, not going to be * able to do sensible anyway */ } return rc; } __attribute__ ((unused)) static int do_nop(struct gard_ctx *ctx, int argc, char **argv) { fprintf(stderr, "Unimplemented action '%s'\n", argv[0]); return EXIT_SUCCESS; } struct { const char *name; const char *desc; int (*fn)(struct gard_ctx *, int, char **); } actions[] = { { "list", "List current GARD records", do_list }, { "show", "Show details of a GARD record", do_show }, { "clear", "Clear GARD records", do_clear }, }; static void usage(const char *progname) { unsigned int i; fprintf(stderr, "Usage: %s [-a -e -f -p] []\n\n", progname); fprintf(stderr, "-e --ecc\n\tForce reading/writing with ECC bytes.\n\n"); fprintf(stderr, "-f --file \n\tDon't search for MTD device," " read from .\n\n"); fprintf(stderr, "-p --part\n\tUsed in conjunction with -f to specify" "that just\n"); fprintf(stderr, "\tthe GUARD partition is in and libffs\n"); fprintf(stderr, "\tshouldn't be used.\n\n"); fprintf(stderr, "Where is one of:\n\n"); for (i = 0; i < ARRAY_SIZE(actions); i++) { fprintf(stderr, "\t%-7s\t%s\n", actions[i].name, actions[i].desc); } } static struct option global_options[] = { { "file", required_argument, 0, 'f' }, { "part", no_argument, 0, 'p' }, { "ecc", no_argument, 0, 'e' }, { 0 }, }; static const char *global_optstring = "+ef:p"; int main(int argc, char **argv) { const char *action, *progname; const char *fdt_flash_path = FDT_ACTIVE_FLASH_PATH; char *filename = NULL; struct gard_ctx _ctx, *ctx; int rc, i = 0; bool part = 0; bool ecc = 0; progname = argv[0]; ctx = &_ctx; memset(ctx, 0, sizeof(*ctx)); /* process global options */ for (;;) { int c; c = getopt_long(argc, argv, global_optstring, global_options, NULL); if (c == -1) break; switch (c) { case 'e': ecc = true; break; case 'f': /* If they specify -f twice */ free(filename); filename = strdup(optarg); if (!filename) { fprintf(stderr, "Out of memory\n"); return EXIT_FAILURE; } break; case 'p': part = true; break; case '?': usage(progname); return EXIT_FAILURE; } } /* * It doesn't make sense to specify that we have the gard partition but * read from flash */ if (part && !filename) { usage(progname); return EXIT_FAILURE; } /* do we have a command? */ if (optind == argc) { usage(progname); return EXIT_FAILURE; } argc -= optind; argv += optind; action = argv[0]; if (!filename) { rc = get_dev_mtd(fdt_flash_path, &filename); if (rc) return EXIT_FAILURE; } rc = file_init_path(filename, NULL, &(ctx->bl)); if (rc) return EXIT_FAILURE; rc = blocklevel_get_info(ctx->bl, NULL, &(ctx->f_size), NULL); if (rc) goto out; if (!part) { rc = ffs_init(0, ctx->f_size, ctx->bl, &ctx->ffs, 1); if (rc) goto out; rc = ffs_lookup_part(ctx->ffs, FLASH_GARD_PART, &ctx->gard_part_idx); if (rc) goto out; rc = ffs_part_info(ctx->ffs, ctx->gard_part_idx, NULL, &(ctx->gard_data_pos), &(ctx->gard_data_len), NULL, &(ctx->ecc)); if (rc) goto out; } else { if (ecc) { rc = blocklevel_ecc_protect(ctx->bl, 0, ctx->f_size); if (rc) goto out; } ctx->ecc = ecc; ctx->gard_data_pos = 0; ctx->gard_data_len = ctx->f_size; } rc = check_gard_partition(ctx); if (rc) { fprintf(stderr, "Does not appear to be sane gard data\n"); goto out; } for (i = 0; i < ARRAY_SIZE(actions); i++) { if (!strcmp(actions[i].name, action)) { rc = actions[i].fn(ctx, argc, argv); break; } } out: free(filename); if (ctx->ffs) ffs_close(ctx->ffs); file_exit_close(ctx->bl); if (i == ARRAY_SIZE(actions)) { fprintf(stderr, "%s: '%s' isn't a valid command\n", progname, action); usage(progname); return EXIT_FAILURE; } if (rc > 0) { show_flash_err(rc); if (filename && rc == FFS_ERR_BAD_MAGIC) fprintf(stderr, "Maybe you didn't give a full flash image file?\nDid you mean '--part'?\n"); } return rc; } skiboot-skiboot-5.1.13/external/gard/gard.h000066400000000000000000000045321265204436200205600ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define MAX_PATH_ELEMENTS 10 #define PATH_TYPE_SHIFT 4 #define PATH_ELEMENTS_MASK (0x0F) enum target_type { TYPE_NA = 0x00000000, TYPE_SYS = 0x00000001, TYPE_NODE = 0x00000002, TYPE_DIMM = 0x00000003, TYPE_MEMBUF = 0x00000004, TYPE_PROC = 0x00000005, TYPE_EX = 0x00000006, TYPE_CORE = 0x00000007, TYPE_L2 = 0x00000008, TYPE_L3 = 0x00000009, TYPE_L4 = 0x0000000A, TYPE_MCS = 0x0000000B, TYPE_MBA = 0x0000000D, TYPE_XBUS = 0x0000000E, TYPE_ABUS = 0x0000000F, TYPE_PCI = 0x00000010, TYPE_DPSS = 0x00000011, TYPE_APSS = 0x00000012, TYPE_OCC = 0x00000013, TYPE_PSI = 0x00000014, TYPE_FSP = 0x00000015, TYPE_PNOR = 0x00000016, TYPE_OSC = 0x00000017, TYPE_TODCLK = 0x00000018, TYPE_CONTROL_NODE = 0x00000019, TYPE_OSCREFCLK = 0x0000001A, TYPE_OSCPCICLK = 0x0000001B, TYPE_REFCLKENDPT = 0x0000001C, TYPE_PCICLKENDPT = 0x0000001D, TYPE_NX = 0x0000001E, TYPE_PORE = 0x0000001F, TYPE_PCIESWITCH = 0x00000020, TYPE_CAPP = 0x00000021, TYPE_FSI = 0x00000022, TYPE_TEST_FAIL = 0x00000023, TYPE_LAST_IN_RANGE = 0x00000024, }; enum path_type { PATH_NA = 0x00, PATH_AFFINITY = 0x01, PATH_PHYSICAL = 0x02, PATH_DEVICE = 0x03, PATH_POWER = 0x04, }; struct path_element { uint8_t target_type; uint8_t instance; } __attribute__((packed)); struct entity_path { /* First 4 bits are a path_type enum */ /* Second 4 bits are the amount of path_elements */ uint8_t type_size; struct path_element path_elements[MAX_PATH_ELEMENTS]; } __attribute__((packed)); /* defined by hostboot */ struct gard_record { uint32_t record_id; struct entity_path target_id; uint8_t pad0[3]; uint32_t errlog_eid; uint8_t error_type; uint8_t resource_recovery; uint8_t pad1[6]; } __attribute__((packed)); skiboot-skiboot-5.1.13/external/gard/opal-gard.1000066400000000000000000000015711265204436200214220ustar00rootroot00000000000000.TH opal-gard 1 "19 June 2015" .SH NAME opal-gard \- GUARD Partition manipulation tool for OpenPower hardware .SH SYNOPSIS \fBopal-gard\fP [ \-e | \-f \fIfile\fP | \-p ] \fIcommand\fP .SH DESCRIPTION \fBopal-gard\fP allows reading of the GUARD partition on OpenPower hardware though the exposed mtd flash interface. The actual device (usually \fB/dev/mtd0\fR) is automatically detected. .SS Options \fB\-e, \-\-ecc\fP Force reading/writing with ECC bytes. .TP \fB\-f, \-\-file\fP \fIfile\fR Don't search for the \fB/dev/mtd\fR device, use \fIfile\fP instead. .TP .TP \fB\-p, \-\-part\fP Used in conjunction with \-f to specify that just the GUARD partition is in the \fIfile\fR and that libffs shouldn't be used. .SS Commands \fIcommand\fP may be one of the following .TP \fBlist\fP List current GARD records .TP \fBshow\fP Show details of a GARD record .TP \fBclear\fP Clear GARD records skiboot-skiboot-5.1.13/external/mambo/000077500000000000000000000000001265204436200176445ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/mambo/mambo_utils.tcl000066400000000000000000000123101265204436200226600ustar00rootroot00000000000000 # # behave like gdb # proc p { reg { t 0 } { c 0 } } { switch -regexp $reg { ^r[0-9]+$ { regexp "r(\[0-9\]*)" $reg dummy num set val [mysim cpu $c thread $t display gpr $num] puts "$val" } ^f[0-9]+$ { regexp "f(\[0-9\]*)" $reg dummy num set val [mysim cpu $c thread $t display fpr $num] puts "$val" } ^v[0-9]+$ { regexp "v(\[0-9\]*)" $reg dummy num set val [mysim cpu $c thread $t display vmxr $num] puts "$val" } default { set val [mysim cpu $c thread $t display spr $reg] puts "$val" } } } # # behave like gdb # proc sr { reg val {t 0}} { switch -regexp $reg { ^r[0-9]+$ { regexp "r(\[0-9\]*)" $reg dummy num mysim cpu 0:$t set gpr $num $val } ^f[0-9]+$ { regexp "f(\[0-9\]*)" $reg dummy num mysim cpu 0:$t set fpr $num $val } ^v[0-9]+$ { regexp "v(\[0-9\]*)" $reg dummy num mysim cpu 0:$t set vmxr $num $val } default { mysim cpu 0:$t set spr $reg $val } } p $reg $t } proc b { addr } { mysim trigger set pc $addr "just_stop" set at [i $addr] puts "breakpoint set at $at" } proc wr { start stop } { mysim trigger set memory system w $start $stop 0 "just_stop" } proc c { } { mysim go } proc i { pc { t 0 } { c 0 } } { set pc_laddr [mysim cpu $c util itranslate $pc] set inst [mysim cpu $c memory display $pc_laddr 4] set disasm [mysim cpu $c util ppc_disasm $inst $pc] return "\[$c:$t\]: $pc ($pc_laddr) Enc:$inst : $disasm" } proc ipc { {t 0} {c 0}} { set pc [mysim cpu $c thread $t display spr pc] i $pc $t $c } proc ipca { } { set cpus [myconf query cpus] set threads [myconf query processor/number_of_threads] for { set i 0 } { $i < $cpus } { incr i 1 } { for { set j 0 } { $j < $threads } { incr j 1 } { puts [ipc $j $i] } } } proc pa { spr } { set cpus [myconf query cpus] set threads [myconf query processor/number_of_threads] for { set i 0 } { $i < $cpus } { incr i 1 } { for { set j 0 } { $j < $threads } { incr j 1 } { set val [mysim cpu $i thread $j display spr $spr] puts "CPU: $i THREAD: $j SPR $spr = $val" } } } proc s { } { mysim step 1 ipca } proc z { count } { while { $count > 0 } { s incr count -1 } } proc sample_pc { sample count } { while { $count > 0 } { mysim cycle $sample ipc incr count -1 } } proc e2p { ea } { set pa [ mysim util dtranslate $ea ] puts "$pa" } proc x { pa { size 8 } } { set val [ mysim memory display $pa $size ] puts "$pa : $val" } proc it { ea } { mysim util itranslate $ea } proc dt { ea } { mysim util dtranslate $ea } proc ex { ea { size 8 } } { set pa [ mysim util dtranslate $ea ] set val [ mysim memory display $pa $size ] puts "$pa : $val" } proc hexdump { location count } { set addr [expr $location & 0xfffffffffffffff0] set top [expr $addr + ($count * 15)] for { set i $addr } { $i < $top } { incr i 16 } { set val [expr $i + (4 * 0)] set val0 [format "%08x" [mysim memory display $val 4]] set val [expr $i + (4 * 1)] set val1 [format "%08x" [mysim memory display $val 4]] set val [expr $i + (4 * 2)] set val2 [format "%08x" [mysim memory display $val 4]] set val [expr $i + (4 * 3)] set val3 [format "%08x" [mysim memory display $val 4]] set ascii "(none)" set loc [format "0x%016x" $i] puts "$loc: $val0 $val1 $val2 $val3 $ascii" } } proc slbv {} { puts [mysim cpu 0 display slb valid] } proc regs { { t 0 } { c 0 } } { puts "GPRS:" puts [mysim cpu $c thread $t display gprs] } proc tlbv { { c 0 } } { puts "$c:TLB: ----------------------" puts [mysim cpu $c display tlb valid] } proc just_stop { args } { simstop ipca } proc st { count } { set sp [mysim cpu 0 display gpr 1] puts "SP: $sp" ipc set lr [mysim cpu 0 display spr lr] i $lr while { $count > 0 } { set sp [mysim util itranslate $sp] set lr [mysim memory display [expr $sp++16] 8] i $lr set sp [mysim memory display $sp 8] incr count -1 } } proc mywatch { } { while { [mysim memory display 0x700 8] != 0 } { mysim cycle 1 } puts "condition occured " ipc } # # force gdb to attach # proc gdb { {t 0} } { mysim set fast off mysim debugger wait $t } proc egdb { {t 0} } { set srr0 [mysim cpu 0 display spr srr0] set srr1 [mysim cpu 0 display spr srr1] mysim cpu 0 set spr pc $srr0 mysim cpu 0 set spr msr $srr1 gdb $t } proc bt { {sp 0} } { set t 0 if { $sp < 16 } { set t $sp set sp 0 } if { $sp == 0 } { set sp [mysim cpu 0:$t display gpr 1] } set lr [mysim cpu 0:$t display spr lr] puts "backtrace thread $t, stack $sp" i $lr while { 1 == 1 } { set pa [ mysim util dtranslate $sp ] set bc [ mysim memory display $pa 8 ] set cr [ mysim memory display [expr $pa+8] 8 ] set lr [ mysim memory display [expr $pa+16] 8 ] i $lr if { $bc == 0 } { return } set sp $bc } } proc ton { } {mysim mode turbo } proc toff { } {mysim mode simple } proc don { opt } { simdebug set $opt 1 } proc doff { opt } { simdebug set $opt 0 } skiboot-skiboot-5.1.13/external/mambo/skiboot.tcl000066400000000000000000000117071265204436200220300ustar00rootroot00000000000000# need to get images path defined early source $env(LIB_DIR)/ppc/util.tcl proc mconfig { name env_name def } { global mconf global env if { [info exists env($env_name)] } { set mconf($name) $env($env_name) } if { ![info exists mconf($name)] } { set mconf($name) $def } } mconfig threads THREADS 1 mconfig memory MEM_SIZE 1G # Should we stop on an illeagal instruction mconfig stop_on_ill MAMBO_STOP_ON_ILL false # Location of application binary to load mconfig boot_image SKIBOOT ../../skiboot.lid # Boot: Memory location to load boot_image, for binary or vmlinux mconfig boot_load MAMBO_BOOT_LOAD 0 # Boot: Value of PC after loading, for binary or vmlinux mconfig boot_pc MAMBO_BOOT_PC 0x10 # Payload: Allow for a Linux style ramdisk/initrd if { ![info exists env(SKIBOOT_ZIMAGE)] } { error "Please set SKIBOOT_ZIMAGE to the path of your zImage.epapr" } mconfig payload PAYLOAD $env(SKIBOOT_ZIMAGE) # Paylod: Memory location for a Linux style ramdisk/initrd mconfig payload_addr PAYLOAD_ADDR 0x20000000; # FW: Where should ePAPR Flat Devtree Binary be loaded mconfig epapr_dt_addr EPAPR_DT_ADDR 0x1f00000;# place at 31M # Disk: Location of file to use a bogus disk 0 mconfig rootdisk ROOTDISK none # Disk: File to use for re COW file: none or mconfig rootdisk_cow MAMBO_ROOTDISK_COW none # Disk: COW method to use mconfig rootdisk_cow_method MAMBO_ROOTDISK_COW_METHOD newcow # Disk: COW hash size mconfig rootdisk_cow_hash MAMBO_ROOTDISK_COW_HASH 1024 # Net: What type of networking: none, phea, bogus mconfig net MAMBO_NET none # Net: What is the base interface for the tun/tap device mconfig tap_base MAMBO_NET_TAP_BASE 0 # # Create machine config # define dup pegasus myconf myconf config processor/number_of_threads $mconf(threads) myconf config memory_size $mconf(memory) myconf config processor_option/ATTN_STOP true myconf config processor_option/stop_on_illegal_instruction $mconf(stop_on_ill) myconf config UART/0/enabled false myconf config SimpleUART/enabled false myconf config enable_rtas_support false myconf config processor/cpu_frequency 512M myconf config processor/timebase_frequency 1/1 myconf config enable_pseries_nvram false define machine myconf mysim # # Include various utilities # source $env(LIB_DIR)/common/epapr.tcl if {![info exists of::encode_compat]} { source $env(LIB_DIR)/common/openfirmware_utils.tcl } # Only source mambo_utils.tcl if it exists in the current directory. That # allows running mambo in another directory to the one skiboot.tcl is in. if { [file exists mambo_utils.tcl] } then { source mambo_utils.tcl } # # Instanciate xscom # set xscom_base 0x1A0000000000 mysim xscom create $xscom_base # Setup bogus IO if { $mconf(rootdisk) != "none" } { # Now load the bogus disk image switch $mconf(rootdisk_cow) { none { mysim bogus disk init 0 $mconf(rootdisk) rw puts "bogusdisk initialized for $mconf(rootdisk)" } default { mysim bogus disk init 0 $mconf(rootdisk) \ $mconf(rootdisk_cow_method) \ $mconf(rootdisk_cow) $mconf(rootdisk_cow_hash) } } } switch $mconf(net) { none { puts "No network support selected" } bogus - bogusnet { set net_tap [format "tap%d" $mconf(tap_base)] mysim bogus net init 0 $mconf(net_mac) $net_tap } default { error "Bad net \[none | bogus]: $mconf(net)" } } # Device tree fixups set root_node [mysim of find_device "/"] mysim of addprop $root_node string "epapr-version" "ePAPR-1.0" mysim of setprop $root_node "compatible" "ibm,powernv" set cpus_node [mysim of find_device "/cpus"] mysim of addprop $cpus_node int "#address-cells" 1 mysim of addprop $cpus_node int "#size-cells" 0 set cpu0_node [mysim of find_device "/cpus/PowerPC@0"] mysim of addprop $cpu0_node int "ibm,chip-id" 0 set mem0_node [mysim of find_device "/memory@0"] mysim of addprop $mem0_node int "ibm,chip-id" 0 set xscom_node [ mysim of addchild $root_node xscom [format %x $xscom_base]] set reg [list $xscom_base 0x10000000] mysim of addprop $xscom_node array64 "reg" reg mysim of addprop $xscom_node empty "scom-controller" "" mysim of addprop $xscom_node int "ibm,chip-id" 0 mysim of addprop $xscom_node int "#address-cells" 1 mysim of addprop $xscom_node int "#size-cells" 1 set compat [list] lappend compat "ibm,xscom" lappend compat "ibm,power8-xscom" set compat [of::encode_compat $compat] mysim of addprop $xscom_node byte_array "compatible" $compat # Flatten it epapr::of2dtb mysim $mconf(epapr_dt_addr) # Load images set boot_size [file size $mconf(boot_image)] mysim memory fread $mconf(boot_load) $boot_size $mconf(boot_image) set payload_size [file size $mconf(payload)] mysim memory fread $mconf(payload_addr) $payload_size $mconf(payload) # Init CPUs for { set i 0 } { $i < $mconf(threads) } { incr i } { mysim mcm 0 cpu 0 thread $i set spr pc $mconf(boot_pc) mysim mcm 0 cpu 0 thread $i set gpr 3 $mconf(epapr_dt_addr) mysim mcm 0 cpu 0 thread $i set spr pvr 0x4b0201 mysim mcm 0 cpu 0 thread $i config_on } # Turbo mode & run mysim mode turbo skiboot-skiboot-5.1.13/external/memboot/000077500000000000000000000000001265204436200202135ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/memboot/Makefile000066400000000000000000000002731265204436200216550ustar00rootroot00000000000000all: memboot CFLAGS=-O2 -Wall memboot: memboot.c $(CC) $(CFLAGS) -o $@ $^ .PHONY: clean clean: rm -rf memboot .PHONY: distclean distclean: clean rm -rf *.c~ *.h~ *.i *.s Makefile~ skiboot-skiboot-5.1.13/external/memboot/README000066400000000000000000000012151265204436200210720ustar00rootroot00000000000000memboot ======= This is a small utility designed to be run on the ASPEED BMC used in some OpenPower systems. It allows you to boot the P8 host from the main system memory of the BMC rather than from the flash memory. This is mainly useful for firmware development as it makes it possible to try new firmware images without first having to write the new image to flash memory which can be a lengthy process. Building: --------- CC= make Usage: ----- ./memboot - Will cause the P8 host to boot from BMC system memory at next power on. ./memboot - Will boot the system from flash memory at next power on. skiboot-skiboot-5.1.13/external/memboot/memboot.c000066400000000000000000000071151265204436200220250ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include /* Where to put the firmware image if booting from memory */ #define MEM_IMG_BASE (0x5c000000) /* Start of flash memory if booting from flash */ #define FLASH_IMG_BASE (0x30000000) /* LPC registers */ #define LPC_BASE 0x1e789000 #define LPC_HICR6 0x80 #define LPC_HICR7 0x88 #define LPC_HICR8 0x8c #define LPC_SCR0SIO 0x170 #define MEMBOOT_SIO_VERSION_FLAG 0x42 #define MEMBOOT_SIO_FLAG (0x10 << 8) uint32_t readl(void *addr) { asm volatile("" : : : "memory"); return *(volatile uint32_t *)addr; } void writel(uint32_t val, void *addr) { asm volatile("" : : : "memory"); *(volatile uint32_t *)addr = val; } void copy_flash_img(int mem_fd, int flash_fd, unsigned int size) { static void *memimg, *fwimg; size_t pagesize = getpagesize(); memimg = mmap(NULL, ((size/pagesize)+1)*pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, MEM_IMG_BASE); if (memimg == MAP_FAILED) { perror("Unable to map image destination memory"); exit(1); } fwimg = mmap(NULL,size, PROT_READ, MAP_SHARED, flash_fd, 0); if (fwimg == MAP_FAILED) { perror("Unable to open image source memory"); exit(1); } /* Copy boot image */ memcpy(memimg, fwimg, size); } void boot_firmware_image(int mem_fd, char *filename) { int fw_fd; struct stat st; fw_fd = open(filename, O_RDONLY); if (fw_fd < 0) { perror("Unable to open flash image\n"); exit(1); } if (stat(filename, &st)) { perror("Unable to determine size of firmware image"); exit(1); } if (st.st_size > 32*1024*1024) { fprintf(stderr, "Flash too large (> 32MB)"); exit(1); } copy_flash_img(mem_fd, fw_fd, st.st_size); close(fw_fd); } int main(int argc, char *argv[]) { int mem_fd; void *lpcreg; uint32_t lpc_scr0sio_val; uint32_t lpc_hicr7_val = (FLASH_IMG_BASE | 0xe00); if (argc > 2) { printf("Usage: %s \n", argv[0]); exit(1); } mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) { perror("Unable to open /dev/mem"); exit(1); } lpcreg = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, LPC_BASE); if (lpcreg == MAP_FAILED) { perror("Unable to map LPC register memory"); exit(1); } lpc_scr0sio_val = readl(lpcreg+LPC_SCR0SIO); lpc_scr0sio_val &= ~0xff; lpc_scr0sio_val |= MEMBOOT_SIO_VERSION_FLAG; lpc_scr0sio_val &= ~MEMBOOT_SIO_FLAG; if (argc == 2) { boot_firmware_image(mem_fd, argv[1]); lpc_hicr7_val = (MEM_IMG_BASE | 0xe00); /* Set the boot mode scratch register to indicate a memboot */ lpc_scr0sio_val |= MEMBOOT_SIO_FLAG; printf("Booting from memory after power cycle\n"); } if (readl(lpcreg + LPC_HICR7) != lpc_hicr7_val) { printf("Resetting LPC_HICR7 to 0x%x\n", lpc_hicr7_val); writel(lpc_hicr7_val, lpcreg+LPC_HICR7); } /* Set the magic value */ writel(0x42, lpcreg+LPC_SCR0SIO); writel(lpc_scr0sio_val, lpcreg+LPC_SCR0SIO); printf("LPC_HICR7 = 0x%x\n", lpc_hicr7_val); return 0; } skiboot-skiboot-5.1.13/external/opal-prd/000077500000000000000000000000001265204436200202675ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/opal-prd/.gitignore000066400000000000000000000000511265204436200222530ustar00rootroot00000000000000opal-prd /ccan /libflash /test/test_pnor skiboot-skiboot-5.1.13/external/opal-prd/Makefile000066400000000000000000000034071265204436200217330ustar00rootroot00000000000000CC = $(CROSS_COMPILE)gcc CFLAGS += -m64 -Werror -Wall -g2 -ggdb LDFLAGS += -m64 ASFLAGS = -m64 CPPFLAGS += -I. -I../../include -I../../ prefix = /usr/local/ sbindir = $(prefix)/sbin datadir = $(prefix)/share mandir = $(datadir)/man # Use make V=1 for a verbose build. ifndef V Q_CC= @echo ' CC ' $@; Q_LINK= @echo ' LINK ' $@; Q_LN= @echo ' LN ' $@; Q_MKDIR=@echo ' MKDIR ' $@; endif OBJS = opal-prd.o thunk.o pnor.o i2c.o module.o version.o \ blocklevel.o libffs.o libflash.o ecc.o all: opal-prd LINKS = ccan OPAL_PRD_VERSION ?= $(shell ../../make_version.sh opal-prd) ifdef KERNEL_DIR LINKS += asm/opal-prd.h endif ccan: $(Q_LN)ln -sfr ../../ccan ./ccan asm/opal-prd.h: $(Q_MKDIR)mkdir -p asm $(Q_LN)ln -sfr $(KERNEL_DIR)/arch/powerpc/include/uapi/asm/opal-prd.h \ asm/opal-prd.h $(OBJS): $(LINKS) %.o: %.c $(Q_CC)$(COMPILE.c) $< -o $@ %.o: ../../libflash/%.c $(Q_CC)$(COMPILE.c) $< -o $@ %.o: %.S $(Q_CC)$(COMPILE.S) $< -o $@ opal-prd: $(OBJS) $(Q_LINK)$(LINK.o) -o $@ $^ version.c: ../../make_version.sh .version @(if [ "a$(OPAL_PRD_VERSION)" = "a" ]; then \ echo "#error You need to set OPAL_PRD_VERSION environment variable" > $@ ;\ else \ echo "const char version[] = \"$(OPAL_PRD_VERSION)\";" ;\ fi) > $@ .PHONY: VERSION-always .version: VERSION-always @echo $(OPAL_PRD_VERSION) > $@.tmp @cmp -s $@ $@.tmp || cp $@.tmp $@ @rm -f $@.tmp test: test/test_pnor test/test_pnor: test/test_pnor.o pnor.o libflash/libflash.o libflash/libffs.o $(Q_LINK)$(LINK.o) -o $@ $^ install: all install -D opal-prd $(DESTDIR)$(sbindir)/opal-prd install -D -m 0644 opal-prd.8 $(DESTDIR)$(mandir)/man8/opal-prd.8 clean: $(RM) *.[odsa] opal-prd $(RM) test/*.[odsa] test/test_pnor distclean: clean $(RM) -f $(LINKS) asm skiboot-skiboot-5.1.13/external/opal-prd/config.h000066400000000000000000000005531265204436200217100ustar00rootroot00000000000000/* For CCAN */ #include #include #define HAVE_TYPEOF 1 #define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 #if __BYTE_ORDER == __LITTLE_ENDIAN #define HAVE_BIG_ENDIAN 0 #define HAVE_LITTLE_ENDIAN 1 #else #define HAVE_BIG_ENDIAN 1 #define HAVE_LITTLE_ENDIAN 0 #endif #define HAVE_BYTESWAP_H 1 #define HAVE_BSWAP_64 1 skiboot-skiboot-5.1.13/external/opal-prd/hostboot-interface.h000066400000000000000000000362711265204436200242500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include /* Hostboot runtime interface */ /* Derived from src/include/runtime/interface.h in Hostboot */ #define HOSTBOOT_RUNTIME_INTERFACE_VERSION 1 /** Memory error types defined for memory_error() interface. */ enum MemoryError_t { /** Hardware has reported a solid memory CE that is * correctable, but continues to report errors on subsequent * reads. A second CE on that cache line will result in memory * UE. Therefore, it is advised to migrate off of the address * range as soon as possible. */ MEMORY_ERROR_CE = 0, /** Hardware has reported an uncorrectable error in memory * (memory UE, channel failure, etc). The hypervisor should * migrate any partitions off this address range as soon as * possible. Note that these kind of errors will most likely * result in partition failures. It is advised that the * hypervisor waits some time for PRD to handle hardware * attentions so that the hypervisor will know all areas of * memory that are impacted by the failure. */ MEMORY_ERROR_UE = 1, }; struct host_interfaces { /** Interface version. */ uint64_t interface_version; /** Put a string to the console. */ void (*puts)(const char*); /** Critical failure in runtime execution. */ void (*assert)(void); /** OPTIONAL. Hint to environment that the page may be executed. */ int (*set_page_execute)(void*); /** malloc */ void *(*malloc)(size_t); /** free */ void (*free)(void*); /** realloc */ void *(*realloc)(void*, size_t); /** * @brief Send a PEL to the FSP * @param[in] plid Platform Log identifier * @param[in] data size in bytes * @param[in] pointer to data * @return 0 on success else error code * @platform FSP */ int (*send_error_log)(uint32_t,uint32_t,void *); /** * @brief Scan communication read * @param[in] chip_id (based on devtree defn) * @param[in] address * @param[in] pointer to 8-byte data buffer * @return 0 on success else return code * @platform FSP,OpenPOWER */ int (*scom_read)(uint64_t, uint64_t, void*); /** * @brief Scan communication write * @param[in] chip_id (based on devtree defn) * @param[in] address * @param[in] pointer to 8-byte data buffer * @return 0 on success else return code * @platform FSP,OpenPOWER */ int (*scom_write)(uint64_t, uint64_t, const void *); /** * @brief Load a LID from PNOR, FSP, etc. * * @param[in] LID number. * @param[out] Allocated buffer for LID. * @param[out] Size of LID (in bytes). * * @return 0 on success, else RC. * @platform FSP */ int (*lid_load)(uint32_t lid, void **buf, size_t *len); /** * @brief Release memory from previously loaded LID. * * @param[in] Allocated buffer for LID to release. * * @return 0 on success, else RC. * @platform FSP */ int (*lid_unload)(void *buf); /** * @brief Get the address of a reserved memory region by its devtree * name. * * @param[in] Devtree name (ex. "ibm,hbrt-vpd-image") * @param[in] Devtree instance * @return physical address of region (or NULL). * @platform FSP,OpenPOWER */ uint64_t (*get_reserved_mem)(const char *name, uint32_t instance); /** * @brief Force a core to be awake, or clear the force * @param[in] i_core Core to wake up (pid) * @param[in] i_mode 0=force awake * 1=clear force * 2=clear all previous forces * @return rc non-zero on error * @platform FSP */ int (*wakeup)( uint32_t i_core, uint32_t i_mode ); /** * @brief Delay/sleep for at least the time given * @param[in] seconds * @param[in] nano seconds * @platform FSP,OpenPOWER */ void (*nanosleep)(uint64_t i_seconds, uint64_t i_nano_seconds); /** * @brief Report an OCC error to the host * @param[in] Failing status that identifies the nature of the fail * @param[in] Identifier that specifies the failing part * @platform FSP */ void (*report_occ_failure)( uint64_t i_status, uint64_t i_partId ); /** * @brief Reads the clock value from a POSIX clock. * @param[in] i_clkId - The clock ID to read. * @param[out] o_tp - The timespec struct to store the clock value in. * * @return 0 or -(errno). * @retval 0 - SUCCESS. * @retval -EINVAL - Invalid clock requested. * @retval -EFAULT - NULL ptr given for timespec struct. * * @platform OpenPOWER */ int (*clock_gettime)( clockid_t i_clkId, struct timespec* o_tp ); /** * @brief Read Pnor * @param[in] i_proc: processor Id * @param[in] i_partitionName: name of the partition to read * @param[in] i_offset: offset within the partition * @param[out] o_data: pointer to the data read * @param[in] i_sizeBytes: size of data to read * @retval rc - number of bytes read, or non-zero on error * @platform OpenPOWER */ int (*pnor_read) ( uint32_t i_proc, const char* i_partitionName, uint64_t i_offset, void* o_data, size_t i_sizeBytes ); /** * @brief Write to Pnor * @param[in] i_proc: processor Id * @param[in] i_partitionName: name of the partition to write * @param[in] i_offset: offset withing the partition * @param[in] i_data: pointer to the data to write * @param[in] i_sizeBytes: size of data to write * @retval rc - number of bytes written, or non-zero on error * @platform OpenPOWER */ int (*pnor_write) ( uint32_t i_proc, const char* i_partitionName, uint64_t i_offset, void* i_data, size_t i_sizeBytes ); /** * i2c master description: chip, engine and port packed into * a single 64-bit argument * * --------------------------------------------------- * | chip | reserved | eng | port | * | (32) | (16) | (8) | (8) | * --------------------------------------------------- */ #define HBRT_I2C_MASTER_CHIP_SHIFT 32 #define HBRT_I2C_MASTER_CHIP_MASK (0xfffffffful << 32) #define HBRT_I2C_MASTER_ENGINE_SHIFT 8 #define HBRT_I2C_MASTER_ENGINE_MASK (0xfful << 8) #define HBRT_I2C_MASTER_PORT_SHIFT 0 #define HBRT_I2C_MASTER_PORT_MASK (0xfful) /** * @brief Read data from an i2c device * @param[in] i_master - Chip/engine/port of i2c bus * @param[in] i_devAddr - I2C address of device * @param[in] i_offsetSize - Length of offset (in bytes) * @param[in] i_offset - Offset within device to read * @param[in] i_length - Number of bytes to read * @param[out] o_data - Data that was read * @return 0 on success else return code * @platform OpenPOWER */ int (*i2c_read)( uint64_t i_master, uint16_t i_devAddr, uint32_t i_offsetSize, uint32_t i_offset, uint32_t i_length, void* o_data ); /** * @brief Write data to an i2c device * @param[in] i_master - Chip/engine/port of i2c bus * @param[in] i_devAddr - I2C address of device * @param[in] i_offsetSize - Length of offset (in bytes) * @param[in] i_offset - Offset within device to write * @param[in] i_length - Number of bytes to write * @param[in] Data to write * @return 0 on success else return code * @platform OpenPOWER */ int (*i2c_write)( uint64_t i_master, uint16_t i_devAddr, uint32_t i_offsetSize, uint32_t i_offset, uint32_t i_length, void* i_data ); /** * Perform an IPMI transaction * @param[in] netfn The IPMI netfn byte * @param[in] cmd The IPMI cmd byte * @param[in] tx_buf The IPMI packet to send to the host * @param[in] tx_size The number of bytes, to send * @param[in] rx_buf A buffer to be populated with the IPMI * response. * @param[inout] rx_size The allocated size of the rx buffer on * input, updated to the size of the response on output. * This should always begin with the IPMI completion * code. */ int (*ipmi_msg)(uint8_t netfn, uint8_t cmd, void *tx_buf, size_t tx_size, void *rx_buf, size_t *rx_size); /** * @brief Hardware has reported a memory error. This function requests * the hypervisor to remove the all addresses within the address range * given (including endpoints) from the available memory space. * * It is understood that the hypervisor may not be able to immediately * deallocate the memory because it may be in use by a partition. * Therefore, the hypervisor should cache all requests and deallocate * the memory once it has been freed. * * @param i_startAddr The beginning address of the range. * @param i_endAddr The end address of the range. * @param i_errorType See enum MemoryError_t. * * @return 0 if the request is successfully received. Any value other * than 0 on failure. The hypervisor should cache the request and * return immediately. It should not wait for the request to be * applied. See note above. */ int (*memory_error)( uint64_t i_startAddr, uint64_t i_endAddr, enum MemoryError_t i_errorType ); }; struct runtime_interfaces { /** Interface version. */ uint64_t interface_version; /** * @brief Execute CxxTests that may be contained in the image. * * @param[in] - Pointer to CxxTestStats structure for results reporting. */ void (*cxxtestExecute)(void *); /** * @brief Get a list of lids numbers of the lids known to HostBoot * * @param[out] o_num - the number of lids in the list * @return a pointer to the list * @platform FSP */ const uint32_t * (*get_lid_list)(size_t * o_num); /** * @brief Load OCC Image and common data into mainstore, also setup OCC * BARSs * * @param[in] i_homer_addr_phys - The physical mainstore address of the * start of the HOMER image * @param[in] i_homer_addr_va - Virtual memory address of the HOMER * image * @param[in] i_common_addr_phys - The physical mainstore address * of the OCC common area. * @param[in] i_common_addr_va - Virtual memory address of the common * area * @param[in] i_chip - The HW chip id (XSCOM chip ID) * @return 0 on success else return code * @platform FSP */ int (*occ_load)(uint64_t i_homer_addr_phys, uint64_t i_homer_addr_va, uint64_t i_common_addr_phys, uint64_t i_common_addr_va, uint64_t i_chip); /** * @brief Start OCC on all chips, by module * * @param[in] i_chip - Array of functional HW chip ids * @Note The caller must include a complete modules worth of chips * @param[in] i_num_chips - Number of chips in the array * @return 0 on success else return code * @platform FSP */ int (*occ_start)(uint64_t* i_chip, size_t i_num_chips); /** * @brief Stop OCC hold OCCs in reset * * @param[in] i_chip - Array of functional HW chip ids * @Note The caller must include a complete modules worth of chips * @param[in] i_num_chips - Number of chips in the array * @return 0 on success else return code * @platform FSP */ int (*occ_stop)(uint64_t* i_chip, size_t i_num_chips); /** * @brief Notify HTMGT that an OCC has an error to report * * @details When an OCC has encountered an error that it wants to * be reported, this interface will be called to trigger * HTMGT to collect and commit the error. * * @param[i] i_chipId - Id of processor with failing OCC * @platform OpenPower */ void (*process_occ_error) (uint64_t i_chipId); /** * @brief Enable chip attentions * * @return 0 on success else return code * @platform OpenPower */ int (*enable_attns)(void); /** * @brief Disable chip attentions * * @return 0 on success else return code * @platform OpenPower */ int (*disable_attns)(void); /** * @brief handle chip attentions * * @param[in] i_proc - processor chip id at attention XSCOM chip id * based on devtree defn * @param[in] i_ipollStatus - processor chip Ipoll status * @param[in] i_ipollMask - processor chip Ipoll mask * @return 0 on success else return code * @platform OpenPower */ int (*handle_attns)(uint64_t i_proc, uint64_t i_ipollStatus, uint64_t i_ipollMask); /** * @brief Notify HTMGT that an OCC has failed and needs to be reset * * @details When BMC detects an OCC failure that requires a reset, * this interface will be called to trigger the OCC reset. HTMGT * maintains a reset count and if there are additional resets * available, the OCCs get reset/reloaded. If the recovery attempts * have been exhauseted or the OCC fails to go active, an unrecoverable * error will be logged and the system will remain in safe mode. * * @param[in] i_chipId ChipID which identifies the OCC reporting an * error * @platform OpenPOWER */ void (*process_occ_reset)(uint64_t i_chipId); /** * @brief Change the OCC state * * @details This is a blocking call that will change the OCC state. * The OCCs will only actuate (update processor frequency/ voltages) * when in Active state. The OCC will only be monitoring/observing * when in Observation state. * * @note When the OCCs are initially started, the state will * default to Active. If the state is changed to Observation, that * state will be retained until the next IPL. (If the OCC would get * reset, it would return to the last requested state) * * @param[in] i_occActivation set to true to move OCC to Active state * or false to move OCC to Observation state * * @return 0 on success, or return code if the state did not change. * @platform OpenPower */ int (*enable_occ_actuation)(bool i_occActivation); /** * @brief Apply a set of attribute overrides * * @param[in] pointer to binary override data * @param[in] length of override data (bytes) * @returns 0 on success, or return code if the command failed * * @platform OpenPower */ int (*apply_attr_override)(uint8_t *i_data, size_t size); /** * @brief Send a pass-through command to HTMGT * * @details This is a blocking call that will send a command to * HTMGT. * * @note If o_rspLength is returned with a non-zero value, the * data at the o_rspData should be dumped to stdout in a * hex dump format. * @note The maximum response data returned will be 4096 bytes * * @param[in] i_cmdLength number of bytes in pass-thru command data * @param[in] *i_cmdData pointer to pass-thru command data * @param[out] *o_rspLength pointer to number of bytes returned in * o_rspData * @param[out] *o_rspData pointer to a 4096 byte buffer that will * contain the response data from the command * * @returns 0 on success, or return code if the command failed * @platform OpenPower */ int (*mfg_htmgt_pass_thru)(uint16_t i_cmdLength, uint8_t *i_cmdData, uint16_t *o_rspLength, uint8_t *o_rspData); /** * @brief Execute an arbitrary command inside hostboot runtime * @param[in] Number of arguments (standard C args) * @param[in] Array of argument values (standard C args) * @param[out] Response message (NULL terminated), memory allocated * by hbrt, if o_outString is NULL then no response will * be sent * @return 0 on success, else error code */ int (*run_command)(int argc, const char **argv, char **o_outString); /* Reserve some space for future growth. */ void (*reserved[29])(void); }; skiboot-skiboot-5.1.13/external/opal-prd/i2c.c000066400000000000000000000143471265204436200211210ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE /* for aspritnf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opal-prd.h" #include "module.h" #include "i2c.h" struct i2c_bus { uint32_t chip_id; uint8_t engine; uint8_t port; const char *devpath; int fd; struct list_node link; }; static struct list_head bus_list = LIST_HEAD_INIT(bus_list); static int i2c_get_dev(uint32_t chip, uint8_t eng, uint8_t port, uint16_t dev) { struct i2c_bus *b, *bus = NULL; list_for_each(&bus_list, b, link) { if (b->chip_id == chip && b->engine == eng && b->port == port) { bus = b; break; } } if (!bus) { pr_log(LOG_WARNING, "I2C: Bus %08x/%d/%d not found", chip, eng, port); return -1; } if (bus->fd < 0) { bus->fd = open(bus->devpath, O_RDWR); if (bus->fd < 0) { pr_log(LOG_ERR, "I2C: Failed to open %s: %m", bus->devpath); return -1; } } /* XXX We could use the I2C_SLAVE ioctl to check if the device * is currently in use by a kernel driver... */ return bus->fd; } int i2c_read(uint32_t chip_id, uint8_t engine, uint8_t port, uint16_t device, uint32_t offset_size, uint32_t offset, uint32_t length, void* data) { struct i2c_rdwr_ioctl_data ioargs; struct i2c_msg msgs[2]; uint8_t obuf[4]; int fd, i, midx = 0; if (offset_size > 4) { pr_log(LOG_ERR, "I2C: Invalid write offset_size %d", offset_size); return -1; } fd = i2c_get_dev(chip_id, engine, port, device); if (fd == -1) return -1; /* If we have an offset, build a message for it */ if (offset_size) { /* The offset has a variable size so let's handle this properly * as it has to be laid out in memory MSB first */ for (i = 0; i < offset_size; i++) obuf[i] = offset >> (8 * (offset_size - i - 1)); msgs[0].addr = device; msgs[0].flags = 0; msgs[0].buf = obuf; msgs[0].len = offset_size; midx = 1; } /* Build the message for the data portion */ msgs[midx].addr = device; msgs[midx].flags = I2C_M_RD; msgs[midx].buf = data; msgs[midx].len = length; midx++; ioargs.msgs = msgs; ioargs.nmsgs = midx; if (ioctl(fd, I2C_RDWR, &ioargs) < 0) { pr_log(LOG_ERR, "I2C: Read error: %m"); return -1; } pr_debug("I2C: Read from %08x:%d:%d@%02x+0x%x %d bytes ok", chip_id, engine, port, device, offset_size ? offset : 0, length); return 0; } int i2c_write(uint32_t chip_id, uint8_t engine, uint8_t port, uint16_t device, uint32_t offset_size, uint32_t offset, uint32_t length, void* data) { struct i2c_rdwr_ioctl_data ioargs; struct i2c_msg msg; int fd, size, i, rc; uint8_t *buf; if (offset_size > 4) { pr_log(LOG_ERR, "I2C: Invalid write offset_size %d", offset_size); return -1; } fd = i2c_get_dev(chip_id, engine, port, device); if (fd == -1) return -1; /* Not all kernel driver versions support breaking up a write into * two components (offset, data), so we coalesce them first and * issue a single write. The offset is layed out in BE format. */ size = offset_size + length; buf = malloc(size); if (!buf) { pr_log(LOG_ERR, "I2C: Out of memory"); return -1; } /* The offset has a variable size so let's handle this properly * as it has to be laid out in memory MSB first */ for (i = 0; i < offset_size; i++) buf[i] = offset >> (8 * (offset_size - i - 1)); /* Copy the remaining data */ memcpy(buf + offset_size, data, length); /* Build the message */ msg.addr = device; msg.flags = 0; msg.buf = buf; msg.len = size; ioargs.msgs = &msg; ioargs.nmsgs = 1; rc = ioctl(fd, I2C_RDWR, &ioargs); free(buf); if (rc < 0) { pr_log(LOG_ERR, "I2C: Write error: %m"); return -1; } return 0; } static void i2c_add_bus(uint32_t chip, uint32_t engine, uint32_t port, const char *devname) { struct i2c_bus *b = malloc(sizeof(struct i2c_bus)); char *dn; if (asprintf(&dn, "/dev/%s", devname) < 0) { pr_log(LOG_ERR, "I2C: Error creating devpath for %s: %m", devname); return; } memset(b, 0, sizeof(*b)); b->chip_id = chip; b->engine = engine; b->port = port; b->devpath = dn; b->fd = -1; list_add(&bus_list, &b->link); } void i2c_init(void) { #define SYSFS "/sys" /* XXX Find it ? */ DIR *devsdir; struct dirent *devent; char dpath[NAME_MAX]; char busname[256]; char *s; FILE *f; unsigned int chip, engine, port; /* Ensure i2c-dev is loaded */ insert_module("i2c-dev"); /* Get directory of i2c char devs in sysfs */ devsdir = opendir(SYSFS "/class/i2c-dev"); if (!devsdir) { pr_log(LOG_ERR, "I2C: Error opening " SYSFS "/class/i2c-dev: %m"); return; } while ((devent = readdir(devsdir)) != NULL) { if (!strcmp(devent->d_name, ".")) continue; if (!strcmp(devent->d_name, "..")) continue; /* Get bus name */ sprintf(dpath, SYSFS "/class/i2c-dev/%s/name", devent->d_name); f = fopen(dpath, "r"); if (!f) { pr_log(LOG_NOTICE, "I2C: Can't open %s: %m, skipping.", dpath); continue; } s = fgets(busname, sizeof(busname), f); fclose(f); if (!s) { pr_log(LOG_NOTICE, "Failed to read %s, skipping.", dpath); continue; } /* Is this a P8 or Centaur i2c bus ? No -> move on */ if (strncmp(s, "p8_", 3) == 0) sscanf(s, "p8_%x_e%dp%d", &chip, &engine, &port); else if (strncmp(s, "cen_", 4) == 0) sscanf(s, "cen_%x_e%dp%d", &chip, &engine, &port); else continue; pr_log(LOG_INFO, "I2C: Found Chip: %08x engine %d port %d", chip, engine, port); i2c_add_bus(chip, engine, port, devent->d_name); } closedir(devsdir); } skiboot-skiboot-5.1.13/external/opal-prd/i2c.h000066400000000000000000000006171265204436200211210ustar00rootroot00000000000000#ifndef __I2C_H #define __I2C_H int i2c_read(uint32_t chip_id, uint8_t engine, uint8_t port, uint16_t device, uint32_t offset_size, uint32_t offset, uint32_t length, void* data); int i2c_write(uint32_t chip_id, uint8_t engine, uint8_t port, uint16_t device, uint32_t offset_size, uint32_t offset, uint32_t length, void* data); void i2c_init(void); #endif /* __I2c_H */ skiboot-skiboot-5.1.13/external/opal-prd/module.c000066400000000000000000000025201265204436200217170ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * imitations under the License. */ #include #include #include #include #include #include "module.h" #include "opal-prd.h" int insert_module(const char *module) { int status; pid_t pid; pid = fork(); if (!pid) { execlp("modprobe", "modprobe", module, NULL); err(EXIT_FAILURE, "Failed to run modprobe"); } pid = waitpid(pid, &status, 0); if (pid < 0) { pr_log(LOG_ERR, "KMOD: waitpid failed for " "modprobe process: %m"); return -1; } if (!WIFEXITED(status)) { pr_log(LOG_WARNING, "KMOD: modprobe %s: process didn't " "exit cleanly", module); return -1; } if (WEXITSTATUS(status) != 0) { pr_log(LOG_WARNING, "KMOD: modprobe %s failed, status %d", module, WEXITSTATUS(status)); return -1; } return 0; } skiboot-skiboot-5.1.13/external/opal-prd/module.h000066400000000000000000000012621265204436200217260ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * imitations under the License. */ #ifndef MODULES_H #define MODULES_H int insert_module(const char *module); #endif /* MODULES_H */ skiboot-skiboot-5.1.13/external/opal-prd/opal-prd.8000066400000000000000000000036111265204436200220770ustar00rootroot00000000000000.TH opal-prd 8 "" .SH NAME opal-prd \- Processor recovery diagnostics daemon for OpenPower hardware .SH SYNOPSIS .SY opal\-prd .OP \-\-debug .OP \-\-file .OP \-\-pnor .OP daemon . .SY opal\-prd .I .OP arguments .YS .SH DESCRIPTION \fBopal-prd\fP is a daemon that listens for hardware diagnostic events (by listening on the \fI/dev/opal-prd\fP device), and executes firmware-provided executable code to handle these events. Only one instance of the daemon can be running at a time. .PP If no arguments are provided, or the \fIdaemon\fP command is used, then the PRD daemon will be started and will listen for incoming hardware events. Generally, this will be run from init as a background service, and not be run as a user or with user interaction. .PP \fIopal-prd\fP will log to syslog, using the LOG_DAEMON facility. Messages will use the string "opal-prd" for their syslog ident. .PP For debugging, run the daemon with the \fI--debug\fP and \fI--stdio\fP options. This will log to stdout (instead of syslog), and enable extra debugging information. .PP A running opal-prd daemon will also listen for control messages from the user; these are sent using the same \fIopal-prd\fP executable, run with the argument: .RS opal-prd [arguments] .RE .PP Note that the daemon must be running in the background here, as a separate process. .PP Currently, there's one command available, 'occ', for controling the on-chip-controllers. That has 3 possible sub-commands: \fIreset\fP, \fIenable\fP, and \fIdisable\fP. .SH OPTIONS .TP \fB\-\-debug\fR verbose logging for debug information .TP \fB\-\-pnor\fR DEVICE use PNOR MTD device .TP \fB\-\-file\fR FILE use FILE for hostboot runtime code (instead of code exported by firmware) .TP \fB\-\-stdio\fR log to stdio, instead of syslog .SH FILES .PD 0 .B /dev/opal-prd .br .B /run/opal-prd-control .br .PD .SH "SEE ALSO" syslog(3) skiboot-skiboot-5.1.13/external/opal-prd/opal-prd.c000066400000000000000000001304371265204436200221610ustar00rootroot00000000000000/* Copyright 2014-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * imitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opal-prd.h" #include "hostboot-interface.h" #include "module.h" #include "pnor.h" #include "i2c.h" struct prd_range { const char *name; uint64_t physaddr; uint64_t size; void *buf; bool multiple; uint32_t instance; }; struct opal_prd_ctx { int fd; int socket; struct opal_prd_info info; struct prd_range *ranges; int n_ranges; bool fw_range_instances; long page_size; void *code_addr; size_t code_size; bool debug; struct pnor pnor; char *hbrt_file_name; bool use_syslog; bool expert_mode; void (*vlog)(int, const char *, va_list); }; enum control_msg_type { CONTROL_MSG_ENABLE_OCCS = 0x00, CONTROL_MSG_DISABLE_OCCS = 0x01, CONTROL_MSG_TEMP_OCC_RESET = 0x02, CONTROL_MSG_TEMP_OCC_ERROR = 0x03, CONTROL_MSG_ATTR_OVERRIDE = 0x04, CONTROL_MSG_HTMGT_PASSTHRU = 0x05, CONTROL_MSG_RUN_CMD = 0x30, }; struct control_msg { enum control_msg_type type; int response; uint32_t argc; uint32_t data_len; uint8_t data[]; }; #define MAX_CONTROL_MSG_BUF 4096 static struct opal_prd_ctx *ctx; static const char *opal_prd_devnode = "/dev/opal-prd"; static const char *opal_prd_socket = "/run/opal-prd-control"; static const char *hbrt_code_region_name = "ibm,hbrt-code-image"; static const int opal_prd_version = 1; static const uint64_t opal_prd_ipoll = 0xf000000000000000; static const char *ipmi_devnode = "/dev/ipmi0"; static const int ipmi_timeout_ms = 5000; static const char *devicetree_base = "/sys/firmware/devicetree/base"; /* Memory error handling */ static const char *mem_offline_soft = "/sys/devices/system/memory/soft_offline_page"; static const char *mem_offline_hard = "/sys/devices/system/memory/hard_offline_page"; #define ADDR_STRING_SZ 20 /* Hold %16lx */ /* This is the "real" HBRT call table for calling into HBRT as * provided by it. It will be used by the assembly thunk */ struct runtime_interfaces *hservice_runtime; struct runtime_interfaces hservice_runtime_fixed; /* This is the callback table provided by assembly code */ extern struct host_interfaces hinterface; /* Create opd to call hostservice init */ struct func_desc { void *addr; void *toc; } hbrt_entry; static struct prd_range *find_range(const char *name, uint32_t instance) { struct prd_range *range; unsigned int i; for (i = 0; i < ctx->n_ranges; i++) { range = &ctx->ranges[i]; if (strcmp(range->name, name)) continue; if (range->multiple && range->instance != instance) continue; return range; } return NULL; } static void pr_log_stdio(int priority, const char *fmt, va_list ap) { if (!ctx->debug && priority >= LOG_DEBUG) return; vprintf(fmt, ap); printf("\n"); } /* standard logging prefixes: * HBRT: Messages from hostboot runtime code * FW: Interactions with OPAL firmware * IMAGE: HBRT image loading * MEM: Memory failure interface * SCOM: Chip SCOM interface * IPMI: IPMI interface * PNOR: PNOR interface * I2C: i2c interface * OCC: OCC interface * CTRL: User-triggered control events * KMOD: Kernel module functions */ void pr_log(int priority, const char *fmt, ...) { va_list ap; va_start(ap, fmt); ctx->vlog(priority, fmt, ap); va_end(ap); } static void pr_log_nocall(const char *name) { pr_log(LOG_WARNING, "HBRT: Call %s not provided", name); } static void hexdump(const uint8_t *data, uint32_t len) { int i; for (i = 0; i < len; i++) { if (i % 16 == 0) printf("\n"); else if(i % 4 == 0) printf(" "); printf("%02x", data[i]); } printf("\n"); } static void pr_log_daemon_init(void) { if (ctx->use_syslog) { openlog("opal-prd", LOG_NDELAY, LOG_DAEMON); ctx->vlog = vsyslog; } } /* HBRT init wrappers */ extern struct runtime_interfaces *call_hbrt_init(struct host_interfaces *); /* hservice Call wrappers */ extern void call_cxxtestExecute(void *); extern int call_handle_attns(uint64_t i_proc, uint64_t i_ipollStatus, uint64_t i_ipollMask); extern void call_process_occ_error (uint64_t i_chipId); extern int call_enable_attns(void); extern int call_enable_occ_actuation(bool i_occActivation); extern void call_process_occ_reset(uint64_t i_chipId); extern int call_mfg_htmgt_pass_thru(uint16_t i_cmdLength, uint8_t *i_cmdData, uint16_t *o_rspLength, uint8_t *o_rspData); extern int call_apply_attr_override(uint8_t *i_data, size_t size); extern int call_run_command(int argc, const char **argv, char **o_outString); void hservice_puts(const char *str) { pr_log(LOG_INFO, "HBRT: %s", str); } void hservice_assert(void) { pr_log(LOG_ERR, "HBRT: Failed assertion! exiting."); exit(EXIT_FAILURE); } void *hservice_malloc(size_t size) { return malloc(size); } void hservice_free(void *ptr) { free(ptr); } void *hservice_realloc(void *ptr, size_t size) { return realloc(ptr, size); } int hservice_scom_read(uint64_t chip_id, uint64_t addr, void *buf) { int rc; struct opal_prd_scom scom; scom.chip = chip_id; scom.addr = addr; rc = ioctl(ctx->fd, OPAL_PRD_SCOM_READ, &scom); if (rc) { pr_log(LOG_ERR, "SCOM: ioctl read(chip 0x%lx, addr 0x%lx) " "failed: %m", chip_id, addr); return 0; } pr_debug("SCOM: read: chip 0x%lx, addr 0x%lx, val 0x%lx", chip_id, addr, scom.data); *(uint64_t *)buf = htobe64(scom.data); return 0; } int hservice_scom_write(uint64_t chip_id, uint64_t addr, const void *buf) { int rc; struct opal_prd_scom scom; scom.chip = chip_id; scom.addr = addr; scom.data = be64toh(*(uint64_t *)buf); rc = ioctl(ctx->fd, OPAL_PRD_SCOM_WRITE, &scom); if (rc) { pr_log(LOG_ERR, "SCOM: ioctl write(chip 0x%lx, addr 0x%lx) " "failed: %m", chip_id, addr); return 0; } pr_debug("SCOM: write: chip 0x%lx, addr 0x%lx, val 0x%lx", chip_id, addr, scom.data); return 0; } uint64_t hservice_get_reserved_mem(const char *name, uint32_t instance) { struct prd_range *range; pr_debug("IMAGE: hservice_get_reserved_mem: %s, %d", name, instance); range = find_range(name, instance); if (!range) { pr_log(LOG_WARNING, "IMAGE: get_reserved_mem: " "no such range %s", name); return 0; } if (!range->buf) { uint64_t align_physaddr, offset; pr_debug("IMAGE: Mapping 0x%016lx 0x%08lx %s[%d]", range->physaddr, range->size, range->name, range->instance); align_physaddr = range->physaddr & ~(ctx->page_size-1); offset = range->physaddr & (ctx->page_size-1); range->buf = mmap(NULL, range->size, PROT_WRITE | PROT_READ, MAP_SHARED, ctx->fd, align_physaddr); if (range->buf == MAP_FAILED) pr_log(LOG_ERR, "IMAGE: mmap of %s[%d](0x%016lx) failed: %m", name, instance, range->physaddr); else range->buf += offset; } if (range->buf == MAP_FAILED) { pr_log(LOG_WARNING, "IMAGE: get_reserved_mem: %s[%d] has no vaddr", name, instance); return 0; } pr_debug( "IMAGE: hservice_get_reserved_mem: %s[%d](0x%016lx) address %p", name, range->instance, range->physaddr, range->buf); return (uint64_t)range->buf; } void hservice_nanosleep(uint64_t i_seconds, uint64_t i_nano_seconds) { const struct timespec ns = { .tv_sec = i_seconds, .tv_nsec = i_nano_seconds }; nanosleep(&ns, NULL); } int hservice_set_page_execute(void *addr) { /* HBRT calls this on the pages that are already being executed, * nothing to do here */ return -1; } int hservice_clock_gettime(clockid_t i_clkId, struct timespec *o_tp) { struct timespec tmp; int rc; rc = clock_gettime(i_clkId, &tmp); if (rc) return rc; o_tp->tv_sec = htobe64(tmp.tv_sec); o_tp->tv_nsec = htobe64(tmp.tv_nsec); return 0; } int hservice_pnor_read(uint32_t i_proc, const char* i_partitionName, uint64_t i_offset, void* o_data, size_t i_sizeBytes) { return pnor_operation(&ctx->pnor, i_partitionName, i_offset, o_data, i_sizeBytes, PNOR_OP_READ); } int hservice_pnor_write(uint32_t i_proc, const char* i_partitionName, uint64_t i_offset, void* o_data, size_t i_sizeBytes) { return pnor_operation(&ctx->pnor, i_partitionName, i_offset, o_data, i_sizeBytes, PNOR_OP_WRITE); } int hservice_i2c_read(uint64_t i_master, uint16_t i_devAddr, uint32_t i_offsetSize, uint32_t i_offset, uint32_t i_length, void* o_data) { uint32_t chip_id; uint8_t engine, port; chip_id = (i_master & HBRT_I2C_MASTER_CHIP_MASK) >> HBRT_I2C_MASTER_CHIP_SHIFT; engine = (i_master & HBRT_I2C_MASTER_ENGINE_MASK) >> HBRT_I2C_MASTER_ENGINE_SHIFT; port = (i_master & HBRT_I2C_MASTER_PORT_MASK) >> HBRT_I2C_MASTER_PORT_SHIFT; return i2c_read(chip_id, engine, port, i_devAddr, i_offsetSize, i_offset, i_length, o_data); } int hservice_i2c_write(uint64_t i_master, uint16_t i_devAddr, uint32_t i_offsetSize, uint32_t i_offset, uint32_t i_length, void* i_data) { uint32_t chip_id; uint8_t engine, port; chip_id = (i_master & HBRT_I2C_MASTER_CHIP_MASK) >> HBRT_I2C_MASTER_CHIP_SHIFT; engine = (i_master & HBRT_I2C_MASTER_ENGINE_MASK) >> HBRT_I2C_MASTER_ENGINE_SHIFT; port = (i_master & HBRT_I2C_MASTER_PORT_MASK) >> HBRT_I2C_MASTER_PORT_SHIFT; return i2c_write(chip_id, engine, port, i_devAddr, i_offsetSize, i_offset, i_length, i_data); } static void ipmi_init(struct opal_prd_ctx *ctx) { insert_module("ipmi_devintf"); } static int ipmi_send(int fd, uint8_t netfn, uint8_t cmd, long seq, uint8_t *buf, size_t len) { struct ipmi_system_interface_addr addr; struct ipmi_req req; int rc; memset(&addr, 0, sizeof(addr)); addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; memset(&req, 0, sizeof(req)); req.addr = (unsigned char *)&addr; req.addr_len = sizeof(addr); req.msgid = seq; req.msg.netfn = netfn; req.msg.cmd = cmd; req.msg.data = buf; req.msg.data_len = len; rc = ioctl(fd, IPMICTL_SEND_COMMAND, &req); if (rc < 0) return -1; return 0; } static int ipmi_recv(int fd, uint8_t *netfn, uint8_t *cmd, long *seq, uint8_t *buf, size_t *len) { struct ipmi_recv recv; struct ipmi_addr addr; int rc; recv.addr = (unsigned char *)&addr; recv.addr_len = sizeof(addr); recv.msg.data = buf; recv.msg.data_len = *len; rc = ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv); if (rc < 0 && errno != EMSGSIZE) { pr_log(LOG_WARNING, "IPMI: recv (%zd bytes) failed: %m", *len); return -1; } else if (rc < 0 && errno == EMSGSIZE) { pr_log(LOG_NOTICE, "IPMI: truncated message (netfn %d, cmd %d, " "size %zd), continuing anyway", recv.msg.netfn, recv.msg.cmd, *len); } *netfn = recv.msg.netfn; *cmd = recv.msg.cmd; *seq = recv.msgid; *len = recv.msg.data_len; return 0; } int hservice_ipmi_msg(uint8_t netfn, uint8_t cmd, void *tx_buf, size_t tx_size, void *rx_buf, size_t *rx_size) { struct timeval start, now, delta; struct pollfd pollfds[1]; static long seq; size_t size; int rc, fd; size = be64toh(*rx_size); fd = open(ipmi_devnode, O_RDWR); if (fd < 0) { pr_log(LOG_WARNING, "IPMI: Failed to open IPMI device %s: %m", ipmi_devnode); return -1; } seq++; pr_debug("IPMI: sending %zd bytes (netfn 0x%02x, cmd 0x%02x)", tx_size, netfn, cmd); rc = ipmi_send(fd, netfn, cmd, seq, tx_buf, tx_size); if (rc) { pr_log(LOG_WARNING, "IPMI: send failed"); goto out; } gettimeofday(&start, NULL); pollfds[0].fd = fd; pollfds[0].events = POLLIN; for (;;) { long rx_seq; int timeout; gettimeofday(&now, NULL); timersub(&now, &start, &delta); timeout = ipmi_timeout_ms - ((delta.tv_sec * 1000) + (delta.tv_usec / 1000)); if (timeout < 0) timeout = 0; rc = poll(pollfds, 1, timeout); if (rc < 0) { pr_log(LOG_ERR, "IPMI: poll(%s) failed: %m", ipmi_devnode); break; } if (rc == 0) { pr_log(LOG_WARNING, "IPMI: response timeout (>%dms)", ipmi_timeout_ms); rc = -1; break; } rc = ipmi_recv(fd, &netfn, &cmd, &rx_seq, rx_buf, &size); if (rc) break; if (seq != rx_seq) { pr_log(LOG_NOTICE, "IPMI: out-of-sequence reply: %ld, " "expected %ld. Dropping message.", rx_seq, seq); continue; } pr_debug("IPMI: received %zd bytes", tx_size); *rx_size = be64toh(size); rc = 0; break; } out: close(fd); return rc; } int hservice_memory_error(uint64_t i_start_addr, uint64_t i_endAddr, enum MemoryError_t i_errorType) { const char *sysfsfile, *typestr; char buf[ADDR_STRING_SZ]; int memfd, rc, n; uint64_t addr; switch(i_errorType) { case MEMORY_ERROR_CE: sysfsfile = mem_offline_soft; typestr = "correctable"; break; case MEMORY_ERROR_UE: sysfsfile = mem_offline_hard; typestr = "uncorrectable"; break; default: pr_log(LOG_WARNING, "MEM: Invalid memory error type %d", i_errorType); return -1; } pr_log(LOG_ERR, "MEM: Memory error: range %016lx-%016lx, type: %s", i_start_addr, i_endAddr, typestr); memfd = open(sysfsfile, O_WRONLY); if (memfd < 0) { pr_log(LOG_CRIT, "MEM: Failed to offline memory! " "Unable to open sysfs node %s: %m", sysfsfile); return -1; } for (addr = i_start_addr; addr <= i_endAddr; addr += ctx->page_size) { n = snprintf(buf, ADDR_STRING_SZ, "0x%lx", addr); rc = write(memfd, buf, n); if (rc != n) { pr_log(LOG_CRIT, "MEM: Failed to offline memory! " "page addr: %016lx type: %d: %m", addr, i_errorType); return rc; } } return 0; } void hservices_init(struct opal_prd_ctx *ctx, void *code) { uint64_t *s, *d; int i, sz; pr_debug("IMAGE: code address: %p", code); /* We enter at 0x100 into the image. */ /* Load func desc in BE since we reverse it in thunk */ hbrt_entry.addr = (void *)htobe64((unsigned long)code + 0x100); hbrt_entry.toc = 0; /* No toc for init entry point */ if (memcmp(code, "HBRTVERS", 8) != 0) pr_log(LOG_ERR, "IMAGE: Bad signature for " "ibm,hbrt-code-image! exiting"); pr_debug("IMAGE: calling ibm,hbrt_init()"); hservice_runtime = call_hbrt_init(&hinterface); pr_log(LOG_NOTICE, "IMAGE: hbrt_init complete, version %016lx", hservice_runtime->interface_version); sz = sizeof(struct runtime_interfaces)/sizeof(uint64_t); s = (uint64_t *)hservice_runtime; d = (uint64_t *)&hservice_runtime_fixed; /* Byte swap the function pointers */ for (i = 0; i < sz; i++) d[i] = be64toh(s[i]); } static void fixup_hinterface_table(void) { uint64_t *t64; unsigned int i, sz; /* Swap interface version */ hinterface.interface_version = htobe64(hinterface.interface_version); /* Swap OPDs */ sz = sizeof(struct host_interfaces) / sizeof(uint64_t); t64 = (uint64_t *)&hinterface; for (i = 1; i < sz; i++) { uint64_t *opd = (uint64_t *)t64[i]; if (!opd) continue; t64[i] = htobe64(t64[i]); opd[0] = htobe64(opd[0]); opd[1] = htobe64(opd[1]); opd[2] = htobe64(opd[2]); } } static int map_hbrt_file(struct opal_prd_ctx *ctx, const char *name) { struct stat statbuf; int fd, rc; void *buf; fd = open(name, O_RDONLY); if (fd < 0) { pr_log(LOG_ERR, "IMAGE: HBRT file open(%s) failed: %m", name); return -1; } rc = fstat(fd, &statbuf); if (rc < 0) { pr_log(LOG_ERR, "IMAGE: HBRT file fstat(%s) failed: %m", name); close(fd); return -1; } buf = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); close(fd); if (buf == MAP_FAILED) { pr_log(LOG_ERR, "IMAGE: HBRT file mmap(%s, 0x%zx) failed: %m", name, statbuf.st_size); return -1; } ctx->code_addr = buf; ctx->code_size = statbuf.st_size; return -0; } static int map_hbrt_physmem(struct opal_prd_ctx *ctx, const char *name) { struct prd_range *range; void *buf; range = find_range(name, 0); if (!range) { pr_log(LOG_ERR, "IMAGE: can't find code region %s", name); return -1; } buf = mmap(NULL, range->size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, ctx->fd, range->physaddr); if (buf == MAP_FAILED) { pr_log(LOG_ERR, "IMAGE: mmap(range:%s, " "phys:0x%016lx, size:0x%016lx) failed: %m", name, range->physaddr, range->size); return -1; } ctx->code_addr = buf; ctx->code_size = range->size; return 0; } static void dump_hbrt_map(struct opal_prd_ctx *ctx) { const char *dump_name = "hbrt.bin"; int fd, rc; if (!ctx->debug) return; fd = open(dump_name, O_WRONLY | O_CREAT, 0644); if (fd < 0) { pr_log(LOG_NOTICE, "IMAGE: couldn't debug image %s for writing", dump_name); return; } rc = ftruncate(fd, 0); if (rc < 0) { pr_log(LOG_NOTICE, "IMAGE: couldn't truncate image %s for writing", dump_name); return; } rc = write(fd, ctx->code_addr, ctx->code_size); close(fd); if (rc != ctx->code_size) pr_log(LOG_NOTICE, "IMAGE: write to %s failed: %m", dump_name); else pr_debug("IMAGE: dumped HBRT binary to %s", dump_name); } static int open_and_read(const char *path, void **bufp, int *lenp) { struct stat statbuf; int fd, rc, bytes; void *buf; fd = open(path, O_RDONLY); if (fd < 0) return -1; rc = fstat(fd, &statbuf); if (rc) { close(fd); return -1; } buf = malloc(statbuf.st_size); for (rc = bytes = 0; bytes < statbuf.st_size; bytes += rc) { rc = read(fd, buf + bytes, statbuf.st_size - bytes); if (rc < 0) { if (errno == EINTR) continue; break; } else if (rc == 0) break; } if (bytes == statbuf.st_size) rc = 0; if (rc == 0) { if (lenp) *lenp = bytes; if (bufp) *bufp = buf; } else { free(buf); } close(fd); return rc == 0 ? 0 : -1; } static int prd_init_one_range(struct opal_prd_ctx *ctx, const char *path, struct dirent *dirent) { char *label_path, *reg_path, *instance_path; struct prd_range *range; int label_len, len, rc; __be64 *reg; char *label; void *buf; rc = asprintf(&label_path, "%s/%s/ibm,prd-label", path, dirent->d_name); if (rc < 0) { pr_log(LOG_ERR, "FW: error creating 'ibm,prd-label' path " "node: %m"); return -1; } rc = asprintf(&instance_path, "%s/%s/ibm,prd-instance", path, dirent->d_name); if (rc < 0) { pr_log(LOG_ERR, "FW: error creating 'ibm,prd-instance' path " "node: %m"); return -1; } rc = asprintf(®_path, "%s/%s/reg", path, dirent->d_name); if (rc < 0) { pr_log(LOG_ERR, "FW: error creating 'reg' path " " node: %m"); return -1; } reg = NULL; label = NULL; rc = -1; rc = open_and_read(label_path, &buf, &label_len); if (rc) goto out_free; label = buf; if (label[label_len-1] != '\0') pr_log(LOG_INFO, "FW: node %s has invalid ibm,prd-label - " "not nul-terminated", dirent->d_name); rc = open_and_read(reg_path, &buf, &len); if (rc) goto out_free; reg = buf; if (len != 2 * sizeof(*reg)) { pr_log(LOG_ERR, "FW: node %s has invalid 'reg' size: %d", dirent->d_name, len); goto out_free; } ctx->ranges = realloc(ctx->ranges, ++ctx->n_ranges * sizeof(*range)); range = &ctx->ranges[ctx->n_ranges - 1]; range->name = strndup(label, label_len); range->physaddr = be64toh(reg[0]); range->size = be64toh(reg[1]); range->buf = NULL; range->multiple = false; range->instance = 0; /* optional instance */ rc = open_and_read(instance_path, &buf, &len); if (!rc && len == sizeof(uint32_t)) { range->multiple = true; range->instance = be32toh(*(uint32_t *)buf); ctx->fw_range_instances = true; } rc = 0; out_free: free(reg); free(label); free(instance_path); free(reg_path); free(label_path); return rc; } static int compare_ranges(const void *ap, const void *bp) { const struct prd_range *a = ap, *b = bp; int rc; rc = strcmp(a->name, b->name); if (rc) return rc; if (a->physaddr < b->physaddr) return -1; else if (a->physaddr > b->physaddr) return 1; return 0; } static void assign_range_instances(struct opal_prd_ctx *ctx) { int i; if (!ctx->n_ranges) return; ctx->ranges[0].multiple = false; ctx->ranges[0].instance = 0; for (i = 1; i < ctx->n_ranges; i++) { struct prd_range *cur, *prev; cur = &ctx->ranges[i]; prev = &ctx->ranges[i-1]; if (!strcmp(cur->name, prev->name)) { prev->multiple = true; cur->multiple = true; cur->instance = prev->instance + 1; } else { cur->multiple = false; cur->instance = 0; } } } static void print_ranges(struct opal_prd_ctx *ctx) { int i; if (ctx->n_ranges == 0) pr_log(LOG_INFO, "FW: No PRD ranges"); pr_log(LOG_DEBUG, "FW: %d PRD ranges, instances assigned by %s", ctx->n_ranges, ctx->fw_range_instances ? "firmware" : "userspace"); for (i = 0; i < ctx->n_ranges; i++) { struct prd_range *range = &ctx->ranges[i]; char instance_str[20]; if (range->multiple) snprintf(instance_str, sizeof(instance_str), " [%d]", range->instance); else instance_str[0] = '\0'; pr_log(LOG_DEBUG, "FW: %016lx-%016lx %s%s", range->physaddr, range->physaddr + range->size - 1, range->name, instance_str); } } static int prd_init_ranges(struct opal_prd_ctx *ctx) { struct dirent *dirent; char *path; DIR *dir; int rc; rc = asprintf(&path, "%s/reserved-memory", devicetree_base); if (rc < 0) { pr_log(LOG_ERR, "FW: error creating 'reserved-memory' path " "node: %m"); return -1; } rc = -1; dir = opendir(path); if (!dir) { pr_log(LOG_ERR, "FW: can't open reserved-memory device-tree " "node: %m"); goto out_free; } for (;;) { dirent = readdir(dir); if (!dirent) break; prd_init_one_range(ctx, path, dirent); } rc = 0; /* sort ranges and assign instance numbers for duplicates (if the * firmware doesn't number instances for us) */ qsort(ctx->ranges, ctx->n_ranges, sizeof(struct prd_range), compare_ranges); if (!ctx->fw_range_instances) assign_range_instances(ctx); print_ranges(ctx); out_free: free(path); closedir(dir); return rc; } bool find_string(const char *buffer, size_t len, const char *s) { const char *c, *end; if (!buffer) return false; c = buffer; end = c + len; while (c < end) { if (!strcasecmp(s, c)) return true; c += strlen(c) + 1; } return false; } static int is_prd_supported(void) { char *path; int rc; int len; char *buf; rc = asprintf(&path, "%s/ibm,opal/diagnostics/compatible", devicetree_base); if (rc < 0) { pr_log(LOG_ERR, "FW: error creating 'compatible' node path: %m"); return -1; } rc = open_and_read(path, (void *) &buf, &len); if (rc) goto out_free; if (buf[len - 1] != '\0') pr_log(LOG_INFO, "FW: node %s is not nul-terminated", path); rc = find_string(buf, len, "ibm,opal-prd") ? 0 : -1; free(buf); out_free: free(path); return rc; } static int prd_init(struct opal_prd_ctx *ctx) { int rc; ctx->page_size = sysconf(_SC_PAGE_SIZE); /* set up the device, and do our get_info ioctl */ ctx->fd = open(opal_prd_devnode, O_RDWR); if (ctx->fd < 0) { pr_log(LOG_ERR, "FW: Can't open PRD device %s: %m", opal_prd_devnode); return -1; } rc = ioctl(ctx->fd, OPAL_PRD_GET_INFO, &ctx->info); if (rc) { pr_log(LOG_ERR, "FW: Can't query PRD information: %m"); return -1; } rc = prd_init_ranges(ctx); if (rc) { pr_log(LOG_ERR, "FW: can't parse PRD memory information"); return -1; } return 0; } static int handle_msg_attn(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg) { uint64_t proc, ipoll_mask, ipoll_status; int rc; proc = be64toh(msg->attn.proc); ipoll_status = be64toh(msg->attn.ipoll_status); ipoll_mask = be64toh(msg->attn.ipoll_mask); if (!hservice_runtime->handle_attns) { pr_log_nocall("handle_attns"); return -1; } rc = call_handle_attns(proc, ipoll_status, ipoll_mask); if (rc) { pr_log(LOG_ERR, "HBRT: handle_attns(%lx,%lx,%lx) failed, rc %d", proc, ipoll_status, ipoll_mask, rc); return -1; } /* send the response */ msg->hdr.type = OPAL_PRD_MSG_TYPE_ATTN_ACK; msg->hdr.size = htobe16(sizeof(*msg)); msg->attn_ack.proc = htobe64(proc); msg->attn_ack.ipoll_ack = htobe64(ipoll_status); rc = write(ctx->fd, msg, sizeof(*msg)); if (rc != sizeof(*msg)) { pr_log(LOG_WARNING, "FW: Failed to send ATTN_ACK message: %m"); return -1; } return 0; } static int handle_msg_occ_error(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg) { uint32_t proc; proc = be64toh(msg->occ_error.chip); if (!hservice_runtime->process_occ_error) { pr_log_nocall("process_occ_error"); return -1; } call_process_occ_error(proc); return 0; } static int handle_msg_occ_reset(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg) { uint32_t proc; proc = be64toh(msg->occ_reset.chip); if (!hservice_runtime->process_occ_reset) { pr_log_nocall("process_occ_reset"); return -1; } call_process_occ_reset(proc); return 0; } static int handle_prd_msg(struct opal_prd_ctx *ctx) { struct opal_prd_msg msg; int size; int rc; rc = read(ctx->fd, &msg, sizeof(msg)); if (rc < 0 && errno == EAGAIN) return -1; if (rc != sizeof(msg)) { pr_log(LOG_WARNING, "FW: Error reading events from OPAL: %m"); return -1; } size = htobe16(msg.hdr.size); if (size < sizeof(msg)) { pr_log(LOG_ERR, "FW: Mismatched message size " "between opal-prd and firmware " "(%d from FW, %zd expected)", size, sizeof(msg)); return -1; } switch (msg.hdr.type) { case OPAL_PRD_MSG_TYPE_ATTN: rc = handle_msg_attn(ctx, &msg); break; case OPAL_PRD_MSG_TYPE_OCC_RESET: rc = handle_msg_occ_reset(ctx, &msg); break; case OPAL_PRD_MSG_TYPE_OCC_ERROR: rc = handle_msg_occ_error(ctx, &msg); break; default: pr_log(LOG_WARNING, "Invalid incoming message type 0x%x", msg.hdr.type); return -1; } return 0; } static void handle_prd_control_occ_error(struct control_msg *msg) { if (!hservice_runtime->process_occ_error) { pr_log_nocall("process_occ_error"); return; } pr_debug("CTRL: calling process_occ_error(0)"); call_process_occ_error(0); msg->data_len = 0; msg->response = 0; } static void handle_prd_control_occ_reset(struct control_msg *msg) { if (!hservice_runtime->process_occ_reset) { pr_log_nocall("process_occ_reset"); return; } pr_debug("CTRL: calling process_occ_reset(0)"); call_process_occ_reset(0); msg->data_len = 0; msg->response = 0; } static void handle_prd_control_occ_actuation(struct control_msg *msg, bool enable) { if (!hservice_runtime->enable_occ_actuation) { pr_log_nocall("enable_occ_actuation"); return; } pr_debug("CTRL: calling enable_occ_actuation(%s)", enable ? "true" : "false"); msg->data_len = 0; msg->response = call_enable_occ_actuation(enable); } static void handle_prd_control_attr_override(struct control_msg *send_msg, struct control_msg *recv_msg) { if (!hservice_runtime->apply_attr_override) { pr_log_nocall("apply_attr_override"); return; } pr_debug("CTRL: calling apply_attr_override"); send_msg->response = call_apply_attr_override( recv_msg->data, recv_msg->data_len); send_msg->data_len = 0; } static void handle_prd_control_htmgt_passthru(struct control_msg *send_msg, struct control_msg *recv_msg) { uint16_t rsp_len; if (!hservice_runtime->mfg_htmgt_pass_thru) { pr_log_nocall("mfg_htmgt_pass_thru"); return; } pr_debug("CTRL: calling mfg_htmgt_pass_thru"); send_msg->response = call_mfg_htmgt_pass_thru(recv_msg->data_len, recv_msg->data, &rsp_len, send_msg->data); send_msg->data_len = be16toh(rsp_len); if (send_msg->data_len > MAX_CONTROL_MSG_BUF) { pr_log(LOG_ERR, "CTRL: response buffer overrun, data len: %d", send_msg->data_len); send_msg->data_len = MAX_CONTROL_MSG_BUF; } } static void handle_prd_control_run_cmd(struct control_msg *send_msg, struct control_msg *recv_msg) { char *runcmd_output, *s; const char **argv; int i, argc; size_t size; if (!hservice_runtime->run_command) { pr_log_nocall("run_command"); return; } argc = recv_msg->argc; pr_debug("CTRL: run_command, argc:%d\n", argc); argv = malloc(argc * sizeof(*argv)); if (!argv) { pr_log(LOG_ERR, "CTRL: argv buffer malloc failed: %m"); return; } s = (char *)recv_msg->data; size = 0; for (i = 0; i < argc; i++) { argv[i] = (char *)htobe64((uint64_t)&s[size]); size += (strlen(&s[size]) + 1); } /* Call HBRT */ send_msg->response = call_run_command(argc, argv, &runcmd_output); runcmd_output = (char *)be64toh((uint64_t)runcmd_output); free(argv); s = (char *)send_msg->data; if (runcmd_output) { size = strlen(runcmd_output); if (size >= MAX_CONTROL_MSG_BUF) { pr_log(LOG_WARNING, "CTRL: output message truncated"); runcmd_output[MAX_CONTROL_MSG_BUF] = '\0'; size = MAX_CONTROL_MSG_BUF; } strcpy(s, runcmd_output); send_msg->data_len = size + 1; free(runcmd_output); } else { strcpy(s, "Null"); send_msg->data_len = strlen("Null") + 1; } } static void handle_prd_control(struct opal_prd_ctx *ctx, int fd) { struct control_msg msg, *recv_msg, *send_msg; bool enabled = false; int rc, size; /* Default reply, in the error path */ send_msg = &msg; /* Peek into the socket to ascertain the size of the available data */ rc = recv(fd, &msg, sizeof(msg), MSG_PEEK); if (rc != sizeof(msg)) { pr_log(LOG_WARNING, "CTRL: failed to receive control " "message: %m"); msg.response = -1; msg.data_len = 0; goto out_send; } size = sizeof(*recv_msg) + msg.data_len; /* Default reply, in the error path */ msg.data_len = 0; msg.response = -1; recv_msg = malloc(size); if (!recv_msg) { pr_log(LOG_ERR, "CTRL: message buffer malloc failed: %m"); goto out_send; } rc = recv(fd, recv_msg, size, MSG_TRUNC); if (rc != size) { pr_log(LOG_WARNING, "CTRL: failed to receive control " "message: %m"); goto out_free_recv; } send_msg = malloc(sizeof(*send_msg) + MAX_CONTROL_MSG_BUF); if (!send_msg) { pr_log(LOG_ERR, "CTRL: message buffer malloc failed: %m"); send_msg = &msg; goto out_free_recv; } send_msg->type = recv_msg->type; send_msg->response = -1; switch (recv_msg->type) { case CONTROL_MSG_ENABLE_OCCS: enabled = true; /* fall through */ case CONTROL_MSG_DISABLE_OCCS: handle_prd_control_occ_actuation(send_msg, enabled); break; case CONTROL_MSG_TEMP_OCC_RESET: handle_prd_control_occ_reset(send_msg); break; case CONTROL_MSG_TEMP_OCC_ERROR: handle_prd_control_occ_error(send_msg); break; case CONTROL_MSG_ATTR_OVERRIDE: handle_prd_control_attr_override(send_msg, recv_msg); break; case CONTROL_MSG_HTMGT_PASSTHRU: handle_prd_control_htmgt_passthru(send_msg, recv_msg); break; case CONTROL_MSG_RUN_CMD: handle_prd_control_run_cmd(send_msg, recv_msg); break; default: pr_log(LOG_WARNING, "CTRL: Unknown control message action %d", recv_msg->type); send_msg->data_len = 0; break; } out_free_recv: free(recv_msg); out_send: size = sizeof(*send_msg) + send_msg->data_len; rc = send(fd, send_msg, size, MSG_DONTWAIT | MSG_NOSIGNAL); if (rc && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EPIPE)) pr_debug("CTRL: control send() returned %d, ignoring failure", rc); else if (rc != size) pr_log(LOG_NOTICE, "CTRL: Failed to send control response: %m"); if (send_msg != &msg) free(send_msg); } static int run_attn_loop(struct opal_prd_ctx *ctx) { struct pollfd pollfds[2]; struct opal_prd_msg msg; int rc, fd; if (hservice_runtime->enable_attns) { pr_debug("HBRT: calling enable_attns"); rc = call_enable_attns(); if (rc) { pr_log(LOG_ERR, "HBRT: enable_attns() failed, " "aborting"); return -1; } } /* send init message, to unmask interrupts */ msg.hdr.type = OPAL_PRD_MSG_TYPE_INIT; msg.hdr.size = htobe16(sizeof(msg)); msg.init.version = htobe64(opal_prd_version); msg.init.ipoll = htobe64(opal_prd_ipoll); pr_debug("FW: writing init message"); rc = write(ctx->fd, &msg, sizeof(msg)); if (rc != sizeof(msg)) { pr_log(LOG_ERR, "FW: Init message failed: %m. Aborting."); return -1; } pollfds[0].fd = ctx->fd; pollfds[0].events = POLLIN | POLLERR; pollfds[1].fd = ctx->socket; pollfds[1].events = POLLIN | POLLERR; for (;;) { rc = poll(pollfds, 2, -1); if (rc < 0) { pr_log(LOG_ERR, "FW: event poll failed: %m"); exit(EXIT_FAILURE); } if (!rc) continue; if (pollfds[0].revents & POLLIN) handle_prd_msg(ctx); if (pollfds[1].revents & POLLIN) { fd = accept(ctx->socket, NULL, NULL); if (fd < 0) { pr_log(LOG_NOTICE, "CTRL: accept failed: %m"); continue; } handle_prd_control(ctx, fd); close(fd); } } return 0; } static int init_control_socket(struct opal_prd_ctx *ctx) { struct sockaddr_un addr; int fd, rc; unlink(opal_prd_socket); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, opal_prd_socket); fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) { pr_log(LOG_WARNING, "CTRL: Can't open control socket %s: %m", opal_prd_socket); return -1; } rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if (rc) { pr_log(LOG_WARNING, "CTRL: Can't bind control socket %s: %m", opal_prd_socket); close(fd); return -1; } rc = listen(fd, 0); if (rc) { pr_log(LOG_WARNING, "CTRL: Can't listen on " "control socket %s: %m", opal_prd_socket); close(fd); return -1; } pr_log(LOG_INFO, "CTRL: Listening on control socket %s", opal_prd_socket); ctx->socket = fd; return 0; } static int run_prd_daemon(struct opal_prd_ctx *ctx) { int rc; /* log to syslog */ pr_log_daemon_init(); ctx->fd = -1; ctx->socket = -1; i2c_init(); #ifdef DEBUG_I2C { uint8_t foo[128]; int i; rc = i2c_read(0, 1, 2, 0x50, 2, 0x10, 128, foo); pr_debug("I2C: read rc: %d", rc); for (i = 0; i < sizeof(foo); i += 8) { pr_debug("I2C: %02x %02x %02x %02x %02x %02x %02x %02x", foo[i + 0], foo[i + 1], foo[i + 2], foo[i + 3], foo[i + 4], foo[i + 5], foo[i + 6], foo[i + 7]); } } #endif rc = init_control_socket(ctx); if (rc) { pr_log(LOG_WARNING, "CTRL: Error initialising PRD control: %m"); goto out_close; } rc = prd_init(ctx); if (rc) { pr_log(LOG_ERR, "FW: Error initialising PRD channel"); goto out_close; } if (ctx->hbrt_file_name) { rc = map_hbrt_file(ctx, ctx->hbrt_file_name); if (rc) { pr_log(LOG_ERR, "IMAGE: Can't access hbrt file %s", ctx->hbrt_file_name); goto out_close; } } else { rc = map_hbrt_physmem(ctx, hbrt_code_region_name); if (rc) { pr_log(LOG_ERR, "IMAGE: Can't access hbrt " "physical memory"); goto out_close; } dump_hbrt_map(ctx); } pr_debug("IMAGE: hbrt map at %p, size 0x%zx", ctx->code_addr, ctx->code_size); fixup_hinterface_table(); if (ctx->pnor.path) { rc = pnor_init(&ctx->pnor); if (rc) { pr_log(LOG_ERR, "PNOR: Failed to open pnor: %m"); goto out_close; } } ipmi_init(ctx); pr_debug("HBRT: calling hservices_init"); hservices_init(ctx, ctx->code_addr); pr_debug("HBRT: hservices_init done"); /* Test a scom */ if (ctx->debug) { uint64_t val; pr_debug("SCOM: trying scom read"); fflush(stdout); hservice_scom_read(0x00, 0xf000f, &val); pr_debug("SCOM: f00f: %lx", be64toh(val)); } run_attn_loop(ctx); rc = 0; out_close: pnor_close(&ctx->pnor); if (ctx->fd != -1) close(ctx->fd); if (ctx->socket != -1) close(ctx->socket); return rc; } static int send_prd_control(struct control_msg *send_msg, struct control_msg **recv_msg) { struct sockaddr_un addr; struct control_msg *msg; int sd, rc, size; sd = socket(AF_UNIX, SOCK_STREAM, 0); if (!sd) { pr_log(LOG_ERR, "CTRL: Failed to create control socket: %m"); return -1; } addr.sun_family = AF_UNIX; strcpy(addr.sun_path, opal_prd_socket); rc = connect(sd, (struct sockaddr *)&addr, sizeof(addr)); if (rc) { pr_log(LOG_ERR, "CTRL: Failed to connect to prd daemon: %m"); goto out_close; } size = sizeof(*send_msg) + send_msg->data_len; rc = send(sd, send_msg, size, 0); if (rc != size) { pr_log(LOG_ERR, "CTRL: Failed to send control message: %m"); rc = -1; goto out_close; } size = sizeof(*msg) + MAX_CONTROL_MSG_BUF; msg = malloc(size); if (!msg) { pr_log(LOG_ERR, "CTRL: msg buffer malloc failed: %m"); rc = -1; goto out_close; } *recv_msg = msg; /* wait for our reply */ rc = recv(sd, msg, size, 0); if (rc < 0) { pr_log(LOG_ERR, "CTRL: Failed to receive control message: %m"); goto out_close; } else if (rc != (sizeof(*msg) + msg->data_len)) { pr_log(LOG_WARNING, "CTRL: Short read from control socket"); rc = -1; goto out_close; } rc = msg->response; out_close: close(sd); return rc; } static int send_occ_control(struct opal_prd_ctx *ctx, const char *str) { struct control_msg send_msg, *recv_msg = NULL; int rc; memset(&send_msg, 0, sizeof(send_msg)); if (!strcmp(str, "enable")) send_msg.type = CONTROL_MSG_ENABLE_OCCS; else if (!strcmp(str, "disable")) send_msg.type = CONTROL_MSG_DISABLE_OCCS; else if (!strcmp(str, "reset")) send_msg.type = CONTROL_MSG_TEMP_OCC_RESET; else if (!strcmp(str, "process-error")) send_msg.type = CONTROL_MSG_TEMP_OCC_ERROR; else { pr_log(LOG_ERR, "CTRL: Invalid OCC action '%s'", str); return -1; } rc = send_prd_control(&send_msg, &recv_msg); if (recv_msg) { if (recv_msg->response || ctx->debug) pr_debug("CTRL: OCC action %s returned status %d", str, recv_msg->response); free(recv_msg); } return rc; } static int send_attr_override(struct opal_prd_ctx *ctx, uint32_t argc, char *argv[]) { struct control_msg *send_msg, *recv_msg = NULL; struct stat statbuf; size_t sz; FILE *fd; int rc; rc = stat(argv[0], &statbuf); if (rc) { pr_log(LOG_ERR, "CTRL: stat() failed on the file: %m"); return -1; } send_msg = malloc(sizeof(*send_msg) + statbuf.st_size); if (!send_msg) { pr_log(LOG_ERR, "CTRL: msg buffer malloc failed: %m"); return -1; } send_msg->type = CONTROL_MSG_ATTR_OVERRIDE; send_msg->data_len = statbuf.st_size; fd = fopen(argv[0], "r"); if (!fd) { pr_log(LOG_NOTICE, "CTRL: can't open %s: %m", argv[0]); rc = -1; goto out_free; } sz = fread(send_msg->data, 1, send_msg->data_len, fd); fclose(fd); if (sz != statbuf.st_size) { pr_log(LOG_ERR, "CTRL: short read from the file"); rc = -1; goto out_free; } rc = send_prd_control(send_msg, &recv_msg); if (recv_msg) { if (recv_msg->response || ctx->debug) pr_debug("CTRL: attribute override returned status %d", recv_msg->response); free(recv_msg); } out_free: free(send_msg); return rc; } static int send_htmgt_passthru(struct opal_prd_ctx *ctx, int argc, char *argv[]) { struct control_msg *send_msg, *recv_msg = NULL; int rc, i; if (!ctx->expert_mode) { pr_log(LOG_WARNING, "CTRL: need to be in expert mode"); return -1; } send_msg = malloc(sizeof(*send_msg) + argc); if (!send_msg) { pr_log(LOG_ERR, "CTRL: message buffer malloc failed: %m"); return -1; } send_msg->type = CONTROL_MSG_HTMGT_PASSTHRU; send_msg->data_len = argc; if (ctx->debug) pr_debug("CTRL: HTMGT passthru arguments:"); for (i = 0; i < argc; i++) { if (ctx->debug) pr_debug("argv[%d] = %s", i, argv[i]); sscanf(argv[i], "%hhx", &send_msg->data[i]); } rc = send_prd_control(send_msg, &recv_msg); free(send_msg); if (recv_msg) { if (recv_msg->response || ctx->debug) pr_debug("CTRL: HTMGT passthru returned status %d", recv_msg->response); if (recv_msg->response == 0 && recv_msg->data_len) hexdump(recv_msg->data, recv_msg->data_len); free(recv_msg); } return rc; } static int send_run_command(struct opal_prd_ctx *ctx, int argc, char *argv[]) { struct control_msg *send_msg, *recv_msg = NULL; uint32_t size = 0; int rc, i; char *s; if (!ctx->expert_mode) { pr_log(LOG_WARNING, "CTRL: need to be in expert mode"); return -1; } if (ctx->debug) { pr_debug("CTRL: run command arguments:"); for (i=0; i < argc; i++) pr_debug("argv[%d] = %s", i, argv[i]); } for (i = 0; i < argc; i++) size += (strlen(argv[i]) + 1); send_msg = malloc(sizeof(*send_msg) + size); if (!send_msg) { pr_log(LOG_ERR, "CTRL: msg buffer malloc failed: %m"); return -1; } /* Setup message */ send_msg->type = CONTROL_MSG_RUN_CMD; send_msg->argc = argc; send_msg->data_len = size; s = (char *)send_msg->data; for (i = 0; i < argc; i++) { strcpy(s, argv[i]); s = s + strlen(argv[i]) + 1; } rc = send_prd_control(send_msg, &recv_msg); free(send_msg); if (recv_msg) { if (!rc) pr_log(LOG_INFO, "Received: %s", recv_msg->data); if (recv_msg->response || ctx->debug) pr_debug("CTRL: run command returned status %d", recv_msg->response); free(recv_msg); } return rc; } static void usage(const char *progname) { printf("Usage:\n"); printf("\t%s [--debug] [--file ] [--pnor ]\n", progname); printf("\t%s occ \n", progname); printf("\t%s htmgt-passthru \n", progname); printf("\t%s override \n", progname); printf("\t%s run [arg 0] [arg 1]..[arg n]\n", progname); printf("\n"); printf("Options:\n" "\t--debug verbose logging for debug information\n" "\t--pnor DEVICE use PNOR MTD device\n" "\t--file FILE use FILE for hostboot runtime code (instead of code\n" "\t exported by firmware)\n" "\t--stdio log to stdio, instead of syslog\n"); } static void print_version(void) { extern const char version[]; printf("opal-prd %s\n", version); } static struct option opal_diag_options[] = { {"file", required_argument, NULL, 'f'}, {"pnor", required_argument, NULL, 'p'}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"stdio", no_argument, NULL, 's'}, {"expert-mode", no_argument, NULL, 'e'}, { 0 }, }; enum action { ACTION_RUN_DAEMON, ACTION_OCC_CONTROL, ACTION_ATTR_OVERRIDE, ACTION_HTMGT_PASSTHRU, ACTION_RUN_COMMAND, }; static int parse_action(const char *str, enum action *action) { int rc; if (!strcmp(str, "occ")) { *action = ACTION_OCC_CONTROL; rc = 0; } else if (!strcmp(str, "daemon")) { *action = ACTION_RUN_DAEMON; rc = 0; } else if (!strcmp(str, "override")) { *action = ACTION_ATTR_OVERRIDE; rc = 0; } else if (!strcmp(str, "htmgt-passthru")) { *action = ACTION_HTMGT_PASSTHRU; rc = 0; } else if (!strcmp(str, "run")) { *action = ACTION_RUN_COMMAND; return 0; } else { pr_log(LOG_ERR, "CTRL: unknown argument '%s'", str); rc = -1; } return rc; } int main(int argc, char *argv[]) { struct opal_prd_ctx _ctx; enum action action; int rc; ctx = &_ctx; memset(ctx, 0, sizeof(*ctx)); ctx->vlog = pr_log_stdio; ctx->use_syslog = true; /* Parse options */ for (;;) { int c; c = getopt_long(argc, argv, "f:p:dhse", opal_diag_options, NULL); if (c == -1) break; switch (c) { case 'f': ctx->hbrt_file_name = optarg; break; case 'd': ctx->debug = true; break; case 'p': ctx->pnor.path = strndup(optarg, PATH_MAX); break; case 's': ctx->use_syslog = false; break; case 'h': usage(argv[0]); return EXIT_SUCCESS; case 'e': ctx->expert_mode = true; break; case 'v': print_version(); return EXIT_SUCCESS; case '?': default: usage(argv[0]); return EXIT_FAILURE; } } if (optind < argc) { rc = parse_action(argv[optind], &action); if (rc) return EXIT_FAILURE; } else { action = ACTION_RUN_DAEMON; } if (is_prd_supported() < 0) { pr_log(LOG_ERR, "CTRL: PowerNV OPAL runtime diagnostic " "is not supported on this system"); return -1; } switch (action) { case ACTION_RUN_DAEMON: rc = run_prd_daemon(ctx); break; case ACTION_OCC_CONTROL: if (optind + 1 >= argc) { pr_log(LOG_ERR, "CTRL: occ command requires " "an argument"); return EXIT_FAILURE; } rc = send_occ_control(ctx, argv[optind + 1]); break; case ACTION_ATTR_OVERRIDE: if (optind + 1 >= argc) { pr_log(LOG_ERR, "CTRL: attribute override command " "requires an argument"); return EXIT_FAILURE; } rc = send_attr_override(ctx, argc - optind - 1, &argv[optind + 1]); break; case ACTION_HTMGT_PASSTHRU: if (optind + 1 >= argc) { pr_log(LOG_ERR, "CTRL: htmgt passthru requires at least " "one argument"); return EXIT_FAILURE; } rc = send_htmgt_passthru(ctx, argc - optind - 1, &argv[optind + 1]); break; case ACTION_RUN_COMMAND: if (optind + 1 >= argc) { pr_log(LOG_ERR, "CTRL: run command requires " "argument(s)"); return EXIT_FAILURE; } rc = send_run_command(ctx, argc - optind - 1, &argv[optind + 1]); break; default: break; } return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } skiboot-skiboot-5.1.13/external/opal-prd/opal-prd.h000066400000000000000000000014751265204436200221650ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * imitations under the License. */ #ifndef OPAL_PRD_H #define OPAL_PRD_H #include #define pr_debug(fmt, ...) pr_log(LOG_DEBUG, fmt, ## __VA_ARGS__) void pr_log(int priority, const char *fmt, ...) __attribute__((format(printf, 2, 3))); #endif /* OPAL_PRD_H */ skiboot-skiboot-5.1.13/external/opal-prd/pnor.c000066400000000000000000000170471265204436200214220ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "pnor.h" #include "opal-prd.h" int pnor_init(struct pnor *pnor) { int rc, fd; mtd_info_t mtd_info; if (!pnor) return -1; /* Open device and ffs */ fd = open(pnor->path, O_RDWR); if (fd < 0) { perror(pnor->path); return -1; } /* Hack so we can test on non-mtd file descriptors */ #if defined(__powerpc__) rc = ioctl(fd, MEMGETINFO, &mtd_info); if (rc < 0) { pr_log(LOG_ERR, "PNOR: ioctl failed to get pnor info: %m"); goto out; } pnor->size = mtd_info.size; pnor->erasesize = mtd_info.erasesize; #else pnor->size = lseek(fd, 0, SEEK_END); if (pnor->size < 0) { perror(pnor->path); goto out; } /* Fake it */ pnor->erasesize = 1024; #endif pr_debug("PNOR: Found PNOR: %d bytes (%d blocks)", pnor->size, pnor->erasesize); rc = ffs_open_image(fd, pnor->size, 0, &pnor->ffsh); if (rc) pr_log(LOG_ERR, "PNOR: Failed to open pnor partition table"); out: close(fd); return rc; } void pnor_close(struct pnor *pnor) { if (!pnor) return; if (pnor->ffsh) ffs_close(pnor->ffsh); if (pnor->path) free(pnor->path); } void dump_parts(struct ffs_handle *ffs) { int i, rc; uint32_t start, size, act_size; char *name; pr_debug("PNOR: %10s %8s %8s %8s", "name", "start", "size", "act_size"); for (i = 0; ; i++) { rc = ffs_part_info(ffs, i, &name, &start, &size, &act_size, NULL); if (rc) break; pr_debug("PNOR: %10s %08x %08x %08x", name, start, size, act_size); free(name); } } static int mtd_write(struct pnor *pnor, int fd, void *data, uint64_t offset, size_t len) { int write_start, write_len, start_waste, rc; bool end_waste = false; uint8_t *buf; struct erase_info_user erase; if (len > pnor->size || offset > pnor->size || len + offset > pnor->size) return -ERANGE; start_waste = offset % pnor->erasesize; write_start = offset - start_waste; /* Align size to multiple of block size */ write_len = (len + start_waste) & ~(pnor->erasesize - 1); if ((len + start_waste) > write_len) { end_waste = true; write_len += pnor->erasesize; } buf = malloc(write_len); if (start_waste) { rc = lseek(fd, write_start, SEEK_SET); if (rc < 0) { pr_log(LOG_ERR, "PNOR: lseek write_start(0x%x) " "failed; %m", write_start); goto out; } rc = read(fd, buf, pnor->erasesize); if (rc < 0) { pr_log(LOG_ERR, "PNOR: read(0x%x bytes) failed: %m", pnor->erasesize); goto out; } } if (end_waste) { rc = lseek(fd, write_start + write_len - pnor->erasesize, SEEK_SET); if (rc < 0) { perror("lseek last write block"); pr_log(LOG_ERR, "PNOR: lseek last write block(0x%x) " "failed; %m", write_start + write_len - pnor->erasesize); goto out; } rc = read(fd, buf + write_len - pnor->erasesize, pnor->erasesize); if (rc < 0) { pr_log(LOG_ERR, "PNOR: read(0x%x bytes) failed: %m", pnor->erasesize); goto out; } } /* Put data in the correct spot */ memcpy(buf + start_waste, data, len); /* Not sure if this is required */ rc = lseek(fd, 0, SEEK_SET); if (rc < 0) { pr_log(LOG_NOTICE, "PNOR: lseek(0) failed: %m"); goto out; } /* Erase */ erase.start = write_start; erase.length = write_len; rc = ioctl(fd, MEMERASE, &erase); if (rc < 0) { pr_log(LOG_ERR, "PNOR: erase(start 0x%x, len 0x%x) ioctl " "failed: %m", write_start, write_len); goto out; } /* Write */ rc = lseek(fd, write_start, SEEK_SET); if (rc < 0) { pr_log(LOG_ERR, "PNOR: lseek write_start(0x%x) failed: %m", write_start); goto out; } rc = write(fd, buf, write_len); if (rc < 0) { pr_log(LOG_ERR, "PNOR: write(0x%x bytes) failed: %m", write_len); goto out; } /* We have succeded, report the requested write size */ rc = len; out: free(buf); return rc; } static int mtd_read(struct pnor *pnor, int fd, void *data, uint64_t offset, size_t len) { int read_start, read_len, start_waste, rc; int mask = pnor->erasesize - 1; void *buf; if (len > pnor->size || offset > pnor->size || len + offset > pnor->size) return -ERANGE; /* Align start to erase block size */ start_waste = offset % pnor->erasesize; read_start = offset - start_waste; /* Align size to multiple of block size */ read_len = (len + start_waste) & ~mask; if ((len + start_waste) > read_len) read_len += pnor->erasesize; /* Ensure read is not out of bounds */ if (read_start + read_len > pnor->size) { pr_log(LOG_ERR, "PNOR: read out of bounds"); return -ERANGE; } buf = malloc(read_len); rc = lseek(fd, read_start, SEEK_SET); if (rc < 0) { pr_log(LOG_ERR, "PNOR: lseek read_start(0x%x) failed: %m", read_start); goto out; } rc = read(fd, buf, read_len); if (rc < 0) { pr_log(LOG_ERR, "PNOR: write(offset 0x%x, len 0x%x) " "failed: %m", read_start, read_len); goto out; } /* Copy data into destination, carefully avoiding the extra data we * added to align to block size */ memcpy(data, buf + start_waste, len); rc = len; out: free(buf); return rc; } /* Similar to read(2), this performs partial operations where the number of * bytes read/written may be less than size. * * Returns number of bytes written, or a negative value on failure. */ int pnor_operation(struct pnor *pnor, const char *name, uint64_t offset, void *data, size_t requested_size, enum pnor_op op) { int rc, fd; uint32_t pstart, psize, idx; int size; if (!pnor->ffsh) { pr_log(LOG_ERR, "PNOR: ffs not initialised"); return -EBUSY; } rc = ffs_lookup_part(pnor->ffsh, name, &idx); if (rc) { pr_log(LOG_WARNING, "PNOR: no partiton named '%s'", name); return -ENOENT; } ffs_part_info(pnor->ffsh, idx, NULL, &pstart, &psize, NULL, NULL); if (rc) { pr_log(LOG_ERR, "PNOR: unable to fetch partition info for %s", name); return -ENOENT; } if (offset > psize) { pr_log(LOG_WARNING, "PNOR: partition %s(size 0x%x) " "offset (0x%lx) out of bounds", name, psize, offset); return -ERANGE; } /* Large requests are trimmed */ if (requested_size > psize) size = psize; else size = requested_size; if (size + offset > psize) size = psize - offset; if (size < 0) { pr_log(LOG_WARNING, "PNOR: partition %s(size 0x%x) " "read size (0x%zx) and offset (0x%lx) " "out of bounds", name, psize, requested_size, offset); return -ERANGE; } fd = open(pnor->path, O_RDWR); if (fd < 0) { perror(pnor->path); return fd; } switch (op) { case PNOR_OP_READ: rc = mtd_read(pnor, fd, data, pstart + offset, size); break; case PNOR_OP_WRITE: rc = mtd_write(pnor, fd, data, pstart + offset, size); break; default: rc = -EIO; pr_log(LOG_ERR, "PNOR: Invalid operation"); goto out; } if (rc < 0) pr_log(LOG_ERR, "PNOR: MTD operation failed"); else if (rc != size) pr_log(LOG_WARNING, "PNOR: mtd operation " "returned %d, expected %d", rc, size); out: close(fd); return rc; } skiboot-skiboot-5.1.13/external/opal-prd/pnor.h000066400000000000000000000006731265204436200214240ustar00rootroot00000000000000#ifndef PNOR_H #define PNOR_H #include struct pnor { char *path; struct ffs_handle *ffsh; uint32_t size; uint32_t erasesize; }; enum pnor_op { PNOR_OP_READ, PNOR_OP_WRITE, }; extern int pnor_operation(struct pnor *pnor, const char *name, uint64_t offset, void *data, size_t size, enum pnor_op); extern int pnor_init(struct pnor *pnor); extern void pnor_close(struct pnor *pnor); #endif /*PNOR_H*/ skiboot-skiboot-5.1.13/external/opal-prd/test/000077500000000000000000000000001265204436200212465ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/opal-prd/test/test_pnor.c000066400000000000000000000021041265204436200234240ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include extern void dump_parts(struct ffs_handle *ffs); int main(int argc, char **argv) { struct pnor pnor; int rc; if (argc != 2) { printf("usage: %s [pnor file]\n", argv[0]); exit(EXIT_FAILURE); } pnor.path = strndup(argv[1], PATH_MAX); rc = pnor_init(&pnor); assert(rc); dump_parts(pnor.ffsh); pnor_close(&pnor); return 0; } skiboot-skiboot-5.1.13/external/opal-prd/test/test_pnor_ops.c000066400000000000000000000115721265204436200243160ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #undef ioctl #define ioctl(d, req, arg) test_ioctl(d, req, arg) int test_ioctl(int fd, int req, void *arg) { if (req == MEMERASE) { uint8_t *buf; struct erase_info_user *erase = arg; buf = malloc(erase->length); memset(buf, 'E', erase->length); lseek(fd, erase->start, SEEK_SET); write(fd, buf, erase->length); free(buf); } return 0; } #include "../pnor.c" bool compare_data(int fd, const uint8_t *check) { uint8_t buf[16]; int offset = 0; int bytes_read; int i; lseek(fd, 0, SEEK_SET); do { bytes_read = read(fd, buf, sizeof(buf)); i = 0; while (i < bytes_read) if (buf[i++] != check[offset++]) return false; } while (bytes_read == sizeof(buf)); out: lseek(fd, 0, SEEK_SET); return true; } void print_buf(uint8_t *buf, size_t len) { int i; for (i = 0; i < len; i++) { if (i % 16 == 0) printf("\n%06x : ", i); printf("%c ", buf[i]); } printf("\n"); } void print_file(int fd) { uint8_t buf[16]; int offset = 0; int bytes_read; int i; lseek(fd, 0, SEEK_SET); do { bytes_read = read(fd, buf, sizeof(buf)); if (bytes_read == 0) break; printf ("%06x : ", offset); for (i = 0; i < bytes_read; ++i) printf("%c ", buf[i]); printf("\n"); offset += bytes_read; } while (bytes_read == sizeof(buf)); lseek(fd, 0, SEEK_SET); } const uint8_t empty[32] = { 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'}; const uint8_t test_one[32] = { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'}; const uint8_t test_three[32] = { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M'}; int main(int argc, char **argv) { int fd, i, rc; struct pnor pnor; uint8_t data[24]; char filename[24]; strcpy(filename, "/tmp/pnor-XXXXXX"); fd = mkstemp(filename); if (fd < 0) { perror("mkstemp"); return EXIT_FAILURE; } /* So the file dissapears when we exit */ unlink(filename); /* E for empty */ memset(data, 'E', sizeof(data)); for (i = 0; i < 2; i++) write(fd, data, 16); /* Adjust this if making the file smaller */ pnor.size = 32; /* This is fake. Make it smaller than the size */ pnor.erasesize = 4; printf("Write: "); memset(data, 'A', sizeof(data)); rc = mtd_write(&pnor, fd, data, 0, 23); if (rc == 23 && compare_data(fd, test_one)) printf("PASS\n"); else printf("FAIL: %d\n", rc); printf("Read: "); memset(data, '0', sizeof(data)); rc = mtd_read(&pnor, fd, data, 7, 24); if (rc == 24 && !memcmp(data, &test_one[7], 24)) printf("PASS\n"); else printf("FAIL\n"); printf("Write with offset: "); memset(data, 'M', sizeof(data)); rc = mtd_write(&pnor, fd, data, 24, 8); if (rc == 8 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Write size past the end: "); rc = mtd_write(&pnor, fd, data, 0, 64); if (rc == -1 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL: %d\n", rc); printf("Write size past the end with offset: "); rc = mtd_write(&pnor, fd, data, 24, 24); if (rc == -1 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Write with offset past the end: "); rc = mtd_write(&pnor, fd, data, 64, 12); if (rc == -1 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Zero sized write: "); rc = mtd_write(&pnor, fd, data, 0, 0); if (rc == 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Zero sized write with offset: "); rc = mtd_write(&pnor, fd, data, 12, 0); if (rc == 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Read size past the end: "); rc = mtd_read(&pnor, fd, data, 0, 64); if (rc != 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Read size past the end with offset: "); rc = mtd_read(&pnor, fd, data, 24, 24); if (rc != 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Read with offset past the end: "); rc = mtd_read(&pnor, fd, data, 64, 12); if (rc != 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Zero sized read: "); rc = mtd_read(&pnor, fd, data, 0, 0); if (rc == 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); printf("Zero sized read with offset: "); rc = mtd_read(&pnor, fd, data, 12, 0); if (rc == 0 && compare_data(fd, test_three)) printf("PASS\n"); else printf("FAIL\n"); return 0; } skiboot-skiboot-5.1.13/external/opal-prd/thunk.S000066400000000000000000000113641265204436200215510ustar00rootroot00000000000000#include #include #ifndef __NR_switch_endian #define __NR_switch_endian 363 #endif /* a constant to use in the SI field of a little-endian D-form instruction */ #define le_si16(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8)) .text /* * Call into a HBRT BE function * Func desc (opd) will be in BE * Use ldbrx to load from opd */ call_be: /* Before we switch, we need to perform some ABI * conversion. We are currently running LE with the * new ABI v2. The GPR content is the same, we do * need save/restore and adjust r2. At this point r11 * contain the OPD */ nop nop /* We first create a stack frame compatible with BE, we * do a big one just in case... we save LR into our caller's * frame and r2 in our own frame. This is a BE formatted * frame so we store it as 40(r1), not 24(r1) */ stdu %r1,-128(%r1) mflr %r0 std %r0,(128 + 16)(%r1) std %r2,40(%r1) /* Grab the target r2 and function pointer */ #if __BYTE_ORDER == __LITTLE_ENDIAN ldbrx %r0, 0, %r11 li %r2, 8 ldbrx %r2, %r2, %r11 #else ld %r0,0(%r11) ld %r2,8(%r11) #endif mtlr %r0 #if __BYTE_ORDER == __LITTLE_ENDIAN /* Switch to the "other endian" */ li %r0,__NR_switch_endian sc /* Branch to LR */ .long 0x2100804e /* (byteswapped blrl) */ /* Switch endian back */ .long 0x00000038 | le_si16(__NR_switch_endian) /* byteswapped li %r0,__NR_switch_endian */ .long 0x02000044 /* byteswapped sc */ #else bctrl #endif /* Recover our r2, LR, undo stack frame ... */ ld %r2,40(%r1) ld %r0,(128+16)(%r1) addi %r1,%r1,128 mtlr %r0 blr #define CALL_THUNK(name, idx) \ .globl call_##name ;\ call_##name: ;\ ld %r11,hservice_runtime_fixed@got(%r2) ;\ ld %r11,(idx * 8)(%r11) ;\ b call_be /* Instanciate call to HBRT thunks */ CALL_THUNK(cxxtestExecute, 1) CALL_THUNK(get_lid_list, 2) CALL_THUNK(occ_load, 3) CALL_THUNK(occ_start, 4) CALL_THUNK(occ_stop, 5) CALL_THUNK(process_occ_error, 6) CALL_THUNK(enable_attns, 7) CALL_THUNK(disable_attns, 8) CALL_THUNK(handle_attns, 9) CALL_THUNK(process_occ_reset, 10) CALL_THUNK(enable_occ_actuation, 11) CALL_THUNK(apply_attr_override, 12) CALL_THUNK(mfg_htmgt_pass_thru, 13) CALL_THUNK(run_command, 14) .globl call_hbrt_init call_hbrt_init: ld %r11,hbrt_entry@got(%r2) b call_be #if __BYTE_ORDER == __LITTLE_ENDIAN /* Callback from HBRT, stack conversion and call into C code, * we arrive here from the thunk macro with r11 containing the * target function and r2 already set from the OPD. */ call_le: /* Create a LE stack frame, save LR */ stdu %r1,-32(%r1) mflr %r0 std %r0,(32+16)(%r1) /* Branch to original function */ mtlr %r11 blrl /* Restore stack and LR */ ld %r0,(32+16)(%r1) addi %r1,%r1,32 mtlr %r0 /* Switch endian back to BE */ li %r0,__NR_switch_endian sc /* Return to BE */ .long 0x2000804e /* byteswapped blr */ /* Callback from HBRT. There is one entry point per function. * * We assume the proper r2 is already set via the OPD, so we grab our * target function pointer in r11 and jump to call_le */ #define CALLBACK_THUNK(name) \ .pushsection ".text","ax" ;\ .globl name##_thunk ;\ name##_thunk: ;\ .long 0x00000038 | le_si16(__NR_switch_endian) ;\ /* byteswapped li %r0,__NR_switch_endian */ ;\ .long 0x02000044 /* byteswapped sc */ ;\ ld %r11,name@got(%r2) ;\ b call_le ;\ .popsection ;\ .pushsection ".data.thunk_opd","aw" ;\ 1: .llong name##_thunk, .TOC., 0 ;\ .popsection ;\ .llong 1b #else /* __BYTE_ORDER == __LITTLE_ENDIAN */ #define CALLBACK_THUNK(name) \ .llong name #endif #define DISABLED_THUNK(name) .llong 0x0 /* Here's the callback table generation. It creates the table and * all the thunks for all the callbacks from HBRT to us */ .data .globl hinterface hinterface: /* HBRT interface version */ .llong 1 /* Callout pointers */ CALLBACK_THUNK(hservice_puts) CALLBACK_THUNK(hservice_assert) CALLBACK_THUNK(hservice_set_page_execute) CALLBACK_THUNK(hservice_malloc) CALLBACK_THUNK(hservice_free) CALLBACK_THUNK(hservice_realloc) DISABLED_THUNK(hservice_send_error_log) CALLBACK_THUNK(hservice_scom_read) CALLBACK_THUNK(hservice_scom_write) DISABLED_THUNK(hservice_lid_load) DISABLED_THUNK(hservice_lid_unload) CALLBACK_THUNK(hservice_get_reserved_mem) DISABLED_THUNK(hservice_wakeup) CALLBACK_THUNK(hservice_nanosleep) DISABLED_THUNK(hservice_report_occ_failure) CALLBACK_THUNK(hservice_clock_gettime) CALLBACK_THUNK(hservice_pnor_read) CALLBACK_THUNK(hservice_pnor_write) CALLBACK_THUNK(hservice_i2c_read) CALLBACK_THUNK(hservice_i2c_write) CALLBACK_THUNK(hservice_ipmi_msg) CALLBACK_THUNK(hservice_memory_error) /* Reserved space for future growth */ .space 32*8,0 /* Eye catcher for debugging */ .llong 0xdeadbeef skiboot-skiboot-5.1.13/external/pflash/000077500000000000000000000000001265204436200200265ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/pflash/Makefile000066400000000000000000000023011265204436200214620ustar00rootroot00000000000000include rules.mk GET_ARCH = ../../external/common/get_arch.sh include ../../external/common/rules.mk all: $(EXE) .PHONY: links links: libflash ccan common make_version.sh libflash: ln -sf ../../libflash . ccan: ln -sf ../../ccan . common: ln -sf ../common . make_version.sh: ln -sf ../../make_version.sh $(OBJS): | links arch_links .PHONY: VERSION-always .version: VERSION-always @echo $(PFLASH_VERSION) > $@.tmp @cmp -s $@ $@.tmp || cp $@.tmp $@ @rm -f $@.tmp .PHONY: dist #File is named $(PFLASH_VERSION).tar because the expectation is that pflash- #is always at the start of the verion. This remains consistent with skiboot #version strings dist: links .version find -L ../pflash/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(PFLASH_VERSION).tar tar --transform 's/Makefile.dist/Makefile/' -rhf $(PFLASH_VERSION).tar \ ../pflash/Makefile.dist ../pflash/rules.mk \ ../pflash/.version ../pflash/make_version.sh \ ../pflash/common/* clean: arch_clean rm -f $(OBJS) $(EXE) *.o *.d libflash/test/test_flash libflash/test/*.o distclean: clean rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~ rm -f libflash ccan .version .version.tmp rm -f common io.h make_version.sh skiboot-skiboot-5.1.13/external/pflash/Makefile.dist000066400000000000000000000002161265204436200224270ustar00rootroot00000000000000include rules.mk GET_ARCH = common/get_arch.sh include common/rules.mk all: $(EXE) clean: rm -f $(OBJS) *.o distclean: clean rm -f $(EXE) skiboot-skiboot-5.1.13/external/pflash/TODO000066400000000000000000000003241265204436200205150ustar00rootroot00000000000000- PCI backend for host - Use proper GPIO APIs on ARM - Use IPMI for lock/unlock on host - Timeouts and flashing errors handling - Lock handling - Support pnor "update" mode which only update selected partitions skiboot-skiboot-5.1.13/external/pflash/ast.h000066400000000000000000000030461265204436200207710ustar00rootroot00000000000000#ifndef __AST_H #define __AST_H /* * AHB bus registers */ /* SPI Flash controller #1 (BMC) */ #define BMC_SPI_FCTL_BASE 0x1E620000 #define BMC_SPI_FCTL_CTRL (BMC_SPI_FCTL_BASE + 0x10) #define BMC_SPI_FREAD_TIMING (BMC_SPI_FCTL_BASE + 0x94) #define BMC_FLASH_BASE 0x20000000 /* SPI Flash controller #2 (PNOR) */ #define PNOR_SPI_FCTL_BASE 0x1E630000 #define PNOR_SPI_FCTL_CONF (PNOR_SPI_FCTL_BASE + 0x00) #define PNOR_SPI_FCTL_CTRL (PNOR_SPI_FCTL_BASE + 0x04) #define PNOR_SPI_FREAD_TIMING (PNOR_SPI_FCTL_BASE + 0x14) #define PNOR_FLASH_BASE 0x30000000 /* LPC registers */ #define LPC_BASE 0x1e789000 #define LPC_HICR6 (LPC_BASE + 0x80) #define LPC_HICR7 (LPC_BASE + 0x88) #define LPC_HICR8 (LPC_BASE + 0x8c) /* SCU registers */ #define SCU_BASE 0x1e6e2000 #define SCU_HW_STRAPPING (SCU_BASE + 0x70) /* * AHB Accessors */ #ifndef __SKIBOOT__ #include "common/io.h" #else /* * Register accessors, return byteswapped values * (IE. LE registers) */ void ast_ahb_writel(uint32_t val, uint32_t reg); uint32_t ast_ahb_readl(uint32_t reg); /* * copy to/from accessors. Cannot cross IDSEL boundaries (256M) */ int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len); int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len); void ast_io_init(void); #endif /* __SKIBOOT__ */ /* * SPI Flash controllers */ #define AST_SF_TYPE_PNOR 0 #define AST_SF_TYPE_BMC 1 #define AST_SF_TYPE_MEM 2 struct spi_flash_ctrl; int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl); void ast_sf_close(struct spi_flash_ctrl *ctrl); #endif /* __AST_H */ skiboot-skiboot-5.1.13/external/pflash/config.h000066400000000000000000000005531265204436200214470ustar00rootroot00000000000000/* For CCAN */ #include #include #define HAVE_TYPEOF 1 #define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 #if __BYTE_ORDER == __LITTLE_ENDIAN #define HAVE_BIG_ENDIAN 0 #define HAVE_LITTLE_ENDIAN 1 #else #define HAVE_BIG_ENDIAN 1 #define HAVE_LITTLE_ENDIAN 0 #endif #define HAVE_BYTESWAP_H 1 #define HAVE_BSWAP_64 1 skiboot-skiboot-5.1.13/external/pflash/lpc.h000066400000000000000000000017641265204436200207650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PFLASH_LPC_H #define __PFLASH_LPC_H /* Fake routines for accessing the LPC bus from within linux used by pflash * and implemented by powerpc_io.c or arm_io.c */ extern int64_t lpc_fw_read32(uint32_t *val, uint32_t addr); extern int64_t lpc_fw_write32(uint32_t val, uint32_t addr); extern void lpc_outb(uint8_t data, uint32_t addr); extern uint8_t lpc_inb(uint32_t addr); #endif /* __PFLASH_LPC_H */ skiboot-skiboot-5.1.13/external/pflash/pflash.c000066400000000000000000000512711265204436200214550ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "progress.h" #define __aligned(x) __attribute__((aligned(x))) /* Full pflash version number (possibly includes gitid). */ extern const char version[]; static bool must_confirm = true; static bool dummy_run; static int need_relock; static bool bmc_flash; static uint32_t ffs_toc = 0; static int flash_side = 0; #define FILE_BUF_SIZE 0x10000 static uint8_t file_buf[FILE_BUF_SIZE] __aligned(0x1000); static struct blocklevel_device *bl; static struct ffs_handle *ffsh; static uint32_t fl_total_size, fl_erase_granule; static const char *fl_name; static int32_t ffs_index = -1; static void check_confirm(void) { char yes[8], *p; if (!must_confirm) return; printf("WARNING ! This will modify your %s flash chip content !\n", bmc_flash ? "BMC" : "HOST"); printf("Enter \"yes\" to confirm:"); memset(yes, 0, sizeof(yes)); if (!fgets(yes, 7, stdin)) exit(1); p = strchr(yes, 10); if (p) *p = 0; p = strchr(yes, 13); if (p) *p = 0; if (strcmp(yes, "yes")) { printf("Operation cancelled !\n"); exit(1); } must_confirm = false; } static void print_ffs_info(uint32_t toc_offset) { struct ffs_handle *ffs_handle; uint32_t other_side_offset = 0; int rc; uint32_t i; rc = ffs_init(toc_offset, fl_total_size, bl, &ffs_handle, 0); if (rc) { fprintf(stderr, "Error %d opening ffs !\n", rc); return; } printf("\n"); printf("TOC@0x%08x Partitions:\n", toc_offset); printf("-----------\n"); for (i = 0;; i++) { uint32_t start, size, act, end; bool ecc; char *name; rc = ffs_part_info(ffs_handle, i, &name, &start, &size, &act, &ecc); if (rc == FFS_ERR_PART_NOT_FOUND) break; if (rc) { fprintf(stderr, "Error %d scanning partitions\n", rc); break; } end = start + size; printf("ID=%02d %15s %08x..%08x (actual=%08x) %s\n", i, name, start, end, act, ecc ? "[ECC]" : ""); if (strcmp(name, "OTHER_SIDE") == 0) other_side_offset = start; free(name); } ffs_close(ffs_handle); if (other_side_offset) print_ffs_info(other_side_offset); } static void print_flash_info(uint32_t toc) { printf("Flash info:\n"); printf("-----------\n"); printf("Name = %s\n", fl_name); printf("Total size = %dMB \n", fl_total_size >> 20); printf("Erase granule = %dKB \n", fl_erase_granule >> 10); if (bmc_flash) return; print_ffs_info(toc); } static int open_partition(const char *name) { uint32_t index; int rc; /* Open libffs if needed */ if (!ffsh) { rc = ffs_init(ffs_toc, fl_total_size, bl, &ffsh, 0); if (rc) { fprintf(stderr, "Error %d opening ffs !\n", rc); if (ffs_toc) fprintf(stderr, "You specified 0x%08x as the libffs TOC" " looks like it doesn't exist\n", ffs_toc); return rc; } } /* Find partition */ rc = ffs_lookup_part(ffsh, name, &index); if (rc == FFS_ERR_PART_NOT_FOUND) { fprintf(stderr, "Partition '%s' not found !\n", name); return rc; } if (rc) { fprintf(stderr, "Error %d looking for partition '%s' !\n", rc, name); return rc; } ffs_index = index; return 0; } static void lookup_partition(const char *name) { int rc; if (flash_side == 1) { uint32_t other_side_offset; rc = open_partition("OTHER_SIDE"); if (rc == FFS_ERR_PART_NOT_FOUND) fprintf(stderr, "side 1 was specified but there doesn't appear" " to be a second side to this flash\n"); if (rc) exit(1); /* Just need to know where it starts */ rc = ffs_part_info(ffsh, ffs_index, NULL, &other_side_offset, NULL, NULL, NULL); if (rc) exit(1); ffs_close(ffsh); ffsh = NULL; ffs_toc = other_side_offset; } rc = open_partition(name); if (rc) exit(1); } static void erase_chip(void) { int rc; printf("About to erase chip !\n"); check_confirm(); printf("Erasing... (may take a while !) "); fflush(stdout); if (dummy_run) { printf("skipped (dummy)\n"); return; } rc = arch_flash_erase_chip(bl); if (rc) { fprintf(stderr, "Error %d erasing chip\n", rc); exit(1); } printf("done !\n"); } static void erase_range(uint32_t start, uint32_t size, bool will_program) { uint32_t done = 0; int rc; printf("About to erase 0x%08x..0x%08x !\n", start, start + size); check_confirm(); if (dummy_run) { printf("skipped (dummy)\n"); return; } printf("Erasing...\n"); progress_init(size >> 8); while(size) { /* If aligned to 64k and at least 64k, use 64k erase */ if ((start & 0xffff) == 0 && size >= 0x10000) { rc = blocklevel_erase(bl, start, 0x10000); if (rc) { fprintf(stderr, "Error %d erasing 0x%08x\n", rc, start); exit(1); } start += 0x10000; size -= 0x10000; done += 0x10000; } else { rc = blocklevel_erase(bl, start, 0x1000); if (rc) { fprintf(stderr, "Error %d erasing 0x%08x\n", rc, start); exit(1); } start += 0x1000; size -= 0x1000; done += 0x1000; } progress_tick(done >> 8); } progress_end(); /* If this is a flash partition, mark it empty if we aren't * going to program over it as well */ if (ffsh && ffs_index >= 0 && !will_program) { printf("Updating actual size in partition header...\n"); ffs_update_act_size(ffsh, ffs_index, 0); } } static void set_ecc(uint32_t start, uint32_t size) { uint32_t i = start + 8; uint8_t ecc = 0; printf("About to erase and set ECC bits in region 0x%08x to 0x%08x\n", start, start + size); check_confirm(); erase_range(start, size, true); printf("Programming ECC bits...\n"); progress_init(size); while (i < start + size) { blocklevel_write(bl, i, &ecc, sizeof(ecc)); i += 9; progress_tick(i - start); } progress_end(); } static void program_file(const char *file, uint32_t start, uint32_t size) { int fd, rc; ssize_t len; uint32_t actual_size = 0; fd = open(file, O_RDONLY); if (fd == -1) { perror("Failed to open file"); exit(1); } printf("About to program \"%s\" at 0x%08x..0x%08x !\n", file, start, start + size); check_confirm(); if (dummy_run) { printf("skipped (dummy)\n"); return; } printf("Programming & Verifying...\n"); progress_init(size >> 8); while(size) { len = read(fd, file_buf, FILE_BUF_SIZE); if (len < 0) { perror("Error reading file"); exit(1); } if (len == 0) break; if (len > size) len = size; size -= len; actual_size += len; rc = blocklevel_write(bl, start, file_buf, len); if (rc) { if (rc == FLASH_ERR_VERIFY_FAILURE) fprintf(stderr, "Verification failed for" " chunk at 0x%08x\n", start); else fprintf(stderr, "Flash write error %d for" " chunk at 0x%08x\n", rc, start); exit(1); } start += len; progress_tick(actual_size >> 8); } progress_end(); close(fd); /* If this is a flash partition, adjust its size */ if (ffsh && ffs_index >= 0) { printf("Updating actual size in partition header...\n"); ffs_update_act_size(ffsh, ffs_index, actual_size); } } static void do_read_file(const char *file, uint32_t start, uint32_t size) { int fd, rc; ssize_t len; uint32_t done = 0; fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 00666); if (fd == -1) { perror("Failed to open file"); exit(1); } printf("Reading to \"%s\" from 0x%08x..0x%08x !\n", file, start, start + size); progress_init(size >> 8); while(size) { len = size > FILE_BUF_SIZE ? FILE_BUF_SIZE : size; rc = blocklevel_read(bl, start, file_buf, len); if (rc) { fprintf(stderr, "Flash read error %d for" " chunk at 0x%08x\n", rc, start); exit(1); } rc = write(fd, file_buf, len); if (rc < 0) { perror("Error writing file"); exit(1); } start += len; size -= len; done += len; progress_tick(done >> 8); } progress_end(); close(fd); } static void enable_4B_addresses(void) { int rc; printf("Switching to 4-bytes address mode\n"); rc = arch_flash_4b_mode(bl, true); if (rc) { if (rc == -1) { fprintf(stderr, "Switching address mode not availible on this architecture\n"); } else { fprintf(stderr, "Error %d enabling 4b mode\n", rc); } exit(1); } } static void disable_4B_addresses(void) { int rc; printf("Switching to 3-bytes address mode\n"); rc = arch_flash_4b_mode(bl, false); if (rc) { if (rc == -1) { fprintf(stderr, "Switching address mode not availible on this architecture\n"); } else { fprintf(stderr, "Error %d enabling 4b mode\n", rc); } exit(1); } } static void print_version(void) { printf("Open-Power Flash tool %s\n", version); } static void print_help(const char *pname) { printf("Usage: %s [options] commands...\n\n", pname); printf(" Options:\n"); printf("\t-a address, --address=address\n"); printf("\t\tSpecify the start address for erasing, reading\n"); printf("\t\tor flashing\n\n"); printf("\t-s size, --size=size\n"); printf("\t\tSpecify the size in bytes for erasing, reading\n"); printf("\t\tor flashing\n\n"); printf("\t-P part_name, --partition=part_name\n"); printf("\t\tSpecify the partition whose content is to be erased\n"); printf("\t\tprogrammed or read. This is an alternative to -a and -s\n"); printf("\t\tif both -P and -s are specified, the smallest of the\n"); printf("\t\ttwo will be used\n\n"); printf("\t-f, --force\n"); printf("\t\tDon't ask for confirmation before erasing or flashing\n\n"); printf("\t-d, --dummy\n"); printf("\t\tDon't write to flash\n\n"); #ifdef __powerpc__ printf("\t-l, --lpc\n"); printf("\t\tUse LPC accesses instead of PCI\n\n"); #endif printf("\t-b, --bmc\n"); printf("\t\tTarget BMC flash instead of host flash\n\n"); printf("\t-S, --side\n"); printf("\t\tSide of the flash on which to operate, 0 (default) or 1\n\n"); printf("\t-T, --toc\n"); printf("\t\tlibffs TOC on which to operate, defaults to 0.\n"); printf("\t\tleading 0x is required for interpretation of a hex value\n\n"); printf(" Commands:\n"); printf("\t-4, --enable-4B\n"); printf("\t\tSwitch the flash and controller to 4-bytes address\n"); printf("\t\tmode (no confirmation needed).\n\n"); printf("\t-3, --disable-4B\n"); printf("\t\tSwitch the flash and controller to 3-bytes address\n"); printf("\t\tmode (no confirmation needed).\n\n"); printf("\t-r file, --read=file\n"); printf("\t\tRead flash content from address into file, use -s\n"); printf("\t\tto specify the size to read (or it will use the source\n"); printf("\t\tfile size if used in conjunction with -p and -s is not\n"); printf("\t\tspecified). When using -r together with -e or -p, the\n"); printf("\t\tread will be peformed first\n\n"); printf("\t-E, --erase-all\n"); printf("\t\tErase entire flash chip\n"); printf("\t\t(Not supported on all chips/controllers)\n\n"); printf("\t-e, --erase\n"); printf("\t\tErase the specified region. If size or address are not\n"); printf("\t\tspecified, but \'--program\' is used, then the file\n"); printf("\t\tsize will be used (rounded to an erase block) and the\n"); printf("\t\taddress defaults to 0.\n\n"); printf("\t-p file, --program=file\n"); printf("\t\tWill program the file to flash. If the address is not\n"); printf("\t\tspecified, it will use 0. If the size is not specified\n"); printf("\t\tit will use the file size. Otherwise it will limit to\n"); printf("\t\tthe specified size (whatever is smaller). If used in\n"); printf("\t\tconjunction with any erase command, the erase will\n"); printf("\t\ttake place first.\n\n"); printf("\t-t, --tune\n"); printf("\t\tJust tune the flash controller & access size\n"); printf("\t\t(Implicit for all other operations)\n\n"); printf("\t-c --clear\n"); printf("\t\tUsed to ECC clear a partition of the flash\n"); printf("\t\tMust be used in conjunction with -P. Will erase the\n"); printf("\t\tpartition and then set all the ECC bits as they should be\n\n"); printf("\t-i, --info\n"); printf("\t\tDisplay some information about the flash.\n\n"); printf("\t-h, --help\n"); printf("\t\tThis message.\n\n"); } void exiting(int d, void *p) { if (need_relock) arch_flash_set_wrprotect(bl, 1); arch_flash_close(bl, NULL); } int main(int argc, char *argv[]) { const char *pname = argv[0]; uint32_t address = 0, read_size = 0, write_size = 0; uint32_t erase_start = 0, erase_size = 0; bool erase = false, do_clear = false; bool program = false, erase_all = false, info = false, do_read = false; bool enable_4B = false, disable_4B = false; bool show_help = false, show_version = false; bool no_action = false, tune = false; char *write_file = NULL, *read_file = NULL, *part_name = NULL; bool ffs_toc_seen = false; int rc; while(1) { static struct option long_opts[] = { {"address", required_argument, NULL, 'a'}, {"size", required_argument, NULL, 's'}, {"partition", required_argument, NULL, 'P'}, {"bmc", no_argument, NULL, 'b'}, {"enable-4B", no_argument, NULL, '4'}, {"disable-4B", no_argument, NULL, '3'}, {"read", required_argument, NULL, 'r'}, {"erase-all", no_argument, NULL, 'E'}, {"erase", no_argument, NULL, 'e'}, {"program", required_argument, NULL, 'p'}, {"force", no_argument, NULL, 'f'}, {"info", no_argument, NULL, 'i'}, {"tune", no_argument, NULL, 't'}, {"dummy", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"debug", no_argument, NULL, 'g'}, {"side", required_argument, NULL, 'S'}, {"toc", required_argument, NULL, 'T'}, {"clear", no_argument, NULL, 'c'} }; int c, oidx = 0; c = getopt_long(argc, argv, "a:s:P:r:43Eep:fdihvbtgS:T:c", long_opts, &oidx); if (c == EOF) break; switch(c) { case 'a': address = strtoul(optarg, NULL, 0); break; case 's': read_size = write_size = strtoul(optarg, NULL, 0); break; case 'P': part_name = strdup(optarg); break; case '4': enable_4B = true; break; case '3': disable_4B = true; break; case 'r': do_read = true; read_file = strdup(optarg); break; case 'E': erase_all = erase = true; break; case 'e': erase = true; break; case 'p': program = true; write_file = strdup(optarg); break; case 'f': must_confirm = false; break; case 'd': must_confirm = false; dummy_run = true; break; case 'i': info = true; break; case 'b': bmc_flash = true; break; case 't': tune = true; break; case 'v': show_version = true; break; case 'h': show_help = show_version = true; break; case 'g': libflash_debug = true; break; case 'S': flash_side = atoi(optarg); break; case 'T': ffs_toc_seen = true; ffs_toc = strtoul(optarg, NULL, 0); break; case 'c': do_clear = true; break; default: exit(1); } } /* Check if we need to access the flash at all (which will * also tune them as a side effect */ no_action = !erase && !program && !info && !do_read && !enable_4B && !disable_4B && !tune && !do_clear; /* Nothing to do, if we didn't already, print usage */ if (no_action && !show_version) show_help = show_version = true; if (show_version) print_version(); if (show_help) print_help(pname); if (no_action) return 0; /* --enable-4B and --disable-4B are mutually exclusive */ if (enable_4B && disable_4B) { fprintf(stderr, "--enable-4B and --disable-4B are mutually" " exclusive !\n"); exit(1); } /* 4B not supported on BMC flash */ if (enable_4B && bmc_flash) { fprintf(stderr, "--enable-4B not supported on BMC flash !\n"); exit(1); } /* partitions not supported on BMC flash */ if (part_name && bmc_flash) { fprintf(stderr, "--partition not supported on BMC flash !\n"); exit(1); } /* part-name and erase-all make no sense together */ if (part_name && erase_all) { fprintf(stderr, "--partition and --erase-all are mutually" " exclusive !\n"); exit(1); } /* Read command should always come with a file */ if (do_read && !read_file) { fprintf(stderr, "Read with no file specified !\n"); exit(1); } /* Program command should always come with a file */ if (program && !write_file) { fprintf(stderr, "Program with no file specified !\n"); exit(1); } /* If both partition and address specified, error out */ if (address && part_name) { fprintf(stderr, "Specify partition or address, not both !\n"); exit(1); } if (do_clear && !part_name) { fprintf(stderr, "--clear only supported on a partition name\n"); exit(1); } /* Explicitly only support two sides */ if (flash_side != 0 && flash_side != 1) { fprintf(stderr, "Unexpected value for --side '%d'\n", flash_side); exit(1); } if (ffs_toc_seen && flash_side) { fprintf(stderr, "--toc and --side are exclusive"); exit(1); } /* If file specified but not size, get size from file */ if (write_file && !write_size) { struct stat stbuf; if (stat(write_file, &stbuf)) { perror("Failed to get file size"); exit(1); } write_size = stbuf.st_size; } if (bmc_flash) { if (arch_flash_bmc(NULL, 1) == -1) { fprintf(stderr, "Can't switch to BMC flash on this architecture\n"); exit(1); } } if (arch_flash_init(&bl, NULL)) exit(1); on_exit(exiting, NULL); rc = blocklevel_get_info(bl, &fl_name, &fl_total_size, &fl_erase_granule); if (rc) { fprintf(stderr, "Error %d getting flash info\n", rc); exit(1); } /* If -t is passed, then print a nice message */ if (tune) printf("Flash and controller tuned\n"); /* If read specified and no read_size, use flash size */ if (do_read && !read_size && !part_name) read_size = fl_total_size; /* We have a partition specified, grab the details */ if (part_name) lookup_partition(part_name); /* We have a partition, adjust read/write size if needed */ if (ffsh && ffs_index >= 0) { uint32_t pstart, pmaxsz, pactsize; bool ecc; int rc; rc = ffs_part_info(ffsh, ffs_index, NULL, &pstart, &pmaxsz, &pactsize, &ecc); if (rc) { fprintf(stderr,"Failed to get partition info\n"); exit(1); } if (!ecc && do_clear) { fprintf(stderr, "The partition on which to do --clear " "does not have ECC, are you sure?\n"); check_confirm(); /* Still confirm later on */ must_confirm = true; } /* Read size is obtained from partition "actual" size */ if (!read_size) read_size = pactsize; /* Write size is max size of partition */ if (!write_size) write_size = pmaxsz; /* Crop write size to partition size */ if (write_size > pmaxsz) { printf("WARNING: Size (%d bytes) larger than partition" " (%d bytes), cropping to fit\n", write_size, pmaxsz); write_size = pmaxsz; } /* If erasing, check partition alignment */ if (erase && ((pstart | pmaxsz) & 0xfff)) { fprintf(stderr,"Partition not aligned properly\n"); exit(1); } /* Set address */ address = pstart; } /* Align erase boundaries */ if (erase && !erase_all) { uint32_t mask = 0xfff; uint32_t erase_end; /* Dummy size for erase, will be adjusted later */ if (!write_size) write_size = 1; erase_start = address & ~mask; erase_end = ((address + write_size) + mask) & ~mask; erase_size = erase_end - erase_start; if (erase_start != address || erase_size != write_size) fprintf(stderr, "WARNING: Erase region adjusted" " to 0x%08x..0x%08x\n", erase_start, erase_end); } /* Process commands */ if (enable_4B) enable_4B_addresses(); if (disable_4B) disable_4B_addresses(); if (info) { /* * Don't pass through modfied TOC value if the modification was done * because of --size, but still respect if it came from --toc (we * assume the user knows what they're doing in that case) */ print_flash_info(flash_side ? 0 : ffs_toc); } /* Unlock flash (PNOR only) */ if ((erase || program || do_clear) && !bmc_flash) { need_relock = arch_flash_set_wrprotect(bl, false); if (need_relock == -1) { fprintf(stderr, "Architecture doesn't support write protection on flash\n"); need_relock = 0; exit (1); } } if (do_read) do_read_file(read_file, address, read_size); if (erase_all) erase_chip(); else if (erase) erase_range(erase_start, erase_size, program); if (program) program_file(write_file, address, write_size); if (do_clear) set_ecc(address, write_size); return 0; } skiboot-skiboot-5.1.13/external/pflash/powerpc_io.c000066400000000000000000000154511265204436200223460ustar00rootroot00000000000000#define _GNU_SOURCE /* for strcasestr */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "io.h" /* Big endian warning/note: * * The register accessors return byteswapped data for registers */ uint32_t (*ast_ahb_readl)(uint32_t offset); void (*ast_ahb_writel)(uint32_t val, uint32_t offset); int (*ast_copy_to_ahb)(uint32_t reg, const void *src, uint32_t len); int (*ast_copy_from_ahb)(void *dst, uint32_t reg, uint32_t len); static enum ppc_platform { plat_unknown, plat_rhesus, plat_ast_bmc, } ppc_platform; static int lpc_io_fd = -1, lpc_fw_fd = -1; static uint32_t lpc_old_flash_reg; static uint32_t ahb_flash_base, ahb_flash_size, lpc_flash_offset; static void lpc_outb(uint8_t val, uint16_t port) { int rc; lseek(lpc_io_fd, port, SEEK_SET); rc = write(lpc_io_fd, &val, 1); if (rc != 1) { perror("Can't write to LPC IO"); exit(1); } } static uint8_t lpc_inb(uint16_t port) { uint8_t val; int rc; lseek(lpc_io_fd, port, SEEK_SET); rc = read(lpc_io_fd, &val, 1); if (rc != 1) { perror("Can't read from LPC IO"); exit(1); } return val; } int lpc_fw_write32(uint32_t val, uint32_t addr) { int rc; /* The value passed in is in big endian always */ lseek(lpc_fw_fd, addr, SEEK_SET); rc = write(lpc_fw_fd, &val, 4); if (rc != 4) { perror("Can't write to LPC FW"); exit(1); } return 0; } int lpc_fw_read32(uint32_t *val, uint32_t addr) { int rc; lseek(lpc_fw_fd, addr, SEEK_SET); rc = read(lpc_fw_fd, val, 4); if (rc != 4) { perror("Can't read from LPC FW"); exit(1); } return 0; } static void lpc_sio_outb(uint8_t val, uint8_t reg) { lpc_outb(reg, 0x2e); lpc_outb(val, 0x2f); } static uint8_t lpc_sio_inb(uint8_t reg) { lpc_outb(reg, 0x2e); return lpc_inb(0x2f); } static void lpc_ahb_prep(uint32_t reg, uint8_t type) { /* Address */ lpc_sio_outb((reg >> 24) & 0xff, 0xf0); lpc_sio_outb((reg >> 16) & 0xff, 0xf1); lpc_sio_outb((reg >> 8) & 0xff, 0xf2); lpc_sio_outb((reg ) & 0xff, 0xf3); /* 4 bytes cycle */ lpc_sio_outb(type, 0xf8); } static void lpc_ahb_writel(uint32_t val, uint32_t reg) { lpc_ahb_prep(reg, 2); /* Write data */ lpc_sio_outb(val >> 24, 0xf4); lpc_sio_outb(val >> 16, 0xf5); lpc_sio_outb(val >> 8, 0xf6); lpc_sio_outb(val , 0xf7); /* Trigger */ lpc_sio_outb(0xcf, 0xfe); } static uint32_t lpc_ahb_readl(uint32_t reg) { uint32_t val = 0; lpc_ahb_prep(reg, 2); /* Trigger */ lpc_sio_inb(0xfe); /* Read results */ val = (val << 8) | lpc_sio_inb(0xf4); val = (val << 8) | lpc_sio_inb(0xf5); val = (val << 8) | lpc_sio_inb(0xf6); val = (val << 8) | lpc_sio_inb(0xf7); return val; } static void lpc_ahb_init(bool bmc_flash) { uint32_t b; /* Send SuperIO password */ lpc_outb(0xa5, 0x2e); lpc_outb(0xa5, 0x2e); /* Select logical dev d */ lpc_sio_outb(0x0d, 0x07); /* Enable iLPC->AHB */ lpc_sio_outb(0x01, 0x30); /* Save flash base */ lpc_old_flash_reg = b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88); /* Upate flash base */ if (bmc_flash) { ahb_flash_base = BMC_FLASH_BASE; ahb_flash_size = BMC_FLASH_SIZE; } else { ahb_flash_base = PNOR_FLASH_BASE; ahb_flash_size = PNOR_FLASH_SIZE; } lpc_flash_offset = 0x0e000000; b = (b & 0x0000ffff) | ahb_flash_base; lpc_ahb_writel(b, LPC_CTRL_BASE + 0x88); b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88); } static int lpc_ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len) { int rc; if (reg < ahb_flash_base || (reg + len) > (ahb_flash_base + ahb_flash_size)) return -1; reg = (reg - ahb_flash_base) + lpc_flash_offset; lseek(lpc_fw_fd, reg, SEEK_SET); rc = read(lpc_fw_fd, dst, len); if (rc != len) { perror("Can't read bulk from LPC FW"); exit(1); } return 0; } static int lpc_ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len) { int rc; if (reg < ahb_flash_base || (reg + len) > (ahb_flash_base + ahb_flash_size)) return -1; reg = (reg - ahb_flash_base) + lpc_flash_offset; lseek(lpc_fw_fd, reg, SEEK_SET); rc = write(lpc_fw_fd, src, len); if (rc != len) { perror("Can't write bulk from LPC FW"); exit(1); } return 0; } /* * Write protect: TODO use custom IPMI to control lock from BMC */ static uint32_t lpc_gpio_ctl_readl(uint32_t offset) { return lpc_ahb_readl(GPIO_CTRL_BASE + offset); } static void lpc_gpio_ctl_writel(uint32_t val, uint32_t offset) { lpc_ahb_writel(val, GPIO_CTRL_BASE + offset); } bool set_wrprotect(bool protect) { uint32_t reg; bool was_protected; if (ppc_platform != plat_ast_bmc) return false; reg = lpc_gpio_ctl_readl(0x20); was_protected = !!(reg & 0x00004000); if (protect) reg |= 0x00004000; /* GPIOF[6] value */ else reg &= ~0x00004000; /* GPIOF[6] value */ lpc_gpio_ctl_writel(reg, 0x20); reg = lpc_gpio_ctl_readl(0x24); reg |= 0x00004000; /* GPIOF[6] direction */ lpc_gpio_ctl_writel(reg, 0x24); return was_protected; } static void open_lpc(bool bmc_flash) { lpc_fw_fd = open("/sys/kernel/debug/powerpc/lpc/fw", O_RDWR); if (lpc_fw_fd < 0) { perror("can't open LPC MEM"); exit(1); } if (ppc_platform != plat_ast_bmc) return; lpc_io_fd = open("/sys/kernel/debug/powerpc/lpc/io", O_RDWR); if (lpc_io_fd < 0) { perror("can't open LPC IO"); exit(1); } ast_ahb_readl = lpc_ahb_readl; ast_ahb_writel = lpc_ahb_writel; ast_copy_to_ahb = lpc_ast_copy_to_ahb; ast_copy_from_ahb = lpc_ast_copy_from_ahb; lpc_ahb_init(bmc_flash); } void close_devs(void) { if (lpc_io_fd < 0 || lpc_fw_fd < 0) return; if (ppc_platform != plat_ast_bmc) return; /* Restore flash base */ lpc_ahb_writel(lpc_old_flash_reg, LPC_CTRL_BASE + 0x88); } static void open_pci(bool bmc_flash) { /* XXX */ fprintf(stderr, "WARNING: PCI access method not implemented !\n"); fprintf(stderr, " Use -l or --lpc\n"); exit(1); } static void identify_platform(void) { FILE *cpuinfo; char *lptr = NULL; size_t lsize = 0; bool found = false; ppc_platform = plat_unknown; cpuinfo = fopen("/proc/cpuinfo", "r"); if (!cpuinfo) { perror("Can't open /proc/cpuinfo"); exit(1); } while(!found && getline(&lptr, &lsize, cpuinfo) >= 0) { if (!strncmp(lptr, "model", 5)) { if (strcasestr(lptr, "rhesus")) ppc_platform = plat_rhesus; else if (strcasestr(lptr, "palmetto")) ppc_platform = plat_ast_bmc; found = true; } free(lptr); lptr = NULL; lsize = 0; } } void open_devs(bool use_lpc, bool bmc_flash) { if (ppc_platform == plat_unknown) { fprintf(stderr, "Unsupported platform !\n"); exit(1); } if (use_lpc) open_lpc(bmc_flash); else open_pci(bmc_flash); } void check_platform(bool *has_sfc, bool *has_ast) { identify_platform(); *has_sfc = ppc_platform == plat_rhesus; *has_ast = ppc_platform == plat_ast_bmc; } skiboot-skiboot-5.1.13/external/pflash/progress.c000066400000000000000000000032111265204436200220330ustar00rootroot00000000000000#include #include #include #include #include static unsigned long progress_max; static unsigned int progress_pcent; static unsigned long progress_n_upd; static unsigned int progress_prevsec; static struct timespec progress_start; #define PROGRESS_CHARS 50 void progress_init(unsigned long count) { unsigned int i; progress_max = count; progress_pcent = 0; progress_n_upd = ULONG_MAX; progress_prevsec = UINT_MAX; printf("\r["); for (i = 0; i < PROGRESS_CHARS; i++) printf(" "); printf("] 0%%"); fflush(stdout); clock_gettime(CLOCK_MONOTONIC, &progress_start);} void progress_tick(unsigned long cur) { unsigned int pcent, i, pos, sec; struct timespec now; pcent = (cur * 100) / progress_max; if (progress_pcent == pcent && cur < progress_n_upd && cur < progress_max) return; progress_pcent = pcent; pos = (pcent * PROGRESS_CHARS) / 101; clock_gettime(CLOCK_MONOTONIC, &now); printf("\r["); for (i = 0; i <= pos; i++) printf("="); for (; i < PROGRESS_CHARS; i++) printf(" "); printf("] %d%%", pcent); sec = now.tv_sec - progress_start.tv_sec; if (sec >= 5 && pcent > 0) { unsigned int persec = cur / sec; unsigned int rem_sec; if (!persec) persec = 1; progress_n_upd = cur + persec; rem_sec = ((sec * 100) + (pcent / 2)) / pcent - sec; if (rem_sec > progress_prevsec) rem_sec = progress_prevsec; progress_prevsec = rem_sec; if (rem_sec < 60) printf(" ETA:%ds ", rem_sec); else { printf(" ETA:%d:%02d:%02d ", rem_sec / 3600, (rem_sec / 60) % 60, rem_sec % 60); } } fflush(stdout); } void progress_end(void) { printf("\n"); } skiboot-skiboot-5.1.13/external/pflash/progress.h000066400000000000000000000002571265204436200220470ustar00rootroot00000000000000#ifndef __PROGRESS_H #define __PROGRESS_H void progress_init(unsigned long count); void progress_tick(unsigned long cur); void progress_end(void); #endif /* __PROGRESS_H */ skiboot-skiboot-5.1.13/external/pflash/rules.mk000066400000000000000000000012131265204436200215060ustar00rootroot00000000000000.DEFAULT_GOAL := all CFLAGS = -O2 -Wall -I. LDFLAGS = -lrt OBJS = pflash.o progress.o version.o OBJS += libflash/libflash.o libflash/libffs.o libflash/ecc.o libflash/blocklevel.o libflash/file.o OBJS += common/arch_flash.o EXE = pflash CC = $(CROSS_COMPILE)gcc PFLASH_VERSION ?= $(shell ./make_version.sh $(EXE)) version.c: make_version.sh .version @(if [ "a$(PFLASH_VERSION)" = "a" ]; then \ echo "#error You need to set PFLASH_VERSION environment variable" > $@ ;\ else \ echo "const char version[] = \"$(PFLASH_VERSION)\";" ;\ fi) > $@ %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ $(EXE): $(OBJS) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ skiboot-skiboot-5.1.13/external/read_esel.sh000066400000000000000000000033021265204436200210260ustar00rootroot00000000000000#!/bin/bash # Copyright 2013-2014 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. set -e BMC_HOST=$1 RECORD_ID=$2 BMC_USER=admin BMC_PASS=admin if [ -z "$BMC_HOST" -o -z "$RECORD_ID" ]; then echo "Usage: $0 " echo "Example: $0 bmc 0xa > pel.bin" echo '' echo 'Record ids can be found using ipmitool with the "sel list" command. Records with' echo 'a description of "OEM record df" contain extended SEL information (in PEL' echo 'format) which can be extracted with this tool.' exit -1 fi # Convert a number into 2 hex-bytes in little-endian order function conv_le { echo $(for i in $(printf %04x $1 | grep -o .. | tac); do echo -n "0x$i "; done) } function conv_native { echo -n "0x${2}${1}" } record=$(conv_le $2) offset=0 progress=0 while [ $progress = 0 ]; do result=$(ipmitool -H ${BMC_HOST} -I lan -U ${BMC_USER} -P ${BMC_PASS} raw 0x32 0xf1 ${record} $(conv_le ${offset})) len=$(conv_native $(echo ${result} | cut -d " " -f 1-2)) progress=$(($(echo ${result} | cut -d " " -f 3))) data="$data "$(echo -n ${result} | cut -d " " -f 6-) offset=$(($offset + ${#data}/3)) done echo -n ${data} | cut -d " " -f 1-$(($len)) | xxd -r -p skiboot-skiboot-5.1.13/external/trace/000077500000000000000000000000001265204436200176475ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/trace/Makefile000066400000000000000000000003221265204436200213040ustar00rootroot00000000000000HOSTEND=$(shell uname -m | sed -e 's/^i.*86$$/LITTLE/' -e 's/^x86.*/LITTLE/' -e 's/^ppc.*/BIG/') CFLAGS=-g -Wall -DHAVE_$(HOSTEND)_ENDIAN -I../../include dump_trace: dump_trace.c clean: rm -f dump_trace *.o skiboot-skiboot-5.1.13/external/trace/dump_trace.c000066400000000000000000000121061265204436200221360ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "../../ccan/endian/endian.h" #include "../../ccan/short_types/short_types.h" #include /* Handles trace from debugfs (one record at a time) or file */ static bool get_trace(int fd, union trace *t, int *len) { void *dest = t; int r; /* Move down any extra we read last time. */ if (*len >= sizeof(t->hdr) && *len >= t->hdr.len_div_8 * 8) { u8 rlen = t->hdr.len_div_8 * 8; memmove(dest, dest + rlen, *len - rlen); *len -= rlen; } r = read(fd, dest + *len, sizeof(*t) - *len); if (r < 0) return false; *len += r; /* We should have a complete record. */ return *len >= sizeof(t->hdr) && *len >= t->hdr.len_div_8 * 8; } static void display_header(const struct trace_hdr *h) { static u64 prev_ts; u64 ts = be64_to_cpu(h->timestamp); printf("%16lx (+%8lx) [%03x] : ", ts, prev_ts ? (ts - prev_ts) : 0, be16_to_cpu(h->cpu)); prev_ts = ts; } static void dump_fsp_event(struct trace_fsp_event *t) { printf("FSP_EVT [st=%d] ", be16_to_cpu(t->fsp_state)); switch(be16_to_cpu(t->event)) { case TRACE_FSP_EVT_LINK_DOWN: printf("LINK DOWN"); break; case TRACE_FSP_EVT_DISR_CHG: printf("DISR CHANGE (0x%08x)", be32_to_cpu(t->data[0])); break; case TRACE_FSP_EVT_SOFT_RR: printf("SOFT R&R (DISR=0x%08x)", be32_to_cpu(t->data[0])); break; case TRACE_FSP_EVT_RR_COMPL: printf("R&R COMPLETE"); break; case TRACE_FSP_EVT_HDES_CHG: printf("HDES CHANGE (0x%08x)", be32_to_cpu(t->data[0])); break; case TRACE_FSP_EVT_POLL_IRQ: printf("%s HDIR=%08x CTL=%08x PSI_IRQ=%d", t->data[0] ? "IRQ " : "POLL", be32_to_cpu(t->data[1]), be32_to_cpu(t->data[2]), be32_to_cpu(t->data[3])); break; default: printf("Unknown %d (d: %08x %08x %08x %08x)", be16_to_cpu(t->event), be32_to_cpu(t->data[0]), be32_to_cpu(t->data[1]), be32_to_cpu(t->data[2]), be32_to_cpu(t->data[3])); } printf("\n"); } static void dump_opal_call(struct trace_opal *t) { unsigned int i, n; printf("OPAL CALL %"PRIu64, be64_to_cpu(t->token)); printf(" LR=0x%016"PRIx64" SP=0x%016"PRIx64, be64_to_cpu(t->lr), be64_to_cpu(t->sp)); n = (t->hdr.len_div_8 * 8 - offsetof(union trace, opal.r3_to_11)) / sizeof(u64); for (i = 0; i < n; i++) printf(" R%u=0x%016"PRIx64, i+3, be64_to_cpu(t->r3_to_11[i])); printf("\n"); } static void dump_fsp_msg(struct trace_fsp_msg *t) { unsigned int i; printf("FSP_MSG: CMD %u SEQ %u MOD %u SUB %u DLEN %u %s [", be32_to_cpu(t->word0) & 0xFFFF, be32_to_cpu(t->word0) >> 16, be32_to_cpu(t->word1) >> 8, be32_to_cpu(t->word1) & 0xFF, t->dlen, t->dir == TRACE_FSP_MSG_IN ? "IN" : (t->dir == TRACE_FSP_MSG_OUT ? "OUT" : "UNKNOWN")); for (i = 0; i < t->dlen; i++) printf("%s%02x", i ? " " : "", t->data[i]); printf("]\n"); } static void dump_uart(struct trace_uart *t) { switch(t->ctx) { case TRACE_UART_CTX_IRQ: printf(": IRQ IRQEN=%d IN_CNT=%d\n", !t->irq_state, be16_to_cpu(t->in_count)); break; case TRACE_UART_CTX_POLL: printf(": POLL IRQEN=%d IN_CNT=%d\n", !t->irq_state, be16_to_cpu(t->in_count)); break; case TRACE_UART_CTX_READ: printf(": READ IRQEN=%d IN_CNT=%d READ=%d\n", !t->irq_state, be16_to_cpu(t->in_count), t->cnt); break; default: printf(": ???? IRQEN=%d IN_CNT=%d\n", !t->irq_state, be16_to_cpu(t->in_count)); break; } } int main(int argc, char *argv[]) { int fd, len = 0; union trace t; const char *in = "/sys/kernel/debug/powerpc/opal-trace"; if (argc > 2) errx(1, "Usage: dump_trace [file]"); if (argv[1]) in = argv[1]; fd = open(in, O_RDONLY); if (fd < 0) err(1, "Opening %s", in); while (get_trace(fd, &t, &len)) { display_header(&t.hdr); switch (t.hdr.type) { case TRACE_REPEAT: printf("REPEATS: %u times\n", be32_to_cpu(t.repeat.num)); break; case TRACE_OVERFLOW: printf("**OVERFLOW**: %"PRIu64" bytes missed\n", be64_to_cpu(t.overflow.bytes_missed)); break; case TRACE_OPAL: dump_opal_call(&t.opal); break; case TRACE_FSP_MSG: dump_fsp_msg(&t.fsp_msg); break; case TRACE_FSP_EVENT: dump_fsp_event(&t.fsp_evt); break; case TRACE_UART: dump_uart(&t.uart); break; default: printf("UNKNOWN(%u) CPU %u length %u\n", t.hdr.type, be16_to_cpu(t.hdr.cpu), t.hdr.len_div_8 * 8); } } return 0; } skiboot-skiboot-5.1.13/external/trace/trace.c000066400000000000000000000060501265204436200211120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This example code shows how to read from the trace buffer. */ #include #include "../ccan/endian/endian.h" #include "../ccan/short_types/short_types.h" #include #include bool trace_empty(const struct tracebuf *tb) { const struct trace_repeat *rep; if (tb->rpos == tb->end) return true; /* * If we have a single element only, and it's a repeat buffer * we've already seen every repeat for (yet which may be * incremented in future), we're also empty. */ rep = (void *)tb->buf + be64_to_cpu(tb->rpos & tb->mask); if (be64_to_cpu(tb->end) != be64_to_cpu(tb->rpos) + sizeof(*rep)) return false; if (rep->type != TRACE_REPEAT) return false; if (be16_to_cpu(rep->num) != be32_to_cpu(tb->last_repeat)) return false; return true; } /* You can't read in parallel, so some locking required in caller. */ bool trace_get(union trace *t, struct tracebuf *tb) { u64 start, rpos; size_t len; len = sizeof(*t) < be32_to_cpu(tb->max_size) ? sizeof(*t) : be32_to_cpu(tb->max_size); if (trace_empty(tb)) return false; again: /* * The actual buffer is slightly larger than tbsize, so this * memcpy is always valid. */ memcpy(t, tb->buf + be64_to_cpu(tb->rpos & tb->mask), len); rmb(); /* read barrier, so we read tb->start after copying record. */ start = be64_to_cpu(tb->start); rpos = be64_to_cpu(tb->rpos); /* Now, was that overwritten? */ if (rpos < start) { /* Create overflow record. */ t->overflow.unused64 = 0; t->overflow.type = TRACE_OVERFLOW; t->overflow.len_div_8 = sizeof(t->overflow) / 8; t->overflow.bytes_missed = cpu_to_be64(start - rpos); tb->rpos = cpu_to_be64(start); return true; } /* Repeat entries need special handling */ if (t->hdr.type == TRACE_REPEAT) { u32 num = be16_to_cpu(t->repeat.num); /* In case we've read some already... */ t->repeat.num = cpu_to_be16(num - be32_to_cpu(tb->last_repeat)); /* Record how many repeats we saw this time. */ tb->last_repeat = cpu_to_be32(num); /* Don't report an empty repeat buffer. */ if (t->repeat.num == 0) { /* * This can't be the last buffer, otherwise * trace_empty would have returned true. */ assert(be64_to_cpu(tb->end) > rpos + t->hdr.len_div_8 * 8); /* Skip to next entry. */ tb->rpos = cpu_to_be64(rpos + t->hdr.len_div_8 * 8); tb->last_repeat = 0; goto again; } } else { tb->last_repeat = 0; tb->rpos = cpu_to_be64(rpos + t->hdr.len_div_8 * 8); } return true; } skiboot-skiboot-5.1.13/external/trace/trace.h000066400000000000000000000014261265204436200211210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Is this tracebuf empty? */ bool trace_empty(const struct tracebuf *tracebuf); /* Get the next trace from this buffer (false if empty). */ bool trace_get(union trace *t, struct tracebuf *tb); skiboot-skiboot-5.1.13/external/xscom-utils/000077500000000000000000000000001265204436200210405ustar00rootroot00000000000000skiboot-skiboot-5.1.13/external/xscom-utils/Makefile000066400000000000000000000004631265204436200225030ustar00rootroot00000000000000all: getscom putscom VERSION=0.1 CFLAGS=-O2 -g -Wall -m64 -DVERSION=$(VERSION) getscom: getscom.c xscom.c $(CC) $(CFLAGS) -o $@ $^ putscom: putscom.c xscom.c $(CC) $(CFLAGS) -o $@ $^ .PHONY: clean clean: rm -rf getscom putscom .PHONY: distclean distclean: clean rm -rf *.c~ *.h~ *.i *.s Makefile~ skiboot-skiboot-5.1.13/external/xscom-utils/getscom.c000066400000000000000000000053561265204436200226560ustar00rootroot00000000000000#include #include #include #include #include #include #include "xscom.h" static void print_usage(void) { printf("usage: getscom [-c|--chip chip-id] addr\n"); printf(" getscom -l|--list-chips\n"); printf(" getscom -v|--version\n"); } static void print_chip_info(uint32_t chip_id) { uint64_t f000f, cfam_id; const char *name; char uname_buf[64]; int rc; rc = xscom_read(chip_id, 0xf000f, &f000f); if (rc) return; cfam_id = f000f >> 44; switch(cfam_id & 0xff) { case 0xf9: name = "P7 processor"; break; case 0xe8: name = "P7+ processor"; break; case 0xef: name = "P8E (Murano) processor"; break; case 0xea: name = "P8 (Venice) processor"; break; case 0xe9: name = "Centaur memory buffer"; break; default: snprintf(uname_buf, sizeof(uname_buf), "Unknown ID 0x%02lx", cfam_id & 0xff); name = uname_buf; } printf("%08x | DD%lx.%lx | %s\n", chip_id, (cfam_id >> 16) & 0xf, (cfam_id >> 8) & 0xf, name); } #define VERSION_STR _str(VERSION) #define _str(s) __str(s) #define __str(s) #s int main(int argc, char *argv[]) { uint64_t val, addr = -1ull; uint32_t def_chip, chip_id = 0xffffffff; bool show_help = false; bool list_chips = false; bool show_version = false; bool no_work = false; int rc; while(1) { static struct option long_opts[] = { {"chip", required_argument, NULL, 'c'}, {"list-chips", no_argument, NULL, 'l'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, }; int c, oidx = 0; c = getopt_long(argc, argv, "-c:hlv", long_opts, &oidx); if (c == EOF) break; switch(c) { case 1: addr = strtoull(optarg, NULL, 16); break; case 'c': chip_id = strtoul(optarg, NULL, 0); break; case 'h': show_help = true; break; case 'l': list_chips = true; break; case 'v': show_version = true; break; default: exit(1); } } if (addr == -1ull) no_work = true; if (no_work && !list_chips && !show_version && !show_help) { fprintf(stderr, "Invalid or missing address\n"); print_usage(); exit(1); } if (show_version) printf("xscom utils version %s\n", VERSION_STR); if (show_help) print_usage(); if (no_work && !list_chips) return 0; def_chip = xscom_init(); if (def_chip == 0xffffffff) { fprintf(stderr, "No valid XSCOM chip found\n"); exit(1); } if (list_chips) { printf("Chip ID | Rev | Chip type\n"); printf("---------|-------|--------\n"); xscom_for_each_chip(print_chip_info); } if (no_work) return 0; if (chip_id == 0xffffffff) chip_id = def_chip; rc = xscom_read(chip_id, addr, &val); if (rc) { fprintf(stderr,"Error %d reading XSCOM\n", rc); exit(1); } printf("%" PRIx64 "\n", val); return 0; } skiboot-skiboot-5.1.13/external/xscom-utils/putscom.c000066400000000000000000000037201265204436200227000ustar00rootroot00000000000000#include #include #include #include #include #include #include "xscom.h" static void print_usage(void) { printf("usage: putscom [-c|--chip chip-id] addr value\n"); printf(" putscom -v|--version\n"); } #define VERSION_STR _str(VERSION) #define _str(s) __str(s) #define __str(s) #s int main(int argc, char *argv[]) { uint64_t val = -1ull, addr = -1ull; uint32_t def_chip, chip_id = 0xffffffff; bool show_help = false, got_addr = false, got_val = false; bool show_version = false; bool no_work = false; int rc; while(1) { static struct option long_opts[] = { {"chip", required_argument, NULL, 'c'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, }; int c, oidx = 0; c = getopt_long(argc, argv, "-c:hv", long_opts, &oidx); if (c == EOF) break; switch(c) { case 1: if (!got_addr) { addr = strtoull(optarg, NULL, 16); got_addr = true; break; } val = strtoull(optarg, NULL, 16); got_val = true; break; case 'c': chip_id = strtoul(optarg, NULL, 0); break; case 'v': show_version = true; break; case 'h': show_help = true; break; default: exit(1); } } if (!got_addr || !got_val) no_work = true; if (no_work && !show_version && !show_help) { fprintf(stderr, "Invalid or missing address/value\n"); print_usage(); exit(1); } if (show_version) printf("xscom utils version %s\n", VERSION_STR); if (show_help) print_usage(); if (no_work) return 0; def_chip = xscom_init(); if (def_chip == 0xffffffff) { fprintf(stderr, "No valid XSCOM chip found\n"); exit(1); } if (chip_id == 0xffffffff) chip_id = def_chip; rc = xscom_write(chip_id, addr, val); if (rc) { fprintf(stderr,"Error %d writing XSCOM\n", rc); exit(1); } rc = xscom_read(chip_id, addr, &val); if (rc) { fprintf(stderr,"Error %d reading XSCOM\n", rc); exit(1); } printf("%" PRIx64 "\n", val); return 0; } skiboot-skiboot-5.1.13/external/xscom-utils/xscom.c000066400000000000000000000067001265204436200223400ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xscom.h" #define XSCOM_BASE_PATH "/sys/kernel/debug/powerpc/scom" struct xscom_chip { struct xscom_chip *next; uint32_t chip_id; int fd; }; static struct xscom_chip *xscom_chips; void xscom_for_each_chip(void (*cb)(uint32_t chip_id)) { struct xscom_chip *c; for (c = xscom_chips; c; c = c->next) cb(c->chip_id); } static uint32_t xscom_add_chip(const char *base_path, const char *dname) { char nbuf[strlen(base_path) + strlen(dname) + 16]; struct xscom_chip *chip; int fd; snprintf(nbuf, sizeof(nbuf), "%s/%s/access", base_path, dname); fd = open(nbuf, O_RDWR); if (fd < 0) { perror("Failed to open SCOM access file"); exit(1); } chip = malloc(sizeof(*chip)); assert(chip); memset(chip, 0, sizeof(*chip)); chip->fd = fd; chip->chip_id = strtoul(dname, NULL, 16); chip->next = xscom_chips; xscom_chips = chip; return chip->chip_id; } static bool xscom_check_dirname(const char *n) { while(*n) { char c = toupper(*(n++)); if ((c < 'A' || c > 'Z') && (c < '0' || c > '9')) return false; } return true; } static uint32_t xscom_scan_chips(const char *base_path) { int i, nfiles; struct dirent **filelist; uint32_t lower = 0xffffffff; nfiles = scandir(base_path, &filelist, NULL, alphasort); if (nfiles < 0) { perror("Error accessing sysfs scom directory"); exit(1); } if (nfiles == 0) { fprintf(stderr, "No SCOM dir found in sysfs\n"); exit(1); } for (i = 0; i < nfiles; i++) { struct dirent *d = filelist[i]; uint32_t id; if (d->d_type != DT_DIR) continue; if (!xscom_check_dirname(d->d_name)) continue; id = xscom_add_chip(base_path, d->d_name); if (id < lower) lower = id; free(d); } free(filelist); return lower; } static struct xscom_chip *xscom_find_chip(uint32_t chip_id) { struct xscom_chip *c; for (c = xscom_chips; c; c = c->next) if (c->chip_id == chip_id) return c; return NULL; } static uint64_t xscom_mangle_addr(uint64_t addr) { if (addr & (1ull << 63)) addr |= (1ull << 59); return addr << 3; } int xscom_read(uint32_t chip_id, uint64_t addr, uint64_t *val) { struct xscom_chip *c = xscom_find_chip(chip_id); int rc; if (!c) return -ENODEV; addr = xscom_mangle_addr(addr); lseek64(c->fd, addr, SEEK_SET); rc = read(c->fd, val, 8); if (rc < 0) return -errno; if (rc != 8) return -EIO; return 0; } int xscom_write(uint32_t chip_id, uint64_t addr, uint64_t val) { struct xscom_chip *c = xscom_find_chip(chip_id); int rc; if (!c) return -ENODEV; addr = xscom_mangle_addr(addr); lseek64(c->fd, addr, SEEK_SET); rc = write(c->fd, &val, 8); if (rc < 0) return -errno; if (rc != 8) return -EIO; return 0; } int xscom_read_ex(uint32_t ex_target_id, uint64_t addr, uint64_t *val) { uint32_t chip_id = ex_target_id >> 4;; addr |= (ex_target_id & 0xf) << 24; /* XXX TODO: Special wakeup ? */ return xscom_read(chip_id, addr, val); } int xscom_write_ex(uint32_t ex_target_id, uint64_t addr, uint64_t val) { uint32_t chip_id = ex_target_id >> 4;; addr |= (ex_target_id & 0xf) << 24; /* XXX TODO: Special wakeup ? */ return xscom_write(chip_id, addr, val); } uint32_t xscom_init(void) { return xscom_scan_chips(XSCOM_BASE_PATH); } skiboot-skiboot-5.1.13/external/xscom-utils/xscom.h000066400000000000000000000007421265204436200223450ustar00rootroot00000000000000#ifndef __XSCOM_H #define __XSCOM_H #include extern int xscom_read(uint32_t chip_id, uint64_t addr, uint64_t *val); extern int xscom_write(uint32_t chip_id, uint64_t addr, uint64_t val); extern int xscom_read_ex(uint32_t ex_target_id, uint64_t addr, uint64_t *val); extern int xscom_write_ex(uint32_t ex_target_id, uint64_t addr, uint64_t val); extern void xscom_for_each_chip(void (*cb)(uint32_t chip_id)); extern uint32_t xscom_init(void); #endif /* __XSCOM_H */ skiboot-skiboot-5.1.13/extract-gcov.c000066400000000000000000000150371265204436200175070ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef u32 gcov_unsigned_int; /* You will need to pass -DTARGET__GNUC__=blah when building */ #if TARGET__GNUC__ >= 5 && TARGET__GNUC_MINOR__ >= 1 #define GCOV_COUNTERS 10 #else #if TARGET__GNUC__ >= 4 && TARGET__GNUC_MINOR__ >= 9 #define GCOV_COUNTERS 9 #else #define GCOV_COUNTERS 8 #endif /* GCC 4.9 */ #endif /* GCC 5.1 */ typedef u64 gcov_type; struct gcov_info { gcov_unsigned_int version; u32 _padding; struct gcov_info *next; gcov_unsigned_int stamp; u32 _padding2; const char *filename; u64 merge[GCOV_COUNTERS]; unsigned int n_functions; u32 _padding3; struct gcov_fn_info **functions; }; struct gcov_ctr_info { gcov_unsigned_int num; u32 _padding; gcov_type *values; }__attribute__((packed)); struct gcov_fn_info { const struct gcov_info *key; unsigned int ident; unsigned int lineno_checksum; unsigned int cfg_checksum; u32 _padding; // struct gcov_ctr_info ctrs[0]; } __attribute__((packed)); /* We have a list of all gcov info set up at startup */ struct gcov_info *gcov_info_list; #define SKIBOOT_OFFSET 0x30000000 /* Endian of the machine producing the gcda. Which mean BE. * because skiboot is BE. * If skiboot is ever LE, go have fun. */ static size_t write_u32(int fd, u32 _v) { u32 v = htobe32(_v); return write(fd, &v, sizeof(v)); } static size_t write_u64(int fd, u64 v) { u32 b[2]; b[0] = htobe32(v & 0xffffffffUL); b[1] = htobe32(v >> 32); write(fd, &b[0], sizeof(u32)); write(fd, &b[1], sizeof(u32)); return sizeof(u64); } #define GCOV_DATA_MAGIC ((unsigned int) 0x67636461) #define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000) #define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000) #define GCOV_TAG_FOR_COUNTER(count) \ (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17)) // gcc 4.7/4.8 specific #define GCOV_TAG_FUNCTION_LENGTH 3 size_t skiboot_dump_size = 0x240000; static inline const char* SKIBOOT_ADDR(const char* addr, const void* p) { const char* r= (addr + (be64toh((const u64)p) - SKIBOOT_OFFSET)); assert(r < (addr + skiboot_dump_size)); return r; } static int counter_active(struct gcov_info *info, unsigned int type) { return info->merge[type] ? 1 : 0; } static void write_gcda(char *addr, struct gcov_info* gi) { const char* filename = SKIBOOT_ADDR(addr, gi->filename); int fd; u32 fn; struct gcov_fn_info *fn_info; struct gcov_fn_info **functions; struct gcov_ctr_info *ctr_info; u32 ctr; u32 cv; printf("Writing %s\n", filename); fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); if (fd < 0) { fprintf(stderr, "Error opening file %s: %d %s\n", filename, errno, strerror(errno)); exit(EXIT_FAILURE); } write_u32(fd, GCOV_DATA_MAGIC); write_u32(fd, be32toh(gi->version)); write_u32(fd, be32toh(gi->stamp)); printf("version: %x\tstamp: %d\n", be32toh(gi->version), be32toh(gi->stamp)); printf("nfunctions: %d \n", be32toh(gi->n_functions)); for(fn = 0; fn < be32toh(gi->n_functions); fn++) { functions = (struct gcov_fn_info**) SKIBOOT_ADDR(addr, gi->functions); fn_info = (struct gcov_fn_info*) SKIBOOT_ADDR(addr, functions[fn]); printf("function: %p\n", (void*)be64toh((u64)functions[fn])); write_u32(fd, GCOV_TAG_FUNCTION); write_u32(fd, GCOV_TAG_FUNCTION_LENGTH); write_u32(fd, be32toh(fn_info->ident)); write_u32(fd, be32toh(fn_info->lineno_checksum)); write_u32(fd, be32toh(fn_info->cfg_checksum)); ctr_info = (struct gcov_ctr_info*) ((char*)fn_info + sizeof(struct gcov_fn_info)); for(ctr = 0; ctr < GCOV_COUNTERS; ctr++) { if (!counter_active(gi, ctr)) continue; write_u32(fd, (GCOV_TAG_FOR_COUNTER(ctr))); write_u32(fd, be32toh(ctr_info->num)*2); printf(" ctr %d gcov_ctr_info->num %u\n", ctr, be32toh(ctr_info->num)); for(cv = 0; cv < be32toh(ctr_info->num); cv++) { gcov_type *ctrv = (gcov_type *) SKIBOOT_ADDR(addr, ctr_info->values); //printf("%lx\n", be64toh(ctrv[cv])); write_u64(fd, be64toh(ctrv[cv])); } ctr_info++; } } close(fd); } int main(int argc, char *argv[]) { int r; int fd; struct stat sb; char *addr; u64 gcov_list_addr; printf("sizes: %zu %zu %zu %zu\n", sizeof(gcov_unsigned_int), sizeof(struct gcov_ctr_info), sizeof(struct gcov_fn_info), sizeof(struct gcov_info)); printf("TARGET GNUC: %d.%d\n", TARGET__GNUC__, TARGET__GNUC_MINOR__); printf("GCOV_COUNTERS: %d\n", GCOV_COUNTERS); if (argc < 3) { fprintf(stderr, "Usage:\n" "\t%s skiboot.dump gcov_offset\n\n", argv[0]); return -1; } /* argv[1] = skiboot.dump */ fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "Cannot open dump: %s (error %d %s)\n", argv[1], errno, strerror(errno)); exit(-1); } r = fstat(fd, &sb); if (r < 0) { fprintf(stderr, "Cannot stat dump, %d %s\n", errno, strerror(errno)); exit(-1); } addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); assert(addr != NULL); skiboot_dump_size = sb.st_size; printf("Skiboot memory dump %p - %p\n", (void*)SKIBOOT_OFFSET, (void*)SKIBOOT_OFFSET+sb.st_size); gcov_list_addr = strtoll(argv[2], NULL, 0); gcov_list_addr = (u64)(addr + (gcov_list_addr - SKIBOOT_OFFSET)); gcov_list_addr = be64toh(*(u64*)gcov_list_addr); printf("Skiboot gcov_info_list at %p\n", (void*)gcov_list_addr); do { gcov_info_list = (struct gcov_info *)(addr + (gcov_list_addr - SKIBOOT_OFFSET)); write_gcda(addr, gcov_info_list); gcov_list_addr = be64toh((u64)gcov_info_list->next); } while(gcov_list_addr); munmap(addr, sb.st_size); close(fd); return 0; } skiboot-skiboot-5.1.13/hdata/000077500000000000000000000000001265204436200160105ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hdata/Makefile.inc000066400000000000000000000003561265204436200202240ustar00rootroot00000000000000# -*-Makefile-*- SUBDIRS += hdata HDATA_OBJS = spira.o paca.o pcia.o hdif.o memory.o fsp.o iohub.o vpd.o slca.o HDATA_OBJS += cpu-common.o vpd-common.o hostservices.o DEVSRC_OBJ = hdata/built-in.o $(DEVSRC_OBJ): $(HDATA_OBJS:%=hdata/%) skiboot-skiboot-5.1.13/hdata/cpu-common.c000066400000000000000000000230021265204436200202260ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include "hdata.h" struct dt_node * add_core_common(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, const struct sppaca_cpu_timebase *tb, uint32_t int_server, bool okay) { const char *name; struct dt_node *cpu; uint32_t version; uint64_t freq; const uint8_t pa_features[] = { 6, 0, 0xf6, 0x3f, 0xc7, 0x00, 0x80, 0xc0 }; prlog(PR_INFO, " Cache: I=%u D=%u/%u/%u/%u\n", be32_to_cpu(cache->icache_size_kb), be32_to_cpu(cache->l1_dcache_size_kb), be32_to_cpu(cache->l2_dcache_size_kb), be32_to_cpu(cache->l3_dcache_size_kb), be32_to_cpu(cache->l35_dcache_size_kb)); /* Use the boot CPU PVR to make up a CPU name in the device-tree * since the HDAT doesn't seem to tell.... */ version = mfspr(SPR_PVR); switch(PVR_TYPE(version)) { case PVR_TYPE_P7: name = "PowerPC,POWER7"; break; case PVR_TYPE_P7P: name = "PowerPC,POWER7+"; break; case PVR_TYPE_P8E: case PVR_TYPE_P8: case PVR_TYPE_P8NVL: name = "PowerPC,POWER8"; break; default: name = "PowerPC,Unknown"; } cpu = dt_new_addr(cpus, name, int_server); assert(cpu); dt_add_property_string(cpu, "device_type", "cpu"); dt_add_property_string(cpu, "status", okay ? "okay" : "bad"); dt_add_property_cells(cpu, "reg", int_server); dt_add_property_cells(cpu, "cpu-version", version); dt_add_property(cpu, "64-bit", NULL, 0); dt_add_property(cpu, "32-64-bridge", NULL, 0); dt_add_property(cpu, "graphics", NULL, 0); dt_add_property(cpu, "general-purpose", NULL, 0); dt_add_property_cells(cpu, "ibm,processor-segment-sizes", 0x1c, 0x28, 0xffffffff, 0xffffffff); dt_add_property_cells(cpu, "ibm,processor-page-sizes", 0xc, 0x10, 0x18, 0x22); /* Page size encodings appear to be the same for P7 and P8 */ dt_add_property_cells(cpu, "ibm,segment-page-sizes", 0x0c, 0x000, 3, 0x0c, 0x0000, /* 4K seg 4k pages */ 0x10, 0x0007, /* 4K seg 64k pages */ 0x18, 0x0038, /* 4K seg 16M pages */ 0x10, 0x110, 2, 0x10, 0x0001, /* 64K seg 64k pages */ 0x18, 0x0008, /* 64K seg 16M pages */ 0x18, 0x100, 1, 0x18, 0x0000, /* 16M seg 16M pages */ 0x22, 0x120, 1, 0x22, 0x0003); /* 16G seg 16G pages */ dt_add_property(cpu, "ibm,pa-features", pa_features, sizeof(pa_features)); dt_add_property_cells(cpu, "ibm,slb-size", 0x20); dt_add_property_cells(cpu, "ibm,vmx", 0x2); dt_add_property_cells(cpu, "ibm,dfp", 0x2); dt_add_property_cells(cpu, "ibm,purr", 0x1); dt_add_property_cells(cpu, "ibm,spurr", 0x1); /* * Do not create "clock-frequency" if the frequency doesn't * fit in a single cell */ freq = ((uint64_t)be32_to_cpu(tb->actual_clock_speed)) * 1000000ul; if (freq <= 0xfffffffful) dt_add_property_cells(cpu, "clock-frequency", freq); dt_add_property_cells(cpu, "ibm,extended-clock-frequency", hi32(freq), lo32(freq)); /* FIXME: Hardcoding is bad. */ dt_add_property_cells(cpu, "timebase-frequency", 512000000); dt_add_property_cells(cpu, "ibm,extended-timebase-frequency", 0, 512000000); dt_add_property_cells(cpu, "reservation-granule-size", be32_to_cpu(cache->reservation_size)); dt_add_property_cells(cpu, "d-tlb-size", be32_to_cpu(cache->dtlb_entries)); dt_add_property_cells(cpu, "i-tlb-size", be32_to_cpu(cache->itlb_entries)); /* Assume unified TLB */ dt_add_property_cells(cpu, "tlb-size", be32_to_cpu(cache->dtlb_entries)); dt_add_property_cells(cpu, "d-tlb-sets", be32_to_cpu(cache->dtlb_assoc_sets)); dt_add_property_cells(cpu, "i-tlb-sets", be32_to_cpu(cache->itlb_assoc_sets)); dt_add_property_cells(cpu, "tlb-sets", be32_to_cpu(cache->dtlb_assoc_sets)); dt_add_property_cells(cpu, "d-cache-block-size", be32_to_cpu(cache->dcache_block_size)); dt_add_property_cells(cpu, "i-cache-block-size", be32_to_cpu(cache->icache_block_size)); dt_add_property_cells(cpu, "d-cache-size", be32_to_cpu(cache->l1_dcache_size_kb)*1024); dt_add_property_cells(cpu, "i-cache-size", be32_to_cpu(cache->icache_size_kb)*1024); dt_add_property_cells(cpu, "i-cache-sets", be32_to_cpu(cache->icache_assoc_sets)); dt_add_property_cells(cpu, "d-cache-sets", be32_to_cpu(cache->dcache_assoc_sets)); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(cpu, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l1_dcache_line_size != cache->dcache_block_size) dt_add_property_cells(cpu, "d-cache-line-size", be32_to_cpu(cache->l1_dcache_line_size)); return cpu; } void add_core_attr(struct dt_node *cpu, uint32_t attr) { if (attr & CPU_ATTR_UNIFIED_PL1) dt_add_property(cpu, "cache-unified", NULL, 0); if (attr & CPU_ATTR_SPLIT_TLB) dt_add_property(cpu, "tlb-split", NULL, 0); if (attr & CPU_ATTR_TLBIA) dt_add_property(cpu, "tlbia", NULL, 0); if (attr & CPU_ATTR_PERF_MONITOR) dt_add_property_cells(cpu, "performance-monitor", 0, 1); if (attr & CPU_ATTR_EXTERN_CONT) dt_add_property(cpu, "external-control", NULL, 0); } static struct dt_node *create_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, const char *name, uint32_t unit_addr, int okay) { struct dt_node *node; node = dt_new_addr(cpus, name, unit_addr); assert(node); dt_add_property_string(node, "device_type", "cache"); dt_add_property_cells(node, "reg", unit_addr); dt_add_property_string(node, "status", okay ? "okay" : "bad"); dt_add_property(node, "cache-unified", NULL, 0); /* Assume cache associavitity sets is same for L2, L3 and L3.5 */ dt_add_property_cells(node, "d-cache-sets", be32_to_cpu(cache->l2_cache_assoc_sets)); dt_add_property_cells(node, "i-cache-sets", be32_to_cpu(cache->l2_cache_assoc_sets)); return node; } static struct dt_node *l35_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t unit_addr, int okay) { struct dt_node *node; node = create_cache_node(cpus, cache, "l35-cache", unit_addr, okay); dt_add_property_cells(node, "d-cache-size", be32_to_cpu(cache->l35_dcache_size_kb) * 1024); dt_add_property_cells(node, "i-cache-size", be32_to_cpu(cache->l35_dcache_size_kb) * 1024); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(node, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l35_cache_line_size != cache->dcache_block_size) dt_add_property_cells(node, "d-cache-line-size", be32_to_cpu(cache->l35_cache_line_size)); return node; } static struct dt_node *l3_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t unit_addr, int okay) { struct dt_node *node; node = create_cache_node(cpus, cache, "l3-cache", unit_addr, okay); dt_add_property_cells(node, "d-cache-size", be32_to_cpu(cache->l3_dcache_size_kb) * 1024); dt_add_property_cells(node, "i-cache-size", be32_to_cpu(cache->l3_dcache_size_kb) * 1024); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(node, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l3_line_size != cache->dcache_block_size) dt_add_property_cells(node, "d-cache-line-size", be32_to_cpu(cache->l3_line_size)); return node; } static struct dt_node *l2_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t unit_addr, int okay) { struct dt_node *node; node = create_cache_node(cpus, cache, "l2-cache", unit_addr, okay); dt_add_property_cells(node, "d-cache-size", be32_to_cpu(cache->l2_dcache_size_kb) * 1024); dt_add_property_cells(node, "i-cache-size", be32_to_cpu(cache->l2_dcache_size_kb) * 1024); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(node, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l2_line_size != cache->dcache_block_size) dt_add_property_cells(node, "d-cache-line-size", be32_to_cpu(cache->l2_line_size)); return node; } uint32_t add_core_cache_info(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t int_server, int okay) { struct dt_node *l2_node, *l3_node, *l35_node; uint32_t unit_addr; /* Use Processor Interrupt Line to genarate cache unit address */ unit_addr = 0x20 << 24 | int_server; l2_node = l2_cache_node(cpus, cache, unit_addr, okay); unit_addr = 0x30 << 24 | int_server; l3_node = l3_cache_node(cpus, cache, unit_addr, okay); /* Represents the next level of cache in the memory hierarchy */ dt_add_property_cells(l2_node, "l2-cache", l3_node->phandle); if (be32_to_cpu(cache->l35_dcache_size_kb)) { unit_addr = 0x35 << 24 | int_server; l35_node = l35_cache_node(cpus, cache, unit_addr, okay); dt_add_property_cells(l3_node, "l2-cache", l35_node->phandle); } return l2_node->phandle; } skiboot-skiboot-5.1.13/hdata/fsp.c000066400000000000000000000132671265204436200167550ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include #include #include "hdata.h" static struct dt_node *fsp_create_node(const void *spss, int i, struct dt_node *parent) { const struct spss_sp_impl *sp_impl; struct dt_node *node; unsigned int mask; /* Find an check the SP Implementation structure */ sp_impl = HDIF_get_idata(spss, SPSS_IDATA_SP_IMPL, NULL); if (!CHECK_SPPTR(sp_impl)) { prerror("FSP #%d: SPSS/SP_Implementation not found !\n", i); return NULL; } prlog(PR_INFO, "FSP #%d: FSP HW version %d, SW version %d," " chip DD%d.%d\n", i, be16_to_cpu(sp_impl->hw_version), be16_to_cpu(sp_impl->sw_version), sp_impl->chip_version >> 4, sp_impl->chip_version & 0xf); mask = SPSS_SP_IMPL_FLAGS_INSTALLED | SPSS_SP_IMPL_FLAGS_FUNCTIONAL; if ((be16_to_cpu(sp_impl->func_flags) & mask) != mask) { prerror("FSP #%d: FSP not installed or not functional\n", i); return NULL; } node = dt_new_addr(parent, "fsp", i); assert(node); dt_add_property_cells(node, "reg", i); if (be16_to_cpu(sp_impl->hw_version) == 1) { dt_add_property_strings(node, "compatible", "ibm,fsp", "ibm,fsp1"); /* Offset into the FSP MMIO space where the mailbox * registers are */ /* seen in the FSP1 spec */ dt_add_property_cells(node, "reg-offset", 0xb0016000); } else if (be16_to_cpu(sp_impl->hw_version) == 2) { dt_add_property_strings(node, "compatible", "ibm,fsp", "ibm,fsp2"); dt_add_property_cells(node, "reg-offset", 0xb0011000); } dt_add_property_cells(node, "hw-version", be16_to_cpu(sp_impl->hw_version)); dt_add_property_cells(node, "sw-version", be16_to_cpu(sp_impl->sw_version)); if (be16_to_cpu(sp_impl->func_flags) & SPSS_SP_IMPL_FLAGS_PRIMARY) dt_add_property(node, "primary", NULL, 0); return node; } static uint32_t fsp_create_link(const struct spss_iopath *iopath, int index, int fsp_index) { struct dt_node *node; const char *ststr; bool current = false; bool working = false; uint32_t chip_id; switch(be16_to_cpu(iopath->psi.link_status)) { case SPSS_IO_PATH_PSI_LINK_BAD_FRU: ststr = "Broken"; break; case SPSS_IO_PATH_PSI_LINK_CURRENT: ststr = "Active"; current = working = true; break; case SPSS_IO_PATH_PSI_LINK_BACKUP: ststr = "Backup"; working = true; break; default: ststr = "Unknown"; } prlog(PR_DEBUG, "FSP #%d: IO PATH %d is %s PSI Link, GXHB at %llx\n", fsp_index, index, ststr, (long long)be64_to_cpu(iopath->psi.gxhb_base)); chip_id = pcid_to_chip_id(be32_to_cpu(iopath->psi.proc_chip_id)); node = dt_find_compatible_node_on_chip(dt_root, NULL, "ibm,psihb-x", chip_id); if (!node) { prerror("FSP #%d: Can't find psihb node for link %d\n", fsp_index, index); } else { if (current) dt_add_property(node, "boot-link", NULL, 0); dt_add_property_strings(node, "status", working ? "ok" : "bad"); } return chip_id; } static void fsp_create_links(const void *spss, int index, struct dt_node *fsp_node) { uint32_t *links = NULL; unsigned int i, lp, lcount = 0; int count; count = HDIF_get_iarray_size(spss, SPSS_IDATA_SP_IOPATH); if (count < 0) { prerror("FSP #%d: Can't find IO PATH array size !\n", index); return; } prlog(PR_DEBUG, "FSP #%d: Found %d IO PATH\n", index, count); /* Iterate all links */ for (i = 0; i < count; i++) { const struct spss_iopath *iopath; unsigned int iopath_sz; uint32_t chip; iopath = HDIF_get_iarray_item(spss, SPSS_IDATA_SP_IOPATH, i, &iopath_sz); if (!CHECK_SPPTR(iopath)) { prerror("FSP #%d: Can't find IO PATH %d\n", index, i); break; } if (be16_to_cpu(iopath->iopath_type) != SPSS_IOPATH_TYPE_PSI) { prerror("FSP #%d: Unsupported IO PATH %d type 0x%04x\n", index, i, iopath->iopath_type); continue; } chip = fsp_create_link(iopath, i, index); lp = lcount++; links = realloc(links, 4 * lcount); links[lp] = chip; } if (links) dt_add_property(fsp_node, "ibm,psi-links", links, lcount * 4); free(links); } void fsp_parse(void) { const void *base_spss, *spss; struct dt_node *fsp_root, *fsp_node; int i; /* * Note on DT representation of the PSI links and FSPs: * * We create a XSCOM node for each PSI host bridge(one per chip), * * This is done in spira.c * * We do not create the /psi MMIO variant at this stage, it will * be added by the psi driver in skiboot. * * We do not put the FSP(s) as children of these. Instead, we create * a top-level /fsps node with the FSPs as children. * * Each FSP then has a "links" property which is an array of chip IDs */ /* Find SPSS in SPIRA */ base_spss = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG); if (!base_spss) { prlog(PR_WARNING, "FSP: No SPSS in SPIRA !\n"); return; } fsp_root = dt_new(dt_root, "fsps"); assert(fsp_root); dt_add_property_cells(fsp_root, "#address-cells", 1); dt_add_property_cells(fsp_root, "#size-cells", 0); /* Iterate FSPs in SPIRA */ for_each_ntuple_idx(&spira.ntuples.sp_subsys, spss, i, SPSS_HDIF_SIG) { fsp_node = fsp_create_node(spss, i, fsp_root); if (fsp_node) fsp_create_links(spss, i, fsp_node); } } skiboot-skiboot-5.1.13/hdata/hdata.h000066400000000000000000000035201265204436200172420ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __HDATA_H #define __HDATA_H struct dt_node; extern void memory_parse(void); extern void paca_parse(void); extern bool pcia_parse(void); extern void fsp_parse(void); extern void io_parse(void); extern struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr, int indx_fru, int indx_vpd); extern void vpd_parse(void); extern struct dt_node *find_xscom_for_chip(uint32_t chip_id); extern uint32_t pcid_to_chip_id(uint32_t proc_chip_id); extern struct dt_node *add_core_common(struct dt_node *cpus, const struct sppaca_cpu_cache *cache, const struct sppaca_cpu_timebase *tb, uint32_t int_server, bool okay); extern void add_core_attr(struct dt_node *cpu, uint32_t attr); extern uint32_t add_core_cache_info(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t int_server, int okay); extern const struct slca_entry *slca_get_entry(uint16_t slca_index); extern const char *slca_get_vpd_name(uint16_t slca_index); extern const char *slca_get_loc_code_index(uint16_t slca_index); extern void slca_vpd_add_loc_code(struct dt_node *node, uint16_t slca_index); extern void slca_dt_add_sai_node(void); extern bool hservices_from_hdat(const void *fdt, size_t size); #endif /* __HDATA_H */ skiboot-skiboot-5.1.13/hdata/hdif.c000066400000000000000000000067071265204436200171000ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "hdif.h" const void *HDIF_get_idata(const struct HDIF_common_hdr *hdif, unsigned int di, unsigned int *size) { const struct HDIF_common_hdr *hdr = hdif; const struct HDIF_idata_ptr *iptr; if (hdr->d1f0 != BE16_TO_CPU(0xd1f0)) { prerror("HDIF: Bad header format !\n"); return NULL; } if (di >= be16_to_cpu(hdr->idptr_count)) { prerror("HDIF: idata index out of range !\n"); return NULL; } iptr = (void *)hdif + be32_to_cpu(hdr->idptr_off) + di * sizeof(struct HDIF_idata_ptr); if (size) *size = be32_to_cpu(iptr->size); return (void *)hdif + be32_to_cpu(iptr->offset); } const void *HDIF_get_iarray_item(const struct HDIF_common_hdr *hdif, unsigned int di, unsigned int ai, unsigned int *size) { const struct HDIF_array_hdr *ahdr; unsigned int asize; const void *arr; arr = HDIF_get_idata(hdif, di, &asize); if (!arr) return NULL; if (asize < sizeof(struct HDIF_array_hdr)) { prerror("HDIF: idata block too small for array !\n"); return NULL; } ahdr = arr; if (ai >= be32_to_cpu(ahdr->ecnt)) { prerror("HDIF: idata array index out of range !\n"); return NULL; } if (size) *size = be32_to_cpu(ahdr->eactsz); return arr + be32_to_cpu(ahdr->offset) + ai * be32_to_cpu(ahdr->esize); } int HDIF_get_iarray_size(const struct HDIF_common_hdr *hdif, unsigned int di) { const struct HDIF_array_hdr *ahdr; unsigned int asize; const void *arr; arr = HDIF_get_idata(hdif, di, &asize); if (!arr) return -1; if (asize < sizeof(struct HDIF_array_hdr)) { prerror("HDIF: idata block too small for array !\n"); return -1; } ahdr = arr; return be32_to_cpu(ahdr->ecnt); } struct HDIF_child_ptr * HDIF_child_arr(const struct HDIF_common_hdr *hdif, unsigned int idx) { struct HDIF_child_ptr *children; children = (void *)hdif + be32_to_cpu(hdif->child_off); if (idx >= be16_to_cpu(hdif->child_count)) { prerror("HDIF: child array idx out of range!\n"); return NULL; } return &children[idx]; } struct HDIF_common_hdr *HDIF_child(const struct HDIF_common_hdr *hdif, const struct HDIF_child_ptr *child, unsigned int idx, const char *eyecatcher) { void *base = (void *)hdif; struct HDIF_common_hdr *ret; long child_off; /* child must be in hdif's child array */ child_off = (void *)child - (base + be32_to_cpu(hdif->child_off)); assert(child_off % sizeof(struct HDIF_child_ptr) == 0); assert(child_off / sizeof(struct HDIF_child_ptr) < be16_to_cpu(hdif->child_count)); assert(idx < be32_to_cpu(child->count)); if (be32_to_cpu(child->size) < sizeof(struct HDIF_common_hdr)) { prerror("HDIF: %s child #%i too small: %u\n", eyecatcher, idx, be32_to_cpu(child->size)); return NULL; } ret = base + be32_to_cpu(child->offset) + be32_to_cpu(child->size) * idx; if (!HDIF_check(ret, eyecatcher)) { prerror("HDIF: %s child #%i bad type\n", eyecatcher, idx); return NULL; } return ret; } skiboot-skiboot-5.1.13/hdata/hdif.h000066400000000000000000000100121265204436200170650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __HDIF_H #define __HDIF_H #include #include #include struct HDIF_common_hdr { __be16 d1f0; /* 0xd1f0 */ char id[6]; /* eye catcher string */ __be16 instnum; /* instance number */ __be16 version; /* version */ __be32 total_len; /* total structure length */ __be32 hdr_len; /* header length (currently 0x20) */ __be32 idptr_off; /* offset to idata pointers */ __be16 idptr_count; /* number of idata pointers */ __be16 child_count; /* number of child structures */ __be32 child_off; /* offset to child structures array */ } __packed __align(0x10); struct HDIF_idata_ptr { __be32 offset; __be32 size; } __packed __align(0x8); struct HDIF_array_hdr { __be32 offset; __be32 ecnt; __be32 esize; __be32 eactsz; } __packed __align(0x10); struct HDIF_child_ptr { __be32 offset; __be32 size; __be32 count; } __packed; #define HDIF_HDR_LEN (sizeof(struct HDIF_common_hdr)) #define HDIF_ARRAY_OFFSET (sizeof(struct HDIF_array_hdr)) #define HDIF_ID(_id) .d1f0 = CPU_TO_BE16(0xd1f0), .id = _id #define HDIF_SIMPLE_HDR(id, vers, type) \ { \ HDIF_ID(id), \ .instnum = CPU_TO_BE16(0), \ .version = CPU_TO_BE16(vers), \ .total_len = CPU_TO_BE32(sizeof(type)), \ .hdr_len = CPU_TO_BE32(HDIF_HDR_LEN), \ .idptr_off = CPU_TO_BE32(HDIF_HDR_LEN), \ .idptr_count = CPU_TO_BE16(1), \ .child_count = CPU_TO_BE16(0), \ .child_off = CPU_TO_BE32(0), \ } #define HDIF_IDATA_PTR(_offset, _size) \ { \ .offset = CPU_TO_BE32(_offset), \ .size = _size, \ } static inline bool HDIF_check(const void *hdif, const char id[]) { const struct HDIF_common_hdr *hdr = hdif; return hdr->d1f0 == CPU_TO_BE16(0xd1f0) && memcmp(hdr->id, id, sizeof(hdr->id)) == 0; } /* HDIF_get_idata - Get a pointer to internal data block * * @hdif : HDIF structure pointer * @di : Index of the idata pointer * @size : Return the data size (or NULL if ignored) */ extern const void *HDIF_get_idata(const struct HDIF_common_hdr *hdif, unsigned int di, unsigned int *size); /* HDIF_get_iarray - Get a pointer to an elemnt of an internal data array * * @hdif : HDIF structure pointer * @di : Index of the idata pointer * @ai : Index in the resulting array * @size : Return the entry actual size (or NULL if ignored) */ extern const void *HDIF_get_iarray_item(const struct HDIF_common_hdr *hdif, unsigned int di, unsigned int ai, unsigned int *size); /* HDIF_get_iarray_size - Get the number of elements of an internal data array * * @hdif : HDIF structure pointer * @di : Index of the idata pointer * * A negative result means an error */ extern int HDIF_get_iarray_size(const struct HDIF_common_hdr *hdif, unsigned int di); /* HDIF_child_arr - Get a child array from this HDIF. * * @hdif : HDIF structure pointer * @idx : the child to get * * NULL means an error (not that many children). */ extern struct HDIF_child_ptr * HDIF_child_arr(const struct HDIF_common_hdr *hdif, unsigned int idx); /* HDIF_child - Deref a child_ptr entry. * * @hdif : HDIF structure pointer * @child : the child returned from HDIF_child_arr * @idx : the index of the child to get (< child->count). * @eyecatcher: the 6-char ID expected for this child. * * NULL means an error. */ extern struct HDIF_common_hdr *HDIF_child(const struct HDIF_common_hdr *hdif, const struct HDIF_child_ptr *child, unsigned int idx, const char *eyecatcher); #endif /* __HDIF_H */ skiboot-skiboot-5.1.13/hdata/hostservices.c000066400000000000000000000050201265204436200206720ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "spira.h" #include "hdata.h" static void merge_property(const struct dt_node *src_root, struct dt_node *dst_root, const char *name) { const struct dt_property *src; struct dt_property *dst; /* Nothing to merge if old one doesn't exist. */ src = dt_find_property(src_root, name); if (!src) return; /* Just create a new one if there's none in dst. */ dst = __dt_find_property(dst_root, name); if (!dst) { dt_add_property(dst_root, name, src->prop, src->len); return; } /* Append src to dst. */ dt_resize_property(&dst, dst->len + src->len); memcpy(dst->prop + dst->len, src->prop, src->len); dst->len += src->len; } static void hservice_parse_dt_tree(const struct dt_node *src) { const struct dt_property *sprop; /* Copy/merge reserved names & ranges properties. */ list_for_each(&src->properties, sprop, list) { if (streq(sprop->name, "reserved-names") || streq(sprop->name, "reserved-ranges") || streq(sprop->name, "ibm,enabled-idle-states")) merge_property(src, dt_root, sprop->name); } } /* Get host services information from hdat. */ bool hservices_from_hdat(const void *fdt, size_t size) { int err; struct dt_node *hservices; prlog(PR_DEBUG, "HBRT: Found mini-DT at 0x%p size: 0x%08lx\n", fdt, size); /* For diagnostic purposes, we copy the whole blob over */ dt_add_property(dt_root, "ibm,hbrt-mini-fdt", fdt, size); /* Parse & extract relevant properties */ err = fdt_check_header(fdt); if (err) { prerror("HBRT: fdt blob %p hdr error %d\n", fdt, err); return false; } hservices = dt_new_root("ibm,hostservices"); err = dt_expand_node(hservices, fdt, 0); if (err < 0) { prerror("HBRT: fdt blob %p parse error %d\n", fdt, err); return false; } hservice_parse_dt_tree(hservices); return true; } skiboot-skiboot-5.1.13/hdata/iohub.c000066400000000000000000000515371265204436200172750ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include #include #include #include #include #include #include #include "hdata.h" static void io_add_common(struct dt_node *hn, const struct cechub_io_hub *hub) { dt_add_property_cells(hn, "#address-cells", 2); dt_add_property_cells(hn, "#size-cells", 2); dt_add_property_cells(hn, "ibm,buid-ext", be32_to_cpu(hub->buid_ext)); dt_add_property_cells(hn, "ibm,chip-id", pcid_to_chip_id(be32_to_cpu(hub->proc_chip_id))); dt_add_property_cells(hn, "ibm,gx-index", be32_to_cpu(hub->gx_index)); dt_add_property_cells(hn, "revision", be32_to_cpu(hub->ec_level)); /* Instead of exposing the GX BARs as separate ranges as we *should* * do in an ideal world, we just create a pass-through ranges and * we use separate properties for the BARs. * * This is hackish but will do for now and avoids us having to * do too complex ranges property parsing */ dt_add_property(hn, "ranges", NULL, 0); dt_add_property_cells(hn, "ibm,gx-bar-1", hi32(hub->gx_ctrl_bar1), lo32(hub->gx_ctrl_bar1)); dt_add_property_cells(hn, "ibm,gx-bar-2", hi32(hub->gx_ctrl_bar2), lo32(hub->gx_ctrl_bar2)); /* Add presence detect if valid */ if (hub->flags & CECHUB_HUB_FLAG_FAB_BR0_PDT) dt_add_property_cells(hn, "ibm,br0-presence-detect", hub->fab_br0_pdt); if (hub->flags & CECHUB_HUB_FLAG_FAB_BR1_PDT) dt_add_property_cells(hn, "ibm,br1-presence-detect", hub->fab_br1_pdt); } static bool io_get_lx_info(const void *kwvpd, unsigned int kwvpd_sz, int lx_idx, struct dt_node *hn) { const void *lxr; char recname[5]; /* Find LXRn, where n is the index passed in*/ strcpy(recname, "LXR0"); recname[3] += lx_idx; lxr = vpd_find(kwvpd, kwvpd_sz, recname, "LX", NULL); if (!lxr) { /* Not found, try VINI */ lxr = vpd_find(kwvpd, kwvpd_sz, "VINI", "LX", NULL); if (lxr) lx_idx = VPD_LOAD_LXRN_VINI; } if (!lxr) { prlog(PR_DEBUG, "CEC: LXR%x not found !\n", lx_idx); return false; } prlog(PR_DEBUG, "CEC: LXRn=%d LXR=%016lx\n", lx_idx, lxr ? *(unsigned long *)lxr : 0); prlog(PR_DEBUG, "CEC: LX Info added to %llx\n", (long long)hn); /* Add the LX info */ if (!dt_has_node_property(hn, "ibm,vpd-lx-info", NULL)) { dt_add_property_cells(hn, "ibm,vpd-lx-info", lx_idx, ((uint32_t *)lxr)[0], ((uint32_t *)lxr)[1]); } return true; } static void io_get_loc_code(const void *sp_iohubs, struct dt_node *hn, const char *prop_name) { const struct spira_fru_id *fru_id; unsigned int fru_id_sz; char loc_code[LOC_CODE_SIZE + 1]; const char *slca_loc_code; /* Find SLCA Index */ fru_id = HDIF_get_idata(sp_iohubs, CECHUB_FRU_ID_DATA, &fru_id_sz); if (fru_id) { memset(loc_code, 0, sizeof(loc_code)); /* Find LOC Code from SLCA Index */ slca_loc_code = slca_get_loc_code_index(be16_to_cpu(fru_id->slca_index)); if (slca_loc_code) { strncpy(loc_code, slca_loc_code, LOC_CODE_SIZE); if (!dt_has_node_property(hn, prop_name, NULL)) { dt_add_property(hn, prop_name, loc_code, strlen(loc_code) + 1); } prlog(PR_DEBUG, "CEC: %s: %s (SLCA rsrc 0x%x)\n", prop_name, loc_code, be16_to_cpu(fru_id->rsrc_id)); } else { prlog(PR_DEBUG, "CEC: SLCA Loc not found: " "index %d\n", fru_id->slca_index); } } else { prlog(PR_DEBUG, "CEC: Hub FRU ID not found...\n"); } } static struct dt_node *io_add_p5ioc2(const struct cechub_io_hub *hub, const void *sp_iohubs) { struct dt_node *hn; uint64_t reg[2]; const void *kwvpd; unsigned int kwvpd_sz; prlog(PR_DEBUG, " GX#%d BUID_Ext = 0x%x\n", be32_to_cpu(hub->gx_index), be32_to_cpu(hub->buid_ext)); prlog(PR_DEBUG, " GX BAR 0 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar0)); prlog(PR_DEBUG, " GX BAR 1 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar1)); prlog(PR_DEBUG, " GX BAR 2 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar2)); prlog(PR_DEBUG, " GX BAR 3 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar3)); prlog(PR_DEBUG, " GX BAR 4 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar4)); /* We assume SBAR == GX0 + some hard coded offset */ reg[0] = cleanup_addr(be64_to_cpu(hub->gx_ctrl_bar0) + P5IOC2_REGS_OFFSET); reg[1] = 0x2000000; hn = dt_new_addr(dt_root, "io-hub", reg[0]); if (!hn) return NULL; dt_add_property(hn, "reg", reg, sizeof(reg)); dt_add_property_strings(hn, "compatible", "ibm,p5ioc2"); kwvpd = HDIF_get_idata(sp_iohubs, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz); if (kwvpd && kwvpd != sp_iohubs) { /* * XX We don't know how to properly find the LXRn * record so for now we'll just try LXR0 and if not * found, we try LXR1 */ if (!io_get_lx_info(kwvpd, kwvpd_sz, 0, hn)) io_get_lx_info(kwvpd, kwvpd_sz, 1, hn); } else prlog(PR_DEBUG, "CEC: P5IOC2 Keywords not found.\n"); /* Get slots base loc code */ io_get_loc_code(sp_iohubs, hn, "ibm,io-base-loc-code"); return hn; } static struct dt_node *io_add_p7ioc(const struct cechub_io_hub *hub, const void *sp_iohubs) { struct dt_node *hn; uint64_t reg[2]; const void *kwvpd; unsigned int kwvpd_sz; prlog(PR_DEBUG, " GX#%d BUID_Ext = 0x%x\n", be32_to_cpu(hub->gx_index), be32_to_cpu(hub->buid_ext)); prlog(PR_DEBUG, " GX BAR 0 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar0)); prlog(PR_DEBUG, " GX BAR 1 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar1)); prlog(PR_DEBUG, " GX BAR 2 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar2)); prlog(PR_DEBUG, " GX BAR 3 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar3)); prlog(PR_DEBUG, " GX BAR 4 = 0x%016"PRIx64"\n", be64_to_cpu(hub->gx_ctrl_bar4)); /* We only know about memory map 1 */ if (hub->mem_map_vers != 1) { prerror("P7IOC: Unknown memory map %d\n", hub->mem_map_vers); /* We try to continue anyway ... */ } reg[0] = cleanup_addr(be64_to_cpu(hub->gx_ctrl_bar1)); reg[1] = 0x2000000; hn = dt_new_addr(dt_root, "io-hub", reg[0]); if (!hn) return NULL; dt_add_property(hn, "reg", reg, sizeof(reg)); dt_add_property_strings(hn, "compatible", "ibm,p7ioc", "ibm,ioda-hub"); kwvpd = HDIF_get_idata(sp_iohubs, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz); if (kwvpd && kwvpd != sp_iohubs) { /* * XX We don't know how to properly find the LXRn * record so for now we'll just try LXR0 and if not * found, we try LXR1 */ if (!io_get_lx_info(kwvpd, kwvpd_sz, 0, hn)) io_get_lx_info(kwvpd, kwvpd_sz, 1, hn); } else { prlog(PR_DEBUG, "CEC: P7IOC Keywords not found.\n"); } io_get_loc_code(sp_iohubs, hn, "ibm,io-base-loc-code"); return hn; } static struct dt_node *io_add_phb3(const struct cechub_io_hub *hub, const struct HDIF_common_hdr *sp_iohubs, unsigned int index, struct dt_node *xcom, unsigned int pe_xscom, unsigned int pci_xscom, unsigned int spci_xscom) { struct dt_node *pbcq; uint32_t reg[6]; unsigned int hdif_vers; /* Get HDIF version */ hdif_vers = be16_to_cpu(sp_iohubs->version); /* Create PBCQ node under xscom */ pbcq = dt_new_addr(xcom, "pbcq", pe_xscom); if (!pbcq) return NULL; /* "reg" property contains in order the PE, PCI and SPCI XSCOM * addresses */ reg[0] = pe_xscom; reg[1] = 0x20; reg[2] = pci_xscom; reg[3] = 0x05; reg[4] = spci_xscom; reg[5] = 0x15; dt_add_property(pbcq, "reg", reg, sizeof(reg)); /* A couple more things ... */ dt_add_property_strings(pbcq, "compatible", "ibm,power8-pbcq"); dt_add_property_cells(pbcq, "ibm,phb-index", index); dt_add_property_cells(pbcq, "ibm,hub-id", be16_to_cpu(hub->hub_num)); /* The loc code of the PHB itself is different from the base * loc code of the slots (It's actually the DCM's loc code). */ io_get_loc_code(sp_iohubs, pbcq, "ibm,loc-code"); /* We indicate that this is an IBM setup, which means that * the presence detect A/B bits are meaningful. So far we * don't know whether they make any sense on customer setups * so we only set that when booting with HDAT */ dt_add_property(pbcq, "ibm,use-ab-detect", NULL, 0); /* HDAT spec has these in version 0x6A and later */ if (hdif_vers >= 0x6a) { u64 eq0 = be64_to_cpu(hub->phb_lane_eq[index][0]); u64 eq1 = be64_to_cpu(hub->phb_lane_eq[index][1]); u64 eq2 = be64_to_cpu(hub->phb_lane_eq[index][2]); u64 eq3 = be64_to_cpu(hub->phb_lane_eq[index][3]); dt_add_property_cells(pbcq, "ibm,lane-eq", hi32(eq0), lo32(eq0), hi32(eq1), lo32(eq1), hi32(eq2), lo32(eq2), hi32(eq3), lo32(eq3)); } /* Currently we only create a PBCQ node, the actual PHB nodes * will be added by sapphire later on. */ return pbcq; } static struct dt_node *io_add_p8(const struct cechub_io_hub *hub, const struct HDIF_common_hdr *sp_iohubs) { struct dt_node *xscom; unsigned int i, chip_id; chip_id = pcid_to_chip_id(be32_to_cpu(hub->proc_chip_id)); prlog(PR_INFO, "CEC: HW CHIP=0x%x, HW TOPO=0x%04x\n", chip_id, be16_to_cpu(hub->hw_topology)); xscom = find_xscom_for_chip(chip_id); if (!xscom) { prerror("P8: Can't find XSCOM for chip %d\n", chip_id); return NULL; } /* Create PHBs, max 3 */ for (i = 0; i < 3; i++) { if (hub->fab_br0_pdt & (0x80 >> i)) /* XSCOM addresses are the same on Murano and Venice */ io_add_phb3(hub, sp_iohubs, i, xscom, 0x02012000 + (i * 0x400), 0x09012000 + (i * 0x400), 0x09013c00 + (i * 0x40)); } /* HACK: We return the XSCOM device for the VPD info */ return xscom; } static void io_add_p8_cec_vpd(const struct HDIF_common_hdr *sp_iohubs) { const struct HDIF_child_ptr *iokids; const void *iokid; const void *kwvpd; unsigned int kwvpd_sz; /* P8 LXR0 kept in IO KID Keyword VPD */ iokids = HDIF_child_arr(sp_iohubs, CECHUB_CHILD_IO_KIDS); if (!CHECK_SPPTR(iokids)) { prlog(PR_WARNING, "CEC: No IOKID child array !\n"); return; } if (!iokids->count) { prlog(PR_WARNING, "CEC: IOKID count is 0 !\n"); return; } if (iokids->count > 1) { prlog(PR_WARNING, "CEC: WARNING ! More than 1 IO KID !!! (%d)\n", iokids->count); /* Ignoring the additional ones */ } iokid = HDIF_child(sp_iohubs, iokids, 0, "IO KID"); if (!iokid) { prlog(PR_WARNING, "CEC: No IO KID structure in child array !\n"); return; } /* Grab base location code for slots */ io_get_loc_code(iokid, dt_root, "ibm,io-base-loc-code"); kwvpd = HDIF_get_idata(iokid, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz); if (!kwvpd) { prlog(PR_WARNING, "CEC: No VPD entry in IO KID !\n"); return; } /* Grab LX load info */ io_get_lx_info(kwvpd, kwvpd_sz, 0, dt_root); } static struct dt_node *io_add_hea(const struct cechub_io_hub *hub, const void *sp_io) { struct dt_node *np, *gnp; uint64_t reg[2]; unsigned int i, vpd_sz; uint8_t kw_sz; const void *iokid, *vpd, *ccin; const uint8_t *mac; const struct HDIF_child_ptr *iokids; /* * We have a table of supported dauther cards looked up * by CCIN. We don't use the 1008 slot map in the VPD. * * This is basically translated from BML and will do for * now especially since we don't really support p5ioc2 * machine, this is just for lab use * * This is mostly untested on 10G ... we might need more * info about the PHY in that case */ const struct hea_iocard { const char ccin[4]; struct { uint32_t speed; uint16_t ports; uint16_t phy_id; } pg[2]; } hea_iocards[] = { { .ccin = "1818", /* HV4 something */ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, }, { .ccin = "1819", /* HV4 Titov Card */ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 }, }, { .ccin = "1830", /* HV4 Sergei Card */ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 0 }, .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 0 }, }, { .ccin = "181A", /* L4 Evans Card */ .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 }, }, { .ccin = "181B", /* L4 Weber Card */ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 0 }, .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 0 }, }, { .ccin = "181C", /* HV4 Gibson Card */ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 }, }, { .ccin = "2BC4", /* MR Riverside 2 */ .pg[0] = { .speed = 1000, .ports = 1, .phy_id = 1 }, .pg[1] = { .speed = 1000, .ports = 1, .phy_id = 1 }, }, { .ccin = "2BC5", /* MR Lions 2 */ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 1 }, .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 1 }, }, { .ccin = "2BC6", /* MR Onion 2 */ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 1 }, .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 1 }, }, { .ccin = "266D", /* Jupiter Bonzai */ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 1 }, .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 1 }, }, /* The blade use an IO KID that's a bit oddball and seems to * represent the backplane itself, but let's use it anyway * * XXX Probably want a different PHY type ! */ { .ccin = "531C", /* P7 Blade */ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, }, }; const struct hea_iocard *card = NULL; /* WARNING: This makes quite a lot of nasty assumptions * that appear to hold true on the few machines I care * about, which is good enough for now. We don't officially * support p5ioc2 anyway... */ /* Get first IO KID, we only support one. Real support would * mean using the FRU ID and the SLCA to find the right "stuff" * but at this stage it's unnecessary */ iokids = HDIF_child_arr(sp_io, CECHUB_CHILD_IO_KIDS); if (!CHECK_SPPTR(iokids)) { prerror("HEA: no IOKID in HDAT child array !\n"); return NULL; } if (!iokids->count) { prerror("HEA: IOKID count is 0 !\n"); return NULL; } if (iokids->count > 1) { prlog(PR_WARNING, "HEA: WARNING ! More than 1 IO KID !!! (%d)\n", iokids->count); } iokid = HDIF_child(sp_io, iokids, 0, "IO KID"); if (!iokid) { prerror("HEA: Failed to retrieve IO KID 0 !\n"); return NULL; } /* Grab VPD */ vpd = HDIF_get_idata(iokid, IOKID_KW_VPD, &vpd_sz); if (!CHECK_SPPTR(vpd)) { prerror("HEA: Failed to retrieve VPD from IO KID !\n"); return NULL; } /* Grab the MAC address */ mac = vpd_find(vpd, vpd_sz, "VINI", "B1", &kw_sz); if (!mac || kw_sz < 8) { prerror("HEA: Failed to retrieve MAC Address !\n"); return NULL; } /* Grab the CCIN (card ID) */ ccin = vpd_find(vpd, vpd_sz, "VINI", "CC", &kw_sz); if (!ccin || kw_sz < 4) { prerror("HEA: Failed to retrieve CCIN !\n"); return NULL; } /* Now we could try to parse the 1008 slot map etc... but instead * we'll do like BML and grab the CCIN & use it for known cards. * We also grab the MAC */ for (i = 0; i < ARRAY_SIZE(hea_iocards) && !card; i++) { if (strncmp(hea_iocards[i].ccin, ccin, 4)) continue; card = &hea_iocards[i]; } if (!card) { prerror("HEA: Unknown CCIN 0x%.4s!\n", (const char *)ccin); return NULL; } /* Assume base address is BAR3 + 0x4000000000 */ reg[0] = hub->gx_ctrl_bar3 + 0x4000000000; reg[1] = 0xc0000000; prlog(PR_DEBUG, "CEC: * Adding HEA to P5IOC2, assuming GBA=0x%llx\n", (long long)reg[0]); np = dt_new_addr(dt_root, "ibm,hea", reg[0]); if (!np) return NULL; dt_add_property(np, "reg", reg, sizeof(reg)); dt_add_property_strings(np, "compatible", "ibm,p5ioc2-hea"); dt_add_property_cells(np, "#address-cells", 1); dt_add_property_cells(np, "#size-cells", 0); dt_add_property(np, "ibm,vpd", vpd, vpd_sz); dt_add_property_cells(np, "#mac-address", mac[7]); dt_add_property(np, "mac-address-base", mac, 6); /* BUID is base + 0x30 */ dt_add_property(np, "interrupt-controller", NULL, 0); dt_add_property_cells(np, "interrupt-base", ((hub->buid_ext << 9) | 0x30) << 4); dt_add_property_cells(np, "interrupt-max-count", 128); /* Always 2 port groups */ for (i = 0; i < 2; i++) { unsigned int clause; switch(card->pg[i].speed) { case 1000: clause = 0x22; break; case 10000: clause = 0x45; break; default: /* Unused port group */ continue; } gnp = dt_new_addr(np, "portgroup", i + 1); if (!gnp) continue; dt_add_property_cells(gnp, "reg", i + 1); dt_add_property_cells(gnp, "speed", card->pg[i].speed); /* XX FIXME */ dt_add_property_strings(gnp, "phy-type", "mdio"); dt_add_property_cells(gnp, "phy-mdio-addr", card->pg[i].phy_id); dt_add_property_cells(gnp, "phy-mdio-clause", clause); dt_add_property_cells(gnp, "subports", card->pg[i].ports); } return np; } static void io_parse_fru(const void *sp_iohubs) { unsigned int i; struct dt_node *hn; int count; count = HDIF_get_iarray_size(sp_iohubs, CECHUB_FRU_IO_HUBS); if (count < 1) { prerror("CEC: IO FRU with no chips !\n"); return; } prlog(PR_INFO, "CEC: %d chips in FRU\n", count); /* Iterate IO hub array */ for (i = 0; i < count; i++) { const struct cechub_io_hub *hub; unsigned int size, hub_id; hub = HDIF_get_iarray_item(sp_iohubs, CECHUB_FRU_IO_HUBS, i, &size); if (!hub || size < CECHUB_IOHUB_MIN_SIZE) { prerror("CEC: IO-HUB Chip %d bad idata\n", i); continue; } switch (hub->flags & CECHUB_HUB_FLAG_STATE_MASK) { case CECHUB_HUB_FLAG_STATE_OK: prlog(PR_DEBUG, "CEC: IO Hub Chip #%d OK\n", i); break; case CECHUB_HUB_FLAG_STATE_FAILURES: prlog(PR_WARNING, "CEC: IO Hub Chip #%d OK" " with failures\n", i); break; case CECHUB_HUB_FLAG_STATE_NOT_INST: prlog(PR_DEBUG, "CEC: IO Hub Chip #%d" " Not installed\n", i); continue; case CECHUB_HUB_FLAG_STATE_UNUSABLE: prlog(PR_DEBUG, "CEC: IO Hub Chip #%d Unusable", i); continue; } hub_id = be16_to_cpu(hub->iohub_id); /* GX BAR assignment */ prlog(PR_DEBUG, "CEC: PChip: %d HUB ID: %04x [EC=0x%x]" " Hub#=%d)\n", be32_to_cpu(hub->proc_chip_id), hub_id, be32_to_cpu(hub->ec_level), be16_to_cpu(hub->hub_num)); switch(hub_id) { case CECHUB_HUB_P7IOC: prlog(PR_INFO, "CEC: P7IOC !\n"); hn = io_add_p7ioc(hub, sp_iohubs); io_add_common(hn, hub); break; case CECHUB_HUB_P5IOC2: prlog(PR_INFO, "CEC: P5IOC2 !\n"); hn = io_add_p5ioc2(hub, sp_iohubs); io_add_common(hn, hub); io_add_hea(hub, sp_iohubs); break; case CECHUB_HUB_MURANO: case CECHUB_HUB_MURANO_SEGU: prlog(PR_INFO, "CEC: Murano !\n"); hn = io_add_p8(hub, sp_iohubs); break; case CECHUB_HUB_VENICE_WYATT: prlog(PR_INFO, "CEC: Venice !\n"); hn = io_add_p8(hub, sp_iohubs); break; default: prlog(PR_ERR, "CEC: Hub ID 0x%04x unsupported !\n", hub_id); hn = NULL; } } /* On P8, grab the CEC VPD */ if (proc_gen == proc_gen_p8) io_add_p8_cec_vpd(sp_iohubs); } void io_parse(void) { const struct HDIF_common_hdr *sp_iohubs; unsigned int i, size; /* Look for IO Hubs */ if (!get_hdif(&spira.ntuples.cec_iohub_fru, "IO HUB")) { prerror("CEC: Cannot locate IO Hub FRU data !\n"); return; } /* * Note about LXRn numbering ... * * I can't completely make sense of what that is supposed to be, so * for now, what we do is look for the first one we can find and * increment it for each chip. Works for the machines I have here */ for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, sp_iohubs, i, CECHUB_FRU_HDIF_SIG) { const struct cechub_hub_fru_id *fru_id_data; unsigned int type; static const char *typestr[] = { "Reservation", "Card", "CPU Card", "Backplane", "Backplane Extension" }; fru_id_data = HDIF_get_idata(sp_iohubs, CECHUB_FRU_ID_DATA_AREA, &size); if (!fru_id_data || size < sizeof(struct cechub_hub_fru_id)) { prerror("CEC: IO-HUB FRU %d, bad ID data\n", i); continue; } type = be32_to_cpu(fru_id_data->card_type); prlog(PR_INFO, "CEC: HUB FRU %d is %s\n", i, type > 4 ? "Unknown" : typestr[type]); /* * We currently only handle the backplane (Juno) and * processor FRU (P8 machines) */ if (type != CECHUB_FRU_TYPE_CEC_BKPLANE && type != CECHUB_FRU_TYPE_CPU_CARD) { prerror("CEC: Unsupported type\n"); continue; } /* We don't support Hubs connected to pass-through ports */ if (fru_id_data->flags & (CECHUB_FRU_FLAG_HEADLESS | CECHUB_FRU_FLAG_PASSTHROUGH)) { prerror("CEC: Headless or Passthrough unsupported\n"); continue; } /* Ok, we have a reasonable candidate */ io_parse_fru(sp_iohubs); } } skiboot-skiboot-5.1.13/hdata/memory.c000066400000000000000000000260151265204436200174700ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "spira.h" #include "hdata.h" struct HDIF_ram_area_id { __be16 id; #define RAM_AREA_INSTALLED 0x8000 #define RAM_AREA_FUNCTIONAL 0x4000 __be16 flags; }; struct HDIF_ram_area_size { __be64 mb; }; struct ram_area { const struct HDIF_ram_area_id *raid; const struct HDIF_ram_area_size *rasize; }; struct HDIF_ms_area_address_range { __be64 start; __be64 end; __be32 chip; __be32 mirror_attr; __be64 mirror_start; }; struct HDIF_ms_area_id { __be16 id; #define MS_PTYPE_RISER_CARD 0x8000 #define MS_PTYPE_MEM_CARD 0x4000 #define MS_PTYPE_CEC_FRU 0x2000 #define MS_PTYPE_HYBRID_CARD 0x1000 __be16 parent_type; #define MS_AREA_INSTALLED 0x8000 #define MS_AREA_FUNCTIONAL 0x4000 #define MS_AREA_SHARED 0x2000 __be16 flags; __be16 share_id; }; static struct dt_node *find_shared(struct dt_node *root, u16 id, u64 start, u64 len) { struct dt_node *i; for (i = dt_first(root); i; i = dt_next(root, i)) { __be64 reg[2]; const struct dt_property *shared, *type, *region; type = dt_find_property(i, "device_type"); if (!type || strcmp(type->prop, "memory") != 0) continue; shared = dt_find_property(i, DT_PRIVATE "share-id"); if (!shared || fdt32_to_cpu(*(u32 *)shared->prop) != id) continue; region = dt_find_property(i, "reg"); if (!region) continue; memcpy(reg, region->prop, sizeof(reg)); if (be64_to_cpu(reg[0]) == start && be64_to_cpu(reg[1]) == len) break; } return i; } static void append_chip_id(struct dt_node *mem, u32 id) { struct dt_property *prop; size_t len, i; u32 *p; prop = __dt_find_property(mem, "ibm,chip-id"); if (!prop) return; len = prop->len >> 2; p = (u32 *)prop->prop; /* Check if it exists already */ for (i = 0; i < len; i++) { if (be32_to_cpu(p[i]) == id) return; } /* Add it to the list */ dt_resize_property(&prop, (len + 1) << 2); p = (u32 *)prop->prop; p[len] = cpu_to_be32(id); } static bool add_address_range(struct dt_node *root, const struct HDIF_ms_area_id *id, const struct HDIF_ms_area_address_range *arange) { struct dt_node *mem; u64 reg[2]; char *name; u32 chip_id; size_t namesz = sizeof("memory@") + STR_MAX_CHARS(reg[0]); name = (char*)malloc(namesz); assert(name); chip_id = pcid_to_chip_id(be32_to_cpu(arange->chip)); prlog(PR_DEBUG, " Range: 0x%016llx..0x%016llx " "on Chip 0x%x mattr: 0x%x\n", (long long)arange->start, (long long)arange->end, chip_id, arange->mirror_attr); /* reg contains start and length */ reg[0] = cleanup_addr(be64_to_cpu(arange->start)); reg[1] = cleanup_addr(be64_to_cpu(arange->end)) - reg[0]; if (be16_to_cpu(id->flags) & MS_AREA_SHARED) { /* Only enter shared nodes once. */ mem = find_shared(root, be16_to_cpu(id->share_id), reg[0], reg[1]); if (mem) { append_chip_id(mem, chip_id); free(name); return true; } } snprintf(name, namesz, "memory@%llx", (long long)reg[0]); mem = dt_new(root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property_cells(mem, "ibm,chip-id", chip_id); dt_add_property_u64s(mem, "reg", reg[0], reg[1]); if (be16_to_cpu(id->flags) & MS_AREA_SHARED) dt_add_property_cells(mem, DT_PRIVATE "share-id", be16_to_cpu(id->share_id)); free(name); return true; } static u32 add_chip_id_to_ram_area(const struct HDIF_common_hdr *msarea, struct dt_node *ram_area) { const struct HDIF_array_hdr *arr; const struct HDIF_ms_area_address_range *arange; unsigned int size; u32 chip_id; /* Safe to assume pointers are valid here. */ arr = HDIF_get_idata(msarea, 4, &size); arange = (void *)arr + be32_to_cpu(arr->offset); chip_id = pcid_to_chip_id(be32_to_cpu(arange->chip)); dt_add_property_cells(ram_area, "ibm,chip-id", chip_id); return chip_id; } static void add_bus_freq_to_ram_area(struct dt_node *ram_node, u32 chip_id) { const struct sppcia_cpu_timebase *timebase; bool got_pcia = false; const void *pcia; u64 freq; u32 size; pcia = get_hdif(&spira.ntuples.pcia, SPPCIA_HDIF_SIG); if (!pcia) { prlog(PR_WARNING, "HDAT: Failed to add memory bus frequency " "as PCIA does not exist\n"); return; } for_each_pcia(pcia) { const struct sppcia_core_unique *id; id = HDIF_get_idata(pcia, SPPCIA_IDATA_CORE_UNIQUE, &size); if (!id || size < sizeof(*id)) { prlog(PR_WARNING, "HDAT: Bad id size %u @ %p\n", size, id); return; } if (chip_id == pcid_to_chip_id(be32_to_cpu(id->proc_chip_id))) { got_pcia = true; break; } } if (got_pcia == false) return; timebase = HDIF_get_idata(pcia, SPPCIA_IDATA_TIMEBASE, &size); if (!timebase || size < sizeof(*timebase)) { prlog(PR_ERR, "HDAT: Bad timebase size %u @ %p\n", size, timebase); return; } freq = ((u64)be32_to_cpu(timebase->memory_bus_frequency)) *1000000ul; dt_add_property_cells(ram_node, "ibm,memory-bus-frequency", hi32(freq), lo32(freq)); } static void add_size_to_ram_area(struct dt_node *ram_node, const struct HDIF_common_hdr *hdr, int indx_vpd) { const void *fruvpd; unsigned int fruvpd_sz; const void *kw; char *str; uint8_t kwsz; fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz); if (!CHECK_SPPTR(fruvpd)) return; /* DIMM Size */ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SZ", &kwsz); if (!kw) return; str = zalloc(kwsz + 1); if (!str){ prerror("Allocation failed\n"); return; } memcpy(str, kw, kwsz); dt_add_property_string(ram_node, "size", str); free(str); } static void vpd_add_ram_area(const struct HDIF_common_hdr *msarea) { unsigned int i; unsigned int ram_sz; const struct HDIF_common_hdr *ramarea; const struct HDIF_child_ptr *ramptr; const struct HDIF_ram_area_id *ram_id; struct dt_node *ram_node; u32 chip_id; ramptr = HDIF_child_arr(msarea, 0); if (!CHECK_SPPTR(ramptr)) { prerror("MS AREA: No RAM area at %p\n", msarea); return; } for (i = 0; i < be32_to_cpu(ramptr->count); i++) { ramarea = HDIF_child(msarea, ramptr, i, "RAM "); if (!CHECK_SPPTR(ramarea)) continue; ram_id = HDIF_get_idata(ramarea, 2, &ram_sz); if (!CHECK_SPPTR(ram_id)) continue; if ((be16_to_cpu(ram_id->flags) & RAM_AREA_INSTALLED) && (be16_to_cpu(ram_id->flags) & RAM_AREA_FUNCTIONAL)) { ram_node = dt_add_vpd_node(ramarea, 0, 1); if (ram_node) { chip_id = add_chip_id_to_ram_area(msarea, ram_node); add_bus_freq_to_ram_area(ram_node, chip_id); add_size_to_ram_area(ram_node, ramarea, 1); } } } } static void get_msareas(struct dt_node *root, const struct HDIF_common_hdr *ms_vpd) { unsigned int i; const struct HDIF_child_ptr *msptr; /* First childptr refers to msareas. */ msptr = HDIF_child_arr(ms_vpd, MSVPD_CHILD_MS_AREAS); if (!CHECK_SPPTR(msptr)) { prerror("MS VPD: no children at %p\n", ms_vpd); return; } for (i = 0; i < be32_to_cpu(msptr->count); i++) { const struct HDIF_common_hdr *msarea; const struct HDIF_array_hdr *arr; const struct HDIF_ms_area_address_range *arange; const struct HDIF_ms_area_id *id; const void *fruid; unsigned int size, j; u16 flags; msarea = HDIF_child(ms_vpd, msptr, i, "MSAREA"); if (!CHECK_SPPTR(msarea)) return; id = HDIF_get_idata(msarea, 2, &size); if (!CHECK_SPPTR(id)) return; if (size < sizeof(*id)) { prerror("MS VPD: %p msarea #%i id size too small!\n", ms_vpd, i); return; } flags = be16_to_cpu(id->flags); prlog(PR_DEBUG, "MS VPD: %p, area %i: %s %s %s\n", ms_vpd, i, flags & MS_AREA_INSTALLED ? "installed" : "not installed", flags & MS_AREA_FUNCTIONAL ? "functional" : "not functional", flags & MS_AREA_SHARED ? "shared" : "not shared"); if ((flags & (MS_AREA_INSTALLED|MS_AREA_FUNCTIONAL)) != (MS_AREA_INSTALLED|MS_AREA_FUNCTIONAL)) continue; arr = HDIF_get_idata(msarea, 4, &size); if (!CHECK_SPPTR(arr)) continue; if (size < sizeof(*arr)) { prerror("MS VPD: %p msarea #%i arr size too small!\n", ms_vpd, i); return; } if (be32_to_cpu(arr->eactsz) < sizeof(*arange)) { prerror("MS VPD: %p msarea #%i arange size too small!\n", ms_vpd, i); return; } fruid = HDIF_get_idata(msarea, 0, &size); if (!CHECK_SPPTR(fruid)) return; /* Add Raiser card VPD */ if (be16_to_cpu(id->parent_type) & MS_PTYPE_RISER_CARD) dt_add_vpd_node(msarea, 0, 1); /* Add RAM Area VPD */ vpd_add_ram_area(msarea); /* This offset is from the arr, not the header! */ arange = (void *)arr + be32_to_cpu(arr->offset); for (j = 0; j < be32_to_cpu(arr->ecnt); j++) { if (!add_address_range(root, id, arange)) return; arange = (void *)arange + be32_to_cpu(arr->esize); } } } static bool __memory_parse(struct dt_node *root) { struct HDIF_common_hdr *ms_vpd; const struct msvpd_ms_addr_config *msac; const struct msvpd_total_config_ms *tcms; unsigned int size; ms_vpd = get_hdif(&spira.ntuples.ms_vpd, MSVPD_HDIF_SIG); if (!ms_vpd) { prerror("MS VPD: invalid\n"); op_display(OP_FATAL, OP_MOD_MEM, 0x0000); return false; } if (be32_to_cpu(spira.ntuples.ms_vpd.act_len) < sizeof(*ms_vpd)) { prerror("MS VPD: invalid size %u\n", be32_to_cpu(spira.ntuples.ms_vpd.act_len)); op_display(OP_FATAL, OP_MOD_MEM, 0x0001); return false; } prlog(PR_DEBUG, "MS VPD: is at %p\n", ms_vpd); msac = HDIF_get_idata(ms_vpd, MSVPD_IDATA_MS_ADDR_CONFIG, &size); if (!CHECK_SPPTR(msac) || size < sizeof(*msac)) { prerror("MS VPD: bad msac size %u @ %p\n", size, msac); op_display(OP_FATAL, OP_MOD_MEM, 0x0002); return false; } prlog(PR_DEBUG, "MS VPD: MSAC is at %p\n", msac); dt_add_property_u64(dt_root, DT_PRIVATE "maxmem", be64_to_cpu(msac->max_configured_ms_address)); tcms = HDIF_get_idata(ms_vpd, MSVPD_IDATA_TOTAL_CONFIG_MS, &size); if (!CHECK_SPPTR(tcms) || size < sizeof(*tcms)) { prerror("MS VPD: Bad tcms size %u @ %p\n", size, tcms); op_display(OP_FATAL, OP_MOD_MEM, 0x0003); return false; } prlog(PR_DEBUG, "MS VPD: TCMS is at %p\n", tcms); prlog(PR_DEBUG, "MS VPD: Maximum configured address: 0x%llx\n", (long long)be64_to_cpu(msac->max_configured_ms_address)); prlog(PR_DEBUG, "MS VPD: Maximum possible address: 0x%llx\n", (long long)be64_to_cpu(msac->max_possible_ms_address)); get_msareas(root, ms_vpd); prlog(PR_INFO, "MS VPD: Total MB of RAM: 0x%llx\n", (long long)be64_to_cpu(tcms->total_in_mb)); return true; } void memory_parse(void) { if (!__memory_parse(dt_root)) { prerror("MS VPD: Failed memory init !\n"); abort(); } } skiboot-skiboot-5.1.13/hdata/paca.c000066400000000000000000000215241265204436200170640ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include #include #include #include "hdata.h" #define PACA_MAX_THREADS 4 static unsigned int paca_index(const struct HDIF_common_hdr *paca) { void *start = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG); return ((void *)paca - start) / be32_to_cpu(spira.ntuples.paca.alloc_len); } static struct dt_node *add_cpu_node(struct dt_node *cpus, const struct HDIF_common_hdr *paca, const struct sppaca_cpu_id *id, bool okay) { const struct sppaca_cpu_timebase *timebase; const struct sppaca_cpu_cache *cache; const struct sppaca_cpu_attr *attr; struct dt_node *cpu; u32 no, size, ve_flags, l2_phandle, chip_id; /* We use the process_interrupt_line as the res id */ no = be32_to_cpu(id->process_interrupt_line); ve_flags = be32_to_cpu(id->verify_exists_flags); printf("CPU[%i]: PIR=%i RES=%i %s %s(%u threads)\n", paca_index(paca), be32_to_cpu(id->pir), no, ve_flags & CPU_ID_PACA_RESERVED ? "**RESERVED**" : cpu_state(ve_flags), ve_flags & CPU_ID_SECONDARY_THREAD ? "[secondary] " : (be32_to_cpu(id->pir) == boot_cpu->pir ? "[boot] " : ""), ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK) >> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1); timebase = HDIF_get_idata(paca, SPPACA_IDATA_TIMEBASE, &size); if (!timebase || size < sizeof(*timebase)) { prerror("CPU[%i]: bad timebase size %u @ %p\n", paca_index(paca), size, timebase); return NULL; } cache = HDIF_get_idata(paca, SPPACA_IDATA_CACHE_SIZE, &size); if (!cache || size < sizeof(*cache)) { prerror("CPU[%i]: bad cache size %u @ %p\n", paca_index(paca), size, cache); return NULL; } cpu = add_core_common(cpus, cache, timebase, no, okay); /* Core attributes */ attr = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ATTR, &size); if (attr) add_core_attr(cpu, be32_to_cpu(attr->attr)); /* Add cache info */ l2_phandle = add_core_cache_info(cpus, cache, no, okay); dt_add_property_cells(cpu, "l2-cache", l2_phandle); /* We append the secondary cpus in __cpu_parse */ dt_add_property_cells(cpu, "ibm,ppc-interrupt-server#s", no); dt_add_property_cells(cpu, DT_PRIVATE "hw_proc_id", be32_to_cpu(id->hardware_proc_id)); dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(id->pir)); chip_id = pcid_to_chip_id(be32_to_cpu(id->processor_chip_id)); dt_add_property_cells(cpu, "ibm,chip-id", chip_id); return cpu; } static struct dt_node *find_cpu_by_hardware_proc_id(struct dt_node *root, u32 hw_proc_id) { struct dt_node *i; dt_for_each_node(root, i) { const struct dt_property *prop; if (!dt_has_node_property(i, "device_type", "cpu")) continue; prop = dt_find_property(i, DT_PRIVATE "hw_proc_id"); if (!prop) return NULL; if (be32_to_cpu(*(u32 *)prop->prop) == hw_proc_id) return i; } return NULL; } /* Note that numbers are small. */ static void add_be32_sorted(__be32 arr[], __be32 new, unsigned num) { unsigned int i; /* Walk until we find where we belong (insertion sort). */ for (i = 0; i < num; i++) { if (be32_to_cpu(new) < be32_to_cpu(arr[i])) { __be32 tmp = arr[i]; arr[i] = new; new = tmp; } } arr[i] = new; } static void add_icps(void) { struct dt_node *cpu; unsigned int i; u64 reg[PACA_MAX_THREADS * 2]; struct dt_node *icp; dt_for_each_node(dt_root, cpu) { u32 irange[2], size, pir; const struct dt_property *intsrv; const struct HDIF_common_hdr *paca; u64 ibase; unsigned int num_threads; bool found = false; if (!dt_has_node_property(cpu, "device_type", "cpu")) continue; intsrv = dt_find_property(cpu, "ibm,ppc-interrupt-server#s"); pir = dt_prop_get_u32(cpu, "ibm,pir"); /* Get ibase address */ paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG); for_each_paca(paca) { const struct sppaca_cpu_id *id; id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size); if (!CHECK_SPPTR(id)) continue; if (pir != be32_to_cpu(id->pir)) continue; ibase = cleanup_addr(be64_to_cpu(id->ibase)); found = true; break; } if (!found) return; num_threads = intsrv->len / sizeof(u32); assert(num_threads <= PACA_MAX_THREADS); icp = dt_new_addr(dt_root, "interrupt-controller", ibase); if (!icp) continue; dt_add_property_strings(icp, "compatible", "IBM,ppc-xicp", "IBM,power7-xicp"); irange[0] = dt_property_get_cell(intsrv, 0); /* Index */ irange[1] = num_threads; /* num servers */ dt_add_property(icp, "ibm,interrupt-server-ranges", irange, sizeof(irange)); dt_add_property(icp, "interrupt-controller", NULL, 0); dt_add_property_cells(icp, "#address-cells", 0); dt_add_property_cells(icp, "#interrupt-cells", 1); dt_add_property_string(icp, "device_type", "PowerPC-External-Interrupt-Presentation"); for (i = 0; i < num_threads*2; i += 2) { reg[i] = ibase; /* One page is enough for a handful of regs. */ reg[i+1] = 4096; ibase += reg[i+1]; } dt_add_property(icp, "reg", reg, sizeof(reg)); } } static bool __paca_parse(void) { const struct HDIF_common_hdr *paca; struct dt_node *cpus; paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG); if (!paca) { prerror("Invalid PACA (PCIA = %p)\n", ntuple_addr(&spira.ntuples.pcia)); return false; } if (be32_to_cpu(spira.ntuples.paca.act_len) < sizeof(*paca)) { prerror("PACA: invalid size %u\n", be32_to_cpu(spira.ntuples.paca.act_len)); return false; } cpus = dt_new(dt_root, "cpus"); dt_add_property_cells(cpus, "#address-cells", 1); dt_add_property_cells(cpus, "#size-cells", 0); for_each_paca(paca) { const struct sppaca_cpu_id *id; u32 size, ve_flags; bool okay; id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size); /* The ID structure on Blade314 is only 0x54 long. We can * cope with it as we don't use all the additional fields. * The minimum size we support is 0x40 */ if (!id || size < SPIRA_CPU_ID_MIN_SIZE) { prerror("CPU[%i]: bad id size %u @ %p\n", paca_index(paca), size, id); return false; } ve_flags = be32_to_cpu(id->verify_exists_flags); switch ((ve_flags&CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) { case CPU_ID_VERIFY_USABLE_NO_FAILURES: case CPU_ID_VERIFY_USABLE_FAILURES: okay = true; break; default: okay = false; } printf("CPU[%i]: PIR=%i RES=%i %s\n", paca_index(paca), be32_to_cpu(id->pir), be32_to_cpu(id->process_interrupt_line), okay ? "OK" : "UNAVAILABLE"); /* Secondary threads don't get their own node. */ if (ve_flags & CPU_ID_SECONDARY_THREAD) continue; if (!add_cpu_node(cpus, paca, id, okay)) return false; } /* Now account for secondaries. */ for_each_paca(paca) { const struct dt_property *prop; const struct sppaca_cpu_id *id; u32 size, state, num, ve_flags; struct dt_node *cpu; __be32 *new_prop; id = HDIF_get_idata(paca, 2, &size); if (!CHECK_SPPTR(id)) continue; ve_flags = be32_to_cpu(id->verify_exists_flags); state = (ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT; switch (state) { case CPU_ID_VERIFY_USABLE_NO_FAILURES: case CPU_ID_VERIFY_USABLE_FAILURES: break; default: continue; } /* Only interested in secondary threads. */ if (!(ve_flags & CPU_ID_SECONDARY_THREAD)) continue; cpu = find_cpu_by_hardware_proc_id(cpus, be32_to_cpu(id->hardware_proc_id)); if (!cpu) { prerror("CPU[%i]: could not find primary hwid %i\n", paca_index(paca), be32_to_cpu(id->hardware_proc_id)); return false; } /* Add the cpu #. */ prop = dt_find_property(cpu, "ibm,ppc-interrupt-server#s"); if (!prop) { prerror("CPU[%i]: could not find mapping information\n", paca_index(paca)); return false; } num = prop->len / sizeof(u32); new_prop = malloc((num + 1) * sizeof(u32)); if (!new_prop) { prerror("Property allocation length %zu failed\n", (num + 1) * sizeof(u32)); return false; } memcpy(new_prop, prop->prop, prop->len); add_be32_sorted(new_prop, id->process_interrupt_line, num); dt_del_property(cpu, (struct dt_property *)prop); dt_add_property(cpu, "ibm,ppc-interrupt-server#s", new_prop, (num + 1) * sizeof(__be32)); free(new_prop); } add_icps(); return true; } void paca_parse(void) { if (!__paca_parse()) { prerror("CPU: Initial CPU parsing failed\n"); abort(); } } skiboot-skiboot-5.1.13/hdata/pcia.c000066400000000000000000000146311265204436200170750ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include #include #include "hdata.h" #define PCIA_MAX_THREADS 8 static unsigned int pcia_index(const void *pcia) { return (pcia - (void *)get_hdif(&spira.ntuples.pcia, "SPPCIA")) / be32_to_cpu(spira.ntuples.pcia.alloc_len); } static const struct sppcia_cpu_thread *find_tada(const void *pcia, unsigned int thread) { int count = HDIF_get_iarray_size(pcia, SPPCIA_IDATA_THREAD_ARRAY); int i; if (count < 0) return NULL; for (i = 0; i < count; i++) { const struct sppcia_cpu_thread *t; unsigned int size; t = HDIF_get_iarray_item(pcia, SPPCIA_IDATA_THREAD_ARRAY, i, &size); if (!t || size < sizeof(*t)) continue; if (be32_to_cpu(t->phys_thread_id) == thread) return t; } return NULL; } static void add_icp(const void *pcia, u32 tcount, const char *compat) { const struct sppcia_cpu_thread *t; struct dt_node *icp; __be64 *reg; u32 i, irange[2], rsize; rsize = tcount * 2 * sizeof(__be64); reg = malloc(rsize); assert(reg); /* Suppresses uninitialized warning from gcc */ irange[0] = 0; for (i = 0; i < tcount; i++) { t = find_tada(pcia, i); assert(t); if (i == 0) irange[0] = be32_to_cpu(t->proc_int_line); reg[i * 2] = cpu_to_be64(cleanup_addr(be64_to_cpu(t->ibase))); reg[i * 2 + 1] = cpu_to_be64(0x1000); } irange[1] = tcount; icp = dt_new_addr(dt_root, "interrupt-controller", be64_to_cpu(reg[0])); if (!icp) { free(reg); return; } if (compat) dt_add_property_strings(icp, "compatible", "ibm,ppc-xicp", compat); else dt_add_property_strings(icp, "compatible", "ibm,ppc-xicp"); dt_add_property_cells(icp, "ibm,interrupt-server-ranges", irange[0], irange[1]); dt_add_property(icp, "interrupt-controller", NULL, 0); dt_add_property(icp, "reg", reg, rsize); dt_add_property_cells(icp, "#address-cells", 0); dt_add_property_cells(icp, "#interrupt-cells", 1); dt_add_property_string(icp, "device_type", "PowerPC-External-Interrupt-Presentation"); free(reg); } static struct dt_node *add_core_node(struct dt_node *cpus, const void *pcia, const struct sppcia_core_unique *id, bool okay) { const struct sppcia_cpu_thread *t; const struct sppcia_cpu_timebase *timebase; const struct sppcia_cpu_cache *cache; const struct sppcia_cpu_attr *attr; struct dt_node *cpu; const char *icp_compat; u32 i, size, threads, ve_flags, l2_phandle, chip_id; __be32 iserv[PCIA_MAX_THREADS]; /* Look for thread 0 */ t = find_tada(pcia, 0); if (!t) { prerror("CORE[%i]: Failed to find thread 0 !\n", pcia_index(pcia)); return NULL; } ve_flags = be32_to_cpu(id->verif_exist_flags); threads = ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK) >> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1; assert(threads <= PCIA_MAX_THREADS); prlog(PR_INFO, "CORE[%i]: PIR=%i RES=%i %s %s(%u threads)\n", pcia_index(pcia), t->pir, t->proc_int_line, ve_flags & CPU_ID_PACA_RESERVED ? "**RESERVED**" : cpu_state(ve_flags), be32_to_cpu(t->pir) == boot_cpu->pir ? "[boot] " : "", threads); timebase = HDIF_get_idata(pcia, SPPCIA_IDATA_TIMEBASE, &size); if (!timebase || size < sizeof(*timebase)) { prerror("CORE[%i]: bad timebase size %u @ %p\n", pcia_index(pcia), size, timebase); return NULL; } cache = HDIF_get_idata(pcia, SPPCIA_IDATA_CPU_CACHE, &size); if (!cache || size < sizeof(*cache)) { prerror("CORE[%i]: bad cache size %u @ %p\n", pcia_index(pcia), size, cache); return NULL; } cpu = add_core_common(cpus, cache, timebase, be32_to_cpu(t->proc_int_line), okay); /* Core attributes */ attr = HDIF_get_idata(pcia, SPPCIA_IDATA_CPU_ATTR, &size); if (attr) add_core_attr(cpu, be32_to_cpu(attr->attr)); /* Add cache info */ l2_phandle = add_core_cache_info(cpus, cache, be32_to_cpu(t->proc_int_line), okay); dt_add_property_cells(cpu, "l2-cache", l2_phandle); if (proc_gen == proc_gen_p7) icp_compat = "IBM,power7-icp"; else icp_compat = "IBM,power8-icp"; /* Get HW Chip ID */ chip_id = pcid_to_chip_id(be32_to_cpu(id->proc_chip_id)); dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(t->pir)); dt_add_property_cells(cpu, "ibm,chip-id", chip_id); /* Build ibm,ppc-interrupt-server#s with all threads */ for (i = 0; i < threads; i++) { t = find_tada(pcia, i); if (!t) { threads = i; break; } iserv[i] = t->proc_int_line; assert(t->proc_int_line == t->pir); } dt_add_property(cpu, "ibm,ppc-interrupt-server#s", iserv, 4 * threads); /* Add the ICP node for this CPU */ add_icp(pcia, threads, icp_compat); return cpu; } bool pcia_parse(void) { const void *pcia; struct dt_node *cpus; /* Check PCIA exists... if not, maybe we are getting a PACA ? */ pcia = get_hdif(&spira.ntuples.pcia, "SPPCIA"); if (!pcia) return false; prlog(PR_INFO, "Got PCIA !\n"); cpus = dt_new(dt_root, "cpus"); dt_add_property_cells(cpus, "#address-cells", 1); dt_add_property_cells(cpus, "#size-cells", 0); for_each_pcia(pcia) { const struct sppcia_core_unique *id; u32 size, ve_flags; bool okay; id = HDIF_get_idata(pcia, SPPCIA_IDATA_CORE_UNIQUE, &size); if (!id || size < sizeof(*id)) { prerror("CORE[%i]: bad id size %u @ %p\n", pcia_index(pcia), size, id); return false; } ve_flags = be32_to_cpu(id->verif_exist_flags); switch ((ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) { case CPU_ID_VERIFY_USABLE_NO_FAILURES: case CPU_ID_VERIFY_USABLE_FAILURES: okay = true; break; default: okay = false; } prlog(okay ? PR_INFO : PR_WARNING, "CORE[%i]: HW_PROC_ID=%i PROC_CHIP_ID=%i EC=0x%x %s\n", pcia_index(pcia), be32_to_cpu(id->hw_proc_id), be32_to_cpu(id->proc_chip_id), be32_to_cpu(id->chip_ec_level), okay ? "OK" : "UNAVAILABLE"); if (!add_core_node(cpus, pcia, id, okay)) break; } return true; } skiboot-skiboot-5.1.13/hdata/slca.c000066400000000000000000000100211265204436200170700ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "spira.h" #include "hdata.h" const struct slca_entry *slca_get_entry(uint16_t slca_index) { struct HDIF_common_hdr *slca_hdr; int count; slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG); if (!slca_hdr) { prerror("SLCA Invalid\n"); return NULL; } count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY); if (count < 0) { prerror("SLCA: Can't find SLCA array size!\n"); return NULL; } if (slca_index < count) { const struct slca_entry *s_entry; unsigned int entry_sz; s_entry = HDIF_get_iarray_item(slca_hdr, SLCA_IDATA_ARRAY, slca_index, &entry_sz); if (s_entry && entry_sz >= sizeof(*s_entry)) return s_entry; } else prlog(PR_NOTICE, "SLCA: Can't find slca_entry for index %d\n", slca_index); return NULL; } const char *slca_get_vpd_name(uint16_t slca_index) { const struct slca_entry *s_entry; s_entry = slca_get_entry(slca_index); if (s_entry) return (const char *)s_entry->fru_id; else prlog(PR_NOTICE, "SLCA: Can't find fru_id for index %d\n", slca_index); return NULL; } const char *slca_get_loc_code_index(uint16_t slca_index) { const struct slca_entry *s_entry; s_entry = slca_get_entry(slca_index); if (s_entry) return s_entry->loc_code; else prlog(PR_NOTICE, "SLCA: Entry %d bad idata\n", slca_index); return NULL; } void slca_vpd_add_loc_code(struct dt_node *node, uint16_t slca_index) { const char *fru_loc_code; char loc_code[LOC_CODE_SIZE]; memset(loc_code, 0, sizeof(loc_code)); fru_loc_code = slca_get_loc_code_index(slca_index); if (!fru_loc_code) return; strncpy(loc_code, fru_loc_code, LOC_CODE_SIZE - 1); dt_add_property(node, "ibm,loc-code", loc_code, strlen(loc_code) + 1); } /* * Get System Attention Indicator SLCA entry */ static const struct slca_entry *slca_get_sai_entry(void) { int count; unsigned int i; struct HDIF_common_hdr *slca_hdr; slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG); if (!slca_hdr) { prerror("SLCA Invalid\n"); return NULL; } count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY); if (count < 0) { prerror("SLCA: Can't find SLCA array size!\n"); return NULL; } for (i = 0; i < count; i++) { const struct slca_entry *s_entry; unsigned int entry_sz; s_entry = HDIF_get_iarray_item(slca_hdr, SLCA_IDATA_ARRAY, i, &entry_sz); if (s_entry && VPD_ID(s_entry->fru_id[0], s_entry->fru_id[1]) == SLCA_SAI_INDICATOR_ID) { prlog(PR_TRACE, "SLCA: SAI index: 0x%x\n", s_entry->my_index); prlog(PR_TRACE, "SLCA: SAI location code: %s\n", s_entry->loc_code); return s_entry; } } return NULL; } /* * SLCA structure contains System Attention Indicator location * code (FRU ID = SA). Add this information to device tree * (under '/ibm,opal/led'). */ void slca_dt_add_sai_node(void) { const struct slca_entry *s_entry; struct dt_node *led_node, *sai_node; s_entry = slca_get_sai_entry(); if (!s_entry) return; /* Create /ibm,opal node, if its not created already */ if (!opal_node) return; /* Crete LED parent node */ led_node = dt_find_by_path(opal_node, DT_PROPERTY_LED_NODE); if (!led_node) return; if (s_entry->loc_code_len == 0 || s_entry->loc_code_len > LOC_CODE_SIZE) return; /* Create SAI node */ sai_node = dt_new(led_node, s_entry->loc_code); assert(sai_node); dt_add_property_string(sai_node, DT_PROPERTY_LED_TYPES, LED_TYPE_ATTENTION); } skiboot-skiboot-5.1.13/hdata/spira.c000066400000000000000000000651771265204436200173120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include #include #include #include #include #include "hdata.h" #include "hostservices.h" /* Processor Initialization structure, contains * the initial NIA and MSR values for the entry * point * * Note: It appears to be ignoring the entry point * and always going to 0x180 */ static int cpu_type; __section(".procin.data") struct proc_init_data proc_init_data = { .hdr = HDIF_SIMPLE_HDR("PROCIN", 1, struct proc_init_data), .regs_ptr = HDIF_IDATA_PTR(offsetof(struct proc_init_data, regs), 0x10), .regs = { .nia = CPU_TO_BE64(0x180), .msr = CPU_TO_BE64(0x9000000000000000ULL), /* SF | HV */ }, }; __section(".cpuctrl.data") struct sp_addr_table cpu_ctl_spat_area; __section(".cpuctrl.data") struct sp_attn_area cpu_ctl_sp_attn_area1; __section(".cpuctrl.data") struct sp_attn_area cpu_ctl_sp_attn_area2; __section(".cpuctrl.data") struct hsr_data_area cpu_ctl_hsr_area; __section(".cpuctrl.data") struct cpu_ctl_init_data cpu_ctl_init_data = { .hdr = HDIF_SIMPLE_HDR(CPU_CTL_HDIF_SIG, 2, struct cpu_ctl_init_data), .cpu_ctl = HDIF_IDATA_PTR(offsetof(struct cpu_ctl_init_data, cpu_ctl_lt), sizeof(struct cpu_ctl_legacy_table)), #if !defined(TEST) .cpu_ctl_lt = { .spat = { .addr = CPU_TO_BE64((unsigned long)&(cpu_ctl_spat_area) + SKIBOOT_BASE), .size = CPU_TO_BE64(sizeof(struct sp_addr_table)), }, .sp_attn_area1 = { .addr = CPU_TO_BE64((unsigned long)&(cpu_ctl_sp_attn_area1) + SKIBOOT_BASE), .size = CPU_TO_BE64(sizeof(struct sp_attn_area)), }, .sp_attn_area2 = { .addr = CPU_TO_BE64((unsigned long)&(cpu_ctl_sp_attn_area2) + SKIBOOT_BASE), .size = CPU_TO_BE64(sizeof(struct sp_attn_area)), }, .hsr_area = { .addr = CPU_TO_BE64((unsigned long)&(cpu_ctl_hsr_area) + SKIBOOT_BASE), .size = CPU_TO_BE64(sizeof(struct hsr_data_area)), }, }, #endif }; /* Populate MDST table * * Note that we only pass sapphire console buffer here so that we can * capture early failure logs. Later dump component (fsp_dump_mdst_init) * creates new table with all the memory sections we are interested and * sends updated table to FSP via MBOX. * * To help the FSP distinguishing between TCE tokens and actual physical * addresses, we set the top bit to 1 on physical addresses */ #define ADDR_TOP_BIT (1ul << 63) __section(".mdst.data") struct dump_mdst_table init_mdst_table[2] = { { .addr = CPU_TO_BE64(INMEM_CON_START | ADDR_TOP_BIT), .type = CPU_TO_BE32(DUMP_REGION_CONSOLE), .size = CPU_TO_BE32(INMEM_CON_LEN), }, { .addr = CPU_TO_BE64(HBRT_CON_START | ADDR_TOP_BIT), .type = CPU_TO_BE32(DUMP_REGION_HBRT_LOG), .size = CPU_TO_BE32(HBRT_CON_LEN), }, }; /* SP Interface Root Array, aka SPIRA */ __section(".spira.data") struct spira spira = { .hdr = HDIF_SIMPLE_HDR("SPIRA ", SPIRA_VERSION, struct spira), .ntuples_ptr = HDIF_IDATA_PTR(offsetof(struct spira, ntuples), sizeof(struct spira_ntuples)), .ntuples = { .array_hdr = { .offset = CPU_TO_BE32(HDIF_ARRAY_OFFSET), .ecnt = CPU_TO_BE32(SPIRA_NTUPLES_COUNT), .esize = CPU_TO_BE32(sizeof(struct spira_ntuple)), .eactsz = CPU_TO_BE32(0x18), }, /* We only populate some n-tuples */ .proc_init = { .addr = CPU_TO_BE64(PROCIN_OFF), .alloc_cnt = CPU_TO_BE16(1), .act_cnt = CPU_TO_BE16(1), .alloc_len = CPU_TO_BE32(sizeof(struct proc_init_data)), }, .heap = { .addr = CPU_TO_BE64(SPIRA_HEAP_BASE), .alloc_cnt = CPU_TO_BE16(1), .alloc_len = CPU_TO_BE32(SPIRA_HEAP_SIZE), }, .mdump_src = { .addr = CPU_TO_BE64(MDST_TABLE_OFF), .alloc_cnt = CPU_TO_BE16(ARRAY_SIZE(init_mdst_table)), .act_cnt = CPU_TO_BE16(ARRAY_SIZE(init_mdst_table)), .alloc_len = CPU_TO_BE32(sizeof(init_mdst_table)), }, #if !defined(TEST) .cpu_ctrl = { .addr = CPU_TO_BE64((unsigned long)&cpu_ctl_init_data), .alloc_cnt = CPU_TO_BE16(1), .act_cnt = CPU_TO_BE16(1), .alloc_len = CPU_TO_BE32(sizeof(cpu_ctl_init_data)), }, #endif }, }; /* Overridden for testing. */ #ifndef spira_check_ptr bool spira_check_ptr(const void *ptr, const char *file, unsigned int line) { if (!ptr) return false; if (((unsigned long)ptr) >= SPIRA_HEAP_BASE && ((unsigned long)ptr) < (SPIRA_HEAP_BASE + SPIRA_HEAP_SIZE)) return true; prerror("SPIRA: Bad pointer %p at %s line %d\n", ptr, file, line); return false; } #endif struct HDIF_common_hdr *__get_hdif(struct spira_ntuple *n, const char id[], const char *file, int line) { struct HDIF_common_hdr *h = ntuple_addr(n); if (!spira_check_ptr(h, file, line)) return NULL; if (!HDIF_check(h, id)) { prerror("SPIRA: bad tuple %p: expected %s at %s line %d\n", h, id, file, line); return NULL; } return h; } static struct dt_node *add_xscom_node(uint64_t base, uint32_t hw_id, uint32_t proc_chip_id) { struct dt_node *node; uint64_t addr, size; addr = base | ((uint64_t)hw_id << PPC_BITLSHIFT(28)); size = (u64)1 << PPC_BITLSHIFT(28); prlog(PR_INFO, "XSCOM: Found HW ID 0x%x (PCID 0x%x) @ 0x%llx\n", hw_id, proc_chip_id, (long long)addr); node = dt_new_addr(dt_root, "xscom", addr); if (!node) return NULL; dt_add_property_cells(node, "ibm,chip-id", hw_id); dt_add_property_cells(node, "ibm,proc-chip-id", proc_chip_id); dt_add_property_cells(node, "#address-cells", 1); dt_add_property_cells(node, "#size-cells", 1); dt_add_property(node, "scom-controller", NULL, 0); switch(proc_gen) { case proc_gen_p7: dt_add_property_strings(node, "compatible", "ibm,xscom", "ibm,power7-xscom"); break; case proc_gen_p8: dt_add_property_strings(node, "compatible", "ibm,xscom", "ibm,power8-xscom"); break; default: dt_add_property_strings(node, "compatible", "ibm,xscom"); } dt_add_property_u64s(node, "reg", addr, size); return node; } struct dt_node *find_xscom_for_chip(uint32_t chip_id) { struct dt_node *node; uint32_t id; dt_for_each_compatible(dt_root, node, "ibm,xscom") { id = dt_get_chip_id(node); if (id == chip_id) return node; } return NULL; } static void add_psihb_node(struct dt_node *np) { u32 psi_scom, psi_slen; const char *psi_comp; /* * We add a few things under XSCOM that aren't added * by any other HDAT path */ /* PSI host bridge */ switch(proc_gen) { case proc_gen_p7: psi_scom = 0x2010c00; psi_slen = 0x10; psi_comp = "ibm,power7-psihb-x"; break; case proc_gen_p8: psi_scom = 0x2010900; psi_slen = 0x20; psi_comp = "ibm,power8-psihb-x"; break; default: psi_comp = NULL; } if (psi_comp) { struct dt_node *psi_np; psi_np = dt_new_addr(np, "psihb", psi_scom); if (!psi_np) return; dt_add_property_cells(psi_np, "reg", psi_scom, psi_slen); dt_add_property_strings(psi_np, "compatible", psi_comp, "ibm,psihb-x"); } } static void add_xscom_add_pcia_assoc(struct dt_node *np, uint32_t pcid) { const struct HDIF_common_hdr *hdr; u32 size; /* * The SPPCRD doesn't contain all the affinity data, we have * to dig it out of a core. I assume this is so that node * affinity can be different for groups of cores within the * chip, but for now we are going to ignore that */ hdr = get_hdif(&spira.ntuples.pcia, SPPCIA_HDIF_SIG); if (!hdr) return; for_each_pcia(hdr) { const struct sppcia_core_unique *id; id = HDIF_get_idata(hdr, SPPCIA_IDATA_CORE_UNIQUE, &size); if (!id || size < sizeof(*id)) continue; if (be32_to_cpu(id->proc_chip_id) != pcid) continue; dt_add_property_cells(np, "ibm,ccm-node-id", be32_to_cpu(id->ccm_node_id)); dt_add_property_cells(np, "ibm,hw-card-id", be32_to_cpu(id->hw_card_id)); dt_add_property_cells(np, "ibm,hw-module-id", be32_to_cpu(id->hw_module_id)); if (!dt_find_property(np, "ibm,dbob-id")) dt_add_property_cells(np, "ibm,dbob-id", be32_to_cpu(id->drawer_book_octant_blade_id)); dt_add_property_cells(np, "ibm,mem-interleave-scope", be32_to_cpu(id->memory_interleaving_scope)); return; } } static bool add_xscom_sppcrd(uint64_t xscom_base) { const struct HDIF_common_hdr *hdif; unsigned int i, vpd_sz; const void *vpd; struct dt_node *np; for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i, SPPCRD_HDIF_SIG) { const struct sppcrd_chip_info *cinfo; u32 ve, version; cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL); if (!CHECK_SPPTR(cinfo)) { prerror("XSCOM: Bad ChipID data %d\n", i); continue; } ve = be32_to_cpu(cinfo->verif_exist_flags) & CHIP_VERIFY_MASK; ve >>= CHIP_VERIFY_SHIFT; if (ve == CHIP_VERIFY_NOT_INSTALLED || ve == CHIP_VERIFY_UNUSABLE) continue; /* Create the XSCOM node */ np = add_xscom_node(xscom_base, be32_to_cpu(cinfo->xscom_id), be32_to_cpu(cinfo->proc_chip_id)); if (!np) continue; version = be16_to_cpu(hdif->version); /* Version 0A has additional OCC related stuff */ if (version >= 0x000a) { if (!dt_find_property(np, "ibm,dbob-id")) dt_add_property_cells(np, "ibm,dbob-id", be32_to_cpu(cinfo->dbob_id)); dt_add_property_cells(np, "ibm,occ-functional-state", be32_to_cpu(cinfo->occ_state)); } /* Add chip VPD */ dt_add_vpd_node(hdif, SPPCRD_IDATA_FRU_ID, SPPCRD_IDATA_KW_VPD); /* Add module VPD on version A and later */ if (version >= 0x000a) { vpd = HDIF_get_idata(hdif, SPPCRD_IDATA_MODULE_VPD, &vpd_sz); if (CHECK_SPPTR(vpd)) dt_add_property(np, "ibm,module-vpd", vpd, vpd_sz); } /* * Extract additional associativity information from * the core data. Pick one core on that chip */ add_xscom_add_pcia_assoc(np, be32_to_cpu(cinfo->proc_chip_id)); /* Add PSI Host bridge */ add_psihb_node(np); } return i > 0; } static void add_xscom_sppaca(uint64_t xscom_base) { const struct HDIF_common_hdr *hdif; unsigned int i; struct dt_node *np; for_each_ntuple_idx(&spira.ntuples.paca, hdif, i, PACA_HDIF_SIG) { const struct sppaca_cpu_id *id; unsigned int chip_id, size; int ve; /* We only suport old style PACA on P7 ! */ assert(proc_gen == proc_gen_p7); id = HDIF_get_idata(hdif, SPPACA_IDATA_CPU_ID, &size); if (!CHECK_SPPTR(id)) { prerror("XSCOM: Bad processor data %d\n", i); continue; } ve = be32_to_cpu(id->verify_exists_flags) & CPU_ID_VERIFY_MASK; ve >>= CPU_ID_VERIFY_SHIFT; if (ve == CPU_ID_VERIFY_NOT_INSTALLED || ve == CPU_ID_VERIFY_UNUSABLE) continue; /* Convert to HW chip ID */ chip_id = P7_PIR2GCID(be32_to_cpu(id->pir)); /* do we already have an XSCOM for this chip? */ if (find_xscom_for_chip(chip_id)) continue; /* Create the XSCOM node */ np = add_xscom_node(xscom_base, chip_id, be32_to_cpu(id->processor_chip_id)); if (!np) continue; /* Add chip VPD */ dt_add_vpd_node(hdif, SPPACA_IDATA_FRU_ID, SPPACA_IDATA_KW_VPD); /* Add chip associativity data */ dt_add_property_cells(np, "ibm,ccm-node-id", be32_to_cpu(id->ccm_node_id)); if (size > SPIRA_CPU_ID_MIN_SIZE) { dt_add_property_cells(np, "ibm,hw-card-id", be32_to_cpu(id->hw_card_id)); dt_add_property_cells(np, "ibm,hw-module-id", be32_to_cpu(id->hardware_module_id)); if (!dt_find_property(np, "ibm,dbob-id")) dt_add_property_cells(np, "ibm,dbob-id", be32_to_cpu(id->drawer_book_octant_blade_id)); dt_add_property_cells(np, "ibm,mem-interleave-scope", be32_to_cpu(id->memory_interleaving_scope)); } /* Add PSI Host bridge */ add_psihb_node(np); } } static void add_xscom(void) { const void *ms_vpd; const struct msvpd_pmover_bsr_synchro *pmbs; unsigned int size; uint64_t xscom_base; ms_vpd = get_hdif(&spira.ntuples.ms_vpd, MSVPD_HDIF_SIG); if (!ms_vpd) { prerror("XSCOM: Can't find MS VPD\n"); return; } pmbs = HDIF_get_idata(ms_vpd, MSVPD_IDATA_PMOVER_SYNCHRO, &size); if (!CHECK_SPPTR(pmbs) || size < sizeof(*pmbs)) { prerror("XSCOM: absent or bad PMBS size %u @ %p\n", size, pmbs); return; } if (!(be32_to_cpu(pmbs->flags) & MSVPD_PMS_FLAG_XSCOMBASE_VALID)) { prerror("XSCOM: No XSCOM base in PMBS, using default\n"); return; } xscom_base = be64_to_cpu(pmbs->xscom_addr); /* Some FSP (on P7) give me a crap base address for XSCOM (it has * spurious bits set as far as I can tell). Since only 5 bits 18:22 can * be programmed in hardware, let's isolate these. This seems to give * me the right value on VPL1 */ if (cpu_type == PVR_TYPE_P7) xscom_base &= 0x80003e0000000000ul; /* Get rid of the top bits */ xscom_base = cleanup_addr(xscom_base); /* First, try the new proc_chip ntuples for chip data */ if (add_xscom_sppcrd(xscom_base)) return; /* Otherwise, check the old-style PACA, looking for unique chips */ add_xscom_sppaca(xscom_base); } static void add_chiptod_node(unsigned int chip_id, int flags) { struct dt_node *node, *xscom_node; const char *compat_str; uint32_t addr, len; if ((flags & CHIPTOD_ID_FLAGS_STATUS_MASK) != CHIPTOD_ID_FLAGS_STATUS_OK) return; xscom_node = find_xscom_for_chip(chip_id); if (!xscom_node) { prerror("CHIPTOD: No xscom for chiptod %d?\n", chip_id); return; } addr = 0x40000; len = 0x34; switch(proc_gen) { case proc_gen_p7: compat_str = "ibm,power7-chiptod"; break; case proc_gen_p8: compat_str = "ibm,power8-chiptod"; break; default: return; } prlog(PR_DEBUG, "CHIPTOD: Found on chip 0x%x %s\n", chip_id, (flags & CHIPTOD_ID_FLAGS_PRIMARY) ? "[primary]" : ((flags & CHIPTOD_ID_FLAGS_SECONDARY) ? "[secondary]" : "")); node = dt_new_addr(xscom_node, "chiptod", addr); if (!node) return; dt_add_property_cells(node, "reg", addr, len); dt_add_property_strings(node, "compatible", "ibm,power-chiptod", compat_str); if (flags & CHIPTOD_ID_FLAGS_PRIMARY) dt_add_property(node, "primary", NULL, 0); if (flags & CHIPTOD_ID_FLAGS_SECONDARY) dt_add_property(node, "secondary", NULL, 0); } static bool add_chiptod_old(void) { const void *hdif; unsigned int i; bool found = false; /* * Locate chiptod ID structures in SPIRA */ if (!get_hdif(&spira.ntuples.chip_tod, "TOD ")) return found; for_each_ntuple_idx(&spira.ntuples.chip_tod, hdif, i, "TOD ") { const struct chiptod_chipid *id; id = HDIF_get_idata(hdif, CHIPTOD_IDATA_CHIPID, NULL); if (!CHECK_SPPTR(id)) { prerror("CHIPTOD: Bad ChipID data %d\n", i); continue; } add_chiptod_node(pcid_to_chip_id(be32_to_cpu(id->chip_id)), be32_to_cpu(id->flags)); found = true; } return found; } static bool add_chiptod_new(uint32_t master_cpu) { const void *hdif; unsigned int i, master_chip; bool found = false; /* * Locate Proc Chip ID structures in SPIRA */ if (!get_hdif(&spira.ntuples.proc_chip, SPPCRD_HDIF_SIG)) return found; master_chip = pir_to_chip_id(master_cpu); for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i, SPPCRD_HDIF_SIG) { const struct sppcrd_chip_info *cinfo; const struct sppcrd_chip_tod *tinfo; unsigned int size; u32 ve, flags; cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL); if (!CHECK_SPPTR(cinfo)) { prerror("CHIPTOD: Bad ChipID data %d\n", i); continue; } ve = be32_to_cpu(cinfo->verif_exist_flags) & CHIP_VERIFY_MASK; ve >>= CHIP_VERIFY_SHIFT; if (ve == CHIP_VERIFY_NOT_INSTALLED || ve == CHIP_VERIFY_UNUSABLE) continue; tinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_TOD, &size); if (!CHECK_SPPTR(tinfo)) { prerror("CHIPTOD: Bad TOD data %d\n", i); continue; } flags = be32_to_cpu(tinfo->flags); /* The FSP may strip the chiptod info from HDAT; if we find * a zero-ed out entry, assume that the chiptod is * present, but we don't have any primary/secondary info. In * this case, pick the primary based on the CPU that was * assigned master. */ if (!size) { flags = CHIPTOD_ID_FLAGS_STATUS_OK; if (be32_to_cpu(cinfo->xscom_id) == master_chip) flags |= CHIPTOD_ID_FLAGS_PRIMARY; } add_chiptod_node(be32_to_cpu(cinfo->xscom_id), flags); found = true; } return found; } static void add_nx_node(u32 gcid) { struct dt_node *nx; const char *cp_str; u32 addr; u32 size; struct dt_node *xscom; xscom = find_xscom_for_chip(gcid); if (xscom == NULL) { prerror("NX%d: did not found xscom node.\n", gcid); return; } /* * The NX register space is relatively self contained on P7+ but * a bit more messy on P8. However it's all contained within the * PB chiplet port 1 so we'll stick to that in the "reg" property * and let the NX "driver" deal with the details. */ addr = 0x2010000; size = 0x0004000; switch (proc_gen) { case proc_gen_p7: cp_str = "ibm,power7-nx"; break; case proc_gen_p8: cp_str = "ibm,power8-nx"; break; default: return; } nx = dt_new_addr(xscom, "nx", addr); if (!nx) return; dt_add_property_cells(nx, "reg", addr, size); dt_add_property_strings(nx, "compatible", "ibm,power-nx", cp_str); } static void add_nx(void) { unsigned int i; void *hdif; for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i, SPPCRD_HDIF_SIG) { const struct sppcrd_chip_info *cinfo; u32 ve; cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL); if (!CHECK_SPPTR(cinfo)) { prerror("NX: Bad ChipID data %d\n", i); continue; } ve = be32_to_cpu(cinfo->verif_exist_flags) & CHIP_VERIFY_MASK; ve >>= CHIP_VERIFY_SHIFT; if (ve == CHIP_VERIFY_NOT_INSTALLED || ve == CHIP_VERIFY_UNUSABLE) continue; if (cinfo->nx_state) add_nx_node(be32_to_cpu(cinfo->xscom_id)); } } static void add_iplparams_sys_params(const void *iplp, struct dt_node *node) { const struct iplparams_sysparams *p; u32 sys_type; const char *sys_family; p = HDIF_get_idata(iplp, IPLPARAMS_SYSPARAMS, NULL); if (!CHECK_SPPTR(p)) { prerror("IPLPARAMS: No SYS Parameters\n"); /* Create a generic compatible property */ dt_add_property_string(dt_root, "compatible", "ibm,powernv"); return; } node = dt_new(node, "sys-params"); assert(node); dt_add_property_cells(node, "#address-cells", 0); dt_add_property_cells(node, "#size-cells", 0); dt_add_property_nstr(node, "ibm,sys-model", p->sys_model, 4); /* Compatible is 2 entries: ibm,powernv and ibm, */ sys_type = be32_to_cpu(p->system_type); switch(sys_type >> 28) { case 0: sys_family = "ibm,squadrons"; break; case 1: sys_family = "ibm,eclipz"; break; case 2: sys_family = "ibm,apollo"; break; case 3: sys_family = "ibm,firenze"; break; default: sys_family = NULL; prerror("IPLPARAMS: Unknown system family\n"); break; } dt_add_property_strings(dt_root, "compatible", "ibm,powernv", sys_family); } static void add_iplparams_ipl_params(const void *iplp, struct dt_node *node) { const struct iplparams_iplparams *p; struct dt_node *led_node; p = HDIF_get_idata(iplp, IPLPARAMS_IPLPARAMS, NULL); if (!CHECK_SPPTR(p)) { prerror("IPLPARAMS: No IPL Parameters\n"); return; } node = dt_new(node, "ipl-params"); assert(node); dt_add_property_cells(node, "#address-cells", 0); dt_add_property_cells(node, "#size-cells", 0); /* On an ASM initiated factory reset, this bit will be set * and the FSP expects the firmware to reset the PCI bus * numbers and respond with a Power Down (CE,4D,02) message */ if (p->other_attrib & IPLPARAMS_OATTR_RST_PCI_BUSNO) dt_add_property_cells(node, "pci-busno-reset-ipl", 1); dt_add_property_strings(node, "cec-ipl-side", (p->ipl_side & IPLPARAMS_CEC_FW_IPL_SIDE_TEMP) ? "temp" : "perm"); dt_add_property_strings(node, "fsp-ipl-side", (p->ipl_side & IPLPARAMS_FSP_FW_IPL_SIDE_TEMP) ? "temp" : "perm"); dt_add_property_cells(node, "os-ipl-mode", p->os_ipl_mode); dt_add_property_strings(node, "cec-major-type", p->cec_ipl_maj_type ? "hot" : "cold"); /* Add LED type info under '/ibm,opal/led' node */ led_node = dt_find_by_path(opal_node, DT_PROPERTY_LED_NODE); assert(led_node); if (p->other_attrib & IPLPARAMS_OATRR_LIGHT_PATH) dt_add_property_strings(led_node, DT_PROPERTY_LED_MODE, LED_MODE_LIGHT_PATH); else dt_add_property_strings(led_node, DT_PROPERTY_LED_MODE, LED_MODE_GUIDING_LIGHT); } static void add_iplparams_serials(const void *iplp, struct dt_node *node) { const struct iplparms_serial *ipser; struct dt_node *ser_node; int count, i; count = HDIF_get_iarray_size(iplp, IPLPARMS_IDATA_SERIAL); if (!count) { prerror("IPLPARAMS: No serial ports\n"); return; } prlog(PR_INFO, "IPLPARAMS: %d serial ports in array\n", count); node = dt_new(node, "fsp-serial"); assert(node); dt_add_property_cells(node, "#address-cells", 1); dt_add_property_cells(node, "#size-cells", 0); for (i = 0; i < count; i++) { u16 rsrc_id; ipser = HDIF_get_iarray_item(iplp, IPLPARMS_IDATA_SERIAL, i, NULL); if (!CHECK_SPPTR(ipser)) continue; rsrc_id = be16_to_cpu(ipser->rsrc_id); prlog(PR_INFO, "IPLPARAMS: Serial %d rsrc: %04x loc: %s\n", i, rsrc_id, ipser->loc_code); ser_node = dt_new_addr(node, "serial", rsrc_id); if (!ser_node) continue; dt_add_property_cells(ser_node, "reg", rsrc_id); dt_add_property_nstr(ser_node, "ibm,loc-code", ipser->loc_code, LOC_CODE_SIZE); dt_add_property_string(ser_node, "compatible", "ibm,fsp-serial"); /* XXX handle CALLHOME flag ? */ } } /* * Check for platform dump, if present populate DT */ static void add_iplparams_platform_dump(const void *iplp, struct dt_node *node) { const struct iplparams_dump *ipl_dump; ipl_dump = HDIF_get_idata(iplp, IPLPARAMS_PLATFORM_DUMP, NULL); if (!CHECK_SPPTR(ipl_dump)) return; node = dt_new(node, "platform-dump"); assert(node); if (be32_to_cpu(ipl_dump->dump_id)) { dt_add_property_cells(node, "dump-id", be32_to_cpu(ipl_dump->dump_id)); dt_add_property_u64(node, "total-size", be64_to_cpu(ipl_dump->act_dump_sz)); dt_add_property_u64(node, "hw-dump-size", be32_to_cpu(ipl_dump->act_hw_dump_sz)); dt_add_property_cells(node, "plog-id", be32_to_cpu(ipl_dump->plid)); } } static void add_iplparams(void) { struct dt_node *iplp_node; const void *ipl_parms; ipl_parms = get_hdif(&spira.ntuples.ipl_parms, "IPLPMS"); if (!ipl_parms) { prerror("IPLPARAMS: Cannot find IPL Parms in SPIRA\n"); return; } iplp_node = dt_new(dt_root, "ipl-params"); assert(iplp_node); dt_add_property_cells(iplp_node, "#address-cells", 0); dt_add_property_cells(iplp_node, "#size-cells", 0); add_iplparams_sys_params(ipl_parms, iplp_node); add_iplparams_ipl_params(ipl_parms, iplp_node); add_iplparams_serials(ipl_parms, iplp_node); add_iplparams_platform_dump(ipl_parms, iplp_node); } /* Various structure contain a "proc_chip_id" which is an arbitrary * numbering used by HDAT to reference chips, which doesn't correspond * to the HW IDs. We want to use the HW IDs everywhere in the DT so * we convert using this. * * Note: On P7, the HW ID is the XSCOM "GCID" including the T bit which * is *different* from the chip ID portion of the interrupt server# * (or PIR). See the explanations in chip.h */ uint32_t pcid_to_chip_id(uint32_t proc_chip_id) { unsigned int i; const void *hdif; /* First, try the proc_chip ntuples for chip data */ for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i, SPPCRD_HDIF_SIG) { const struct sppcrd_chip_info *cinfo; cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL); if (!CHECK_SPPTR(cinfo)) { prerror("XSCOM: Bad ChipID data %d\n", i); continue; } if (proc_chip_id == be32_to_cpu(cinfo->proc_chip_id)) return be32_to_cpu(cinfo->xscom_id); } /* Otherwise, check the old-style PACA, looking for unique chips */ for_each_ntuple_idx(&spira.ntuples.paca, hdif, i, PACA_HDIF_SIG) { const struct sppaca_cpu_id *id; /* We only suport old style PACA on P7 ! */ assert(proc_gen == proc_gen_p7); id = HDIF_get_idata(hdif, SPPACA_IDATA_CPU_ID, NULL); if (!CHECK_SPPTR(id)) { prerror("XSCOM: Bad processor data %d\n", i); continue; } if (proc_chip_id == be32_to_cpu(id->processor_chip_id)) return P7_PIR2GCID(be32_to_cpu(id->pir)); } /* Not found, what to do ? Assert ? For now return a number * guaranteed to not exist */ return (uint32_t)-1; } /* Create '/ibm,opal/led' node */ static void dt_init_led_node(void) { struct dt_node *led_node; /* Create /ibm,opal node, if its not created already */ if (!opal_node) { opal_node = dt_new(dt_root, "ibm,opal"); assert(opal_node); } /* Crete LED parent node */ led_node = dt_new(opal_node, DT_PROPERTY_LED_NODE); assert(led_node); } static void dt_init_vpd_node(void) { struct dt_node *dt_vpd; dt_vpd = dt_new(dt_root, "vpd"); assert(dt_vpd); dt_add_property_string(dt_vpd, "compatible", "ibm,opal-v3-vpd"); } static void hostservices_parse(void) { struct HDIF_common_hdr *hs_hdr; const void *dt_blob; unsigned int size; unsigned int ntuples_size; ntuples_size = sizeof(struct HDIF_array_hdr) + be32_to_cpu(spira.ntuples.array_hdr.ecnt) * sizeof(struct spira_ntuple); if (offsetof(struct spira_ntuples, hs_data) >= ntuples_size) { prerror("SPIRA: No host services data found\n"); return; } hs_hdr = get_hdif(&spira.ntuples.hs_data, HSERV_HDIF_SIG); if (!hs_hdr) { prerror("SPIRA: No host services data found\n"); return; } dt_blob = HDIF_get_idata(hs_hdr, 0, &size); if (!dt_blob) { prerror("SPIRA: No host services idata found\n"); return; } hservices_from_hdat(dt_blob, size); } void parse_hdat(bool is_opal, uint32_t master_cpu) { cpu_type = PVR_TYPE(mfspr(SPR_PVR)); prlog(PR_DEBUG, "Parsing HDAT...\n"); dt_root = dt_new_root(""); /* * Basic DT root stuff */ dt_add_property_cells(dt_root, "#address-cells", 2); dt_add_property_cells(dt_root, "#size-cells", 2); dt_add_property_string(dt_root, "lid-type", is_opal ? "opal" : "phyp"); /* Create /vpd node */ dt_init_vpd_node(); /* Create /ibm,opal/led node */ dt_init_led_node(); /* Parse SPPACA and/or PCIA */ if (!pcia_parse()) paca_parse(); /* IPL params */ add_iplparams(); /* Parse MS VPD */ memory_parse(); /* Add XSCOM node (must be before chiptod & IO ) */ add_xscom(); /* Add FSP */ fsp_parse(); /* Add ChipTOD's */ if (!add_chiptod_old() && !add_chiptod_new(master_cpu)) prerror("CHIPTOD: No ChipTOD found !\n"); /* Add NX */ add_nx(); /* Add IO HUBs and/or PHBs */ io_parse(); /* Parse VPD */ vpd_parse(); /* Host services information. */ hostservices_parse(); /* Parse System Attention Indicator inforamtion */ slca_dt_add_sai_node(); prlog(PR_INFO, "Parsing HDAT...done\n"); } skiboot-skiboot-5.1.13/hdata/spira.h000066400000000000000000000571611265204436200173110ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SPIRA_H #define __SPIRA_H #include "hdif.h" /* * The SPIRA structure * * NOTE: This is one of the only HDIF structure that we layout entirely * as a C struct because it's provided by us to the FSP. Almost everything * else is generated by the FSP, and thus must be "parsed" since the various * offsets and alignments might change. */ #define SPIRA_VERSION 0x20 /* Like 730 ? */ struct spira_ntuple { __be64 addr; __be16 alloc_cnt; __be16 act_cnt; __be32 alloc_len; __be32 act_len; __be32 tce_off; __be64 padding; } __packed; #define SPIRA_NTUPLES_COUNT 0x18 struct spira_ntuples { struct HDIF_array_hdr array_hdr; struct spira_ntuple sp_subsys; /* 0x040 */ struct spira_ntuple ipl_parms; /* 0x060 */ struct spira_ntuple nt_enclosure_vpd; /* 0x080 */ struct spira_ntuple slca; /* 0x0a0 */ struct spira_ntuple backplane_vpd; /* 0x0c0 */ struct spira_ntuple system_vpd; /* 0x0e0 */ struct spira_ntuple chip_tod; /* 0x100 */ struct spira_ntuple proc_init; /* 0x120 */ struct spira_ntuple clock_vpd; /* 0x140 */ struct spira_ntuple anchor_vpd; /* 0x160 */ struct spira_ntuple op_panel_vpd; /* 0x180 */ struct spira_ntuple ext_cache_fru_vpd; /* 0x1a0 */ struct spira_ntuple misc_cec_fru_vpd; /* 0x1c0 */ struct spira_ntuple paca; /* 0x1e0 */ struct spira_ntuple ms_vpd; /* 0x200 */ struct spira_ntuple cec_iohub_fru; /* 0x220 */ struct spira_ntuple cpu_ctrl; /* 0x240 */ struct spira_ntuple mdump_src; /* 0x260 */ struct spira_ntuple mdump_dst; /* 0x280 */ struct spira_ntuple mdump_res; /* 0x2a0 */ struct spira_ntuple heap; /* 0x2c0 */ struct spira_ntuple pcia; /* 0x2e0 */ struct spira_ntuple proc_chip; /* 0x300 */ struct spira_ntuple hs_data; /* 0x320 */ }; struct spira { struct HDIF_common_hdr hdr; struct HDIF_idata_ptr ntuples_ptr; __be64 pad; struct spira_ntuples ntuples; u8 reserved[0x4c0]; } __packed __align(0x100); extern struct spira spira; /* This macro can be used to check the validity of a pointer returned * by one of the HDIF API functions. It returns true if the pointer * appears valid. If it's not valid and not NULL, it will print some * error in the log as well. */ #define CHECK_SPPTR(_ptr) spira_check_ptr(_ptr, __FILE__, __LINE__) #define get_hdif(ntuple, id) __get_hdif((ntuple), (id), __FILE__, __LINE__) extern struct HDIF_common_hdr *__get_hdif(struct spira_ntuple *n, const char id[], const char *file, int line); #define for_each_ntuple_idx(_ntuples, _p, _idx, _id) \ for (_p = get_hdif((_ntuples), _id ""), _idx = 0; \ _p && _idx < be16_to_cpu((_ntuples)->act_cnt); \ _p = (void *)_p + be32_to_cpu((_ntuples)->alloc_len), _idx++) #define for_each_ntuple(_ntuples, _p, _id) \ for (_p = get_hdif((_ntuples), _id ""); \ _p && (void *)_p < ntuple_addr(_ntuples) \ + (be16_to_cpu((_ntuples)->act_cnt) * \ be32_to_cpu((_ntuples)->alloc_len)); \ _p = (void *)_p + be32_to_cpu((_ntuples)->alloc_len)) #define for_each_paca(p) for_each_ntuple(&spira.ntuples.paca, p, PACA_HDIF_SIG) #define for_each_pcia(p) for_each_ntuple(&spira.ntuples.pcia, p, SPPCIA_HDIF_SIG) /* We override these for testing. */ #ifndef ntuple_addr #define ntuple_addr(_ntuples) ((void *)BE64_TO_CPU((_ntuples)->addr)) #endif #ifndef spira_check_ptr extern bool spira_check_ptr(const void *ptr, const char *file, unsigned int line); #endif struct proc_init_data { struct HDIF_common_hdr hdr; struct HDIF_idata_ptr regs_ptr; struct { __be64 nia; __be64 msr; } regs; } __packed __align(0x10); /* * The FRU ID structure is used in several tuples, so we * define it generically here */ struct spira_fru_id { __be16 slca_index; __be16 rsrc_id; /* formerly VPD port number */ }; /* * The FRU operational status structure is used in several * tuples, so we define it generically here */ struct spira_fru_op_status { uint8_t flags; #define FRU_OP_STATUS_FLAG_USED 0x02 /* If 0 -> not used (redundant) */ #define FRU_OP_STATUS_FLAG_FUNCTIONAL 0x01 /* If 0 -> non-functional */ uint8_t reserved[3]; }; /* * Move VPD related stuff to another file ... */ #define VPD_ID(_a, _b) ((_a) << 8 | (_b)) /* * Service Processor Subsystem Structure * * This structure contains several internal data blocks * describing the service processor(s) in the system */ #define SPSS_HDIF_SIG "SPINFO" /* Idata index 0 : FRU ID Data */ #define SPSS_IDATA_FRU_ID 0 /* Idata index 1 : Keyword VPD for the FSP instance */ #define SPSS_IDATA_KEYWORD_VPD 1 /* Idata index 2 : SP Implementation */ #define SPSS_IDATA_SP_IMPL 2 struct spss_sp_impl { __be16 hw_version; __be16 sw_version; __be16 func_flags; #define SPSS_SP_IMPL_FLAGS_INSTALLED 0x8000 #define SPSS_SP_IMPL_FLAGS_FUNCTIONAL 0x4000 #define SPSS_SP_IMPL_FLAGS_PRIMARY 0x2000 u8 chip_version; u8 reserved; }; /* Idata index 3 is deprecated */ /* Idata index 4 : SP Memory Locator */ #define SPSS_IDATA_SP_MEMLOC 4 /* Idata index 5 : SP I/O path array */ #define SPSS_IDATA_SP_IOPATH 5 /* An HDIF array of IO path */ struct spss_iopath { __be16 iopath_type; #define SPSS_IOPATH_TYPE_IOHUB_PHB 0x0001 #define SPSS_IOPATH_TYPE_PSI 0x0002 union { struct { __be16 iohub_chip_inst; __be16 iohub_chip_port; __be16 phb_id; } __packed iohub_phb; struct { __be16 link_status; #define SPSS_IO_PATH_PSI_LINK_BAD_FRU 0x0000 #define SPSS_IO_PATH_PSI_LINK_CURRENT 0x0001 #define SPSS_IO_PATH_PSI_LINK_BACKUP 0x0002 u8 ml2_version; u8 reserved; __be16 slca_count; u8 slca_idx[16]; __be32 proc_chip_id; __be32 reserved2; __be64 gxhb_base; } __packed psi; }; } __packed; /* * IPL Parms structure * */ /* Idata index 0: System Parameters */ #define IPLPARAMS_SYSPARAMS 0 struct iplparams_sysparams { char sys_model[4]; char cpu_feature_code[4]; __be32 effective_pvr; __be32 system_type; uint8_t num_lpar_oct[8]; __be32 abc_bus_speed; __be32 wxyz_bus_speed; __be32 sys_eco_mode; __be32 sys_attributes; __be32 mem_scrubbing; __be16 cur_spl_value; uint8_t pump_mode; uint8_t use_pore_sleep; __be32 pore_image_size; } __packed; /* Idata index 1: IPL parameters */ #define IPLPARAMS_IPLPARAMS 1 struct iplparams_iplparams { uint8_t reserved; uint8_t hv_ipl_dest; uint8_t ipl_side; #define IPLPARAMS_CEC_FW_IPL_SIDE_TEMP 0x10 #define IPLPARAMS_FSP_FW_IPL_SIDE_TEMP 0x01 uint8_t ipl_speed; __be16 cec_ipl_attrib; uint8_t cec_ipl_maj_type; uint8_t cec_ipl_min_type; uint8_t os_ipl_mode; uint8_t keylock_pos; uint8_t lmb_size; uint8_t deprecated; __be32 max_hsl_opticonnect; __be32 other_attrib; #define IPLPARAMS_OATTR_RST_PCI_BUSNO 0x08000000 #define IPLPARAMS_OATTR_CLEAR_NVRAM 0x04000000 #define IPLPARAMS_OATRR_LIGHT_PATH 0x00000004 __be16 huge_page_count; uint8_t huge_page_size; #define IPLPARAMS_HUGE_PG_SIZE_16G 0 uint8_t num_vlan_switches; __be64 reserved2; }; /* Idata index 4: Platform Dump Descriptor */ #define IPLPARAMS_PLATFORM_DUMP 4 struct iplparams_dump { __be16 flags; uint8_t reserved1; uint8_t policy; #define HYP_DUMP_POLICY_NORMAL 0x00 __be32 dump_id; __be64 reserved2; __be64 act_dump_sz; __be32 max_hw_dump_sz; __be32 act_hw_dump_sz; __be32 max_sp_dump_sz; __be32 plid; }; /* Idata index 8: serial ports */ #define IPLPARMS_IDATA_SERIAL 8 /* An HDIF array of serial descriptions */ struct iplparms_serial { uint8_t loc_code[LOC_CODE_SIZE]; __be16 rsrc_id; __be16 flags; #define PLPARMS_SERIAL_FLAGS_CALLHOME 0x8000 } __packed; /* * Chip TOD structure * * This is an array of 32 entries (I assume per possible chip) */ /* Idata index 0: Chip ID data (array) */ #define CHIPTOD_IDATA_CHIPID 0 struct chiptod_chipid { __be32 chip_id; __be32 flags; #define CHIPTOD_ID_FLAGS_PRIMARY 0x02 #define CHIPTOD_ID_FLAGS_SECONDARY 0x01 #define CHIPTOD_ID_FLAGS_STATUS_MASK 0x0c #define CHIPTOD_ID_FLAGS_STATUS_OK 0x04 #define CHIPTOD_ID_FLAGS_STATUS_NOK 0x08 } __packed; /* Idata index 0: Chip Initialization data */ #define CHIPTOD_IDATA_CHIPINIT 1 struct chiptod_chipinit { __be32 ctrl_reg_internal; __be32 tod_ctrl_reg; } __packed; /* * MS VPD - Memory Description Tree * * One such structure pointing to the various memory arrays * along with other infos about the BCRs, Page Mover, XSCOM,... */ #define MSVPD_HDIF_SIG "MS VPD" /* Idata index 0: Mainstore address config */ #define MSVPD_IDATA_MS_ADDR_CONFIG 0 /* Mainstore Address Configuration */ struct msvpd_ms_addr_config { __be64 max_configured_ms_address; __be64 max_possible_ms_address; __be32 deprecated; __be64 mirrorable_memory_starting_address; } __attribute__((packed)); /* Idata index 1: Total configured mainstore */ #define MSVPD_IDATA_TOTAL_CONFIG_MS 1 struct msvpd_total_config_ms { __be64 total_in_mb; }; /* Idata index 2: Page mover and sync structure */ #define MSVPD_IDATA_PMOVER_SYNCHRO 2 struct msvpd_pmover_bsr_synchro { __be32 flags; #define MSVPD_PMS_FLAG_HWLOCK_EN 0x80000000 #define MSVPD_PMS_FLAG_PMOVER_EN 0x40000000 #define MSVPD_PMS_FLAG_BSR_EN 0x20000000 #define MSVPD_PMS_FLAG_XSCOMBASE_VALID 0x10000000 /* P7 values for BSR mode */ #define MSVPD_PMS_FLAG_P7BSR_1M_MODE 0x00000000 #define MSVPD_PMS_FLAG_P7BSR_2M_MODE 0x02000000 #define MSVPD_PMS_FLAG_P7BSR_4M_MODE 0x04000000 #define MSVPD_PMS_FLAG_P7BSR_8M_MODE 0x06000000 __be32 hwlocks_per_page; __be64 hwlock_addr; __be64 pmover_addr; __be64 bsr_addr; __be64 xscom_addr; }; /* Idata index 3: Memory Trace Array */ /* Idata index 4: UE Address Array */ /* Child index 0: MS area child structure */ #define MSVPD_CHILD_MS_AREAS 0 /* * CEC I/O Hub FRU * * This is an array of CEC Hub FRU HDIF structures * * Each of these has some idata pointers to generic info about the * hub and a possible child pointer for daughter card. * * Actual ports are in the SLCA and need to be cross referenced * * Note that slots meant for the addition of GX+ adapters that * are currently unpopulated but support hotplug will have a * minimum "placeholder" entry, which will be fully populated * when the array is rebuild during concurrent maintainance. * This "placeholder" is called a "reservation". * * WARNING: The array rebuild by concurrent maintainance is not * guaranteed to be in the same order as the IPL array, not is * the order stable between concurrent maintainance operations. * * There's also a child pointer to daugher card structures but * we aren't going to handle that just yet. */ #define CECHUB_FRU_HDIF_SIG "IO HUB" #define IOKID_FRU_HDIF_SIG "IO KID" /* Idata index 0: FRU ID data * * This is a generic struct spira_fru_id defined above */ #define CECHUB_FRU_ID_DATA 0 /* Idata index 1: ASCII Keyword VPD */ #define CECHUB_ASCII_KEYWORD_VPD 1 /* Idata index 2: Hub FRU ID data area */ #define CECHUB_FRU_ID_DATA_AREA 2 struct cechub_hub_fru_id { __be32 card_type; #define CECHUB_FRU_TYPE_IOHUB_RSRV 0 #define CECHUB_FRU_TYPE_IOHUB_CARD 1 #define CECHUB_FRU_TYPE_CPU_CARD 2 #define CECHUB_FRU_TYPE_CEC_BKPLANE 3 #define CECHUB_FRU_TYPE_BKPLANE_EXT 4 __be32 unused; __be16 total_chips; uint8_t flags; #define CECHUB_FRU_FLAG_HEADLESS 0x80 /* not connected to CPU */ #define CECHUB_FRU_FLAG_PASSTHROUGH 0x40 /* connected to passhtrough port of another hub */ uint8_t reserved; __be16 parent_hub_id; /* chip instance number of the hub that contains the passthrough port this one is connected to */ __be16 reserved2; } __packed; /* Idata index 3: IO HUB array */ #define CECHUB_FRU_IO_HUBS 3 /* This is an HDIF array of IO Hub structures */ struct cechub_io_hub { __be64 fmtc_address; __be32 fmtc_tce_size; __be16 hub_num; /* unique hub number (I/O Hub ID) */ uint8_t flags; #define CECHUB_HUB_FLAG_STATE_MASK 0xc0 #define CECHUB_HUB_FLAG_STATE_OK 0x00 #define CECHUB_HUB_FLAG_STATE_FAILURES 0x40 #define CECHUB_HUB_FLAG_STATE_NOT_INST 0x80 #define CECHUB_HUB_FLAG_STATE_UNUSABLE 0xc0 #define CECHUB_HUB_FLAG_MASTER_HUB 0x20 /* HDAT < v9.x only */ #define CECHUB_HUB_FLAG_GARD_MASK_VALID 0x08 /* HDAT < v9.x only */ #define CECHUB_HUB_FLAG_SWITCH_MASK_PDT 0x04 /* HDAT < v9.x only */ #define CECHUB_HUB_FLAG_FAB_BR0_PDT 0x02 /* HDAT < v9.x only */ #define CECHUB_HUB_FLAG_FAB_BR1_PDT 0x01 /* HDAT < v9.x only */ uint8_t nr_ports; /* HDAT < v9.x only */ uint8_t fab_br0_pdt; /* p5ioc2 PCI-X or P8 PHB3's */ #define CECHUB_HUB_FAB_BR0_PDT_PHB0 0x80 #define CECHUB_HUB_FAB_BR0_PDT_PHB1 0x40 #define CECHUB_HUB_FAB_BR0_PDT_PHB2 0x20 #define CECHUB_HUB_FAB_BR0_PDT_PHB3 0x10 uint8_t fab_br1_pdt; /* p5ioc2 & p7ioc PCI-E */ #define CECHUB_HUB_FAB_BR1_PDT_PHB0 0x80 #define CECHUB_HUB_FAB_BR1_PDT_PHB1 0x40 #define CECHUB_HUB_FAB_BR1_PDT_PHB2 0x20 #define CECHUB_HUB_FAB_BR1_PDT_PHB3 0x10 #define CECHUB_HUB_FAB_BR1_PDT_PHB4 0x08 /* p7ioc only */ #define CECHUB_HUB_FAB_BR1_PDT_PHB5 0x04 /* p7ioc only */ __be16 iohub_id; /* the type of hub */ #define CECHUB_HUB_P5IOC2 0x1061 /* from VPL1 */ #define CECHUB_HUB_P7IOC 0x60e7 /* from VPL3 */ #define CECHUB_HUB_MURANO 0x20ef /* Murano from spec */ #define CECHUB_HUB_MURANO_SEGU 0x0001 /* Murano+Seguso from spec */ #define CECHUB_HUB_VENICE_WYATT 0x0010 /* Venice+Wyatt from spec */ __be32 ec_level; __be32 aff_dom2; /* HDAT < v9.x only */ __be32 aff_dom3; /* HDAT < v9.x only */ __be64 reserved; __be32 proc_chip_id; union { /* HDAT < v9.x */ struct { __be32 gx_index; /* GX bus index on cpu */ __be32 buid_ext; /* BUID Extension */ __be32 xscom_chip_id; /* TORRENT ONLY */ }; /* HDAT >= v9.x */ struct { __be32 reserved1; __be32 reserved2; __be16 reserved3; __be16 hw_topology; }; }; __be32 mrid; __be32 mem_map_vers; union { /* HDAT < v9.x */ struct { __be64 gx_ctrl_bar0; __be64 gx_ctrl_bar1; __be64 gx_ctrl_bar2; __be64 gx_ctrl_bar3; __be64 gx_ctrl_bar4; __be32 sw_mask_pdt; __be16 gard_mask; __be16 gx_bus_speed; /* Version 0x58 */ }; /* HDAT >= v9.x, HDIF version 0x6A or later */ struct { /* 4 values per PHB, 4 PHBs */ __be64 phb_lane_eq[4][4]; }; }; } __packed; /* We support structures as small as 0x68 bytes */ #define CECHUB_IOHUB_MIN_SIZE 0x68 /* Child index 0: IO Daugther Card */ #define CECHUB_CHILD_IO_KIDS 0 /* * IO KID is a dauther card structure */ #define IOKID_FRU_ID_DATA 0 #define IOKID_KW_VPD 1 /* * CPU Controls Legacy Structure */ struct cpu_ctl_legacy { __be64 addr; __be64 size; } __packed; /* * CPU Control Legacy table * * Format of this table is defined in FIPS PHYP Attn spec. */ struct cpu_ctl_legacy_table { struct cpu_ctl_legacy spat; struct cpu_ctl_legacy sp_attn_area1; struct cpu_ctl_legacy sp_attn_area2; struct cpu_ctl_legacy hsr_area; struct cpu_ctl_legacy reserved[12]; } __packed; /* * CPU Controls Header Structure */ #define CPU_CTL_HDIF_SIG "CPUCTL" struct cpu_ctl_init_data { struct HDIF_common_hdr hdr; struct HDIF_idata_ptr cpu_ctl; uint8_t reserved[8]; struct cpu_ctl_legacy_table cpu_ctl_lt; } __packed __align(0x10); /* * Slot Location Code Array (aka SLCA) * * This is a pile of location codes referenced by various other * structures such as the IO Hubs for things on the CEC. Not * everything in there is a physical port. The SLCA is actually * a tree which represent the topology of the system. * * The tree works as follow: A parent has a pointer to the first * child. A child has a pointer to its parent. Siblings are * consecutive entries. * * Note: If we ever support concurrent maintainance... this is * completely rebuilt, invalidating all indices, though other * structures that may reference SLCA by index will be rebuilt * as well. * * Note that a lot of that stuff is based on VPD documentation * such as the identification keywords. I will list the ones * I manage to figure out without the doc separately. */ #define SLCA_HDIF_SIG "SLCA " /* Idata index 0 : SLCA root pointer * * The SLCA array is an HDIF array of all the entries. The tree * structure is based on indices inside the entries and order of * the entries */ #define SLCA_IDATA_ARRAY 0 #define SLCA_ROOT_INDEX 0 /* Note: An "index" (or idx) is always an index into the SLCA array * and "id" is a reference to some other object. */ struct slca_entry { __be16 my_index; /* redundant, useful */ __be16 rsrc_id; /* formerly VPD port number */ uint8_t fru_id[2]; /* ASCII VPD ID */ #define SLCA_ROOT_VPD_ID VPD_ID('V','V') #define SLCA_SYSTEM_VPD_ID VPD_ID('S','V') #define SLCA_SAI_INDICATOR_ID VPD_ID('S','A') __be16 parent_index; /* Parent entry index */ uint8_t flags; #define SLCA_FLAG_NON_FUNCTIONAL 0x02 /* For redundant entries */ #define SLCA_FLAG_IMBEDDED 0x01 /* not set => pluggable */ uint8_t old_nr_child; /* Legacy: Nr of children */ __be16 child_index; /* First child index */ __be16 child_rsrc_id; /* Resource ID of first child */ uint8_t loc_code_allen; /* Alloc len of loc code */ uint8_t loc_code_len; /* Loc code len */ uint8_t loc_code[LOC_CODE_SIZE]; /* NULL terminated (thus max 79 chr) */ __be16 first_dup_idx; /* First redundant resource index */ uint8_t nr_dups; /* Number of redundant entries */ uint8_t reserved; __be16 nr_child; /* New version */ uint8_t install_indic; /* Installed indicator */ #define SLCA_INSTALL_NO_HW_PDT 1 /* No HW presence detect */ #define SLCA_INSTALL_INSTALLED 2 #define SLCA_INSTALL_NOT_INSTALLED 3 uint8_t vpd_collected; #define SLCA_VPD_COLLECTED 2 #define SLCA_VPD_NOT_COLLECTED 3 } __packed; /* * System VPD */ #define SYSVPD_HDIF_SIG "SYSVPD" /* Idata index 0 : FRU ID Data */ #define SYSVPD_IDATA_FRU_ID 0 /* Idata index 1 : Keyword VPD */ #define SYSVPD_IDATA_KW_VPD 1 /* Idata index 2 : Operational status */ #define SYSVPD_IDATA_OP_STATUS 2 /* * FRU keyword VPD structure */ #define FRUVPD_HDIF_SIG "FRUVPD" /* Idata index 0 : FRU ID Data */ #define FRUVPD_IDATA_FRU_ID 0 /* Idata index 1 : Keyword VPD */ #define FRUVPD_IDATA_KW_VPD 1 /* Idata index 2 : Operational status */ #define FRUVPD_IDATA_OP_STATUS 2 /* * SPPACA structure. The SPIRA contain an array of these, one * per processor thread */ #define PACA_HDIF_SIG "SPPACA" /* Idata index 0 : FRU ID Data */ #define SPPACA_IDATA_FRU_ID 0 /* Idata index 1 : Keyword VPD */ #define SPPACA_IDATA_KW_VPD 1 /* Idata index 2 : CPU ID data area */ #define SPPACA_IDATA_CPU_ID 2 struct sppaca_cpu_id { __be32 pir; __be32 fru_id; __be32 hardware_proc_id; #define CPU_ID_VERIFY_MASK 0xC0000000 #define CPU_ID_VERIFY_SHIFT 30 #define CPU_ID_VERIFY_USABLE_NO_FAILURES 0 #define CPU_ID_VERIFY_USABLE_FAILURES 1 #define CPU_ID_VERIFY_NOT_INSTALLED 2 #define CPU_ID_VERIFY_UNUSABLE 3 #define CPU_ID_SECONDARY_THREAD 0x20000000 #define CPU_ID_PACA_RESERVED 0x10000000 #define CPU_ID_NUM_SECONDARY_THREAD_MASK 0x00FF0000 #define CPU_ID_NUM_SECONDARY_THREAD_SHIFT 16 __be32 verify_exists_flags; __be32 chip_ec_level; __be32 processor_chip_id; __be32 logical_processor_id; /* This is the resource number, too. */ __be32 process_interrupt_line; __be32 reserved1; __be32 hardware_module_id; __be64 ibase; __be32 deprecated1; __be32 physical_thread_id; __be32 deprecated2; __be32 ccm_node_id; /* This fields are not always present, check struct size */ #define SPIRA_CPU_ID_MIN_SIZE 0x40 __be32 hw_card_id; __be32 internal_drawer_node_id; __be32 drawer_book_octant_blade_id; __be32 memory_interleaving_scope; __be32 lco_target; } __packed; /* Idata index 3 : Timebase data */ #define SPPACA_IDATA_TIMEBASE 3 struct sppaca_cpu_timebase { __be32 cycle_time; __be32 time_base; __be32 actual_clock_speed; __be32 memory_bus_frequency; } __packed; /* Idata index 4 : Cache size structure */ #define SPPACA_IDATA_CACHE_SIZE 4 struct sppaca_cpu_cache { __be32 icache_size_kb; __be32 icache_line_size; __be32 l1_dcache_size_kb; __be32 l1_dcache_line_size; __be32 l2_dcache_size_kb; __be32 l2_line_size; __be32 l3_dcache_size_kb; __be32 l3_line_size; __be32 dcache_block_size; __be32 icache_block_size; __be32 dcache_assoc_sets; __be32 icache_assoc_sets; __be32 dtlb_entries; __be32 dtlb_assoc_sets; __be32 itlb_entries; __be32 itlb_assoc_sets; __be32 reservation_size; __be32 l2_cache_assoc_sets; __be32 l35_dcache_size_kb; __be32 l35_cache_line_size; }; /* Idata index 6 : CPU Attributes */ #define SPPACA_IDATA_CPU_ATTR 6 #define sppaca_cpu_attr sppcia_cpu_attr /* * SPPCIA structure. The SPIRA contain an array of these, one * per processor core */ #define SPPCIA_HDIF_SIG "SPPCIA" /* Idata index 0 : Core unique data */ #define SPPCIA_IDATA_CORE_UNIQUE 0 /* NOTE: This is the same layout as "struct sppaca_cpu_id", * with essentially some fields removed and a reserved * field added */ struct sppcia_core_unique { __be32 reserved; __be32 proc_fru_id; __be32 hw_proc_id; __be32 verif_exist_flags; /* Same as PACA */ __be32 chip_ec_level; __be32 proc_chip_id; __be32 reserved2; __be32 reserved3; __be32 reserved4; __be32 hw_module_id; __be64 reserved5; __be32 reserved6; __be32 reserved7; __be32 reserved8; __be32 ccm_node_id; __be32 hw_card_id; __be32 internal_drawer_node_id; __be32 drawer_book_octant_blade_id; __be32 memory_interleaving_scope; __be32 lco_target; __be32 reserved9; } __packed; /* Idata index 1 : CPU Time base structure */ #define SPPCIA_IDATA_TIMEBASE 1 #define sppcia_cpu_timebase sppaca_cpu_timebase /* Idata index 2 : CPU Cache Size Structure */ #define SPPCIA_IDATA_CPU_CACHE 2 #define sppcia_cpu_cache sppaca_cpu_cache /* Idata index 3 : Thread Array Data * * HDIF array of */ #define SPPCIA_IDATA_THREAD_ARRAY 3 struct sppcia_cpu_thread { __be32 proc_int_line; __be32 phys_thread_id; __be64 ibase; __be32 pir; } __packed; /* Idata index 4 : CPU Attributes */ #define SPPCIA_IDATA_CPU_ATTR 4 struct sppcia_cpu_attr { #define CPU_ATTR_UNIFIED_PL1 0x80 #define CPU_ATTR_SPLIT_TLB 0x40 #define CPU_ATTR_TLBIA 0x20 #define CPU_ATTR_PERF_MONITOR 0x10 #define CPU_ATTR_EXTERN_CONT 0x02 __be32 attr; } __packed; /* * Processor Chip Related Data. The SPIRA contain an array of these, one * per chip */ #define SPPCRD_HDIF_SIG "SPPCRD" /* Idata index 0 : Chip info */ #define SPPCRD_IDATA_CHIP_INFO 0 struct sppcrd_chip_info { __be32 proc_chip_id; __be32 verif_exist_flags; #define CHIP_VERIFY_MASK 0xC0000000 #define CHIP_VERIFY_SHIFT 30 #define CHIP_VERIFY_USABLE_NO_FAILURES 0 #define CHIP_VERIFY_USABLE_FAILURES 1 #define CHIP_VERIFY_NOT_INSTALLED 2 #define CHIP_VERIFY_UNUSABLE 3 __be32 nx_state; __be32 pore_state; __be32 xscom_id; /* Version 0xA */ __be32 reserved; __be32 dbob_id; __be32 occ_state; } __packed; /* Idata index 1 : Chip TOD */ #define SPPCRD_IDATA_CHIP_TOD 1 struct sppcrd_chip_tod { __be32 flags; /* CHIPTOD_ID_... values */ __be32 ctrl_reg_internal; __be32 tod_ctrl_reg; } __packed; /* Idata index 2 : FRU ID */ #define SPPCRD_IDATA_FRU_ID 2 /* Idata index 3 : ASCII Keyword data */ #define SPPCRD_IDATA_KW_VPD 3 /* Idata index 4 : Module VPD */ #define SPPCRD_IDATA_MODULE_VPD 4 /* * Host Services Data. */ #define HSERV_HDIF_SIG "HOSTSR" /* Idata index 0 : System attribute data */ #define HSERV_IDATA_SYS_ATTR 0 static inline const char *cpu_state(u32 flags) { switch ((flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) { case CPU_ID_VERIFY_USABLE_NO_FAILURES: return "OK"; case CPU_ID_VERIFY_USABLE_FAILURES: return "FAILURES"; case CPU_ID_VERIFY_NOT_INSTALLED: return "NOT-INSTALLED"; case CPU_ID_VERIFY_UNUSABLE: return "UNUSABLE"; } return "**UNKNOWN**"; } #endif /* __SPIRA_H */ skiboot-skiboot-5.1.13/hdata/test/000077500000000000000000000000001265204436200167675ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hdata/test/Makefile.check000066400000000000000000000033511265204436200215050ustar00rootroot00000000000000# -*-Makefile-*- HDATA_TEST := hdata/test/hdata_to_dt check: $(HDATA_TEST:%=%-check) $(HDATA_TEST:%=%-gcov-run) coverage: $(HDATA_TEST:%=%-gcov-run) LCOV_EXCLUDE += $(HDATA_TEST:%=%.c) hdata/test/stubs.c LCOV_EXCLUDE += /usr/include/valgrind/memcheck.h hdata/test/hdata_to_dt-check: hdata/test/hdata_to_dt-check-q hdata/test/hdata_to_dt-check-dt # Add some test ntuples for open source version... hdata/test/hdata_to_dt-check-q: hdata/test/hdata_to_dt $(call Q, TEST , $(VALGRIND) hdata/test/hdata_to_dt -q hdata/test/p81-811.spira hdata/test/p81-811.spira.heap, $<) hdata/test/hdata_to_dt-check-dt: hdata/test/hdata_to_dt $(call Q, TEST , $(VALGRIND) hdata/test/hdata_to_dt hdata/test/p81-811.spira hdata/test/p81-811.spira.heap |diff -u hdata/test/p81-811.spira.dt -, $< device-tree) hdata/test/hdata_to_dt-gcov-run: hdata/test/hdata_to_dt-check-dt-gcov-run hdata/test/hdata_to_dt-check-dt-gcov-run: hdata/test/hdata_to_dt-gcov $(call Q, TEST-COVERAGE , ./hdata/test/hdata_to_dt-gcov hdata/test/p81-811.spira hdata/test/p81-811.spira.heap |diff -u hdata/test/p81-811.spira.dt -, $< device-tree) hdata/test/stubs.o: hdata/test/stubs.c $(call Q, HOSTCC , $(HOSTCC) $(HOSTCFLAGS) -g -c -o $@ $<, $<) $(HDATA_TEST) : hdata/test/stubs.o $(CORE_TEST:%=%-gcov): hdata/test/stubs.o $(HDATA_TEST) : % : %.c $(call Q, HOSTCC , $(HOSTCC) $(HOSTCFLAGS) -DTEST -O0 -g -I hdata -I include -I . -I libfdt -o $@ $< hdata/test/stubs.o, $<) $(HDATA_TEST:%=%-gcov): %-gcov : %.c % $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -DTEST -O0 -g -I include -I . -I data -I libfdt -lgcov -o $@ $< hdata/test/stubs.o, $<) -include $(wildcard hdata/test/*.d) clean: hdata-test-clean hdata-test-clean: $(RM) hdata/test/*.o hdata/test/hdata_to_dt skiboot-skiboot-5.1.13/hdata/test/hdata_to_dt.c000066400000000000000000000110341265204436200214040ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Given a hdata dump, output the device tree. */ #include #include #include #include #include #include #include #include #include #include #include "../../libfdt/fdt.c" #include "../../libfdt/fdt_ro.c" struct dt_node *opal_node; /* Our actual map. */ static void *spira_heap; static off_t spira_heap_size; static uint64_t base_addr; /* Override ntuple_addr. */ #define ntuple_addr ntuple_addr struct spira_ntuple; static void *ntuple_addr(const struct spira_ntuple *n); /* Stuff which core expects. */ #define __this_cpu ((struct cpu_thread *)NULL) #define zalloc(expr) calloc(1, (expr)) /* Don't include processor-specific stuff. */ #define __PROCESSOR_H #define PVR_TYPE(_pvr) _pvr /* PVR definitions */ #define PVR_TYPE_P7 0x003f #define PVR_TYPE_P7P 0x004a #define PVR_TYPE_P8E 0x004b #define PVR_TYPE_P8 0x004d #define PVR_TYPE_P8NVL 0x004c #define SPR_PVR 0x11f /* RO: Processor version register */ #define __CPU_H struct cpu_thread { uint32_t pir; }; struct cpu_thread __boot_cpu, *boot_cpu = &__boot_cpu; static unsigned long fake_pvr_type = PVR_TYPE_P7; static inline unsigned long mfspr(unsigned int spr) { assert(spr == SPR_PVR); return fake_pvr_type; } struct dt_node *add_ics_node(void) { return NULL; } #include #include /* Your pointers won't be correct, that's OK. */ #define spira_check_ptr(ptr, file, line) ((ptr) != NULL) #include "../cpu-common.c" #include "../fsp.c" #include "../hdif.c" #include "../iohub.c" #include "../memory.c" #include "../paca.c" #include "../pcia.c" #include "../spira.c" #include "../vpd.c" #include "../vpd-common.c" #include "../slca.c" #include "../hostservices.c" #include "../../core/vpd.c" #include "../../core/device.c" #include "../../core/chip.c" #include "../../test/dt_common.c" #include char __rodata_start[1], __rodata_end[1]; enum proc_gen proc_gen = proc_gen_p7; static void *ntuple_addr(const struct spira_ntuple *n) { uint64_t addr = be64_to_cpu(n->addr); if (n->addr == 0) return NULL; assert(addr >= base_addr); assert(addr < base_addr + spira_heap_size); return spira_heap + ((unsigned long)addr - base_addr); } /* Make sure valgrind knows these are undefined bytes. */ static void undefined_bytes(void *p, size_t len) { VALGRIND_MAKE_MEM_UNDEFINED(p, len); } int main(int argc, char *argv[]) { int fd, r; bool verbose = false, quiet = false, tree_only = false; while (argv[1]) { if (strcmp(argv[1], "-v") == 0) { verbose = true; argv++; argc--; } else if (strcmp(argv[1], "-q") == 0) { quiet = true; argv++; argc--; } else if (strcmp(argv[1], "-t") == 0) { tree_only = true; argv++; argc--; } else break; } if (argc != 3) errx(1, "Usage: hdata [-v|-q|-t] "); /* Copy in spira dump (assumes little has changed!). */ fd = open(argv[1], O_RDONLY); if (fd < 0) err(1, "opening %s", argv[1]); r = read(fd, &spira, sizeof(spira)); if (r < sizeof(spira.hdr)) err(1, "reading %s gave %i", argv[1], r); if (verbose) printf("verbose: read spira %u bytes\n", r); close(fd); undefined_bytes((void *)&spira + r, sizeof(spira) - r); base_addr = be64_to_cpu(spira.ntuples.heap.addr); if (!base_addr) errx(1, "Invalid base addr"); if (verbose) printf("verbose: map.base_addr = %llx\n", (long long)base_addr); fd = open(argv[2], O_RDONLY); if (fd < 0) err(1, "opening %s", argv[2]); spira_heap_size = lseek(fd, 0, SEEK_END); if (spira_heap_size < 0) err(1, "lseek on %s", argv[2]); spira_heap = mmap(NULL, spira_heap_size, PROT_READ, MAP_SHARED, fd, 0); if (spira_heap == MAP_FAILED) err(1, "mmaping %s", argv[3]); if (verbose) printf("verbose: mapped %zu at %p\n", spira_heap_size, spira_heap); close(fd); if (quiet) { fclose(stdout); fclose(stderr); } parse_hdat(false, 0); if (!quiet) dump_dt(dt_root, 0, !tree_only); dt_free(dt_root); return 0; } skiboot-skiboot-5.1.13/hdata/test/p81-811.spira000066400000000000000000000040001265204436200207400ustar00rootroot00000000000000ÑðSPIRA @ 0 1 €@1 €à1 €€P1 ð1 $1 %@1 &€ 1 '€Ð1 (€Ð1 )€€ 1 8 € 00 1 €$›€1 K1 s €€1@£ø€ø€skiboot-skiboot-5.1.13/hdata/test/p81-811.spira.dt000066400000000000000000024077671265204436200214030ustar00rootroot00000000000000Got PCIA ! CORE[0]: HW_PROC_ID=0 PROC_CHIP_ID=0 EC=0x21 OK CORE[0]: PIR=536870912 RES=536870912 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[1]: HW_PROC_ID=2 PROC_CHIP_ID=0 EC=0x21 OK CORE[1]: PIR=805306368 RES=805306368 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[2]: HW_PROC_ID=3 PROC_CHIP_ID=0 EC=0x21 OK CORE[2]: PIR=1610612736 RES=1610612736 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[3]: HW_PROC_ID=4 PROC_CHIP_ID=0 EC=0x21 OK CORE[3]: PIR=1744830464 RES=1744830464 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[4]: HW_PROC_ID=5 PROC_CHIP_ID=0 EC=0x21 OK CORE[4]: PIR=1879048192 RES=1879048192 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[5]: HW_PROC_ID=7 PROC_CHIP_ID=1 EC=0x21 OK CORE[5]: PIR=-1476395008 RES=-1476395008 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[6]: HW_PROC_ID=8 PROC_CHIP_ID=1 EC=0x21 OK CORE[6]: PIR=-1342177280 RES=-1342177280 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[7]: HW_PROC_ID=9 PROC_CHIP_ID=1 EC=0x21 OK CORE[7]: PIR=-536870912 RES=-536870912 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[8]: HW_PROC_ID=10 PROC_CHIP_ID=1 EC=0x21 OK CORE[8]: PIR=-402653184 RES=-402653184 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[9]: HW_PROC_ID=11 PROC_CHIP_ID=1 EC=0x21 OK CORE[9]: PIR=-268435456 RES=-268435456 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[10]: HW_PROC_ID=13 PROC_CHIP_ID=2 EC=0x21 OK CORE[10]: PIR=671612928 RES=671612928 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[11]: HW_PROC_ID=14 PROC_CHIP_ID=2 EC=0x21 OK CORE[11]: PIR=805830656 RES=805830656 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[12]: HW_PROC_ID=15 PROC_CHIP_ID=2 EC=0x21 OK CORE[12]: PIR=1611137024 RES=1611137024 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[13]: HW_PROC_ID=16 PROC_CHIP_ID=2 EC=0x21 OK CORE[13]: PIR=1745354752 RES=1745354752 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[14]: HW_PROC_ID=17 PROC_CHIP_ID=2 EC=0x21 OK CORE[14]: PIR=1879572480 RES=1879572480 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[15]: HW_PROC_ID=18 PROC_CHIP_ID=3 EC=0x21 OK CORE[15]: PIR=-1610088448 RES=-1610088448 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[16]: HW_PROC_ID=19 PROC_CHIP_ID=3 EC=0x21 OK CORE[16]: PIR=-1475870720 RES=-1475870720 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[17]: HW_PROC_ID=20 PROC_CHIP_ID=3 EC=0x21 OK CORE[17]: PIR=-1341652992 RES=-1341652992 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[18]: HW_PROC_ID=22 PROC_CHIP_ID=3 EC=0x21 OK CORE[18]: PIR=-402128896 RES=-402128896 OK (8 threads) Cache: I=32 D=64/512/8192/0 CORE[19]: HW_PROC_ID=23 PROC_CHIP_ID=3 EC=0x21 OK CORE[19]: PIR=-267911168 RES=-267911168 OK (8 threads) Cache: I=32 D=64/512/8192/0 IPLPARAMS: 1 serial ports in array IPLPARAMS: Serial 0 rsrc: 2a00 loc: U78CB.001.WZS00AL-P1-C1-T1 MS VPD: Total MB of RAM: 0x20000 XSCOM: Found HW ID 0x0 (PCID 0x0) @ 0x3c0000000000 VPD: CCIN desc not available for : 54E8 XSCOM: Found HW ID 0x1 (PCID 0x1) @ 0x3c0800000000 VPD: CCIN desc not available for : 54E8 XSCOM: Found HW ID 0x10 (PCID 0x2) @ 0x3c8000000000 VPD: CCIN desc not available for : 54E8 XSCOM: Found HW ID 0x11 (PCID 0x3) @ 0x3c8800000000 VPD: CCIN desc not available for : 54E8 FSP #0: FSP HW version 2, SW version 1, chip DD1.0 CEC: HUB FRU 0 is CPU Card CEC: 2 chips in FRU CEC: Murano ! CEC: HW CHIP=0x0, HW TOPO=0x0000 CEC: Murano ! CEC: HW CHIP=0x1, HW TOPO=0x0010 CEC: HUB FRU 1 is CPU Card CEC: 2 chips in FRU CEC: Murano ! CEC: HW CHIP=0x10, HW TOPO=0x0100 CEC: Murano ! CEC: HW CHIP=0x11, HW TOPO=0x0110 VPD: CCIN desc not available for : 2B08 Parsing HDAT...done node: prop: #address-cells size: 4 val: 00000002 prop: #size-cells size: 4 val: 00000002 prop: lid-type size: 5 val: 7068797000 prop: compatible size: 24 val: 69626d2c706f7765726e760069626d2c666972656e7a6500 prop: skiboot,maxmem size: 8 val: 8000001fffffffff prop: model size: 9 val: 383234372d32324c00 prop: model-name size: 23 val: 49424d20506f7765722053797374656d20533832324c00 prop: vendor size: 4 val: 49424d00 prop: system-id size: 8 val: 3130313043384100 prop: system-brand size: 3 val: 533000 prop: ibm,hbrt-mini-fdt size: 4096 val: d00dfeed0000026e0000012800000214000000280000001100000010000000000000005a000000 ec0000001ffd70000000000000001000000000001ffd6bd00000000000000430000000001ffd5860 00000000000013700000000101c984fc3000000101c984fdd00000001ffd7000000000001ffd7000 0000000000001000000000001ffd6bd0000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000010000000000000003000000300000004a000000 1ffd70000000000000001000000000001ffd6bd00000000000000430000000001ffd586000000000 0000137000000000030000003d0000003b69626d2c686272742d7670642d696d6167650069626d2c 686272742d7461726765742d696d6167650069626d2c686272742d636f64652d696d616765000000 000000000300000018000000236e617000666173742d736c65657000727677696e6b6c6500000000 03000000040000001b1000000000000003000000040000000f000000020000000300000004000000 0000000002000000020000000923616464726573732d63656c6c73002373697a652d63656c6c7300 7068616e646c650069626d2c656e61626c65642d69646c652d737461746573007265736572766564 2d6e616d65730072657365727665642d72616e676573000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000 prop: reserved-ranges size: 48 val: 0000001ffd70000000000000001000000000001ffd6bd00000000000000430000000001ffd5860 000000000000137000 prop: reserved-names size: 61 val: 69626d2c686272742d7670642d696d6167650069626d2c686272742d7461726765742d696d6167 650069626d2c686272742d636f64652d696d61676500 prop: ibm,enabled-idle-states size: 24 val: 6e617000666173742d736c65657000727677696e6b6c6500 node: cpus prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000000 node: PowerPC,POWER7@20 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000020 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000007 prop: ibm,pir size: 4 val: 00000020 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000002000000021000000220000002300000024000000250000002600000027 node: PowerPC,POWER7@30 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000030 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000000b prop: ibm,pir size: 4 val: 00000030 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000003000000031000000320000003300000034000000350000003600000037 node: PowerPC,POWER7@60 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000060 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000000f prop: ibm,pir size: 4 val: 00000060 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000006000000061000000620000006300000064000000650000006600000067 node: PowerPC,POWER7@68 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000068 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000013 prop: ibm,pir size: 4 val: 00000068 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,ppc-interrupt-server#s size: 32 val: 00000068000000690000006a0000006b0000006c0000006d0000006e0000006f node: PowerPC,POWER7@70 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000070 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000017 prop: ibm,pir size: 4 val: 00000070 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000007000000071000000720000007300000074000000750000007600000077 node: PowerPC,POWER7@a8 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000000a8 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000001b prop: ibm,pir size: 4 val: 000000a8 prop: ibm,chip-id size: 4 val: 00000001 prop: ibm,ppc-interrupt-server#s size: 32 val: 000000a8000000a9000000aa000000ab000000ac000000ad000000ae000000af node: PowerPC,POWER7@b0 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000000b0 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000001f prop: ibm,pir size: 4 val: 000000b0 prop: ibm,chip-id size: 4 val: 00000001 prop: ibm,ppc-interrupt-server#s size: 32 val: 000000b0000000b1000000b2000000b3000000b4000000b5000000b6000000b7 node: PowerPC,POWER7@e0 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000000e0 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000023 prop: ibm,pir size: 4 val: 000000e0 prop: ibm,chip-id size: 4 val: 00000001 prop: ibm,ppc-interrupt-server#s size: 32 val: 000000e0000000e1000000e2000000e3000000e4000000e5000000e6000000e7 node: PowerPC,POWER7@e8 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000000e8 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000027 prop: ibm,pir size: 4 val: 000000e8 prop: ibm,chip-id size: 4 val: 00000001 prop: ibm,ppc-interrupt-server#s size: 32 val: 000000e8000000e9000000ea000000eb000000ec000000ed000000ee000000ef node: PowerPC,POWER7@f0 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000000f0 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000002b prop: ibm,pir size: 4 val: 000000f0 prop: ibm,chip-id size: 4 val: 00000001 prop: ibm,ppc-interrupt-server#s size: 32 val: 000000f0000000f1000000f2000000f3000000f4000000f5000000f6000000f7 node: PowerPC,POWER7@828 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000828 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000002f prop: ibm,pir size: 4 val: 00000828 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,ppc-interrupt-server#s size: 32 val: 00000828000008290000082a0000082b0000082c0000082d0000082e0000082f node: PowerPC,POWER7@830 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000830 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000033 prop: ibm,pir size: 4 val: 00000830 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000083000000831000008320000083300000834000008350000083600000837 node: PowerPC,POWER7@860 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000860 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000037 prop: ibm,pir size: 4 val: 00000860 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000086000000861000008620000086300000864000008650000086600000867 node: PowerPC,POWER7@868 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000868 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000003b prop: ibm,pir size: 4 val: 00000868 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,ppc-interrupt-server#s size: 32 val: 00000868000008690000086a0000086b0000086c0000086d0000086e0000086f node: PowerPC,POWER7@870 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 00000870 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000003f prop: ibm,pir size: 4 val: 00000870 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,ppc-interrupt-server#s size: 32 val: 0000087000000871000008720000087300000874000008750000087600000877 node: PowerPC,POWER7@8a0 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000008a0 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000043 prop: ibm,pir size: 4 val: 000008a0 prop: ibm,chip-id size: 4 val: 00000011 prop: ibm,ppc-interrupt-server#s size: 32 val: 000008a0000008a1000008a2000008a3000008a4000008a5000008a6000008a7 node: PowerPC,POWER7@8a8 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000008a8 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000047 prop: ibm,pir size: 4 val: 000008a8 prop: ibm,chip-id size: 4 val: 00000011 prop: ibm,ppc-interrupt-server#s size: 32 val: 000008a8000008a9000008aa000008ab000008ac000008ad000008ae000008af node: PowerPC,POWER7@8b0 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000008b0 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000004b prop: ibm,pir size: 4 val: 000008b0 prop: ibm,chip-id size: 4 val: 00000011 prop: ibm,ppc-interrupt-server#s size: 32 val: 000008b0000008b1000008b2000008b3000008b4000008b5000008b6000008b7 node: PowerPC,POWER7@8e8 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000008e8 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 0000004f prop: ibm,pir size: 4 val: 000008e8 prop: ibm,chip-id size: 4 val: 00000011 prop: ibm,ppc-interrupt-server#s size: 32 val: 000008e8000008e9000008ea000008eb000008ec000008ed000008ee000008ef node: PowerPC,POWER7@8f0 prop: device_type size: 4 val: 63707500 prop: status size: 5 val: 6f6b617900 prop: reg size: 4 val: 000008f0 prop: cpu-version size: 4 val: 0000003f prop: 64-bit size: 0 val: prop: 32-64-bridge size: 0 val: prop: graphics size: 0 val: prop: general-purpose size: 0 val: prop: ibm,processor-segment-sizes size: 16 val: 0000001c00000028ffffffffffffffff prop: ibm,processor-page-sizes size: 16 val: 0000000c000000100000001800000022 prop: ibm,segment-page-sizes size: 104 val: 0000000c00000000000000030000000c000000000000001000000007000000180000003800 0000100000011000000002000000100000000100000018000000080000001800000100000000 0100000018000000000000002200000120000000010000002200000003 prop: ibm,pa-features size: 8 val: 0600f63fc70080c0 prop: ibm,slb-size size: 4 val: 00000020 prop: ibm,vmx size: 4 val: 00000002 prop: ibm,dfp size: 4 val: 00000002 prop: ibm,purr size: 4 val: 00000001 prop: ibm,spurr size: 4 val: 00000001 prop: clock-frequency size: 4 val: cc255a40 prop: ibm,extended-clock-frequency size: 8 val: 00000000cc255a40 prop: timebase-frequency size: 4 val: 1e848000 prop: ibm,extended-timebase-frequency size: 8 val: 000000001e848000 prop: reservation-granule-size size: 4 val: 00000080 prop: d-tlb-size size: 4 val: 00000800 prop: i-tlb-size size: 4 val: 00000000 prop: tlb-size size: 4 val: 00000800 prop: d-tlb-sets size: 4 val: 00000004 prop: i-tlb-sets size: 4 val: 00000000 prop: tlb-sets size: 4 val: 00000004 prop: d-cache-block-size size: 4 val: 00000080 prop: i-cache-block-size size: 4 val: 00000080 prop: d-cache-size size: 4 val: 00010000 prop: i-cache-size size: 4 val: 00008000 prop: i-cache-sets size: 4 val: 00000004 prop: d-cache-sets size: 4 val: 00000008 prop: performance-monitor size: 8 val: 0000000000000001 prop: l2-cache size: 4 val: 00000053 prop: ibm,pir size: 4 val: 000008f0 prop: ibm,chip-id size: 4 val: 00000011 prop: ibm,ppc-interrupt-server#s size: 32 val: 000008f0000008f1000008f2000008f3000008f4000008f5000008f6000008f7 node: l2-cache@20000020 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000020 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000008 node: l2-cache@20000030 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000030 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 0000000c node: l2-cache@20000060 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000060 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000010 node: l2-cache@20000068 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000068 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000014 node: l2-cache@20000070 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000070 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000018 node: l2-cache@200000a8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200000a8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 0000001c node: l2-cache@200000b0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200000b0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000020 node: l2-cache@200000e0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200000e0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000024 node: l2-cache@200000e8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200000e8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000028 node: l2-cache@200000f0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200000f0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 0000002c node: l2-cache@20000828 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000828 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000030 node: l2-cache@20000830 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000830 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000034 node: l2-cache@20000860 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000860 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000038 node: l2-cache@20000868 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000868 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 0000003c node: l2-cache@20000870 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 20000870 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000040 node: l2-cache@200008a0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200008a0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000044 node: l2-cache@200008a8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200008a8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000048 node: l2-cache@200008b0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200008b0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 0000004c node: l2-cache@200008e8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200008e8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000050 node: l2-cache@200008f0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 200008f0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00080000 prop: i-cache-size size: 4 val: 00080000 prop: l2-cache size: 4 val: 00000054 node: l3-cache@30000020 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000020 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000030 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000030 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000060 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000060 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000068 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000068 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000070 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000070 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300000a8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300000a8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300000b0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300000b0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300000e0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300000e0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300000e8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300000e8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300000f0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300000f0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000828 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000828 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000830 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000830 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000860 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000860 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000868 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000868 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@30000870 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 30000870 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300008a0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300008a0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300008a8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300008a8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300008b0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300008b0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300008e8 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300008e8 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: l3-cache@300008f0 prop: device_type size: 6 val: 636163686500 prop: reg size: 4 val: 300008f0 prop: status size: 5 val: 6f6b617900 prop: cache-unified size: 0 val: prop: d-cache-sets size: 4 val: 00000008 prop: i-cache-sets size: 4 val: 00000008 prop: d-cache-size size: 4 val: 00800000 prop: i-cache-size size: 4 val: 00800000 node: fsps prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000000 node: fsp@0 prop: reg size: 4 val: 00000000 prop: compatible size: 17 val: 69626d2c6673700069626d2c6673703200 prop: reg-offset size: 4 val: b0011000 prop: hw-version size: 4 val: 00000002 prop: sw-version size: 4 val: 00000001 prop: primary size: 0 val: prop: ibm,psi-links size: 8 val: 0000000010000000 node: ibm,opal node: leds prop: led-mode size: 10 val: 6c696768747061746800 node: U8247.22L.1010C8A prop: led-types size: 10 val: 617474656e74696f6e00 node: interrupt-controller@3ffff80020000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000002000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8002000000000000000010000003ffff8002100000000000000010000003ffff8002 200000000000000010000003ffff8002300000000000000010000003ffff800240000000000000 0010000003ffff8002500000000000000010000003ffff8002600000000000000010000003ffff 800270000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80030000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000003000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8003000000000000000010000003ffff8003100000000000000010000003ffff8003 200000000000000010000003ffff8003300000000000000010000003ffff800340000000000000 0010000003ffff8003500000000000000010000003ffff8003600000000000000010000003ffff 800370000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80060000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000006000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8006000000000000000010000003ffff8006100000000000000010000003ffff8006 200000000000000010000003ffff8006300000000000000010000003ffff800640000000000000 0010000003ffff8006500000000000000010000003ffff8006600000000000000010000003ffff 800670000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80068000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000006800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8006800000000000000010000003ffff8006900000000000000010000003ffff8006 a00000000000000010000003ffff8006b00000000000000010000003ffff8006c0000000000000 0010000003ffff8006d00000000000000010000003ffff8006e00000000000000010000003ffff 8006f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80070000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000007000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8007000000000000000010000003ffff8007100000000000000010000003ffff8007 200000000000000010000003ffff8007300000000000000010000003ffff800740000000000000 0010000003ffff8007500000000000000010000003ffff8007600000000000000010000003ffff 800770000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80128000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000000a800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8012800000000000000010000003ffff8012900000000000000010000003ffff8012 a00000000000000010000003ffff8012b00000000000000010000003ffff8012c0000000000000 0010000003ffff8012d00000000000000010000003ffff8012e00000000000000010000003ffff 8012f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80130000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000000b000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8013000000000000000010000003ffff8013100000000000000010000003ffff8013 200000000000000010000003ffff8013300000000000000010000003ffff801340000000000000 0010000003ffff8013500000000000000010000003ffff8013600000000000000010000003ffff 801370000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80160000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000000e000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8016000000000000000010000003ffff8016100000000000000010000003ffff8016 200000000000000010000003ffff8016300000000000000010000003ffff801640000000000000 0010000003ffff8016500000000000000010000003ffff8016600000000000000010000003ffff 801670000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80168000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000000e800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8016800000000000000010000003ffff8016900000000000000010000003ffff8016 a00000000000000010000003ffff8016b00000000000000010000003ffff8016c0000000000000 0010000003ffff8016d00000000000000010000003ffff8016e00000000000000010000003ffff 8016f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80170000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000000f000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8017000000000000000010000003ffff8017100000000000000010000003ffff8017 200000000000000010000003ffff8017300000000000000010000003ffff801740000000000000 0010000003ffff8017500000000000000010000003ffff8017600000000000000010000003ffff 801770000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80828000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000082800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8082800000000000000010000003ffff8082900000000000000010000003ffff8082 a00000000000000010000003ffff8082b00000000000000010000003ffff8082c0000000000000 0010000003ffff8082d00000000000000010000003ffff8082e00000000000000010000003ffff 8082f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80830000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000083000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8083000000000000000010000003ffff8083100000000000000010000003ffff8083 200000000000000010000003ffff8083300000000000000010000003ffff808340000000000000 0010000003ffff8083500000000000000010000003ffff8083600000000000000010000003ffff 808370000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80860000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000086000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8086000000000000000010000003ffff8086100000000000000010000003ffff8086 200000000000000010000003ffff8086300000000000000010000003ffff808640000000000000 0010000003ffff8086500000000000000010000003ffff8086600000000000000010000003ffff 808670000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80868000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000086800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8086800000000000000010000003ffff8086900000000000000010000003ffff8086 a00000000000000010000003ffff8086b00000000000000010000003ffff8086c0000000000000 0010000003ffff8086d00000000000000010000003ffff8086e00000000000000010000003ffff 8086f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80870000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 0000087000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8087000000000000000010000003ffff8087100000000000000010000003ffff8087 200000000000000010000003ffff8087300000000000000010000003ffff808740000000000000 0010000003ffff8087500000000000000010000003ffff8087600000000000000010000003ffff 808770000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80920000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000008a000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8092000000000000000010000003ffff8092100000000000000010000003ffff8092 200000000000000010000003ffff8092300000000000000010000003ffff809240000000000000 0010000003ffff8092500000000000000010000003ffff8092600000000000000010000003ffff 809270000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80928000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000008a800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8092800000000000000010000003ffff8092900000000000000010000003ffff8092 a00000000000000010000003ffff8092b00000000000000010000003ffff8092c0000000000000 0010000003ffff8092d00000000000000010000003ffff8092e00000000000000010000003ffff 8092f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80930000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000008b000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8093000000000000000010000003ffff8093100000000000000010000003ffff8093 200000000000000010000003ffff8093300000000000000010000003ffff809340000000000000 0010000003ffff8093500000000000000010000003ffff8093600000000000000010000003ffff 809370000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80968000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000008e800000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8096800000000000000010000003ffff8096900000000000000010000003ffff8096 a00000000000000010000003ffff8096b00000000000000010000003ffff8096c0000000000000 0010000003ffff8096d00000000000000010000003ffff8096e00000000000000010000003ffff 8096f0000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: interrupt-controller@3ffff80970000 prop: compatible size: 28 val: 69626d2c7070632d786963700049424d2c706f776572372d69637000 prop: ibm,interrupt-server-ranges size: 8 val: 000008f000000008 prop: interrupt-controller size: 0 val: prop: reg size: 128 val: 0003ffff8097000000000000000010000003ffff8097100000000000000010000003ffff8097 200000000000000010000003ffff8097300000000000000010000003ffff809740000000000000 0010000003ffff8097500000000000000010000003ffff8097600000000000000010000003ffff 809770000000000000001000 prop: #address-cells size: 4 val: 00000000 prop: #interrupt-cells size: 4 val: 00000001 prop: device_type size: 40 val: 506f77657250432d45787465726e616c2d496e746572727570742d50726573656e746174696f 6e00 node: ipl-params prop: #address-cells size: 4 val: 00000000 prop: #size-cells size: 4 val: 00000000 node: fsp-serial prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000000 node: serial@2a00 prop: reg size: 4 val: 00002a00 prop: ibm,loc-code size: 27 val: 55373843422e3030312e575a533030414c2d50312d43312d543100 prop: compatible size: 15 val: 69626d2c6673702d73657269616c00 node: ipl-params prop: #address-cells size: 4 val: 00000000 prop: #size-cells size: 4 val: 00000000 prop: cec-ipl-side size: 5 val: 7065726d00 prop: fsp-ipl-side size: 5 val: 7065726d00 prop: os-ipl-mode size: 4 val: 00000000 prop: cec-major-type size: 5 val: 636f6c6400 node: platform-dump node: sys-params prop: #address-cells size: 4 val: 00000000 prop: #size-cells size: 4 val: 00000000 prop: ibm,sys-model size: 5 val: 2032324c00 node: memory@0 prop: device_type size: 7 val: 6d656d6f727900 prop: ibm,chip-id size: 4 val: 00000000 prop: reg size: 16 val: 00000000000000000000001000000000 prop: skiboot,share-id size: 4 val: 00000000 node: memory@1000000000 prop: device_type size: 7 val: 6d656d6f727900 prop: ibm,chip-id size: 4 val: 00000010 prop: reg size: 16 val: 00000010000000000000001000000000 prop: skiboot,share-id size: 4 val: 00000001 node: vpd prop: compatible size: 16 val: 69626d2c6f70616c2d76332d76706400 prop: ibm,vpd size: 184 val: 84b0005254045653595344520653595354454d42520253305345073130313043384153470720 202020202020544d08383234372d32324c544e0820202020202020204d4e072020202020202049 440220205355060004ac1a43544e4e1020202020202020202020202020202020524704f0c00000 52420433202020574e0c43303530373630373830443246562053563831305f3032390000000000 0000000000000000000000000000000000005046030000007800000000 prop: ibm,loc-code size: 18 val: 55383234372e32324c2e3130313043384100 node: root-node-vpd@a000 prop: ibm,loc-code size: 18 val: 55383234372e32324c2e3130313043384100 prop: fru-type size: 2 val: 5656 node: enclosure-fault-led@a300 prop: ibm,loc-code size: 18 val: 55373843422e3030312e575a533030414c00 prop: fru-type size: 2 val: 4546 node: enclosure-led@a200 prop: ibm,loc-code size: 18 val: 55373843422e3030312e575a533030414c00 prop: fru-type size: 2 val: 4549 node: enclosure@1e00 prop: ibm,loc-code size: 18 val: 55373843422e3030312e575a533030414c00 prop: fru-type size: 2 val: 4556 prop: ibm,vpd size: 252 val: 848c0052540456494e49445210492f4f204241434b504c414e4520202043450131565a02 3032464e0730304532303232504e0730304533393937534e0c594c3130554634324c303133 4343043243443650520821000000000000004845043030303243540480f300264857020004 4233060000000000014234010042370c000000000000000000000000504602000078841c00 5254044c585230565a0230314c580831000401003000425046020000788444005254045643 454e445206434543202020534507575a533030414c544d0820202020202020204643083738 43422d30303152470400000000524204202020205046030000007800000000 prop: fru-number size: 8 val: 3030453230323200 prop: serial-number size: 13 val: 594c3130554634324c30313300 prop: part-number size: 8 val: 3030453339393700 prop: ccin size: 5 val: 3243443600 prop: description size: 19 val: 53797374656d20706c616e6172203253325500 node: air-mover@3a00 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d413100 prop: fru-type size: 2 val: 414d node: air-mover@3a01 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d413300 prop: fru-type size: 2 val: 414d node: air-mover@3a02 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d413400 prop: fru-type size: 2 val: 414d node: air-mover@3a03 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d413600 prop: fru-type size: 2 val: 414d node: air-mover@3a04 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d413200 prop: fru-type size: 2 val: 414d node: air-mover@3a05 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d413500 prop: fru-type size: 2 val: 414d node: backplane@800 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d503100 prop: fru-type size: 2 val: 4250 prop: ibm,vpd size: 180 val: 848c0052540456494e49445210492f4f204241434b504c414e4520202043450131565a 023032464e0730304532303232504e0730304533393937534e0c594c3130554634324c30 31334343043243443650520821000000000000004845043030303243540480f300264857 0200044233060000000000014234010042370c0000000000000000000000005046020000 78841c005254044c585230565a0230314c58083100040100300042504602000078000000 00 prop: fru-number size: 8 val: 3030453230323200 prop: serial-number size: 13 val: 594c3130554634324c30313300 prop: part-number size: 8 val: 3030453339393700 prop: ccin size: 5 val: 3243443600 prop: description size: 19 val: 53797374656d20706c616e6172203253325500 node: anchor-card@500 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43313300 prop: fru-type size: 2 val: 4156 prop: ibm,vpd size: 212 val: 84cc0052540456494e49445210414e43484f52202020202020202020204345013156 5a023031464e0730304533343237504e0730304532313438534e0c594c313031313432 523030304343043532344450520881003000000000004845043030313043540440b400 0048570200014233060000000000014234010042370c00000000000000000000000042 393c43534055110351dd6da2535049652f45748f7b044d31c531833edf1e75724d328d 48c1b4246b02ee4d3367b454d8489f3a254d340dadc44124b5edd55046030000007800 000000 prop: fru-number size: 8 val: 3030453334323700 prop: serial-number size: 13 val: 594c3130313134325230303000 prop: part-number size: 8 val: 3030453231343800 prop: ccin size: 5 val: 3532344400 prop: description size: 36 val: 53797374656d20416e63686f722043617264202d2049424d20506f77657220383232 4c00 node: backplane-extender@900 prop: ibm,loc-code size: 24 val: 55373843422e3030312e575a533030414c2d50312d433100 prop: fru-type size: 2 val: 4258 prop: ibm,vpd size: 136 val: 84800052540456494e494452104e415449564520492f4f2043415244204345013156 5a023031464e0730304532313634504e0730304533383131534e0c594c313055463432 4c303031434304324230424845043030303143540480b5000048570200014233060000 000000004234010042370c000000000000000000000000504601007800000000 prop: fru-number size: 8 val: 3030453231363400 prop: serial-number size: 13 val: 594c3130554634324c30303100 prop: part-number size: 8 val: 3030453338313100 prop: ccin size: 5 val: 3242304200 prop: description size: 16 val: 4e617469766520492f4f204361726400 node: serial-connector@2a00 prop: ibm,loc-code size: 27 val: 55373843422e3030312e575a533030414c2d50312d43312d543100 prop: fru-type size: 2 val: 4353 node: usb-connector@2901 prop: ibm,loc-code size: 27 val: 55373843422e3030312e575a533030414c2d50312d43312d543200 prop: fru-type size: 2 val: 4355 node: usb-connector@2904 prop: ibm,loc-code size: 27 val: 55373843422e3030312e575a533030414c2d50312d43312d543300 prop: fru-type size: 2 val: 4355 node: ethernet-connector@2800 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d54312000 prop: fru-type size: 2 val: 4345 node: ethernet-connector@2801 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d54322000 prop: fru-type size: 2 val: 4345 node: ms-dimm@d000 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43313600 prop: fru-type size: 2 val: 4d53 prop: ibm,vpd size: 156 val: 84940052540456494e4944521049424d2033324742204d5320202020204345013156 5a023132464e0730304a41363634504e0730304a41363634534e0c594831304d553432 44314139434304333145395052084900000000010000535a0730303332373638484504 303030314354040000000048570201004233060000000000004234010042370c000000 0000000000000000005046007800000000 prop: fru-number size: 8 val: 30304a4136363400 prop: serial-number size: 13 val: 594831304d5534324431413900 prop: part-number size: 8 val: 30304a4136363400 prop: ccin size: 5 val: 3331453900 prop: description size: 11 val: 33324742204344494d4d00 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,memory-bus-frequency size: 8 val: 0000000000000000 prop: size size: 8 val: 3030333237363800 node: ms-dimm@d002 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43313800 prop: fru-type size: 2 val: 4d53 prop: ibm,vpd size: 156 val: 84940052540456494e4944521049424d2033324742204d5320202020204345013156 5a023132464e0730304a41363634504e0730304a41363634534e0c594831304d553432 44314136434304333145395052084900000000010000535a0730303332373638484504 303030314354040000000048570201004233060000000000004234010042370c000000 0000000000000000005046007800000000 prop: fru-number size: 8 val: 30304a4136363400 prop: serial-number size: 13 val: 594831304d5534324431413600 prop: part-number size: 8 val: 30304a4136363400 prop: ccin size: 5 val: 3331453900 prop: description size: 11 val: 33324742204344494d4d00 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,memory-bus-frequency size: 8 val: 0000000000000000 prop: size size: 8 val: 3030333237363800 node: ms-dimm@d008 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43323400 prop: fru-type size: 2 val: 4d53 prop: ibm,vpd size: 156 val: 84940052540456494e4944521049424d2033324742204d5320202020204345013156 5a023132464e0730304a41363634504e0730304a41363634534e0c594831304d553432 43313754434304333145395052084900000000010000535a0730303332373638484504 303030314354040000000048570201004233060000000000004234010042370c000000 0000000000000000005046007800000000 prop: fru-number size: 8 val: 30304a4136363400 prop: serial-number size: 13 val: 594831304d5534324331375400 prop: part-number size: 8 val: 30304a4136363400 prop: ccin size: 5 val: 3331453900 prop: description size: 11 val: 33324742204344494d4d00 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,memory-bus-frequency size: 8 val: 0000000000000000 prop: size size: 8 val: 3030333237363800 node: ms-dimm@d00a prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43323600 prop: fru-type size: 2 val: 4d53 prop: ibm,vpd size: 156 val: 84940052540456494e4944521049424d2033324742204d5320202020204345013156 5a023132464e0730304a41363634504e0730304a41363634534e0c594831304d553432 43314148434304333145395052084900000000010000535a0730303332373638484504 303030314354040000000048570201004233060000000000004234010042370c000000 0000000000000000005046007800000000 prop: fru-number size: 8 val: 30304a4136363400 prop: serial-number size: 13 val: 594831304d5534324331414800 prop: part-number size: 8 val: 30304a4136363400 prop: ccin size: 5 val: 3331453900 prop: description size: 11 val: 33324742204344494d4d00 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,memory-bus-frequency size: 8 val: 0000000000000000 prop: size size: 8 val: 3030333237363800 node: processor@1000 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333200 prop: fru-type size: 2 val: 5046 prop: ibm,vpd size: 145 val: 848c0052540456494e4944521031302d5741592050524f432043554f44464e073030 4658353138504e0730304658373430534e0c5941313933323039363935314343043534 4538484504303030314354040000000048570200014233060000000000004234010042 370c0000000000000000000000005052083500500122008001565a0230314345013150 460200007800 prop: fru-number size: 8 val: 3030465835313800 prop: serial-number size: 13 val: 59413139333230393639353100 prop: part-number size: 8 val: 3030465837343000 prop: ccin size: 5 val: 3534453800 prop: description size: 8 val: 556e6b6e6f776e00 node: processor@1001 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333200 prop: fru-type size: 2 val: 5046 prop: ibm,vpd size: 145 val: 848c0052540456494e4944521031302d5741592050524f432043554f44464e073030 4658353138504e0730304658373430534e0c5941313933323039363935314343043534 4538484504303030314354040000000048570200014233060000000000004234010042 370c0000000000000000000000005052083500500122008001565a0230314345013150 460200007800 prop: fru-number size: 8 val: 3030465835313800 prop: serial-number size: 13 val: 59413139333230393639353100 prop: part-number size: 8 val: 3030465837343000 prop: ccin size: 5 val: 3534453800 prop: description size: 8 val: 556e6b6e6f776e00 node: processor@1002 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333300 prop: fru-type size: 2 val: 5046 prop: ibm,vpd size: 145 val: 848c0052540456494e4944521031302d5741592050524f432043554f44464e073030 4658353138504e0730304658373430534e0c5941313933323039363935304343043534 4538484504303030314354040000000048570200014233060000000000004234010042 370c0000000000000000000000005052083500500122008001565a0230314345013150 460200007800 prop: fru-number size: 8 val: 3030465835313800 prop: serial-number size: 13 val: 59413139333230393639353000 prop: part-number size: 8 val: 3030465837343000 prop: ccin size: 5 val: 3534453800 prop: description size: 8 val: 556e6b6e6f776e00 node: processor@1003 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333300 prop: fru-type size: 2 val: 5046 prop: ibm,vpd size: 145 val: 848c0052540456494e4944521031302d5741592050524f432043554f44464e073030 4658353138504e0730304658373430534e0c5941313933323039363935304343043534 4538484504303030314354040000000048570200014233060000000000004234010042 370c0000000000000000000000005052083500500122008001565a0230314345013150 460200007800 prop: fru-number size: 8 val: 3030465835313800 prop: serial-number size: 13 val: 59413139333230393639353000 prop: part-number size: 8 val: 3030465837343000 prop: ccin size: 5 val: 3534453800 prop: description size: 8 val: 556e6b6e6f776e00 node: usb-connector@2900 prop: ibm,loc-code size: 24 val: 55373843422e3030312e575a533030414c2d50312d543500 prop: fru-type size: 2 val: 4355 node: usb-connector@2902 prop: ibm,loc-code size: 24 val: 55373843422e3030312e575a533030414c2d50312d543600 prop: fru-type size: 2 val: 4355 node: usb-connector@2903 prop: ibm,loc-code size: 24 val: 55373843422e3030312e575a533030414c2d50312d543300 prop: fru-type size: 2 val: 4355 node: usb-connector@2905 prop: ibm,loc-code size: 24 val: 55373843422e3030312e575a533030414c2d50312d543400 prop: fru-type size: 2 val: 4355 node: dasd-backplane@2400 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d503200 prop: fru-type size: 2 val: 4442 node: dasd-backplane@2401 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d503200 prop: fru-type size: 2 val: 4442 node: op-panel@300 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d443100 prop: fru-type size: 2 val: 4f50 prop: ibm,vpd size: 136 val: 84800052540456494e49445210434543204f502050414e454c2020202043450131565a 023032464e0730304531393636504e0730304533373730534e0c594c3130554634313830 3046434304324230384845043030303143540480b5000048570200034233060000000000 004234010042370c000000000000000000000000504601007800000000 prop: fru-number size: 8 val: 3030453139363600 prop: serial-number size: 13 val: 594c3130554634313830304600 prop: part-number size: 8 val: 3030453337373000 prop: ccin size: 5 val: 3242303800 prop: description size: 8 val: 556e6b6e6f776e00 node: power-supply@3100 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d453100 prop: fru-type size: 2 val: 5053 node: power-supply@3101 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d453200 prop: fru-type size: 2 val: 5053 node: service-processor@200 prop: ibm,loc-code size: 21 val: 55373843422e3030312e575a533030414c2d503100 prop: fru-type size: 2 val: 5350 prop: ibm,vpd size: 332 val: 848c0052540456494e49445210492f4f204241434b504c414e4520202043450131565a 023032464e0730304532303232504e0730304533393937534e0c594c3130554634324c30 31334343043243443650520821000000000000004845043030303243540480f300264857 0200044233060000000000014234010042370c0000000000000000000000005046020000 78841c005254044c585230565a0230314c58083100040100300042504602000078845000 5254045652313044521046535020202020202020202020202020464704564e535044430f 424420323031323038323330383030464c14503120202020202020202020202020202020 202050460300000078844000525404565731304452104653502056573130202020202020 202047442000000000000000000000000000000000000000000000000000000000000000 005046007800000000 prop: fru-number size: 8 val: 3030453230323200 prop: serial-number size: 13 val: 594c3130554634324c30313300 prop: part-number size: 8 val: 3030453339393700 prop: ccin size: 5 val: 3243443600 prop: description size: 19 val: 53797374656d20706c616e6172203253325500 node: root-node-vpd@a001 prop: ibm,loc-code size: 18 val: 55383234372e32324c2e3130313043384100 prop: fru-type size: 2 val: 5656 node: system-vpd@1c00 prop: ibm,loc-code size: 18 val: 55383234372e32324c2e3130313043384100 prop: fru-type size: 2 val: 5356 node: xscom@3c0000000000 prop: ibm,chip-id size: 4 val: 00000000 prop: ibm,proc-chip-id size: 4 val: 00000000 prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000001 prop: scom-controller size: 0 val: prop: compatible size: 27 val: 69626d2c7873636f6d0069626d2c706f776572372d7873636f6d00 prop: reg size: 16 val: 00003c00000000000000000800000000 prop: ibm,dbob-id size: 4 val: 00000000 prop: ibm,occ-functional-state size: 4 val: 00000001 prop: ibm,module-vpd size: 65536 val: 000f17ba5598401f3bd42b84280052540456484452564402303150540e56544f43d500370074 01d5b85d0050460800000000000000007884700152540456544f435054fc56494e49d500ab0190 00b1b8240043503030ff003b02406161a050185652544eff007b6314011ca0450056535243ff00 8f647800fe9f1e0056524d4cff0007653400f19f0d0056574d4cff003b65a001899f6800435250 30ff00db66b0005d9f2c004c525034ff008b674c06ca9d93014c525035ff00d76d4c06379c9301 4c525036ff0023744c06a49a93014c525043ff006f7a4c06119993014c525044ff00bb804c067e 9793014c525045ff0007874c06eb9593014c524d30ff00538d3000df950c004c524d31ff00838d 3000d3950c004c524930ff00b38d3000c7950c004c524931ff00e38d3000bb950c004c575034ff 00138ebc008c952f005054624c575035ff00cf8ebc005d952f004c575036ff008b8fbc002e952f 004c575043ff004790bc00ff942f004c575044ff000391bc00d0942f004c575045ff00bf91bc00 a1942f0056455230ff007b92dc006a9437004d455230ff005793dc003394370050460200007884 8c0052540456494e4944521031302d5741592050524f432043554f44464e073030465835313850 4e0730304658373430534e0c594131393332303936393531434304353445384845043030303143 54040000000048570200014233060000000000004234010042370c000000000000000000000000 5052083500500122008001565a02303143450131504602000078843c6152540443503030564402 3031504741010000f180f4000000f000000000000000e100f70000000000000000000000000000 00000000000000f3009300f30000000000000000000000f300f300f30000004d4b050100000000 2347043001525334010000002000000005000001f8080002000100a20817e00000000000005253 340100000020000000060000023d040002000100a00821f0100000000000525334010000002000 00000600000136001002000100a30811d020000000000052533401000000200000000600000113 0c0000100100a40810c0300000000000525334010000002000000006000002f6080002000100aa ff27d020000000000052533401000000280000001700003190040002000100a5ff47816200a162 01916143e0000000000052533401000000200000000d00000ba5010002000100a8ff12f2181228 01000052533401000000200000000500000328002002000100a9ff31a000000000000052533401 0000002000000005000000df001002000100a6ff6f030000000000005253340100000020000000 0700000e26020002000100a7ff161902000000000052533401000000400000004d000012520600 00100100ab1479121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e12 64a1877b02000052533401000000400000004d00001252060000100100ab1579121f141f181e11 1c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000 400000004d00001252060000100100ab1679121f141f181e111c12f18e12f18e12f18e12f18e12 f182e18e12f18e12f18e1264a1877b02000052533401000000400000004d000012520600001001 00ab1c79121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a187 7b02000052533401000000400000004d00001252060000100100ab1d79121f141f181e111c12f1 8e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000400000 004d00001252060000100100ab1e79121f141f181e111c12f18e12f18e12f18e12f18e12f182e1 8e12f18e12f18e1264a1877b02000052533401000001200000020c00000e1309e000100100ac14 1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3 f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0 4c11a37c1d7401f0411e6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12 a3f82d6803e08c11a57c1041d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f 04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d0300000525334010000012000 00020c00000e1309e000100100ac151f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d7401f0411e6803e08c12a3f82d6803e08c12a3 f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a57c1041d6401f04c11a37c1d6401f04 c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f 0430d030000052533401000001200000020c00000e1309e000100100ac161f6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d7401f041 1e6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a5 7c1041d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04 c11a37c1d6401f04c11a37c1d6401f0430d030000052533401000001200000020c00000e1309e0 00100100ac1c1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d7401f0411e6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c11a57c1041d6401f04c11a37c1d6401f04c11a37c1d6401f04c1 1a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d0300000525334 01000001200000020c00000e1309e000100100ac1d1f6803e08c12a3f82d6803e08c12a3f82d68 03e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d 6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d7401f0411e6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a57c1041d6401f04c11a 37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c1 1a37c1d6401f0430d030000052533401000001200000020c00000e1309e000100100ac1e1f6803 e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d68 03e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d 6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d7401f0411e6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c11a57c1041d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a 37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d0300000525334010000002000000007 00000a71080002000100b202123c010000000000525334010000002000000007000035c7040002 000100af02656903000000000052533401000000200000000700001571020002000100b002253c 01000000000052533401000000200000000500000734010002000100b10271d000000000000052 533401000000200000000600001694004002000100ae02264d0000000000005253340100000020 00000006000005c7004000100100b4025690300000000000525334010000002000000006000003 c3001002000100ad02368030000000000052533401000000200000000700000ceb0d0000100100 b302147a03000000000052533401000000200000000700000ed5020000100100b502166d010000 00000052533401000000200000000600000217080002000100bb0920d030000000000052533401 000000200000000700000841004002000100b60910280100000000005253340100000020000000 0500000330002002000100b70931c0000000000000525334010000002000000006000001b60010 02000100bc0915d020000000000052533401000000200000000700000a630f2000100100bd0912 380300000000005253340100000020000000060000033e080002000100c00031f0200000000000 52533401000000200000000600000495040002000100be0044d010000000000052533401000000 2000000006000001b2020002000100c10015c02000000000005253340100000020000000060000 0457010002000100bf0042d0300000000000525334010000002000000006000001860010020001 00c200149020000000000052533401000000200000000500000570090000100100c30053c00000 0000000052533401000000200000000600000397080002000100c70434d0300000000000525334 0100000020000000060000025f020002000100a10822f030000000000052533401000000200000 00060000041f040002000100b80940f03000000000005253340100000020000000060000041f02 0002000100b90940f03000000000005253340100000020000000060000040f040002000100c504 40b0300000000000525334010000002000000006000002ab020002000100c40425a03000000000 0052533401000000200000000600000543010002000100c6045280300000000000525334010000 002000000006000002b5090000100100c80425d010000000000052533401000000200000000600 0003b1010002000100ba0935c01000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000002352902f01525334010000002000000005000001a00c0000200100e0 08158000000000000052533401000000480000005100000e46060000200100e11414b2e12b3254 19322432d1b2f1de23b7b2337931585923f3d2ec12b23f1f2fc7b31b85d316817d020000000000 00000052533401000000400000004700000e46060000200100e11510c316241e22da4c5b5a2a3a 23da2b11a2e5194102c23f2d415818683198392fc26f0200000000005253340100000040000000 4a00000e46060000200100e11652f479f1a2f12a2b12092686f2666d23f4b315814e27e4e2fcf3 1f8d1d3831f8e2fc10a02000000052533401000000300000002700000e46060000200100e11c20 e32b477a28c1d26c1f155b31f822e22a4f02000000000052533401000000280000001f00000e46 060000200100e11d35d317421e31d846c1510c23722d020052533401000000300000002d00000e 46060000200100e11e119249438469e1c24d20e31f827e23f4f55207e25f020000525334010000 02f8000005b50000390509e000200100e2146b9efb7f3edffd78f3e3c9a69a61d11b11b11b111b 111b11b112818b18b18f18b18b18b18b18b182b181b18b18f18b18b181e218e218e11b218b18a1 1b11b111c18b18f18b18f18f182b18b18f18b18b18b18f18b182f181f18b18b18b181e11c18a11 f11b11b218e11c18a2181b14b14b12b16b16f16b16b12b14b141b12b1ab12b16b1cb18b1eb14f1 c2b121c12a222a222a222a222a222a121b16b12b16b12b14b16b16b16f12b161b12b12b12b121b 121f127f16f12b16b16f1eb12b18b18b161b181b18b1cb1ab1eb16b162b12b12b12f12b12b12f1 2b121f16f14b16b16b14b16b12b14b16b141f14b14b14b16b12b16b16b142b12f12b12f12b12b1 2f122b16b12b14b12f16b12b16f161f121b12f12b122f14b12f14b12f16b14f14b161b12b14b16 b141b16b12b142b16b12f14b12b16b16b14b12b1228181b181b18b18b18b182a11b11c18e218a2 18a11b218a11b112c181f18b18b18b18b183b18b18b18b182e13c18a13f218a11b238a238a228a 238a121b238e12b11b11b218a238a12b11b238a131c18f18f18f181b18b181e12b16b141b16b16 b14b14b14b167f121b12f12b121b12b121b121f16b14b16b14b12b12b141f14b12b16b16f16b16 f16b147f12b12b12b12f12b12b12b12f121b11b11f111b11b111b111b13b12b13b11b11f13f11b 11b111b11b112b11b111b111b11b11c18b181a218b181a2187f18f18b18b18b18b18b18b18b183 b181b181b18b181a11b218a11c18e218a218a218a11b2181934a8a11b218f18a218e218a218f18 a2181b18a218a218b18e218a218a11c18a218a2181f181f18b18b18f18b181b1c1b14b1cb14b1c b14f142f141f14f142b18f14f18b18b1cf14f187e214b14a21cb18a11b11b21cf1ca214a2181a2 1cb14a228a12b214a21ca23ca224a13b12c141f14f14f14b143b18b1cb1cb141b1cb1cb14b14b1 81f1c1b1cf1cb14b18b1cb187f14b14f14b14b14b14b14f14b1410b14b18b1cf14b1cb181b1415 c22c16a2587b0100000000000052533401000002c8000005590000390509e000200100e2156b8a 050281421f18b18f18f18b18f181f18b18b181f181f187f18f18b18f18b181f187f18b181f18b1 8f18b181f122b12b12b122e22aa232a214a216a222a11b23ea224b14a23ca2141b1cb12f14b14b 14b1eb1ef1e1f16b12b141b12b16b14b14b12b121b16f14b1eb12b14b1eb1cb14f1c1b12b121f1 2b12b12b12b1210a22ea23ea23ca21ca238b16a23eb1aa212a232a23e2f16b12b16b16b16f12b1 61a224a244a23aa244a22ca12b27ea266a226a222a1416c12b12b12b16f16b16b14f14b141f12b 12b12b12f12b12b12b12b121f16b12b16b12b14b16b16b16b12b121b16b12f16f14b16b12b16b1 6b141b18b14b12b1eb1eb18b1eb1ab18b1611c18b181b183a11c18a218a218a218a228a238e228 a228b181b18b18b18f18f18b18f18b181b18b18f181b18f18b1810a2b8a12b16b258a2f8e2f8a1 4b2a8a19b27816d232a228a216a22ca23ea21ea23eb14a23ca22c1f14b14b12b16b14b16b16b16 b14b16b161f12b121f12b12f121f12b16b14b14b16b16b16b14b12b16b121f16b12b14b16f16b1 6b14b162f13f12b12b13b11f11b111f12b13b12b11b13b13b11f13b131b14b13b14b14b15b16b1 7b16b15b12b1416918b181b18f181b18b181d218a11c181a11b218b18b18a11b117f111c18a218 b18a218f18b18b181f18b181b18b18b182f181b181b18b181b181a21cf14b1ca11c14a21ce11b2 1cb141b141b14f14b14b141b141b1cb18a234a218a214a228a23ca234a228a234b1c2b14f1cb1c b1cb14b1cb18b147f14b14b18b1cb14b1cb1cb14b1cb14b1c1a13c14a22ca228a224b1ca23ca23 8a12b22ca2281b14f1cf14f1cf1cb14b1c1f141b14f14b14f14b142f141b14f142a23ca234a234 b18b14a218a23ca22ca23ca23ca2281b14b18b18b14f1cb1cb1cb1cb18b1c1f14f18f14b1cb18b 14b1cb1c2f12b12b12b1226b31043831d858010000000052533401000002e00000058c00003905 09e000200100e2166b9f7e7c7f1ffdf9f1e7c9a69a61d11b11f11f13b13b13b13b125818b18b18 2a218e11f11b11b218b18b18b18a2181a218a228a218a2a8a1bc18a2b8a228a2a8a13b23819216 a31e8a11b18a218b18a31f8a1aab1f8010801f81a218a11f11c18b18a218e11b11118182b18b18 f18b184b12b12b122f122b12b121b1216f12b121b12b121b12b121b14f12b161b16f14b16b161b 12f14b14b14b16b16b123b12b12b12b12f12b12b12b12b1216b14a218a12b218a22ca23aa23ea2 22a216b14b1c10b14b141f16b14b12b141f12b121f12b12f12b121f16f12f14f16b12b12b123f1 2b12b12b121b121b1cb12f1eb1af1eb1eb1ab12b1e1b11b248a258b18a11b218a278a17b278e16 1b11c18e218a218e218a218a218a218a2181b18f18b18f18b183f18b18b181b18b183b18b18f18 f181b182a11b11b111818a218b18a1118181b18b18f18b18a11b218a11b218b18b181e12b12f12 1b12b121b121b12f12b12f12b12f12f121b16b1ab1ab1ab12b1eb1eb16b12b1ab1a1b12b16f12b 12b12b16f16f141b14f14b14b14b16b16b16b14f161f12f12b12b12b121b121e21ca27ea26ca26 2e278a27eb1aa21aa278a2421b16b11b16b17b11b16b17b12b16b11b171f13f12b13b11b13b13b 132b12b15b16f16b14b17f13b16b152b238a11b218a13b238a238b18b18a2181b18f18f18b18b1 8b18b18b18b181a238a218a12b13c18a11b238a13b11b238a121b218a2181a218e218b18f18b18 16a11b218a12f12b11b238a13c18a228a2287f14b14f14f14b14b14f142b14f1cb18a11b21ce21 8a218b141b1ca218a21cf1cb14a21ca218a21cf1416b14b141f14b143f14b1cb1cb18b18b1c1b1 8b181b1cb14b1cb14b18b18b1c1b14b142b1cb18b1cf1cf1cf1c1b1cb1cb1cb18f1cb1cb18b14b 14b142f14b14f141f141b18b14b18b18b18b18b1cb1cb18f147f12b12b16f14b14b16b14b14b16 3f23b4c22f10a31f81f2262b2c87b01000005253340100000310000005e90000390509e0002001 00e21c6b92f97c7f3ffdf9f1e3c9a69a61d12b13b12b11f13b13b11b13b12b132c18b181b18b18 1b182f18b18f18f18b18b1810a238f18a11b11b238e12b238a2281f18b18b18b18f18b182e11c1 8a11b278a15b15b278e17b228a23810b12b16b16b14f16b14b12b14b161b18a216a216a212a21a a218a21eb16a218b1ca2181f16b14b141b16b14b16f121b16b12b16b16b14b16b16b16b14f141f 12f12b12f12b12b12f121b122f12b12f12b121b12f12b12b12f12b12b12f121b12b12f121b12f1 22b14b12b12b14b12b14b16f122b121b121b12f12f121b14a212a218a21aa218b1ea21ea21ca21 ca214b141b14b16b141b14b16f12f1210b12f16b14b16b16b16b12f142b12b16b14b12b16f162b 16b12b18b16f12b1e1b16b1c1f12f12f12b121f121c18a2181b18a218a218a218a11b11c181f18 b18b18f18b18b181b1810a13b218b181a238a11b12b218b18d25fc182f18b182e218b18b18b181 a218b18a11b218a2181e11b218a218a218e218f18b18a111f14b12b12b18b16b12b1eb14f18b14 1b12b1eb1cb12b12b1eb1eb1c1b141b12a236b1ea218b1eb1aa23ea228a22ab14a111c16b12f16 b16f16b16b12f121b16b16b14b14b14b12b16b16f12b141b16b12b14f16f16f14b141f1ea11c1a b1ea21ca21ea21ea11b21ca218b161b12b13f131b13f13b12b1316b218a218a218b18e218a218a 111c181a238a228a11b218a228a13b238a238a11c18b1816a218a218e218a218b18a218b18b18a 2181e11b12c18a13b228a12b238a218a238a12b111b11c18b18a218a11b218a218a11b218a218b 181f181b18b18b181b1810b14f14f14f14f14f141b1cb14b1cb1cf18b1cf1cb14b141b14b1ca21 8b1cb18b14a21cb1ce11b2181f14f14f14b14b14b14b14b141a214b18a11c18b1ca214a21ca11b 11c1ca2142b18b181b1cb14f18b183f14b14b14b1410f14f14b141b14f14f141b1ca21cb18a11b 21ce21cb1cb18a11b21c2f14b14b14b143b14f14b14f14b14f14f141b14b16b14b14b12b12b16b 14f12b1615b31f82e27a3a31081831e87b010000000052533401000003400000064d0000390509 e000200100e21d6b977a7ebe9f18c5f1e7c9a69a62911b11b11b11f11b11b1138181b18b18b181 b181f18b18b18f18f18f18b1810b181f18f18b18b18b181e13b228b18a12b11b11b238a12c18b1 8a2381b18f18a11c18a218a218a218e218a2181a11c18a13b12b218b18a238b18b18a11b2281b1 6b12b14b14f16b16f16b16b121b18b12b14b161b1eb18b18b18b181b1ab18b1cb12b1cb12b1e1b 14b141f12b12f12f121f121b1ab16b1ab1cb18b1cb1eb14b1eb12b121b14b121f14b16b14b12b1 61f12b12b12b12b12f123b18b16b18a216a21aa21aa21ee214a212b1c1b12b16b12b12b14b12b1 6f12b12b167f12b16b1af14b12b1eb1eb12b1ab121b1cf18b1ab1ab1cb1eb1af16b121b121b12b 12f1211f16f12b16f14b16b12b12b141f14f14b16b14b14b16b16b12f121b18b16a11b214a218b 1ca21eb1ab14a21ca2162818b18b18b18b18b181b18b182f18f18b18b18b18b18b181a12b12b23 8a13b11f238a13b228a11b2381e111818a218b18a218a11b218e21816b181b18f18b18b18b18f1 81e1eb16b12f18b12b1eb1eb1ab1eb1c1b18b18a21aa216a21ab12a21ea21ea21eb1ca112816b1 61b14b16f16b14b121b12b121b12b12b12f12b122b12b12b12b12b12b12b1211b16b12b12b14f1 4b16b12b16f141b12f13b13b11f13b13b11f121b14b12b17b12b14b14b17b14b14b16b141b13b1 2b1cb13b11b1ab1fb14f15b191b228a228a1db13b11b14b2f8a2c8a2f8a1cb2481a228a11b12b1 1f228a238b18e12b121c18a228e12c18a13b238a228a238b18b187e218b18a218a11b11b218a21 8a11181810a218a13b268a14b268a238a278a14b15b278a238196168012b1ba31b8a268a94801f 8017b51801ba21c1c14b18b1cb1cb1cb18b1cf18b18b183f14b14b14b14b141f1cb1cf1cb14b18 b1cb14f1cb1410b14b18b18b18b1cb1cb14b14b1c2b14b14f14f14f14f141b14b1cf14b14b14b1 cf1cb18b141a21cb1cb1ca21ca12b13b23ca22ce218a11281ca21ca218b1ca11b21ca214b1ca21 8a2181a214e21ca11b11b214a21ca21ca218a11c1c7f14f14b14b14f143a218a218b18a214f1ca 21ca11c14a214a111b214a216b1ea212a212a11b21ea214b1ca11b218191d33f01000052533401 000003680000069d0000390509e000200100e21e6b9efc7d3fdffd79f3e3c9a69a639111b113b1 3b228a228b18b18a228a238a12b238b18a2387f18a218b18e218a11b218b18a1111818a112b218 a218e2181e218a218a218b18a218e218a11b218a11c187f16f14b12f14b16b16f14b141b1eb12b 14b16b18b16b1eb1cb1cb16b1a2f12f12b121f121b12a21eb12f18a214a21ea21ca21aa214b1c2 f12f12b12b12f121f12b12f12b12b12b12b12b122b12b12b14b14f16b16b16b12b161f12b16b14 b12f14b16f14b16b121b16b16b12b14b16f16b14f141f16b16b14b12b12b12b16b14b12f161b12 b12f161b16b14b12b14b121f121b12f12b12f12b121b12b14b14b14b14b16b16b14b14b16b127f 16b16b16b16f16b161b12b121b12b1cb1cb14b1cb14b1eb12b1611b218a11b13b13b11b238a13b 238a11b2281a11c18f18e11b218e218a11b111b248e14b278a14c18a278a248a17b16b2481b18f 18b181b18f18b181f181b18b18b18b18f182b181b181b18b18b18b18b181a16b12b248a258a15b 238a278a278a15b218a131f121b12b12f12f12b12b122b12b12b12b12b12b12b12b12f418f9c12 f121f12b12b12b12b121f1ab12b16b18b14b1eb14b14b18b181b16b14f14b12b12b16b16b16f16 1b16a11c14b1ca212b14a21ea21ab14b14b141f14b16b12b16f16b12b12b16b1210b11b11f11b1 1b111f111f11f11b11f11b113818a2181e11b218a218e11b111ba3080188028a13b228af108038 80308028895208031b11b11b11b111b11b11b11f111f218a218a13c18a228a238a11b238a218b1 81b18b18f18b18b18b18b18f181e11b11c18a218a218a11b218f18e2181a1cb1fb2b8a13b1bb28 8a2f8a1fb218a12b2481e248a258a228a11b268a278a12b258a11c181f18b14f14b18b1cf1cb1c b1c1a17b18b248a21ca15c1ca2fca21ca2bca2f8a141f22ca238a11b21ca12b23ca218a224a228 1f14b14b14b14b14b14b141b141f18b1c1b1cb18b1cb14b18b141e21ca12b234a13b13b214a23c a228a23ca2241f1cb18b18f18b1cb1cb14f1c2b18f141b1cb18b1cb181e238a234a23cf1ca21ca 23cb18b14a214a2341b18b1c1f1cb1cb1cb18b1cb1c1a224a278a26ca234a214a248a27ca278a1 4c1ca2641a214e214a21ca21ca214a21ca11c1ca11c1c1b14b14a214b18b1ce21cb1cb1ca214b1 c14b1d20d27e6a010000525334010000002000000007000009530d0000200100e302112c030000 0000005253340100000020000000060000047e004000200100e40243f020000000000052533401 0000002000000006000008dc020000200100e502106f0000000000005253340100000020000000 05000005100f2000200100e60950c0000000000000525334010000002000000006000007310900 00200100e70071c010000000000052533401000000200000000600000175040000200100e80013 d010000000000052533401000000200000000600000189020000200100e90014a0100000000000 52533401000000200000000600000339090000200100ea0431e010000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000023560301010000000100000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000020dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000 00000000000000000000000000000000030dc400c8012c00dc001e07ec00bb00f000d000180e8c 00d2016800e500240000000000000000000000000000000000000000040dc400c8012c00dc001e 07ec00bb00f000d000180e8c00d2016800e5002400000000000000000000000000000000000000 00050dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000000000 00000000000000000000000000534231c0f5040691c218bf26ad36bbb21e2349657abcb01dbe58 6626f5040691c218bf26ad36bbb21e2349657abcb01dbe58662650420501000000005046020000 788410015254045652544e534f020000494efe0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046007884740052540456535243494e6400000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046030000007884300052540456524d4c5644023031504e0730304b56363237534e0c59413139 3332303936393531545604303030345046010078849c0152540456574d4c56440230314f430400 000308464f1102ffffffffffffffffffffffffffffffff23495401010454000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000494e140131393336333530303030323432303134000000 5046030000007884ac0052540443525030564402303145442101000000000000000000000730d0 002eab620491000000483018ea20028a00598054450b0130323033514651415346444405013032 3032535403010000444e4901250000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000049510b0100000000000000000000504603000000788448065254044c5250345644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100c2013a00d6002407ed00a600 a800be00180e6b00cc016a00e00028000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000544313140000191e01f6191f01f3191e01f6191e01f7234d010103000002bd 032001390000034003a301c30000031403a30238000002d003a302d5000003c304260215000003 97042602bf00000353042603a0000002ef042604680000044704aa02560000041b04aa032a0000 03d704aa04310000037204aa053a000002dc04aa06070000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52503556440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00c2013a00d6002407ed00a600a800be00180e6b00cc016a00e000280000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec000000050000000500000005 00000005234d010103000002be0320012f0000034003a301bc0000031403a30233000002cf03a3 02ce000003c30426020d00000397042602bf0000035304260396000002ef0426045e0000044804 aa024c0000041c04aa0334000003d704aa04310000037204aa0535000002dc04aa060400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525036564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100c2013a00d6002407ed00a600a800be00180e6b00cc016a00e0 002800000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191f01f1191e01f8191f01f3191f01ef234d010103000002be032001340000034003a301c400 00031403a30230000002d003a302d0000003c30426020b00000397042602bc0000035304260398 000002ef0426045e0000044704aa024c0000041c04aa0319000003d704aa04270000037204aa05 2d000002dc04aa05ff000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000434801004951090200000000000000005046020000788448065254044c5250435644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100c2013a00d6002407ed00a600 a800be00180e6b00cc016a00e00028000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000544313140000191e01f9191e01f8191f01f4191f01f4234d010103000002bd 032001390000034003a201c30000031403a2023f000002d003a202d8000003c304250215000003 97042502c600000352042503a2000002ef0425046d0000044704a902580000041b04a9032d0000 03d604a9043d0000037204a90541000002db04a9060c0000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52504456440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00c2013a00d6002407ed00a600a800be00180e6b00cc016a00e000280000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191f01f2191e01f5191f01f2 192001ee234d010103000002bc0320013a0000034003a201c10000031403a2023f000002d003a2 02d5000003c30425021200000397042502cb00000353042503a7000002ee0425046d0000044704 a902530000041a04a9032d000003d604a9043b0000037204a90541000002db04a9060900000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525045564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100c2013a00d6002407ed00a600a800be00180e6b00cc016a00e0 002800000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191e01f7191f01f2191f01f1191e01f7234d010103000002bd032001310000034003a201bd00 00031403a20235000002cf03a202d3000003c30425021000000397042502c10000035204250398 000002ee042504600000044704a9024c0000041b04a90320000003d604a904310000037204a905 3a000002db04a90604000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000043480100495109020000000000000000504602000078842c005254044c524d305644 02303254430516000000004d430400000000494e08000000000000000050460300000078842c00 5254044c524d31564402303254430516000000004d430400000000494e08000000000000000050 460300000078842c005254044c52493056440230325443051600000000494e1000000000000000 000000000000000000504602000078842c005254044c5249315644023032544305160000000049 4e100000000000000000000000000000000050460200007884b8005254044c5750345644023031 2332440001040f0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000233344000104 0f0000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000494e13000000000000000000 000000000000000000005046030000007884b8005254044c57503556440230312332440001040f 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000002333440001040f000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000494e1300000000000000000000000000000000 0000005046030000007884b8005254044c57503656440230312332440001040f00000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002333440001040f00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000494e130000000000000000000000000000000000000050460300 00007884b8005254044c57504356440230312332440001040f0000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000002333440001040f0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000494e13000000000000000000000000000000000000005046030000007884b80052 54044c57504456440230312332440001040f000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000002333440001040f000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000049 4e13000000000000000000000000000000000000005046030000007884b8005254044c57504556 440230312332440001040f00000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000002333 440001040f00000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000494e130000000000 00000000000000000000000000005046030000007884d8005254045645523056440230312349c4 000104300000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000504601007884d8005254044d45523056440230312349c40001043000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000050460100780003ca8f47b6f3 61985975001fffffffffffffffffff001fffffffffffffffffff001fffffffffffffffffff001f ff73ff7f5faffffe970013c889c766e861985975001fffffffffffffffffff001fffffffffffff ffffff001fffffffffffffffffff001fff73ff7f5faffffe970c16d717318e036fdd1be66effff 377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6cf0c12d717318e 036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2a e6cf0c06d517518e036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef77 77a6a82c0c869c6af6cd0c52e71211ee036ddd1be66effff377ffdab15e55fb4009a31771ff7bb 7ffddfbf6efcef7777a6a82c1c869c2af6ed0c56e71231ee036ddd1be66effff377ffdab15e55f b4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6ef0c52e71231ee036ddd1be66eff ff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2ae6ef6918590545 d2099333385d17691c590545d0099333385d577912dd01457a7f02d359631d7916dd0145787f02 d359635d4e392e346b6c688f0a597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbcb6dbdb6eab1821bb221206748115043232f1fa0b1512ee90c9 600c2c43812edd04882a550d8f1cbb706fff9efefbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa09bf1a86e3d2e347b6c688f0a597b27b5bee6f0f24b05149462 069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbcfef3dece2f1ae99b08caba70 dae4aa2786dbda4d9806d6b2ab402622a1aa6b0522f2727f2eb7be8b7267ff9efefbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa09bf1a84e212c342b6c688f0a 597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bcbedbdb6eb912a1f32106a4708b94a5ac2cbbcb2f9930f4b28b463788d18067f522c4626d0db3 9eab6267ff9efefbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a0dbf9a86fb526307bec608e0a597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbcfedbde6ead0828532e9f0020fb144d8b9a5b086c8b88e871cb 4f3b1285c00c4c163219dca2b49abdeb27bebedafbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa2dbf9a04fb926306bec608e0a597b27b5bee6f0f24b05149462 069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbef693ffee9900f5e0423b3a9c b3adcabe1ed5a96e8e08d05b81e8ba34f5980f70bcfe314721bb9edbfa6fbe9efafbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa29bf1a06fbd26307bec608e0a 597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bcb6dbdb6ead18e39b6d1f36c0a3d66c0c20339a688622c0e1dbe9973c9e1144dc1e1a63ed6fb0 1abb7a6fbebefafbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a29bf1a0000f1635ba3b1dc5fb50db001505e173787b56b5d5e0001fffffffffffffffffff001f f677eba7ac607d7d8250fae7fa59ea8c7d55aa2b15d5e87a9548aabf7fefff55ffefffffeaafff ffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffef ffffeaafffffefff55ffefffffeaafffffefff55bea75fefaa876e38035910b4eeba3734b24232 67956ed088a71514240714b888a2f0cfef999bfb3777feeefffddfffbbbfff677eda8e1f5d58b6 22614f255372b04bbd13ef6cffff777fffbb7ffddfff6effff777fffbb7ffddfff6effff777fff bb7ffddfff6effff777fffbb7ffddfff6effff777fffb93ffcccde6eece36801d828852128b41e 76e87256795db4970be2b501801484c26405427b310117ea1e967e59e73213039f022a0cb15450 e2d361b5ab175012c0a40c11d23b33c964e74a57e17465fa4976ed43b72240b855d7b26b709432 0830734e09f7553950eb371820927c7cd6ac1b01df34f72e670b35330d53d389501902e5556d44 3a2b5162710844c5364547345a400d35a61e25b928738a1a044317a14440d37d03b1082767424f 68d2047046247824d5154f4ae2f175b59612619e66610e80d24861bf62c5ee73f342e6c0453648 6a723526cb36f60e1881516a543a260e56f4c72874d063b03f27457005ce6d407111927b516834 f0b039248f12f07f273d0a33d86430925af30c363f4a53cb5c024f735140776f6a02e100e7723a 70bb64f164463f4100ea73702f25497085ce6d407111927a516832f0d72976d37b93b8260d6a34 ab10758f4b368761572c945530454003f7be70b440c37e21c4dd0a6498621c2407376d94ca58a7 9100c12007cc2c405111925a556832f0d72976d063b03f27557a242c75c4ff19617462e508e3d3 7d25456214f862a102b68b68874e2861a063a20ea64e04717e30762436835ef7677422437a1372 166c3270c72976d063b03f27457005ce6d4071198271402e30e4bb61e10b52b182034c70e33225 463e69509c001762d01c1494777932cc60ae3ab19449c15f69943b23af24e6052875d860304f25 453005ce6d407111927a516832f05439465c2313b963783e315d6010c509f63e63914252035c02 0a5b83df459438b40310908c4052a602a416c7fb59072a1a774f21497085ce6d407111927a5168 32f0d72976d06bb1bf27057604f52d756869735a37c63446804dc5423bf5bb50853eb6f069b0de 62b774022b4cf7961987d91b332624b14687f920b32772875541667082e84404aa33749a11bc68 37a1243391028561323512e76c19304869b613621e1cf7da44651703a57813d31cd32d2c070420 265251983c94dc10f5100a208e672f4e63fb11315f52f08c135770b32051f40d6bc52b65fc1a47 7259032c52f199341c5c44a824b7b40b24e9263402a4dc5593ea31a44b03f948a72235e59a7337 ca24b260d6c14c874919f15d22fc0c946751c7fe0a4196651f2a85e350c15630454a24053001f0 4c14011ac16a635802d6055d956951b29a16db0640b90c67d041451421e504e52a4157d170f2cb 74f35ef7ff3df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7fe77 cf7ed77a6d63b7114563752c66854f7443ee63f32f14d12ae39460830f2a67a117f038e17d28b0 3352a7bf26175a13515547881173aa11202034c12550c12a554f63fe3ce0fa3567f17133fe53a4 1ca5547937fd69677d724300b08808620b59207214f466a68d0422ae39460f135748438714f2c2 10676e760f46e1f16442687b305d60b314409f61f2091bc51810051e73f91503cd52a5fb62a174 71c804c2c34b14bb275f74777e45036d7a80ca009520636704b1706280a231e816533c3df37379 e25e03c418c7e44423ee7046e4324848a6d749b22b30747b14732614892902de00b68e12f62e81 bf34744809e2cf46a53cc3f631c5835b00a876b82ca2c305630531b34065097203e54c062a29e0 45410432f7d3249306218089210760365d4964f03a34f231e632e3086517d421e077738a7064ac 48454859e70d309816209879e4d1004447319732f4a264b15a51a44604ab60c3706c96cd4310f0 36b64e16f80857f11b4115519e42f7cf5d075b21b3f4507c7e06cb3c64b939628713570c837865 625f2a36bb41060eb67e0dc1d128e68566715653cf2c62a918a39321340664e749746c01f04424 e978e3aa1dc70600706405086a570338a0c538b40744725c83c145069c41d07407843a97f64873 ff6207b566703c91243c80336a26ab21e334a59508e6917242d5212c36f7af30c10943d3eb1521 2481180d53c10012ce741042454a04f15833a43314c54e657a34e16b63a34774011ce5026001f4 08632d146e5ce3732484e122d703029a64e41711e2f743c447368f7a456579455f71d6d236ab76 216f65525e4a10ee233120057f09953d21140472cd1ea63204618c41077253ca5eb24a11532c73 201e16447cc17b5dd2330a220201e222e24f6446e1517006255a5400e718312c0b15d9150876d3 e009a74b49b22535c50216803df4b829f05b37074e24e975675369156325a43e15ef71c7d53197 e150683802d218249b4ad08812c22621c85922d26082c701e170937724422b3a555c43016ca55b 30e15423978f646554f4c158b07619542156043213f45cc1fb73b42923db2881ed2d30b159961e 109a14936769479851831923064e555a59f7f459015e63165a107b79b0e03a33a5343e7e12d861 c1801b87a512da4604c54857fe6a80f244a776c14f38b3837a001310120c814235949053d73a04 596860871437340b3459066208a17470720d53d542025118b6be54668b30464f03264e90ef38b0 4b43d6d264812cb49a49231278a665226a6e33a278e12218e7f4477d28b13d08340e30460a2146 72e2eb08808d32858e628074d3e429e2cf0353e322324ca09b74d69d71457c00d73a90d425f1f0 2b374670325435505486b319e6c5308a00b54251927a601709712616a7556cb70810e03277ee06 f1d01013d95a275a61a35c144e209447693738277560714121a26670e3f250d822c72a29853768 c15c100c2c160620254c60d07c45740ac06d0582e903a67b51753406cb68622663a75734686cd1 dc3920ca51c5d1569b30813e0d042460778a35100660ee51d7b82ae47e519352259a6d706a40b5 dd018d04f61808f5ce3000a9525846a5a614e2664af1f266eb14316821f5a923805635a60a05f8 4dd53401e65a26686a72b22591f673d47240a44c67ef2413033aa43331bb3e15b341f0ee5a1704 034640013618c0ed59f2a022321665f015a41f23966e161b58e4783d52eb7ba06e023476e6855c e56052a1e870a904162105f10069d4a6610b7627af39a43e78317e245a74c54a10b45f7a175872 892c718e55747878173630491a129b04d31a18629e663a46c3cf2143cf6ba6ad376908b1894051 31684006543002c6f10df2fd3af3ff77ff5ef3ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff73f7fc57cf72c77b6d30e97b33df77ef5ef7fb7137fa73019b41197a70f618f531603091054e 30513344618f71204101d75a40491933565ad39524d80e75484511b30941ec01b94250f305f717 78e0c4625452d39108c70040a59e4206f264017a19319d44e96633131426800932c50631023e7d 0221118a1fa88be808a9a901bd0ed1c4e8ae485ca0ee4505671e40080bec64c9fb11c68853a300 21353bec7a04a18ea4adea8153876b5b01ff43f932c00bf509e22651d00f2feae802970709e055 31860fa8820b7d6ea6661089c0819d200de1808ed5115f068e8a00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000 prop: ibm,ccm-node-id size: 4 val: 00000000 prop: ibm,hw-card-id size: 4 val: 00000000 prop: ibm,hw-module-id size: 4 val: 00000000 prop: ibm,mem-interleave-scope size: 4 val: 00000000 node: chiptod@40000 prop: reg size: 8 val: 0004000000000034 prop: compatible size: 37 val: 69626d2c706f7765722d63686970746f640069626d2c706f776572372d63686970746f6400 prop: primary size: 0 val: node: nx@2010000 prop: reg size: 8 val: 0201000000004000 prop: compatible size: 27 val: 69626d2c706f7765722d6e780069626d2c706f776572372d6e7800 node: pbcq@2012000 prop: reg size: 24 val: 00200102200000000020010905000000003c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000000 prop: ibm,hub-id size: 4 val: 00000000 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333200 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 7777777777777777777777777777777777777777777777777777777777777777 node: pbcq@2012400 prop: reg size: 24 val: 00240102200000000024010905000000403c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000001 prop: ibm,hub-id size: 4 val: 00000000 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333200 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 7777777777777777777777777777777700000000000000000000000000000000 node: psihb@2010c00 prop: reg size: 8 val: 02010c0000000010 prop: compatible size: 31 val: 69626d2c706f776572372d70736968622d780069626d2c70736968622d7800 prop: boot-link size: 0 val: prop: status size: 3 val: 6f6b00 node: xscom@3c0800000000 prop: ibm,chip-id size: 4 val: 00000001 prop: ibm,proc-chip-id size: 4 val: 00000001 prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000001 prop: scom-controller size: 0 val: prop: compatible size: 27 val: 69626d2c7873636f6d0069626d2c706f776572372d7873636f6d00 prop: reg size: 16 val: 00003c08000000000000000800000000 prop: ibm,dbob-id size: 4 val: 00000000 prop: ibm,occ-functional-state size: 4 val: 00000001 prop: ibm,module-vpd size: 65536 val: 000f17ba5598401f3bd42b84280052540456484452564402303150540e56544f43d500370074 01d5b85d0050460800000000000000007884700152540456544f435054fc56494e49d500ab0190 00b1b8240043503030ff003b02406161a050185652544eff007b6314011ca0450056535243ff00 8f647800fe9f1e0056524d4cff0007653400f19f0d0056574d4cff003b65a001899f6800435250 30ff00db66b0005d9f2c004c525034ff008b674c06ca9d93014c525035ff00d76d4c06379c9301 4c525036ff0023744c06a49a93014c525043ff006f7a4c06119993014c525044ff00bb804c067e 9793014c525045ff0007874c06eb9593014c524d30ff00538d3000df950c004c524d31ff00838d 3000d3950c004c524930ff00b38d3000c7950c004c524931ff00e38d3000bb950c004c575034ff 00138ebc008c952f005054624c575035ff00cf8ebc005d952f004c575036ff008b8fbc002e952f 004c575043ff004790bc00ff942f004c575044ff000391bc00d0942f004c575045ff00bf91bc00 a1942f0056455230ff007b92dc006a9437004d455230ff005793dc003394370050460200007884 8c0052540456494e4944521031302d5741592050524f432043554f44464e073030465835313850 4e0730304658373430534e0c594131393332303936393531434304353445384845043030303143 54040000000048570200014233060000000000004234010042370c000000000000000000000000 5052083500500122008001565a02303143450131504602000078843c6152540443503030564402 3031504741010000f180f4000000f000000000000000e100f70000000000000000000000000000 000000000000009300f300f30000000000000000000000f300f300f30000004d4b050100000000 2347043001525334010000002000000005000001f8080002000100a20817e00000000000005253 340100000020000000060000023d040002000100a00821f0100000000000525334010000002000 00000600000136001002000100a30811d020000000000052533401000000200000000600000113 0c0000100100a40810c0300000000000525334010000002000000006000002f6080002000100aa ff27d020000000000052533401000000280000001700003190040002000100a5ff47816200a162 01916143e0000000000052533401000000200000000d00000ba5010002000100a8ff12f2181228 01000052533401000000200000000500000328002002000100a9ff31a000000000000052533401 0000002000000005000000df001002000100a6ff6f030000000000005253340100000020000000 0700000e26020002000100a7ff161902000000000052533401000000400000004d000012520600 00100100ab1479121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e12 64a1877b02000052533401000000400000004d00001252060000100100ab1579121f141f181e11 1c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000 400000004d00001252060000100100ab1679121f141f181e111c12f18e12f18e12f18e12f18e12 f182e18e12f18e12f18e1264a1877b02000052533401000000400000004d000012520600001001 00ab1c79121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a187 7b02000052533401000000400000004d00001252060000100100ab1d79121f141f181e111c12f1 8e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000400000 004d00001252060000100100ab1e79121f141f181e111c12f18e12f18e12f18e12f18e12f182e1 8e12f18e12f18e1264a1877b02000052533401000001280000021100000e1309e000100100ac14 1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3 f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0 4c11a37c1da401f0412081b6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08 c12a3f82d6803e08c11a77c104821b6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d 6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d03000000000000000052 533401000001280000021100000e1309e000100100ac151f6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1da401f0412081b6803e08c1 2a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a77c104821b64 01f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d 6401f04c11a37c1d6401f0430d03000000000000000052533401000001280000021100000e1309 e000100100ac161f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1da401f0412081b6803e08c12a3f82d6803e08c12a3f82d6803e08c1 2a3f82d6803e08c12a3f82d6803e08c11a77c104821b6401f04c11a37c1d6401f04c11a37c1d64 01f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d030000 00000000000052533401000001280000021100000e1309e000100100ac1c1f6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1da401f041 2081b6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c1 1a77c104821b6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d64 01f04c11a37c1d6401f04c11a37c1d6401f0430d03000000000000000052533401000001280000 021100000e1309e000100100ac1d1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1da401f0412081b6803e08c12a3f82d6803e08c12a 3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a77c104821b6401f04c11a37c1d6401 f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d64 01f0430d03000000000000000052533401000001280000021100000e1309e000100100ac1e1f68 03e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1da401f0412081b6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a 3f82d6803e08c11a77c104821b6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401 f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d030000000000000000525334 01000000200000000700000a71080002000100b202123c01000000000052533401000000200000 0007000035c7040002000100af0265690300000000005253340100000020000000070000157102 0002000100b002253c01000000000052533401000000200000000500000734010002000100b102 71d000000000000052533401000000200000000600001694004002000100ae02264d0000000000 00525334010000002000000006000005c7004000100100b4025690300000000000525334010000 002000000006000003c3001002000100ad02368030000000000052533401000000200000000700 000ceb0d0000100100b302147a03000000000052533401000000200000000700000ed502000010 0100b502166d01000000000052533401000000200000000600000217080002000100bb0920d030 000000000052533401000000200000000700000841004002000100b60910280100000000005253 3401000000200000000500000330002002000100b70931c0000000000000525334010000002000 000006000001b6001002000100bc0915d020000000000052533401000000200000000700000a63 0f2000100100bd0912380300000000005253340100000020000000060000033e080002000100c0 0031f020000000000052533401000000200000000600000495040002000100be0044d010000000 0000525334010000002000000006000001b2020002000100c10015c02000000000005253340100 0000200000000600000457010002000100bf0042d0300000000000525334010000002000000006 00000186001002000100c200149020000000000052533401000000200000000500000570090000 100100c30053c000000000000052533401000000200000000600000397080002000100c70434d0 3000000000005253340100000020000000060000025f020002000100a10822f030000000000052 53340100000020000000060000041f040002000100b80940f03000000000005253340100000020 000000060000041f020002000100b90940f0300000000000525334010000002000000006000004 0f040002000100c50440b0300000000000525334010000002000000006000002ab020002000100 c40425a030000000000052533401000000200000000600000543010002000100c6045280300000 000000525334010000002000000006000002b5090000100100c80425d010000000000052533401 0000002000000006000003b1010002000100ba0935c01000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000002352902f01525334010000002000000005000001a00c0000200100e0 08158000000000000052533401000000400000004100000e46060000200100e11451e336817a1b 1d23f1f2fc14a23f4f31486a22e7a23f2821b3f23f2e26813c0200000000000000005253340100 0000400000004700000e46060000200100e1151492512a25437e5813f420a2fc12a2fc3a31f83a 31f84f3fff5a266182fc5f22716c02000000000052533401000000380000003a00000e46060000 200100e11615a32e435a2cd23925e5d2fc3b26c20b22d1a3188792142e31f815b0200000005253 3401000000300000002700000e46060000200100e11c77a23f5b31e8318222e22711831f82d2fc 6b02000000000052533401000000300000003000000e46060000200100e11d7c3146100f27e191 c21f31e85e2881d56818810a27e4f02052533401000000380000003c00000e46060000200100e1 1e5481913f31f87f31b8e2fc1a25615823f16e284692fc1d21c282fc10a0200000525334010000 02c00000054c0000390509e000200100e2146b8a05028144a111f11f114818f18b18f18b182f18 a11b12c18a218b18a238a11b12b12b2182f181b18b182f18f18b18b18b18b18b18b18b18b181f1 8f181b1820b12b12b12b12b12f12f122b1ab18f1af16b1eb1eb12b18b1a1b12f121f12b12f1210 b12b12b12b12b12b12b12b121b121b16b16b14b121b16b12b14b16b1211b12b12b12b12b12b12f 1210b12b12b121b123b12f1eb18b16b1ab1eb16b18b12b1210f121f12b121b1226b12b12f12b12 f12b121b218a111818a11c18a218f18a11c181a2a8a1cb238a218a268a248a2f8a288a2b8a228a 2481a12f13b238a218a13b238b18a11b2382b18b18a218e238a238a12b12b238a131c18b18f18b 18f18b18b18b18b181b181b18b18b18b18b184b18b18f18b18b182e121b12b12b12b121f121b12 b12f12b12b12b12b12b12b12b121b1cb12b14f1eb1eb1eb1eb14b1cb1810b12b12b12b12f12b12 b12f1210f12b121b12f12b121e24fa23ea24fa257a275a242a27fa273a23db18a2121b12b11b14 b14b13b17b17b14b17b13b171b11f12b12b11b12b13f13f131b11b238a12c18a218a218a238e22 8a228b182f18b18f18b1811a218a11b218b18b18a218a218a218e2181f18b18b18b18f18b183b1 8b18f18f18b18b18b18b18b181a12b13b12b13b228e238a12c18a238a111c181b18f18b18b181b 181b14b1cb14b18f14b1cb18f1cb1c1b14f1cf18b18b1cf14b141f14b18b14b18b1cb18b1cf14b 1cb1c1b18b18b14b1cf18b1cb1cb18b18b181b14a228b18a228a11c18a23ca23ca21ca214a131b 21cb1ca218b1ca218a11b21ca11c14a218a2141a11b11c14b1cb14e21ca218e218b141b1cb1cb1 4b14f18b1cf1cb141f1ca21cf14e21ca21cf18b1ca111c18f18b1cb18b18b1cb14b1cb14b141a1 9b2f4a16b2fca2c4a298a2fcb18b14a23ca2cc359132831f85c31f85e31f858010000052533401 000003f0000007a10000390509e000200100e2156b9cfb7dff3ffdf8f2e3c9a69a61d1bb1ab12b 1ea21ea21ba21fa21da21fb14b191f11b218a218a11b218a218b18a218a11c181f18a11b218a21 8a218a218a11b218f181b18a258a12b268a13b14b278a278a268a248a2587f18a218a111f218a1 11818a2181b18b18a218a11b218a218a218a111818a2181a248a11b278b18a218a248a278a11b2 78a238a2181b14b14b14f14b12b16f12f161b1cb1eb14b14b1ab16b1eb1ab14b18b1a1a27ea2e6 a2d8a2dca276a28aa2fea21ea2f4a25ea1c1c181b16b12b1eb1eb12b18b12b1c1f12b12b121b12 b122f16b14b12b16b12b12b16b14b16b12b141b16b141b14b1eb1eb14b1cb1a1f14a21ab16a21c a212a21ca21ea214a214a21ca21e1a232a236a224a218a21aa21aa23ea11b238a226b121b12b16 b14f12f16b12b14b16b121b14b14b14b12b12f16f12f141b1eb16b12b1cb1eb1eb1eb12f1cb161 f121b12f12b12b12f121f12f16f16b16f123b12b12b12f12b12b12f1210b14f14b14b14b16b16b 12b121ea118019801bc18b4801daa1f8010801eb64801881b18f18a218b18a11b218a11c18a11b 2181a248b18a11b218a14b258a278a11b16b15c181a238a238e228e228a238e13c18a131c18b18 1f18b18b18f18b18193188a1fb248af88015801d801f80821fab198013801181b181b181b18b18 b183a12f12b12b12b12b12b12b12b121b14b12b18f16b12b1eb1cb16b1e4b12b12b12b12b12b12 1f121b12f12b12f12b122b121b12b1211f12b12f121b121b12b121b131f11b13b13b12b12b11b1 11f13b12f13b12b13b13b13b11b121b19b1bb14b17b15b1ab1fb1ab1eb11b141c18b18b181f18b 18b18b182a228e228a11b228a238a228a11c18a2181a12c18a268a258a13b17b278a278a15b218 a268d3418b18e218b18a218e218a218a11b113c18b18f18b18b18b181e13b238a218a268a218a1 4b278a16b14b248a171a212b5e801ba212b1cb15a61f801fb2d8a18a31581b181f18b18b18b18b 18b181f1ca12f11c18e23ca238a22ca13b13281cf14b14b18b1cb14b1cf181b18a214b18a218e2 14a21cb18a214b1cb181a274a17b24ca234a244a23ca27ca12c18a274a151b264a22ca268a27ca 13b11b27ca26ca274a142818a218a214b1ca218e21cb1ca21cb141e214a21cb14b1cb1ca218a21 cb1ca21cb1ca2181f14b14b18b1cb14b1cb1cb18b1cb1c1b14b1cb18b14b14f1cb18f14b181f18 b18b1cb14b18b1cb14b14b1cb1c1b5401bca2d4a2a8b1ca1da31fce2dca1bb1d1b6680194a2eca 8a401fc01b31fca264a2b4a6b401b41a11b214e11b21ca22ca23ca234b18a21ca2281a214a214b 18b12e21ca21ea21ca21ca21ca2186d5540fc25f01000000000000000052533401000003780000 06b40000390509e000200100e2166b94f8ff7fdffdf8f1e3c9a69a61d17b14b14b15b17b11b17b 13b14b11b1428182b18f18f181b18b18b181f181f181b18a228a13b13c18a11b238a12c18a11c1 81a12b11b228a13b218a238a238a12b228a12b2282f18f18b181b18b182b181b18b18f182a11b2 18a218f18a218a218a218a11b11c181f12b12b121b124f12f12b12f12b12b121b14b12b12b16b1 4b12b161b16b121f12b12b121b12b12f122f12f12b12b12b12b122b1ab1ab1ab14b12b1cb1eb1a b1ef162f14b16b12b16b12f161f12f12f12f12b12b123f12f12b121b12b127f12b12f14b16b14b 16b16f16b161a11b214b16b16f18a21eb16b1ab1ea21a1f12b16b12b16b14b16f12f121f12a218 a11b21cb12a21ea21eb1aa212b121b121b12b12f121f121f12f121b12b12b12b12b121b1ab18b1 6b18b18b18b1eb1eb14b1220c18b18b18b18f18b1810f181b18b18b181b182a218a218a11c18a1 1b218a218a218a2181e12b228b18a11c18a11b238a11c18a11b121f14b1ca21ab18b1ea214a21e a11b212b18b141a234a21ca216a12b22ca21ca27ea234a27ca12b2787f14a21ab1ea238a214a22 ca23eb14a216b18a131b212b1ea21cb1ca21ab1ea21eb18a216a214a21cd318eb12b14b16b1cb1 6b18b1eb14b18b1eb161b1cb16b1eb14b14b1ab1ef14b14b141b11b11b11f11b11b11b11b11f11 1b11b13b13b11b12b13b13f13b11b121b16b14b17b11b11b16b17b17b11f16e25cc18f181f18b1 8f18b181a218e11b218b18a11b218b18a218b18a2181a238e12c18a238a12b238a13b238b18a23 81e218a218b18b18b18a218a11b218e2181b181b18b18b18b18b183b18b181b18b18b18f182a11 1c18a218a218e218a2181e17b258a218a248a248a278a278a11b16b14c181b1cb1cb14f1cb18b1 cb14b18b1c1e218b14a11b218f1ca21cb1cf1cb1c7e2d4a294a21ca2b8a2dca19b2fca29ca16b2 6ca1d1c1cb1cb14b18b18b1cb1cb18b14f181a214b18b18a11c1ce21cb18b18a2141e11b23ca11 b224a234e23ca11b13b12b23c1b1cb14b18b14b14b1cb1cb1cb18f141f14b14b14f14b14b14b14 b14b141a21ca11f214a214e21ca214b14a11b2141f14b14b141b14f14b14b141b1cb1cf1cb18b1 8b1cb181b141e218a21cb18a214b14a21cb18a21cb1ca2181b12b12f12b12b12b121b12b122b23 f6e2fc7831f82831f8392c86b0100000000000005253340100000310000005e70000390509e000 200100e21c6b9cf8fdbf3ffdf8f1e3c9a69a61d17b14b13b17b15b16b17b17b11b11b1116c18b1 8b18b18b18f18f18b18b181a16f268b18a278a248a278a17b14b17c181b18b18b18f18b18b181b 18b181a13b218b18a218b18a218a238a228a218a238a121c18b18b18b18b18f1811f121f12f12b 12b12b12b121b12b12b12b12b12b12b12b12b123b12b12b12f12b122f16b18b1ef1eb14b1eb12b 1ab12b1e1a21aa21cb18a212b14b12a21ea214a218b1cb161b12b12b12b12b12f121f121b12f12 1b12b12b12b12b12b121b12f12b12b12f12b12b122b1aa214a21ab18b1cb12a21ea21eb1ea212b 1c1b14b12b16b16b14b14b16f12b16b121b1eb1ef16b1ab1cb1eb18b18b16b141b1aa216b14b14 a214a21ca21ee21ca212b141b12b16b16b12b12b12b16b12f12b141b121b14b16f16b12b16b12b 161f122b121f121b16b12b18b1ab14b1eb1e1b1ab1a3818b18f18f18f1810b18b18b18b18f18f1 8b18b181e11b238a218a11b11b238a238a13b238a2281a11b11b218e218e218b18a11b218a1111 818b18a11c18a218a218a218a218a1116f122f12b122f14f16b16b14b14b16b12b12b14b121f14 b14b12b16b12b161f141b16b14b14b12b12f16b16b14b16b1210b15f1bb15b1bb1fb12b16b1cb1 624f11b11b11f11c18a218e218b18a111c18b18b18b181b18f18b181e228a228a218a238a238b1 8a238b18a13b228a111b218a218a11b218a11b11b218a2181a2181a11b218a2181b18a218a11f2 18a2181f18f18f18b18b18b18b181e11c18a218b18a228a11b238a238b18a228a2382b14b14f14 b141f1479d23e65c0c6630fa2ff1943fffc13f43fff2814f18b14b18b1cb14b14b14b184b14f14 1932b4e23ca224a12f228a12b23ca23ca13b13b2182b1cf18b14b1cb14b14b1cb181a214b1cb18 a218a218a11b21cb1cb14b1cb1c1b1cf14b181b1c1b14b1c1a12b264a25cb14a244b18a27cb18e 238a27810f1cf1cf1cb14f14b181b14b14b14f14b14b14b14b142f12b12f12b12b121f122c15e2 1f182fcf23f182fc4c1e1b27e6e23ae2327d01000000000052533401000003d80000077e000039 0509e000200100e21d6b91fe7e7fbffd79f1e3c9a69a61d13b16b17f11b13b17b11b17f141b111 818a11c18b18a218f18a218a2187e11b218a11b111818a218b18b182b18b181b18b18b18b18b18 f181a2f8a1fb1eb278a228a2b8a2f8a248a238e121b218e238a218a13b12b238b18a12c18b187e 21cb12b1ca21ca21ea21ca21ea21ea21ca216a2181b18a21ea218a21ea214b16a21eb18b18b12a 2141b12b12b14f12f16b14b16b16b141b12b12f121b12b12b12f121b14b12b16b12b16f16f16b1 2b121b1ab14a23ea222a238a12b23ea232a23cb14b1c1b14b12b16b12b16b16b16b14f121e21ca 24aa26ca236a254a25aa27ea236a25ea27ca2721b1cb12b12b14b1eb16b1eb18b16f181b1eb14b 16b1ab14b1ab1eb1ab1eb12b123b12f12b1211b1cb1ab18f18b16b1eb18b18b16b181b14b12b12 b14b12b14b16b12f12b167f12b12b12b121b12f12b12b127ea1080128014b218a228a63801f8a1 6a21ec4801a1b248a17b14c18a15b238a278a11b268a278a131b248a11b228a268a248a14b278a 248a14b218a2581a11c18a218a218b18a11b218a218a11c18b181a12b11b228a13b11b12b238a1 3b12b2281d3238a598034b6a80258a1aa63f801aab158030802581a1fb2b8a288b18b18a2f8a2f 8a288a278a1ab2b82a1eb18b12b1ab14b1eb18b1cb12b181b1cb1eb16b18b14b12b1eb12b1eb16 b161b12a22aa232a23ea228b18a23eb1ea224b1eb141b12b12b12b12f12b12b1211a21ab1cb16a 21ab14a212a21ea212b1cb12a21c1b12b14b16f16b12b16f14b16b121b13f11b13b12b11b13b11 f12b131b1fb16b1cb15b16b1cb1fb1db1bb1d1f14b13b11b16b14b14b17b14b17b11b141c18b18 f18f18b181b181e15b248a11b17b278e278a16c18a14b2381a218e218a218a11c18a218b18a218 e2181b18b18b181b18b18b18b18b181e12b218e13b228a228a238b18e12b11e253b11b11c18a21 81a218b18b18b181e12b238a12b11b13b11b238a11f121f218a11c18a218b18e218b18b18b18a1 11b274e1ab1cb244a2b4a2fca25ca2fca2a4a2d81a22ca248b1ca21ca13b25ca27ca238a26cb1c a22c1a22ca21ca23ca22ca12b228a23ca22cb14a234a121c14b142b14b141b14d225b16b12b2f4 a2b8a21ca224a2fca2e8a21ca264a1d1b268a95c015c016bf54015401fc01b408617c01ca31841 b1cb1cf18b14a214a21ca214b1cb18a2181f1cb1cb14b1cb1cb1cf142b14b1cf18b14f1cb18b18 b1cb141b141b14b14f141b14b141b14b18f14b1cb1cb1cb18b142a238a218a23cb14a214a13b23 cb18a23ca11b2341b14b142b141b14b141f12b121b12b12b12b1210a1125e01000525334010000 03a0000007030000390509e000200100e21e6b94ff7c7efffdf8f1e5c9a69a610911b218a218e2 18a218a218a11b218b18a2181b18a11f11b228a11b238b18a238a13b1228182b18b182e11c18f1 8a11c18a218a11b11f2181f18a278a248a12b268a278a278a14b14b2581a11f11b218a218a11b2 18a11b218b18a112c18b181b18f182b14f16b12b12b12b16b16f1619311eb14e212a216a21ca21 8a21ea218a21ca218a2141b12b16b1cb16b1cb16b1ef1eb1ab1e1f1ea21ea21cb12b16a21ea21e a21eb1eb181f16b14b16b16b12b16b14b14b14b161b1cf1ab14b1cb1cb1eb18b12b14b182b12b1 2b12f123b16f14b16b12b12b16b161b161b16b12b16b14b12b16b16b14b14b12b161a258a24ea2 72a246a27ea27ca27ea252a22aa212a2521b16b1eb16b18f1ab1ef16f1e193128aea6010c018c0 1ccafb601fe017401d4087118010c1f16b16b14b14b16b16f14f161f12f12f12b12b12b12b12b1 22b12f12f12b12b12b12b121b161b14f12b16b16b12b14b161b14f16b16b16b16b16b16f12b141 b11b218a111818b18a218a218a218b18a2181b18f18f18f18b18f181e11b218f18a11c18a218a1 1f11c181e218b18a13b238a218a238a12b218a1110c18a228b18a228a238a11b238b18a238a218 a2281a11b16b248a14b13b16b278e17b238183194f14b16f12f14b16b16b163b12f12b12b12b12 f12b121a218b16a11b21ca218a218a21ea11b216b14b1c1b121f12f12f1210e222a236a22ee226 a212a23ea13b23ee21c1a21eb18a21cf1eb1aa21ea21aa214b14b1e1f12b11b15b12b14b17b14b 13b16b141a219b13a217b16b18b1ba21fa214a21de2161b1db1cb14b18b1cb12b1fb1ab1fb112c 18b18b18b18b18b18f182a238b18e228a218a13b238b18a132b1db2c8a2d8a248a1db248a2f8a1 bb11b19b2e87f18a111b11c18a218f18a218b181a13b258a16c18a268a228a278a11b16f2781a2 38a11b218a12b11b218a238a238a218e1110c1cf14b1cb1cb1cb1cb1cb181e234b14a238a234a2 3cb14a23ca22ca13b23ca11a33247b14f14f14f141b141e258a17c14a12b16f27ca214a12b27ca 21c2a214b18a214a11b21ca218a21ca21cb141b141f14b14b1cf1cb14b141a2a4a21ca284a2e4a 2dcb18a2fca19b13b284a1a1c18b1cb14b1cb1cb14b1c1f58020cf14b1cf14b1cb14b1cf14b181 f14b141f14b14f142a21cb14a214b18e218a21cb18b14a21cb181b14b142b14b14b142f12b12b1 2f12b12b12b12f124d24c4c2fc13d27e7d01000000000000005253340100000020000000070000 09530d0000200100e302112c0300000000005253340100000020000000060000047e0040002001 00e40243f0200000000000525334010000002000000006000008dc020000200100e502106f0000 00000000525334010000002000000005000005100f2000200100e60950c0000000000000525334 01000000200000000600000731090000200100e70071c010000000000052533401000000200000 000600000175040000200100e80013d01000000000005253340100000020000000060000018902 0000200100e90014a010000000000052533401000000200000000600000339090000200100ea04 31e010000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000023560301010000000100000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000020dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000 00000000000000000000000000000000030dc400c8012c00dc001e07ec00bb00f000d000180e8c 00d2016800e500240000000000000000000000000000000000000000040dc400c8012c00dc001e 07ec00bb00f000d000180e8c00d2016800e5002400000000000000000000000000000000000000 00050dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000000000 0000000000000000000000000053423100bdd5a46cc218bf26ad36bbb21e2349657abcb01dbe58 6626bdd5a46cc218bf26ad36bbb21e2349657abcb01dbe58662650420501000000005046020000 788410015254045652544e534f020000494efe0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046007884740052540456535243494e6400000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046030000007884300052540456524d4c5644023031504e0730304b56363237534e0c59413139 3332303936393531545604303030345046010078849c0152540456574d4c56440230314f430400 000308464f1102ffffffffffffffffffffffffffffffff23495401010454000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000494e140131393336333530303030323432303134000000 5046030000007884ac0052540443525030564402303145442101000000000000000000000f30d0 002eab620497000000f06018e108828a00598054450b0130323033514651415346444405013032 3032535403010000444e4901240000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000049510b0100000000000000000000504603000000788448065254044c5250345644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100c2013a00d6002407ed00a600 a800be00180e6b00cc016a00e00028000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000054431314000000000005000000050000000500000005234d010103000002bd 032001790000034003a201ec0000031403a201a7000002ce03a202e7000003c304260251000003 97042602f100000353042603c8000002ee0426049f0000044704aa026a0000041b04aa03660000 03d704aa040b0000037204a90537000002dc04a906a20000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52503556440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00c2013a00d6002407ed00a600a800be00180e6b00cc016a00e000280000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191e01f9191d01fe191e01f6 191f01f3234d010103000002bc031f012f0000034003a201de0000031403a20267000002d003a2 033e000003c30426021a000003970426035f00000352042603cd000002ef042604920000044704 aa02710000041b04a9036b000003d604a9048b0000037204a90569000002db04a9060c00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525036564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100c2013a00d6002407ed00a600a800be00180e6b00cc016a00e0 002800000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191e01f7191e01fb191e01f8191f01f2234d010103000002bd032001560000034003a2019800 00031403a2026c000002cf03a20325000003c30426028500000397042602e40000035104260410 000002ee042504cc0000044604aa028a0000041c04aa0348000003d704aa04630000037204a905 6e000002dc04a90648000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000434801004951090200000000000000005046020000788448065254044c5250435644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100c2013a00d6002407ed00a600 a800be00180e6b00cc016a00e00028000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000544313140000191e01f9191e01f6191e01f8191f01f3234d010103000002bb 031e01510000033e03a101d60000031203a1024e000002ce03a1031b000003c20424023a000003 96042402f600000351042403cf000002ed0424049f0000044604a802830000041a04a803500000 03d604a8047c0000037204a80580000002db04a806390000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52504456440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00c2013a00d6002407ed00a600a800be00180e6b00cc016a00e000280000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191e01fb191e01f9191f01f3 191e01f8234d010103000002bb031e01790000033f03a101d10000031203a1024c000002ce03a1 02fd000003c204240235000003960424032500000350042403ac000002ed042404bd0000044504 a802990000041a04a802ce000003d504a8049f0000037104a80530000002da04a8061d00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525045564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100c2013a00d6002407ed00a600a800be00180e6b00cc016a00e0 002800000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191d01fe191e01f8191e01f7191f01f2234d010103000002bb031e015a0000033e03a101ca00 00031203a10262000002cd03a10320000003c10424022600000396042402e400000352042403ed 000002ed042405210000044504a802710000041a04a80325000003d504a8045b0000037104a805 5d000002db04a8063b000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000043480100495109020000000000000000504602000078842c005254044c524d305644 02303254430516000000004d430400000000494e08000000000000000050460300000078842c00 5254044c524d31564402303254430516000000004d430400000000494e08000000000000000050 460300000078842c005254044c52493056440230325443051600000000494e1000000000000000 000000000000000000504602000078842c005254044c5249315644023032544305160000000049 4e100000000000000000000000000000000050460200007884b8005254044c5750345644023031 2332440001040f0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000233344000104 0f0000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000494e13000000000000000000 000000000000000000005046030000007884b8005254044c57503556440230312332440001040f 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000002333440001040f000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000494e1300000000000000000000000000000000 0000005046030000007884b8005254044c57503656440230312332440001040f00000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002333440001040f00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000494e130000000000000000000000000000000000000050460300 00007884b8005254044c57504356440230312332440001040f0000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000002333440001040f0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000494e13000000000000000000000000000000000000005046030000007884b80052 54044c57504456440230312332440001040f000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000002333440001040f000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000049 4e13000000000000000000000000000000000000005046030000007884b8005254044c57504556 440230312332440001040f00000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000002333 440001040f00000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000494e130000000000 00000000000000000000000000005046030000007884d8005254045645523056440230312349c4 000104300000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000504601007884d8005254044d45523056440230312349c40001043000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000050460100780003ca8f47b6f3 61985975001fffffffffffffffffff001fffffffffffffffffff001fffffffffffffffffff001f ff73ff7f5faffffe970013c889c766e861985975001fffffffffffffffffff001fffffffffffff ffffff001fffffffffffffffffff001fff73ff7f5faffffe970c16d717318e036fdd1be66effff 377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6cf0c12d717318e 036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2a e6cf0c06d517518e036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef77 77a6a82c0c869c6af6cd0c52e71211ee036ddd1be66effff377ffdab15e55fb4009a31771ff7bb 7ffddfbf6efcef7777a6a82c1c869c2af6ed0c56e71231ee036ddd1be66effff377ffdab15e55f b4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6ef0c52e71231ee036ddd1be66eff ff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2ae6ef6918590545 d2099333385d17691c590545d0099333385d577912dd01457a7f02d359631d7916dd0145787f02 d359635d4e392e346b6c688f0a597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbcf6dbda4eaf102b5b419a308eab998893a220e225d428ee5bb0 012d1618f2003ca860e9f6a4b09e4b626fffbedefbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa09bf1a86e3d2e347b6c688f0a597b27b5bee6f0f24b05149462 069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbcbef3dbce3b80785a234306dc 91fdc303a20002259698285bf8c1393280382fbd3e9cb24dcea7988b7067bf9edefbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa09bf1a84e212c342b6c688f0a 597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bcbedbdb4ead18aab2469e84ba8b7da687be201900dc227e734801aa1621fb43d31828abe6edaf 9c0b7267ff9edefbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a0dbf9a86fb526307bec608e0a597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbcb6dbdb6ead10e3da688982b0811e6f2d301c7001838024b227 09ae2622d126090e6af8ef449a3a5bfa67bf9efafbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa2dbf9a04fb926306bec608e0a597b27b5bee6f0f24b05149462 069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbcbef3dbee2b92b1530bca06cc 89984c979ad44a0680be56d9bb8e03bc9fb2268892e460ef4cb2beab6267ff9efefbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa29bf1a06fbd26307bec608e0a 597b27b5bee6f0f24b05149462069906f8f23cee9a1edbe969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bef6bbff4e1f92ec616e6b1a00d98fc80b30f4d82fc7945e79400f02b0adc84144288e0b6440a4 3889682fbfbefafbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a29bf1a0000f1604c89bdc07fa5793001506ddf37c3adeb719b9001fffffffffffffffffff001f f677eba7ac607d7d8250fae7fa59ea8c7d55aa2b15d5e87a9548aabf7fefff55ffefffffeaafff ffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffef ffffeaafffffefff55ffefffffeaafffffefff55bea75fefaa876e38035910b4eeba3734b24232 67956ed088a71514240714b888a2f0cfef999bfb3777feeefffddfffbbbfff677eda8e1f5d58b6 22614f255372b04bbd13ef6cffff777fffbb7ffddfff6effff777fffbb7ffddfff6effff777fff bb7ffddfff6effff777fffbb7ffddfff6effff777fffb93ffcccde6eece32801d828852128b41f 76d87246785d979f2b629501801484c26405427b310117ea1e967e59e73213039f022a0cb15450 e2d361b5ab175012c0a40c11d23b33c964e74a57e17465fa4976ed43b72240b855d7b26b709432 0830734e09f7553950eb371820927c7cd6ac1b01df34f72e670b35330d53d389501902e5556d44 3a2b5162710844c5364547345a400d35a61e25b928738a1a044317a14440d37d03b1082767424f 68d2047046247824d5154f4ae2f175b59612619e66610e80d24861bf62c5ee73f242e6c4453644 6b5241264b36f60e1881516a543a260e56f4c72874d063b03f27457005ce6d407111927a516838 d13335a3c803b0b3351d0ed04b4d158b11843e63a164b5362844dc6a517b752d08121b2001ae00 76aa062436c7fb59072a1a774f21497085ce6d407111927a516832f0d72976d06390b927f56e15 2675167178e39011ec26e4e600f23873134135705853152497f94324e712a61837b240145a1272 74778518734e7860ef1a905256683270c72976d063b03f27457005ce6940f309817661363833db 7cf5e830057960f04a011248c2c309d3f167113e80e765131f4b30f415ac1a359449c15f69943b 23af24e6052875d860304f25453005ce6d407111927a516830e0f5255616634739527c72047904 513428d63644392477bf50141b2bf57a26383801471996ed7054e427360a44bb6913491a73c901 495805ce0c405111927a516832f0d72976d063a03e37b76205882960396aa1ff02e83cc4ba18a4 b003d21b51fe6031276c66f922679c13e276e3d621715e705624760b4ef7677422437a1372166c 3270c72976d063b03f27457005ce6d40f119b07670303af4d731c7a65190bf213c74054a5421cb 636252371b66e18335a14958f616242e76d77a2ca5113285a2018d2c104b69a60333075b30a31a 92426dc11f003190670b68218d68034210528d60ef6ab0a84101296b7114423930348930510253 339851912ce2c129b7dd73469312067c84f67103be50106d42e362050220465418b3f837906c72 ea4d873710504341f52c116a15d39d00e39a773a2c456440524b13702815c37011e95d86085910 0d538d60410369545921b18a14fa4280210c26f103a52936f432c46d09207a2154c042c924d42c 4136997a545855ca60b5ee5dd42c72643840b40611862db6b012043c233a0c205d6007d62ab0c9 07270e23797987d12a77f7617f0ef3ff7cf7df7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7fe77 cf7ed77a6d63b7114563752d64850b7453207202f931a20292e12dd73813577465f03a670e3576 f85af7a204e520e5911065f82ac2f9062376e0fa0c76e858734507bb1695cd69b1fc39b57d52cf 04b1803971f4115155400f2a872874d39352220a45dc36707e20242d69b2d773846c44976d5078 71a7d354a60af52565309c71155d056156968208f26213e27262161605814d24b762e11304f270 456350d0e111343b25841cf7ac24a1cd18116a50a35c533e4cd384190469470452118e2df2ee5b 746900ad20158f31a4312214be17f638a49f0c811212c10a11c75ed2ec2c838f2ad58d75af7ec0 081d846129f11b61220ec62445e478412264436b34725701d5cf20115c50f472b3935424c42106 73535634b1c21df58b4041a715260e218f04c65e1b90b213393410df79c18821261c068476b550 61840e7a14d4264b4033d075347679855810406a546f38579333d4d5315940f2b135500b716469 56245a929f0d334a58f72e03847834224d549233c65452960ae68758b7506176e945844cf3d931 65107be46831821c50f93593f059d58d44556a15e461c70c10d74b662702972551b51333a41343 d97830c43c24bd5991a5435a2657d449a1624822f7628a3004ed48542a49705b51990ab7ef3c57 9b7102c9336b4c221c7033ea2816db075618f5b609108429e44f73f118b76f2c65a029d038744c 62e3b44926d810a7e6662f40a41464e20c1a239a61ba6477c814628661f6ad737d1c668124d026 32070c728970d0ed01175161b49e73231c77a35017ac2375df57b212a0126994d878559a069300 d1f33da417280281259f6223851872fb0a11de43136aa1e575b4634bf48d633256d60b79d41a00 c7a161e218d34f4036d3400157348f4ce6066db6e179037d34136c26b04526d353d43d26a53a86 4931d25323b035227c78a4097c14bd33537f31fb24141a1d04e8381798646532669641c4b96893 a146365247ab4016217803d7263b1280763cb3c17852e941223c214255a1d522068b517b32b1dd 21a65a19645f071402757f11e7535034b501f94622515d357e7367e861857a20603ca4fa50830b 279034f5c45193a029c0f114a97281411c45d97100da16a662d79d09e00a68308665fb7c072e38 e44b78a3a542a220f61f5122317aa0a367055420dc5436542ba01522902634766df2e20906a673 5e64333e7c50511101df55525a223a11418b404152011232e49644466679f5e613300074bc6063 da7256f9214b4664cb19328c72212c357d7ad7f81535466bb0bd60f73ab6411ca4715a224d3523 32f0c630935f4b079b574c7ce1366c474d03376e36934ec5bd2ca3ef2ac67830a056116279572f 4b966227893ec5a168f4b04b754925950291034572604175eb02941c508b20c4f03aa65f13a120 12fd0022e77185bb66ea1af7c411a5ba1964e015977af13b2ca42918067605485e77823c34c411 9507137834a2a61c850d1324db000c1c90b27547e138a467757f1861011d114c11c27d622326e6 e52855c911951e622048a3fc44e0c34870b814447491d210b50252074472b44266476dc03d4837 00600f1683110d572c68e28210c004d53a5c76840902fc66ff4a81686891a14120e542c67e67e7 50f24629a18746440c91ac2c96b6238424510716f5c24c77db13e358265f7ed76000a1b3731147 67531c81c564b29150648446850494f550176479a49922252ad2077542a72a27b0127f60973a2c d3a60a300224de1447ce2456bb69b54551ba1eb48718866d59f135737c38148341f38c51071622 1722a10e4cd3bf12f79526485063d645761422f27d158602125214e56703c450350b3a83882147 bf3354d110c046534015d46f29918877d23c127951330c50a39a15500c26932835945af77d275c 1c00b37885b4627736170c26e3673ca15203d54337b126207759a06e1245ac02a13ec3f36c06b0 1992be75735c55260c814458671850a84e03ec59b58d1a426850373005e911533f39b2ca55f35e a4f010524d53b53910d902e10010f19473420013762e56cd6854de2085de03d04026b600e71d7b f41d55be4e72fb7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff73f7fc57cf72c77b6d30e97b33df77ef5ef7fb7137fa73019b41197a70f618f531603091054e 30513344618f71204101d75a40491933565ad39524d80e75484511b309516d017b58505369f6a0 40200800dc5ab28d28c31841a5be4206f264017a19319d44e96633131426800932c50631023e7d 0221118a1fa88be808a9a901bd0ed1c4e8ae485ca0ee4505671e40080bec64c9fb11c68853a300 21353bec7a04a18ea4adea8153876b5b01ff43f932c00bf509e22651d00f2feae802970709e055 31860fa8820b7d6ea6661089c0819d200de1808ed5115f068e8a00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000 prop: ibm,ccm-node-id size: 4 val: 00000000 prop: ibm,hw-card-id size: 4 val: 00000000 prop: ibm,hw-module-id size: 4 val: 00000000 prop: ibm,mem-interleave-scope size: 4 val: 00000000 node: chiptod@40000 prop: reg size: 8 val: 0004000000000034 prop: compatible size: 37 val: 69626d2c706f7765722d63686970746f640069626d2c706f776572372d63686970746f6400 node: nx@2010000 prop: reg size: 8 val: 0201000000004000 prop: compatible size: 27 val: 69626d2c706f7765722d6e780069626d2c706f776572372d6e7800 node: pbcq@2012000 prop: reg size: 24 val: 00200102200000000020010905000000003c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000000 prop: ibm,hub-id size: 4 val: 00000001 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333200 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 7777777777777777777777777777777777777777777777777777777777777777 node: pbcq@2012400 prop: reg size: 24 val: 00240102200000000024010905000000403c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000001 prop: ibm,hub-id size: 4 val: 00000001 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333200 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 6868686868686868686868686868686800000000000000000000000000000000 node: psihb@2010c00 prop: reg size: 8 val: 02010c0000000010 prop: compatible size: 31 val: 69626d2c706f776572372d70736968622d780069626d2c70736968622d7800 node: xscom@3c8000000000 prop: ibm,chip-id size: 4 val: 00000010 prop: ibm,proc-chip-id size: 4 val: 00000002 prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000001 prop: scom-controller size: 0 val: prop: compatible size: 27 val: 69626d2c7873636f6d0069626d2c706f776572372d7873636f6d00 prop: reg size: 16 val: 00003c80000000000000000800000000 prop: ibm,dbob-id size: 4 val: 00000000 prop: ibm,occ-functional-state size: 4 val: 00000001 prop: ibm,module-vpd size: 65536 val: 000f17ba5598401f3bd42b84280052540456484452564402303150540e56544f43d500370074 01d5b85d0050460800000000000000007884700152540456544f435054fc56494e49d500ab0190 00b1b8240043503030ff003b02406161a050185652544eff007b6314011ca0450056535243ff00 8f647800fe9f1e0056524d4cff0007653400f19f0d0056574d4cff003b65a001899f6800435250 30ff00db66b0005d9f2c004c525034ff008b674c06ca9d93014c525035ff00d76d4c06379c9301 4c525036ff0023744c06a49a93014c525043ff006f7a4c06119993014c525044ff00bb804c067e 9793014c525045ff0007874c06eb9593014c524d30ff00538d3000df950c004c524d31ff00838d 3000d3950c004c524930ff00b38d3000c7950c004c524931ff00e38d3000bb950c004c575034ff 00138ebc008c952f005054624c575035ff00cf8ebc005d952f004c575036ff008b8fbc002e952f 004c575043ff004790bc00ff942f004c575044ff000391bc00d0942f004c575045ff00bf91bc00 a1942f0056455230ff007b92dc006a9437004d455230ff005793dc003394370050460200007884 8c0052540456494e4944521031302d5741592050524f432043554f44464e073030465835313850 4e0730304658373430534e0c594131393332303936393530434304353445384845043030303143 54040000000048570200014233060000000000004234010042370c000000000000000000000000 5052083500500122008001565a02303143450131504602000078843c6152540443503030564402 3031504741010000f180f4000000f000000000000000e100f70000000000000000000000000000 000000000000009300f300f30000000000000000000000f300f300f30000004d4b050100000000 2347043001525334010000002000000005000001f8080002000100a20817e00000000000005253 340100000020000000060000023d040002000100a00821f0100000000000525334010000002000 00000600000136001002000100a30811d020000000000052533401000000200000000600000113 0c0000100100a40810c0300000000000525334010000002000000006000002f6080002000100aa ff27d020000000000052533401000000280000001700003190040002000100a5ff47816200a162 01916143e0000000000052533401000000200000000d00000ba5010002000100a8ff12f2181228 01000052533401000000200000000500000328002002000100a9ff31a000000000000052533401 0000002000000005000000df001002000100a6ff6f030000000000005253340100000020000000 0700000e26020002000100a7ff161902000000000052533401000000400000004d000012520600 00100100ab1479121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e12 64a1877b02000052533401000000400000004d00001252060000100100ab1579121f141f181e11 1c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000 400000004d00001252060000100100ab1679121f141f181e111c12f18e12f18e12f18e12f18e12 f182e18e12f18e12f18e1264a1877b02000052533401000000400000004d000012520600001001 00ab1c79121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a187 7b02000052533401000000400000004d00001252060000100100ab1d79121f141f181e111c12f1 8e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000400000 004d00001252060000100100ab1e79121f141f181e111c12f18e12f18e12f18e12f18e12f182e1 8e12f18e12f18e1264a1877b02000052533401000001200000020e00000e1309e000100100ac14 1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3 f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0 4c11a37c1d8401f04121d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c1 2a3f82d6803e08c11a67c10481c6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d640 1f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d03000525334010000012000 00020e00000e1309e000100100ac151f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d8401f04121d6803e08c12a3f82d6803e08c12a 3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a67c10481c6401f04c11a37c1d6401f 04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d640 1f0430d0300052533401000001200000020e00000e1309e000100100ac161f6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d8401f041 21d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a 67c10481c6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f 04c11a37c1d6401f04c11a37c1d6401f0430d0300052533401000001200000020e00000e1309e0 00100100ac1c1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d8401f04121d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f 82d6803e08c12a3f82d6803e08c11a67c10481c6401f04c11a37c1d6401f04c11a37c1d6401f04 c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d03000525334 01000001200000020e00000e1309e000100100ac1d1f6803e08c12a3f82d6803e08c12a3f82d68 03e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d 6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d8401f04121d6803e08c12a3f82 d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a67c10481c6401f04c1 1a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04 c11a37c1d6401f0430d0300052533401000001200000020e00000e1309e000100100ac1e1f6803 e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d68 03e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d 6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d8401f04121d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82 d6803e08c11a67c10481c6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c1 1a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d03000525334010000002000000007 00000a71080002000100b202123c010000000000525334010000002000000007000035c7040002 000100af02656903000000000052533401000000200000000700001571020002000100b002253c 01000000000052533401000000200000000500000734010002000100b10271d000000000000052 533401000000200000000600001694004002000100ae02264d0000000000005253340100000020 00000006000005c7004000100100b4025690300000000000525334010000002000000006000003 c3001002000100ad02368030000000000052533401000000200000000700000ceb0d0000100100 b302147a03000000000052533401000000200000000700000ed5020000100100b502166d010000 00000052533401000000200000000600000217080002000100bb0920d030000000000052533401 000000200000000700000841004002000100b60910280100000000005253340100000020000000 0500000330002002000100b70931c0000000000000525334010000002000000006000001b60010 02000100bc0915d020000000000052533401000000200000000700000a630f2000100100bd0912 380300000000005253340100000020000000060000033e080002000100c00031f0200000000000 52533401000000200000000600000495040002000100be0044d010000000000052533401000000 2000000006000001b2020002000100c10015c02000000000005253340100000020000000060000 0457010002000100bf0042d0300000000000525334010000002000000006000001860010020001 00c200149020000000000052533401000000200000000500000570090000100100c30053c00000 0000000052533401000000200000000600000397080002000100c70434d0300000000000525334 0100000020000000060000025f020002000100a10822f030000000000052533401000000200000 00060000041f040002000100b80940f03000000000005253340100000020000000060000041f02 0002000100b90940f03000000000005253340100000020000000060000040f040002000100c504 40b0300000000000525334010000002000000006000002ab020002000100c40425a03000000000 0052533401000000200000000600000543010002000100c6045280300000000000525334010000 002000000006000002b5090000100100c80425d010000000000052533401000000200000000600 0003b1010002000100ba0935c01000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000002352902f01525334010000002000000005000001a00c0000200100e0 08158000000000000052533401000000380000003d00000e46060000200100e1141192fd582845 3d31f810b31f85b27e2c272e23f20a23f682fc11c27e4f02000052533401000000500000006700 000e46060000200100e1157831aa44a26b2a28d2192544922110a2fc3d3d543c5fc01cc31f8e2f c2822c11a41ffed2fc1e23fe31a81f31482a5a40fc6b0200000000005253340100000038000000 3300000e46060000200100e1165492cb3b4102710f2fc10923f34c27ee22b1f2fcc31f817d0200 00000000000052533401000000400000004e00000e46060000200100e11c5294102b2c2ad2f2fc 15e2124b47ff86a5fc01c1f4403ce2724e23f3e397f7c27e12f23f10c020005253340100000040 0000005000000e46060000200100e11d51e62049943f27110d23c5f221e31e814f1a2a32881282 3f7e2884f2fcb57e02e1d21ee21d10c02052533401000000400000004100000e46060000200100 e11e21c314453c23ff26815b51d6634d2162f2fce25ae23f7d1e11823f5b31f84d020000000000 00000052533401000002e0000005840000390509e000200100e2146b8a05028144a13b13b12b11 f13b13b12b13b12b121b218f18a218a11b218a218b18a218a11b111c18a11b11b218a11b11b218 b18a218e112818b18b18f18b183a11b218f18e11b218a21811f18b18b181b181f181b181b181b1 8b18b18b18b181b12b1ab18a21aa21ca21aa21ee214a11b21a1b12b1ca11c16a212a216a21ef18 e21a1b14b1cb1cb1ab16b12b1eb1eb12b14b1e1a212a218a21ca11c16a21ca21eb1cb1ea214b16 1b16b16b1af1cb1ab1eb14b12b14b141f161b12f16b12f16b121b14b1ea214a21cb1ca214a21ea 212b1aa216b1e1b12b12f12b12b12b12b12f122f12b12b12f12f1210f14b18b16b1eb16f1eb1eb 1cf1216b16b12f16b16b14b16b163b12f16b12b16b16b14b14b12b161b121b12b12f12b12b122a 214a13b12b21ea13c16a23ee22ea218b181c18a218a228e11f238a12b11b11b137f19b15b1ef1e b298a2f8a1eb1cb14b2681931e8a238a568011af1e8016801f801c8a1eb5c801e2818b181f18f1 8b18b181a238a12f11b238a228a238a238a238e2281b18a218a218a11b218a218a218f18a218a1 110b16b16b16b12b12b14b16b14b14b12b161b12b16a214a11b21ab16a21eb18b1ab12a2167f12 2f12b12f1216f17b14b12b11f13b17b13b14b14b121b11b11b11b11b11b11b1111f12b228a12c1 8e13b238a218a238a12b131b218f18e218b18a218e218a218b187f18f181b18b181b181e228e12 b218a238a238a238a218a218a12b2381f18b18b18b18b18b18b18b18b18b187f18a228a228a11b 13b12b238a13b13b218b187e11b258a274a214b1ca278a27ca264a258a238a27c1b1cf1c1f1cf1 8b14b142f141b14b14b14f147f14a214f18b1ce21ca11b218b18b142b14b18b1cf1cb1cb18b18b 181a13b234a12b224e238a23ca224f14a131c1cb14b14b18b1cf1cb18b18f181b14b14f141b14b 14b14192287c1cf1cb18b14b18b1cf1425922928234681ee27e292761280100000000000005253 340100000308000005df0000390509e000200100e2156b52fefca2fffdf8f2e5c9a69a61c22fa2 2bb16b12a23ca215a23fa22ca22db182f218a218a218e218b18b18a2181e13c18a218a218a238a 11b238a13b238a21825a12b13b238a14b228a238a278a218b18a17b2687f14b16b12f16b16b16b 12f12b141b14f16b16b16b12b16b14b12b141f14b16b12b12b14f16b16b12b14b142b14f12b14b 16b12b12b141f1cb1ab1ab18f16b1eb1cb14b162f12b12b12f12b12b12b12b121b12b12b14b12b 16b12b16b141b122b12b12b12f12b12b12b121f12f12b12f12b12b12b12b12b121b12b12b12b14 f14b16b163b12f12b12f12b122c12f12b12b12e222a2221a1210b12f12b14b12b16b16b12b16f1 21b1eb16b1eb1cb12b14b1eb1af1ab1624f111818b18b18a218a218a218a11b11b2187f18b18b1 8b18b18b18b18b18b18f18196198012a61d8016a21db67801f8a248a1eb268a191b11b218a13f2 38a218a238b18b18a218a2281b18f181f18b183a12b12b12f12b12b121b121f12b16f12b12b12b 16b12f14b141f12b1cb12b14b1eb1eb1cf18b181b16b14b14b14b14b16b16b14f12b141a21ab1a a226a224a22ea13b23ea236a1111818b14b1eb1cb1cb1ab1eb18b1cf1c1b12b11f13b11f13b13b 11b11b111f111f11b11f11b111f17b16b15b14b17b16b17f16b12b1316b248a238e258a14b218a 278a238b18a12b111c18e11c18a11c18a218a111818a21810b18f18f18b18b181b181a12b228a1 1b13b12b11b238a218e2381f18a13b13b228e238a238e11b11b117f21ca218a218b14e218a21ca 214a11b214b1c1b1c1f18b18a21cb1ca218a11c1c1b18b18b1cb1cb1cb1cb1cb18b14b1cb1c2b1 41f14b14b14b14b141b1cb1cf18b14b18b1cb1cf141f14a21cb18b14a218a214a21cb18f18a214 7e11b218b14a218b1ca11b23ca21cb18a214a21c1a22cf18a234a214a12b23ca21ca11b228a224 1a218b18b18a214a224a23ca23ca13c18b18a131c14f14b14b14f14f14f141b18f1cf18a214a21 ca11c1ca11b21c1b12b12b121b12b12b12b12b12b1210b2f411a27ee23e1280100525334010000 0330000006230000390509e000200100e2166b86fafd7f818d78f1e5c9a69a610e18f18f18b181 b181f18e11b11b218b18a218a218a218a11b2181b18a11b218e218b18a218a218b18b182f18f18 a11b218a111c181b18f18b18b18f18f18b1816f14b16b1af16b16b1eb1cf1cb1c1a214a11b21eb 18a212e21eb1aa218b1ea2181b12b14b12b14f14b16f12b16b161f12b12f12b12b121b12b121a2 2ca224a23eb12a11c18a23ea21ea13b212a2261b18b16f1cb16b1ab1eb1cf181f1eb14b1c1b1cb 1ef1ab14b181b16b16f12b12f16b12b16f121b14b16f16f16b16b14b12b14b147f14f16b121b16 b16f121f12f14b14b14b16b16f14b14b163f12b12b121b122f12b12b12b12f12b12b121f121b12 f12b125b12b12f12b12b121b1cf1eb16b12b1cb1eb1cb14b14b1e1f218a11c18b18b18a218a218 a11b2181f18b18b18a218b18a11b2181a218b181b18f18b181b18b18b18b18b181a11b11b11c18 a11b218a218a11c18a218a2181b18a218a218a218a11c18a2181a11b2181a218a218a218f18a11 b218a218e112c18f181b18f18f181e12b12b121f121b121f16f12b16b14b14b16b12b12b161f16 b121b14b12b16b14b14f161a218a25ea266a17b236a212a27ea24aa268a27ea2781b16b14b16f1 2b16b16f14b161f16f141b16b16b16b16f162f12b12b12b121b12b121b12f11b11f12b13f13b11 b131b12f12f13f13b131b111b1cb1bb1bb1cb19b1db1fb18b15b16b191f111818b18a218a2181b 18a2181b18b181f18b18b182f18e218a11c18a11b218a11f11c1811f18b18a218e11b218a2181e 218b18e218a11b218a218a218b18b181e218f18a218b18a218b18a218a2182b18a11b11f218a21 8a218b18f181b141b14b14b14b14f142b14b14f14b14f144b14b14f14b14b14b14f147f1cb181b 1cb18b1cf18b18b1c1a21ca11c14b18b1cb18a21ca218a214b18a21c1a218a214f18a218b1ca21 cb14b14a218b1c1a11c14b18b1cb1cb1ca21cb1ca11c14b141f141f14b141b141f1da218b14b19 b1cb11a21db1ca219b19a2181e14f141b14b14f14b1428141b14b14b14b14b142b14f18b1cb14f 1cb1cf1cb141f121b12b12b126e22f6d22610b22210d0100000000000000525334010000033800 0006360000390509e000200100e21c6d33fea1818db972e5c9a69a64911b11b11b11b1128182f1 8b181b181b18f18b18b18b18b18f18f181f18a13b238a11b11b238b18f182f18b18b18b18b181b 18b181f18b18b18f18b18b18f1810a218a11b11b218a11c18a218a11f218a111c14b1eb1ab16b1 2b1ea21ea212b18b1ca111c12b228e248a12b268a16b228a248a14b1210b1eb14b14b1ab1cb1eb 16b12b1eb121b12b16b16b14b12b14b16b14b12f161b122f12f122b121b12b14b12b16f14f141b 14b16b14b14b14f16b16b14b161f1ab12b14b1ab16b14b1ef12b1ab161b12b16b12b16b14b12b1 6f14b12b167f18b14b18b18a11c14a21eb18a21aa1128121b12b12f12b12f12b127f12f12b12b1 2b12b12b12b12b121f12b12b12b12b12f12b12b12b1210b2181b18a218e218a111818a111b258a 12b238a258a15b14b278a11b238a15b2181b18b181b18f183b18b18b18f18b18b18f18b18b181a 11b218e11b11f218f18e111c18a218a218a218f18a218a218a112b228a238a12b228a238a218a2 38a238b18a11b21817a12f121b12b12b122b18b1ab1ab18b1eb1eb1eb1cb1ab18b121a11b21ea2 14a216b18b18a21ea214b12f141b121b12b12f12b12b12b121e218b12a11b21ab1cb1ca21eb18b 16b1ca21c1b11f11f11b11b11f112b13f13f12b11b13b13f12b131b17b12b17b13b11b14b17b13 b14b11b1128181f18b181b18b182f181b18b18b18b18b181a238a238a228a228e258a278a11f21 8a2181f181b18f183a218a218a11c18b18a11b218a11f218a111b11c18f18a11c18a218b18e218 a2181b18b18b18b18b18b18a218a11b218f1810b14f14f14b14b14b142b141f14b1ca21cb14b14 b141f14b14f14b14b14b14b14b14b142b14b14b14f14b14b14b14f141a214e21ca214a214b1ca2 1cb1ca214b18b181f18b14f14b18b1cb1cb14b14b1c1b18b18b1cb18a218b14a21cf1ca21c1f18 f14b14b1cb1cb1cb14f14b1c1a238a234a22ca11b224e27cb18e2341f14b18b18b18f1cb1cb18b 1cb1c1f18e111814b18a21ca214a21ca214b1c1f1cb18b14b1cb18b1cb14b1cb1cb181a294a254 a2b4a25ca2eca2d8a2fca25ca238a264a1e32f27ef3108f256f312858010000000000052533401 00000330000006290000390509e000200100e21d6b81fffeff418d7972e5c9a69a610a18b181b1 8f18b18b182a1fc18a1fb238a15b238a2f8a2a8a218a258a1711818f18f18b18b182e11b11b218 b18e218e11b1117c121f12b12b122f14b16b16b14b16b14b16b12b12f121b14b14f14b14b12b16 b14b16b141e23ca12c1ca21cb12a232a23ea12b224a21aa2281b12b121b12f12b12b12b12b121b 12b12b12b12b12b12b12b122f12b1ab16b12b1cf1eb1ab1ab18b141b1ab16b18b1ab14f1eb1ef1 ab1c1b12b12f121b121b12b121b16b16b12b12b12b14b16b16b12b122b12b121b12b121b12b121 b12b16b1af1cf1eb1eb16b14b1c1b1eb16b14b1cb12b1cb1eb14f121f12b12f16f16b16b12f16b 161b18b1ef12b18b18b1eb1af1ab1c1f12f12b12b12b121b121f12f12b121b12f12b122b218a21 8a11b11f218b18a218a11b2181a218a238a11b238a13b13b238a12b228a228b181a228a12b12b2 58a15b17b278a228a14c18a151b11b218b18e218a11b2181e2181a248e228a268a258a15b278a2 38a13b2681e12f11b3202ff203820282010203b122818f181b18f18b182e12b12b16b12b12b16b 14b12f121b12b121b12b12b12b12f121f1eb12b12b1cb1eb16b1eb1ab1ab14b121a226a238a224 b1ea12c1ea23ea23ca232a12b21e1b12b16b14b12b14b16b16f12b16b121f12f12b12f12f122b1 eb16b14b18b12b1cb1eb14b1ab1eb141b12b13f12b13b12b13b12f13b131b17b1db1bb15b19b15 b1fb17b1ab1f1f111b11b11b11b11f11f111b228a228a218a238a13b11b238a238a11b11b228d2 5fb12b288a2a8a218b18a18b2f8a288a1cb15b24810c12a282a282b12b12a18c12a28229218a21 8e218a12f238a12c18a238a134818b18b18b18b18b181a1ca21aa717801a8a16a213a31f8a6e80 1f8a12b2881e218a218e218b18a218e218a1110c142f141b141f14e234a224a214e23cb18b1ca1 1b121c14b18b14b1cb1cb1cb1cb14b18b18b181a11b228b14b14a12b228a23ca234a23ca228a21 c33b1ca214a25ca23ca12f27ca12b23ca15b131c1cb14b18b1cb1cf1cf14b1cb1410b14b14b14b 14b14b14b14b143b12b12f12b12b12b12108294292fc168463f858010000000052533401000002 f8000005b40000390509e000200100e21e6b85fefe81818d78f2e5c9a69a61d13b13b11b13b11b 12b13b12b11b13b131b12b13b13b218e12b238a218a11b218a1216b11b228a228a218e228a238a 11b12b218b181a11b11b11c18e218a218e218a11c181b18b182b18b18f181f18b18b181f18f18b 18b187f16b12b12b14b12b14b16f12f121b12b14b121f16f14b142b121b12b12b12b123b121b12 f12f12b122b12b12f14b14b16b14b16b12b161b14b14b14b14b12b14b16f16f147f12f12f12b12 b121b12b121f18b18b1cf18b1eb12f162b1ef1cb16f1eb1cb12b1cb1e1b12f12b12f12b123b12f 12b12b12b12b12f122f16b1eb1eb14b1eb1eb14f1eb1e3f12b12b12b12f121b1aa218b14b12a11 c1ea21eb1ca21ab16a111c12f12b12b12b12b12f12b122b12f11c18e238a13b218b18a121f17b1 8b298a14b278a2f8a18b18f2e81a218a12b218a218a12b11b238a13c18a238a2181a268a14c18a 278a228a15b278a238e11b2781a11f11b218a218a218a218a218a218a218a111c18b18f18b18b1 8b18f1811a12b12b12b12b12b12b121f127e212a216a216b14a21ce21ea218a21cb16a2181b12f 12b121b12b12b12b121e21eb1aa21ea11b21aa21aa21ea212e21ca2141b16b12f16b16b12b16f1 4b16b161b1ea216b16a218b12b1aa21ea21aa214b1eb1c16b12b12b13b12f11b13b11b13b13b12 1c18b18f18b18b18b18b18f18b181a228f18a14f15b278a258a258a15b151b16b11f13b17b248a 278a13b258b18a121b218a218a11b218a11c18a218b18a21810f18b182b18f182f18f18b18f18b 18f18b1810a218b14a214b14a218a21ca218a21ca21cb141b14f18a21ca214b1ca21ca218a11c1 4a111c14b1cb14b1cb18b18b1cf14b18b141f141b14b18b1cf1cf1c10b1cb1c1f1cb18b1cf141b 181f14f1cb141b187f18b182b1cb14b14b1cb181b1ca214b14b1ca214b1ca21ce214b1ca111c14 b14b14b141b141b14b141b14b14f14f14b141f141f14b14f14f14b14b142b12b16f16f12b16b14 b12b1635d010000000000000525334010000002000000007000009530d0000200100e302112c03 00000000005253340100000020000000060000047e004000200100e40243f02000000000005253 34010000002000000006000008dc020000200100e502106f000000000000525334010000002000 000005000005100f2000200100e60950c000000000000052533401000000200000000600000731 090000200100e70071c010000000000052533401000000200000000600000175040000200100e8 0013d010000000000052533401000000200000000600000189020000200100e90014a010000000 000052533401000000200000000600000339090000200100ea0431e01000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000023560301010000000100000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000020dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000 00000000000000000000000000000000030dc400c8012c00dc001e07ec00bb00f000d000180e8c 00d2016800e500240000000000000000000000000000000000000000040dc400c8012c00dc001e 07ec00bb00f000d000180e8c00d2016800e5002400000000000000000000000000000000000000 00050dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000000000 00000000000000000000000000534231007287334ac218bf26ad36bbb21e2349657abcb01dbe58 66267287334ac218bf26ad36bbb21e2349657abcb01dbe58662650420501000000005046020000 788410015254045652544e534f020000494efe0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046007884740052540456535243494e6400000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046030000007884300052540456524d4c5644023031504e0730304b56363237534e0c59413139 3332303936393530545604303030345046010078849c0152540456574d4c56440230314f430400 000307464f1102ffffffffffffffffffffffffffffffff23495401010454000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000494e140131393336333530303030323432303134000000 5046030000007884ac0052540443525030564402303145442101000000000000000000000030d0 002e7bc104e0000000607869a038408128698054450b0130323033514651415346444405013032 3032535403010000444e4901240000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000049510b0100000000000000000000504603000000788448065254044c5250345644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100be013600d2002407ed00a400 aa00be00180e6b00c6016000da0026000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000054431314000000000005000000050000000500000005234d010103000002bd 0321017e0000034103a401fc0099031603a30346000002d003a302f1009903c304260262009903 980426032500000354042603ff000002ee042604d30099044704aa03250099041b04aa03bb0099 03d604aa04ce0099037204aa05b7009902db04aa06860000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52503556440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00be013600d2002407ed00a400aa00be00180e6b00c6016000da00260000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191d01fe191d0000191e01fb 191f01f4234d010103000002bd032101a00000034103a401e00099031403a30256000002d003a3 0339009903c304260256000003990426032a00000351042603f0009902ef042604f90099044704 aa029e0099041b04aa0375009903d604aa04ab0099037204aa05ad000002dd04aa068e00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525036564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100be013600d2002407ed00a400aa00be00180e6b00c6016000da 002600000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191d0003191e01fa191e01fa191e01f6234d010103000002be032101aa0000034103a401d400 00031503a3025d000002d003a30320009903c40426020b000003970426031b0000035204260436 000002ef0426053a0099044704aa028d0099041a04aa0375009903d704aa04a90099037204aa05 c1000002dc04aa06cc000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000434801004951090200000000000000005046020000788448065254044c5250435644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100be013600d2002407ed00a400 aa00be00180e6b00c6016000da0026000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000544313140000191d0002191d0001191d01fe191d0000234d010103000002bc 032001770000034103a301d90000031503a30279000002d003a3032f009903c30426026f009903 97042602f300000351042603d2000002ef042604e50099044704aa02850099041c04aa039b0099 03d704aa04c40099037204aa05ad000002dc04aa06890000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52504456440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00be013600d2002407ed00a400aa00be00180e6b00c6016000da00260000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191d0005191d01fd191d01ff 191e01f8234d010103000002bd032001a10000034003a302080000031503a3025d000002cf03a3 0366000003c30426025800990397042601e80000035304260357000002ef042604e00099044704 aa041d0099041b04aa0357009903d604aa04420099037304aa05cd009902dc04a9065e00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525045564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100be013600d2002407ed00a400aa00be00180e6b00c6016000da 002600000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191d0003191d0005191e01fb191e01fa234d010103000002bd0320018f0000034003a3022600 00031403a30279000002d003a302b7009903c30426025b00990397042602e900000352042603cd 000002ef042604860099044704aa026a0099041b04aa036e009903d704aa04c20099037204aa05 c1009902dc04aa067c000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000043480100495109020000000000000000504602000078842c005254044c524d305644 02303254430516000000004d430400000000494e08000000000000000050460300000078842c00 5254044c524d31564402303254430516000000004d430400000000494e08000000000000000050 460300000078842c005254044c52493056440230325443051600000000494e1000000000000000 000000000000000000504602000078842c005254044c5249315644023032544305160000000049 4e100000000000000000000000000000000050460200007884b8005254044c5750345644023031 2332440001040f0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000233344000104 0f0000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000494e13000000000000000000 000000000000000000005046030000007884b8005254044c57503556440230312332440001040f 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000002333440001040f000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000494e1300000000000000000000000000000000 0000005046030000007884b8005254044c57503656440230312332440001040f00000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002333440001040f00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000494e130000000000000000000000000000000000000050460300 00007884b8005254044c57504356440230312332440001040f0000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000002333440001040f0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000494e13000000000000000000000000000000000000005046030000007884b80052 54044c57504456440230312332440001040f000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000002333440001040f000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000049 4e13000000000000000000000000000000000000005046030000007884b8005254044c57504556 440230312332440001040f00000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000002333 440001040f00000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000494e130000000000 00000000000000000000000000005046030000007884d8005254045645523056440230312349c4 000104300000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000504601007884d8005254044d45523056440230312349c40001043000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000050460100780003ca8f47b6f3 61985975001fffffffffffffffffff001fffffffffffffffffff001fffffffffffffffffff001f ff73ff7f5faffffe970013c889c766e861985975001fffffffffffffffffff001fffffffffffff ffffff001fffffffffffffffffff001fff73ff7f5faffffe970c16d717318e036fdd1be66effff 377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6cf0c12d717318e 036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2a e6cf0c06d517518e036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef77 77a6a82c0c869c6af6cd0c52e71211ee036ddd1be66effff377ffdab15e55fb4009a31771ff7bb 7ffddfbf6efcef7777a6a82c1c869c2af6ed0c56e71231ee036ddd1be66effff377ffdab15e55f b4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6ef0c52e71231ee036ddd1be66eff ff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2ae6ef6918590545 d2099333385d17691c590545d0099333385d577912dd01457a7f02d359631d7916dd0145787f02 d359635d4e392e346b6c688f0a597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbefed3de6f0d0a6e5b0797a2e8c38d68af3a55e22898323cc87e 460716f3aa4ac02ab419824a9e887b6a67be9edafbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa09bf1a86e3d2e347b6c688f0a597b27a5bee690d26f87068171 6a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbef6fbdfef399275132cd3a63a b118a4b82ed333228284cefaf5003832c41261f58472990940b5a83b6a67bebedafbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa09bf1a84e212c342b6c688f0a 597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef befed3da6b1910e57848a09c62bb66c9b58c5beb2dc09e56c8666c3814861827d83e02c3df2b92 9a59fa2fbe9efafbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a0dbf9a86fb526307bec608e0a597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbefed3df4faf12adbb2f88a0bec9ba2006981deb24d9888a3240 25bc8c541148d9246c004705879ce96827be9efafbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa2dbf9a04fb926306bec608e0a597b27a5bee690d26f87068171 6a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbcf6f3dbee9998391a6cc4b4ca b0698fa00618734adc00c2a8b7252b0ee23267e924de92654c9cbea96a2ffebefafbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa29bf1a06fbd26307bec608e0a 597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bef6bbff4e1f92ec604e6b1cd0f8a12ab42a93230a0e020c481f831d3af34026480e4cb113ca80 0e497227febefefbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a29bf1a0001faebec9e5de9bfbb0810015068b9a245b7247e3b0001fffffffffffffffffff001f f677eba7ac607d7d8250facffc590a887d158a2a15f5e87a9548aabf7fefff55ffefffffeaafff ffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffef ffffeaafffffefff55ffefffffeaafffffefff55bea75fefaa876e38035910b4eeba3734b24032 67956fd088a31514240714b888a2f0cfef999bfb3777feeefffddfffbbbfff677eda8e1f5d58b6 22614f255372b04bbd13ef6cffff777fffbb7ffddfff6effff777fffbb7ffddfff6effff777fff bb7ffddfff6effff777fffbb7ffddfff6effff777fffb93ffcccde6eece36801d828852128b41f 76d87246785d979f2b629501801484c26405427b310117ea1e967e59e73213039f022a0cb15450 e2d361b5ab175012c0a40c11d23b33c964e74a57e17465fa4976ed43b72240b855d7b26b709432 0830734e09f7553950eb371820927c7cd6ac1b01df34f72e670b35330d53d389501902e5556d44 3a2b5162710844c5364547345a400d35a61e25b928738a1a044317a14440d37d03b1082767424f 68d2047046247824d5154f4ae2f175b59612619e66610e80d24861bf62c5ee73f342e6c0452648 6a723527cb16f60e1881516a543a260e56f4c72874d063b03f27457005ce6d407111927a511836 f17135278e6b54ca11692eb5182c42ed41602002e948f0f84da5b17094d076ad32c3f141636238 70fb64f164463f4100ea73702f25497085ce6d407111927a516832f0d72d76d073a036168e74f7 5a4cc03d12200973a26a24280535f672155a40831c62f849a5570b62a023146407376d94ca58a7 9100c12007cc2c405111925a556832f0d72976d063b03f276572358875b06678b32b44440e77df 4495b20895b663356ac07b0197b972d674127244e0420df55e30762436835ef7677422437a1372 166c3270c72976d063b03f27457005ce6d40b111817160872885cb1997772237b8628310e3dc0d b1ad3a975624cf2e648351c5e743548c63a67a378409c15f69943b23af24e6052875d860304f25 453005ce6d407111927a517830e09529375362702e75b84450ca08b4d539e20654e97eb5484081 ba71915874db7cd6956112ac42528606a416c7fb59072a1a774f21497085ce6d407111927a5168 32f0d72976d06380bd07827635db0d967d330032771c7032616df4e63894de707516732f3886d0 2a756020270cf7965987d91b332624b14687f920b32772875541667082e84404aa33749a11bc68 37a1243391028561323512e76c19304869b613621e1cf7da44651703a57813d31cd32d2c070420 265251983c94dc10f5100a208e672f4e63fb11315f52f08c135770b32051f40d6bc52b65fc1a47 7259032c52f199341c5c44a824b7b40b24e9263402a4dc5593ea31a44b03f948a72235e59a7337 ca24b260d6c14c874919f15d22fc0c946751c7fe0a4196651f2a85e350c15630454a24053001f0 4c14011ac16a635802d6055d956951b29a16db0640b90c67d041451421e504e52a4157d170f2cb 74f35ef7ff3df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7fe77 cf7ed77a6d63b7114563752e6685267cd3e078466235aa2886ec09c63d5ab3c5003652b5be0c37 0528f0c8434436e3476586d04275f353476c935424c7ec79833f271912d6366de59278603a4368 5812945523b81bd53971da7051fe7c075459769014521020ee59a5c472471e16eb70619d79f2d5 69814f66ac6875f524768832d50c736a38d64b35e45c51a69f66544c868b29413f72c36c756716 941c7851ce53e0d423fb6af61829d3906a31af24d040f2de3d522868037872831225a31173b330 864871cb06e4a241f4ee7b91f8071e52252650d3083216db466a78a4e45853373af5d362145626 280597547996bf66585033223dc20909c4b401263837f33820a213d66f658520e37279b76d5032 6153bc22317609069d4b152066f84627f05d326653635056455e270f24877a4bb69e04b5229653 5872a5198375308d44043130c07e2020fe007e2661e82c57713111c625044ca67d2c86153ac4b4 70fd2472cf11b5921bb14205143ec6a02402b538a67b132f0c43a33177042274f135b72a03856d 56656366c030852c601c4de7ba12e105663d6e14bc6c031701d67e41cc08b3c644304f63069f11 4c50734f0c150c2a660066071ce1f121064449320e149c46401651c4181b843b64455815e16d81 9310949243d512848065c3870070e322ed06b59c0c117d202730308f64d4af3016b96bc3e170a4 18843d35100548211444ba1a22d47ca01a52d17953b33c165e14c6d318440c369c3ce4400dc4ba 61b15a43cc36117c6d721e282267548e20a0c24823d36bd258269b7c847828b3de28b654715966 f3db01a5730ac54931fc46833678468612425b12ec6050a05826913b209a76911657fc09b3820a e45707b178a56c4cd2d450663b13bb74703874556879d62271d92c660838d3c27bb14d049f5227 a640c6ac72176003793a04f660351931220656721653f53d058a30d7b0022a12472e4cc5d00b22 5a66fd7e763f3814621391d704de0890a029344f12e35b234b50168621c57942474d2777388382 30522231e0d644ec70a46949e28a32735733762ea13d2515ea70565d671672a1355547ea09932e 614c1e336a05072550573450474a44e759a74b20463326027a32233c01c4502082445c3a73fb29 04683a50f0455760638078c54b236408706d2c809d65e1e651f2f243b30285744dc1c73ba38a66 e60487750cb50562405541e816f0af5971ba31e56c21f46a00fc45426778a5a701a648b2673974 ed53046041356e302458418c59f76a50ba6c462445c6ab63472643c26671767072525912cc24aa 16e08c4cb7520a0791321576909c2c67d01102e777fd62808e51755100268d27d15002546d0216 7b655f20f95e81ea09523b70844c30301c42641440121a470724451c13a23de4ff19501611e852 150c3926b822a7f763fe02757e4505460b62c936ce0e75367991764a061744e072206108f14c5a 96c076426e21ed24220428804d213e4aa1bb0172323054d5159850012b7971a431d5ed153e0424 0518e69c589010015a4484675c42940be68227fa1a31b26925ef58136b31626ab5dc7820da0116 3e700634b5424523aa70363406733ae6b245c3671ac04241a90e154821809e5ae07477ff1cf590 10e29351601116be2c706005e27c3076c474267251673d97bf79e06d747f3c760b4c05602bb128 544600b3e241b1f91246ca74471c741504059f1a86f5024b6ac31428b4a4789794431654d75721 b1f953f0e537825054b07403ef0370e0157660a04318b1697ba78770014471c800a70e30638c31 3c6006fa10b5cd03304175fd26b55910f0c132068600675c17c679c58e7b7570212346656e19d6 c748709230ac34f2051c143150c15603bb1a14032cb55c39a037560f5060b91cd6d71b77ef77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff73f7fc57cf72c77b6d30e97b33df77ef5ef7fb7137fa73019b41197a70f618f531603091054e 30513344618f71204101d75a40491933565ad39524d80e75484511b309416f217d5a00c67183b5 7b168c65791ef30b29c71841a59e4206f264017a19319c44e96233131424800932c50631023e7d 0221118a1fa88be808a9a901bd0ed1c4e8ae485ca0ee4505671e40080bec64c9fb11c68853a300 21353bec7a04a18ea4adea8153876b5b01ff43f932c00bf509e22651d00f2feae802970709e055 31860fa8820b7d6ea6661089c0819d200de1808ed5115f068e8a00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000 prop: ibm,ccm-node-id size: 4 val: 00000000 prop: ibm,hw-card-id size: 4 val: 00000000 prop: ibm,hw-module-id size: 4 val: 00000001 prop: ibm,mem-interleave-scope size: 4 val: 00000000 node: chiptod@40000 prop: reg size: 8 val: 0004000000000034 prop: compatible size: 37 val: 69626d2c706f7765722d63686970746f640069626d2c706f776572372d63686970746f6400 prop: secondary size: 0 val: node: nx@2010000 prop: reg size: 8 val: 0201000000004000 prop: compatible size: 27 val: 69626d2c706f7765722d6e780069626d2c706f776572372d6e7800 node: pbcq@2012000 prop: reg size: 24 val: 00200102200000000020010905000000003c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000000 prop: ibm,hub-id size: 4 val: 00000002 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333300 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 7777777777777777777777777777777777777777777777777777777777777777 node: psihb@2010c00 prop: reg size: 8 val: 02010c0000000010 prop: compatible size: 31 val: 69626d2c706f776572372d70736968622d780069626d2c70736968622d7800 prop: status size: 3 val: 6f6b00 node: xscom@3c8800000000 prop: ibm,chip-id size: 4 val: 00000011 prop: ibm,proc-chip-id size: 4 val: 00000003 prop: #address-cells size: 4 val: 00000001 prop: #size-cells size: 4 val: 00000001 prop: scom-controller size: 0 val: prop: compatible size: 27 val: 69626d2c7873636f6d0069626d2c706f776572372d7873636f6d00 prop: reg size: 16 val: 00003c88000000000000000800000000 prop: ibm,dbob-id size: 4 val: 00000000 prop: ibm,occ-functional-state size: 4 val: 00000001 prop: ibm,module-vpd size: 65536 val: 000f17ba5598401f3bd42b84280052540456484452564402303150540e56544f43d500370074 01d5b85d0050460800000000000000007884700152540456544f435054fc56494e49d500ab0190 00b1b8240043503030ff003b02406161a050185652544eff007b6314011ca0450056535243ff00 8f647800fe9f1e0056524d4cff0007653400f19f0d0056574d4cff003b65a001899f6800435250 30ff00db66b0005d9f2c004c525034ff008b674c06ca9d93014c525035ff00d76d4c06379c9301 4c525036ff0023744c06a49a93014c525043ff006f7a4c06119993014c525044ff00bb804c067e 9793014c525045ff0007874c06eb9593014c524d30ff00538d3000df950c004c524d31ff00838d 3000d3950c004c524930ff00b38d3000c7950c004c524931ff00e38d3000bb950c004c575034ff 00138ebc008c952f005054624c575035ff00cf8ebc005d952f004c575036ff008b8fbc002e952f 004c575043ff004790bc00ff942f004c575044ff000391bc00d0942f004c575045ff00bf91bc00 a1942f0056455230ff007b92dc006a9437004d455230ff005793dc003394370050460200007884 8c0052540456494e4944521031302d5741592050524f432043554f44464e073030465835313850 4e0730304658373430534e0c594131393332303936393530434304353445384845043030303143 54040000000048570200014233060000000000004234010042370c000000000000000000000000 5052083500500122008001565a02303143450131504602000078843c6152540443503030564402 3031504741010000f180f4000000f000000000000000e100f70000000000000000000000000000 00000000000000f300f300f300000000000000000000009300f300f30000004d4b050100000000 2347043001525334010000002000000005000001f8080002000100a20817e00000000000005253 340100000020000000060000023d040002000100a00821f0100000000000525334010000002000 00000600000136001002000100a30811d020000000000052533401000000200000000600000113 0c0000100100a40810c0300000000000525334010000002000000006000002f6080002000100aa ff27d020000000000052533401000000280000001700003190040002000100a5ff47816200a162 01916143e0000000000052533401000000200000000d00000ba5010002000100a8ff12f2181228 01000052533401000000200000000500000328002002000100a9ff31a000000000000052533401 0000002000000005000000df001002000100a6ff6f030000000000005253340100000020000000 0700000e26020002000100a7ff161902000000000052533401000000400000004d000012520600 00100100ab1479121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e12 64a1877b02000052533401000000400000004d00001252060000100100ab1579121f141f181e11 1c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000 400000004d00001252060000100100ab1679121f141f181e111c12f18e12f18e12f18e12f18e12 f182e18e12f18e12f18e1264a1877b02000052533401000000400000004d000012520600001001 00ab1c79121f141f181e111c12f18e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a187 7b02000052533401000000400000004d00001252060000100100ab1d79121f141f181e111c12f1 8e12f18e12f18e12f18e12f182e18e12f18e12f18e1264a1877b02000052533401000000400000 004d00001252060000100100ab1e79121f141f181e111c12f18e12f18e12f18e12f18e12f182e1 8e12f18e12f18e1264a1877b02000052533401000001280000021100000e1309e000100100ac14 1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3 f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0 4c11a37c1d7401f041a181b6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08 c12a3f82d6803e08c11a77c104021b6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d 6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d03000000000000000052 533401000001280000021100000e1309e000100100ac151f6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d7401f041a181b6803e08c1 2a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a77c104021b64 01f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d 6401f04c11a37c1d6401f0430d03000000000000000052533401000001280000021100000e1309 e000100100ac161f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c 11a37c1d6401f04c11a37c1d7401f041a181b6803e08c12a3f82d6803e08c12a3f82d6803e08c1 2a3f82d6803e08c12a3f82d6803e08c11a77c104021b6401f04c11a37c1d6401f04c11a37c1d64 01f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d030000 00000000000052533401000001280000021100000e1309e000100100ac1c1f6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f8 2d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d7401f041 a181b6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c1 1a77c104021b6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d64 01f04c11a37c1d6401f04c11a37c1d6401f0430d03000000000000000052533401000001280000 021100000e1309e000100100ac1d1f6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d6401f04c11a37c1d6401f04c11a37c1d7401f041a181b6803e08c12a3f82d6803e08c12a 3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c11a77c104021b6401f04c11a37c1d6401 f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d64 01f0430d03000000000000000052533401000001280000021100000e1309e000100100ac1e1f68 03e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d 6803e08c12a3f82d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c 1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a3 7c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f04c11 a37c1d7401f041a181b6803e08c12a3f82d6803e08c12a3f82d6803e08c12a3f82d6803e08c12a 3f82d6803e08c11a77c104021b6401f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401 f04c11a37c1d6401f04c11a37c1d6401f04c11a37c1d6401f0430d030000000000000000525334 01000000200000000700000a71080002000100b202123c01000000000052533401000000200000 0007000035c7040002000100af0265690300000000005253340100000020000000070000157102 0002000100b002253c01000000000052533401000000200000000500000734010002000100b102 71d000000000000052533401000000200000000600001694004002000100ae02264d0000000000 00525334010000002000000006000005c7004000100100b4025690300000000000525334010000 002000000006000003c3001002000100ad02368030000000000052533401000000200000000700 000ceb0d0000100100b302147a03000000000052533401000000200000000700000ed502000010 0100b502166d01000000000052533401000000200000000600000217080002000100bb0920d030 000000000052533401000000200000000700000841004002000100b60910280100000000005253 3401000000200000000500000330002002000100b70931c0000000000000525334010000002000 000006000001b6001002000100bc0915d020000000000052533401000000200000000700000a63 0f2000100100bd0912380300000000005253340100000020000000060000033e080002000100c0 0031f020000000000052533401000000200000000600000495040002000100be0044d010000000 0000525334010000002000000006000001b2020002000100c10015c02000000000005253340100 0000200000000600000457010002000100bf0042d0300000000000525334010000002000000006 00000186001002000100c200149020000000000052533401000000200000000500000570090000 100100c30053c000000000000052533401000000200000000600000397080002000100c70434d0 3000000000005253340100000020000000060000025f020002000100a10822f030000000000052 53340100000020000000060000041f040002000100b80940f03000000000005253340100000020 000000060000041f020002000100b90940f0300000000000525334010000002000000006000004 0f040002000100c50440b0300000000000525334010000002000000006000002ab020002000100 c40425a030000000000052533401000000200000000600000543010002000100c6045280300000 000000525334010000002000000006000002b5090000100100c80425d010000000000052533401 0000002000000006000003b1010002000100ba0935c01000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000002352902f01525334010000002000000005000001a00c0000200100e0 08158000000000000052533401000000400000004600000e46060000200100e11416c323441b2c c10e23f3b5f403c14c2e42a23f16a2cce27e783fffe31f84f21c10a02000000000005253340100 0000380000004000000e46060000200100e11554b23113b31f81e47ff86e2c422f26cc31f8f23f d27e5d234e27a4f23f16c02052533401000000380000003900000e46060000200100e11610f2e9 5827436829924e2fc2a23d2d294382fc11827e22e31f813c020000000052533401000000400000 004500000e46060000200100e11c52f2631a413d12c336825a2283c46ff8c23312c2ec1e1411d3 1f83d21a5f31f87b0200000000000052533401000000480000005200000e46060000200100e11d 22929431e2f9e22b2b31ac13843ffc5f21711b57e04e6a52503723d31281b57e01639131a31f84 d0200000000000000052533401000000300000002a00000e46060000200100e11e11f26d118274 27a21116a2fc30f2fc24821213b0200000005253340100000278000004b40000390509e0002001 00e2146b937d7f3f9f18c4f3e7c9a69a61d12b13b13f13b13b13b11f11b111c18a218f18e11b21 8a218b18e111b218a218b18e11c18a218a218a218e2181a11b11f218b18b18a218a11b218a218b 181a11f11c18b18b18a218e218e2181e238a218a11b13b11b238b18a12f2181b18a228e218a228 a228a238b18a238a228a2387f12b14b1cf1eb1ab1eb1ef18b167f16b1eb14b1cb14b1eb1ef1cb1 2b121b141b16f14b16b14f16b167f1ab16b18b18f12b1eb1ef14b141b12b16b12b14b12f16b12b 14f121b16b16f12b14b16b16b12b12b14b1416b12f12b12f12b12b122f12b14b12f14b14b16f14 f123f12b12b12b12f121b16b16b16b12f12b16b12b142b12b12b12b12b12f12f12f121b14b14f1 6f14b16b14b12b14b1611c18b18b18b18b18b18b1810f18b181b18b18b18b18b181e218b18b18a 218b18b18a2181a112818f18b181b18b18b18f187f18b18b181b18b18b18f182a12f12b12b12f1 21b1210b12b14b14b14b12b16b16b14b162b14b14b16f16f1627b11f11b11b11b11f112b13b13b 11f11f17b14b15b12b141a218b12b15a214a214a21aa21fa21fa213b13a219108182f18b18b18b 1820f18f18b18b18f181b18b18b18b18f18b18f18b18b182a228a11b11c18a238a13b238a11b22 87e11181ca218a214a218a21ca11c14a214a2181b14f14b14f14b14b14b14b1410e214b18b18b1 8b14a21cb18b14b1ca21810b14f14f14b14b143f14b14b14b14b141f143b14f14b14b14b141f18 b14b1cb14b18b18b1cf1cb1810a27ca22ca21ca224a268a268a27cf1ca228a152814b141f14f14 f141f142b14b14b1415c2943927c12d27e10d0100000000000005253340100000298000004f300 00390509e000200100e2156b92fc7ede9ffdf9f017c9a69a62913b11b12b12b13b13b13b12f131 b15b11b12c18a248a12b278e13b268a2181a218a11c18b18a11b218a218b18b18a112818a12c18 a12b228a218a238a218a13b228a114281aa214a21ab12a21eb12a21eb12a21ca11c181b14b12b1 6b14b14b14b16b14b14b161f14b16b12b12b12b12b16b14b12b12b141b12b12b14b16b12b12b16 f12b14b121b16b14f16b12f16b12b14f143b12b12b12b12b12f123f12b12f12b1210b12b12f12b 12b12b12b12f121f18a21ea21ab1eb1ea212a21ea214a11b21eb126d3102f16b12b12b14b16b14 b16f14b14b142b12f12b12b1227c18f18b18f18b181b181b18a238a11b238a11b218a238a12b12 c181e11b11c18a11c18a11b218e112b298a18b1ba213a21de31f8a17b17a712801381b18b18b18 1f18b18b18b18b181b182f18b18f18b181a111b218f18a218a218a218a2182e16b16b14b14b14b 16b12b14b14b161b12f12f12f12b121b121b1ab14b1ab1eb14b16b1eb1ab16b14b1a7f12f12b12 b12b12b121b1210b12b121f12b12b12b122a212b19b13b11b11b11a21fa219a216a217a21d7f12 b121f12b13b11b12b1238181f18b18b18b18b182b18b18b18f18b18b18b18b181b18a218b18b18 b18a218a218a218a218b182b18a218a218f18a218e218e21810b18a218a218e218a218a11c18a1 1b21816b18b141b18b14b1cb18b18b14b1c1b18b1ca218a21ca11b218a21cb1cb14a21c1f1cb18 b1cf14b1cb1cb18b182b1cb18b1cb1cb1cb1cb1cf18b18b181a214a218a214b14e218a21ca11c1 4a21ca21410b14f1cb14b14b1cb1cb1cb14b1c1b142f14b14f1425b14b14b14b141b14b14f14b1 47f12f12f12b12b12b12b12b12b1221e2581390100000000000000525334010000026800000491 0000390509e000200100e2166b98fd7ebfdffdf8f1e3c9a69a610a181f18f18b18f181e11c18a2 18a11b218a11b2181a218a111c18b182b183a13c18a12b12b238e238a13b218b18a2187f18b181 e11b218a218a11c18a21812f12b121b127f12b14f16b14f16b12b12b16b167f122f12f12f121b1 6f12b12b12f16b16b16b14b121b18b1ab18b16b16b1ab1eb16b1cf121b12f12b12f12b12b12b12 b12b121f12f121b12f12b1211b12b121b12b12b1234b16b12b12b1c1b1eb14b14b1410c18e11b1 1c18a218a238a218a12b2381e228a258a218b18b18a258a278a16b17b228a141b12b11b13b218a 228a12b238a11b238a2282f181b18b18f182b18a218b18a12f228a238f18a2384f18b18b182b18 b18b18b181b18f18f181e12f121b12b121b1210b12b12b12f12f121b12b121b12b12b12f12f121 b12b121f14b12b12b12f16b16b14f127f1ab1ab16b12b1cb1ab1eb1ab1cb18b182b111b11b11b1 1f1110b11b111b11b11b11b11f11b1110818a228e238a11b12b238a13c18a11b1110c18b181b18 b18b18b18b18b181e218b18b18e11b218a11b218f187e13b248a13b268a258a228a278a248a248 a228a2781b18a218a238a238a218a218a238e228b18a121c1ca11c1ca214a214a218a21ca218a2 1ca21ca21c193194a238a268a16a3148a97c01fc01aa31aca244a2581b1cb14f14b1cb1cb1c1f1 c33b14b1c1f14b1cb1cb14b1cb1c7f14f14f14b14b14b14b14b14b141b14f14f14b14b14b14b14 f141b14f18b1cb1cb18b1cb1cb1cb14b1c2b141f14b14b142b12b12f16b16b12b16b16b12b14b1 44d2fc3c25c13c22810e01000000000000000052533401000002a8000005200000390509e00020 0100e21c6b8a05028146a11f11f11b11118181b18f18b18b1811b18b181b18b18f18b1816e218b 18a218b181a218b18a218e2181b12b12b14b14b12b12b16b16b14b12b121b12b12b16b16b16b16 b16f14f141b12b121f12b12b12f12b121b12b121f12b12f12b122b16f14b14b12b16b16b16b12b 141b12b1eb18b16b1ab16b1ef16b1eb1e3b12f12b12f12b121b16b1ab1ab1eb1cb1cb1eb1ab14b 16b1c7f12b12b14b12b14f16b12b12b14b121b12b12b12f16b16b16b16b14f122b121b12b12b12 f1210b12b12f12b12b12b121b1210b14b12b14f16b16b16b12b16b141f12f12b12f12b12f12f12 1c182b18b18f18b182b181f18b18b18f181f18b18f18b18f18b18f1810b18a11c18b18e11b218f 18a11c1810b18f181b18b18b18b183a12b121b12f12b12b121b121f12f12b121b121f12b121b12 b12b12b1211b12f12b12b12b12b121b121b121b12b12f12b12b122a21ca11c1ca11b21ab1ea21e a21eb1ca21ab1c1b13b11b13b12b13b13b13b11b12b131f1cb18b12b19b13b15b1fb15b1af1f1b 11f13b11b11f13b12b11b13b111c182b18b18b18f182b182b18b1811b18b18b18b18f18b18f18b 181e12b218e238a3302b42038ba82030202821d218a11c18a11b11f218b18b18a11c181a218b18 a218b181a218f18e111b12b13b12b238a12b228a238b18a13b228a2381f14b14b141b14b14b142 a12b22ca13b21ca234a22ca23ca11b238b14a22c1b1cf14b14f1cb1cb14b18b182b141b14f14f1 4b14b143b14b14b14f14b14b1416b14b14f14b14f141f141b1cf1cb1cb1cf1cf18f141b1ca1118 18b18a214a21ca214b18a21ca2141f18a218a11b21ce21ce214b18a21c1b1cb14f18b18b1cb1cb 14f181f18b14b18f18b1cb1cf14f141b12b12b141b16b16b12b16b166c2bc20b27e6a010525334 01000002e00000058e0000390509e000200100e21d6b96fa7c7f9ffd79f1e7c9a69a61d11b11b1 1b11b11f113b13b238a12b218e228a238a218b18a12b2381b18a11f218e218a218b18a218a11c1 81b182f181f187f18a218a111818b18a218a218b18a11b1134814b14b16b16b12b16f14f121b12 b14b1ab14f1ab1ef1ab1e1f12b16b1cf12b12b1eb12b14b14b181b12b16b16b18b1eb12b1eb18b 1ab181f1cb1ab16b16b1cb1eb1eb18b12b1ab1e7f12b12b121b12b12f12b122b1cb1eb16b18f1e b16b1ab1c10b12b12b12b12f12b121b122b12b12f12b12b12f1210f1ab14b14b12b1eb14b1ef1e f143b12b12b12f12b121f16b12f14b12f16b14b16b14b141f11b11b2181a218a218e2182f18b18 f18b181b181f18b181b18f18f18b18b181b18a218a218e11c18a218a218b182b18a2181a11c18a 218e1118181f18f18b18b18b18b18f18b181a12b218b18a238a13c18a238b18a1318181e12b122 b12b12b12b122b18b1eb1cf16b1eb1af18b121b18b1ef1ab14b1cb1eb1ab14b1cb161a21ea22ca 236a234f1aa23ea22aa21ea218a131c1eb1eb1ab16b1ef1eb14b18b1cb1c2b12f12f12f12b12b1 210b13b13b13b11b11b13b12b11b12b121b1ab13b19b1eb1ab11b1fb18b17b1ab1210818f18b18 b18f181b18b187e238a2381a12b218a238a13b228a238a121b13b13b218b18a228b18a238a11b2 38a228a21810f18b18b18b18b18b182e13b228a11b238a12f238a13b12c18a2381b18b18b18b18 f18b18b181b181a218a214a11b214b18a11b21c1a11b21c16f18b14a11c1ca21ca21ca218a11c1 8a21c1b14b14b1cb14b14b1cb1cb18f1cb141b14b18b18b1cb14f1cb14b18b1cb1c1b14b18b14b 18b1cf1cb18b182b14a258a278a14c1ca268a27ca17b24ca26ca2541b1cb18b18f1cf1cb1cb1cb 18b141b14b14f14b14b14b143a214b14a21ca228a228b18a23ca23ca228b14a21c1a214b181a21 8b1ca21ca218b18b18a2187f121f12f12b12f12b1213e32586e22e128010005253340100000200 000003c10000390509e000200100e21e6b9f7b7dbe3ffdf8f1e3c9a69a61d11f111b11b11b11b1 1b11b111b218a218e11b11f218b18a218a11c181b182f181b183f18a11b218e218a11b1117818b 181f18f183b18b18a218a11b218b18a218b18a21816f12b121f12f12f121f121b12f12b1226f12 2b12f12b121f16b12b16b12b12b12b16b14f121f121b121b12b12b122f12f121b121b12b1210b1 2b12b12b12b12b12f12b12b1210b12f12f12b12b12f12b1210f12b12f12b12b123c182b18b18b1 8f181b18b18b181b18b18b18b18b1833e238a12c18a13b238b18a2381a238a23810a1eb1eb14f1 4b1cb1eb1eb1ab14b1a7f121b12f12b12b122f12b121b12f12b12b12b121f12b12b12b121b121f 121b12f121f12b12b12b12b127f12b1cb15b16b1bf1fb1bb18b1fb1710818f18b18f18b183b18a 228a228b18a238e238a228a11f122818f18b18f18b18b182b18f181b18b18b18f181e11b111818 a218b18a218e218a218a111b218f181a218a218a11c18b18a111c181b18f18b18b18b18f182b18 b18f18b18b18b18b18b187f1cb1cb1cb14b14f1c1b1cb1816b141f14b14b14f14b141f14b14b14 f14f14b14b14b141f14b141b14b14b14b14b142b141b141b14f14b14b147e218a11f214b14b1ca 21cb1ca218b1cb1c7f14f141b14b1cb1cf1cb1c30d27e4e31f8149010000000000000000525334 010000002000000007000009530d0000200100e302112c03000000000052533401000000200000 00060000047e004000200100e40243f0200000000000525334010000002000000006000008dc02 0000200100e502106f000000000000525334010000002000000005000005100f2000200100e609 50c000000000000052533401000000200000000600000731090000200100e70071c01000000000 0052533401000000200000000600000175040000200100e80013d0100000000000525334010000 00200000000600000189020000200100e90014a010000000000052533401000000200000000600 000339090000200100ea0431e01000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000023560301010000000100000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000020dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000 00000000000000000000000000000000030dc400c8012c00dc001e07ec00bb00f000d000180e8c 00d2016800e500240000000000000000000000000000000000000000040dc400c8012c00dc001e 07ec00bb00f000d000180e8c00d2016800e5002400000000000000000000000000000000000000 00050dc400c8012c00dc001e07ec00bb00f000d000180e8c00d2016800e5002400000000000000 0000000000000000000000000053423100b375fcf4c218bf26ad36bbb21e2349657abcb01dbe58 6626b375fcf4c218bf26ad36bbb21e2349657abcb01dbe58662650420501000000005046020000 788410015254045652544e534f020000494efe0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046007884740052540456535243494e6400000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 5046030000007884300052540456524d4c5644023031504e0730304b56363237534e0c59413139 3332303936393530545604303030345046010078849c0152540456574d4c56440230314f430400 000307464f1102ffffffffffffffffffffffffffffffff23495401010454000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000494e140131393336333530303030323432303134000000 5046030000007884ac00525404435250305644023031454421010000000000000000000008b0d0 002eab62043c000000487818ec18028a00598054450b0130323033514651415346444405013032 3032535403010000444e4901240000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000049510b0100000000000000000000504603000000788448065254044c5250345644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100be013600d2002407ed00a400 aa00be00180e6b00c6016000da0026000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000544313140000191f01f3191f01f3191e01f5192001ec234d010103000002bd 0320016b0000034003a301e30000031503a30262000002d003a3030c000003c30426023d000003 97042602f600000352042603eb000002ee042604b80099044704aa027e0099041b04aa035c0099 03d704aa04810099037304aa058c000002dc04aa064d0000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52503556440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00be013600d2002407ed00a400aa00be00180e6b00c6016000da00260000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191f01f2192001ed191f01f1 191f01f4234d010103000002bc0320015d0000034003a301d10000031403a3026a000002d003a3 030a000003c30426022e00000397042602e900000353042603c8000002ed042604a10099044804 aa02760099041c04aa0352009903d704aa046d0099037304aa0578000002dc04aa064f00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525036564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100be013600d2002407ed00a400aa00be00180e6b00c6016000da 002600000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191e01f7191e01f5191e01f5191f01f1234d010103000002bc032001710000034003a301e000 00031403a30260000002d003a30314000003c30426024700000397042602e900000352042603d9 000002ee042604bd0099044804aa02740099041b04aa034d009903d704aa04680099037204aa05 73000002dc04aa064d000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000434801004951090200000000000000005046020000788448065254044c5250435644 023031235603010100000001000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000020d6100be013600d2002407ed00a400 aa00be00180e6b00c6016000da0026000000000000000000000000000000000000000003000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000004000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000005000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000023500104010000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000054431314000000000005000000050000000500000005234d010103000002bc 031f01630000033f03a201db0000031403a20279000002cf03a2030a000003c204250244000003 960425030200000352042503de000002ec042504ab0099044704a902830099041a04a903570099 03d604a904790000037204a90591000002db04a906520000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000434801004951090200000000000000005046020000 788448065254044c52504456440230312356030101000000010000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000020d61 00be013600d2002407ed00a400aa00be00180e6b00c6016000da00260000000000000000000000 000000000000000000030000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000040000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000050000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000235001040100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000054431314c9ec191f01f3191e01f8191f01f2 191e01f6234d010103000002bc031f016b0000033f03a201e80000031403a20279000002cf03a2 0316000003c20425024700000396042502f800000352042503dc000002ee042504b80099044604 a9027e0099041a04a9035f009903d604a904810000037204a9059b000002dc04a9065e00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000004348010049510902 00000000000000005046020000788448065254044c525045564402303123560301010000000100 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000020d6100be013600d2002407ed00a400aa00be00180e6b00c6016000da 002600000000000000000000000000000000000000000300000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000400000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000500000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002350010401000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000005443131400 00191e01f8191e01f6191e01f6191f01ef234d010103000002bc031f01730000033f03a201d100 00031403a20274000002cf03a20302000003c20425024100990396042502e900000351042503d7 000002ee042504a10099044704a902850099041a04a9034b000003d604a904630000037204a905 76000002db04a9064f000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000043480100495109020000000000000000504602000078842c005254044c524d305644 02303254430516000000004d430400000000494e08000000000000000050460300000078842c00 5254044c524d31564402303254430516000000004d430400000000494e08000000000000000050 460300000078842c005254044c52493056440230325443051600000000494e1000000000000000 000000000000000000504602000078842c005254044c5249315644023032544305160000000049 4e100000000000000000000000000000000050460200007884b8005254044c5750345644023031 2332440001040f0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000233344000104 0f0000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000494e13000000000000000000 000000000000000000005046030000007884b8005254044c57503556440230312332440001040f 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000002333440001040f000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000494e1300000000000000000000000000000000 0000005046030000007884b8005254044c57503656440230312332440001040f00000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000002333440001040f00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000494e130000000000000000000000000000000000000050460300 00007884b8005254044c57504356440230312332440001040f0000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000002333440001040f0000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000494e13000000000000000000000000000000000000005046030000007884b80052 54044c57504456440230312332440001040f000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000002333440001040f000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000049 4e13000000000000000000000000000000000000005046030000007884b8005254044c57504556 440230312332440001040f00000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000002333 440001040f00000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000494e130000000000 00000000000000000000000000005046030000007884d8005254045645523056440230312349c4 000104300000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000504601007884d8005254044d45523056440230312349c40001043000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000050460100780003ca8f47b6f3 61985975001fffffffffffffffffff001fffffffffffffffffff001fffffffffffffffffff001f ff73ff7f5faffffe970013c889c766e861985975001fffffffffffffffffff001fffffffffffff ffffff001fffffffffffffffffff001fff73ff7f5faffffe970c16d717318e036fdd1be66effff 377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6cf0c12d717318e 036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2a e6cf0c06d517518e036fdd1be66effff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef77 77a6a82c0c869c6af6cd0c52e71211ee036ddd1be66effff377ffdab15e55fb4009a31771ff7bb 7ffddfbf6efcef7777a6a82c1c869c2af6ed0c56e71231ee036ddd1be66effff377ffdab15e55f b4009a31771ff7bb7ffddfbf6efcef7777a6a82c0c869c6ae6ef0c52e71231ee036ddd1be66eff ff377ffdab15e55fb4009a31771ff7bb7ffddfbf6efcef7777a6a82c1c869c2ae6ef6918590545 d2099333385d17691c590545d0099333385d577912dd01457a7f02d359631d7916dd0145787f02 d359635d4e392e346b6c688f0a597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbcbedbdf4ead00a3936e059658913bcc870695f320db06e881c0 4b089a83334a0832e0f34d06b53e5b7067ffbefefbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa09bf1a86e3d2e347b6c688f0a597b27a5bee690d26f87068171 6a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbcfef3dace2d8ab1fb01d10422 89fa85211a757a2bc502a0f98c6db994a1e80d3b1acc68afc78d1cbb7a67bfbedafbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa09bf1a84e212c342b6c688f0a 597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bef6bbff4e1f9aed616be9b850d16105aa161973424730d8bb452f2f1ccfd04cfa90e2bb65a581 9c4b6267ffbedefbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a0dbf9a86fb526307bec608e0a597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf 36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be 9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefe fbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fefbcb6dbdb4ebd12639b6c9d0672eb3d61949c1e10641f84388abe 009b3484aa0c53226e79ed43a31c4b7867be9efafbefedbfbefffb6fffbefefbffefbfbefffb6f ffbefefbfbef3db6b69a474f8aa2dbf9a04fb926306bec608e0a597b27a5bee690d26f87068171 6a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefefbffafb7befffb6f7fbefefaff e7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fff befefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbf befffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbe fefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbe fffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fedbcfef3d3cf1b906e7b43138e44 61eca19c803e900d17825e28068b2026eee9290720ce61550789b80bf867bebefafbefedbfbeff fb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8aa29bf1a06fbd26307bec608e0a 597b27a5bee690d26f870681716a670852a1b2a79e2ad96969bf36fef9ffefbfbeffdb6fffbefe fbffafb7befffb6f7fbefefaffe7bbbc5ffb476dbedeebb4e7be9aeffb6fbfbefefbffefbfbeff fb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefb ffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb 6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbff efbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6f ffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffef bfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fffbefefbffefbfbefffb6fef bcfedbde4eb9827a1a6c978cbcb88f2439bc561a260fbe7840660f928e3e3307680238284f09b6 bedb6a6fbe9efafbefedbfbefffb6fffbefefbffefbfbefffb6fffbefefbfbef3db6b69a474f8a a29bf1a0000f175d395ebdc7baa23d001504f9f3747bd6b459d1001fffffffffffffffffff001f f677eba7ac607d7d8250facffc590a887d158a2a15f5e87a9548aabf7fefff55ffefffffeaafff ffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffefffffeaafffffefff55ffef ffffeaafffffefff55ffefffffeaafffffefff55bea75fefaa876e38035910b4eeba3734b24032 67956fd088a31514240714b888a2f0cfef999bfb3777feeefffddfffbbbfff677eda8e1f5d58b6 22614f255372b04bbd13ef6cffff777fffbb7ffddfff6effff777fffbb7ffddfff6effff777fff bb7ffddfff6effff777fffbb7ffddfff6effff777fffb93ffcccde6eece32801d828852128b41f 76d87256595db7572be0b501801404d26405427b310117ea1e967e59e73213039f022a0cb15450 e2d361b5ab175012c0a40c11d23b33c964e74a57e17465fa4976ed43b72240b855d7b26b709432 0830734e09f7553950eb371820927c7cd6ac1b01df34f72e670b35330d53d389501902e5556d44 3a2b5162710844c5364547345a400d35a61e25b928738a1a044317a14440d37d03b1082767424f 68d2047046247824d5154f4ae2f175b59612619e66610e80d24861bf62c5ee73f242e6c4453644 6b5241264b36f60e1881516a543a260e56f4c72874d063b03f27457005ce6d407111927a516838 d1b23591884b91b7375d2ed74b4d118b11843e63a164b5362844dc6a517b752d08121b2001ae00 76aa062436c7fb59072a1a774f21497085ce6d407111927a516832f0d72976d06b90b807e56005 7655507178e3b011ec26e4e604f2b873114135705853152497f94324e712a61837b240145a1272 74778518734e7860ef1a905256683270c72976d063b03f27457005ce6d40f31181f661ba28f1d1 7cf5e031057960f14a011248c2c30bd3b167113e80e765131f4b30f415ac1a359449c15f69943b 23af24e6052875d860304f25453005ce6d407111927a517830f0b7255613634329547c72267944 41362ad67644390477bf50141b2bf57a26383801471996ed7054e427360a44bb6913491a73c901 495805ce0c405111927a516832f0d72976d063b03e37657205c06974194aa1ff02e818c0ba18a6 b043d21b51fe6031276c66f922679c13e276e3d621715e705624760b4ef7677422437a1372166c 3270c72976d063b03f27457005ce6d407119b27760383cf49e38e5a65991af213c74055a5421cb 636252371b66e18335a14958f616242e76d77a2ca5113285a2018d2c104b69a60333075b30a31a 92426dc11f003190670b68218d68034210528d60ef6ab0a84101296b7114423930348930510253 339851912ce2c129b7dd73469312067c84f67103be50106d42e362050220465418b3f837906c72 ea4d873710504341f52c116a15d39d00e39a773a2c456440524b13702815c37011e95d86085910 0d538d60410369545921b18a14fa4280210c26f103a52936f432c46d09207a2154c042c924d42c 4136997a545855ca60b5ee5dd42c72643840b40611862db6b012043c233a0c205d6007d62ab0c9 07270e23797987d12a77f7617f0ef3ff7cf7df7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7fe77 cf7ed77a6d63b7114563752d66852a7432af7a517950657054e440650320b5e163b040677b6914 662a9665473058c1010db26e60267d11ee7041ed35835121e06267127c17902d50aa09c29d2682 5602836815237bf47220ec62569604777412b45a16035215b07852ad79c7c543033e14f515777a 41738065192846e730760c62c153674268c09034d66c53d28a232278c1cc51734713c61e544b68 f1f16982560371b850b10e53b440b11373745302ca5480a17d57707955da275028915275406568 244a26b85e464141d3cb03813971bf62558a0d524219675e44682034eb1952c630207607b45004 c57c50826b35db06465c617208d08e11e05210565eb1b600a66c51a0a8541d42b44a5014521a71 9e725b7453591cc62a31c7a7455e28b4df65656d51f03f53a86605884875a172e22e354372c590 0cc14639057a03705627de61564039102515b76814f26503e03af74b074400f70e54b3f4320635 121564842f60d23260527615706495024431d2710443613a0ee44279854f71e7f9116828418b5c e1db5b53b913cd18b1417de1e24ba18a514e7682282854b462927043dc2e83877983dd5121df01 bf2e84873c73851945bf51f21085b65c842143208932c04651d33cc2420954b723106e377a4816 1160877334386e47f118a27a0313a972e944a64b35730c63817147b370604d7982c841d1d845cc 34b6df04214529f672665412e36855e3d570c62261241e917d35d5596b736f70d95050f169f15b 69a55454203a17d339b41668d2e5277008e4b0606375316454715e08654f35f0007883ec06e050 a6f028751d3b0265541230c12a6534d21a975620fd6e66f87d16486342d110e43c443238a36a78 912f70175240e334d5ba5ba7ca742b7ad18921865d5ab7e703fa32b6441187941a04cd20002802 ff55f4cc3996e8517b3ed4331c173f3b63a701f12c975751a0075a001e21e660f67c1810aa5ab6 e326c778f3e65880ba31e65523741463fb54906522375a55107a63041cd6954287c361c636813f 14d37f1095d644862c07665854eb23c35632617c944419143933d42c35324840f02d93905075d7 200a0671ee3d36534ba0aa718c58f06421606c23c4aa651a3a625451c4d31017df773040563425 451442250e463c24904625f32a09053d37ef6c86e22514c218d01154ec1a022a50642e02444a51 e73615582016ac1014df15e168e7e46917e53b87fb524340f6ba4dc48e3152cc14f166753f4c27 96233226001108729a48b0152aa6dd647a46d7835491a54bf7ce45e82e020d1053b31be1da1769 3a73eb11051903322671e11eb4957137b871d64133665ec7c014b1b00a571d25e66800ca24764b 2ab11e410e20863d6123f61313b174a536d65570241f69d148175b7cb31c05540c6855eb207c04 d7f751f18e31565c543c0c360614003a29219d403d4cc1243073983a3313240d5ef7d96da4f600 e75612a03037f944868862633572d14c86c838235811f79a106014446501b4990052a461a27c17 ce58f10a52d28201927c40242806da6bf672248620f2005c5456183017319b00b2070de4db7195 ff35f73e75ef3df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff 7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff 7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7e f7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7b f7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7 ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7 ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff 7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff 77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7d f7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77 ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7ff7bf7ff77ff7ef7ff7df7 ff73f7fc57cf72c77b6d30e97b33df77ef5ef7fb7137fa73019b41197a70f618f531603091054e 30513344618f71204101d75a40491933565ad39524d80e75484511b30951ed11fb4c40b3253438 5a120c04781e118b18431040a5be4206f264017a19319c44e96233131424800932c50631023e7d 0221118a1fa88be808a9a901bd0ed1c4e8ae485ca0ee4505671e40080bec64c9fb11c68853a300 21353bec7a04a18ea4adea8153876b5b01ff43f932c00bf509e22651d00f2feae802970709e055 31860fa8820b7d6ea6661089c0819d200de1808ed5115f068e8a00000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000 prop: ibm,ccm-node-id size: 4 val: 00000000 prop: ibm,hw-card-id size: 4 val: 00000000 prop: ibm,hw-module-id size: 4 val: 00000001 prop: ibm,mem-interleave-scope size: 4 val: 00000000 node: chiptod@40000 prop: reg size: 8 val: 0004000000000034 prop: compatible size: 37 val: 69626d2c706f7765722d63686970746f640069626d2c706f776572372d63686970746f6400 node: nx@2010000 prop: reg size: 8 val: 0201000000004000 prop: compatible size: 27 val: 69626d2c706f7765722d6e780069626d2c706f776572372d6e7800 node: pbcq@2012000 prop: reg size: 24 val: 00200102200000000020010905000000003c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000000 prop: ibm,hub-id size: 4 val: 00000003 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333300 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 7777777777777777777777777777777777777777777777777777777777777777 node: pbcq@2012400 prop: reg size: 24 val: 00240102200000000024010905000000403c010915000000 prop: compatible size: 16 val: 69626d2c706f776572382d7062637100 prop: ibm,phb-index size: 4 val: 00000001 prop: ibm,hub-id size: 4 val: 00000003 prop: ibm,loc-code size: 25 val: 55373843422e3030312e575a533030414c2d50312d43333300 prop: ibm,use-ab-detect size: 0 val: prop: ibm,lane-eq size: 32 val: 7777777777777777777777777777777700000000000000000000000000000000 node: psihb@2010c00 prop: reg size: 8 val: 02010c0000000010 prop: compatible size: 31 val: 69626d2c706f776572372d70736968622d780069626d2c70736968622d7800 skiboot-skiboot-5.1.13/hdata/test/p81-811.spira.heap000066400000000000000000200131001265204436200216570ustar00rootroot00000000000000ÑðSPINFO!@ `dL°¸ Ø` „ŒRTVINIDRI/O BACKPLANE CE1VZ02FN00E2022PN00E3997SN YL10UF42L013CC2CD6PR!HE0002CT€ó&HWB3B4B7 PFx„RTLXR0VZ01LX10BPFx„PRTVR10DRFSP FGVNSPDCBD 201208230800FLP1 PFx„@RTVW10DRFSP VW10 GD PFxà(( €ÿþ€ €ÿþ€ÑðIPLPMS\à h@¨ È èì( ¸ 22L524DK0E00 € 7  ÐPFÿ X'f€ªþ` @ÀTTU78CB.001.WZS00AL-P1-C1-T1*ÑðFRUVPD P @HüD„ŒRTVINIDRI/O BACKPLANE CE1VZ02FN00E2022PN00E3997SN YL10UF42L013CC2CD6PR!HE0002CT€ó&HWB3B4B7 PFx„RTLXR0VZ01LX10BPFx„DRTVCENDRCEC SEWZS00ALTM FC78CB-001RGRB PFxÑðSLCA !ð 0¸Ihh VV PU8247.22L.1010C8A VVÿÿÿÿPU8247.22L.1010C8AÿÿSVÿÿÿÿPU8247.22L.1010C8ASVÿÿÿÿPU8247.22L.1010C8Aÿÿ¡SAÿÿÿÿPU8247.22L.1010C8AÿÿEVPU78CB.001.WZS00ALÿÿ¢EIÿÿÿÿPU78CB.001.WZS00ALÿÿ£EFÿÿÿÿPU78CB.001.WZS00ALÿÿBP- PU78CB.001.WZS00AL-P1ÿÿ- SPÿÿÿÿPU78CB.001.WZS00AL-P1ÿÿ OPÿÿÿÿPU78CB.001.WZS00AL-D1ÿÿ $DBC,PU78CB.001.WZS00AL-P2ÿÿ $DBÿÿÿÿPU78CB.001.WZS00AL-P2ÿÿ 0POÿÿÿÿPU78CB.001.WZS00AL-P1ÿÿ1PSÿÿÿÿPU78CB.001.WZS00AL-E1ÿÿ1PSÿÿÿÿPU78CB.001.WZS00AL-E2ÿÿ:AMÿÿÿÿPU78CB.001.WZS00AL-A1ÿÿ:AMÿÿÿÿPU78CB.001.WZS00AL-A3ÿÿ:AMÿÿÿÿPU78CB.001.WZS00AL-A4ÿÿ:AMÿÿÿÿPU78CB.001.WZS00AL-A6ÿÿ:AMÿÿÿÿPU78CB.001.WZS00AL-A2ÿÿ:AMÿÿÿÿPU78CB.001.WZS00AL-A5ÿÿ BXD)PU78CB.001.WZS00AL-P1-C1ÿÿNBÿÿÿÿPU78CB.001.WZS00AL-P1-E1ÿÿPFÿÿÿÿPU78CB.001.WZS00AL-P1-C32ÿÿPFÿÿÿÿPU78CB.001.WZS00AL-P1-C32ÿÿPFÿÿÿÿPU78CB.001.WZS00AL-P1-C33ÿÿPFÿÿÿÿPU78CB.001.WZS00AL-P1-C33ÿÿAVÿÿÿÿPU78CB.001.WZS00AL-P1-C13AVÿÿÿÿPU78CB.001.WZS00AL-P1-C13ÿÿ#ICÿÿÿÿPU78CB.001.WZS00AL-P1-C3ÿÿ#ICÿÿÿÿPU78CB.001.WZS00AL-P1-C5ÿÿ #ICÿÿÿÿPU78CB.001.WZS00AL-P1-C7ÿÿ!#ICÿÿÿÿPU78CB.001.WZS00AL-P1-C6ÿÿ"(CEÿÿÿÿPU78CB.001.WZS00AL-P1-T1 ÿÿ#(CEÿÿÿÿPU78CB.001.WZS00AL-P1-T2 ÿÿ$)CUÿÿÿÿPU78CB.001.WZS00AL-P1-T5ÿÿ%)CUÿÿÿÿPU78CB.001.WZS00AL-P1-T6ÿÿ&)CUÿÿÿÿPU78CB.001.WZS00AL-P1-T3ÿÿ')CUÿÿÿÿPU78CB.001.WZS00AL-P1-T4ÿÿ(6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C11ÿÿ)6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C10ÿÿ*6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C9ÿÿ+6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C12ÿÿ,6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C7ÿÿ-6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C3ÿÿ.6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C5ÿÿ/6PIÿÿÿÿPU78CB.001.WZS00AL-P1-C6ÿÿ06PIÿÿÿÿPU78CB.001.WZS00AL-P1-C2ÿÿ1;RDGPU78CB.001.WZS00AL-P1-C14ÿÿ2;RDHPU78CB.001.WZS00AL-P1-C15ÿÿ3ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C16ÿÿ4ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C17ÿÿ5ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C18ÿÿ6ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C19ÿÿ7ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C20ÿÿ8ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C21ÿÿ9ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C22ÿÿ:ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C23ÿÿ;ÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C24ÿÿ<Ð MSÿÿÿÿPU78CB.001.WZS00AL-P1-C25ÿÿ=Ð MSÿÿÿÿPU78CB.001.WZS00AL-P1-C26ÿÿ>Ð MSÿÿÿÿPU78CB.001.WZS00AL-P1-C27ÿÿ?Ð MSÿÿÿÿPU78CB.001.WZS00AL-P1-C28ÿÿ@Ð MSÿÿÿÿPU78CB.001.WZS00AL-P1-C29ÿÿAÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C30ÿÿBÐMSÿÿÿÿPU78CB.001.WZS00AL-P1-C31ÿÿC,ID ÿÿÿÿPU78CB.001.WZS00AL-P2-D15ÿÿD)CUÿÿÿÿPU78CB.001.WZS00AL-P1-C1-T2ÿÿE)CUÿÿÿÿPU78CB.001.WZS00AL-P1-C1-T3ÿÿF*CSÿÿÿÿPU78CB.001.WZS00AL-P1-C1-T1ÿÿGNB1ÿÿÿÿPU78CB.001.WZS00AL-P1-C14-C1ÿÿHNB2ÿÿÿÿPU78CB.001.WZS00AL-P1-C15-C1ÿÿÑðFRUVPD  @H´D„ŒRTVINIDRI/O BACKPLANE CE1VZ02FN00E2022PN00E3997SN YL10UF42L013CC2CD6PR!HE0002CT€ó&HWB3B4B7 PFx„RTLXR0VZ01LX10BPFxÑðSYSVPD  @H¸D„°RTVSYSDRSYSTEMBRS0SE1010C8ASG TM8247-22LTN MN ID SU¬CTNN RGðÀRB3 WN C050760780D2FV SV810_029PFxÑðFRUVPD  @HÔD„ÌRTVINIDRANCHOR CE1VZ01FN00E3427PN00E2148SN YL101142R000CC524DPR0HE0010CT@´HWB3B4B7 B9ßurM2HÁ´$kîM3g´TØHŸ:%M4 ­ÄA$µíÕPFxÑðFRUVPD Ð @HˆD „€RTVINIDRCEC OP PANEL CE1VZ02FN00E1966PN00E3770SN YL10UF41800FCC2B08HE0001CT€µHWB3B4B7 PFxÑðFRUVPD Ð @HˆD „€RTVINIDRNATIVE I/O CARD CE1VZ01FN00E2164PN00E3811SN YL10UF42L001CC2B0BHE0001CT€µHWB3B4B7 PFxÑðMS VPD#à Hh„Œ(´Äà€ÿÿÿÿ€ÿÿÿÿ€€üÑðMSAREA" p”œ08@ÐØ8@3Є”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42D1A9CC31E9PRISZ0032768HE0001CTHWB3B4B7 PFx@à€  ï!`é ÑðMSAREA" p”œ08@ÐØ805Є”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42D1A6CC31E9PRISZ0032768HE0001CTHWB3B4B7 PFx@à€  ï!`é ÑðMSAREA" p”œ08@ÐØ8 ;Є”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42C17TCC31E9PRISZ0032768HE0001CTHWB3B4B7 PFx@à€   ï!`é ÑðMSAREA" p”œ08@ÐØ8=Ð „”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42C1AHCC31E9PRISZ0032768HE0001CTHWB3B4B7 PFx@à€   ï!`é ÑðRAM  PTœðô3Є”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42D1A9CC31E9PRISZ0032768HE0001CTHWB3B4B7 PFxÀ€ÑðRAM   PTœðô5Є”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42D1A6CC31E9PRISZ0032768HE0001CTHWB3B4B7 PFxÀ€ÑðRAM   PTœðô;Є”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42C17TCC31E9PRISZ0032768HE0001CTHWB3B4B7 PFxÀ€ÑðRAM   PTœðô=Ð „”RTVINIDRIBM 32GB MS CE1VZ12FN00JA664PN00JA664SN YH10MU42C1AHCC31E9PRISZ0032768HE0001CTHWB3B4B7 PFxÀ€ÑðIO HUBj Ppt„ ÀÀÀ!wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwÀ!wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwhhhhhhhhhhhhhhhhÑðIO KID  @H´D„ŒRTVINIDRI/O BACKPLANE CE1VZ02FN00E2022PN00E3997SN YL10UF42L013CC2CD6PR!HE0002CT€ó&HWB3B4B7 PFx„RTLXR0VZ01LX10BPFxÑðIO HUBj Ppt„ ÀÀ€!wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwÀ!wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwÑðIO KID  @H´D„ŒRTVINIDRI/O BACKPLANE CE1VZ02FN00E2022PN00E3997SN YL10UF42L013CC2CD6PR!HE0002CT€ó&HWB3B4B7 PFx„RTLXR0VZ01LX10BPFxÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€ €ÿÿ€ !€ÿÿ€!"€ÿÿ€ "#€ÿÿ€0#$€ÿÿ€@$%€ÿÿ€P%&€ÿÿ€`&'€ÿÿ€p'ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€0€ÿÿ€01€ÿÿ€12€ÿÿ€ 23€ÿÿ€034€ÿÿ€@45€ÿÿ€P56€ÿÿ€`67€ÿÿ€p7ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€`€ÿÿ€`a€ÿÿ€ab€ÿÿ€ bc€ÿÿ€0cd€ÿÿ€@de€ÿÿ€Pef€ÿÿ€`fg€ÿÿ€pgÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€h€ÿÿ€€hi€ÿÿ€ij€ÿÿ€ jk€ÿÿ€°kl€ÿÿ€Àlm€ÿÿ€Ðmn€ÿÿ€àno€ÿÿ€ðoÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€p€ÿÿ€pq€ÿÿ€qr€ÿÿ€ rs€ÿÿ€0st€ÿÿ€@tu€ÿÿ€Puv€ÿÿ€`vw€ÿÿ€pwÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€¨€ÿÿ€€¨©€ÿÿ€©ª€ÿÿ€ ª«€ÿÿ€°«¬€ÿÿ€À¬­€ÿÿ€Э®€ÿÿ€ய€ÿÿ€ð¯ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€°€ÿÿ€°±€ÿÿ€±²€ÿÿ€ ²³€ÿÿ€0³´€ÿÿ€@´µ€ÿÿ€Pµ¶€ÿÿ€`¶·€ÿÿ€p·ÑðSPPCIAÌ `X¸ÈP°È !L‰Ô€ a €@€€ €€€€à€ÿÿ€àá€ÿÿ€áâ€ÿÿ€ âã€ÿÿ€0ãä€ÿÿ€@äå€ÿÿ€P忀ÿÿ€`æç€ÿÿ€pçÑðSPPCIAÌ `X¸ÈP°È !L‰Ô€ a €@€€ €€€€è€ÿÿ€€èé€ÿÿ€éê€ÿÿ€ êë€ÿÿ€°ëì€ÿÿ€Àìí€ÿÿ€Ðíî€ÿÿ€àîï€ÿÿ€ðïÑðSPPCIAÌ `X¸ÈP°È !L‰Ô€ a €@€€ €€€€ð€ÿÿ€ðñ€ÿÿ€ñò€ÿÿ€ òó€ÿÿ€0óô€ÿÿ€@ôõ€ÿÿ€Põö€ÿÿ€`ö÷€ÿÿ€p÷ÑðSPPCIAÌ `X¸ÈP°È !L‰Ô€ a €@€€ €€€€(€ÿÿ€‚€()€ÿÿ€‚)*€ÿÿ€‚ *+€ÿÿ€‚°+,€ÿÿ€‚À,-€ÿÿ€‚Ð-.€ÿÿ€‚à./€ÿÿ€‚ð/ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€0€ÿÿ€ƒ01€ÿÿ€ƒ12€ÿÿ€ƒ 23€ÿÿ€ƒ034€ÿÿ€ƒ@45€ÿÿ€ƒP56€ÿÿ€ƒ`67€ÿÿ€ƒp7ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€`€ÿÿ€†`a€ÿÿ€†ab€ÿÿ€† bc€ÿÿ€†0cd€ÿÿ€†@de€ÿÿ€†Pef€ÿÿ€†`fg€ÿÿ€†pgÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€h€ÿÿ€†€hi€ÿÿ€†ij€ÿÿ€† jk€ÿÿ€†°kl€ÿÿ€†Àlm€ÿÿ€†Ðmn€ÿÿ€†àno€ÿÿ€†ðoÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€p€ÿÿ€‡pq€ÿÿ€‡qr€ÿÿ€‡ rs€ÿÿ€‡0st€ÿÿ€‡@tu€ÿÿ€‡Puv€ÿÿ€‡`vw€ÿÿ€‡pwÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€ €ÿÿ€’ ¡€ÿÿ€’¡¢€ÿÿ€’ ¢£€ÿÿ€’0£¤€ÿÿ€’@¤¥€ÿÿ€’P¥¦€ÿÿ€’`¦§€ÿÿ€’p§ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€¨€ÿÿ€’€¨©€ÿÿ€’©ª€ÿÿ€’ ª«€ÿÿ€’°«¬€ÿÿ€’À¬­€ÿÿ€’Э®€ÿÿ€’ய€ÿÿ€’ð¯ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€°€ÿÿ€“°±€ÿÿ€“±²€ÿÿ€“ ²³€ÿÿ€“0³´€ÿÿ€“@´µ€ÿÿ€“Pµ¶€ÿÿ€“`¶·€ÿÿ€“p·ÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€è€ÿÿ€–€èé€ÿÿ€–éê€ÿÿ€– êë€ÿÿ€–°ëì€ÿÿ€–Àìí€ÿÿ€–Ðíî€ÿÿ€–àîï€ÿÿ€–ðïÑðSPPCIAÌ `X¸ÈP°È!L‰Ô€ a €@€€ €€€€ð€ÿÿ€—ðñ€ÿÿ€—ñò€ÿÿ€— òó€ÿÿ€—0óô€ÿÿ€—@ôõ€ÿÿ€—Põö€ÿÿ€—`ö÷€ÿÿ€—p÷ÑðSPPCRD = `8˜ ¤¨‘9!ó?„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096951CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFxºU˜@;Ô+„(RTVHDRVD01PTVTOCÕ7tÕ¸]PFx„pRTVTOCPTüVINIÕ«±¸$CP00ÿ;@aa PVRTNÿ{c EVSRCÿdxþŸVRMLÿe4ñŸ VWMLÿ;e ‰ŸhCRP0ÿÛf°]Ÿ,LRP4ÿ‹gLÊ“LRP5ÿ×mL7œ“LRP6ÿ#tL¤š“LRPCÿozL™“LRPDÿ»€L~—“LRPEÿ‡Lë•“LRM0ÿS0ß• LRM1ÿƒ0Ó• LRI0ÿ³0Ç• LRI1ÿã0»• LWP4ÿ޼Œ•/PTbLWP5ÿÏŽ¼]•/LWP6ÿ‹¼.•/LWPCÿG¼ÿ”/LWPDÿ‘¼Ð”/LWPEÿ¿‘¼¡”/VER0ÿ{’Üj”7MER0ÿW“Ü3”7PFx„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096951CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFx„À1ð RS4 •¾DÐRS4 ²ÁÀ RS4 W¿BÐ0RS4 †Â RS4 p ÃSÀRS4 —Ç4Ð0RS4 _¡"ð0RS4 ¸ @ð0RS4 ¹ @ð0RS4 Å@°0RS4 «Ä% 0RS4 CÆR€0RS4 µ È%ÐRS4 ±º 5À#R/RS4   à€RS4HQF á²á+2T2$2ѲñÞ#·²3y1XY#óÒì²?/dz…Ó}RS4@GF áÃ$"ÚL[Z*:#Ú+¢åAÂ?-AXh1˜9/ÂoRS4@JF áRôyñ¢ñ*+ &†òfm#ô³N'äâüó81øâü  RS40'F á ã+Gz(ÁÒl[1ø"â*ORS4(F á5ÓB1ØFÁQ #r-RS40-F á’IC„iáÂM ã‚~#ôõRâ_RS4øµ9 à âkžû>ßýxóãɦšaѱ±‹±±‹±‹±‚±±‹ñ‹±ââá!‹¡±Á‹ñ‹ñ+±±‹±±‚ññ‹±‹ÁŠñ²á¢±K±kñk±+±A±+±+±Ë±ëñ±!Á*"*"*"*"*"*±+±+±k±o±a±+±+ñ+±o±+±‹±Ë±ë±b±+±/±+ñ+ñK±k±k±K±AñK±K±+±k+ñ+ñ+±/+±+±/±+ñañ!±/±"ñKñKñkñK±K±A±k±B±kñK±k±K±"±±‹±‹*±â¢¡!бÁñ‹±‹±ƒ±‹±‹.ÁŠò¡#Š#Š"Š#Š#ޱ²¢8¡+²8¡1Áñ±á+±A±k±K±Kñ+±!±!ñk±k±+±AñK±kñkñk±+±/±+±/±±±±;±;±ñ±±+±±±Á‹!‹!‡ñ±‹±‹±‹±ƒ±±±‹²¡â¢¢¡!“JвñŠ!Ž!Š!¢¢¢±Ž!Š!ŠÁŠ!Š!ññ‹±±±Á±K±K±O/ñB±ñ‹±Ïñ‡â±J!Ë¡²ñÊ!J!¢±J"в¢¢<¢$¡;ÁAñOñK;±Ë±A±Ë±K±ñÁ±Ï±K±Ë±O±K±KñK±K±Ï±ËÂ,¢X{RS4ÈY9 à âkŠB±ñ‹ññ‹±ññ‡ñ±±ñ‡ñ‹±±ñ"±+±"â*¢2¢¢¢"¡#ê"K¢<¢±/±K±ëñáñk±A±+±K±+ñK±+±ë±O±!ñ+±+±! "ê#ê#Ê!Ê#‹¢>±ª!*#*#âñk±k±o±a¢$¢D¢:¢D¢,¡+'ê&j"j"*Á+±+ñk±O±Añ+±+ñ+±+±!ñk±k±K±k±+±/ñK±+±k±K±ë±‹±«±a±±ƒ¡¢¢¢¢(¢8â(¢(±±‹±ñ‹ñ‹±ñ‹¢¸¡+²X¢øâø¡K*вxÒ2¢(¢¢,¢>¢¢>±J#Ê"ÁñK±+±K±k±K±añ+±/±k±K±k±K±k±+±o±k±bñ?±+±±ñ+±+±;±±1±K±K±[±{±[±Ai±±±Ò¡²±‹¡¢±Š!±‹±±‹±‚ñ±±‹!ϱÊÁJ!β±A±A±O±K±Š#J!Š!J"Š#Ê#J"Š#K+ñ˱˱˱GñK±‹±K±Ë±Ë±Á¡<¢,¢(¢$±Ê#Ê#в,¢(ñÏñϱKñKñK/ñB¢<¢4¢4±‹¢¢<¢,¢<¢<¢(±‹±O±Ë±Ë±ÁñOñK±‹±Ë/±+±"k181ØXRS4àŒ9 à âkŸ~|ýùñçɦšaÑñ±;±;X±‹*!Žñ²±‹±Š!¢¢(¢¢¨¡¼¢¸¢(¢¨¡;#’£Š±Š!‹£Š«€€¢¡Á‹¢á‚±‹ñ‹K±+/+±!±!o±!±+±!±O±a±o±kñK±K±k;±+±/±+±+±J!в¢,¢:¢>¢"¢±K±K±K±Añ+±/±!ñoñO±+±#ñ+±+±/±¯±ë±+²H¢X±Š²¢x¡{'ŽÁŽ!Š!Ž!Š!Š!Š!Š!±±±ƒñ‹±±‹;±ñ±‚¡±Š!‹¡±‹ñ‹¡!ⱋ±/±!±!±/±/±/ñ!±k±«±+±ë±+±¡±+ñ+±+ñoñK±K±k±Oñ+±+!Ê'ê&Ê&.'Š'뢢x¢B±±{±k±+±ñ+±±;+±[ñk±±k+#в¡;#Š#‹±Š!±ñ‹±‹±‹±¢8¢¡+Áв8¡;²8¡!²¢!Ž!‹ñ‹¡!Šñ+²8¡<¢(¢(±OñK±O+ñË¡!Î!Š!‹¢¢ñË¢¢¢ñAk±AñK?±Ë±‹±Á±‹±K±K±‹±B±Ë±Ïñϱ˱±Ë±K±BñKñAñA±‹±‹±‹±Ë±±+ñK±k±K?#´Â/£ò&+,‡°RS4é9 à âk’ù|?ýùñãɦšaÑ+±+ñ;±±+,±±‹/±ñ‹± #¡²8á+#Š"ñ‹±‹ñ‹.Áвx¡[²xá{"Š# ±k±O±K±K¢¢¢¢¢¢±j!‹¢±K±Kñ!±k±k±K±k±Oñ+ñ+±//±/±!±/±+ñ+±/±/ñ"±K±+±+±o+ñ/¢¢¢¢±ê!ê!Ê!Ê!K±k±oñ! ñk±k±kñB±+±K±o+±+±o±á±kñ/±!ñ!ÁŠ!±Š!Š!Š!᱋ñ‹±± ²±¢8¡²±%ü/±‚ⱋ±¢±Š²¢²¢¢âñ‹¡ñK±+±k±ëñ‹±ë±+±ë±Á±A±*#k¢±ë¢>¢(¢*±J±/±o±kñ!±k±K±K±kñ+±+ñoñK¡±ê!Ê!ê!ê²¢±a±+ñ1±?±+²¢¢±Ž!Š!Š#Š"в¢(¡;#Š#ŠÁ‹¢¢â¢±Š!‹±Š!áÁв(¡+#Š!Š#б±±Š!в¢¡!Š!‹±‹±OñOñO±K±Ï±Ï±K±Ê!‹±‹¢±Î²ñO±K±K±A¢±ŠÁ‹¢¢¡ÁÊ!B±‹±O±ƒñK±KñO±A±OñA±Ê!Ë¡!Î!Ë±Š²/±K±C±O±O±OñA±K±K±+±kñ+³‚âz:11è{RS4@M9 à âk—z~¾ŸÅñçɦšb‘±ñ±±‹±±ñ‹±ñ± ñ‹±‹²(±Š±²8¡,±Š#±¡¢¢¢â¢Áб+!‹¢8±‹¡"±k±Kñkñk±!±‹±K±‹±‹±‹±+±+±Añ+ñ/±k±Ë±Ë±K±+±!ñK±K±añ+±+±/;±k¢¢¢¢â¢±Á±+±+±K±o±+±kñK±ë±+±!±Ï±«±Ë±¯±!±!±+ñ!ñ+ñK±+±AñO±k±K±kñ!±‹¡!J!‹¢±«¢¢(±‹±‹±±‹/ñ‹±‹±‹±+#б#в(¡#áŠ!‹¢¡!Ž!kñ‹±‹ñáë±/±+±ë±ë±Š!ª!j!«¢¢¢±Ê(±a±Kñk±!±+±+ñ++±+±+±+±k±+ñK±+ñA±/±;ñ;±±+±+±K±K±k±+±;±«±O±‘²(¢(¡Û±²ø¢È¢ø¡Ë$¢(¡±"Š#‹á+¢(á,¡;#Š"Š#‹±‡â±Š!б!Š!Š¢¡;&вh¢8¢x¡K²x¢8ah+£Š&Š”€€µº!ÁÁK±Ë±Ë±Ï±‹?±K±K±Ï±K±Ëñ˱K±‹±Ë±K±Â±KñOñO±Ï±K±Ï±‹!˱Ê!ʱ;#Ê"Î!Š(¢¢±Ê²¢±Ê!Š!¢â¡²¢¢¢¡ñK±O:!Š!‹¢ñÊ!ÊÁJ!J!J!k¢¢¡!ê!K¡!‘Ó?RS4h9 à âkžü}?ßýyóãɦšc‘±±;"Š"‹±Š"Š#в8±Š#‡ñŠ!‹â¡!‹¡¡²¢â!Š!Š!‹¢â¡!ŠÁ‡ño±/±kñK±+±k±k±Ë±k/ñ+¢±/¢¢¢¢¢±Âñ/±+ñ!ñ+ñ+±+±++±+±O±k±+±k±/±o±k±k±KñkñAñk±K±+±k±/±/±K±Kñ+ñ+±K±K±k±K±k±k±o±a±+±Ë±K±K±+²¡±;²8¡;#в(Áá!Ž!б²HáK'ŠÁŠ'Š$бk$±±±±ñ±‹±‹ñ‚±±±‹±‹±¡k²H¢X¡[#Š'Š'в¡1ñ!±+ñ/±++±+±+±+±/Aœñ!ñ+±+±!ñ«±k±K±K±‹±O±+±k±o¡±Ê!+¢¢±K±AñK±+ñk±+±! ±±ññ8¢²¢á£ˆŠ²(¯€8€0€(‰R±±±±!Š!ŠÁŠ"Š#в8¢±±‹ñ‹±‹±±¢¢¡!â±û+б»(Š/в¡+$âH¢X¢(¡&Š'вX¡±O±‹ñ˱Á¡{²H¢¡\¢ü¢¢¼¢ø¡Aò,¢8¡!ʲ<¢¢$¢(±K±K±K±Á±Ë±Ë±‹!ʲ4¡;²¢<¢(¢<¢$±‹ñ‹±Ëñ±±‹±â8¢4¢<ñÊ!Ê#˱J!J#A±‹±Ë±‹±Á¢$¢x¢l¢4¢¢H¢|¢x¡L¢d!N!J!Ê!Ê!J!ÊÁÊÁÁ±K¢±‹â±Ë¢±ÁK Ò~jRS4  S ã,RS4 ~@ äCð RS4 Ü åoRS4  æ PÀRS4 1 çqÀRS4 u èÐRS4 ‰ é RS4 9 ê1à#V ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$SB1Àõ‘¿&­6»²#Iez¼°¾Xf&õ‘¿&­6»²#Iez¼°¾Xf&PBPFx„RTVRTNSOINþPFx„tRTVSRCINdPFx„0RTVRMLVD01PN00KV627SN YA1932096951TV0004PFx„œRTVWMLVD01OCFOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ITTIN1936350000242014PFx„¬RTCRP0VD01ED!0Ð.«b‘H0ê ŠY€TE 0203QFQASFDD0202STDNI%IQ PFx„HRTLRP4VD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCöóö÷#M½ 9@£Ã£8УÕÃ&—&¿S& ï&hGªVª*ת1rª:ܪCHIQ PFx„HRTLRP5VD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCÉì#M¾ /@£¼£3Ï£ÎÃ& —&¿S&–ï&^HªLª4ת1rª5ܪCHIQ PFx„HRTLRP6VD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCñøóï#M¾ 4@£Ä£0УÐÃ& —&¼S&˜ï&^GªLª×ª'rª-ܪÿCHIQ PFx„HRTLRPCVD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCùøôô#M½ 9@¢Ã¢?ТØÃ%—%ÆR%¢ï%mG©X©-Ö©=r©AÛ© CHIQ PFx„HRTLRPDVD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCÉìòõò î#M¼ :@¢Á¢?ТÕÃ%—%ËS%§î%mG©S©-Ö©;r©AÛ© CHIQ PFx„HRTLRPEVD01#V aÂ:Ö$í¦¨¾kÌjà(#PTC÷òñ÷#M½ 1@¢½¢5Ï¢ÓÃ%—%ÁR%˜î%`G©L© Ö©1r©:Û©CHIQ PFx„,RTLRM0VD02TCMCINPFx„,RTLRM1VD02TCMCINPFx„,RTLRI0VD02TCINPFx„,RTLRI1VD02TCINPFx„¸RTLWP4VD01#2D#3DINPFx„¸RTLWP5VD01#2D#3DINPFx„¸RTLWP6VD01#2D#3DINPFx„¸RTLWPCVD01#2D#3DINPFx„¸RTLWPDVD01#2D#3DINPFx„¸RTLWPEVD01#2D#3DINPFx„ØRTVER0VD01#IÄ0PFx„ØRTMER0VD01#IÄ0PFxÊG¶óa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ—ȉÇfèa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ— ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæÏ ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æÏ ÕQŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjöÍ RçîmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*öí Vç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæï Rç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æïiYEÒ “38]iYEÐ “38]WyÝEzÓYcyÝExÓYc]N9.4klh Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¶ÛÛn«!»"t22ñú îÉ` ,C.݈*U »poÿžþûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨n=.4{lh Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¼þóÞÎ/é›ʺpÚäª'†ÛÚM˜Ö²«@&"¡ªk"òr.·¾‹rgÿžþûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨N!,4+lh Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¾ÛÛn¹¡ó!¤p‹”¥¬,»Ë/™0ô²‹F7ˆÑ€gõ"Äbm ³ž«bgÿžþûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ Ûù¨oµ&0{ì`Ž Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼þÛÞn­(S.Ÿ ûM‹š[l‹ˆèqËO;…À L2Ü¢´š½ë'¾¾Úûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢Ûù O¹&0kì`Ž Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¾ö“ÿî™õàB;:œ³­Ê¾Õ©nŽÐ[èº4õ˜p¼þ1G!»žÛúo¾žúûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ o½&0{ì`Ž Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¶ÛÛn­ã›m6À£Öl 3šh†"ÀáÛé—<žDÜcío°»zo¾¾úûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ 5º;ÅûPÛásx{VµÕàÿÿÿÿÿÿÿÿÿöw맬`}}‚PúçúYêŒ}Uª+Õèz•Hª¿ïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿU¾§_慎n8Y´îº74²B2g•nЈ§$¸ˆ¢ðÏï™›û7wþîÿýßÿ»¿ÿg~ÚŽ]X¶"aO%Sr°K½ïlÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ¹?üÌÞnìãhØ(…!(´vèrVy]´— âµ€„ÂdB{1ê–~Yç2Ÿ* ±TPâÓaµ«PÀ¤ Ò;3ÉdçJWáteúIvíC·"@¸Uײkp”20sN ÷U9Pë7 ’||Ö¬ß4÷.g 53 SÓ‰PåUmD:+QbqDÅ6EG4Z@ 5¦%¹(sŠC¡D@Ó}±'gBOhÒpF$x$ÕOJâñuµ–ažfa€ÒHa¿bÅîsóBæÀE6Hjr5&Ë6öQjT:&VôÇ(tÐc°?'EpÎm@q’{Qh4ð°9$ð'= 3Ød0’Zó 6?JSË\OsQ@wojáçr:p»dñdF?Aêsp/%Ip…Îm@q’zQh2ð×)vÓ{“¸& j4«uK6‡aW,”U0E@÷¾p´@Ã~!ÄÝ d˜b$7m”ÊX§‘Á Ì,@Q’ZUh2ð×)vÐc°?'Uz$,uÄÿatbåãÓ}%Ebøb¡¶‹h‡N(a c¢¦Nq~0v$6ƒ^÷gt"Czrl2pÇ)vÐc°?'EpÎm@q‚q@.0ä»aá R±‚Lpã2%F>iPœbДwy2Ì`®:±”IÁ_i”;#¯$æ(uØ`0O%E0Îm@q’zQh2ðT9F\#¹cx>1]`Å ö>c‘BR\ [ƒßE”8´Œ@R¦¤ÇûY*wO!Ip…Îm@q’zQh2ð×)vÐk±¿'võ-uhisZ7Æ4F€MÅB;õ»P…>¶ði°Þb·t+L÷–‡Ù3&$±F‡ù ³'r‡UAfp‚èDª3tš¼h7¡$3‘…a25çl0Hi¶b÷ÚDe¥xÓÓ-, &RQ˜<”Üõ Žg/Ncû1_RðŒWp³ Qô kÅ+eüGrY,Rñ™4\D¨$·´ $é&4¤ÜU“ê1¤KùH§"5åšs7Ê$²`ÖÁL‡Iñ]"ü ”gQÇþ A–e*…ãPÁV0EJ$0ðLÁjcXÖ]•iQ²šÛ@¹ gÐAE!åå*AWÑpòËtó^÷ÿ=÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷þwÏ~×zmc·Ecu,f…OtCîcó/Ñ*ã”`ƒ*g¡ð8á}(°3R§¿&ZQUGˆsª 4Á%PÁ*UOcþ<àú5gñq3þS¤¥Ty7ýig}rC°ˆb Y rôf¦"®9FWHC‡òÂgnvFáñdBh{0]`³@Ÿaò ÅsùÍR¥ûb¡tqÈÂÃK»'_tw~Emz€Ê• cg±pb€¢1èS<=ósyâ^ÄÇäD#îpFä2HH¦×I²+0t{s&‰)Þ¶Žö.¿4tH âÏF¥<Ãö1Ń[¨v¸,¢Ãc1³@e råL*)àEA2÷Ó$“!€‰!`6]Idð:4ò1æ2ãeÔ!àwsŠpd¬HEHYç 0˜ ˜yäÑDG1—2ô¢d±ZQ¤F«`Ãpl–ÍCð6¶NøWñAQžB÷Ï][!³ôP|~ËïqÇÕ1—áPh8Ò$›JЈÂ&!ÈY"Ò`‚Çáp“w$B+:U\Cl¥[0áT#—deTôÁX°vT!V2ô\Áûs´)#Û(í-0±Y–š“giG˜Qƒ#NUZY÷ôY^cZ{y°à:3¥4>~ØaÁ€‡¥ÚFÅHWþj€òD§vÁO8³ƒz B5”S×:Yh`‡74 4Yb¡tpr SÕBQ¶¾Tf‹0FO&Nï8°KCÖÒd,´šI#x¦e"jn3¢xá"çôG}(±=40F !Frâë€2…Žb€tÓä)âÏSã"2L ›tÖqE|×:Ô%ñð+7Fp2T5PT†³æÅ0еBQ’z` q&§Ul·à2wîñÐÙZ'Za£\N ”Gi78'u`qA!¢fpãòPØ"Ç*)…7hÁ\ , %L`Ð|Et Àm‚é¦{Qu4Ëhb&c§W4hlÑÜ9 ÊQÅÑV›0> $`wŠ5`îQ׸*ä~Q“R%šmpj@µÝöõÎ0©RXF¥¦âfJñòfë1h!õ©#€V5¦ øMÕ4æZ&hjr²%‘ösÔr@¤Lgï$:¤31»>³AðîZF@6ÀíYò "2eð¤#–nXäx=Rë{ n4væ…\å`R¡èp©!ñiÔ¦a v'¯9¤>x1~$ZtÅJ´_zXr‰,qŽUtxx60I›Óbžf:FÃÏ!CÏk¦­7i±‰@Q1h@T0Æñ òý:óÿwÿ^óÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿs÷üWÏrÇ{m0é{3ßwï^÷ûq7ús›Azpöõ1`0‘N0Q3Daq A×Z@I3VZÓ•$ØuHE³ Aì¹BPó÷xàÄbTRÓ‘Ç@¥žBòdz1Déf3&€ 2Å1>}!Ѝ‹è©©½ÑÄè®H\ îEg@ ìdÉûƈS£!5;ìz¡Ž¤­êS‡k[ÿCù2À õ â&QÐ/êè— àU1†¨‚ }n¦f‰À ဎÕ_ŽŠÑðSPPCRD = `8˜ ¤¨‘9!ó?„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096951CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFxºU˜@;Ô+„(RTVHDRVD01PTVTOCÕ7tÕ¸]PFx„pRTVTOCPTüVINIÕ«±¸$CP00ÿ;@aa PVRTNÿ{c EVSRCÿdxþŸVRMLÿe4ñŸ VWMLÿ;e ‰ŸhCRP0ÿÛf°]Ÿ,LRP4ÿ‹gLÊ“LRP5ÿ×mL7œ“LRP6ÿ#tL¤š“LRPCÿozL™“LRPDÿ»€L~—“LRPEÿ‡Lë•“LRM0ÿS0ß• LRM1ÿƒ0Ó• LRI0ÿ³0Ç• LRI1ÿã0»• LWP4ÿ޼Œ•/PTbLWP5ÿÏŽ¼]•/LWP6ÿ‹¼.•/LWPCÿG¼ÿ”/LWPDÿ‘¼Ð”/LWPEÿ¿‘¼¡”/VER0ÿ{’Üj”7MER0ÿW“Ü3”7PFx„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096951CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFx„Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁ‚dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|¤ðA ¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁ‚dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|¤ðA ¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁ‚dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|¤ðA ¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁ‚dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|¤ðA ¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁ‚dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|¤ðA ¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁ‚dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4  q²<RS4 5ǯeiRS4 q°%<RS4 4±qÐRS4 ”@®&MRS4 Ç@´V0RS4 í6€0RS4  ë ³zRS4 ÕµmRS4 » Ð0RS4 A@¶ (RS4 0 · 1ÀRS4 ¶¼ Ð RS4  c ½ 8RS4 >À1ð RS4 •¾DÐRS4 ²ÁÀ RS4 W¿BÐ0RS4 †Â RS4 p ÃSÀRS4 —Ç4Ð0RS4 _¡"ð0RS4 ¸ @ð0RS4 ¹ @ð0RS4 Å@°0RS4 «Ä% 0RS4 CÆR€0RS4 µ È%ÐRS4 ±º 5À#R/RS4   à€RS4@AF áQã6z#ñòü¢?O1Hj"ç¢?(!³ò?.&<RS4@GF á’Q*%C~Xô ¢ü¢ü:1ø:1øO?ÿZ&a‚ü_"qlRS48:F á£.CZ,Ò9%åÒü;& "Ñ£‡’.1ø° RS40'F áw¢?[1è1‚"â'ƒ‚ÒükRS400F á|1F'á‘Â1è^(Õh 'äð RS48<F áT‘?1ø1¸âü%aX#ñn(F’ü!‚ü  RS4ÀL9 à âkŠD¡ñHñ‹ñ‹/¡ÁŠ!‹¢8¡±+!‚ñ±‹/ñ‹±‹±‹±‹±ñ ±+±+±/ñ"±«ñ¯±ë±+±¡±/±/±+±+±+±+±k±!±k±K±!±+±+±/±+±!±#±/±‹±«±k±+ñ!ñ+&±+ñ+ñ+!Š¡¢ñŠÁ¢¨¡Ë#Š!Š&Š$Š/Š(Š+Š"Š$¡/²8¢¡;#‹¡#‚±‹¢â8¢8¡+²8¡1Á‹ñ‹ñ‹±‹±±±‹±‹±„±‹ñ‹±‚á!±+±+±/±+±+±+±!±Ë±O±ë±ë±Ë±+±+ñ+±/ñ+ñ+$ú#ê$ú%z'Z$*'ú':#Û¢±±K±{±K±;ñ+±±?ñ1±#ŠÁŠ!Š!Š#Ž"Š"‹/±±!ⱋ¢¢¢â±‹±±ƒ±‹ñ±‹±‹±¡+±+²(â8¡,¢8¡Á±±‹±Ë±±ËñËñϱ‹ñK±‹±‹±‹ñK±Á±‹±Kñ‹±Ë±‹¢(±Š"ŠÁŠ#Ê#Ê!Ê!J!Ë¢±Ê!в¡¢¢±±Ëâ¢â±A±Ë±Kñ‹ñË¢ñN!Ê!ϱÊñ‹±‹±Ë±Ë±A¡›/J²ü¢Ä¢˜¢ü±‹¢<¢Ì5‘2ƒ…Ã…ã…€RS4ð¡9 à âkœû}ÿ?ýøòãɦšaÑ»±+¢¢¢¢¢±K²¢¡!Š!‹¢¡¡!Š!Š!Š!вñ±Š%вh¡;²x¢x¢h¢H¢X¢¡ò¡Š!±‹¢¡!Š!Š!Š¢$вx±Š!Š$Š'вx¢8¢±KñK±oña±Ë±K±«±ë±K±¡¢~¢æ¢Ø¢Ü¢v¢Š¢þ¢¢ô¢^¡ÁÁ±k±ë±+±+±+±"ñk±+±+±k±k±A±k±ë±K±¡ñJ!«¢¢¢¢¢¢¢¢#*#j"J!Š!ª!ª#ê²8¢&±!±+±Oñk±K±!±K±K±/ñ/±k±Ë±ë±/±añ!±/±+ñ!ñ/ñkñ#±+±/±+ñ! ñK±K±k±!ꀀÁ‹HÚ¡øëd€±¢±Š²¡¡!¢H±Š²¡K%Š'бkÁ¢8¢8â(â(¢8á<¡1Á‹±‹ñ‹1ˆ¡û$Šø€€€€‚«€€±±±‹±ƒ¡/±+±+±+±!±K±±+±Ë±ä±+±+±+ñ+ñ++±!±/±!±1ñ±;±+±ñ;ñ;±;±;±!±›±K±[±û±ë±AÁ‹±ñ‹±‹*"Ž"в(¢8¢(¡¢ÁŠ&Š%б{'Š'в¢hÓA‹â±Š!Ž!Š!бÁ‹ñ‹±‹²8¢¢h¢¡K'бK$Š!+^€¢±Ë¦€²Ø¡Š1X±‹±‹±ñÊñâ<¢8¢,¡;(ñK±‹±Kñ±Š!K¢â¢±Š!K±¢t¡{$Ê#J$J#Ê'ÊÁŠ'J&J"Ê&Š'ʱ'Ê&Ê'J(¢¢±Ê!Ž!Ë¢±A⢱K±Ê!Š!Ë¢±Ê!ñK±‹±K±Ë±Ë±Ë±KñËñK±‹±K±Ë±K±Áµ@Ê-J*‹¡Ú1üâÜ¡»f€J.ÊŠ@À1ü¢d¢´¦´´²á!Ê"Ê#Ê#K¢¢(!J!K±.!Ê!ê!Ê!Ê!Ê!†ÕTÂ_RS4x´9 à âk”øÿßýøñãɦšaÑ{±K±{±{±K±B‚±ñ±‹±ññ±Š"б<¡#ŠÁŠÁ¡+²(¡;!Š#Š#в(¡+"‚ñ±±‹+±*²¢ñŠ!Š!Š!б±+Oñ+ñ+±!±K±+±K±a±k±+±//ñ+±+±"±«±«±+±ë±ï/±k±kñañ/ñ/±+?ñ+±'ñ+ñK±K±o±a¡!K±o¢±k±ê!¡ñ+±+±Kñ/¢¡!Ë¢¢±ª!+±/ñ!±+±+±!±«±k±‹±ë±K Á‹±‹ñ‹ñ±‹±±‚¢¢¡¡!Š!Š!Š!á+"‹¡¡#ŠÁб!ñK¢±‹¢¢¡!+±A¢4¢¢¡+"Ê!Ê'ê#J'ʲx¢±ê#Š!J"Ê#뢱Š!+¢±Ê!«¢±Š!j!J!Í1ޱ+±k±k±ë±‹±a±Ë±ë±K±ï±K±ñ±±±;±±;ñ;±!±k±{±±{±â\Á±±¢á!‹¡!‹¢±Š!¢8á,¢8¡+#в8±Š#⢱‹±Š!Š²â±‹±‹;±±‹±*¢¢â¢²X¢¢H¢H¢x¢x¡±L±Ëñ˱˱‹!‹¡!¢±Ï±ÇâÔ¢”¢¢¸¢Ü¡›/Ê)ʲl¡ÑÁ˱K±‹±Ë±O!K±ŠÁÎ!˱Š!Aá#ʲ$¢4â<¡±+#Á±Ë±‹±K±Ë±±KñK±K±K!Êò¢â¢±J²±KñK±A±Ëñ˱‹±±A⢱Š!K¢±Š!Ë¢±/±+±!±++#öâüx1ø(1ø9,†°RS4ç9 à âkœøý¿?ýøñãɦšaÑ{±;±[±{±±l±‹±‹ñ±‹òh±Š'Š$Š'бKÁ±‹±±‹±¡;!‹¢±Š!Š#Š"Š!Š#б‹±‹ññ+±+±!±+±+±+±+±#±+±/±"ñk±ï±K±+±+!ª!Ë¢±K¢¢¢±Ë±+±+ñ!ñ!±/±+±+±!±/±+ñ+±"±ª!J!«±Ë¢¢±ê!+±+±k±Kñ+±!±ëñk±Ë±‹±k¢±K¢¢¢â¢±A±+±k±+±kñ+±o±+±++±+±«±ë±£‹ññ ±‹±ñ‹±á#Š!б#Š#в8¢(±!Ž!Ž!‹¡!Š‹¡¢¢¢¢¡o/±"ñO±k±K±+±K±K±k±añA±k±K±/±k±k±_±[±û±k±bO±ñ¢â±Š±‹±±±â(¢(¢¢8¢8±Š#‹¡;"Š!Š!в¡²¢!¡!Š!±Š!Šò¢ñ±‹±‹ÁŠ!‹¢(¡#Š#‹¢(¢8+±O±AñG#æ\ f0ú/ñ”?ÿÁ?CÿòO±K±Ë±K±„±O2´â<¢$¡/"в<¢<¡;²+ñ‹±Ë±K±¢±Ë¢¢¡!˱K±Á±Ï±±Á±K²d¢\±J$K¢|±Ž#Š'ñϱO±±K±O±K±K/±/±+,â/Ï#ñ‚üL'æâ:â2}RS4Ø~9 à âk‘þ~¿ýyñãɦšaÑ;±±;±ñA±ŠÁ‹¢ñŠ!Š!‡á!бŠ!‹±‚±‹±‹±‹ñ¢ø¡û²x¢(¢¸¢ø¢H¢8á!²â8¢¡;²8±ŠÁ‹~!˱Ê!Ê!ê!Ê!ê!ê!Ê!j!±Š!ê!Š!ê!K¢±‹±*!A±+±Oñk±k±A±+ñ!±+±/±+±+ño±+±J#ê"*#в>¢2¢<±K±+±+±k±O!Ê$ª&Ê#j%J%ª'ê#j%ê'Ê'!±Ë±+±ë±ë±o±K±«±«±«±+;ñ+±Ë±±k±‹±k±+±K±K±/±gñ+±+ñ+±'ꀀ²¢(¦8ø¡j!ìH¡²H¡{Áв8¢x¡&Š'Š$в(¢h¢H¡K'Š$в¢XÁŠ!Š!‹¡!Š!ŠÁ‹±"б²8¡;²(28¥˜Kj€%Ц?€«€0€%¡û+Š(‹±Š/Š/Š(Š'в¸*±‹±«±ë±Ë±±Ë±k±K±ë±ë±a±*"ª#*#ê"‹¢>±ê"K±A±+±+ñ+±!!«±j!«¢¢¢±Ë¢±Kñk±o±kñ±+±;ñ+±k±[±Ë±Û±ÑñK±±K±{±{±AÁ‹ñ±±á[$б{'Ž'ŠÁв8!Ž!Š!ŠÁŠ!‹¢â±‹±‹±‹²á;"Š"Š#‹á+âS±ÁŠ!¢±‹±á+#б±#Šñ!ò¡¢±Ž!‹±‹¡²t᫲D¢´¢ü¢\¢ü¢¤¢Ø"Ê$‹¢¡;%Ê'Ê#Š&Ë¢,"Ê!Ê#Ê"ʲ(¢<¢,±J#J±B±KÒ%±k²ô¢¸¢¢$¢ü¢è¢¢d¡Ñ²h©\\kõ@@À@†À£A±Ëñ‹¢¢¢±Ë¢±Ë±Ë±Ï+±Ï±O±‹±Ë±O±A±KñK±Ë±‹*#Š!Š#Ë¢¡;#Ë¢<¡#A±K+±Añ+±+±! %àRS4 9 à âk”ÿ|~ÿýøñåɦša ²¢â¢¢¡!‹¢¡²(¡#‹¢8¡;(+±‚áñŠÁŠ!б!ñŠ'Š$вh¢x¢x¡K²Xñ!Š!в¡!‹¡Á‹ñ‚±O±+±+±o1±N!*!j!Ê!Š!ê!Š!Ê!Š!A±+±Ë±Ë±ï±«¢¢±+¢¢¢±ë±K±k±k±K±a±Ï±K±Ë±‹±K+±+ñ#±o±k±+±a±a±k±k±+±k±K±a¢X¢N¢r¢F¢~¢|¢~¢R¢*¢¢R±ë±±ïñᓊê`ÀÀÊû`à@@‡€Áñk±K±kñOñ/±+±+±"±/ñ+±+±!±a±O±k±+±a±O±k±k±o±A±!бŠ!Š!Š!‹¢ññ‹ñá!¡¢¡Áⱊ²8¢¢8¡+!ŠÁŠ"‹¢(¢8¡#‹¢8¢¢(±k$б;²xá{#ƒO±oñK±k;ñ+±+ñ+!‹¡!Ê!Š!Š!ê²±Kñ/â"¢6¢.â&¢¢>¡;#î!Á¢±Š!ϱª!ê!ª!K±áñ+±[±K±K±k!›¢±k±º!ú!J!Þ!a±Û±K±Ë±û±û,±‹±‹±*#‹â(¢¡;#‹¡2±Û,Š-Š$вH¢ø¡»±›.‡ñŠÁŠ!¢±¡;%ŠÁŠ&Š"Š'бo'¢8¡!б!Š#Š#Š!ŽÁϱ˱˱Ë#K¢8¢4¢<±J#Ê"ʲ<¡3${ñOñA±AâX¡|¡+ò|¢¡+'Ê!¢±Š!J²¢¢¢±A±AñK±Ï±K*J!Ê(J.J-Ë¢ü¡›²„¡¡Á‹±K±Ë±Áõ€ ϱϱ˱ϱñK±O*!Ë¢±Ž!Š!˱J!˱B±K±Bñ+±/±+±/M$ÄÂüÒ~}RS4  S ã,RS4 ~@ äCð RS4 Ü åoRS4  æ PÀRS4 1 çqÀRS4 u èÐRS4 ‰ é RS4 9 ê1à#V ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$SB1½Õ¤l¿&­6»²#Iez¼°¾Xf&½Õ¤l¿&­6»²#Iez¼°¾Xf&PBPFx„RTVRTNSOINþPFx„tRTVSRCINdPFx„0RTVRMLVD01PN00KV627SN YA1932096951TV0004PFx„œRTVWMLVD01OCFOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ITTIN1936350000242014PFx„¬RTCRP0VD01ED!0Ð.«b—ð`ႊY€TE 0203QFQASFDD0202STDNI$IQ PFx„HRTLRP4VD01#V aÂ:Ö$í¦¨¾kÌjà(#PTC#M½ y@¢ì¢§Î¢çÃ&Q—&ñS&Èî&ŸGªjªfת r©7Ü©¢CHIQ PFx„HRTLRP5VD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCÉìùþöó#M¼/@¢Þ¢gТ>Ã&—&_R&Íï&’Gªq©kÖ©‹r©iÛ© CHIQ PFx„HRTLRP6VD01#V aÂ:Ö$í¦¨¾kÌjà(#PTC÷ûøò#M½ V@¢˜¢lÏ¢%Ã&…—&äQ&î%ÌFªŠªHתcr©nÜ©HCHIQ PFx„HRTLRPCVD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCùöøó#M»Q>¡Ö¡NΡÂ$:–$öQ$Ïí$ŸF¨ƒ¨PÖ¨|r¨€Û¨9CHIQ PFx„HRTLRPDVD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCÉìûùóø#M»y?¡Ñ¡LΡýÂ$5–$%P$¬í$½E¨™¨ÎÕ¨Ÿq¨0Ú¨CHIQ PFx„HRTLRPEVD01#V aÂ:Ö$í¦¨¾kÌjà(#PTCþø÷ò#M»Z>¡Ê¡bÍ¡ Á$&–$äR$íí$!E¨q¨%Õ¨[q¨]Û¨;CHIQ PFx„,RTLRM0VD02TCMCINPFx„,RTLRM1VD02TCMCINPFx„,RTLRI0VD02TCINPFx„,RTLRI1VD02TCINPFx„¸RTLWP4VD01#2D#3DINPFx„¸RTLWP5VD01#2D#3DINPFx„¸RTLWP6VD01#2D#3DINPFx„¸RTLWPCVD01#2D#3DINPFx„¸RTLWPDVD01#2D#3DINPFx„¸RTLWPEVD01#2D#3DINPFx„ØRTVER0VD01#IÄ0PFx„ØRTMER0VD01#IÄ0PFxÊG¶óa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ—ȉÇfèa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ— ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæÏ ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æÏ ÕQŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjöÍ RçîmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*öí Vç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæï Rç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æïiYEÒ “38]iYEÐ “38]WyÝEzÓYcyÝExÓYc]N9.4klh Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼öÛÚN¯+[Aš0Ž«™ˆ“¢ â%Ô(î[°-ò<¨`éö¤°žKboÿ¾Þûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨n=.4{lh Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¼¾óÛÎ;€xZ#CÜ‘ýâ%–˜([øÁ92€8/½>œ²MΧ˜‹pg¿žÞûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨N!,4+lh Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¾ÛÛN­ª²Fž„º‹}¦‡¾ Ü"~sHª!ûCÓ(«æí¯œ rgÿžÞûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ Ûù¨oµ&0{ì`Ž Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¶ÛÛn­ãÚh‰‚°o-0pƒ€$²' ®&"Ñ& jøïDš:[úg¿žúûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢Ûù O¹&0kì`Ž Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¼¾óÛî+’±S Ể˜L—šÔJ€¾VÙ»Ž¼Ÿ²&ˆ’ä`ïL²¾«bgÿžþûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ o½&0{ì`Ž Y{'µ¾æðòK”b™øò<îšÛéi¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¾ö»ÿN’ìankÙÈ 0ôØ/Ç”^y@°­ÈAD(Ž d@¤8‰h/¿¾úûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ È›ÜúW“Ýó|:Þ·¹ÿÿÿÿÿÿÿÿÿöw맬`}}‚PúçúYêŒ}Uª+Õèz•Hª¿ïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿU¾§_慎n8Y´îº74²B2g•nЈ§$¸ˆ¢ðÏï™›û7wþîÿýßÿ»¿ÿg~ÚŽ]X¶"aO%Sr°K½ïlÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ¹?üÌÞnìã(Ø(…!(´vØrFx]—Ÿ+b•€„ÂdB{1ê–~Yç2Ÿ* ±TPâÓaµ«PÀ¤ Ò;3ÉdçJWáteúIvíC·"@¸Uײkp”20sN ÷U9Pë7 ’||Ö¬ß4÷.g 53 SÓ‰PåUmD:+QbqDÅ6EG4Z@ 5¦%¹(sŠC¡D@Ó}±'gBOhÒpF$x$ÕOJâñuµ–ažfa€ÒHa¿bÅîsòBæÄE6DkRA&K6öQjT:&VôÇ(tÐc°?'EpÎm@q’zQh8Ñ35£È°³5ÐKM‹„>c¡dµ6(DÜjQ{u- ®vª$6ÇûY*wO!Ip…Îm@q’zQh2ð×)vÐc¹'õn&uqxãì&äæò8sA5pXS$—ùC$ç¦7²@Zrtw…sNx`ïRVh2pÇ)vÐc°?'EpÎi@ó va683Û|õè0y`ðJHÂà Óñg>€çeK0ô¬5”IÁ_i”;#¯$æ(uØ`0O%E0Îm@q’zQh0àõ%VcG9R|ryQ4(Ö6D9$w¿P+õz&88G–ípTä'6 D»iIsÉIXÎ @Q’zQh2ð×)vÐc >7·bˆ)`9j¡ÿè<夰ÒQþ`1'lfù"gœâvãÖ!q^pV$v N÷gt"Czrl2pÇ)vÐc°?'EpÎm@ñ°vp0:ô×1ǦQ¿!LÓ„iGRŽ-òî[ti­ 1¤1"¾ö8¤Ÿ Á Ç^Òì,ƒ*Õu¯~À„a)ña"Æ$EäxA"dCk4rWÕÏ \Pôr³“T$Ä!sSV4±Âõ‹@A§&!Æ^²94ßyÁˆ!&„vµPa„zÔ&K@3Ðu4vy…X@jTo8W“3ÔÕ1Y@ò±5P qdiV$Z’Ÿ 3JX÷.„x4"MT’3ÆTR– æ‡X·PavéE„LóÙ1e{äh1‚Pù5“ðYÕDUjäaÇ ×Kf'—%Qµ3¤CÙx0Ä<$½Y‘¥CZ&WÔI¡bH"÷bŠ0íHT*Ip[Q™ ·ï|PQßURZ":A‹@AR2ä–DFfyõæ0t¼`cÚrVù!KFdË2Œr!,5}z×ø5Fk°½`÷:¶A¤qZ"M5#2ðÆ0“_K›WL|á6lGM7n6“NŽ,£ï*Æx0 VbyW/K–b'‰>Å¡hô°KuI%•‘Er`Auë”P‹ Äð:¦_¡ ý"çq…»fê÷Ä¥ºdà—zñ;,¤)vH^w‚<4Ä•x4¢¦… $Û ²uGá8¤guaLÂ}b#&æå(UÉ•b H£üDàÃHp¸Dt‘ÒµRDr´BfGmÀ=H7`ƒ W,hâ‚ÀÕ:\v„ üfÿJhh‘¡A åBÆ~gçPòF)¡‡FD ‘¬,–¶#„$QõÂLwÛãX&_~×`¡³sGgSÅd²‘Pd„F…”õPdy¤™"%*ÒuB§*'°`—:,Ó¦ 0$ÞGÎ$V»iµEQº´‡†mYñ5s|8ƒAóŒQ""¡LÓ¿÷•&HPcÖEv"ò}†RågÄP5 :ƒˆ!G¿3TÑÀFS@Ôo)‘ˆwÒ<yQ3 P£šP &“(5”Z÷}'\³x…´bw6 &ãg<¡RÕC7±& wY nE¬¡>Ãól°’¾us\U& DXgP¨NìYµBhP70éS?9²ÊUó^¤ðRMSµ9Ùáñ”sBv.VÍhTÞ …ÞÐ@&¶ç{ôU¾Nrû}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿs÷üWÏrÇ{m0é{3ßwï^÷ûq7ús›Azpöõ1`0‘N0Q3Daq A×Z@I3VZÓ•$ØuHE³ Qm{XPSiö @ ÜZ²(ÃA¥¾Bòdz1Déf3&€ 2Å1>}!Ѝ‹è©©½ÑÄè®H\ îEg@ ìdÉûƈS£!5;ìz¡Ž¤­êS‡k[ÿCù2À õ â&QÐ/êè— àU1†¨‚ }n¦f‰À ဎÕ_ŽŠÑðSPPCRD = `8˜ ¤¨‘9!ó?„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096950CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFxºU˜@;Ô+„(RTVHDRVD01PTVTOCÕ7tÕ¸]PFx„pRTVTOCPTüVINIÕ«±¸$CP00ÿ;@aa PVRTNÿ{c EVSRCÿdxþŸVRMLÿe4ñŸ VWMLÿ;e ‰ŸhCRP0ÿÛf°]Ÿ,LRP4ÿ‹gLÊ“LRP5ÿ×mL7œ“LRP6ÿ#tL¤š“LRPCÿozL™“LRPDÿ»€L~—“LRPEÿ‡Lë•“LRM0ÿS0ß• LRM1ÿƒ0Ó• LRI0ÿ³0Ç• LRI1ÿã0»• LWP4ÿ޼Œ•/PTbLWP5ÿÏŽ¼]•/LWP6ÿ‹¼.•/LWPCÿG¼ÿ”/LWPDÿ‘¼Ð”/LWPEÿ¿‘¼¡”/VER0ÿ{’Üj”7MER0ÿW“Ü3”7PFx„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096950CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFx„Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁgÁÆ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@0Ð0RS4  à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|„ðA!Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁgÁÆ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@0Ð0RS4  à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|„ðA!Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁgÁÆ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@0Ð0RS4  à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|„ðA!Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁgÁÆ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@0Ð0RS4  à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|„ðA!Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁgÁÆ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@0Ð0RS4  à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|„ðA!Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁgÁÆ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@Á7ÁÖ@0Ð0RS4  q²<RS4 5ǯeiRS4 q°%<RS4 4±qÐRS4 ”@®&MRS4 Ç@´V0RS4 í6€0RS4  ë ³zRS4 ÕµmRS4 » Ð0RS4 A@¶ (RS4 0 · 1ÀRS4 ¶¼ Ð RS4  c ½ 8RS4 >À1ð RS4 •¾DÐRS4 ²ÁÀ RS4 W¿BÐ0RS4 †Â RS4 p ÃSÀRS4 —Ç4Ð0RS4 _¡"ð0RS4 ¸ @ð0RS4 ¹ @ð0RS4 Å@°0RS4 «Ä% 0RS4 CÆR€0RS4 µ È%ÐRS4 ±º 5À#R/RS4   à€RS48=F á’ýX(E=1ø³…²~,'.#ò #ö‚üÂ~ORS4PgF áx1ªD¢k*(Ò%D’!¢ü==T<_ÀÃŽ/‚,¤þÒü#þ1¨1H*Z@ükRS483F áT’Ë;Aq/Á #óL'î"±òüÃ}RS4@NF áR”+,*ÒòüâKGÿ†¥üÁô@<ârN#óã—÷Â~ò?À RS4@PF áQæ I”?' #Åò!ãO*2ˆ‚?~(„òüµ~áÒâÀ RS4@AF á!ÃE<#ÿ&[QÖcM!bòüâZâ?}‚?[1øMRS4à„9 à âkŠD¡;±+ñ;±+±+!¢¡!Š!‹¢¡¡²¡²±Š!Ž(±‹ñ‹:²ñ޲¢ñ‹±±ñ±±±‹±‹±±+±Š!ª!Ê!ª!î!J²±ÊÁj!*!j!ïâ±Ë±«±+±ë±K!*!Š!ÊÁj!Ê!ë±ê!K±kñ˱ë±+±Aña±/±/±!±K¢¢±Ê!J!ê!+¢±á±+ñ+±+±//±+ñ/ñK±k±o±ëñ!k±/±k±k;ñk±k±K±+±/±+*!J±+!êÁj#î"ê!‹¢¢(á#б±7ñ›±ï²˜¢ø¡ë±K&“Š#ŠV€¯€€€ŠµÈâ‹ñ‹±¢8¡/²8¢(¢8¢8¢8â(¢¢¡!Š!Š!¢¡ ±k±+±K±K±+±j!J²±j!뱫¢/±/ñ{±+ñ;±;±K±±±ñ+"ŠÁ޲8¢¢8¡+!ⱊ!Ž!Š!‹ñ±‹"޲¢8¢8¢8¢¢¡+#ñ‹±‹±‹±‹±‹¢(¢(¡±+#б;!‹~²X¢t¢±Ê'Š'Ê&J%Š#Š'Á±Ïñ‹±BñA±K±O¢ñ‹â¡!‹±B±K±Ï±Ë±‹²4¡+"N#Š#Ê"O¡1Á˱K±Ï±‹ñ±KñA±K±A’(|ñ˱K±Ï%’)(#Fî'â’v€RS4ß9 à âkRþü¢ÿýøòåɦšaÂ/¢+±k¢<¢¢?¢,¢-±‚ò¢¢â±‹¢ÁŠ!Š!Š#в8¡;#Š!‚Z±;#в(¢8¢x¢±Š²h±kñk±kñ+ñk±k±k±+±k±+ñk±+±B±O±K±+±Añ˱«ñk±Ë±bñ+±/±+±+±+±+±+±A±"±+±/±+±!ñ/±/±+±+±!±+±+ñK±c±/±/±"Á/±+â"¢"±/±K±k±+ñ!±ë±ë±+±ëñ«$ñ‹±Š!Š!Š!б!‡ñ‹±‹±‹±‹±a˜*aØj!Ûg€Š$вh¡‘±!Šò8¢¢8±‹¢¢(ññ‹:±+ñ+±!±!ñ+ñ+±+±/±Añ+±+±ë±Ï±±k±K±K±kñ+!«¢&¢$¢.¡;#ê#j‹±ë±Ë±ë±Ï±±±;±±±ñ{±[±{±±+²H¢8âX¡K!Š'Š#‹¡+á¡¢¡Š! ñ±‹²(¡±+²8¢â8¡;²(â8¢8á±ò¢¢±N!Š!Ê!J²±Á±Áñ‹¢±Ê!ŠÁÁ±‹±Ë±Ë±Ë±K±Â±AñK±K±A±Ëñ‹±‹±Ï¢±‹¢¢¢±¢~²±J!‹¡#Ê!Ë¢¢"Ï¢4¢¡+#Ê!ʲ(¢$!‹±Š!J"J#Ê#ÊÁ‹¡1ÁO±KñOñA±ñŠ!J!ÊÁʲ±+±+±+±! /A'î#á(RS40#9 à âk†úýxñåɦšañ±±ñޱ!‹¢¢¢¡!±Š²â±Š!Š!‹±‚ñ¡!Šñ‹±ñ‹ñK±¯±k±Ï±Á¢¡!ë¢â±ª!‹¢±K±O±o±k±/±+±!¢,¢$¢>±*ÁŠ#ê!ê²¢&±o±k±ëññë±Á±Ëñ«±±kñ+ñk±o±oñk±K±Kñk±oñK±K±o±K?±+/±+±/±+ñ+[±/±+ñë±+±ë±K±áò¡±‹¢¢¡!ñ‹±Š!‹¡!¢±±±±‹±‹±¡±¡!Š!ŠÁŠ!Š!±Š!Š!Š!ŠÁŠ!¡!¢¢¢ñв¢áÁñ±+ñ+±K±k±+±!±K±k±O!Š%ê&j²6¢¢~¢J¢h¢~¢x±Kñ+±o±año±k±o/±+±!±+ññ+ñ;±1±/ñ?±1±±Ë±»±›±û±[±‘ñ‹¢¢¢±ñ‹±‚ñŽ!ŠÁв¡Á±Š!޲¢!‹â¡!Š!Š!‹±âñŠ!‹¢±Š!Š!‚±Š±!Š!Š!‹ñ±A±K±KñB±KñKñD±KñK±KñGñ˱‹ñ‹±Á¢¡±‹±Š!Ê!Š!K¢!Š!O¢±Ê!˱J!‹ÁK±Ë±Ê!Ë¡±AñAñK¢±K±Ë¢±Ê!›¢ñA±KñK(±K±K+ñ‹±O±Ï±Añ!±+±&â/m"a "! RS4869 à âm3þ¡¹råɦšd‘±±‚ñ‹ñ‹±‹±ññв8¡²8±/±‹±‹±ñ‹±±‹ñ !б!ŠÁŠ!Šò¡ÁK±«±+¢¢±‹¡Á+"Ž$вh¡k"Š$б! ±K±«±ë±+±!±+±k±+±k±//ñ"±!±+±+ñO±k±Kñk±K±+±«±Kñ+±a±+±+±K±o±+±K±ŠÁJ!뢡!±+ñ+ñ+ñ+±+±+±+±+±+ñ+±+²¢â¡Š%в8¢X¡[²x¡#в±±;±‹ñ‹±±‹²áòñŽ¢¢¢ñŠ!Š!Š+"Š#в(¢8¢¢8¢8±Š²¡/±++±«±‹±ë±Ë±‹²¢¢±‹¢¢±/±/±+±!â±*²±Ë¢±‹±Ê!Á±ñ±+ñ?±±?±1±{±{±±{±K±ñ‹±‚ñ±‹±‹±¢8¢8¢(¢(âX¢x¡!Š!ñ±:!Š!ŠÁ‹¡!Šò¡±ñŠÁŠ!‹â¢±‹±‹±Š!вñ ñO±K±B±AñK¢±K±AñKñK±K±K±B±K±O±K±O!N!Ê!J!K¢±Ê!K±ñ‹ñK±Ë±K±Á±‹±Ë¢±J!Ï¢ñK±Ë±ËñK#Š#J"ʲ$â|±Ž#AñK±‹ñ˱‹±ÁñޱŠ!Ê!J!Ê!K±‹±Ë±Ë±Ë±¢”¢T¢´¢\¢ì¢Ø¢ü¢\¢8¢d¡ã/'ï1òVó…€RS40)9 à âkÿþÿAyråɦša ±±±‹*Áв8¡[#Š/Š*Š!Š%Šñ‹±‚á²±Ž!ޱ|±+/±k±K±K±+ñ!±KñK±+±K±Aâ<¡,¢±*#*#ê²$¢¢(±!±/±+±+±+±+±+±"ñ+±k±Ï±«±‹±k±«ñëñ«±/±!±k±+±+±k±++±!±+±!±+±¯ñë±k±Á±ë±K±+±ëñ!ñ+ño±kñk±ï±‹±ëñ«ñ+±+ñ+ñ++!Š!б!‹¢¡!¢¢8¡#б;#в(¢(±¢(¡+²X¡[²x¢(¡L¡Q±!‹â¡!â$Ž"Š&Š%вx¢8¡;&á/³ /ò‚‚±"ñ‹.±+±+±k±/±!±+±+ñ!ñë±+±ë±ë±«±!¢&¢8¢$±êÁê#ê#Ê#*²±k±+±kñ+±!ñ/±/ñ"±ë±K±+±ë±«±A±+ñ+±+±/±1±{±»±›±û±«±±ñ²(¢(¢¢8¡;²8¢8¡²(Ò_±+(Š*Š!‹¡‹/Š(б[$ ¢‚¢‚±+¡Œ¢‚)!Š!Ž!Šò8¡,¢8¡4‹±‹±‹¢§€Š¢£Šn€Š²ˆ!Š!Ž!‹¢â¡ /â4¢$¢â<±‹¡±‹±Ë±Ë±K±‹²(±K¡+"Š#Ê#J#Ê"Š!Ã;¢¢\¢<¡/'ʲ<¡[±K±Ëñϱ˱K±K±K±K;±/±+±!)B’ü„cøXRS4ø´9 à âk…þþxòåɦšaÑ;±±±;±±1±+±;!޲8¢¡!б"Š"Š!Ž"Š#б+!‹±ÁŽ!Š!Ž!ŠÁ±‹+±±‹ñ‹±‡ñk±+±+±oñ!±+±!ño±B±!±+±+;ñ/±"±+ñK±k±k±a±K±K±+±oñGñ/ñ+±!±+±‹ñ‹±/+ñËñë±+±á±/±/±#±/±+±+ñ"ñk±ë±ë±O±ãñ+±+ñ!±ª!‹±*Áê!뢱jñ+±+±/±"±/ÁŽ#в±Š±‹)вx¢ø¡‹òè!в¢¡+²8¡<¢8¢&ŠÁŠ'Š"вx¢8á'¡²¢¢¢¢¢¢¡Á‹ñ‹±‹ñ±+±+±+~!*!j!k¢â¢¢±j!±/±!±+±+!뢡!ª!ª!ê!.!Ê!A±kñk±+ñK±a±ê!k¢±+¢¢¢±ë±+±;ñ±±;±±‹±‹ñ‹"¡O²x¢X¢X¡[±±{$Š'вX±Š!Š!в¡¢±Š!±‚±/ñ‹ñ‹ñ‹¢±J!K¢¢¢¢¢±A±O¢¢±Ê!Ê!ŠÁJ±Ë±Ë±‹ñK±AñA±K±ÏñÁ ±Áñ˱Ïñ˱‚±Ë±K±±Ê!K±Ê!K¢â±Ê±K±A±A±K±OñK±OñK±B±+ño±k±+5ÐRS4  S ã,RS4 ~@ äCð RS4 Ü åoRS4  æ PÀRS4 1 çqÀRS4 u èÐRS4 ‰ é RS4 9 ê1à#V ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$SB1r‡3J¿&­6»²#Iez¼°¾Xf&r‡3J¿&­6»²#Iez¼°¾Xf&PBPFx„RTVRTNSOINþPFx„tRTVSRCINdPFx„0RTVRMLVD01PN00KV627SN YA1932096950TV0004PFx„œRTVWMLVD01OCFOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ITTIN1936350000242014PFx„¬RTCRP0VD01ED!0Ð.{Áà`xi 8@(i€TE 0203QFQASFDD0202STDNI$IQ PFx„HRTLRP4VD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTC#M½!~A¤ü™£FУñ™Ã&b™˜&%T&ÿî&Ó™Gª%™ª»™ÖªΙrª·™Ûª†CHIQ PFx„HRTLRP5VD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCÉìþûô#M½! A¤à™£VУ9™Ã&V™&*Q&ð™ï&ù™Gªž™ªu™Öª«™rª­ÝªŽCHIQ PFx„HRTLRP6VD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCúúö#M¾!ªA¤Ô£]У ™Ä& —&R&6ï&:™Gª™ªu™×ª©™rªÁܪÌCHIQ PFx„HRTLRPCVD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCþ#M¼ wA£Ù£yУ/™Ã&o™—&óQ&Òï&å™Gª…™ª›™×ªÄ™rª­Üª‰CHIQ PFx„HRTLRPDVD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCÉìýÿø#M½ ¡@££]Ï£fÃ&X™—&èS&Wï&à™Gª™ªW™ÖªB™sªÍ™Ü©^CHIQ PFx„HRTLRPEVD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCûú#M½ @£&£yУ·™Ã&[™—&éR&Íï&†™Gªj™ªn™×ª™rªÁ™Üª|CHIQ PFx„,RTLRM0VD02TCMCINPFx„,RTLRM1VD02TCMCINPFx„,RTLRI0VD02TCINPFx„,RTLRI1VD02TCINPFx„¸RTLWP4VD01#2D#3DINPFx„¸RTLWP5VD01#2D#3DINPFx„¸RTLWP6VD01#2D#3DINPFx„¸RTLWPCVD01#2D#3DINPFx„¸RTLWPDVD01#2D#3DINPFx„¸RTLWPEVD01#2D#3DINPFx„ØRTVER0VD01#IÄ0PFx„ØRTMER0VD01#IÄ0PFxÊG¶óa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ—ȉÇfèa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ— ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæÏ ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æÏ ÕQŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjöÍ RçîmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*öí Vç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæï Rç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æïiYEÒ “38]iYEÐ “38]WyÝEzÓYcyÝExÓYc]N9.4klh Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¾þÓÞo n[—¢èÃh¯:Uâ(˜2<È~FóªJÀ*´‚Jžˆ{jg¾žÚûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨n=.4{lh Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¾öûßï9’u,Ó¦:±¤¸.Ó3"‚„Îúõ82Äaõ„r™ @µ¨;jg¾¾Úûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨N!,4+lh Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¾þÓÚkåxH œb»fɵŒ[ë-ÀžVÈfl8†'Ø>Ãß+’šYú/¾žúûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ Ûù¨oµ&0{ì`Ž Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¾þÓßO¯­»/ˆ ¾Éº ˜ë$ÙˆŠ2@%¼ŒTHÙ$lG‡œéh'¾žúûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢Ûù O¹&0kì`Ž Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¼öóÛ9lĴʰi sJܨ·%+â2gé$Þ’eLœ¾©j/þ¾úûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ o½&0{ì`Ž Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¾ö»ÿN’ì`NkÐø¡*´*“#  Hƒ:ó@&HL±Ê€Ir'þ¾þûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ ®¾ÉåÞ›û°‹š$[rGã°ÿÿÿÿÿÿÿÿÿöw맬`}}‚PúÏüY ˆ}Š*õèz•Hª¿ïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿU¾§_慎n8Y´îº74²@2g•oЈ£$¸ˆ¢ðÏï™›û7wþîÿýßÿ»¿ÿg~ÚŽ]X¶"aO%Sr°K½ïlÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ¹?üÌÞnìãhØ(…!(´vØrFx]—Ÿ+b•€„ÂdB{1ê–~Yç2Ÿ* ±TPâÓaµ«PÀ¤ Ò;3ÉdçJWáteúIvíC·"@¸Uײkp”20sN ÷U9Pë7 ’||Ö¬ß4÷.g 53 SÓ‰PåUmD:+QbqDÅ6EG4Z@ 5¦%¹(sŠC¡D@Ó}±'gBOhÒpF$x$ÕOJâñuµ–ažfa€ÒHa¿bÅîsóBæÀE&Hjr5'ËöQjT:&VôÇ(tÐc°?'EpÎm@q’zQ6ñq5'ŽkTÊi.µ,BíA` éHðøM¥±p”Ðv­2ÃñAcb8pûdñdF?Aêsp/%Ip…Îm@q’zQh2ð×-vÐs 6Žt÷ZLÀ= s¢j$(5örZ@ƒbøI¥W b #d7m”ÊX§‘Á Ì,@Q’ZUh2ð×)vÐc°?'er5ˆu°fx³+DDwßD•²•¶c5jÀ{—¹rÖtrDàB õ^0v$6ƒ^÷gt"Czrl2pÇ)vÐc°?'EpÎm@±q`‡(…Ë—w"7¸bƒãÜ ±­:—V$Ï.dƒQÅçCTŒc¦z7„ Á_i”;#¯$æ(uØ`0O%E0Îm@q’zQx0à•)7Sbp.u¸DPÊ´Õ9âTé~µH@ºq‘XtÛ|Ö•a¬BR†¤ÇûY*wO!Ip…Îm@q’zQh2ð×)vÐc€½‚v5Û –}32wp2amôæ8”Þpus/8†Ð*u` ' ÷–Y‡Ù3&$±F‡ù ³'r‡UAfp‚èDª3tš¼h7¡$3‘…a25çl0Hi¶b÷ÚDe¥xÓÓ-, &RQ˜<”Üõ Žg/Ncû1_RðŒWp³ Qô kÅ+eüGrY,Rñ™4\D¨$·´ $é&4¤ÜU“ê1¤KùH§"5åšs7Ê$²`ÖÁL‡Iñ]"ü ”gQÇþ A–e*…ãPÁV0EJ$0ðLÁjcXÖ]•iQ²šÛ@¹ gÐAE!åå*AWÑpòËtó^÷ÿ=÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷þwÏ~×zmc·Ecu.f…&|ÓàxFb5ª(†ì Æ=Z³Å6Rµ¾ 7(ðÈCD6ãGe†ÐBuóSGl“T$Çìyƒ?'Ö6må’x`:ChX”U#¸Õ9qÚpQþ|TYvR îY¥ÄrGëpayòÕiOf¬huõ$vˆ2Õ sj8ÖK5ä\Q¦ŸfTL†‹)A?rÃlug”xQÎSàÔ#ûjö)Ój1¯$Ð@òÞ=R(hxrƒ%£s³0†HqËä¢Aôî{‘øR%&PÓ2ÛFjx¤äXS7:õÓbV&(—Ty–¿fXP3"= Ĵ&87ó8 ¢Öoe… ãry·mP2aS¼"1v K føF'ð]2fScPVE^'$‡zK¶žµ"–SXr¥ƒu0D10À~ þ~&aè,Wq1Æ%L¦},†:Ä´pý$rϵ’±B>Æ $µ8¦{/ C£1w"tñ5·*…mVecfÀ0…,`Mçºáf=n¼lÖ~A̳ÆD0OcŸLPsO  *ffáñ!DI2œF@QÄ„;dEXám“”’CÕ„€eÇpã"íµœ } '00dÔ¯0¹kÃáp¤„=5H!Dº"Ô| RÑyS³<^ÆÓD 6œ<ä@ ĺa±ZCÌ6|mr("gTŽ  ÂH#ÓkÒX&›|„x(³Þ(¶TqYfóÛ¥s ÅI1üFƒ6xF†B[ì`P X&‘; šv‘Wü ³‚ äW±x¥lLÒÔPf;»tp8tUhyÖ"qÙ,f8ÓÂ{±MŸR'¦@Ƭr`y:ö`51"VrSõ=Š0×°*G.LÅÐ "Zfý~v?8b‘×Þ )4Oã[#KP†!ÅyBGM'w8ƒ‚0R"1àÖDìp¤iIâŠ2sW3v.¡=%êpV]gr¡5UGê “.aL3j%PW4PGJDçY§K F3&z2#<ÄP ‚D\:sû)h:PðEW`c€xÅK#dpm,€eáæQòòC³…tMÁÇ;£Šfæ‡u µb@UAèð¯Yqº1ål!ôjüEBgx¥§¦H²g9tíS`A5n0$XAŒY÷jPºlF$EÆ«cG&CÂfqvprRYÌ$ªàŒL·R ‘2vœ,gÐçwýb€ŽQuQ&'ÑPTm{e_ ù^ê R;p„L00Bd@G$E¢=äÿPèR 9&¸"§÷cþu~EF bÉ6Îu6y‘vJDàr añLZ–ÀvBn!í$"(€M!>J¡»r20TÕ˜P+yq¤1Õí>$æœXZD„g\B” æ‚'ú1²i%ïXk1bjµÜx Ú>p4µBE#ªp64s:æ²EÃgÀBA©H!€žZàtwÿõâ“Q`¾,p`â|0vÄt&rQg=—¿yàmt}!Ѝ‹è©©½ÑÄè®H\ îEg@ ìdÉûƈS£!5;ìz¡Ž¤­êS‡k[ÿCù2À õ â&QÐ/êè— àU1†¨‚ }n¦f‰À ဎÕ_ŽŠÑðSPPCRD = `8˜ ¤¨‘9!ó?„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096950CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFxºU˜@;Ô+„(RTVHDRVD01PTVTOCÕ7tÕ¸]PFx„pRTVTOCPTüVINIÕ«±¸$CP00ÿ;@aa PVRTNÿ{c EVSRCÿdxþŸVRMLÿe4ñŸ VWMLÿ;e ‰ŸhCRP0ÿÛf°]Ÿ,LRP4ÿ‹gLÊ“LRP5ÿ×mL7œ“LRP6ÿ#tL¤š“LRPCÿozL™“LRPDÿ»€L~—“LRPEÿ‡Lë•“LRM0ÿS0ß• LRM1ÿƒ0Ó• LRI0ÿ³0Ç• LRI1ÿã0»• LWP4ÿ޼Œ•/PTbLWP5ÿÏŽ¼]•/LWP6ÿ‹¼.•/LWPCÿG¼ÿ”/LWPDÿ‘¼Ð”/LWPEÿ¿‘¼¡”/VER0ÿ{’Üj”7MER0ÿW“Ü3”7PFx„ŒRTVINIDR10-WAY PROC CUODFN00FX518PN00FX740SN YA1932096950CC54E8HE0001CTHWB3B4B7 PR5P"€VZ01CE1PFx„Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁdðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|tðA¡¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁdðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|tðA¡¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁdðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|tðA¡¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁdðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|tðA¡¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁdðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4( à¬hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-hàŒ£ø-dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðL£|tðA¡¶€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>Á*?‚Ö€>ÁwÁdðL£|dðL£|dðL£|dðL£|dðL£|dðL£|dðC RS4  q²<RS4 5ǯeiRS4 q°%<RS4 4±qÐRS4 ”@®&MRS4 Ç@´V0RS4 í6€0RS4  ë ³zRS4 ÕµmRS4 » Ð0RS4 A@¶ (RS4 0 · 1ÀRS4 ¶¼ Ð RS4  c ½ 8RS4 >À1ð RS4 •¾DÐRS4 ²ÁÀ RS4 W¿BÐ0RS4 †Â RS4 p ÃSÀRS4 —Ç4Ð0RS4 _¡"ð0RS4 ¸ @ð0RS4 ¹ @ð0RS4 Å@°0RS4 «Ä% 0RS4 CÆR€0RS4 µ È%ÐRS4 ±º 5À#R/RS4   à€RS4@FF áÃ#D,Á#óµôÁL.B¢?¢Ìâ~x?ÿã„ò  RS48@F áT²1³äøn,B/&Ì1øò?Ò~]#N'¤ò?À RS489F áòéX'Ch)’N/¢=-)C‚ü‚~"ã<RS4@EF áRòcA=Ã6‚Z"ƒÄoøÂ3ÂìÓƒÒ_1ø{RS4HRF á"’”1âùâ++1¬„?ü_!qWàNjRP7#Óµ~c‘1£„Ð RS40*F áòm‚t'¢¢ü0òü$‚° RS4x´9 à âk“}?ŸÄóçɦšaÑ+±?±;±±ÁŠ!á!Š!‹á²¢±ŽÁŠ!Š!Š!Ž!¡ò±‹¢¡!Š!‹ñ±‹¢ââ#Š!б;²8±Šò¢(â¢(¢(¢8±Š#Š"Š#‡ñ+±Ï±«±ï±gñk±K±K±ï±+ñK±O±gñ«±‹ñ+±ï±A±+±+±/±+ñ!±kñ+±k±+±K±/±/±+/±KñK±oñ#ñ+±+ñ!±k±kñ+±++±+±+ñ/ñ!±Kño±k±+±a±‹±‹±‹ñ‹±‹±‹!‹±Š!‹±Š!¡±±‹±±‹±‹ñ‚¡/±+ñ!±! ±K±K±k±K+±Kño'±±±+±;ñ±K±+!‹±Z!J!J!ª!ú!ú!;¢‚ñ‹±‹ ñ±‹ñ±‹±‹ñ‹ñ‹±‚¢(¡ÁŠ#в8¡"‡áÊ!Š!J!Š!ÊÁJ!J!±O±O±K±Kⱋ±‹¢±‹±Ê! ñO±K?±K±K;ñK±K±K±K±‹ñË¢|¢,¢¢$¢h¢h¢|ñÊ"Š(±AñOñAñB±K±A\)C’|Ò~ÐRS4˜ó9 à âk’ü~ÞŸýùðɦšb‘;±+±;±;ñ1±[±,¢H¡+'޲h¢!ŠÁ‹¡!Š!‹±Š(¡,¡+"Š!Š#Š!в(¡(¢¢±*!뢱*!ÊÁ±K±k±K±k±K±k±+±+±K±+±+±k±+ñ+±!±kñkñk±O;±+±+ñ#ñ+ñ+±+ñ+±+±/¢¢±ë¢¢¢¡!ëm1ñk±+±k±o±K+ñ+±"|ñ‹ñ‹¢8¡#в¢8¡+ÁáÁŠÁŠ²á²˜¡‹¢¢ãбzq(8±‹±‹±‹/±±¡²ñŠ!Š!Š!Š!‚ák±K±K±+±Kñ/ñ+±K±ë±k±«±Kñ+±+±!±! ±!ñ+±+*!+±;±¢¢¢¢¢±!ñ+±±#ñ‹±‹±‚±‹±±‹±‹¢±‹±Š!Š!Š!Š!‹+¢¢ñŠ!Ž!Ž! ¢¢â¢¡¡!k±A±‹±Ë±‹±Á±‹¢¢¡!Š!˱J!Áñ˱ϱ˱‹+±‹±Ë±Ëñ‹±¢¢¢±N!Š!ÊÁJ!Ê!A ñ˱K±Ë±K/±O%±K±K±O±Gñ/ñ+±+±+±"%9RS4h‘9 à âk˜ý~¿ßýøñãɦša ñ‹ñᢡ!в!б‚±ƒ¡<¡+²8â8¡;!‹¢±á!Š!ŠÁŠ!/±!±'ñ+ñkñk±+±gñ"ñ/ñ!±o±+ñk±k±!±‹±‹±k±ë±Ïñ+ñ+±+±+ñ!±/±!±!±+±#K±+±Á±ë±KÁޱ¢¢8¢¡+#â(¢X¢±‹¢X¢x¡k²(¡A±+±;!Š"в8¡#Š"‚ñ±‹ñ‚±Š!‹¡/"Š#¢8O±‹+±‹±±ñá/±!±! ±+ñ/±!±+±/ñ!±+±+±/±kñ'ñ«±k±Ë±ë±Ë±‚±±±±±±±¢(â8¡²8¡<¡Á‹±‹±‹±â±‹á!вñ‡á;$вh¢X¢(¢x¢H¢H¢(¢x¢¢8¢8¢¢¢8â(±Š¡¢¢¢¢¢¢¢¢1”¢8¢h¡j1H©|üª1¬¢D¢X±O±Ë±ÁñÃ;±ÁñK±Ë±ËñO±K±K±KñO±K±KñA±O±Ë±‹±Ë±K+±K+±/±k±k±+±DÒü<%Á<"RS4¨ 9 à âkŠF¡ñ±±‹±‹±±n!‹¢±¢±Š!Ž!±+±K±+±k±K±!±+±k±k±oñA±+±+ñ+±!ñ+ñ++ñK±+±k±+±ë±k±kñk±ã±/±/±!±k±«±Ë±ë±K±Çñ+±K±O±+±K±+ñk±k±O+±+ñ! ±/±+±!±! ±+ñk±k±kñ+ñ+ñ/+±±‚±ñ‹±±±±±ŠÁ‹á!¡±±‹±ƒ¡+ñ+±!±!ñ/±!±!ñ+±+±!ñ+±+±!±!±!±+ñ+±"¢¡¡!«¢¢±Ê!«±±+±;±±1ñ˱+±;±û±¯ñ;±±+±;+±‹ñ‚±‚±‹±‹±‹ñ‹ñ‹²â8£0+B‹¨ 0 (!Ò¡¡ò±‹¡!‹¢±¢ñޱ;²8¡+"Š#‹¡;"Š#ñK±A±K±B¡+"ʲ¢4¢,¢<¡#‹¢,ñKñ˱K±‚±A±OñK±C±K±O±K±KñKñAñA±Ï±ËñÏñA±Ê±Š!J!Ê!K¢¢¢¡!Î!Î!K¢±O±‹±Ëññ‹±±ËñO±+±k±kl+ 'æ RS4àŽ9 à âk–ú|Ÿýyñçɦšaѱ±;²8¡+!Ž"Š#Š!‹¡+#±Šò⢱Š!ŠÁ±‚ññ‡ñŠ!бŠ!Š!‹¡4K±k±+ñO±K±O±ï±áñ+±Ï±+±+±K±k±‹±+±‹±ñ˱k±Ë±ë±+±çñ+±!±+ñ++±ë±±k±Á ±+±/±!±"±+ñ+±/ñ«±K±ë±ïñC±+±/±!ñkñKñk±k±Añ²!Š!Ž!‚ñ‹ñ‹±±ñ‹±±Š!Š!ŽÁŠ!Š!‹+¢ÁŠ!Žñ‹±‹±±¡+!‹¢8¡<¢8±Š±"±+±++±ëñk±¯±!±‹ñ«±Ë±«±Ë!ê"Ê#j#O¢>¢*¢¢¡1Á뱫±ï±K±Ë+ñ/ñ+±! ±;±±;±±!±«±›±«±û±{±!ñ‹±±‡â8¢8²¢8¡;"Š#б;!‹¢(±Š#в8¢(¢ñ‹±‹±‹.²(¡#Šò8¡;ÁŠ#±‹±‹ñ‹±±¢¢¡!K¡!Á¡!Áo±JÁÊ!Ê!Ê!ŠÁŠ!Á±K±Ë±K±Ëñ˱‹±Ëñ˱‹±Á±K±K±Ï±‹+¢X¢x¡L¢h¢|¡{$Ê&Ê%A±Ë±ñ˱˱A±KñK±K:!K¢¢(¢(±Š#Ê#Ê"‹¢!K!‹¢¢±‹¢ñ+ñ+ã%†â.€RS4Á9 à âkŸ{}¾?ýøñãɦšaѱ±±²¢áò±Š!ŠÁ±‚ñ±ƒñŠ²â¡‹ñƒ±‹¢¡!‹¢±Š!o±!ñ/ñ!ñ!±/±"o+ñ+±+±+±+±O±+/ñ!±!±+±+±+±+ñ+±! ñ/±+ñ+ñ+ñ+±#Á‚±‹±±‹±‹±‹3â8¡,¡;#‹¢8#Š# ±ëñK±ë±«±§ñ!±/±+/±!±/±+±!ñ+±+ñ!ñ+±+±'ñ+±[±¿±»±û±±ƒ±Š"Š"‹¢8â8¢(¡(ñ‹ñ‹±‚±±‹ñᢱŠ!Ž!Š!Š!!Š!ŠÁ‹¡Á±±‹±+±±‹±‹±‡ñ˱˱O±k±KñK±KñO±K±AñK±K±K+ñK±Gâ¡!K±Ê!Ë¢±ËñA±K±Ï±Ã 'äãIRS4  S ã,RS4 ~@ äCð RS4 Ü åoRS4  æ PÀRS4 1 çqÀRS4 u èÐRS4 ‰ é RS4 9 ê1à#V ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$ ÄÈ,Üì»ðÐŒÒhå$SB1³uüô¿&­6»²#Iez¼°¾Xf&³uüô¿&­6»²#Iez¼°¾Xf&PBPFx„RTVRTNSOINþPFx„tRTVSRCINdPFx„0RTVRMLVD01PN00KV627SN YA1932096950TV0004PFx„œRTVWMLVD01OCFOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ITTIN1936350000242014PFx„¬RTCRP0VD01ED!°Ð.«b<HxìŠY€TE 0203QFQASFDD0202STDNI$IQ PFx„HRTLRP4VD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCóóõ ì#M½ k@£ã£bУ Ã&=—&öR&ëî&¸™Gª~™ª\™×ª™sªŒÜªMCHIQ PFx„HRTLRP5VD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCÉìò íñô#M¼ ]@£Ñ£jУ Ã&.—&éS&Èí&¡™Hªv™ªR™×ªm™sªxܪOCHIQ PFx„HRTLRP6VD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTC÷õõñ#M¼ q@£à£`УÃ&G—&éR&Ùî&½™Hªt™ªM™×ªh™rªsܪMCHIQ PFx„HRTLRPCVD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTC#M¼c?¢Û¢yÏ¢ Â%D–%R%Þì%«™G©ƒ™©W™Ö©yr©‘Û©RCHIQ PFx„HRTLRPDVD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCÉìóøòö#M¼k?¢è¢yÏ¢Â%G–%øR%Üî%¸™F©~™©_™Ö©r©›Ü©^CHIQ PFx„HRTLRPEVD01#V a¾6Ò$í¤ª¾kÆ`Ú&#PTCøööï#M¼s?¢Ñ¢tÏ¢Â%A™–%éQ%×î%¡™G©…™©KÖ©cr©vÛ©OCHIQ PFx„,RTLRM0VD02TCMCINPFx„,RTLRM1VD02TCMCINPFx„,RTLRI0VD02TCINPFx„,RTLRI1VD02TCINPFx„¸RTLWP4VD01#2D#3DINPFx„¸RTLWP5VD01#2D#3DINPFx„¸RTLWP6VD01#2D#3DINPFx„¸RTLWPCVD01#2D#3DINPFx„¸RTLWPDVD01#2D#3DINPFx„¸RTLWPEVD01#2D#3DINPFx„ØRTVER0VD01#IÄ0PFx„ØRTMER0VD01#IÄ0PFxÊG¶óa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ—ȉÇfèa˜Yuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÿ_¯ÿþ— ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæÏ ×1ŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æÏ ÕQŽoÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjöÍ RçîmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*öí Vç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨, †œjæï Rç1îmÝænÿÿ7ý«å_´š1w÷»ýß¿nüïww¦¨,†œ*æïiYEÒ “38]iYEÐ “38]WyÝEzÓYcyÝExÓYc]N9.4klh Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¾ÛßN­£“n–X‘;̇•ó ÛèÀKšƒ3J2àóMµ>[pgÿ¾þûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨n=.4{lh Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¼þóÚÎ-бûÑ"‰ú…!uz+Å ùŒm¹”¡è ;Ìh¯Ç»zg¿¾Úûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ ›ñ¨N!,4+lh Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¾ö»ÿNšíaké¸PÑaªsBG0Ø»E//ÏÐLúâ»e¥œKbgÿ¾Þûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ Ûù¨oµ&0{ì`Ž Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼¶ÛÛN½c›lrë=a”œd„8о›4„ª S"nyíC£Kxg¾žúûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢Ûù O¹&0kì`Ž Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoí¼þóÓÏn{CŽDa졜€> ‚^(‹ &îé) ÎaU‰¸ øg¾¾úûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ o½&0{ì`Ž Y{'¥¾æÒo‡qjgR¡²§ž*Ùii¿6þùÿ￾ÿÛoÿ¾þûÿ¯·¾ÿûo¾þúÿ综_ûGm¾Þë´ç¾šïûo¿¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûÿ￾ÿûoï¼þÛÞN¹‚zl—Œ¼¸$9¼V&¾x@f’Ž>3h8(O ¶¾Ûjo¾žúûïí¿¾ÿûoÿ¾þûÿ￾ÿûoÿ¾þûûï=¶¶šGOŠ¢›ñ ]9^½Çº¢=ùót{Ö´YÑÿÿÿÿÿÿÿÿÿöw맬`}}‚PúÏüY ˆ}Š*õèz•Hª¿ïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿUÿïÿÿê¯ÿÿïÿU¾§_慎n8Y´îº74²@2g•oЈ£$¸ˆ¢ðÏï™›û7wþîÿýßÿ»¿ÿg~ÚŽ]X¶"aO%Sr°K½ïlÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ»ýßÿnÿÿwÿ¹?üÌÞnìã(Ø(…!(´vØrVY]·W+ീÒdB{1ê–~Yç2Ÿ* ±TPâÓaµ«PÀ¤ Ò;3ÉdçJWáteúIvíC·"@¸Uײkp”20sN ÷U9Pë7 ’||Ö¬ß4÷.g 53 SÓ‰PåUmD:+QbqDÅ6EG4Z@ 5¦%¹(sŠC¡D@Ó}±'gBOhÒpF$x$ÕOJâñuµ–ažfa€ÒHa¿bÅîsòBæÄE6DkRA&K6öQjT:&VôÇ(tÐc°?'EpÎm@q’zQh8Ѳ5‘ˆK‘·7].×KM‹„>c¡dµ6(DÜjQ{u- ®vª$6ÇûY*wO!Ip…Îm@q’zQh2ð×)vÐk¸å`vUPqxã°ì&äæò¸sA5pXS$—ùC$ç¦7²@Zrtw…sNx`ïRVh2pÇ)vÐc°?'EpÎm@óöaº(ñÑ|õà1y`ñJHÂà ӱg>€çeK0ô¬5”IÁ_i”;#¯$æ(uØ`0O%E0Îm@q’zQx0ð·%VcC)T|r&yDA6*ÖvD9w¿P+õz&88G–ípTä'6 D»iIsÉIXÎ @Q’zQh2ð×)vÐc°>7erÀitJ¡ÿèÀº¦°CÒQþ`1'lfù"gœâvãÖ!q^pV$v N÷gt"Czrl2pÇ)vÐc°?'EpÎm@q²w`8<ôž8å¦Y‘¯!õwzAs€e(Fç0v bÁSgBhÀ4ÖlSÒŠ#"xÁÌQsGÆTKhññi‚Vq¸P±S´@±stSÊT€¡}WpyUÚ'P(‘Ru@eh$J&¸^FAAÓË9q¿bUŠ RBg^Dh 4ëRÆ0 v´PÅ|P‚k5ÛF\arÐŽàRV^±¶¦lQ ¨TB´JPRqžr[tSYÆ*1ǧE^(´ßeemQð?S¨fˆHu¡râ.5CrÅ ÁF9zpV'ÞaV@9%·hòeà:÷KD÷T³ô25d„/`Ò2`Rvpd•D1ÒqCa:äBy…Oqçùh(A‹\áÛ[S¹Í±A}áâK¡ŠQNv‚((T´b’pCÜ.ƒ‡yƒÝQ!ß¿.„‡Ô3?;c§ñ,—WQ Z!æ`ö|ªZ¶ã&ÇxóæX€º1æU#tcûTe"7ZUzcÖ•B‡ÃaÆ6?Ó•ÖD†,fXTë#ÃV2a|”D93Ô,52H@ð-“Pu× qî=6SK ªqŒXðd!`l#Īe:bTQÄÓßw0@V4%EB%F<$F%ó* =7ïl†â%ÂÐTì*Pd.DJQç6X ¬ßáhçäiå;‡ûRC@öºMÄŽ1RÌñfu?L'–#2&ršH°*¦ÝdzF׃T‘¥K÷ÎEè. S³áÚi:së2&qá´•q7¸qÖA3f^ÇÀ±° W%æhÊ$vK*±A †=a#ö±t¥6ÖUp$iÑH[|³T hUë |×÷QñŽ1V\T< 6:)!@=LÁ$0s˜:3$ ^÷Ùm¤öçV 07ùD†ˆbc5rÑL†È8#X÷š`De´™R¤a¢|ÎXñ RÒ‚’|@$(Úkör$† ò\TV01›² äÛq•ÿ5÷>uï=÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿ{÷ÿwÿ~÷ÿ}÷ÿs÷üWÏrÇ{m0é{3ßwï^÷ûq7ús›Azpöõ1`0‘N0Q3Daq A×Z@I3VZÓ•$ØuHE³ QíûL@³%48Z x‹C@¥¾Bòdz1œDéb3$€ 2Å1>}!Ѝ‹è©©½ÑÄè®H\ îEg@ ìdÉûƈS£!5;ìz¡Ž¤­êS‡k[ÿCù2À õ â&QÐ/êè— àU1†¨‚ }n¦f‰À ဎÕ_ŽŠÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðSPPCRD ¬ `8˜ €ÑðHOSTSRP (HPè0Ð þín((ZìýpýkÐ0ýX`pÉ„ü0É„ýÐýpýpýkÐ0JýpýkÐ0ýX`p=;ibm,hbrt-vpd-imageibm,hbrt-target-imageibm,hbrt-code-image#napfast-sleeprvwinkle #address-cells#size-cellsphandleibm,enabled-idle-statesreserved-namesreserved-rangesÑðHS KID è0 (èskiboot-skiboot-5.1.13/hdata/test/stubs.c000066400000000000000000000025371265204436200203020ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include void _prlog(int log_level __attribute__((unused)), const char* fmt, ...) __attribute__((format (printf, 2, 3))); #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif #define prlog(l, f, ...) do { _prlog(l, pr_fmt(f), ##__VA_ARGS__); } while(0) void _prlog(int log_level __attribute__((unused)), const char* fmt, ...) { va_list ap; va_start(ap, fmt); if (log_level < 7) vprintf(fmt, ap); va_end(ap); } /* Add any stub functions required for linking here. */ static void stub_function(void) { abort(); } #define STUB(fnname) \ void fnname(void) __attribute__((weak, alias ("stub_function"))) STUB(op_display); STUB(fsp_preload_lid); STUB(fsp_wait_lid_loaded); STUB(fsp_adjust_lid_side); skiboot-skiboot-5.1.13/hdata/vpd-common.c000066400000000000000000000022321265204436200202320ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include static const struct machine_info machine_table[] = { {"8247-21L", "IBM Power System S812L"}, {"8247-22L", "IBM Power System S822L"}, {"8247-24L", "IBM Power System S824L"}, {"8286-41A", "IBM Power System S814"}, {"8286-22A", "IBM Power System S822"}, {"8286-42A", "IBM Power System S824"}, }; const struct machine_info *machine_info_lookup(char *mtm) { int i; for(i = 0; i < ARRAY_SIZE(machine_table); i++) if (!strcmp(machine_table[i].mtm, mtm)) return &machine_table[i]; return NULL; } skiboot-skiboot-5.1.13/hdata/vpd.c000066400000000000000000000434111265204436200167500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "spira.h" #include "hdata.h" #include #include "hdata.h" #include struct card_info { const char *ccin; /* Customer card identification number */ const char *description; }; static const struct card_info card_table[] = { {"2B06", "System planar 2S4U"}, {"2B07", "System planar 1S4U"}, {"2B2E", "System planar 2S2U"}, {"2B2F", "System planar 1S2U"}, {"2CD4", "System planar 2S4U"}, {"2CD5", "System planar 1S4U"}, {"2CD6", "System planar 2S2U"}, {"2CD7", "System planar 1S2U"}, {"2CD7", "System planar 1S2U"}, {"2B09", "Base JBOD, RAID and Backplane HD"}, {"57D7", "Split JBOD, RAID Card"}, {"2B0B", "Native I/O Card"}, /* Anchor cards */ {"52FE", "System Anchor Card - IBM Power 824"}, {"52F2", "System Anchor Card - IBM Power 814"}, {"52F5", "System Anchor Card - IBM Power 822"}, {"561A", "System Anchor Card - IBM Power 824L"}, {"524D", "System Anchor Card - IBM Power 822L"}, {"560F", "System Anchor Card - IBM Power 812L"}, {"561C", "System Anchor Card - DS8870"}, /* Memory DIMMs */ {"31E0", "16GB CDIMM"}, {"31E8", "16GB CDIMM"}, {"31E1", "32GB CDIMM"}, {"31E9", "32GB CDIMM"}, {"31E2", "64GB CDIMM"}, {"31EA", "64GB CDIMM"}, /* Power supplies */ {"2B1D", "Power Supply 900W AC"}, {"2B1E", "Power Supply 1400W AC"}, {"2B75", "Power Supply 1400W HVDC"}, /* Fans */ {"2B1F", "Fan 4U (A1, A2, A3, A4)"}, {"2B29", "Fan 2U (A1, A2, A3, A4, A5, A6)"}, /* Other cards */ }; static struct dt_node *dt_create_vpd_node(struct dt_node *parent, const struct slca_entry *entry); static const struct card_info *card_info_lookup(char *ccin) { int i; for(i = 0; i < ARRAY_SIZE(card_table); i++) if (!strcmp(card_table[i].ccin, ccin)) return &card_table[i]; return NULL; } static void vpd_vini_parse(struct dt_node *node, const void *fruvpd, unsigned int fruvpd_sz) { const void *kw; char *str; uint8_t kwsz; const struct card_info *cinfo; /* FRU Stocking Part Number */ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &kwsz); if (kw) { str = zalloc(kwsz + 1); if (!str) goto no_memory; memcpy(str, kw, kwsz); dt_add_property_string(node, "fru-number", str); free(str); } /* Serial Number */ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &kwsz); if (kw) { str = zalloc(kwsz + 1); if (!str) goto no_memory; memcpy(str, kw, kwsz); dt_add_property_string(node, "serial-number", str); free(str); } /* Part Number */ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &kwsz); if (kw) { str = zalloc(kwsz + 1); if (!str) goto no_memory; memcpy(str, kw, kwsz); dt_add_property_string(node, "part-number", str); free(str); } /* Customer Card Identification Number (CCIN) */ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &kwsz); if (kw) { str = zalloc(kwsz + 1); if (!str) goto no_memory; memcpy(str, kw, kwsz); dt_add_property_string(node, "ccin", str); cinfo = card_info_lookup(str); if (cinfo) { dt_add_property_string(node, "description", cinfo->description); } else { dt_add_property_string(node, "description", "Unknown"); prlog(PR_WARNING, "VPD: CCIN desc not available for : %s\n", str); } free(str); } return; no_memory: prerror("VPD: memory allocation failure in VINI parsing\n"); } static const char *vpd_map_name(const char *vpd_name) { /* vpd_name is a 2 char array */ switch (vpd_name[0]) { case 'A': switch (vpd_name[1]) { case 'A': return "ac-power-supply"; case 'M': return "air-mover"; case 'V': return "anchor-card"; } break; case 'B': switch (vpd_name[1]) { case 'A': return "bus-adapter-card"; case 'C': return "battery-charger"; case 'D': return "bus-daughter-card"; case 'E': return "bus-expansion-card"; case 'P': return "backplane"; case 'R': return "backplane-riser"; case 'X': return "backplane-extender"; } break; case 'C': switch (vpd_name[1]) { case 'A': return "calgary-bridge"; case 'B': return "infiniband-connector"; case 'C': return "clock-card"; case 'D': return "card-connector"; case 'E': return "ethernet-connector"; case 'L': return "calgary-phb"; case 'I': return "capacity-card"; case 'O': return "sma-connector"; case 'P': return "processor-capacity-card"; case 'R': return "rio-connector"; case 'S': return "serial-connector"; case 'U': return "usb-connector"; } break; case 'D': switch (vpd_name[1]) { case 'B': return "dasd-backplane"; case 'C': return "drawer-card"; case 'E': return "drawer-extension"; case 'I': return "drawer-interposer"; case 'L': return "p7ih-dlink-connector"; case 'T': return "legacy-pci-card"; case 'V': return "media-drawer-led"; } break; case 'E': switch (vpd_name[1]) { case 'I': return "enclosure-led"; case 'F': return "enclosure-fault-led"; case 'S': return "embedded-sas"; case 'T': return "ethernet-riser"; case 'V': return "enclosure"; } break; case 'F': switch (vpd_name[1]) { case 'M': return "frame"; } break; case 'H': switch (vpd_name[1]) { case 'B': return "host-rio-pci-card"; case 'D': return "high-speed-card"; case 'M': return "hmc-connector"; } break; case 'I': switch (vpd_name[1]) { case 'B': return "io-backplane"; case 'C': return "io-card"; case 'D': return "ide-connector"; case 'I': return "io-drawer-led"; case 'P': return "interplane-card"; case 'S': return "smp-vbus-cable"; case 'T': return "enclosure-cable"; case 'V': return "io-enclosure"; } break; case 'K': switch (vpd_name[1]) { case 'V': return "keyboard-led"; } break; case 'L': switch (vpd_name[1]) { case '2': return "l2-cache-module"; case '3': return "l3-cache-module"; case 'C': return "squadrons-light-connector"; case 'R': return "p7ih-connector"; case 'O': return "system-locate-led"; case 'T': return "squadrons-light-strip"; } break; case 'M': switch (vpd_name[1]) { case 'B': return "media-backplane"; case 'E': return "map-extension"; case 'M': return "mip-meter"; case 'S': return "ms-dimm"; } break; case 'N': switch (vpd_name[1]) { case 'B': return "nvram-battery"; case 'C': return "sp-node-controller"; case 'D': return "numa-dimm"; } break; case 'O': switch (vpd_name[1]) { case 'D': return "cuod-card"; case 'P': return "op-panel"; case 'S': return "oscillator"; } break; case 'P': switch (vpd_name[1]) { case '2': return "ioc"; case '5': return "ioc-bridge"; case 'B': return "io-drawer-backplane"; case 'C': return "power-capacitor"; case 'D': return "processor-card"; case 'F': return "processor"; case 'I': return "ioc-phb"; case 'O': return "spcn"; case 'N': return "spcn-connector"; case 'R': return "pci-riser-card"; case 'S': return "power-supply"; case 'T': return "pass-through-card"; case 'X': return "psc-sync-card"; case 'W': return "power-connector"; } break; case 'R': switch (vpd_name[1]) { case 'G': return "regulator"; case 'I': return "riser"; case 'K': return "rack-indicator"; case 'W': return "riscwatch-connector"; } break; case 'S': switch (vpd_name[1]) { case 'A': return "sys-attn-led"; case 'B': return "backup-sysvpd"; case 'C': return "scsi-connector"; case 'D': return "sas-connector"; case 'I': return "scsi-ide-converter"; case 'L': return "phb-slot"; case 'P': return "service-processor"; case 'R': return "service-card"; case 'S': return "soft-switch"; case 'V': return "system-vpd"; case 'Y': return "legacy-sysvpd"; } break; case 'T': switch (vpd_name[1]) { case 'D': return "tod-clock"; case 'I': return "torrent-pcie-phb"; case 'L': return "torrent-riser"; case 'M': return "thermal-sensor"; case 'P': return "tpmd-adapter"; case 'R': return "torrent-bridge"; } break; case 'V': switch (vpd_name[1]) { case 'V': return "root-node-vpd"; } break; case 'W': switch (vpd_name[1]) { case 'D': return "water_device"; } break; } prlog(PR_WARNING, "VPD: Could not map FRU ID %s to a known name\n", vpd_name); return "Unknown"; } static bool valid_child_entry(const struct slca_entry *entry) { if ((entry->install_indic == SLCA_INSTALL_INSTALLED) && (entry->vpd_collected == SLCA_VPD_COLLECTED)) return true; return false; } static void vpd_add_children(struct dt_node *parent, uint16_t slca_index) { const struct slca_entry *s_entry, *child; uint16_t current_child_index, max_index; s_entry = slca_get_entry(slca_index); if (!s_entry || (s_entry->nr_child == 0)) return; /* * This slca_entry has children. Parse the children array * and add nodes for valid entries. * * A child entry is valid if all of the following criteria is met * a. SLCA_INSTALL_INSTALLED is set in s_entry->install_indic * b. SLCA_VPD_COLLECTED is set in s_entry->vpd_collected * c. The SLCA is not a duplicate entry. */ /* current_index tracks where we are right now in the array */ current_child_index = be16_to_cpu(s_entry->child_index); /* max_index tracks how far down the array we must traverse */ max_index = be16_to_cpu(s_entry->child_index) + be16_to_cpu(s_entry->nr_child); while (current_child_index < max_index) { child = slca_get_entry(current_child_index); if (!child) return; if (valid_child_entry(child)) { struct dt_node *node; node = dt_create_vpd_node(parent, child); if (!node) return; } /* Skip dups -- currently we presume dups are contiguous */ if (child->nr_dups > 0) current_child_index += child->nr_dups; current_child_index++; } return; } /* Create the vpd node and add its children */ static struct dt_node *dt_create_vpd_node(struct dt_node *parent, const struct slca_entry *entry) { struct dt_node *node; const char *name; uint64_t addr; name = vpd_map_name(entry->fru_id); addr = (uint64_t)be16_to_cpu(entry->rsrc_id); node = dt_new_addr(parent, name, addr); if (!node) { prerror("VPD: Creating node at %s@%"PRIx64" failed\n", name, addr); return NULL; } /* Add location code */ slca_vpd_add_loc_code(node, be16_to_cpu(entry->my_index)); /* Add FRU label */ dt_add_property(node, "fru-type", entry->fru_id, 2); /* Recursively add children */ vpd_add_children(node, be16_to_cpu(entry->my_index)); return node; } struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr, int indx_fru, int indx_vpd) { const struct spira_fru_id *fru_id; unsigned int fruvpd_sz, fru_id_sz; const struct slca_entry *entry; struct dt_node *dt_vpd, *node; static bool first = true; const void *fruvpd; const char *name; uint64_t addr; char *lname; int len; fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz); if (!fru_id) return NULL; fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz); if (!CHECK_SPPTR(fruvpd)) return NULL; dt_vpd = dt_find_by_path(dt_root, "/vpd"); if (!dt_vpd) return NULL; if (first) { entry = slca_get_entry(SLCA_ROOT_INDEX); if (!entry) { prerror("VPD: Could not find the slca root entry\n"); return NULL; } node = dt_create_vpd_node(dt_vpd, entry); if (!node) return NULL; first = false; } entry = slca_get_entry(be16_to_cpu(fru_id->slca_index)); if (!entry) return NULL; name = vpd_map_name(entry->fru_id); addr = (uint64_t)be16_to_cpu(entry->rsrc_id); len = strlen(name) + STR_MAX_CHARS(addr) + 2; lname = zalloc(len); if (!lname) { prerror("VPD: Failed to allocate memory\n"); return NULL; } snprintf(lname, len, "%s@%llx", name, (long long)addr); /* Get the node already created */ node = dt_find_by_name(dt_vpd, lname); free(lname); /* * It is unlikely that node not found because vpd nodes have the * corresponding slca entry which we would have used to populate the vpd * tree during the 'first' pass above so that we just need to perform * VINI parse and add the vpd data.. * Still, we consider this case and create fresh node under '/vpd' if * 'node' not found. */ if (!node) { node = dt_create_vpd_node(dt_vpd, entry); if (!node) return NULL; } /* Parse VPD fields, ensure that it has not been added already */ if (!dt_find_property(node, "ibm,vpd")) { dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz); vpd_vini_parse(node, fruvpd, fruvpd_sz); } return node; } static void sysvpd_parse(void) { const char *model; const char *system_id; const char *brand; char *str; uint8_t sz; const void *sysvpd; unsigned int sysvpd_sz; unsigned int fru_id_sz; struct dt_node *dt_vpd; const struct spira_fru_id *fru_id; struct HDIF_common_hdr *sysvpd_hdr; const struct machine_info *mi; sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG); if (!sysvpd_hdr) goto no_sysvpd; fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz); if (!fru_id) goto no_sysvpd;; sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz); if (!CHECK_SPPTR(sysvpd)) goto no_sysvpd; /* Add system VPD */ dt_vpd = dt_find_by_path(dt_root, "/vpd"); if (dt_vpd) { dt_add_property(dt_vpd, "ibm,vpd", sysvpd, sysvpd_sz); slca_vpd_add_loc_code(dt_vpd, be16_to_cpu(fru_id->slca_index)); } model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz); if (!model) goto no_sysvpd; str = zalloc(sz + 1); if (!str) goto no_sysvpd; memcpy(str, model, sz); dt_add_property_string(dt_root, "model", str); mi = machine_info_lookup(str); if (mi) { dt_add_property_string(dt_root, "model-name", mi->name); } else { dt_add_property_string(dt_root, "model-name", "Unknown"); prlog(PR_WARNING, "VPD: Model name %s not known\n", str); } free(str); dt_add_property_string(dt_root, "vendor", "IBM"); system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz); if (!system_id) goto no_sysid; str = zalloc(sz + 1); if (!str) goto no_sysid; memcpy(str, system_id, sz); dt_add_property_string(dt_root, "system-id", str); free(str); brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz); if (!brand) goto no_brand; str = zalloc(sz + 1); if (!str) goto no_brand; memcpy(str, brand, sz); dt_add_property_string(dt_root, "system-brand", str); free(str); return; no_brand: dt_add_property_string(dt_root, "system-brand", "Unknown"); return; no_sysid: dt_add_property_string(dt_root, "system-id", "Unknown"); return; no_sysvpd: dt_add_property_string(dt_root, "model", "Unknown"); } static void iokid_vpd_parse(const struct HDIF_common_hdr *iohub_hdr) { const struct HDIF_child_ptr *iokids; const struct HDIF_common_hdr *iokid; unsigned int i; iokids = HDIF_child_arr(iohub_hdr, CECHUB_CHILD_IO_KIDS); if (!CHECK_SPPTR(iokids)) { prerror("VPD: No IOKID child array\n"); return; } for (i = 0; i < be32_to_cpu(iokids->count); i++) { iokid = HDIF_child(iohub_hdr, iokids, i, IOKID_FRU_HDIF_SIG); /* IO KID VPD structure layout is similar to FRUVPD */ if (iokid) dt_add_vpd_node(iokid, FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD); } } static void iohub_vpd_parse(void) { const struct HDIF_common_hdr *iohub_hdr; const struct cechub_hub_fru_id *fru_id_data; unsigned int i, vpd_sz, fru_id_sz; if (!get_hdif(&spira.ntuples.cec_iohub_fru, CECHUB_FRU_HDIF_SIG)) { prerror("VPD: Could not find IO HUB FRU data\n"); return; } for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, iohub_hdr, i, CECHUB_FRU_HDIF_SIG) { fru_id_data = HDIF_get_idata(iohub_hdr, CECHUB_FRU_ID_DATA_AREA, &fru_id_sz); /* P8, IO HUB is on processor card and we have a * daughter card array */ if (fru_id_data && be32_to_cpu(fru_id_data->card_type) == CECHUB_FRU_TYPE_CPU_CARD) { iokid_vpd_parse(iohub_hdr); continue; } /* On P7, the keyword VPD will not be NULL */ if (HDIF_get_idata(iohub_hdr, CECHUB_ASCII_KEYWORD_VPD, &vpd_sz)) dt_add_vpd_node(iohub_hdr, CECHUB_FRU_ID_DATA, CECHUB_ASCII_KEYWORD_VPD); } } static void _vpd_parse(struct spira_ntuple tuple) { const struct HDIF_common_hdr *fruvpd_hdr; unsigned int i; if (!get_hdif(&tuple, FRUVPD_HDIF_SIG)) return; for_each_ntuple_idx(&tuple, fruvpd_hdr, i, FRUVPD_HDIF_SIG) dt_add_vpd_node(fruvpd_hdr, FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD); } void vpd_parse(void) { const struct HDIF_common_hdr *fruvpd_hdr; /* Enclosure */ _vpd_parse(spira.ntuples.nt_enclosure_vpd); /* Backplane */ _vpd_parse(spira.ntuples.backplane_vpd); /* System VPD uses the VSYS record, so its special */ sysvpd_parse(); /* clock card -- does this use the FRUVPD sig? */ _vpd_parse(spira.ntuples.clock_vpd); /* Anchor card */ _vpd_parse(spira.ntuples.anchor_vpd); /* Op panel -- does this use the FRUVPD sig? */ _vpd_parse(spira.ntuples.op_panel_vpd); /* External cache FRU vpd -- does this use the FRUVPD sig? */ _vpd_parse(spira.ntuples.ext_cache_fru_vpd); /* Misc CEC FRU */ _vpd_parse(spira.ntuples.misc_cec_fru_vpd); /* CEC IO HUB FRU */ iohub_vpd_parse(); /* * SP subsystem -- while the rest of the SPINFO structure is * different, the FRU ID data and pointer pair to keyword VPD * are the same offset as a FRUVPD entry. So reuse it */ fruvpd_hdr = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG); if (fruvpd_hdr) dt_add_vpd_node(fruvpd_hdr, FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD); } skiboot-skiboot-5.1.13/hw/000077500000000000000000000000001265204436200153455ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hw/Makefile.inc000066400000000000000000000010711265204436200175540ustar00rootroot00000000000000# -*-Makefile-*- SUBDIRS += hw HW_OBJS = xscom.o chiptod.o gx.o cec.o lpc.o lpc-uart.o psi.o HW_OBJS += homer.o slw.o occ.o fsi-master.o centaur.o HW_OBJS += nx.o nx-rng.o nx-crypto.o nx-842.o HW_OBJS += p7ioc.o p7ioc-inits.o p7ioc-phb.o p5ioc2.o p5ioc2-phb.o HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o prd.o HW_OBJS += dts.o lpc-rtc.o HW=hw/built-in.o include $(SRC)/hw/fsp/Makefile.inc include $(SRC)/hw/ec/Makefile.inc include $(SRC)/hw/ast-bmc/Makefile.inc include $(SRC)/hw/ipmi/Makefile.inc $(HW): $(HW_OBJS:%=hw/%) $(FSP) $(EC) $(AST_BMC) $(IPMI) skiboot-skiboot-5.1.13/hw/ast-bmc/000077500000000000000000000000001265204436200166735ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hw/ast-bmc/Makefile.inc000066400000000000000000000002111265204436200210750ustar00rootroot00000000000000SUBDIRS += hw/ast-bmc AST_BMC_OBJS = ast-io.o ast-sf-ctrl.o AST_BMC = hw/ast-bmc/built-in.o $(AST_BMC): $(AST_BMC_OBJS:%=hw/ast-bmc/%) skiboot-skiboot-5.1.13/hw/ast-bmc/ast-io.c000066400000000000000000000263521265204436200202430ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Note about accesses to the AST2400 internal memory map: * * There are two ways to genrate accesses to the AHB bus of the AST2400 * from the host. The LPC->AHB bridge and the iLPC->AHB bridge. * * LPC->AHB bridge * --------------- * * This bridge directly converts memory or firmware accesses using * a set of registers for establishing a remapping window. We prefer * using FW space as normal memory space is limited to byte accesses * to a fixed 256M window, while FW space allows us to use different * access sizes and to control the IDSEL bits which essentially enable * a full 4G address space. * * The way FW accesses map onto AHB is controlled via two registers * in the BMC's LPC host controller: * * HICR7 at 0x1e789088 [31:16] : ADRBASE * [15:00] : HWMBASE * * HICR8 at 0x1e78908c [31:16] : ADRMASK * [15:00] : HWNCARE * * All decoding/remapping happens on the top 16 bits of the LPC address * named LPC_ADDR as follow: * * - For decoding, LPC_ADDR bits are compared with HWMBASE if the * corresponding bit in HWNCARE is 0. * * - For remapping, the AHB address is constructed by taking bits * from LPC_ADDR if the corresponding bit in ADRMASK is 0 or in * ADRBASE if the corresponding bit in ADRMASK is 1 * * Example of 2MB SPI flash, LPC 0xFCE00000~0xFCFFFFFF onto * AHB 0x30000000~0x301FFFFF (SPI flash) * * ADRBASE=0x3000 HWMBASE=0xFCE0 * ADRMASK=0xFFE0 HWNCARE=0x001F * * This comes pre-configured by the BMC or HostBoot to access the PNOR * flash from IDSEL 0 as follow: * * ADRBASE=0x3000 HWMBASE=0x0e00 for 32MB * ADRMASK=0xfe00 HWNCARE=0x01ff * * Which means mapping of LPC 0x0e000000..0x0fffffff onto * AHB 0x30000000..0x31ffffff * * iLPC->AHB bridge * --------------- * * This bridge is hosted in the SuperIO part of the BMC and is * controlled by a series of byte-sized registers accessed indirectly * via IO ports 0x2e and 0x2f. * * Via these, byte by byte, we can construct an AHB address and * fill a data buffer to trigger a write cycle, or we can do a * read cycle and read back the data, byte after byte. * * This is fairly convoluted and slow but works regardless of what * mapping was established in the LPC->AHB bridge. * * For the time being, we use the iLPC->AHB for everything except * pnor accesses. In the long run, we will reconfigure the LPC->AHB * to provide more direct access to all of the BMC address space but * we'll only do that after the boot script/program on the BMC is * updated to restore the bridge to a state compatible with the SBE * expectations on boot. */ #include #include #include #include "ast.h" #define BMC_SIO_SCR28 0x28 #define BOOT_FLAGS_VERSION 0x42 #define BMC_SIO_SCR29 0x29 #define BMC_SIO_SCR29_MEMBOOT 0x10 enum { BMC_SIO_DEV_NONE = -1, BMC_SIO_DEV_UART1 = 2, BMC_SIO_DEV_UART2 = 3, BMC_SIO_DEV_SWC = 4, BMC_SIO_DEV_KBC = 5, BMC_SIO_DEV_P80 = 7, BMC_SIO_DEV_UART3 = 0xb, BMC_SIO_DEV_UART4 = 0xc, BMC_SIO_DEV_LPC2AHB = 0xd, BMC_SIO_DEV_MBOX = 0xe, }; static struct lock bmc_sio_lock = LOCK_UNLOCKED; static int bmc_sio_cur_dev = BMC_SIO_DEV_NONE; /* * SuperIO indirect accesses */ static void bmc_sio_outb(uint8_t val, uint8_t reg) { lpc_outb(reg, 0x2e); lpc_outb(val, 0x2f); } static uint8_t bmc_sio_inb(uint8_t reg) { lpc_outb(reg, 0x2e); return lpc_inb(0x2f); } static void bmc_sio_get(int dev) { lock(&bmc_sio_lock); if (bmc_sio_cur_dev == dev || dev < 0) return; if (bmc_sio_cur_dev == BMC_SIO_DEV_NONE) { /* Send SuperIO password */ lpc_outb(0xa5, 0x2e); lpc_outb(0xa5, 0x2e); } /* Select logical dev */ bmc_sio_outb(dev, 0x07); bmc_sio_cur_dev = dev; } static void bmc_sio_put(bool lock_sio) { if (lock_sio) { /* Re-lock SuperIO */ lpc_outb(0xaa, 0x2e); bmc_sio_cur_dev = BMC_SIO_DEV_NONE; } unlock(&bmc_sio_lock); } /* * AHB accesses via iLPC->AHB in SuperIO. Works on byteswapped * values (ie. Little Endian registers) */ static void bmc_sio_ahb_prep(uint32_t reg, uint8_t type) { /* Enable iLPC->AHB */ bmc_sio_outb(0x01, 0x30); /* Address */ bmc_sio_outb((reg >> 24) & 0xff, 0xf0); bmc_sio_outb((reg >> 16) & 0xff, 0xf1); bmc_sio_outb((reg >> 8) & 0xff, 0xf2); bmc_sio_outb((reg ) & 0xff, 0xf3); /* bytes cycle type */ bmc_sio_outb(type, 0xf8); } static void bmc_sio_ahb_writel(uint32_t val, uint32_t reg) { bmc_sio_get(BMC_SIO_DEV_LPC2AHB); bmc_sio_ahb_prep(reg, 2); /* Write data */ bmc_sio_outb(val >> 24, 0xf4); bmc_sio_outb(val >> 16, 0xf5); bmc_sio_outb(val >> 8, 0xf6); bmc_sio_outb(val , 0xf7); /* Trigger */ bmc_sio_outb(0xcf, 0xfe); bmc_sio_put(false); } static uint32_t bmc_sio_ahb_readl(uint32_t reg) { uint32_t val = 0; bmc_sio_get(BMC_SIO_DEV_LPC2AHB); bmc_sio_ahb_prep(reg, 2); /* Trigger */ bmc_sio_inb(0xfe); /* Read results */ val = (val << 8) | bmc_sio_inb(0xf4); val = (val << 8) | bmc_sio_inb(0xf5); val = (val << 8) | bmc_sio_inb(0xf6); val = (val << 8) | bmc_sio_inb(0xf7); bmc_sio_put(false); return val; } /* * External API * * We only support 4-byte accesses to all of AHB. We additionally * support 1-byte accesses to the flash area only. * * We could support all access sizes via iLPC but we don't need * that for now. */ #define PNOR_AHB_ADDR 0x30000000 static uint32_t pnor_lpc_offset; void ast_ahb_writel(uint32_t val, uint32_t reg) { /* For now, always use iLPC->AHB, it will byteswap */ bmc_sio_ahb_writel(val, reg); } uint32_t ast_ahb_readl(uint32_t reg) { /* For now, always use iLPC->AHB, it will byteswap */ return bmc_sio_ahb_readl(reg); } int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len) { /* Check we don't cross IDSEL segments */ if ((reg ^ (reg + len - 1)) >> 28) return -EINVAL; /* SPI flash, use LPC->AHB bridge */ if ((reg >> 28) == (PNOR_AHB_ADDR >> 28)) { uint32_t chunk, off = reg - PNOR_AHB_ADDR + pnor_lpc_offset; int64_t rc; while(len) { /* Chose access size */ if (len > 3 && !(off & 3)) { rc = lpc_write(OPAL_LPC_FW, off, *(uint32_t *)src, 4); chunk = 4; } else { rc = lpc_write(OPAL_LPC_FW, off, *(uint8_t *)src, 1); chunk = 1; } if (rc) { prerror("AST_IO: lpc_write.sb failure %lld" " to FW 0x%08x\n", rc, off); return rc; } len -= chunk; off += chunk; src += chunk; } return 0; } /* Otherwise we don't do byte access (... yet) */ prerror("AST_IO: Attempted write bytes access to %08x\n", reg); return -EINVAL; } int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len) { /* Check we don't cross IDSEL segments */ if ((reg ^ (reg + len - 1)) >> 28) return -EINVAL; /* SPI flash, use LPC->AHB bridge */ if ((reg >> 28) == (PNOR_AHB_ADDR >> 28)) { uint32_t chunk, off = reg - PNOR_AHB_ADDR + pnor_lpc_offset; int64_t rc; while(len) { uint32_t dat; /* Chose access size */ if (len > 3 && !(off & 3)) { rc = lpc_read(OPAL_LPC_FW, off, &dat, 4); if (!rc) *(uint32_t *)dst = dat; chunk = 4; } else { rc = lpc_read(OPAL_LPC_FW, off, &dat, 1); if (!rc) *(uint8_t *)dst = dat; chunk = 1; } if (rc) { prerror("AST_IO: lpc_read.sb failure %lld" " to FW 0x%08x\n", rc, off); return rc; } len -= chunk; off += chunk; dst += chunk; } return 0; } /* Otherwise we don't do byte access (... yet) */ prerror("AST_IO: Attempted read bytes access to %08x\n", reg); return -EINVAL; } static void ast_setup_sio_irq_polarity(void) { /* Select logical dev 2 */ bmc_sio_get(BMC_SIO_DEV_UART1); bmc_sio_outb(0x01, 0x71); /* level low */ bmc_sio_put(false); /* Select logical dev 3 */ bmc_sio_get(BMC_SIO_DEV_UART2); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev 4 */ bmc_sio_get(BMC_SIO_DEV_SWC); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev 5 */ bmc_sio_get(BMC_SIO_DEV_KBC); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_outb(0x01, 0x73); /* irq level low */ bmc_sio_put(false); /* Select logical dev 7 */ bmc_sio_get(BMC_SIO_DEV_P80); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev d */ bmc_sio_get(BMC_SIO_DEV_UART3); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev c */ bmc_sio_get(BMC_SIO_DEV_UART4); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev d */ bmc_sio_get(BMC_SIO_DEV_LPC2AHB); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev e */ bmc_sio_get(BMC_SIO_DEV_MBOX); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(true); } void ast_io_init(void) { uint32_t hicr7; /* Read the configuration of the LPC->AHB bridge for PNOR * to extract the PNOR LPC offset which can be different * depending on flash size */ hicr7 = bmc_sio_ahb_readl(LPC_HICR7); pnor_lpc_offset = (hicr7 & 0xffffu) << 16; prlog(PR_DEBUG, "AST: PNOR LPC offset: 0x%08x\n", pnor_lpc_offset); /* Configure all AIO interrupts to level low */ ast_setup_sio_irq_polarity(); } bool ast_is_ahb_lpc_pnor(void) { uint8_t boot_version; uint8_t boot_flags; boot_version = bmc_sio_inb(BMC_SIO_SCR28); if (boot_version != BOOT_FLAGS_VERSION) return true; boot_flags = bmc_sio_inb(BMC_SIO_SCR29); return !(boot_flags & BMC_SIO_SCR29_MEMBOOT); } void ast_setup_ibt(uint16_t io_base, uint8_t irq) { uint32_t v; v = bmc_sio_ahb_readl(LPC_iBTCR0); v = v & ~(0xfffffc00u); v = v | (((uint32_t)io_base) << 16); v = v | (((uint32_t)irq) << 12); bmc_sio_ahb_writel(v, LPC_iBTCR0); } bool ast_is_vuart1_enabled(void) { uint32_t v; v = bmc_sio_ahb_readl(VUART1_GCTRLA); return !!(v & 1); } void ast_setup_vuart1(uint16_t io_base, uint8_t irq) { uint32_t v; /* IRQ level low */ v = bmc_sio_ahb_readl(VUART1_GCTRLA); v = v & ~2u; bmc_sio_ahb_writel(v, VUART1_GCTRLA); v = bmc_sio_ahb_readl(VUART1_GCTRLA); /* IRQ number */ v = bmc_sio_ahb_readl(VUART1_GCTRLB); v = (v & ~0xf0u) | (irq << 4); bmc_sio_ahb_writel(v, VUART1_GCTRLB); /* Address */ bmc_sio_ahb_writel(io_base & 0xff, VUART1_ADDRL); bmc_sio_ahb_writel(io_base >> 8, VUART1_ADDRH); } /* Setup SuperIO UART 1 */ void ast_setup_sio_uart1(uint16_t io_base, uint8_t irq) { bmc_sio_get(BMC_SIO_DEV_UART1); /* Disable UART1 for configuration */ bmc_sio_outb(0x00, 0x30); /* Configure base and interrupt */ bmc_sio_outb(io_base >> 8, 0x60); bmc_sio_outb(io_base & 0xff, 0x61); bmc_sio_outb(irq, 0x70); bmc_sio_outb(0x01, 0x71); /* level low */ /* Enable UART1 */ bmc_sio_outb(0x01, 0x30); bmc_sio_put(true); } void ast_disable_sio_uart1(void) { bmc_sio_get(BMC_SIO_DEV_UART1); /* Disable UART1 */ bmc_sio_outb(0x00, 0x30); bmc_sio_put(true); } skiboot-skiboot-5.1.13/hw/ast-bmc/ast-sf-ctrl.c000066400000000000000000000560721265204436200212100ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "ast.h" #ifndef __unused #define __unused __attribute__((unused)) #endif #define CALIBRATE_BUF_SIZE 16384 struct ast_sf_ctrl { /* We have 2 controllers, one for the BMC flash, one for the PNOR */ uint8_t type; /* Address and previous value of the ctrl register */ uint32_t ctl_reg; /* Control register value for normal commands */ uint32_t ctl_val; /* Control register value for (fast) reads */ uint32_t ctl_read_val; /* Flash read timing register */ uint32_t fread_timing_reg; uint32_t fread_timing_val; /* Address of the flash mapping */ uint32_t flash; /* Current 4b mode */ bool mode_4b; /* Callbacks */ struct spi_flash_ctrl ops; }; static uint32_t ast_ahb_freq; static const uint32_t ast_ct_hclk_divs[] = { 0xf, /* HCLK */ 0x7, /* HCLK/2 */ 0xe, /* HCLK/3 */ 0x6, /* HCLK/4 */ 0xd, /* HCLK/5 */ }; static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd) { /* Switch to user mode, CE# dropped */ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg); /* user mode, CE# active */ ast_ahb_writel(ct->ctl_val | 3, ct->ctl_reg); /* write cmd */ return ast_copy_to_ahb(ct->flash, &cmd, 1); } static void ast_sf_end_cmd(struct ast_sf_ctrl *ct) { /* clear CE# */ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg); /* Switch back to read mode */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); } static int ast_sf_send_addr(struct ast_sf_ctrl *ct, uint32_t addr) { const void *ap; /* Layout address MSB first in memory */ addr = cpu_to_be32(addr); /* Send the right amount of bytes */ ap = (char *)&addr; if (ct->mode_4b) return ast_copy_to_ahb(ct->flash, ap, 4); else return ast_copy_to_ahb(ct->flash, ap + 1, 3); } static int ast_sf_cmd_rd(struct spi_flash_ctrl *ctrl, uint8_t cmd, bool has_addr, uint32_t addr, void *buffer, uint32_t size) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); int rc; rc = ast_sf_start_cmd(ct, cmd); if (rc) goto bail; if (has_addr) { rc = ast_sf_send_addr(ct, addr); if (rc) goto bail; } if (buffer && size) rc = ast_copy_from_ahb(buffer, ct->flash, size); bail: ast_sf_end_cmd(ct); return rc; } static int ast_sf_cmd_wr(struct spi_flash_ctrl *ctrl, uint8_t cmd, bool has_addr, uint32_t addr, const void *buffer, uint32_t size) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); int rc; rc = ast_sf_start_cmd(ct, cmd); if (rc) goto bail; if (has_addr) { rc = ast_sf_send_addr(ct, addr); if (rc) goto bail; } if (buffer && size) rc = ast_copy_to_ahb(ct->flash, buffer, size); bail: ast_sf_end_cmd(ct); return rc; } static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); if (ct->type != AST_SF_TYPE_PNOR) return enable ? FLASH_ERR_4B_NOT_SUPPORTED : 0; /* * We update the "old" value as well since when quitting * we don't restore the mode of the flash itself so we need * to leave the controller in a compatible setup */ if (enable) { ct->ctl_val |= 0x2000; ct->ctl_read_val |= 0x2000; } else { ct->ctl_val &= ~0x2000; ct->ctl_read_val &= ~0x2000; } ct->mode_4b = enable; /* Update read mode */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); return 0; } static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos, void *buf, uint32_t len) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); /* * We are in read mode by default. We don't yet support fancy * things like fast read or X2 mode */ return ast_copy_from_ahb(buf, ct->flash + pos, len); } static void ast_get_ahb_freq(void) { static const uint32_t cpu_freqs_24_48[] = { 384000000, 360000000, 336000000, 408000000 }; static const uint32_t cpu_freqs_25[] = { 400000000, 375000000, 350000000, 425000000 }; static const uint32_t ahb_div[] = { 1, 2, 4, 3 }; uint32_t strap, cpu_clk, div; if (ast_ahb_freq) return; /* HW strapping gives us the CPU freq and AHB divisor */ strap = ast_ahb_readl(SCU_HW_STRAPPING); if (strap & 0x00800000) { FL_DBG("AST: CLKIN 25Mhz\n"); cpu_clk = cpu_freqs_25[(strap >> 8) & 3]; } else { FL_DBG("AST: CLKIN 24/48Mhz\n"); cpu_clk = cpu_freqs_24_48[(strap >> 8) & 3]; } FL_DBG("AST: CPU frequency: %d Mhz\n", cpu_clk / 1000000); div = ahb_div[(strap >> 10) & 3]; ast_ahb_freq = cpu_clk / div; FL_DBG("AST: AHB frequency: %d Mhz\n", ast_ahb_freq / 1000000); } static int ast_sf_check_reads(struct ast_sf_ctrl *ct, const uint8_t *golden_buf, uint8_t *test_buf) { int i, rc; for (i = 0; i < 10; i++) { rc = ast_copy_from_ahb(test_buf, ct->flash, CALIBRATE_BUF_SIZE); if (rc) return rc; if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0) return FLASH_ERR_VERIFY_FAILURE; } return 0; } static int ast_sf_calibrate_reads(struct ast_sf_ctrl *ct, uint32_t hdiv, const uint8_t *golden_buf, uint8_t *test_buf) { int i, rc; int good_pass = -1, pass_count = 0; uint32_t shift = (hdiv - 1) << 2; uint32_t mask = ~(0xfu << shift); #define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8)) /* Try HCLK delay 0..5, each one with/without delay and look for a * good pair. */ for (i = 0; i < 12; i++) { bool pass; ct->fread_timing_val &= mask; ct->fread_timing_val |= FREAD_TPASS(i) << shift; ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); rc = ast_sf_check_reads(ct, golden_buf, test_buf); if (rc && rc != FLASH_ERR_VERIFY_FAILURE) return rc; pass = (rc == 0); FL_DBG(" * [%08x] %d HCLK delay, %dns DI delay : %s\n", ct->fread_timing_val, i/2, (i & 1) ? 0 : 4, pass ? "PASS" : "FAIL"); if (pass) { pass_count++; if (pass_count == 3) { good_pass = i - 1; break; } } else pass_count = 0; } /* No good setting for this frequency */ if (good_pass < 0) return FLASH_ERR_VERIFY_FAILURE; /* We have at least one pass of margin, let's use first pass */ ct->fread_timing_val &= mask; ct->fread_timing_val |= FREAD_TPASS(good_pass) << shift; ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); FL_DBG("AST: * -> good is pass %d [0x%08x]\n", good_pass, ct->fread_timing_val); return 0; } static bool ast_calib_data_usable(const uint8_t *test_buf, uint32_t size) { const uint32_t *tb32 = (const uint32_t *)test_buf; uint32_t i, cnt = 0; /* We check if we have enough words that are neither all 0 * nor all 1's so the calibration can be considered valid. * * I use an arbitrary threshold for now of 64 */ size >>= 2; for (i = 0; i < size; i++) { if (tb32[i] != 0 && tb32[i] != 0xffffffff) cnt++; } return cnt >= 64; } static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct, struct flash_info *info __unused, uint32_t max_freq) { uint8_t *golden_buf, *test_buf; int i, rc, best_div = -1; uint32_t save_read_val = ct->ctl_read_val; test_buf = malloc(CALIBRATE_BUF_SIZE * 2); golden_buf = test_buf + CALIBRATE_BUF_SIZE; /* We start with the dumbest setting and read some data */ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) | (0x00 << 28) | /* Single bit */ (0x00 << 24) | /* CE# max */ (0x03 << 16) | /* use normal reads */ (0x00 << 8) | /* HCLK/16 */ (0x00 << 6) | /* no dummy cycle */ (0x00); /* normal read */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); rc = ast_copy_from_ahb(golden_buf, ct->flash, CALIBRATE_BUF_SIZE); if (rc) { free(test_buf); return rc; } /* Establish our read mode with freq field set to 0 */ ct->ctl_read_val = save_read_val & 0xfffff0ff; /* Check if calibration data is suitable */ if (!ast_calib_data_usable(golden_buf, CALIBRATE_BUF_SIZE)) { FL_INF("AST: Calibration area too uniform, " "using low speed\n"); ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); free(test_buf); return 0; } /* Now we iterate the HCLK dividers until we find our breaking point */ for (i = 5; i > 0; i--) { uint32_t tv, freq; /* Compare timing to max */ freq = ast_ahb_freq / i; if (freq >= max_freq) continue; /* Set the timing */ tv = ct->ctl_read_val | (ast_ct_hclk_divs[i - 1] << 8); ast_ahb_writel(tv, ct->ctl_reg); FL_DBG("AST: Trying HCLK/%d...\n", i); rc = ast_sf_calibrate_reads(ct, i, golden_buf, test_buf); /* Some other error occurred, bail out */ if (rc && rc != FLASH_ERR_VERIFY_FAILURE) { free(test_buf); return rc; } if (rc == 0) best_div = i; } free(test_buf); /* Nothing found ? */ if (best_div < 0) FL_ERR("AST: No good frequency, using dumb slow\n"); else { FL_DBG("AST: Found good read timings at HCLK/%d\n", best_div); ct->ctl_read_val |= (ast_ct_hclk_divs[best_div - 1] << 8); } ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); return 0; } static int ast_sf_get_hclk(uint32_t *ctl_val, uint32_t max_freq) { int i; /* It appears that running commands at HCLK/2 on some micron * chips results in occasionally reads of bogus status (that * or unrelated chip hangs). * * Since we cannot calibrate properly the reads for commands, * instead, let's limit our SPI frequency to HCLK/4 to stay * on the safe side of things */ #define MIN_CMD_FREQ 4 for (i = MIN_CMD_FREQ; i <= 5; i++) { uint32_t freq = ast_ahb_freq / i; if (freq >= max_freq) continue; *ctl_val |= (ast_ct_hclk_divs[i - 1] << 8); return i; } return 0; } static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info) { int rc, div __unused; uint8_t srcr[2]; /* * Those Macronix chips support dual reads at 104Mhz * and dual IO at 84Mhz with 4 dummies. * * Our calibration algo should give us something along * the lines of HCLK/3 (HCLK/2 seems to work sometimes * but appears to be fairly unreliable) which is 64Mhz * * So we chose dual IO mode. * * The CE# inactive width for reads must be 7ns, we set it * to 3T which is about 15ns at the fastest speed we support * HCLK/2) as I've had issue with smaller values. * * For write and program it's 30ns so let's set the value * for normal ops to 6T. * * Preserve the current 4b mode. */ FL_DBG("AST: Setting up Macronix...\n"); /* * Read the status and config registers */ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDSR, false, 0, &srcr[0], 1); if (rc != 0) { FL_ERR("AST: Failed to read status\n"); return rc; } rc = ast_sf_cmd_rd(&ct->ops, CMD_RDCR, false, 0, &srcr[1], 1); if (rc != 0) { FL_ERR("AST: Failed to read configuration\n"); return rc; } FL_DBG("AST: Macronix SR:CR: 0x%02x:%02x\n", srcr[0], srcr[1]); /* Switch to 8 dummy cycles to enable 104Mhz operations */ srcr[1] = (srcr[1] & 0x3f) | 0x80; rc = fl_wren(&ct->ops); if (rc) { FL_ERR("AST: Failed to WREN for Macronix config\n"); return rc; } rc = ast_sf_cmd_wr(&ct->ops, CMD_WRSR, false, 0, srcr, 2); if (rc != 0) { FL_ERR("AST: Failed to write Macronix config\n"); return rc; } rc = fl_sync_wait_idle(&ct->ops);; if (rc != 0) { FL_ERR("AST: Failed waiting for config write\n"); return rc; } FL_DBG("AST: Macronix SR:CR: 0x%02x:%02x\n", srcr[0], srcr[1]); /* Use 2READ */ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) | (0x03 << 28) | /* Dual IO */ (0x0d << 24) | /* CE# width 3T */ (0xbb << 16) | /* 2READ command */ (0x00 << 8) | /* HCLK/16 (optimize later) */ (0x02 << 6) | /* 2 bytes dummy cycle (8 clocks) */ (0x01); /* fast read */ /* Configure SPI flash read timing */ rc = ast_sf_optimize_reads(ct, info, 104000000); if (rc) { FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc); return rc; } /* * For other commands and writes also increase the SPI clock * to HCLK/2 since the chip supports up to 133Mhz and set * CE# inactive to 6T. We request a timing that is 20% below * the limit of the chip, so about 106Mhz which should fit. */ ct->ctl_val = (ct->ctl_val & 0x2000) | (0x00 << 28) | /* Single bit */ (0x0a << 24) | /* CE# width 6T (b1010) */ (0x00 << 16) | /* no command */ (0x00 << 8) | /* HCLK/16 (done later) */ (0x00 << 6) | /* no dummy cycle */ (0x00); /* normal read */ div = ast_sf_get_hclk(&ct->ctl_val, 106000000); FL_DBG("AST: Command timing set to HCLK/%d\n", div); /* Update chip with current read config */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); return 0; } static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info) { int rc, div __unused; FL_DBG("AST: Setting up Windbond...\n"); /* * This Windbond chip support dual reads at 104Mhz * with 8 dummy cycles. * * The CE# inactive width for reads must be 10ns, we set it * to 3T which is about 15.6ns. */ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) | (0x02 << 28) | /* Dual bit data only */ (0x0e << 24) | /* CE# width 2T (b1110) */ (0x3b << 16) | /* DREAD command */ (0x00 << 8) | /* HCLK/16 */ (0x01 << 6) | /* 1-byte dummy cycle */ (0x01); /* fast read */ /* Configure SPI flash read timing */ rc = ast_sf_optimize_reads(ct, info, 104000000); if (rc) { FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc); return rc; } /* * For other commands and writes also increase the SPI clock * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive * for write and erase is 50ns so let's set it to 10T. */ ct->ctl_val = (ct->ctl_read_val & 0x2000) | (0x00 << 28) | /* Single bit */ (0x06 << 24) | /* CE# width 10T (b0110) */ (0x00 << 16) | /* no command */ (0x00 << 8) | /* HCLK/16 */ (0x00 << 6) | /* no dummy cycle */ (0x01); /* fast read */ div = ast_sf_get_hclk(&ct->ctl_val, 106000000); FL_DBG("AST: Command timing set to HCLK/%d\n", div); /* Update chip with current read config */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); return 0; } static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info) { uint8_t vconf, ext_id[6]; int rc, div __unused; FL_DBG("AST: Setting up Micron...\n"); /* * Read the extended chip ID to try to detect old vs. new * flashes since old Micron flashes have a lot of issues */ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDID, false, 0, ext_id, 6); if (rc != 0) { FL_ERR("AST: Failed to read Micron ext ID, sticking to dumb speed\n"); return 0; } /* Check ID matches expectations */ if (ext_id[0] != ((info->id >> 16) & 0xff) || ext_id[1] != ((info->id >> 8) & 0xff) || ext_id[2] != ((info->id ) & 0xff)) { FL_ERR("AST: Micron ext ID mismatch, sticking to dumb speed\n"); return 0; } FL_DBG("AST: Micron ext ID byte: 0x%02x\n", ext_id[4]); /* Check for old (<45nm) chips, don't try to be fancy on those */ if (!(ext_id[4] & 0x40)) { FL_DBG("AST: Old chip, using dumb timings\n"); goto dumb; } /* * Read the micron specific volatile configuration reg */ rc = ast_sf_cmd_rd(&ct->ops, CMD_MIC_RDVCONF, false, 0, &vconf, 1); if (rc != 0) { FL_ERR("AST: Failed to read Micron vconf, sticking to dumb speed\n"); goto dumb; } FL_DBG("AST: Micron VCONF: 0x%02x\n", vconf); /* Switch to 8 dummy cycles (we might be able to operate with 4 * but let's keep some margin */ vconf = (vconf & 0x0f) | 0x80; rc = ast_sf_cmd_wr(&ct->ops, CMD_MIC_WRVCONF, false, 0, &vconf, 1); if (rc != 0) { FL_ERR("AST: Failed to write Micron vconf, " " sticking to dumb speed\n"); goto dumb; } rc = fl_sync_wait_idle(&ct->ops);; if (rc != 0) { FL_ERR("AST: Failed waiting for config write\n"); return rc; } FL_DBG("AST: Updated to : 0x%02x\n", vconf); /* * Try to do full dual IO, with 8 dummy cycles it supports 133Mhz * * The CE# inactive width for reads must be 20ns, we set it * to 4T which is about 20.8ns. */ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) | (0x03 << 28) | /* Single bit */ (0x0c << 24) | /* CE# 4T */ (0xbb << 16) | /* 2READ command */ (0x00 << 8) | /* HCLK/16 (optimize later) */ (0x02 << 6) | /* 8 dummy cycles (2 bytes) */ (0x01); /* fast read */ /* Configure SPI flash read timing */ rc = ast_sf_optimize_reads(ct, info, 133000000); if (rc) { FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc); return rc; } /* * For other commands and writes also increase the SPI clock * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive * for write and erase is 50ns so let's set it to 10T. */ ct->ctl_val = (ct->ctl_read_val & 0x2000) | (0x00 << 28) | /* Single bit */ (0x06 << 24) | /* CE# width 10T (b0110) */ (0x00 << 16) | /* no command */ (0x00 << 8) | /* HCLK/16 */ (0x00 << 6) | /* no dummy cycle */ (0x00); /* norm read */ div = ast_sf_get_hclk(&ct->ctl_val, 133000000); FL_DBG("AST: Command timing set to HCLK/%d\n", div); /* Update chip with current read config */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); return 0; dumb: ct->ctl_val = ct->ctl_read_val = (ct->ctl_read_val & 0x2000) | (0x00 << 28) | /* Single bit */ (0x00 << 24) | /* CE# max */ (0x03 << 16) | /* use normal reads */ (0x06 << 8) | /* HCLK/4 */ (0x00 << 6) | /* no dummy cycle */ (0x00); /* normal read */ /* Update chip with current read config */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); return 0; } static int ast_sf_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); struct flash_info *info = ctrl->finfo; (void)tsize; /* * Configure better timings and read mode for known * flash chips */ switch(info->id) { case 0xc22018: /* MX25L12835F */ case 0xc22019: /* MX25L25635F */ case 0xc2201a: /* MX66L51235F */ return ast_sf_setup_macronix(ct, info); case 0xef4018: /* W25Q128BV */ return ast_sf_setup_winbond(ct, info); case 0x20ba20: /* MT25Qx512xx */ return ast_sf_setup_micron(ct, info); } /* No special tuning */ return 0; } static bool ast_sf_init_pnor(struct ast_sf_ctrl *ct) { uint32_t reg; ct->ctl_reg = PNOR_SPI_FCTL_CTRL; ct->fread_timing_reg = PNOR_SPI_FREAD_TIMING; ct->flash = PNOR_FLASH_BASE; /* Enable writing to the controller */ reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF); if (reg == 0xffffffff) { FL_ERR("AST_SF: Failed read from controller config\n"); return false; } ast_ahb_writel(reg | 1, PNOR_SPI_FCTL_CONF); /* * Snapshot control reg and sanitize it for our * use, switching to 1-bit mode, clearing user * mode if set, etc... * * Also configure SPI clock to something safe * like HCLK/8 (24Mhz) */ ct->ctl_val = ast_ahb_readl(ct->ctl_reg); if (ct->ctl_val == 0xffffffff) { FL_ERR("AST_SF: Failed read from controller control\n"); return false; } ct->ctl_val = (ct->ctl_val & 0x2000) | (0x00 << 28) | /* Single bit */ (0x00 << 24) | /* CE# width 16T */ (0x00 << 16) | /* no command */ (0x04 << 8) | /* HCLK/8 */ (0x00 << 6) | /* no dummy cycle */ (0x00); /* normal read */ /* Initial read mode is default */ ct->ctl_read_val = ct->ctl_val; /* Initial read timings all 0 */ ct->fread_timing_val = 0; /* Configure for read */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); if (ct->ctl_val & 0x2000) ct->mode_4b = true; else ct->mode_4b = false; return true; } static bool ast_sf_init_bmc(struct ast_sf_ctrl *ct) { ct->ctl_reg = BMC_SPI_FCTL_CTRL; ct->fread_timing_reg = BMC_SPI_FREAD_TIMING; ct->flash = BMC_FLASH_BASE; /* * Snapshot control reg and sanitize it for our * use, switching to 1-bit mode, clearing user * mode if set, etc... * * Also configure SPI clock to something safe * like HCLK/8 (24Mhz) */ ct->ctl_val = (0x00 << 28) | /* Single bit */ (0x00 << 24) | /* CE# width 16T */ (0x00 << 16) | /* no command */ (0x04 << 8) | /* HCLK/8 */ (0x00 << 6) | /* no dummy cycle */ (0x00); /* normal read */ /* Initial read mode is default */ ct->ctl_read_val = ct->ctl_val; /* Initial read timings all 0 */ ct->fread_timing_val = 0; /* Configure for read */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); ct->mode_4b = false; return true; } static int ast_mem_set4b(struct spi_flash_ctrl *ctrl __unused, bool enable __unused) { return 0; } static int ast_mem_setup(struct spi_flash_ctrl *ctrl __unused, uint32_t *tsize __unused) { return 0; } static int ast_mem_chipid(struct spi_flash_ctrl *ctrl __unused, uint8_t *id_buf, uint32_t *id_size) { if (*id_size < 3) return -1; id_buf[0] = 0xaa; id_buf[1] = 0x55; id_buf[2] = 0xaa; *id_size = 3; return 0; } static int ast_mem_write(struct spi_flash_ctrl *ctrl, uint32_t pos, const void *buf, uint32_t len) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); /* * This only works when the ahb is pointed at system memory. */ return ast_copy_to_ahb(ct->flash + pos, buf, len); } static int ast_mem_erase(struct spi_flash_ctrl *ctrl, uint32_t addr, uint32_t size) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); uint32_t pos, len, end = addr + size; uint64_t zero = 0; int ret; for (pos = addr; pos < end; pos += sizeof(zero)) { if (pos + sizeof(zero) > end) len = end - pos; else len = sizeof(zero); ret = ast_copy_to_ahb(ct->flash + pos, &zero, len); if (ret) return ret; } return 0; } int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl) { struct ast_sf_ctrl *ct; if (type != AST_SF_TYPE_PNOR && type != AST_SF_TYPE_BMC && type != AST_SF_TYPE_MEM) return -EINVAL; *ctrl = NULL; ct = malloc(sizeof(*ct)); if (!ct) { FL_ERR("AST_SF: Failed to allocate\n"); return -ENOMEM; } memset(ct, 0, sizeof(*ct)); ct->type = type; if (type == AST_SF_TYPE_MEM) { ct->ops.cmd_wr = NULL; ct->ops.cmd_rd = NULL; ct->ops.read = ast_sf_read; ct->ops.set_4b = ast_mem_set4b; ct->ops.write = ast_mem_write; ct->ops.erase = ast_mem_erase; ct->ops.setup = ast_mem_setup; ct->ops.chip_id = ast_mem_chipid; ct->flash = PNOR_FLASH_BASE; } else { ct->ops.cmd_wr = ast_sf_cmd_wr; ct->ops.cmd_rd = ast_sf_cmd_rd; ct->ops.set_4b = ast_sf_set_4b; ct->ops.read = ast_sf_read; ct->ops.setup = ast_sf_setup; } ast_get_ahb_freq(); if (type == AST_SF_TYPE_PNOR) { if (!ast_sf_init_pnor(ct)) goto fail; } else if (type == AST_SF_TYPE_BMC) { if (!ast_sf_init_bmc(ct)) goto fail; } *ctrl = &ct->ops; return 0; fail: free(ct); return -EIO; } void ast_sf_close(struct spi_flash_ctrl *ctrl) { struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); /* Restore control reg to read */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); /* Additional cleanup */ if (ct->type == AST_SF_TYPE_PNOR) { uint32_t reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF); if (reg != 0xffffffff) ast_ahb_writel(reg & ~1, PNOR_SPI_FCTL_CONF); } /* Free the whole lot */ free(ct); } skiboot-skiboot-5.1.13/hw/bt.c000066400000000000000000000317061265204436200161250ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include /* BT registers */ #define BT_CTRL 0 #define BT_CTRL_B_BUSY 0x80 #define BT_CTRL_H_BUSY 0x40 #define BT_CTRL_OEM0 0x20 #define BT_CTRL_SMS_ATN 0x10 #define BT_CTRL_B2H_ATN 0x08 #define BT_CTRL_H2B_ATN 0x04 #define BT_CTRL_CLR_RD_PTR 0x02 #define BT_CTRL_CLR_WR_PTR 0x01 #define BT_HOST2BMC 1 #define BT_INTMASK 2 #define BT_INTMASK_B2H_IRQEN 0x01 #define BT_INTMASK_B2H_IRQ 0x02 #define BT_INTMASK_BMC_HWRST 0x80 /* Default poll interval before interrupts are working */ #define BT_DEFAULT_POLL_MS 200 /* * Minimum size of an IPMI request/response including * mandatory headers. */ #define BT_MIN_REQ_LEN 3 #define BT_MIN_RESP_LEN 4 /* * How long (in uS) to poll for new ipmi data. */ #define POLL_TIMEOUT 10000 /* * Maximum number of outstanding messages to allow in the queue. */ #define BT_MAX_QUEUE_LEN 10 /* * How long (in TB ticks) before a message is timed out. */ #define BT_MSG_TIMEOUT (secs_to_tb(3)) /* * Maximum number of times to attempt sending a message before giving up. */ #define BT_MAX_RETRY_COUNT 1 #define BT_QUEUE_DEBUG 0 #define BT_ERR(msg, fmt, args...) \ do { prerror("BT seq 0x%02x netfn 0x%02x cmd 0x%02x: " fmt "\n", \ (msg)->seq, (msg)->ipmi_msg.netfn, (msg)->ipmi_msg.cmd, ##args); \ } while(0) enum bt_states { BT_STATE_IDLE = 0, BT_STATE_RESP_WAIT, }; struct bt_msg { struct list_node link; unsigned long tb; uint8_t seq; uint8_t retry_count; struct ipmi_msg ipmi_msg; }; struct bt { uint32_t base_addr; enum bt_states state; struct lock lock; struct list_head msgq; struct timer poller; bool irq_ok; int queue_len; }; static struct bt bt; static int ipmi_seq; static inline uint8_t bt_inb(uint32_t reg) { return lpc_inb(bt.base_addr + reg); } static inline void bt_outb(uint8_t data, uint32_t reg) { lpc_outb(data, bt.base_addr + reg); } static inline void bt_set_h_busy(bool value) { uint8_t rval; rval = bt_inb(BT_CTRL); if (value != !!(rval & BT_CTRL_H_BUSY)) bt_outb(BT_CTRL_H_BUSY, BT_CTRL); } static inline bool bt_idle(void) { uint8_t bt_ctrl = bt_inb(BT_CTRL); return !(bt_ctrl & BT_CTRL_B_BUSY) && !(bt_ctrl & BT_CTRL_H2B_ATN); } static inline void bt_set_state(enum bt_states next_state) { bt.state = next_state; } /* Must be called with bt.lock held */ static void bt_msg_del(struct bt_msg *bt_msg) { list_del(&bt_msg->link); bt.queue_len--; unlock(&bt.lock); ipmi_cmd_done(bt_msg->ipmi_msg.cmd, IPMI_NETFN_RETURN_CODE(bt_msg->ipmi_msg.netfn), IPMI_TIMEOUT_ERR, &bt_msg->ipmi_msg); lock(&bt.lock); } static void bt_init_interface(void) { /* Clear interrupt condition & enable irq */ bt_outb(BT_INTMASK_B2H_IRQ | BT_INTMASK_B2H_IRQEN, BT_INTMASK); /* Take care of a stable H_BUSY if any */ bt_set_h_busy(false); bt_set_state(BT_STATE_IDLE); } static void bt_reset_interface(void) { bt_outb(BT_INTMASK_BMC_HWRST, BT_INTMASK); bt_init_interface(); } /* Try and send a message from the message queue. Caller must hold * bt.bt_lock and bt.lock and ensue the message queue is not * empty. */ static void bt_send_msg(void) { int i; struct bt_msg *bt_msg; struct ipmi_msg *ipmi_msg; bt_msg = list_top(&bt.msgq, struct bt_msg, link); assert(bt_msg); ipmi_msg = &bt_msg->ipmi_msg; /* Send the message */ bt_outb(BT_CTRL_CLR_WR_PTR, BT_CTRL); /* Byte 1 - Length */ bt_outb(ipmi_msg->req_size + BT_MIN_REQ_LEN, BT_HOST2BMC); /* Byte 2 - NetFn/LUN */ bt_outb(ipmi_msg->netfn, BT_HOST2BMC); /* Byte 3 - Seq */ bt_outb(bt_msg->seq, BT_HOST2BMC); /* Byte 4 - Cmd */ bt_outb(ipmi_msg->cmd, BT_HOST2BMC); /* Byte 5:N - Data */ for (i = 0; i < ipmi_msg->req_size; i++) bt_outb(ipmi_msg->data[i], BT_HOST2BMC); bt_msg->tb = mftb(); bt_outb(BT_CTRL_H2B_ATN, BT_CTRL); bt_set_state(BT_STATE_RESP_WAIT); return; } static void bt_flush_msg(void) { bt_outb(BT_CTRL_B2H_ATN | BT_CTRL_CLR_RD_PTR, BT_CTRL); bt_set_h_busy(false); } static void bt_get_resp(void) { int i; struct bt_msg *tmp_bt_msg, *bt_msg = NULL; struct ipmi_msg *ipmi_msg; uint8_t resp_len, netfn, seq, cmd; uint8_t cc = IPMI_CC_NO_ERROR; /* Indicate to the BMC that we are busy */ bt_set_h_busy(true); /* Clear B2H_ATN and read pointer */ bt_outb(BT_CTRL_B2H_ATN, BT_CTRL); bt_outb(BT_CTRL_CLR_RD_PTR, BT_CTRL); /* Read the response */ /* Byte 1 - Length (includes header size) */ resp_len = bt_inb(BT_HOST2BMC) - BT_MIN_RESP_LEN; /* Byte 2 - NetFn/LUN */ netfn = bt_inb(BT_HOST2BMC); /* Byte 3 - Seq */ seq = bt_inb(BT_HOST2BMC); /* Byte 4 - Cmd */ cmd = bt_inb(BT_HOST2BMC); /* Byte 5 - Completion Code */ cc = bt_inb(BT_HOST2BMC); /* Find the corresponding message */ list_for_each(&bt.msgq, tmp_bt_msg, link) { if (tmp_bt_msg->seq == seq) { bt_msg = tmp_bt_msg; break; } } if (!bt_msg) { /* A response to a message we no longer care about. */ prlog(PR_INFO, "BT: Nobody cared about a response to an BT/IPMI message\n"); bt_flush_msg(); bt_set_state(BT_STATE_IDLE); return; } ipmi_msg = &bt_msg->ipmi_msg; /* * Make sure we have enough room to store the response. As all values * are unsigned we will also trigger this error if * bt_inb(BT_HOST2BMC) < BT_MIN_RESP_LEN (which should never occur). */ if (resp_len > ipmi_msg->resp_size) { BT_ERR(bt_msg, "Invalid resp_len %d", resp_len); resp_len = ipmi_msg->resp_size; cc = IPMI_ERR_MSG_TRUNCATED; } ipmi_msg->resp_size = resp_len; /* Byte 6:N - Data */ for (i = 0; i < resp_len; i++) ipmi_msg->data[i] = bt_inb(BT_HOST2BMC); bt_set_h_busy(false); bt_set_state(BT_STATE_IDLE); list_del(&bt_msg->link); bt.queue_len--; unlock(&bt.lock); /* * Call the IPMI layer to finish processing the message. */ #if BT_QUEUE_DEBUG prlog(PR_DEBUG, "cmd 0x%02x done\n", seq); #endif ipmi_cmd_done(cmd, netfn, cc, ipmi_msg); lock(&bt.lock); return; } static void bt_expire_old_msg(uint64_t tb) { struct bt_msg *bt_msg; bt_msg = list_top(&bt.msgq, struct bt_msg, link); if (bt_msg && bt_msg->tb > 0 && (bt_msg->tb + BT_MSG_TIMEOUT) < tb) { if (bt_msg->retry_count < BT_MAX_RETRY_COUNT) { /* A message timeout is usually due to the BMC clearing the H2B_ATN flag without actually doing anything. The data will still be in the FIFO so just reset the flag.*/ BT_ERR(bt_msg, "Retry sending message"); bt_msg->retry_count++; bt_msg->tb = tb; bt_outb(BT_CTRL_H2B_ATN, BT_CTRL); } else { BT_ERR(bt_msg, "Timeout sending message"); bt_msg_del(bt_msg); /* Timing out a message is inherently racy as the BMC may start writing just as we decide to kill the message. Hopefully resetting the interface is sufficient to guard against such things. */ bt_reset_interface(); } } } #if BT_QUEUE_DEBUG static void print_debug_queue_info(void) { struct bt_msg *msg; static bool printed = false; if (!list_empty(&bt.msgq)) { printed = false; prlog(PR_DEBUG, "-------- BT Msg Queue --------\n"); list_for_each(&bt.msgq, msg, link) { prlog(PR_DEBUG, "Seq: 0x%02x Cmd: 0x%02x\n", msg->seq, msg->ipmi_msg.cmd); } prlog(PR_DEBUG, "-----------------------------\n"); } else if (!printed) { printed = true; prlog(PR_DEBUG, "----- BT Msg Queue Empty -----\n"); } } #else static void print_debug_queue_info(void) {} #endif static void bt_send_and_unlock(void) { if (lpc_ok() && bt_idle() && !list_empty(&bt.msgq) && bt.state == BT_STATE_IDLE) bt_send_msg(); unlock(&bt.lock); return; } static void bt_poll(struct timer *t __unused, void *data __unused, uint64_t now) { uint8_t bt_ctrl; /* Don't do anything if the LPC bus is offline */ if (!lpc_ok()) return; /* If we can't get the lock assume someone else will notice * the new message and process it. */ lock(&bt.lock); print_debug_queue_info(); bt_ctrl = bt_inb(BT_CTRL); /* Is there a response waiting for us? */ if (bt.state == BT_STATE_RESP_WAIT && (bt_ctrl & BT_CTRL_B2H_ATN)) bt_get_resp(); bt_expire_old_msg(now); /* Check for sms_atn */ if (bt_inb(BT_CTRL) & BT_CTRL_SMS_ATN) { bt_outb(BT_CTRL_SMS_ATN, BT_CTRL); unlock(&bt.lock); ipmi_sms_attention(); lock(&bt.lock); } /* Send messages if we can. If the BMC was really quick we could loop back to the start and check for a response instead of unlocking, but testing shows the BMC isn't that fast so we will wait for the IRQ or a call to the pollers instead. */ bt_send_and_unlock(); schedule_timer(&bt.poller, bt.irq_ok ? TIMER_POLL : msecs_to_tb(BT_DEFAULT_POLL_MS)); } static void bt_add_msg(struct bt_msg *bt_msg) { bt_msg->tb = 0; bt_msg->seq = ipmi_seq++; bt_msg->retry_count = 0; bt.queue_len++; if (bt.queue_len > BT_MAX_QUEUE_LEN) { /* Maximum queue length exceeded - remove the oldest message from the queue. */ BT_ERR(bt_msg, "Maximum queue length exceeded"); bt_msg = list_tail(&bt.msgq, struct bt_msg, link); assert(bt_msg); BT_ERR(bt_msg, "Removed from queue"); bt_msg_del(bt_msg); } } static int bt_add_ipmi_msg_head(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); lock(&bt.lock); bt_add_msg(bt_msg); list_add(&bt.msgq, &bt_msg->link); bt_send_and_unlock(); return 0; } static int bt_add_ipmi_msg(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); lock(&bt.lock); bt_add_msg(bt_msg); list_add_tail(&bt.msgq, &bt_msg->link); bt_send_and_unlock(); return 0; } static void bt_irq(uint32_t chip_id __unused, uint32_t irq_mask __unused) { uint8_t ireg; ireg = bt_inb(BT_INTMASK); bt.irq_ok = true; if (ireg & BT_INTMASK_B2H_IRQ) { bt_outb(BT_INTMASK_B2H_IRQ | BT_INTMASK_B2H_IRQEN, BT_INTMASK); bt_poll(NULL, NULL, mftb()); } } /* * Allocate an ipmi message and bt container and return the ipmi * message struct. Allocates enough space for the request and response * data. */ static struct ipmi_msg *bt_alloc_ipmi_msg(size_t request_size, size_t response_size) { struct bt_msg *bt_msg; bt_msg = zalloc(sizeof(struct bt_msg) + MAX(request_size, response_size)); if (!bt_msg) return NULL; bt_msg->ipmi_msg.req_size = request_size; bt_msg->ipmi_msg.resp_size = response_size; bt_msg->ipmi_msg.data = (uint8_t *) (bt_msg + 1); return &bt_msg->ipmi_msg; } /* * Free a previously allocated ipmi message. */ static void bt_free_ipmi_msg(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); free(bt_msg); } /* * Remove a message from the queue. The memory allocated for the ipmi message * will need to be freed by the caller with bt_free_ipmi_msg() as it will no * longer be in the queue of messages. */ static int bt_del_ipmi_msg(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); lock(&bt.lock); list_del_from(&bt.msgq, &bt_msg->link); bt.queue_len--; bt_send_and_unlock(); return 0; } static struct ipmi_backend bt_backend = { .alloc_msg = bt_alloc_ipmi_msg, .free_msg = bt_free_ipmi_msg, .queue_msg = bt_add_ipmi_msg, .queue_msg_head = bt_add_ipmi_msg_head, .dequeue_msg = bt_del_ipmi_msg, }; static struct lpc_client bt_lpc_client = { .interrupt = bt_irq, }; void bt_init(void) { struct dt_node *n; const struct dt_property *prop; uint32_t irq; /* We support only one */ n = dt_find_compatible_node(dt_root, NULL, "ipmi-bt"); if (!n) return; /* Get IO base */ prop = dt_find_property(n, "reg"); if (!prop) { prerror("BT: Can't find reg property\n"); return; } if (dt_property_get_cell(prop, 0) != OPAL_LPC_IO) { prerror("BT: Only supports IO addresses\n"); return; } bt.base_addr = dt_property_get_cell(prop, 1); init_timer(&bt.poller, bt_poll, NULL); bt_init_interface(); init_lock(&bt.lock); /* * The iBT interface comes up in the busy state until the daemon has * initialised it. */ bt_set_state(BT_STATE_IDLE); list_head_init(&bt.msgq); bt.queue_len = 0; printf("BT: Interface initialized, IO 0x%04x\n", bt.base_addr); ipmi_register_backend(&bt_backend); /* We initially schedule the poller as a relatively fast timer, at * least until we have at least one interrupt occurring at which * point we turn it into a background poller */ schedule_timer(&bt.poller, msecs_to_tb(BT_DEFAULT_POLL_MS)); irq = dt_prop_get_u32(n, "interrupts"); bt_lpc_client.interrupts = LPC_IRQ(irq); lpc_register_client(dt_get_chip_id(n), &bt_lpc_client); prlog(PR_DEBUG, "BT: Using LPC IRQ %d\n", irq); } skiboot-skiboot-5.1.13/hw/cec.c000066400000000000000000000040271265204436200162460ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* * Note: This file os only used on P7/P7+ */ #define MAX_IO_HUBS 0x80 static struct io_hub *cec_iohubs[MAX_IO_HUBS]; struct io_hub *cec_get_hub_by_id(uint32_t hub_id) { if (hub_id >= MAX_IO_HUBS) return NULL; return cec_iohubs[hub_id]; } void cec_register(struct io_hub *hub) { cec_iohubs[hub->hub_id] = hub; } void cec_reset(void) { unsigned int i; /* Reset IO Hubs */ for (i = 0; i < MAX_IO_HUBS; i++) { if (!cec_iohubs[i] || !cec_iohubs[i]->ops->reset) continue; cec_iohubs[i]->ops->reset(cec_iohubs[i]); } } static int64_t opal_pci_set_hub_tce_memory(uint64_t hub_id, uint64_t tce_mem_addr, uint64_t tce_mem_size) { struct io_hub *hub = cec_get_hub_by_id(hub_id); if (!hub) return OPAL_PARAMETER; if (!hub->ops->set_tce_mem) return OPAL_UNSUPPORTED; return hub->ops->set_tce_mem(hub, tce_mem_addr, tce_mem_size); } opal_call(OPAL_PCI_SET_HUB_TCE_MEMORY, opal_pci_set_hub_tce_memory, 3); static int64_t opal_pci_get_hub_diag_data(uint64_t hub_id, void *diag_buffer, uint64_t diag_buffer_len) { struct io_hub *hub = cec_get_hub_by_id(hub_id); if (!hub) return OPAL_PARAMETER; if (!hub->ops->get_diag_data) return OPAL_UNSUPPORTED; return hub->ops->get_diag_data(hub, diag_buffer, diag_buffer_len); } opal_call(OPAL_PCI_GET_HUB_DIAG_DATA, opal_pci_get_hub_diag_data, 3); skiboot-skiboot-5.1.13/hw/centaur.c000066400000000000000000000325761265204436200171670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include /* * Centaur chip IDs are using the XSCOM "partID" encoding * described in xscom.h. recap: * * 0b1000.0000.0000.0000.0000.00NN.NCCC.MMMM * N=Node, C=Chip, M=Memory Channel * * We currently use FSI exclusively for centaur access. We can * start using MMIO on Centaur DD2.x when we have a way to handle * machine checks happening inside Sapphire which we don't at the * moment. */ /* Is that correct ? */ #define MAX_CENTAURS_PER_CHIP 8 /* * FSI2PIB register definitions (this could be moved out if we were to * support FSI master to other chips. */ #define FSI_DATA0_REG 0x1000 #define FSI_DATA1_REG 0x1004 #define FSI_CMD_REG 0x1008 #define FSI_CMD_WR 0x80000000 #define FSI_CMD_RD 0x00000000 #define FSI_ENG_RESET_REG 0x1018 #define FSI_STATUS_REG 0x101c #define FSI_STATUS_ABORT 0x00100000 #define FSI_STATUS_ERRORS 0x00007000 /* Some Centaur XSCOMs we care about */ #define SCAC_CONFIG_REG 0x020115ce #define SCAC_CONFIG_SET 0x020115cf #define SCAC_CONFIG_CLR 0x020115d0 #define SCAC_ENABLE_MSK PPC_BIT(0) #define cent_log(__lev, __c, __fmt, ...) \ prlog(__lev, "CENTAUR %x: " __fmt, __c->part_id, ##__VA_ARGS__) static int64_t centaur_fsiscom_complete(struct centaur_chip *centaur) { int64_t rc; uint32_t stat; rc = mfsi_read(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_STATUS_REG, &stat); if (rc) { cent_log(PR_ERR, centaur, "MFSI read error %lld reading STAT\n", rc); return rc; } if ((stat & (FSI_STATUS_ABORT | FSI_STATUS_ERRORS)) == 0) return OPAL_SUCCESS; cent_log(PR_ERR, centaur, "Remote FSI SCOM error, status=0x%08x\n", stat); /* All 1's ? Assume it's gone */ if (stat == 0xffffffffu) { cent_log(PR_ERR, centaur, "Chip appears to be dead !\n"); centaur->valid = false; /* Here, hostboot grabs a pile of FFDC from the FSI layer, * we could do that too ... */ return OPAL_HARDWARE; } /* Here HB prints the GPx registers which I believe are only * in the host (FSI master). We skip that for now, we don't have * a good API to them */ /* Recovery sequence from HostBoot fsiscom.C * if SCOM fails and FSI Master displays "MasterTimeOut" * then 7,6 * else if SCOM fails and FSI2PIB Status shows PIB abort * then just perform unit reset (6) and wait 1 ms * else (PIB_abort='0' but PIB error is unequal 0) * then just perform unit reset (6) (wait not needed). * * Note: Waiting 1ms inside OPAL is a BIG NO NO !!! We have * no choice but doing it at the moment but that will have * to be fixed one way or another, possibly by returning some * kind of busy status until the delay is expired. */ rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_ENG_RESET_REG, 0); if (rc) { cent_log(PR_ERR, centaur, "MFSI write error %lld resetting SCOM engine\n", rc); } return OPAL_HARDWARE; } static int64_t centaur_fsiscom_read(struct centaur_chip *centaur, uint32_t pcb_addr, uint64_t *val) { int64_t rc; uint32_t data0, data1; rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_CMD_REG, pcb_addr | FSI_CMD_RD); if (rc) { cent_log(PR_ERR, centaur, "MFSI write error %lld writing CMD\n", rc); return rc; } rc = centaur_fsiscom_complete(centaur); if (rc) return rc; rc = mfsi_read(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_DATA0_REG, &data0); if (rc) { cent_log(PR_ERR, centaur, "MFSI read error %lld reading DATA0\n", rc); return rc; } rc = mfsi_read(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_DATA1_REG, &data1); if (rc) { cent_log(PR_ERR, centaur, "MFSI read error %lld readking DATA1\n", rc); return rc; } *val = (((uint64_t)data0) << 32) | data1; return OPAL_SUCCESS; } static int64_t centaur_fsiscom_write(struct centaur_chip *centaur, uint32_t pcb_addr, uint64_t val) { int64_t rc; rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_DATA0_REG, hi32(val)); if (rc) { cent_log(PR_ERR, centaur, "MFSI write error %lld writing DATA0\n", rc); return rc; } rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_DATA1_REG, lo32(val)); if (rc) { cent_log(PR_ERR, centaur, "MFSI write error %lld writing DATA1\n", rc); return rc; } rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, centaur->fsi_master_port, FSI_CMD_REG, pcb_addr | FSI_CMD_WR); if (rc) { cent_log(PR_ERR, centaur, "MFSI write error %lld writing CMD\n", rc); return rc; } return centaur_fsiscom_complete(centaur); } struct centaur_chip *get_centaur(uint32_t part_id) { uint32_t hchip_id, mchan; struct proc_chip *hchip; struct centaur_chip *centaur; if ((part_id >> 28) != 8) { prerror("CENTAUR: Invalid part ID 0x%x\n", part_id); return NULL; } hchip_id = (part_id & 0x0fffffff) >> 4; mchan = part_id & 0xf; hchip = get_chip(hchip_id); if (!hchip) { prerror("CENTAUR: Centaur 0x%x not found on non-existing chip 0%x\n", part_id, hchip_id); return NULL; } if (mchan >= MAX_CENTAURS_PER_CHIP) { prerror("CENTAUR: Centaur 0x%x channel out of bounds !\n", part_id); return NULL; } if (!hchip->centaurs) { prerror("CENTAUR: Centaur 0x%x not found on chip 0%x (no centaurs)\n", part_id, hchip_id); return NULL; } centaur = &hchip->centaurs[mchan]; if (!centaur->valid) { prerror("CENTAUR: Centaur 0x%x not valid on chip 0%x\n", part_id, hchip_id); return NULL; } return centaur; } /* * Indirect XSCOM access functions. Copied from xscom.c, at a * latter date, we should merge these properly. */ static void centaur_xscom_handle_ind_error(struct centaur_chip *centaur, uint64_t data, uint64_t pcb_addr, bool is_write) { unsigned int stat = GETFIELD(XSCOM_DATA_IND_ERR, data); bool timeout = !(data & XSCOM_DATA_IND_COMPLETE); /* XXX: Create error log entry ? */ if (timeout) cent_log(PR_ERR, centaur, "inddirect %s timeout, pcb_addr=0x%llx stat=0x%x\n", is_write ? "write" : "read", pcb_addr, stat); else cent_log(PR_ERR, centaur, "indirect %s error, pcb_addr=0x%llx stat=0x%x\n", is_write ? "write" : "read", pcb_addr, stat); } static int centaur_xscom_ind_read(struct centaur_chip *centaur, uint64_t pcb_addr, uint64_t *val) { uint32_t addr; uint64_t data; int rc, retries; /* Write indirect address */ addr = pcb_addr & 0x7fffffff; data = XSCOM_DATA_IND_READ | (pcb_addr & XSCOM_ADDR_IND_ADDR); rc = centaur_fsiscom_write(centaur, addr, data); if (rc) goto bail; /* Wait for completion */ for (retries = 0; retries < XSCOM_IND_MAX_RETRIES; retries++) { rc = centaur_fsiscom_read(centaur, addr, &data); if (rc) goto bail; if ((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) { *val = data & XSCOM_DATA_IND_DATA; break; } if ((data & XSCOM_DATA_IND_COMPLETE) || (retries >= XSCOM_IND_MAX_RETRIES)) { centaur_xscom_handle_ind_error(centaur, data, pcb_addr, false); rc = OPAL_HARDWARE; goto bail; } } bail: if (rc) *val = (uint64_t)-1; return rc; } static int centaur_xscom_ind_write(struct centaur_chip *centaur, uint64_t pcb_addr, uint64_t val) { uint32_t addr; uint64_t data; int rc, retries; /* Write indirect address & data */ addr = pcb_addr & 0x7fffffff; data = pcb_addr & XSCOM_ADDR_IND_ADDR; data |= val & XSCOM_ADDR_IND_DATA; rc = centaur_fsiscom_write(centaur, addr, data); if (rc) goto bail; /* Wait for completion */ for (retries = 0; retries < XSCOM_IND_MAX_RETRIES; retries++) { rc = centaur_fsiscom_read(centaur, addr, &data); if (rc) goto bail; if ((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) break; if ((data & XSCOM_DATA_IND_COMPLETE) || (retries >= XSCOM_IND_MAX_RETRIES)) { centaur_xscom_handle_ind_error(centaur, data, pcb_addr, true); rc = OPAL_HARDWARE; goto bail; } } bail: return rc; } int64_t centaur_xscom_read(uint32_t id, uint64_t pcb_addr, uint64_t *val) { struct centaur_chip *centaur = get_centaur(id); int64_t rc; if (!centaur) return OPAL_PARAMETER; lock(¢aur->lock); if (pcb_addr & XSCOM_ADDR_IND_FLAG) rc = centaur_xscom_ind_read(centaur, pcb_addr, val); else rc = centaur_fsiscom_read(centaur, pcb_addr, val); unlock(¢aur->lock); return rc; } int64_t centaur_xscom_write(uint32_t id, uint64_t pcb_addr, uint64_t val) { struct centaur_chip *centaur = get_centaur(id); int64_t rc; if (!centaur) return OPAL_PARAMETER; lock(¢aur->lock); if (pcb_addr & XSCOM_ADDR_IND_FLAG) rc = centaur_xscom_ind_write(centaur, pcb_addr, val); else rc = centaur_fsiscom_write(centaur, pcb_addr, val); unlock(¢aur->lock); return rc; } static bool centaur_check_id(struct centaur_chip *centaur) { int64_t rc; uint64_t val; rc = centaur_fsiscom_read(centaur, 0xf000f, &val); if (rc) { cent_log(PR_ERR, centaur, " FSISCOM error %lld reading ID register\n", rc); return false; } /* Extract CFAM id */ val >>= 44; /* Identify chip */ if ((val & 0xff) != 0xe9) { cent_log(PR_ERR, centaur, " CFAM ID 0x%02x is not a Centaur !\n", (unsigned int)(val & 0xff)); return false; } /* Get EC level from CFAM ID */ centaur->ec_level = ((val >> 16) & 0xf) << 4; centaur->ec_level |= (val >> 8) & 0xf; return true; } static bool centaur_add(uint32_t part_id, uint32_t mchip, uint32_t meng, uint32_t mport) { uint32_t hchip_id, mchan; struct proc_chip *hchip; struct centaur_chip *centaur; if ((part_id >> 28) != 8) { prerror("CENTAUR: Invalid part ID 0x%x\n", part_id); return false; } hchip_id = (part_id & 0x0fffffff) >> 4; mchan = part_id & 0xf; printf("CENTAUR: Found centaur for chip 0x%x channel %d\n", hchip_id, mchan); printf("CENTAUR: FSI host: 0x%x cMFSI%d port %d\n", mchip, meng, mport); hchip = get_chip(hchip_id); if (!hchip) { prerror("CENTAUR: No such chip !!!\n"); return false; } if (mchan >= MAX_CENTAURS_PER_CHIP) { prerror("CENTAUR: Channel out of bounds !\n"); return false; } if (!hchip->centaurs) { hchip->centaurs = zalloc(sizeof(struct centaur_chip) * MAX_CENTAURS_PER_CHIP); assert(hchip->centaurs); } centaur = &hchip->centaurs[mchan]; if (centaur->valid) { prerror("CENTAUR: Duplicate centaur !\n"); return false; } centaur->part_id = part_id; centaur->fsi_master_chip_id = mchip; centaur->fsi_master_port = mport; centaur->fsi_master_engine = meng ? MFSI_cMFSI1 : MFSI_cMFSI0; init_lock(¢aur->lock); if (!centaur_check_id(centaur)) return false; cent_log(PR_INFO, centaur, "Found DD%x.%x chip\n", centaur->ec_level >> 4, centaur->ec_level & 0xf); centaur->valid = true; return true; } /* Returns how long to wait for logic to stop in TB ticks or a negative * value on error */ int64_t centaur_disable_sensor_cache(uint32_t part_id) { struct centaur_chip *centaur = get_centaur(part_id); int64_t rc = 0; uint64_t ctrl; if (!centaur) return false; lock(¢aur->lock); centaur->scache_disable_count++; if (centaur->scache_disable_count == 1) { centaur->scache_was_enabled = false; rc = centaur_fsiscom_read(centaur, SCAC_CONFIG_REG, &ctrl); if (rc) goto bail; centaur->scache_was_enabled = !!(ctrl & SCAC_ENABLE_MSK); rc = centaur_fsiscom_write(centaur, SCAC_CONFIG_CLR, SCAC_ENABLE_MSK); if (rc) goto bail; rc = msecs_to_tb(30); } bail: unlock(¢aur->lock); return rc; } int64_t centaur_enable_sensor_cache(uint32_t part_id) { struct centaur_chip *centaur = get_centaur(part_id); int64_t rc = 0; if (!centaur) return false; lock(¢aur->lock); if (centaur->scache_disable_count == 0) { cent_log(PR_ERR, centaur, "Cache count going negative !\n"); backtrace(); goto bail; } centaur->scache_disable_count--; if (centaur->scache_disable_count == 0 && centaur->scache_was_enabled) rc = centaur_fsiscom_write(centaur, SCAC_CONFIG_SET, SCAC_ENABLE_MSK); bail: unlock(¢aur->lock); return rc; } void centaur_init(void) { struct dt_node *cn; dt_for_each_compatible(dt_root, cn, "ibm,centaur") { uint32_t chip_id, mchip, meng, mport; chip_id = dt_prop_get_u32(cn, "ibm,chip-id"); mchip = dt_prop_get_u32(cn, "ibm,fsi-master-chip-id"); meng = dt_prop_get_cell(cn, "ibm,fsi-master-port", 0); mport = dt_prop_get_cell(cn, "ibm,fsi-master-port", 1); /* * If adding the centaur succeeds, we expose it to * Linux as a scom-controller */ if (centaur_add(chip_id, mchip, meng, mport)) dt_add_property(cn, "scom-controller", NULL, 0); } } skiboot-skiboot-5.1.13/hw/chiptod.c000066400000000000000000001463071265204436200171560ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Handle ChipTOD chip & configure core and CAPP timebases */ #include #include #include #include #include #include #include #include #include /* TOD chip XSCOM addresses */ #define TOD_MASTER_PATH_CTRL 0x00040000 /* Master Path ctrl reg */ #define TOD_PRI_PORT0_CTRL 0x00040001 /* Primary port0 ctrl reg */ #define TOD_PRI_PORT1_CTRL 0x00040002 /* Primary port1 ctrl reg */ #define TOD_SEC_PORT0_CTRL 0x00040003 /* Secondary p0 ctrl reg */ #define TOD_SEC_PORT1_CTRL 0x00040004 /* Secondary p1 ctrl reg */ #define TOD_SLAVE_PATH_CTRL 0x00040005 /* Slave Path ctrl reg */ #define TOD_INTERNAL_PATH_CTRL 0x00040006 /* Internal Path ctrl reg */ /* -- TOD primary/secondary master/slave control register -- */ #define TOD_PSMS_CTRL 0x00040007 #define TOD_PSMSC_PM_TOD_SELECT PPC_BIT(1) /* Primary Master TOD */ #define TOD_PSMSC_PM_DRAW_SELECT PPC_BIT(2) /* Primary Master Drawer */ #define TOD_PSMSC_SM_TOD_SELECT PPC_BIT(9) /* Secondary Master TOD */ #define TOD_PSMSC_SM_DRAW_SELECT PPC_BIT(10) /* Secondary Master Draw */ /* -- TOD primary/secondary master/slave status register -- */ #define TOD_STATUS 0x00040008 #define TOD_ST_TOPOLOGY_SELECT PPC_BITMASK(0, 2) #define TOD_ST_MPATH0_STEP_VALID PPC_BIT(6) /* MasterPath0 step valid */ #define TOD_ST_MPATH1_STEP_VALID PPC_BIT(7) /* MasterPath1 step valid */ #define TOD_ST_SPATH0_STEP_VALID PPC_BIT(8) /* SlavePath0 step valid */ #define TOD_ST_SPATH1_STEP_VALID PPC_BIT(10) /* SlavePath1 step valid */ /* Primary master/slave path select (0 = PATH_0, 1 = PATH_1) */ #define TOD_ST_PRI_MPATH_SELECT PPC_BIT(12) /* Primary MPath Select */ #define TOD_ST_PRI_SPATH_SELECT PPC_BIT(15) /* Primary SPath Select */ /* Secondary master/slave path select (0 = PATH_0, 1 = PATH_1) */ #define TOD_ST_SEC_MPATH_SELECT PPC_BIT(16) /* Secondary MPath Select */ #define TOD_ST_SEC_SPATH_SELECT PPC_BIT(19) /* Secondary SPath Select */ #define TOD_ST_ACTIVE_MASTER PPC_BIT(23) #define TOD_ST_BACKUP_MASTER PPC_BIT(24) /* TOD chip XSCOM addresses */ #define TOD_CHIP_CTRL 0x00040010 /* Chip control register */ #define TOD_TTYPE_0 0x00040011 #define TOD_TTYPE_1 0x00040012 /* PSS switch */ #define TOD_TTYPE_2 0x00040013 /* Enable step checkers */ #define TOD_TTYPE_3 0x00040014 /* Request TOD */ #define TOD_TTYPE_4 0x00040015 /* Send TOD */ #define TOD_TTYPE_5 0x00040016 /* Invalidate TOD */ #define TOD_CHIPTOD_TO_TB 0x00040017 #define TOD_LOAD_TOD_MOD 0x00040018 #define TOD_CHIPTOD_VALUE 0x00040020 #define TOD_CHIPTOD_LOAD_TB 0x00040021 #define TOD_CHIPTOD_FSM 0x00040024 /* -- TOD PIB Master reg -- */ #define TOD_PIB_MASTER 0x00040027 #define TOD_PIBM_ADDR_CFG_MCAST PPC_BIT(25) #define TOD_PIBM_ADDR_CFG_SLADDR PPC_BITMASK(26,31) #define TOD_PIBM_TTYPE4_SEND_MODE PPC_BIT(32) #define TOD_PIBM_TTYPE4_SEND_ENBL PPC_BIT(33) /* -- TOD Error interrupt register -- */ #define TOD_ERROR 0x00040030 /* SYNC errors */ #define TOD_ERR_CRMO_PARITY PPC_BIT(0) #define TOD_ERR_OSC0_PARITY PPC_BIT(1) #define TOD_ERR_OSC1_PARITY PPC_BIT(2) #define TOD_ERR_PPORT0_CREG_PARITY PPC_BIT(3) #define TOD_ERR_PPORT1_CREG_PARITY PPC_BIT(4) #define TOD_ERR_SPORT0_CREG_PARITY PPC_BIT(5) #define TOD_ERR_SPORT1_CREG_PARITY PPC_BIT(6) #define TOD_ERR_SPATH_CREG_PARITY PPC_BIT(7) #define TOD_ERR_IPATH_CREG_PARITY PPC_BIT(8) #define TOD_ERR_PSMS_CREG_PARITY PPC_BIT(9) #define TOD_ERR_CRITC_PARITY PPC_BIT(13) #define TOD_ERR_MP0_STEP_CHECK PPC_BIT(14) #define TOD_ERR_MP1_STEP_CHECK PPC_BIT(15) #define TOD_ERR_PSS_HAMMING_DISTANCE PPC_BIT(18) #define TOD_ERR_DELAY_COMPL_PARITY PPC_BIT(22) /* CNTR errors */ #define TOD_ERR_CTCR_PARITY PPC_BIT(32) #define TOD_ERR_TOD_SYNC_CHECK PPC_BIT(33) #define TOD_ERR_TOD_FSM_PARITY PPC_BIT(34) #define TOD_ERR_TOD_REGISTER_PARITY PPC_BIT(35) #define TOD_ERR_OVERFLOW_YR2042 PPC_BIT(36) #define TOD_ERR_TOD_WOF_LSTEP_PARITY PPC_BIT(37) #define TOD_ERR_TTYPE0_RECVD PPC_BIT(38) #define TOD_ERR_TTYPE1_RECVD PPC_BIT(39) #define TOD_ERR_TTYPE2_RECVD PPC_BIT(40) #define TOD_ERR_TTYPE3_RECVD PPC_BIT(41) #define TOD_ERR_TTYPE4_RECVD PPC_BIT(42) #define TOD_ERR_TTYPE5_RECVD PPC_BIT(43) /* -- TOD Error interrupt register -- */ #define TOD_ERROR_INJECT 0x00040031 /* Local FIR EH.TPCHIP.TPC.LOCAL_FIR */ #define LOCAL_CORE_FIR 0x0104000C #define LFIR_SWITCH_COMPLETE PPC_BIT(18) /* Magic TB value. One step cycle ahead of sync */ #define INIT_TB 0x000000000001ff0 /* Number of iterations for the various timeouts */ #define TIMEOUT_LOOPS 20000000 /* TOD active Primary/secondary configuration */ #define TOD_PRI_CONF_IN_USE 0 /* Tod using primary topology*/ #define TOD_SEC_CONF_IN_USE 7 /* Tod using secondary topo */ /* Timebase State Machine error state */ #define TBST_STATE_ERROR 9 static enum chiptod_type { chiptod_unknown, chiptod_p7, chiptod_p8 } chiptod_type; enum chiptod_chip_role { chiptod_chip_role_UNKNOWN = -1, chiptod_chip_role_MDMT = 0, /* Master Drawer Master TOD */ chiptod_chip_role_MDST, /* Master Drawer Slave TOD */ chiptod_chip_role_SDMT, /* Slave Drawer Master TOD */ chiptod_chip_role_SDST, /* Slave Drawer Slave TOD */ }; enum chiptod_chip_status { chiptod_active_master = 0, /* Chip TOD is Active master */ chiptod_backup_master = 1, /* Chip TOD is backup master */ chiptod_backup_disabled, /* Chip TOD is backup but disabled */ }; struct chiptod_chip_config_info { int32_t id; /* chip id */ enum chiptod_chip_role role; /* Chip role */ enum chiptod_chip_status status; /* active/backup/disabled */ }; static int32_t chiptod_primary = -1; static int32_t chiptod_secondary = -1; static enum chiptod_topology current_topology = chiptod_topo_unknown; /* * chiptod_topology_info holds primary/secondary chip configuration info. * This info is initialized during chiptod_init(). This is an array of two: * [0] = [chiptod_topo_primary] = Primary topology config info * [1] = [chiptod_topo_secondary] = Secondary topology config info */ static struct chiptod_chip_config_info chiptod_topology_info[2]; /* * Array of TOD control registers that holds last known valid values. * * Cache chiptod control register values at following instances: * 1. Chiptod initialization * 2. After topology switch is complete. * 3. Upon receiving enable/disable topology request from FSP. * * Cache following chip TOD control registers: * - Master Path control register (0x00040000) * - Primary Port-0 control register (0x00040001) * - Primary Port-1 control register (0x00040002) * - Secondary Port-0 control register (0x00040003) * - Secondary Port-1 control register (0x00040004) * - Slave Path control register (0x00040005) * - Internal Path control register (0x00040006) * - Primary/secondary master/slave control register (0x00040007) * - Chip control register (0x00040010) * * This data is used for restoring respective TOD registers to sane values * whenever parity errors are reported on these registers (through HMI). * The error_bit maps to corresponding bit from TOD error register that * reports parity error on respective TOD registers. */ static struct chiptod_tod_regs { /* error bit from TOD Error reg */ const uint64_t error_bit; /* xscom address of TOD register to be restored. */ const uint64_t xscom_addr; /* per chip cached value of TOD control registers to be restored. */ struct { uint64_t data; bool valid; } val[MAX_CHIPS]; } chiptod_tod_regs[] = { { TOD_ERR_CRMO_PARITY, TOD_MASTER_PATH_CTRL, { } }, { TOD_ERR_PPORT0_CREG_PARITY, TOD_PRI_PORT0_CTRL, { } }, { TOD_ERR_PPORT1_CREG_PARITY, TOD_PRI_PORT1_CTRL, { } }, { TOD_ERR_SPORT0_CREG_PARITY, TOD_SEC_PORT0_CTRL, { } }, { TOD_ERR_SPORT1_CREG_PARITY, TOD_SEC_PORT1_CTRL, { } }, { TOD_ERR_SPATH_CREG_PARITY, TOD_SLAVE_PATH_CTRL, { } }, { TOD_ERR_IPATH_CREG_PARITY, TOD_INTERNAL_PATH_CTRL, { } }, { TOD_ERR_PSMS_CREG_PARITY, TOD_PSMS_CTRL, { } }, { TOD_ERR_CTCR_PARITY, TOD_CHIP_CTRL, { } }, }; /* The base TFMR value is the same for the whole machine * for now as far as I can tell */ static uint64_t base_tfmr; /* * For now, we use a global lock for runtime chiptod operations, * eventually make this a per-core lock for wakeup rsync and * take all of them for RAS cases. */ static struct lock chiptod_lock = LOCK_UNLOCKED; static void _chiptod_cache_tod_regs(int32_t chip_id) { int i; for (i = 0; i < ARRAY_SIZE(chiptod_tod_regs); i++) { if (xscom_read(chip_id, chiptod_tod_regs[i].xscom_addr, &(chiptod_tod_regs[i].val[chip_id].data)) != 0) { prerror("CHIPTOD: XSCOM error reading 0x%08llx reg.\n", chiptod_tod_regs[i].xscom_addr); /* Invalidate this record and continue */ chiptod_tod_regs[i].val[chip_id].valid = 0; continue; } chiptod_tod_regs[i].val[chip_id].valid = 1; } } static void chiptod_cache_tod_registers(void) { struct proc_chip *chip; for_each_chip(chip) _chiptod_cache_tod_regs(chip->id); } static void print_topo_info(enum chiptod_topology topo) { const char *role[] = { "Unknown", "MDMT", "MDST", "SDMT", "SDST" }; const char *status[] = { "Unknown", "Active Master", "Backup Master", "Backup Master Disabled" }; prlog(PR_DEBUG, "CHIPTOD: chip id: %d, Role: %s, Status: %s\n", chiptod_topology_info[topo].id, role[chiptod_topology_info[topo].role + 1], status[chiptod_topology_info[topo].status + 1]); } static void print_topology_info(void) { const char *topo[] = { "Unknown", "Primary", "Secondary" }; if (current_topology < 0) return; prlog(PR_DEBUG, "CHIPTOD: TOD Topology in Use: %s\n", topo[current_topology+1]); prlog(PR_DEBUG, "CHIPTOD: Primary configuration:\n"); print_topo_info(chiptod_topo_primary); prlog(PR_DEBUG, "CHIPTOD: Secondary configuration:\n"); print_topo_info(chiptod_topo_secondary); } static enum chiptod_topology query_current_topology(void) { uint64_t tod_status; if (xscom_readme(TOD_STATUS, &tod_status) != 0) { prerror("CHIPTOD: XSCOM error reading TOD_STATUS reg\n"); return chiptod_topo_unknown; } /* * Tod status register bit [0-2] tells configuration in use. * 000 <= primary configuration in use * 111 <= secondary configuration in use */ if ((tod_status & TOD_ST_TOPOLOGY_SELECT) == TOD_PRI_CONF_IN_USE) return chiptod_topo_primary; else return chiptod_topo_secondary; } static enum chiptod_chip_role chiptod_get_chip_role(enum chiptod_topology topology, int32_t chip_id) { uint64_t tod_ctrl; enum chiptod_chip_role role = chiptod_chip_role_UNKNOWN; if (chip_id < 0) return role; if (xscom_read(chip_id, TOD_PSMS_CTRL, &tod_ctrl) != 0) { prerror("CHIPTOD: XSCOM error reading TOD_PSMS_CTRL\n"); return chiptod_chip_role_UNKNOWN; } switch (topology) { case chiptod_topo_primary: if (tod_ctrl & TOD_PSMSC_PM_DRAW_SELECT) { if (tod_ctrl & TOD_PSMSC_PM_TOD_SELECT) role = chiptod_chip_role_MDMT; else role = chiptod_chip_role_MDST; } else { if (tod_ctrl & TOD_PSMSC_PM_TOD_SELECT) role = chiptod_chip_role_SDMT; else role = chiptod_chip_role_SDST; } break; case chiptod_topo_secondary: if (tod_ctrl & TOD_PSMSC_SM_DRAW_SELECT) { if (tod_ctrl & TOD_PSMSC_SM_TOD_SELECT) role = chiptod_chip_role_MDMT; else role = chiptod_chip_role_MDST; } else { if (tod_ctrl & TOD_PSMSC_SM_TOD_SELECT) role = chiptod_chip_role_SDMT; else role = chiptod_chip_role_SDST; } break; case chiptod_topo_unknown: default: break; } return role; } /* * Check and return the status of sync step network for a given * topology configuration. * Return values: * true: Sync Step network is running * false: Sync Step network is not running */ static bool chiptod_sync_step_check_running(enum chiptod_topology topology) { uint64_t tod_status; enum chiptod_chip_role role; bool running = false; int32_t chip_id = chiptod_topology_info[topology].id; /* Sanity check */ if (chip_id < 0) return false; if (xscom_read(chip_id, TOD_STATUS, &tod_status) != 0) { prerror("CHIPTOD: XSCOM error reading TOD_STATUS reg\n"); return false; } switch (topology) { case chiptod_topo_primary: /* Primary configuration */ role = chiptod_topology_info[topology].role; if (role == chiptod_chip_role_MDMT) { /* * Chip is using Master path. * Check if it is using path_0/path_1 and then * validity of that path. * * TOD_STATUS[12]: 0 = PATH_0, 1 = PATH_1 */ if (tod_status & TOD_ST_PRI_MPATH_SELECT) { if (tod_status & TOD_ST_MPATH1_STEP_VALID) running = true; } else { if (tod_status & TOD_ST_MPATH0_STEP_VALID) running = true; } } else { /* * Chip is using Slave path. * * TOD_STATUS[15]: 0 = PATH_0, 1 = PATH_1 */ if (tod_status & TOD_ST_PRI_SPATH_SELECT) { if (tod_status & TOD_ST_SPATH1_STEP_VALID) running = true; } else { if (tod_status & TOD_ST_SPATH0_STEP_VALID) running = true; } } break; case chiptod_topo_secondary: /* Secondary configuration */ role = chiptod_topology_info[topology].role; if (role == chiptod_chip_role_MDMT) { /* * Chip is using Master path. * Check if it is using path_0/path_1 and then * validity of that path. * * TOD_STATUS[12]: 0 = PATH_0, 1 = PATH_1 */ if (tod_status & TOD_ST_SEC_MPATH_SELECT) { if (tod_status & TOD_ST_MPATH1_STEP_VALID) running = true; } else { if (tod_status & TOD_ST_MPATH0_STEP_VALID) running = true; } } else { /* * Chip is using Slave path. * * TOD_STATUS[15]: 0 = PATH_0, 1 = PATH_1 */ if (tod_status & TOD_ST_SEC_SPATH_SELECT) { if (tod_status & TOD_ST_SPATH1_STEP_VALID) running = true; } else { if (tod_status & TOD_ST_SPATH0_STEP_VALID) running = true; } } break; default: break; } return running; } static enum chiptod_chip_status _chiptod_get_chip_status(int32_t chip_id) { uint64_t tod_status; enum chiptod_chip_status status = -1; if (xscom_read(chip_id, TOD_STATUS, &tod_status) != 0) { prerror("CHIPTOD: XSCOM error reading TOD_STATUS reg\n"); return status; } if (tod_status & TOD_ST_ACTIVE_MASTER) status = chiptod_active_master; else if (tod_status & TOD_ST_BACKUP_MASTER) status = chiptod_backup_master; return status; } static enum chiptod_chip_status chiptod_get_chip_status(enum chiptod_topology topology) { return _chiptod_get_chip_status(chiptod_topology_info[topology].id); } static void chiptod_update_topology(enum chiptod_topology topo) { int32_t chip_id = chiptod_topology_info[topo].id; chiptod_topology_info[topo].role = chiptod_get_chip_role(topo, chip_id); chiptod_topology_info[topo].status = chiptod_get_chip_status(topo); /* * If chip TOD on this topology is a backup master then check if * sync/step network is running on this topology. If not, * then mark status as backup not valid. */ if ((chiptod_topology_info[topo].status == chiptod_backup_master) && !chiptod_sync_step_check_running(topo)) chiptod_topology_info[topo].status = chiptod_backup_disabled; } static void chiptod_setup_base_tfmr(void) { struct dt_node *cpu = this_cpu()->node; uint64_t core_freq, tod_freq; uint64_t mcbs; base_tfmr = SPR_TFMR_TB_ECLIPZ; /* Get CPU and TOD freqs in Hz */ if (dt_has_node_property(cpu,"ibm,extended-clock-frequency", NULL)) core_freq = dt_prop_get_u64(cpu,"ibm,extended-clock-frequency"); else core_freq = dt_prop_get_u32(cpu, "clock-frequency"); tod_freq = 32000000; /* Calculate the "Max Cycles Between Steps" value according * to the magic formula: * * mcbs = (core_freq * max_jitter_factor) / (4 * tod_freq) / 100; * * The max jitter factor is set to 240 based on what pHyp uses. */ mcbs = (core_freq * 240) / (4 * tod_freq) / 100; prlog(PR_INFO, "CHIPTOD: Calculated MCBS is 0x%llx" " (Cfreq=%lld Tfreq=%lld)\n", mcbs, core_freq, tod_freq); /* Bake that all into TFMR */ base_tfmr = SETFIELD(SPR_TFMR_MAX_CYC_BET_STEPS, base_tfmr, mcbs); base_tfmr = SETFIELD(SPR_TFMR_N_CLKS_PER_STEP, base_tfmr, 0); base_tfmr = SETFIELD(SPR_TFMR_SYNC_BIT_SEL, base_tfmr, 4); } static bool chiptod_mod_tb(void) { uint64_t tfmr = base_tfmr; uint64_t timeout = 0; /* Switch timebase to "Not Set" state */ mtspr(SPR_TFMR, tfmr | SPR_TFMR_LOAD_TOD_MOD); do { if (++timeout >= (TIMEOUT_LOOPS*2)) { prerror("CHIPTOD: TB \"Not Set\" timeout\n"); return false; } tfmr = mfspr(SPR_TFMR); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CHIPTOD: TB \"Not Set\" TFMR corrupt\n"); return false; } if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == 9) { prerror("CHIPTOD: TB \"Not Set\" TOD in error state\n"); return false; } } while(tfmr & SPR_TFMR_LOAD_TOD_MOD); return true; } static bool chiptod_interrupt_check(void) { uint64_t tfmr = mfspr(SPR_TFMR); uint64_t timeout = 0; do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CHIPTOD: Interrupt check fail\n"); return false; } tfmr = mfspr(SPR_TFMR); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CHIPTOD: Interrupt check TFMR corrupt !\n"); return false; } } while(tfmr & SPR_TFMR_CHIP_TOD_INTERRUPT); return true; } static bool chiptod_running_check(uint32_t chip_id) { uint64_t tval; if (xscom_read(chip_id, TOD_CHIPTOD_FSM, &tval) != 0) { prerror("CHIPTOD: XSCOM error polling run\n"); return false; } if (tval & 0x0800000000000000UL) return true; else return false; } static bool chiptod_poll_running(void) { uint64_t timeout = 0; uint64_t tval; /* Chip TOD running check */ do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CHIPTOD: Running check fail timeout\n"); return false; } if (xscom_readme(TOD_CHIPTOD_FSM, &tval) != 0) { prerror("CHIPTOD: XSCOM error polling run\n"); return false; } } while(!(tval & 0x0800000000000000UL)); return true; } static bool chiptod_to_tb(void) { uint64_t tval, tfmr, tvbits; uint64_t timeout = 0; /* Tell the ChipTOD about our fabric address * * The pib_master value is calculated from the CPU core ID, given in * the PIR. Because we have different core/thread arrangements in the * PIR between p7 and p8, we need to do the calculation differently. * * p7: 0b00001 || 3-bit core id * p8: 0b0001 || 4-bit core id */ if (xscom_readme(TOD_PIB_MASTER, &tval) != 0) { prerror("CHIPTOD: XSCOM error reading PIB_MASTER\n"); return false; } if (chiptod_type == chiptod_p8) { tvbits = (this_cpu()->pir >> 3) & 0xf; tvbits |= 0x10; } else { tvbits = (this_cpu()->pir >> 2) & 0x7; tvbits |= 0x08; } tval &= ~TOD_PIBM_ADDR_CFG_MCAST; tval = SETFIELD(TOD_PIBM_ADDR_CFG_SLADDR, tval, tvbits); if (xscom_writeme(TOD_PIB_MASTER, tval) != 0) { prerror("CHIPTOD: XSCOM error writing PIB_MASTER\n"); return false; } /* Make us ready to get the TB from the chipTOD */ mtspr(SPR_TFMR, base_tfmr | SPR_TFMR_MOVE_CHIP_TOD_TO_TB); /* Tell the ChipTOD to send it */ if (xscom_writeme(TOD_CHIPTOD_TO_TB, (1ULL << 63)) != 0) { prerror("CHIPTOD: XSCOM error writing CHIPTOD_TO_TB\n"); return false; } /* Wait for it to complete */ timeout = 0; do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CHIPTOD: Chip to TB timeout\n"); return false; } tfmr = mfspr(SPR_TFMR); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CHIPTOD: MoveToTB: corrupt TFMR !\n"); return false; } } while(tfmr & SPR_TFMR_MOVE_CHIP_TOD_TO_TB); return true; } static bool chiptod_check_tb_running(void) { /* We used to wait for two SYNC pulses in TFMR but that * doesn't seem to occur in sim, so instead we use a * method similar to what pHyp does which is to check for * TFMR SPR_TFMR_TB_VALID and not SPR_TFMR_TFMR_CORRUPT */ #if 0 uint64_t tfmr, timeout; unsigned int i; for (i = 0; i < 2; i++) { tfmr = mfspr(SPR_TFMR); tfmr &= ~SPR_TFMR_TB_SYNC_OCCURED; mtspr(SPR_TFMR, tfmr); timeout = 0; do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CHIPTOD: No sync pulses\n"); return false; } tfmr = mfspr(SPR_TFMR); } while(!(tfmr & SPR_TFMR_TB_SYNC_OCCURED)); } #else uint64_t tfmr = mfspr(SPR_TFMR); return (tfmr & SPR_TFMR_TB_VALID) && !(tfmr & SPR_TFMR_TFMR_CORRUPT); #endif return true; } static bool chiptod_reset_tb_errors(void) { uint64_t tfmr; unsigned long timeout = 0; /* Ask for automatic clear of errors */ tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS; /* Additionally pHyp sets these (write-1-to-clear ?) */ tfmr |= SPR_TFMR_TB_MISSING_SYNC; tfmr |= SPR_TFMR_TB_MISSING_STEP; tfmr |= SPR_TFMR_TB_RESIDUE_ERR; mtspr(SPR_TFMR, tfmr); /* We have to write "Clear TB Errors" again */ tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS; mtspr(SPR_TFMR, tfmr); do { if (++timeout >= TIMEOUT_LOOPS) { /* Don't actually do anything on error for * now ... not much we can do, panic maybe ? */ prerror("CHIPTOD: TB error reset timeout !\n"); return false; } tfmr = mfspr(SPR_TFMR); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CHIPTOD: TB error reset: corrupt TFMR !\n"); return false; } } while(tfmr & SPR_TFMR_CLEAR_TB_ERRORS); return true; } static void chiptod_cleanup_thread_tfmr(void) { uint64_t tfmr = base_tfmr; tfmr |= SPR_TFMR_PURR_PARITY_ERR; tfmr |= SPR_TFMR_SPURR_PARITY_ERR; tfmr |= SPR_TFMR_DEC_PARITY_ERR; tfmr |= SPR_TFMR_TFMR_CORRUPT; tfmr |= SPR_TFMR_PURR_OVERFLOW; tfmr |= SPR_TFMR_SPURR_OVERFLOW; mtspr(SPR_TFMR, tfmr); } static void chiptod_reset_tod_errors(void) { uint64_t terr; /* * At boot, we clear the errors that the firmware is * supposed to handle. List provided by the pHyp folks. */ terr = TOD_ERR_CRITC_PARITY; terr |= TOD_ERR_PSS_HAMMING_DISTANCE; terr |= TOD_ERR_DELAY_COMPL_PARITY; terr |= TOD_ERR_CTCR_PARITY; terr |= TOD_ERR_TOD_SYNC_CHECK; terr |= TOD_ERR_TOD_FSM_PARITY; terr |= TOD_ERR_TOD_REGISTER_PARITY; if (xscom_writeme(TOD_ERROR, terr) != 0) { prerror("CHIPTOD: XSCOM error writing TOD_ERROR !\n"); /* Not much we can do here ... abort ? */ } } static void chiptod_sync_master(void *data) { bool *result = data; prlog(PR_DEBUG, "CHIPTOD: Master sync on CPU PIR 0x%04x...\n", this_cpu()->pir); /* Apply base tfmr */ mtspr(SPR_TFMR, base_tfmr); /* From recipe provided by pHyp folks, reset various errors * before attempting the sync */ chiptod_reset_tb_errors(); /* Cleanup thread tfmr bits */ chiptod_cleanup_thread_tfmr(); /* Reset errors in the chiptod itself */ chiptod_reset_tod_errors(); /* Switch timebase to "Not Set" state */ if (!chiptod_mod_tb()) goto error; prlog(PR_INSANE, "SYNC MASTER Step 2 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Chip TOD step checkers enable */ if (xscom_writeme(TOD_TTYPE_2, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error enabling steppers\n"); goto error; } prlog(PR_INSANE, "SYNC MASTER Step 3 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Chip TOD interrupt check */ if (!chiptod_interrupt_check()) goto error; prlog(PR_INSANE, "SYNC MASTER Step 4 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Switch local chiptod to "Not Set" state */ if (xscom_writeme(TOD_LOAD_TOD_MOD, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error sending LOAD_TOD_MOD\n"); goto error; } /* Switch all remote chiptod to "Not Set" state */ if (xscom_writeme(TOD_TTYPE_5, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error sending TTYPE_5\n"); goto error; } /* Chip TOD load initial value */ if (xscom_writeme(TOD_CHIPTOD_LOAD_TB, INIT_TB) != 0) { prerror("CHIPTOD: XSCOM error setting init TB\n"); goto error; } prlog(PR_INSANE, "SYNC MASTER Step 5 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); if (!chiptod_poll_running()) goto error; prlog(PR_INSANE, "SYNC MASTER Step 6 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Move chiptod value to core TB */ if (!chiptod_to_tb()) goto error; prlog(PR_INSANE, "SYNC MASTER Step 7 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Send local chip TOD to all chips TOD */ if (xscom_writeme(TOD_TTYPE_4, (1ULL << 63)) != 0) { prerror("CHIPTOD: XSCOM error sending TTYPE_4\n"); goto error; } /* Check if TB is running */ if (!chiptod_check_tb_running()) goto error; prlog(PR_INSANE, "Master sync completed, TB=%lx\n", mfspr(SPR_TBRL)); /* * A little delay to make sure the remote chips get up to * speed before we start syncing them. * * We have to do it here because we know our TB is running * while the boot thread TB might not yet. */ time_wait_ms(1); *result = true; return; error: prerror("CHIPTOD: Master sync failed! TFMR=0x%016lx\n", mfspr(SPR_TFMR)); *result = false; } static void chiptod_sync_slave(void *data) { bool *result = data; /* Only get primaries, not threads */ if (this_cpu()->is_secondary) { /* On secondaries we just cleanup the TFMR */ chiptod_cleanup_thread_tfmr(); *result = true; return; } prlog(PR_DEBUG, "CHIPTOD: Slave sync on CPU PIR 0x%04x...\n", this_cpu()->pir); /* Apply base tfmr */ mtspr(SPR_TFMR, base_tfmr); /* From recipe provided by pHyp folks, reset various errors * before attempting the sync */ chiptod_reset_tb_errors(); /* Cleanup thread tfmr bits */ chiptod_cleanup_thread_tfmr(); /* Switch timebase to "Not Set" state */ if (!chiptod_mod_tb()) goto error; prlog(PR_INSANE, "SYNC SLAVE Step 2 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Chip TOD running check */ if (!chiptod_poll_running()) goto error; prlog(PR_INSANE, "SYNC SLAVE Step 3 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Chip TOD interrupt check */ if (!chiptod_interrupt_check()) goto error; prlog(PR_INSANE, "SYNC SLAVE Step 4 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Move chiptod value to core TB */ if (!chiptod_to_tb()) goto error; prlog(PR_INSANE, "SYNC SLAVE Step 5 TFMR=0x%016lx\n", mfspr(SPR_TFMR)); /* Check if TB is running */ if (!chiptod_check_tb_running()) goto error; prlog(PR_INSANE, "Slave sync completed, TB=%lx\n", mfspr(SPR_TBRL)); *result = true; return; error: prerror("CHIPTOD: Slave sync failed ! TFMR=0x%016lx\n", mfspr(SPR_TFMR)); *result = false; } bool chiptod_wakeup_resync(void) { if (chiptod_primary < 0) return 0; lock(&chiptod_lock); /* Apply base tfmr */ mtspr(SPR_TFMR, base_tfmr); /* From recipe provided by pHyp folks, reset various errors * before attempting the sync */ chiptod_reset_tb_errors(); /* Cleanup thread tfmr bits */ chiptod_cleanup_thread_tfmr(); /* Switch timebase to "Not Set" state */ if (!chiptod_mod_tb()) goto error; /* Move chiptod value to core TB */ if (!chiptod_to_tb()) goto error; unlock(&chiptod_lock); return true; error: prerror("CHIPTOD: Resync failed ! TFMR=0x%16lx\n", mfspr(SPR_TFMR)); unlock(&chiptod_lock); return false; } static int chiptod_recover_tod_errors(void) { uint64_t terr; uint64_t treset = 0; int i; int32_t chip_id = this_cpu()->chip_id; /* Read TOD error register */ if (xscom_readme(TOD_ERROR, &terr) != 0) { prerror("CHIPTOD: XSCOM error reading TOD_ERROR reg\n"); return 0; } /* Check for sync check error and recover */ if ((terr & TOD_ERR_TOD_SYNC_CHECK) || (terr & TOD_ERR_TOD_FSM_PARITY) || (terr & TOD_ERR_CTCR_PARITY) || (terr & TOD_ERR_PSS_HAMMING_DISTANCE) || (terr & TOD_ERR_DELAY_COMPL_PARITY) || (terr & TOD_ERR_TOD_REGISTER_PARITY)) { chiptod_reset_tod_errors(); } /* * Check for TOD control register parity errors and restore those * registers with last saved valid values. */ for (i = 0; i < ARRAY_SIZE(chiptod_tod_regs); i++) { if (!(terr & chiptod_tod_regs[i].error_bit)) continue; /* Check if we have valid last saved register value. */ if (!chiptod_tod_regs[i].val[chip_id].valid) { prerror("CHIPTOD: Failed to restore TOD register: " "%08llx", chiptod_tod_regs[i].xscom_addr); return 0; } prlog(PR_DEBUG, "CHIPTOD: parity error, " "Restoring TOD register: %08llx\n", chiptod_tod_regs[i].xscom_addr); if (xscom_writeme(chiptod_tod_regs[i].xscom_addr, chiptod_tod_regs[i].val[chip_id].data) != 0) { prerror("CHIPTOD: XSCOM error writing 0x%08llx reg.\n", chiptod_tod_regs[i].xscom_addr); return 0; } treset |= chiptod_tod_regs[i].error_bit; } if (treset && (xscom_writeme(TOD_ERROR, treset) != 0)) { prerror("CHIPTOD: XSCOM error writing TOD_ERROR !\n"); return 0; } /* We have handled all the TOD errors routed to hypervisor */ return 1; } static int32_t chiptod_get_active_master(void) { if (current_topology < 0) return -1; if (chiptod_topology_info[current_topology].status == chiptod_active_master) return chiptod_topology_info[current_topology].id; return -1; } /* Return true if Active master TOD is running. */ static bool chiptod_master_running(void) { int32_t active_master_chip; active_master_chip = chiptod_get_active_master(); if (active_master_chip != -1) { if (chiptod_running_check(active_master_chip)) return true; } return false; } static bool chiptod_set_ttype4_mode(struct proc_chip *chip, bool enable) { uint64_t tval; /* Sanity check */ if (!chip) return false; if (xscom_read(chip->id, TOD_PIB_MASTER, &tval) != 0) { prerror("CHIPTOD: XSCOM error reading PIB_MASTER\n"); return false; } if (enable) { /* * Enable TTYPE4 send mode. This allows TOD to respond to * TTYPE3 request. */ tval |= TOD_PIBM_TTYPE4_SEND_MODE; tval |= TOD_PIBM_TTYPE4_SEND_ENBL; } else { /* Disable TTYPE4 send mode. */ tval &= ~TOD_PIBM_TTYPE4_SEND_MODE; tval &= ~TOD_PIBM_TTYPE4_SEND_ENBL; } if (xscom_write(chip->id, TOD_PIB_MASTER, tval) != 0) { prerror("CHIPTOD: XSCOM error writing PIB_MASTER\n"); return false; } return true; } /* Stop TODs on slave chips in backup topology. */ static void chiptod_stop_slave_tods(void) { struct proc_chip *chip = NULL; enum chiptod_topology backup_topo; uint64_t terr = 0; /* Inject TOD sync check error on salve TODs to stop them. */ terr |= TOD_ERR_TOD_SYNC_CHECK; if (current_topology == chiptod_topo_primary) backup_topo = chiptod_topo_secondary; else backup_topo = chiptod_topo_primary; for_each_chip(chip) { enum chiptod_chip_role role; /* Current chip TOD is already in stooped state */ if (chip->id == this_cpu()->chip_id) continue; role = chiptod_get_chip_role(backup_topo, chip->id); /* Skip backup master chip TOD. */ if (role == chiptod_chip_role_MDMT) continue; if (xscom_write(chip->id, TOD_ERROR_INJECT, terr) != 0) prerror("CHIPTOD: XSCOM error writing TOD_ERROR_INJ\n"); if (chiptod_running_check(chip->id)) { prlog(PR_DEBUG, "CHIPTOD: Failed to stop TOD on slave CHIP [%d]\n", chip->id); } } } static bool is_topology_switch_required(void) { int32_t active_master_chip; uint64_t tod_error; active_master_chip = chiptod_get_active_master(); /* Check if TOD is running on Active master. */ if (chiptod_master_running()) return false; /* * Check if sync/step network is running. * * If sync/step network is not running on current active topology * then we need switch topology to recover from TOD error. */ if (!chiptod_sync_step_check_running(current_topology)) { prlog(PR_DEBUG, "CHIPTOD: Sync/Step network not running\n"); return true; } /* * Check if there is a step check error reported on * Active master. */ if (xscom_read(active_master_chip, TOD_ERROR, &tod_error) != 0) { prerror("CHIPTOD: XSCOM error reading TOD_ERROR reg\n"); /* * Can't do anything here. But we already found that * sync/step network is running. Hence return false. */ return false; } if (tod_error & TOD_ERR_MP0_STEP_CHECK) { prlog(PR_DEBUG, "CHIPTOD: TOD step check error\n"); return true; } return false; } static bool chiptod_backup_valid(void) { enum chiptod_topology backup_topo; if (current_topology < 0) return false; if (current_topology == chiptod_topo_primary) backup_topo = chiptod_topo_secondary; else backup_topo = chiptod_topo_primary; if (chiptod_topology_info[backup_topo].status == chiptod_backup_master) return chiptod_sync_step_check_running(backup_topo); return false; } static void chiptod_topology_switch_complete(void) { /* * After the topology switch, we may have a non-functional backup * topology, and we won't be able to recover from future TOD errors * that requires topology switch. Someone needs to either fix it OR * configure new functional backup topology. * * Bit 18 of the Pervasive FIR is used to signal that TOD error * analysis needs to be performed. This allows FSP/PRD to * investigate and re-configure new backup topology if required. * Once new backup topology is configured and ready, FSP sends a * mailbox command xE6, s/c 0x06, mod 0, to enable the backup * topology. * * This isn't documented anywhere. This info is provided by FSP * folks. */ if (xscom_writeme(LOCAL_CORE_FIR, LFIR_SWITCH_COMPLETE) != 0) { prerror("CHIPTOD: XSCOM error writing LOCAL_CORE_FIR\n"); return; } /* Save TOD control registers values. */ chiptod_cache_tod_registers(); prlog(PR_DEBUG, "CHIPTOD: Topology switch complete\n"); print_topology_info(); } /* * Sync up TOD with other chips and get TOD in running state. * Check if current topology is active and running. If not, then * trigger a topology switch. */ static int chiptod_start_tod(void) { struct proc_chip *chip = NULL; int rc = 1; /* Do a topology switch if required. */ if (is_topology_switch_required()) { int32_t mchip = chiptod_get_active_master(); prlog(PR_DEBUG, "CHIPTOD: Need topology switch to recover\n"); /* * There is a failure in StepSync network in current * active topology. TOD is not running on active master chip. * We need to sync with backup master chip TOD. * But before we do that we need to switch topology to make * backup master as the new active master. Once we switch the * topology we can then request TOD value from new active * master. But make sure we move local chiptod to Not Set * before requesting TOD value. * * Before triggering a topology switch, check if backup * is valid and stop all slave TODs in backup topology. */ if (!chiptod_backup_valid()) { prerror("CHIPTOD: Backup master is not enabled.\n"); prerror("CHIPTOD: Can not do a topology switch.\n"); return 0; } chiptod_stop_slave_tods(); if (xscom_write(mchip, TOD_TTYPE_1, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error switching primary/secondary\n"); return 0; } /* Update topology info. */ current_topology = query_current_topology(); chiptod_update_topology(chiptod_topo_primary); chiptod_update_topology(chiptod_topo_secondary); /* * We just switched topologies to recover. * Check if new master TOD is running. */ if (!chiptod_master_running()) { prerror("CHIPTOD: TOD is not running on new master.\n"); return 0; } /* * Enable step checkers on all Chip TODs * * During topology switch, step checkers are disabled * on all Chip TODs by default. Enable them. */ if (xscom_writeme(TOD_TTYPE_2, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error enabling steppers\n"); return 0; } chiptod_topology_switch_complete(); } if (!chiptod_master_running()) { /* * Active Master TOD is not running, which means it won't * respond to TTYPE_3 request. * * Find a chip that has TOD in running state and configure * it to respond to TTYPE_3 request. */ for_each_chip(chip) { if (chiptod_running_check(chip->id)) { if (chiptod_set_ttype4_mode(chip, true)) break; } } } /* Switch local chiptod to "Not Set" state */ if (xscom_writeme(TOD_LOAD_TOD_MOD, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error sending LOAD_TOD_MOD\n"); return 0; } /* * Request the current TOD value from another chip. * This will move TOD in running state */ if (xscom_writeme(TOD_TTYPE_3, (1UL << 63)) != 0) { prerror("CHIPTOD: XSCOM error sending TTYPE_3\n"); return 0; } /* Check if chip TOD is running. */ if (!chiptod_poll_running()) rc = 0; /* Restore the ttype4_mode. */ chiptod_set_ttype4_mode(chip, false); return rc; } static bool tfmr_recover_tb_errors(uint64_t tfmr) { uint64_t tfmr_reset_error; unsigned long timeout = 0; /* Ask for automatic clear of errors */ tfmr_reset_error = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS; /* Additionally pHyp sets these (write-1-to-clear ?) */ if (tfmr & SPR_TFMR_TB_MISSING_SYNC) tfmr_reset_error |= SPR_TFMR_TB_MISSING_SYNC; if (tfmr & SPR_TFMR_TB_MISSING_STEP) tfmr_reset_error |= SPR_TFMR_TB_MISSING_STEP; /* * write 1 to bit 45 to clear TB residue the error. * TB register has already been reset to zero as part pre-recovery. */ if (tfmr & SPR_TFMR_TB_RESIDUE_ERR) tfmr_reset_error |= SPR_TFMR_TB_RESIDUE_ERR; if (tfmr & SPR_TFMR_FW_CONTROL_ERR) tfmr_reset_error |= SPR_TFMR_FW_CONTROL_ERR; if (tfmr & SPR_TFMR_TBST_CORRUPT) tfmr_reset_error |= SPR_TFMR_TBST_CORRUPT; mtspr(SPR_TFMR, tfmr_reset_error); /* We have to write "Clear TB Errors" again */ tfmr_reset_error = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS; mtspr(SPR_TFMR, tfmr_reset_error); do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CHIPTOD: TB error reset timeout !\n"); return false; } tfmr = mfspr(SPR_TFMR); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CHIPTOD: TB error reset: corrupt TFMR !\n"); return false; } } while (tfmr & SPR_TFMR_CLEAR_TB_ERRORS); return true; } static bool tfmr_recover_non_tb_errors(uint64_t tfmr) { uint64_t tfmr_reset_errors = 0; /* * write 1 to bit 26 to clear TFMR HDEC parity error. * HDEC register has already been reset to zero as part pre-recovery. */ if (tfmr & SPR_TFMR_HDEC_PARITY_ERROR) tfmr_reset_errors |= SPR_TFMR_HDEC_PARITY_ERROR; if (tfmr & SPR_TFMR_DEC_PARITY_ERR) { /* Set DEC with all ones */ mtspr(SPR_DEC, ~0); /* set bit 59 to clear TFMR DEC parity error. */ tfmr_reset_errors |= SPR_TFMR_DEC_PARITY_ERR; } /* * Reset PURR/SPURR to recover. We also need help from KVM * layer to handle this change in PURR/SPURR. That needs * to be handled in kernel KVM layer. For now, to recover just * reset it. */ if (tfmr & SPR_TFMR_PURR_PARITY_ERR) { /* set PURR register with sane value or reset it. */ mtspr(SPR_PURR, 0); /* set bit 57 to clear TFMR PURR parity error. */ tfmr_reset_errors |= SPR_TFMR_PURR_PARITY_ERR; } if (tfmr & SPR_TFMR_SPURR_PARITY_ERR) { /* set PURR register with sane value or reset it. */ mtspr(SPR_SPURR, 0); /* set bit 58 to clear TFMR PURR parity error. */ tfmr_reset_errors |= SPR_TFMR_SPURR_PARITY_ERR; } /* Write TFMR twice to clear the error */ mtspr(SPR_TFMR, base_tfmr | tfmr_reset_errors); mtspr(SPR_TFMR, base_tfmr | tfmr_reset_errors); /* Get fresh copy of TFMR */ tfmr = mfspr(SPR_TFMR); /* Check if TFMR non-TB errors still present. */ if (tfmr & tfmr_reset_errors) { prerror( "CHIPTOD: TFMR non-TB error recovery failed! TFMR=0x%016lx\n", mfspr(SPR_TFMR)); return false; } return true; } /* * TFMR parity error recovery as per pc_workbook: * MT(TFMR) bits 11 and 60 are b’1’ * MT(HMER) all bits 1 except for bits 4,5 */ static bool chiptod_recover_tfmr_error(void) { uint64_t tfmr; /* Get the base TFMR */ tfmr = base_tfmr; /* Set bit 60 to clear TFMR parity error. */ tfmr |= SPR_TFMR_TFMR_CORRUPT; mtspr(SPR_TFMR, tfmr); /* Write twice to clear the error */ mtspr(SPR_TFMR, tfmr); /* Get fresh copy of TFMR */ tfmr = mfspr(SPR_TFMR); /* Check if TFMR parity error still present. */ if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CHIPTOD: TFMR error recovery: corrupt TFMR !\n"); return false; } /* * Now that we have sane value in TFMR, check if Timebase machine * state is in ERROR state. If yes, clear TB errors so that * Timebase machine state changes to RESET state. Once in RESET state * then we can then load TB with TOD value. */ if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == TBST_STATE_ERROR) { if (!chiptod_reset_tb_errors()) return false; } return true; } /* * Recover from TB and TOD errors. * Timebase register is per core and first thread that gets chance to * handle interrupt would fix actual TFAC errors and rest of the threads * from same core would see no errors. Return -1 if no errors have been * found. The caller (handle_hmi_exception) of this function would not * send an HMI event to host if return value is -1. * * Return values: * 0 <= Failed to recover from errors * 1 <= Successfully recovered from errors * -1 <= No errors found. Errors are already been fixed. */ int chiptod_recover_tb_errors(void) { uint64_t tfmr; int rc = -1; if (chiptod_primary < 0) return 0; lock(&chiptod_lock); /* Get fresh copy of TFMR */ tfmr = mfspr(SPR_TFMR); /* * Check for TFMR parity error and recover from it. * We can not trust any other bits in TFMR If it is corrupt. Fix this * before we do anything. */ if (tfmr & SPR_TFMR_TFMR_CORRUPT) { if (!chiptod_recover_tfmr_error()) { rc = 0; goto error_out; } } /* Get fresh copy of TFMR */ tfmr = mfspr(SPR_TFMR); /* * Check for TB errors. * On Sync check error, bit 44 of TFMR is set. Check for it and * clear it. */ if ((tfmr & SPR_TFMR_TB_MISSING_STEP) || (tfmr & SPR_TFMR_TB_RESIDUE_ERR) || (tfmr & SPR_TFMR_FW_CONTROL_ERR) || (tfmr & SPR_TFMR_TBST_CORRUPT) || (tfmr & SPR_TFMR_TB_MISSING_SYNC)) { if (!tfmr_recover_tb_errors(tfmr)) { rc = 0; goto error_out; } } /* * Check for TOD sync check error. * On TOD errors, bit 51 of TFMR is set. If this bit is on then we * need to fetch TOD error register and recover from TOD errors. * Bit 33 of TOD error register indicates sync check error. */ if (tfmr & SPR_TFMR_CHIP_TOD_INTERRUPT) rc = chiptod_recover_tod_errors(); /* Check if TB is running. If not then we need to get it running. */ if (!(tfmr & SPR_TFMR_TB_VALID)) { rc = 0; /* Place TB in Notset state. */ if (!chiptod_mod_tb()) goto error_out; /* * Before we move TOD to core TB check if TOD is running. * If not, then get TOD in running state. */ if (!chiptod_running_check(this_cpu()->chip_id)) if (!chiptod_start_tod()) goto error_out; /* Move chiptod value to core TB */ if (!chiptod_to_tb()) goto error_out; /* We have successfully able to get TB running. */ rc = 1; } /* * Now that TB is running, check for TFMR non-TB errors. */ if ((tfmr & SPR_TFMR_HDEC_PARITY_ERROR) || (tfmr & SPR_TFMR_PURR_PARITY_ERR) || (tfmr & SPR_TFMR_SPURR_PARITY_ERR) || (tfmr & SPR_TFMR_DEC_PARITY_ERR)) { if (!tfmr_recover_non_tb_errors(tfmr)) { rc = 0; goto error_out; } rc = 1; } error_out: unlock(&chiptod_lock); return rc; } static int64_t opal_resync_timebase(void) { if (!chiptod_wakeup_resync()) { prerror("OPAL: Resync timebase failed on CPU 0x%04x\n", this_cpu()->pir); return OPAL_HARDWARE; } return OPAL_SUCCESS; } opal_call(OPAL_RESYNC_TIMEBASE, opal_resync_timebase, 0); static void chiptod_print_tb(void *data __unused) { prlog(PR_DEBUG, "CHIPTOD: PIR 0x%04x TB=%lx\n", this_cpu()->pir, mfspr(SPR_TBRL)); } static bool chiptod_probe(void) { struct dt_node *np; dt_for_each_compatible(dt_root, np, "ibm,power-chiptod") { uint32_t chip; /* Old DT has chip-id in chiptod node, newer only in the * parent xscom bridge */ chip = dt_get_chip_id(np); if (dt_has_node_property(np, "primary", NULL)) { chiptod_primary = chip; if (dt_node_is_compatible(np,"ibm,power7-chiptod")) chiptod_type = chiptod_p7; if (dt_node_is_compatible(np,"ibm,power8-chiptod")) chiptod_type = chiptod_p8; } if (dt_has_node_property(np, "secondary", NULL)) chiptod_secondary = chip; } if (chiptod_type == chiptod_unknown) { prerror("CHIPTOD: Unknown TOD type !\n"); return false; } return true; } static void chiptod_discover_new_backup(enum chiptod_topology topo) { struct proc_chip *chip = NULL; /* Scan through available chips to find new backup master chip */ for_each_chip(chip) { if (_chiptod_get_chip_status(chip->id) == chiptod_backup_master) break; } /* Found new backup master chip. Update the topology info */ if (chip) { prlog(PR_DEBUG, "CHIPTOD: New backup master: CHIP [%d]\n", chip->id); if (topo == chiptod_topo_primary) chiptod_primary = chip->id; else chiptod_secondary = chip->id; chiptod_topology_info[topo].id = chip->id; chiptod_update_topology(topo); prlog(PR_DEBUG, "CHIPTOD: Backup topology configuration changed.\n"); print_topology_info(); } /* * Topology configuration has changed. Save TOD control registers * values. */ chiptod_cache_tod_registers(); } /* * Enable/disable backup topology. * If request is to enable topology, then discover new backup master * chip and update the topology configuration info. If the request is * to disable topology, then mark the current backup topology as disabled. * Return error (-1) if the action is requested on currenlty active * topology. * * Return values: * true <= Success * false <= Topology is active and in use. */ bool chiptod_adjust_topology(enum chiptod_topology topo, bool enable) { uint8_t rc = true; /* * The FSP can only request that the currently inactive topology * be disabled or enabled. If the requested topology is currently * the active topology, then fail this request with a -1 (TOD * topology in use) status as return code. */ lock(&chiptod_lock); if (topo == current_topology) { rc = false; goto out; } if (enable) chiptod_discover_new_backup(topo); else chiptod_topology_info[topo].status = chiptod_backup_disabled; out: unlock(&chiptod_lock); return rc; } static void chiptod_init_topology_info(void) { /* Find and update current topology in use. */ current_topology = query_current_topology(); /* Initialized primary topology chip config info */ chiptod_topology_info[chiptod_topo_primary].id = chiptod_primary; chiptod_update_topology(chiptod_topo_primary); /* Initialized secondary topology chip config info */ chiptod_topology_info[chiptod_topo_secondary].id = chiptod_secondary; chiptod_update_topology(chiptod_topo_secondary); /* Cache TOD control registers values. */ chiptod_cache_tod_registers(); print_topology_info(); } void chiptod_init(void) { struct cpu_thread *cpu0, *cpu; bool sres; /* Mambo and qemu doesn't simulate the chiptod */ if (chip_quirk(QUIRK_NO_CHIPTOD)) return; op_display(OP_LOG, OP_MOD_CHIPTOD, 0); if (!chiptod_probe()) { prerror("CHIPTOD: Failed ChipTOD detection !\n"); op_display(OP_FATAL, OP_MOD_CHIPTOD, 0); abort(); } op_display(OP_LOG, OP_MOD_CHIPTOD, 1); /* Pick somebody on the primary */ cpu0 = find_cpu_by_chip_id(chiptod_primary); /* Calculate the base TFMR value used for everybody */ chiptod_setup_base_tfmr(); prlog(PR_DEBUG, "CHIPTOD: Base TFMR=0x%016llx\n", base_tfmr); /* Schedule master sync */ sres = false; cpu_wait_job(cpu_queue_job(cpu0, "chiptod_sync_master", chiptod_sync_master, &sres), true); if (!sres) { op_display(OP_FATAL, OP_MOD_CHIPTOD, 2); abort(); } op_display(OP_LOG, OP_MOD_CHIPTOD, 2); /* Schedule slave sync */ for_each_available_cpu(cpu) { /* Skip master */ if (cpu == cpu0) continue; /* Queue job */ sres = false; cpu_wait_job(cpu_queue_job(cpu, "chiptod_sync_slave", chiptod_sync_slave, &sres), true); if (!sres) { op_display(OP_WARN, OP_MOD_CHIPTOD, 3|(cpu->pir << 8)); /* Disable threads */ cpu_disable_all_threads(cpu); } op_display(OP_LOG, OP_MOD_CHIPTOD, 3|(cpu->pir << 8)); } /* Display TBs */ for_each_available_cpu(cpu) { /* Only do primaries, not threads */ if (cpu->is_secondary) continue; cpu_wait_job(cpu_queue_job(cpu, "chiptod_print_tb", chiptod_print_tb, NULL), true); } chiptod_init_topology_info(); op_display(OP_LOG, OP_MOD_CHIPTOD, 4); } /* CAPP timebase sync */ static bool chiptod_capp_reset_tb_errors(uint32_t chip_id) { uint64_t tfmr; unsigned long timeout = 0; /* Ask for automatic clear of errors */ tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS; /* Additionally pHyp sets these (write-1-to-clear ?) */ tfmr |= SPR_TFMR_TB_MISSING_SYNC; tfmr |= SPR_TFMR_TB_MISSING_STEP; tfmr |= SPR_TFMR_TB_RESIDUE_ERR; tfmr |= SPR_TFMR_TBST_CORRUPT; tfmr |= SPR_TFMR_TFMR_CORRUPT; /* Write CAPP TFMR */ xscom_write(chip_id, CAPP_TFMR, tfmr); /* We have to write "Clear TB Errors" again */ tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS; /* Write CAPP TFMR */ xscom_write(chip_id, CAPP_TFMR, tfmr); do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CAPP: TB error reset timeout !\n"); return false; } /* Read CAPP TFMR */ xscom_read(chip_id, CAPP_TFMR, &tfmr); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CAPP: TB error reset: corrupt TFMR!\n"); return false; } } while (tfmr & SPR_TFMR_CLEAR_TB_ERRORS); return true; } static bool chiptod_capp_mod_tb(uint32_t chip_id) { uint64_t timeout = 0; uint64_t tfmr; /* Switch CAPP timebase to "Not Set" state */ tfmr = base_tfmr | SPR_TFMR_LOAD_TOD_MOD; xscom_write(chip_id, CAPP_TFMR, tfmr); do { if (++timeout >= (TIMEOUT_LOOPS*2)) { prerror("CAPP: TB \"Not Set\" timeout\n"); return false; } xscom_read(chip_id, CAPP_TFMR, &tfmr); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CAPP: TB \"Not Set\" TFMR corrupt\n"); return false; } if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == 9) { prerror("CAPP: TB \"Not Set\" TOD in error state\n"); return false; } } while (tfmr & SPR_TFMR_LOAD_TOD_MOD); return true; } static bool chiptod_wait_for_chip_sync(void) { uint64_t tfmr; uint64_t timeout = 0; /* Read core TFMR, mask bit 42, write core TFMR back */ tfmr = mfspr(SPR_TFMR); tfmr &= ~SPR_TFMR_TB_SYNC_OCCURED; mtspr(SPR_TFMR, tfmr); /* Read core TFMR until the TB sync occurred */ do { if (++timeout >= TIMEOUT_LOOPS) { prerror("CHIPTOD: No sync pulses\n"); return false; } tfmr = mfspr(SPR_TFMR); } while (!(tfmr & SPR_TFMR_TB_SYNC_OCCURED)); return true; } static bool chiptod_capp_check_tb_running(uint32_t chip_id) { uint64_t tfmr; uint64_t timeout = 0; /* Read CAPP TFMR until TB becomes valid */ do { if (++timeout >= (TIMEOUT_LOOPS*2)) { prerror("CAPP: TB Invalid!\n"); return false; } xscom_read(chip_id, CAPP_TFMR, &tfmr); if (tfmr & SPR_TFMR_TFMR_CORRUPT) { prerror("CAPP: TFMR corrupt!\n"); return false; } } while (!(tfmr & SPR_TFMR_TB_VALID)); return true; } bool chiptod_capp_timebase_sync(uint32_t chip_id) { uint64_t tfmr; uint64_t capp_tb; int64_t delta; unsigned int retry = 0; /* Set CAPP TFMR to base tfmr value */ xscom_write(chip_id, CAPP_TFMR, base_tfmr); /* Reset CAPP TB errors before attempting the sync */ if (!chiptod_capp_reset_tb_errors(chip_id)) return false; /* Switch CAPP TB to "Not Set" state */ if (!chiptod_capp_mod_tb(chip_id)) return false; /* Sync CAPP TB with core TB, retry while difference > 16usecs */ do { if (retry++ > 5) { prerror("CAPP: TB sync: giving up!\n"); return false; } /* Make CAPP ready to get the TB, wait for chip sync */ tfmr = base_tfmr | SPR_TFMR_MOVE_CHIP_TOD_TO_TB; xscom_write(chip_id, CAPP_TFMR, tfmr); if (!chiptod_wait_for_chip_sync()) return false; /* Set CAPP TB from core TB */ xscom_write(chip_id, CAPP_TB, mftb()); /* Wait for CAPP TFMR tb_valid bit */ if (!chiptod_capp_check_tb_running(chip_id)) return false; /* Read CAPP TB, read core TB, compare */ xscom_read(chip_id, CAPP_TB, &capp_tb); delta = mftb() - capp_tb; if (delta < 0) delta = -delta; } while (tb_to_usecs(delta) > 16); return true; } skiboot-skiboot-5.1.13/hw/dts.c000066400000000000000000000225261265204436200163120ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* Per core Digital Thermal Sensors */ #define EX_THERM_DTS_RESULT0 0x10050000 #define EX_THERM_DTS_RESULT1 0x10050001 /* Per core Digital Thermal Sensors control registers */ #define EX_THERM_MODE_REG 0x1005000F #define EX_THERM_CONTROL_REG 0x10050012 #define EX_THERM_ERR_STATUS_REG 0x10050013 /* Per memory controller Digital Thermal Sensors */ #define THERM_MEM_DTS_RESULT0 0x2050000 /* Per memory controller Digital Thermal Sensors control registers */ #define THERM_MEM_MODE_REG 0x205000F #define THERM_MEM_CONTROL_REG 0x2050012 #define THERM_MEM_ERR_STATUS_REG 0x2050013 struct dts { uint8_t valid; uint8_t trip; int16_t temp; }; /* Different sensor locations */ #define P7_CT_ZONE_LSU 0 #define P7_CT_ZONE_ISU 1 #define P7_CT_ZONE_IFU 2 #define P7_CT_ZONE_VFU 3 #define P7_CT_ZONE_L3C 4 #define P7_CT_ZONES 5 /* Per core Digital Thermal Sensors */ #define EX_THERM_P7_DTS_RESULT0 0x8050000 #define EX_THERM_P7_DTS_RESULT1 0x8050001 /* * DTS2 Thermal Sensor Results * * 0..7 sensor with id 0. * 8..15 sensor with id 1. (Only chiplets) * 16..23 sensor with id 2. (Only chiplets) * 24..31 sensor with id 3. (Only chiplets) * 32..39 sensor with id 4. (Only chiplets) * 40..56 reserved0 * 57 Trip warning history * 58 Trip critical history * 59 Trip fatal history * 60 reserved1 * 61..63 ID of worst case DTS2 (Only valid in EX core chiplets) */ static int dts_read_core_temp_p7(uint32_t pir, struct dts *dts) { int32_t chip_id = pir_to_chip_id(pir); int32_t core = pir_to_core_id(pir); uint64_t dts0; struct dts temps[P7_CT_ZONES]; int i; int rc; rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, EX_THERM_P7_DTS_RESULT0), &dts0); if (rc) return rc; temps[P7_CT_ZONE_LSU].temp = (dts0 >> 56) & 0xff; temps[P7_CT_ZONE_ISU].temp = (dts0 >> 48) & 0xff; temps[P7_CT_ZONE_IFU].temp = (dts0 >> 40) & 0xff; temps[P7_CT_ZONE_VFU].temp = (dts0 >> 32) & 0xff; temps[P7_CT_ZONE_L3C].temp = (dts0 >> 24) & 0xff; /* keep the max DTS */ for (i = 0; i < P7_CT_ZONES; i++) { int16_t t = temps[i].temp; if (t > dts->temp) dts->temp = t; } dts->trip = (dts0 >> 3) & 0xf; prlog(PR_TRACE, "DTS: Chip %x Core %x temp:%dC trip:%x\n", chip_id, core, dts->temp, dts->trip); return 0; } /* Therm mac result masking for DTS (result(0:15) * 0:3 - 0x0 * 4:11 - Temperature in degrees C * 12:13 - trip bits: 00 - no trip; 01 - warning; 10 - critical; 11 - fatal * 14 - spare * 15 - valid */ static void dts_decode_one_dts(uint16_t raw, struct dts *dts) { /* * The value is both signed and unsigned :-) 0xff could be * either 255C or -1C, so for now we treat this as unsigned * which is sufficient for our purpose. We could try to be * a bit smarter and treat it as signed for values between * -10 and 0 and unsigned to 239 or something like that... */ dts->valid = raw & 1; if (dts->valid) { dts->temp = (raw >> 4) & 0xff; dts->trip = (raw >> 2) & 0x3; } else { dts->temp = 0; dts->trip = 0; } } /* Different sensor locations */ #define P8_CT_ZONE_LSU 0 #define P8_CT_ZONE_ISU 1 #define P8_CT_ZONE_FXU 2 #define P8_CT_ZONE_L3C 3 #define P8_CT_ZONES 4 /* * Returns the temperature as the max of all 4 zones and a global trip * attribute. */ static int dts_read_core_temp_p8(uint32_t pir, struct dts *dts) { int32_t chip_id = pir_to_chip_id(pir); int32_t core = pir_to_core_id(pir); uint64_t dts0, dts1; struct dts temps[P8_CT_ZONES]; int i; int rc; rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, EX_THERM_DTS_RESULT0), &dts0); if (rc) return rc; rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, EX_THERM_DTS_RESULT1), &dts1); if (rc) return rc; dts_decode_one_dts(dts0 >> 48, &temps[P8_CT_ZONE_LSU]); dts_decode_one_dts(dts0 >> 32, &temps[P8_CT_ZONE_ISU]); dts_decode_one_dts(dts0 >> 16, &temps[P8_CT_ZONE_FXU]); dts_decode_one_dts(dts1 >> 48, &temps[P8_CT_ZONE_L3C]); for (i = 0; i < P8_CT_ZONES; i++) { int16_t t = temps[i].temp; if (!temps[i].valid) continue; /* keep the max temperature of all 4 sensors */ if (t > dts->temp) dts->temp = t; dts->valid++; dts->trip |= temps[i].trip; } prlog(PR_TRACE, "DTS: Chip %x Core %x temp:%dC trip:%x\n", chip_id, core, dts->temp, dts->trip); /* * FIXME: The trip bits are always set ?! Just discard * them for the moment until we understand why. */ dts->trip = 0; return 0; } static int dts_read_core_temp(uint32_t pir, struct dts *dts) { int rc; switch (proc_gen) { case proc_gen_p7: rc = dts_read_core_temp_p7(pir, dts); break; case proc_gen_p8: rc = dts_read_core_temp_p8(pir, dts); break; default: assert(false); } return rc; } /* Different sensor locations */ #define P8_MEM_DTS0 0 #define P8_MEM_DTS1 1 #define P8_MEM_ZONES 2 static int dts_read_mem_temp(uint32_t chip_id, struct dts *dts) { uint64_t dts0; struct dts temps[P8_MEM_ZONES]; int i; int rc; rc = xscom_read(chip_id, THERM_MEM_DTS_RESULT0, &dts0); if (rc) return rc; dts_decode_one_dts(dts0 >> 48, &temps[P8_MEM_DTS0]); dts_decode_one_dts(dts0 >> 32, &temps[P8_MEM_DTS1]); for (i = 0; i < P8_MEM_ZONES; i++) { int16_t t = temps[i].temp; if (!temps[i].valid) continue; /* keep the max temperature of all 4 sensors */ if (t > dts->temp) dts->temp = t; dts->valid++; dts->trip |= temps[i].trip; } prlog(PR_TRACE, "DTS: Chip %x temp:%dC trip:%x\n", chip_id, dts->temp, dts->trip); /* * FIXME: The trip bits are always set ?! Just discard * them for the moment until we understand why. */ dts->trip = 0; return 0; } /* * DTS sensor class ids. Only one for the moment: the core * temperature. */ enum sensor_dts_class { SENSOR_DTS_CORE_TEMP, SENSOR_DTS_MEM_TEMP, /* To be continued */ }; /* * Attributes for the core temperature sensor */ enum { SENSOR_DTS_ATTR_TEMP_MAX, SENSOR_DTS_ATTR_TEMP_TRIP }; int64_t dts_sensor_read(uint32_t sensor_hndl, uint32_t *sensor_data) { uint8_t attr = sensor_get_attr(sensor_hndl); uint32_t rid = sensor_get_rid(sensor_hndl); struct dts dts; int64_t rc; if (attr > SENSOR_DTS_ATTR_TEMP_TRIP) return OPAL_PARAMETER; memset(&dts, 0, sizeof(struct dts)); switch (sensor_get_frc(sensor_hndl) & ~SENSOR_DTS) { case SENSOR_DTS_CORE_TEMP: rc = dts_read_core_temp(rid, &dts); break; case SENSOR_DTS_MEM_TEMP: /* * restore centaur chip id which was truncated to fit * in the sensor handler */ rid |= 0x80000000; rc = dts_read_mem_temp(rid, &dts); break; default: rc = OPAL_PARAMETER; break; } if (rc) return rc; if (attr == SENSOR_DTS_ATTR_TEMP_MAX) *sensor_data = dts.temp; else if (attr == SENSOR_DTS_ATTR_TEMP_TRIP) *sensor_data = dts.trip; return 0; } bool dts_sensor_create_nodes(struct dt_node *sensors) { uint8_t sensor_class = SENSOR_DTS_CORE_TEMP|SENSOR_DTS; struct proc_chip *chip; struct dt_node *cn; char name[64]; /* build the device tree nodes : * * sensors/core-temp@pir * * The core is identified by its PIR, is stored in the resource * number of the sensor handler. */ for_each_chip(chip) { struct cpu_thread *c; for_each_available_core_in_chip(c, chip->id) { struct dt_node *node; uint32_t handler; snprintf(name, sizeof(name), "core-temp@%x", c->pir); handler = sensor_make_handler(sensor_class, c->pir, SENSOR_DTS_ATTR_TEMP_MAX); node = dt_new(sensors, name); dt_add_property_string(node, "compatible", "ibm,opal-sensor"); dt_add_property_cells(node, "sensor-data", handler); handler = sensor_make_handler(sensor_class, c->pir, SENSOR_DTS_ATTR_TEMP_TRIP); dt_add_property_cells(node, "sensor-status", handler); dt_add_property_string(node, "sensor-type", "temp"); dt_add_property_cells(node, "ibm,pir", c->pir); dt_add_property_string(node, "label", "Core"); } } sensor_class = SENSOR_DTS_MEM_TEMP|SENSOR_DTS; /* * sensors/mem-temp@chip for Centaurs */ dt_for_each_compatible(dt_root, cn, "ibm,centaur") { uint32_t chip_id; struct dt_node *node; uint32_t handler; chip_id = dt_prop_get_u32(cn, "ibm,chip-id"); snprintf(name, sizeof(name), "mem-temp@%x", chip_id); /* * We only have two bytes for the resource * identifier. Let's trunctate the centaur chip id */ handler = sensor_make_handler(sensor_class, chip_id & 0xffff, SENSOR_DTS_ATTR_TEMP_MAX); node = dt_new(sensors, name); dt_add_property_string(node, "compatible", "ibm,opal-sensor"); dt_add_property_cells(node, "sensor-data", handler); handler = sensor_make_handler(sensor_class, chip_id, SENSOR_DTS_ATTR_TEMP_TRIP); dt_add_property_cells(node, "sensor-status", handler); dt_add_property_string(node, "sensor-type", "temp"); dt_add_property_cells(node, "ibm,chip-id", chip_id); dt_add_property_string(node, "label", "Centaur"); } return true; } skiboot-skiboot-5.1.13/hw/ec/000077500000000000000000000000001265204436200157345ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hw/ec/Makefile.inc000066400000000000000000000001741265204436200201460ustar00rootroot00000000000000# -*-Makefile-*- # Sapphire EC makefile SUBDIRS += hw/ec EC_OBJS = gpio.o EC=hw/ec/built-in.o $(EC): $(EC_OBJS:%=hw/ec/%) skiboot-skiboot-5.1.13/hw/ec/gpio.c000066400000000000000000000042561265204436200170450ustar00rootroot00000000000000/* Copyright 2013-2014 Google Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ec/config.h" #include "ec/gpio.h" int ec_gpio_setup(EcGpioPort port, uint8_t pin, int is_output, int pullup_enable) { uint8_t ddr_reg; if (pin > 7) { return -1; } /* Set data direction */ ec_outb(EC_GPIO_INDEX, port * EC_GPIO_PORT_SKIP + EC_GPIO_DDR_OFFSET); ddr_reg = ec_inb(EC_GPIO_DATA); if (is_output) { ddr_reg |= (1 << pin); } else { ddr_reg &= ~(1 << pin); } ec_outb(EC_GPIO_DATA, ddr_reg); /* Set pullup enable for output GPOs */ if (is_output) { uint8_t pup_reg; ec_outb(EC_GPIO_INDEX, port * EC_GPIO_PORT_SKIP + EC_GPIO_PUP_OFFSET); pup_reg = ec_inb(EC_GPIO_DATA); if (pullup_enable) { pup_reg |= (1 << pin); } else { pup_reg &= ~(1 << pin); } ec_outb(EC_GPIO_DATA, pup_reg); } return 0; } int ec_gpio_read(EcGpioPort port, uint8_t pin) { uint8_t pin_reg; if (pin > 7) { return -1; } ec_outb(EC_GPIO_INDEX, port * EC_GPIO_PORT_SKIP + EC_GPIO_PIN_OFFSET); pin_reg = ec_inb(EC_GPIO_DATA); return !!(pin_reg & (1 << pin)); } int ec_gpio_set(EcGpioPort port, uint8_t pin, int val) { uint8_t data_reg; if (pin > 7) { return -1; } ec_outb(EC_GPIO_INDEX, port * EC_GPIO_PORT_SKIP + EC_GPIO_DATA_OFFSET); data_reg = ec_inb(EC_GPIO_DATA); if (val) { data_reg |= (1 << pin); } else { data_reg &= ~(1 << pin); } ec_outb(EC_GPIO_DATA, data_reg); return 0; } skiboot-skiboot-5.1.13/hw/ec/makefile000066400000000000000000000001751265204436200174370ustar00rootroot00000000000000ROOTPATH = ../../.. MODULE = ec OBJS = cmosdd.o gpio.o rhesus.o hostboot.o SUBDIRS = test.d include ${ROOTPATH}/config.mk skiboot-skiboot-5.1.13/hw/fake-rtc.c000066400000000000000000000031001265204436200171770ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include static uint32_t *fake_ymd; static uint64_t *fake_hmsm; static int64_t fake_rtc_write(uint32_t ymd, uint64_t hmsm) { *fake_ymd = ymd; *fake_hmsm = hmsm; return OPAL_SUCCESS; } static int64_t fake_rtc_read(uint32_t *ymd, uint64_t *hmsm) { if (!ymd || !hmsm) return OPAL_PARAMETER; *ymd = *fake_ymd; *hmsm = *fake_hmsm; return OPAL_SUCCESS; } void fake_rtc_init(void) { struct mem_region *rtc_region = NULL; uint32_t *rtc = NULL; /* Read initial values from reserved memory */ rtc_region = find_mem_region("ibm,fake-rtc"); /* Should we register anyway? */ if (!rtc_region) { prlog(PR_TRACE, "No initial RTC value found\n"); return; } rtc = (uint32_t *) rtc_region->start; fake_ymd = rtc; fake_hmsm = ((uint64_t *) &rtc[1]); prlog(PR_TRACE, "Init fake RTC to 0x%x 0x%llx\n", *fake_ymd, *fake_hmsm); opal_register(OPAL_RTC_READ, fake_rtc_read, 2); opal_register(OPAL_RTC_WRITE, fake_rtc_write, 2); } skiboot-skiboot-5.1.13/hw/fsi-master.c000066400000000000000000000456751265204436200176040ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* * FSI Masters sit on OPB busses behind PIB2OPB bridges * * There are two cMFSI behind two different bridges at * different XSCOM addresses. For now we don't have them in * the device-tree so we hard code the address */ #define PIB2OPB_MFSI0_ADDR 0x20000 #define PIB2OPB_MFSI1_ADDR 0x30000 /* * Bridge registers on XSCOM that allow generatoin * of OPB cycles */ #define PIB2OPB_REG_CMD 0x0 #define OPB_CMD_WRITE 0x80000000 #define OPB_CMD_READ 0x00000000 #define OPB_CMD_8BIT 0x00000000 #define OPB_CMD_16BIT 0x20000000 #define OPB_CMD_32BIT 0x60000000 #define PIB2OPB_REG_STAT 0x1 #define OPB_STAT_ANY_ERR 0x80000000 #define OPB_STAT_ERR_OPB 0x7FEC0000 #define OPB_STAT_ERRACK 0x00100000 #define OPB_STAT_BUSY 0x00010000 #define OPB_STAT_READ_VALID 0x00020000 #define OPB_STAT_ERR_CMFSI 0x0000FC00 #define OPB_STAT_ERR_HMFSI 0x000000FC #define OPB_STAT_ERR_BASE (OPB_STAT_ANY_ERR | \ OPB_STAT_ERR_OPB | \ OPB_STAT_ERRACK) #define PIB2OPB_REG_LSTAT 0x2 #define PIB2OPB_REG_RESET 0x4 #define PIB2OPB_REG_cRSIC 0x5 #define PIB2OPB_REG_cRSIM 0x6 #define PIB2OPB_REG_cRSIS 0x7 #define PIB2OPB_REG_hRSIC 0x8 #define PIB2OPB_REG_hRSIM 0x9 #define PIB2OPB_REG_hRSIS 0xA /* Low level errors from OPB contain the status in the bottom 32-bit * and one of these in the top 32-bit */ #define OPB_ERR_XSCOM_ERR 0x100000000ull #define OPB_ERR_TIMEOUT_ERR 0x200000000ull #define OPB_ERR_BAD_OPB_ADDR 0x400000000ull /* * PIB2OPB 0 has 2 MFSIs, cMFSI and hMFSI, PIB2OPB 1 only * has cMFSI */ #define cMFSI_OPB_PORTS_BASE 0x40000 #define cMFSI_OPB_REG_BASE 0x03000 #define hMFSI_OPB_PORTS_BASE 0x80000 #define hMFSI_OPB_REG_BASE 0x03400 #define MFSI_OPB_PORT_STRIDE 0x08000 /* MFSI control registers */ #define MFSI_REG_MSTAP(__n) (0x0D0 + (__n) * 4) #define MFSI_REG_MATRB0 0x1D8 #define MFSI_REG_MDTRB0 0x1DC #define MFSI_REG_MESRB0 0x1D0 #define MFSI_REG_MAESP0 0x050 #define MFSI_REG_MAEB 0x070 #define MFSI_REG_MSCSB0 0x1D4 /* FSI Slave registers */ #define FSI_SLAVE_REGS 0x000800 /**< FSI Slave Register */ #define FSI_SMODE (FSI_SLAVE_REGS | 0x00) #define FSI_SLBUS (FSI_SLAVE_REGS | 0x30) #define FSI_SLRES (FSI_SLAVE_REGS | 0x34) #define FSI2PIB_ENGINE 0x001000 /**< FSI2PIB Engine (SCOM) */ #define FSI2PIB_RESET (FSI2PIB_ENGINE | 0x18) #define FSI2PIB_STATUS (FSI2PIB_ENGINE | 0x1C) #define FSI2PIB_COMPMASK (FSI2PIB_ENGINE | 0x30) #define FSI2PIB_TRUEMASK (FSI2PIB_ENGINE | 0x34) struct mfsi { uint32_t chip_id; uint32_t unit; uint32_t xscom_base; uint32_t ports_base; uint32_t reg_base; uint32_t err_bits; }; #define mfsi_log(__lev, __m, __fmt, ...) \ prlog(__lev, "MFSI %x:%x: " __fmt, __m->chip_id, __m->unit, ##__VA_ARGS__) /* * Use a global FSI lock for now. Beware of re-entrancy * if we ever add support for normal chip XSCOM via FSI, in * which case we'll probably have to consider either per chip * lock (which can have AB->BA deadlock issues) or a re-entrant * global lock or something else. ... */ static struct lock fsi_lock = LOCK_UNLOCKED; /* * OPB accessors */ /* We try up to 1.2ms for an OPB access */ #define MFSI_OPB_MAX_TRIES 1200 static uint64_t mfsi_opb_poll(struct mfsi *mfsi, uint32_t *read_data) { unsigned long retries = MFSI_OPB_MAX_TRIES; uint64_t sval; uint32_t stat; int64_t rc; /* We try again every 10us for a bit more than 1ms */ for (;;) { /* Read OPB status register */ rc = xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_STAT, &sval); if (rc) { /* Do something here ? */ mfsi_log(PR_ERR, mfsi, "XSCOM error %lld read OPB STAT\n", rc); return OPB_ERR_XSCOM_ERR; } mfsi_log(PR_INSANE, mfsi, " STAT=0x%16llx...\n", sval); stat = sval >> 32; /* Complete */ if (!(stat & OPB_STAT_BUSY)) break; if (retries-- == 0) { /* This isn't supposed to happen (HW timeout) */ mfsi_log(PR_ERR, mfsi, "OPB POLL timeout !\n"); return OPB_ERR_TIMEOUT_ERR | (stat & mfsi->err_bits); } time_wait_us(1); } /* Did we have an error ? */ if (stat & mfsi->err_bits) return stat & mfsi->err_bits; if (read_data) { if (!(stat & OPB_STAT_READ_VALID)) { mfsi_log(PR_ERR, mfsi, "Read successful but no data !\n"); /* What do do here ? can it actually happen ? */ sval = 0xffffffff; } *read_data = sval & 0xffffffff; } return 0; } static uint64_t mfsi_opb_read(struct mfsi *mfsi, uint32_t opb_addr, uint32_t *data) { uint64_t opb_cmd = OPB_CMD_READ | OPB_CMD_32BIT; int64_t rc; if (opb_addr > 0x00ffffff) return OPB_ERR_BAD_OPB_ADDR; opb_cmd |= opb_addr; opb_cmd <<= 32; mfsi_log(PR_INSANE, mfsi, "MFSI_OPB_READ: Writing 0x%16llx to XSCOM %x\n", opb_cmd, mfsi->xscom_base); rc = xscom_write(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_CMD, opb_cmd); if (rc) { mfsi_log(PR_ERR, mfsi, "XSCOM error %lld writing OPB CMD\n", rc); return OPB_ERR_XSCOM_ERR; } return mfsi_opb_poll(mfsi, data); } static uint64_t mfsi_opb_write(struct mfsi *mfsi, uint32_t opb_addr, uint32_t data) { uint64_t opb_cmd = OPB_CMD_WRITE | OPB_CMD_32BIT; int64_t rc; if (opb_addr > 0x00ffffff) return OPB_ERR_BAD_OPB_ADDR; opb_cmd |= opb_addr; opb_cmd <<= 32; opb_cmd |= data; mfsi_log(PR_INSANE, mfsi, "MFSI_OPB_WRITE: Writing 0x%16llx to XSCOM %x\n", opb_cmd, mfsi->xscom_base); rc = xscom_write(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_CMD, opb_cmd); if (rc) { mfsi_log(PR_ERR, mfsi, "XSCOM error %lld writing OPB CMD\n", rc); return OPB_ERR_XSCOM_ERR; } return mfsi_opb_poll(mfsi, NULL); } static struct mfsi *mfsi_get(uint32_t chip_id, uint32_t unit) { struct proc_chip *chip = get_chip(chip_id); struct mfsi *mfsi; if (!chip || unit > MFSI_hMFSI0) return NULL; mfsi = &chip->fsi_masters[unit]; if (mfsi->xscom_base == 0) return NULL; return mfsi; } static int64_t mfsi_reset_pib2opb(struct mfsi *mfsi) { uint64_t stat; int64_t rc; rc = xscom_write(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_RESET, (1ul << 63)); if (rc) { mfsi_log(PR_ERR, mfsi, "XSCOM error %lld resetting PIB2OPB\n", rc); return rc; } rc = xscom_write(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_STAT, (1ul << 63)); if (rc) { mfsi_log(PR_ERR, mfsi, "XSCOM error %lld resetting status\n", rc); return rc; } rc = xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_STAT, &stat); if (rc) { mfsi_log(PR_ERR, mfsi, "XSCOM error %lld reading status\n", rc); return rc; } return 0; } static void mfsi_dump_pib2opb_state(struct mfsi *mfsi) { uint64_t val; /* Dump a bunch of registers */ if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_CMD, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB CMD = %016llx\n", val); if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_STAT, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB STAT = %016llx\n", val); if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_LSTAT, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB LSTAT = %016llx\n", val); if (mfsi->unit == MFSI_cMFSI0 || mfsi->unit == MFSI_cMFSI1) { if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_cRSIC, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB cRSIC = %016llx\n", val); if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_cRSIM, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB cRSIM = %016llx\n", val); if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_cRSIS, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB cRSIS = %016llx\n", val); } else if (mfsi->unit == MFSI_hMFSI0) { if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_hRSIC, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB hRSIC = %016llx\n", val); if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_hRSIM, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB hRSIM = %016llx\n", val); if (xscom_read(mfsi->chip_id, mfsi->xscom_base + PIB2OPB_REG_hRSIS, &val)) goto xscom_error; mfsi_log(PR_ERR, mfsi, " PIB2OPB hRSIS = %016llx\n", val); } return; xscom_error: mfsi_log(PR_ERR, mfsi, "XSCOM error reading PIB2OPB registers\n"); } static int64_t mfsi_dump_ctrl_regs(struct mfsi *mfsi) { uint64_t opb_stat; uint32_t i; /* List of registers to dump (from HB) */ static uint32_t dump_regs[] = { MFSI_REG_MATRB0, MFSI_REG_MDTRB0, MFSI_REG_MESRB0, MFSI_REG_MAESP0, MFSI_REG_MAEB, MFSI_REG_MSCSB0, }; static const char * dump_regs_names[] = { "MFSI_REG_MATRB0", "MFSI_REG_MDTRB0", "MFSI_REG_MESRB0", "MFSI_REG_MAESP0", "MFSI_REG_MAEB ", "MFSI_REG_MSCSB0", }; for (i = 0; i < ARRAY_SIZE(dump_regs); i++) { uint32_t val; opb_stat = mfsi_opb_read(mfsi, mfsi->reg_base + dump_regs[i], &val); if (opb_stat) { /* Error on dump, give up */ mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx dumping reg %x\n", opb_stat, dump_regs[i]); return OPAL_HARDWARE; } mfsi_log(PR_ERR, mfsi, " %s = %08x\n", dump_regs_names[i], val); } for (i = 0; i < 8; i++) { uint32_t val; opb_stat = mfsi_opb_read(mfsi, mfsi->reg_base + MFSI_REG_MSTAP(i), &val); if (opb_stat) { /* Error on dump, give up */ mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx dumping reg %x\n", opb_stat, MFSI_REG_MSTAP(i)); return OPAL_HARDWARE; } mfsi_log(PR_ERR, mfsi, " MFSI_REG_MSTAP%d = %08x\n", i, val); } return OPAL_SUCCESS; } static int64_t mfsi_master_cleanup(struct mfsi *mfsi, uint32_t port) { uint64_t opb_stat; uint32_t port_base, compmask, truemask; /* Reset the bridge to clear up the residual errors */ /* bit0 = Bridge: General reset */ opb_stat = mfsi_opb_write(mfsi, mfsi->reg_base + MFSI_REG_MESRB0, 0x80000000u); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx writing reset to MESRB0\n", opb_stat); return OPAL_HARDWARE; } /* Calculate base address of port */ port_base = mfsi->ports_base + port * MFSI_OPB_PORT_STRIDE; /* Perform error reset on Centaur fsi slave: */ /* write 0x4000000 to addr=834 */ opb_stat = mfsi_opb_write(mfsi, port_base + FSI_SLRES, 0x04000000); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx writing reset to FSI slave\n", opb_stat); return OPAL_HARDWARE; } /* Further step is to issue a PIB reset to the FSI2PIB engine * in busy state, i.e. write arbitrary data to 101c * (putcfam 1007) register of the previously failed FSI2PIB * engine on Centaur. * * XXX BenH: Should that be done by the upper FSI XSCOM layer ? */ opb_stat = mfsi_opb_write(mfsi, port_base + FSI2PIB_STATUS, 0xFFFFFFFF); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx clearing FSI2PIB_STATUS\n", opb_stat); return OPAL_HARDWARE; } /* Need to save/restore the true/comp masks or the FSP (PRD ?) will * get annoyed */ opb_stat = mfsi_opb_read(mfsi, port_base + FSI2PIB_COMPMASK, &compmask); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx reading FSI2PIB_COMPMASK\n", opb_stat); return OPAL_HARDWARE; } opb_stat = mfsi_opb_read(mfsi, port_base + FSI2PIB_TRUEMASK, &truemask); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx reading FSI2PIB_TRUEMASK\n", opb_stat); return OPAL_HARDWARE; } /* Then, write arbitrary data to 1018 (putcfam 1006) to * reset any pending FSI2PIB errors. */ opb_stat = mfsi_opb_write(mfsi, port_base + FSI2PIB_RESET, 0xFFFFFFFF); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx writing FSI2PIB_RESET\n", opb_stat); return OPAL_HARDWARE; } /* Restore the true/comp masks */ opb_stat = mfsi_opb_write(mfsi, port_base + FSI2PIB_COMPMASK, compmask); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx writing FSI2PIB_COMPMASK\n", opb_stat); return OPAL_HARDWARE; } opb_stat = mfsi_opb_write(mfsi, port_base + FSI2PIB_TRUEMASK, truemask); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx writing FSI2PIB_TRUEMASK\n", opb_stat); return OPAL_HARDWARE; } return OPAL_SUCCESS; } static int64_t mfsi_analyse_fsi_error(struct mfsi *mfsi) { uint64_t opb_stat; uint32_t mesrb0; /* Most of the code below is adapted from HB. The main difference is * that we don't gard */ /* Read MESRB0 */ opb_stat = mfsi_opb_read(mfsi, mfsi->reg_base + MFSI_REG_MESRB0, &mesrb0); if (opb_stat) { mfsi_log(PR_ERR, mfsi, " OPB stat 0x%016llx reading MESRB0\n", opb_stat); return OPAL_HARDWARE; } mfsi_log(PR_ERR, mfsi, " MESRB0=%08x\n", mesrb0); /* bits 8:15 are internal parity errors in the master */ if (mesrb0 & 0x00FF0000) { mfsi_log(PR_ERR, mfsi, " Master parity error !\n"); } else { /* bits 0:3 are a specific error code */ switch ((mesrb0 & 0xF0000000) >> 28 ) { case 0x1: /* OPB error */ case 0x2: /* Invalid state of OPB state machine */ /* error is inside the OPB logic */ mfsi_log(PR_ERR, mfsi, " OPB logic error !\n"); break; case 0x3: /* Port access error */ /* probably some kind of code collision */ /* could also be something weird in the chip */ mfsi_log(PR_ERR, mfsi, " Port access error !\n"); break; case 0x4: /* ID mismatch */ mfsi_log(PR_ERR, mfsi, " Port ID mismatch !\n"); break; case 0x6: /* port timeout error */ mfsi_log(PR_ERR, mfsi, " Port timeout !\n"); break; case 0x7: /* master timeout error */ mfsi_log(PR_ERR, mfsi, " Master timeout !\n"); break; case 0x9: /* Any error response from Slave */ mfsi_log(PR_ERR, mfsi, " Slave error response !\n"); break; case 0xC: /* bridge parity error */ mfsi_log(PR_ERR, mfsi, " Bridge parity error !\n"); break; case 0xB: /* protocol error */ mfsi_log(PR_ERR, mfsi, " Protocol error !\n"); break; case 0x8: /* master CRC error */ mfsi_log(PR_ERR, mfsi, " Master CRC error !\n"); break; case 0xA: /* Slave CRC error */ mfsi_log(PR_ERR, mfsi, " Slave CRC error !\n"); break; default: mfsi_log(PR_ERR, mfsi, " Unknown error !\n"); break; } } return OPAL_SUCCESS; } static int64_t mfsi_handle_error(struct mfsi *mfsi, uint32_t port, uint64_t opb_stat, uint32_t fsi_addr) { int rc; bool found_root_cause = false; mfsi_log(PR_ERR, mfsi, "Access error on port %d, stat=%012llx\n", port, opb_stat); /* First handle stat codes we synthetized */ if (opb_stat & OPB_ERR_XSCOM_ERR) return OPAL_HARDWARE; if (opb_stat & OPB_ERR_BAD_OPB_ADDR) return OPAL_PARAMETER; /* Dump a bunch of regisers from PIB2OPB and reset it */ mfsi_dump_pib2opb_state(mfsi); /* Reset PIB2OPB */ mfsi_reset_pib2opb(mfsi); /* This one is not supposed to happen but ... */ if (opb_stat & OPB_ERR_TIMEOUT_ERR) return OPAL_HARDWARE; /* Dump some FSI control registers */ rc = mfsi_dump_ctrl_regs(mfsi); /* If that failed, reset PIB2OPB again and return */ if (rc) { mfsi_dump_pib2opb_state(mfsi); mfsi_reset_pib2opb(mfsi); return OPAL_HARDWARE; } /* Now check for known root causes (from HB) */ /* First check if it's a ctrl register access error and we got an OPB NACK, * which means an out of bounds control reg */ if ((opb_stat & OPB_STAT_ERRACK) && ((fsi_addr & ~0x2ffu) == mfsi->reg_base)) { mfsi_log(PR_ERR, mfsi, " Error appears to be out of bounds reg %08x\n", fsi_addr); found_root_cause = true; } /* Else check for other OPB errors */ else if (opb_stat & OPB_STAT_ERR_OPB) { mfsi_log(PR_ERR, mfsi, " Error appears to be an OPB error\n"); found_root_cause = true; } /* Root cause not found, dig into FSI logic */ if (!found_root_cause) { rc = mfsi_analyse_fsi_error(mfsi); if (!rc) { /* If that failed too, reset the PIB2OPB again */ mfsi_reset_pib2opb(mfsi); } } /* Cleanup MFSI master */ mfsi_master_cleanup(mfsi, port); return OPAL_HARDWARE; } int64_t mfsi_read(uint32_t chip, uint32_t unit, uint32_t port, uint32_t fsi_addr, uint32_t *data) { struct mfsi *mfsi = mfsi_get(chip, unit); uint32_t port_addr; uint64_t opb_stat; int64_t rc = OPAL_SUCCESS; if (!mfsi || port > 7) return OPAL_PARAMETER; lock(&fsi_lock); /* Calculate port address */ port_addr = mfsi->ports_base + port * MFSI_OPB_PORT_STRIDE; port_addr += fsi_addr; /* Perform OPB access */ opb_stat = mfsi_opb_read(mfsi, port_addr, data); if (opb_stat) rc = mfsi_handle_error(mfsi, port, opb_stat, port_addr); unlock(&fsi_lock); return rc; } int64_t mfsi_write(uint32_t chip, uint32_t unit, uint32_t port, uint32_t fsi_addr, uint32_t data) { struct mfsi *mfsi = mfsi_get(chip, unit); uint32_t port_addr; uint64_t opb_stat; int64_t rc = OPAL_SUCCESS; if (!mfsi || port > 7) return OPAL_PARAMETER; lock(&fsi_lock); /* Calculate port address */ port_addr = mfsi->ports_base + port * MFSI_OPB_PORT_STRIDE; port_addr += fsi_addr; /* Perform OPB access */ opb_stat = mfsi_opb_write(mfsi, port_addr, data); if (opb_stat) rc = mfsi_handle_error(mfsi, port, opb_stat, port_addr); unlock(&fsi_lock); return rc; } static void mfsi_add(struct proc_chip *chip, struct mfsi *mfsi, uint32_t unit) { mfsi->chip_id = chip->id; mfsi->unit = unit; /* We hard code everything for now */ switch(unit) { case MFSI_cMFSI0: mfsi->xscom_base = PIB2OPB_MFSI0_ADDR; mfsi->ports_base = cMFSI_OPB_PORTS_BASE; mfsi->reg_base = cMFSI_OPB_REG_BASE; mfsi->err_bits = OPB_STAT_ERR_BASE | OPB_STAT_ERR_CMFSI; break; case MFSI_cMFSI1: mfsi->xscom_base = PIB2OPB_MFSI1_ADDR; mfsi->ports_base = cMFSI_OPB_PORTS_BASE; mfsi->reg_base = cMFSI_OPB_REG_BASE; mfsi->err_bits = OPB_STAT_ERR_BASE | OPB_STAT_ERR_CMFSI; break; case MFSI_hMFSI0: mfsi->xscom_base = PIB2OPB_MFSI0_ADDR; mfsi->ports_base = hMFSI_OPB_PORTS_BASE; mfsi->reg_base = hMFSI_OPB_REG_BASE; mfsi->err_bits = OPB_STAT_ERR_BASE | OPB_STAT_ERR_HMFSI; break; default: /* ??? */ return; } /* Hardware Bug HW222712 on Murano DD1.0 causes the * any_error bit to be un-clearable so we just * have to ignore it. Additionally, HostBoot applies * this to Venice too, though the comment there claims * this is a Simics workaround. * * The doc says that bit can be safely ignored, so let's * just not bother and always take it out. */ /* 16: cMFSI any-master-error */ /* 24: hMFSI any-master-error */ mfsi->err_bits &= 0xFFFF7F7F; mfsi_log(PR_INFO, mfsi, "Initialized\n"); } void mfsi_init(void) { struct proc_chip *chip; for_each_chip(chip) { chip->fsi_masters = zalloc(sizeof(struct mfsi) * 3); mfsi_add(chip, &chip->fsi_masters[MFSI_cMFSI0], MFSI_cMFSI0); mfsi_add(chip, &chip->fsi_masters[MFSI_hMFSI0], MFSI_hMFSI0); mfsi_add(chip, &chip->fsi_masters[MFSI_cMFSI1], MFSI_cMFSI1); } } skiboot-skiboot-5.1.13/hw/fsp/000077500000000000000000000000001265204436200161355ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hw/fsp/Makefile.inc000066400000000000000000000006451265204436200203520ustar00rootroot00000000000000SUBDIRS += hw/fsp FSP_OBJS = fsp.o fsp-console.o fsp-rtc.o fsp-nvram.o fsp-sysparam.o FSP_OBJS += fsp-surveillance.o fsp-codeupdate.o fsp-sensor.o FSP_OBJS += fsp-diag.o fsp-leds.o fsp-mem-err.o fsp-op-panel.o FSP_OBJS += fsp-elog-read.o fsp-elog-write.o fsp-epow.o fsp-dpo.o FSP_OBJS += fsp-dump.o fsp-mdst-table.o fsp-chiptod.o fsp-ipmi.o FSP_OBJS += fsp-attn.o FSP = hw/fsp/built-in.o $(FSP): $(FSP_OBJS:%=hw/fsp/%) skiboot-skiboot-5.1.13/hw/fsp/fsp-attn.c000066400000000000000000000100711265204436200200340ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #define TI_CMD_VALID 0x1 /* Command valid */ #define TI_CMD 0xA1 /* Terminate Immediate command */ #define TI_DATA_LEN 0x0400 /* Data length */ /* Controls dump actions * - Non-destructive hardware dump (bit 0) * - memory dump (bit 1) * - Destructive hardware dump (bit 2) */ #define TI_DMP_CTL 0x6 /* Dump type * 0 - Abbreviated hardware dump * 1 - Complete hardware dump * 2 - No hardware dump */ #define TI_DUMP_TYPE 0x1 #define TI_FORMAT 0x02 /* SRC format */ #define TI_SRC_FLAGS 0x0 /* SRC flags */ #define TI_ASCII_WORDS 0x0 /* Number of ASCII words */ /* HEX words: Number of hex words of data added, up to 8 total * this value is one more. */ #define TI_HEX_WORDS 0x02 /* SRC length : 8 byte header, 8 hex words of data and * 32 byte ASCII SRC */ #define TI_SRC_LEN 0x48 static struct ti_attn *ti_attn; /* Initialises SP attention area with default values */ static void init_sp_attn_area(void) { /* Already done */ if (ti_attn) return; /* We are just enabling attention area 1 */ ti_attn = (struct ti_attn *)&cpu_ctl_sp_attn_area1; /* Attention component checks Attn area 2 first, if its NULL * it will check for Attn area 1. */ memset(&cpu_ctl_sp_attn_area1, 0, sizeof(struct sp_attn_area)); memset(&cpu_ctl_sp_attn_area2, 0, sizeof(struct sp_attn_area)); ti_attn->cmd_valid = TI_CMD_VALID; ti_attn->attn_cmd = TI_CMD; ti_attn->data_len = CPU_TO_BE16(TI_DATA_LEN); /* Dump control byte not used as of now */ ti_attn->dump_ctrl =TI_DMP_CTL; ti_attn->dump_type = CPU_TO_BE16(TI_DUMP_TYPE); /* SRC format */ ti_attn->src_fmt = TI_FORMAT; /* SRC flags */ ti_attn->src_flags = TI_SRC_FLAGS; /* #ASCII words */ ti_attn->ascii_cnt = TI_ASCII_WORDS; /* #HEX words */ ti_attn->hex_cnt = TI_HEX_WORDS; ti_attn->src_len = CPU_TO_BE16(TI_SRC_LEN); snprintf(ti_attn->src, SRC_LEN, "%X", generate_src_from_comp(OPAL_RC_ATTN)); } /* Updates src in sp attention area */ static void update_sp_attn_area(const char *msg) { #define STACK_BUF_ENTRIES 20 struct bt_entry bt_buf[STACK_BUF_ENTRIES]; unsigned int ent_cnt, len; if (!fsp_present()) return; /* This can be called early */ if (!ti_attn) init_sp_attn_area(); ti_attn->src_word[0] = (uint32_t)((uint64_t)__builtin_return_address(0) & 0xffffffff); snprintf(ti_attn->msg.version, VERSION_LEN, "%s", version); ent_cnt = STACK_BUF_ENTRIES; __backtrace(bt_buf, &ent_cnt); len = BT_FRAME_LEN; __print_backtrace(mfspr(SPR_PIR), bt_buf, ent_cnt, ti_attn->msg.bt_buf, &len, false); snprintf(ti_attn->msg.file_info, FILE_INFO_LEN, "%s", msg); ti_attn->msg_len = VERSION_LEN + BT_FRAME_LEN + strlen(ti_attn->msg.file_info); } void __attribute__((noreturn)) ibm_fsp_terminate(const char *msg) { unsigned long hid0; /* Update SP attention area */ update_sp_attn_area(msg); /* Update op panel op_display */ op_display(OP_FATAL, OP_MOD_CORE, 0x6666); /* XXX FIXME: We should fsp_poll for a while to ensure any pending * console writes have made it out, but until we have decent PSI * link handling we must not do it forever. Polling can prevent the * FSP from bringing the PSI link up and it can get stuck in a * reboot loop. */ hid0 = mfspr(SPR_HID0); hid0 |= SPR_HID0_ENABLE_ATTN; set_hid0(hid0); trigger_attn(); for (;;) ; } /* Intialises SP attention area */ void fsp_attn_init(void) { if (!fsp_present()) return; init_sp_attn_area(); } skiboot-skiboot-5.1.13/hw/fsp/fsp-chiptod.c000066400000000000000000000041431265204436200205230ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include /* Response status for fsp command 0xE6, s/c 0x06 (Enable/Disable Topology) */ #define FSP_STATUS_TOPO_IN_USE 0xb8 /* topology is in use */ static bool fsp_chiptod_update_topology(uint32_t cmd_sub_mod, struct fsp_msg *msg) { struct fsp_msg *resp; enum chiptod_topology topo; bool action; uint8_t status = 0; switch (cmd_sub_mod) { case FSP_CMD_TOPO_ENABLE_DISABLE: /* * Action Values: 0x00 = Disable, 0x01 = Enable * Topology Values: 0x00 = Primary, 0x01 = Secondary */ action = !!msg->data.bytes[2]; topo = msg->data.bytes[3]; prlog(PR_DEBUG, "CHIPTOD: Topology update event\n"); prlog(PR_DEBUG, "CHIPTOD: Action = %s, Topology = %s\n", action ? "Enable" : "Disable", topo ? "Secondary" : "Primary"); if (!chiptod_adjust_topology(topo, action)) status = FSP_STATUS_TOPO_IN_USE; else status = 0x00; resp = fsp_mkmsg(FSP_RSP_TOPO_ENABLE_DISABLE | status, 0); if (!resp) { prerror("CHIPTOD: Response allocation failed\n"); return false; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("CHIPTOD: Failed to queue response msg\n"); } return true; default: prlog(PR_DEBUG, "CHIPTOD: Unhandled sub cmd: %06x\n", cmd_sub_mod); break; } return false; } static struct fsp_client fsp_chiptod_client = { .message = fsp_chiptod_update_topology, }; void fsp_chiptod_init(void) { /* Register for Class E6 (HW maintanance) */ fsp_register_client(&fsp_chiptod_client, FSP_MCLASS_HW_MAINT); } skiboot-skiboot-5.1.13/hw/fsp/fsp-codeupdate.c000066400000000000000000001002661265204436200212110ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "fsp-codeupdate.h" enum flash_state { FLASH_STATE_ABSENT, FLASH_STATE_INVALID, /* IPL side marker lid is invalid */ FLASH_STATE_READING, FLASH_STATE_READ, FLASH_STATE_ABORT, }; enum lid_fetch_side { FETCH_T_SIDE_ONLY, FETCH_P_SIDE_ONLY, FETCH_BOTH_SIDE, }; static enum flash_state flash_state = FLASH_STATE_INVALID; static enum lid_fetch_side lid_fetch_side = FETCH_BOTH_SIDE; /* Image buffers */ static struct opal_sg_list *image_data; static uint32_t tce_start; static void *lid_data; static char validate_buf[VALIDATE_BUF_SIZE]; /* TCE buffer lock */ static struct lock flash_lock = LOCK_UNLOCKED; /* FW VPD data */ static struct fw_image_vpd fw_vpd[2]; /* Code update related sys parameters */ static uint32_t ipl_side; static uint32_t hmc_managed; static uint32_t update_policy; static uint32_t in_flight_params; /* If non-NULL, this gets called just before rebooting */ int (*fsp_flash_term_hook)(void); DEFINE_LOG_ENTRY(OPAL_RC_CU_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_CU_FLASH, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_CU_SG_LIST, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_CU_COMMIT, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_CU_MSG, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_CU_NOTIFY, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_CU_MARKER_LID, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); static inline void code_update_tce_map(uint32_t tce_offset, void *buffer, uint32_t size) { uint32_t tlen = ALIGN_UP(size, TCE_PSIZE); fsp_tce_map(PSI_DMA_CODE_UPD + tce_offset, buffer, tlen); } static inline void code_update_tce_unmap(uint32_t size) { fsp_tce_unmap(PSI_DMA_CODE_UPD, size); } static inline void set_def_fw_version(uint32_t side) { strncpy(fw_vpd[side].mi_keyword, FW_VERSION_UNKNOWN, MI_KEYWORD_SIZE); strncpy(fw_vpd[side].ext_fw_id, FW_VERSION_UNKNOWN, ML_KEYWORD_SIZE); } /* * Get IPL side */ static void get_ipl_side(void) { struct dt_node *iplp; const char *side = NULL; iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params"); if (iplp) side = dt_prop_get_def(iplp, "cec-ipl-side", NULL); prlog(PR_NOTICE, "CUPD: IPL SIDE = %s\n", side); if (!side || !strcmp(side, "temp")) ipl_side = FW_IPL_SIDE_TEMP; else ipl_side = FW_IPL_SIDE_PERM; } /* * Helper routines to retrieve code update related * system parameters from FSP. */ static void inc_in_flight_param(void) { lock(&flash_lock); in_flight_params++; unlock(&flash_lock); } static void dec_in_flight_param(void) { lock(&flash_lock); assert(in_flight_params > 0); in_flight_params--; unlock(&flash_lock); } static void got_code_update_policy(uint32_t param_id __unused, int err_len, void *data __unused) { if (err_len != 4) { log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Error " "retrieving code update policy: %d\n", err_len); } else prlog(PR_NOTICE, "CUPD: Code update policy from FSP: %d\n", update_policy); dec_in_flight_param(); } static void get_code_update_policy(void) { int rc; inc_in_flight_param(); rc = fsp_get_sys_param(SYS_PARAM_FLASH_POLICY, &update_policy, 4, got_code_update_policy, NULL); if (rc) { log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Error %d queueing param request\n", rc); dec_in_flight_param(); } } static void got_platform_hmc_managed(uint32_t param_id __unused, int err_len, void *data __unused) { if (err_len != 4) { log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Error " "retrieving hmc managed status: %d\n", err_len); } else prlog(PR_NOTICE, "CUPD: HMC managed status from FSP: %d\n", hmc_managed); dec_in_flight_param(); } static void get_platform_hmc_managed(void) { int rc; inc_in_flight_param(); rc = fsp_get_sys_param(SYS_PARAM_HMC_MANAGED, &hmc_managed, 4, got_platform_hmc_managed, NULL); if (rc) { log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Error %d queueing param request\n", rc); dec_in_flight_param(); } } static int64_t code_update_check_state(void) { switch(flash_state) { case FLASH_STATE_ABSENT: return OPAL_HARDWARE; case FLASH_STATE_INVALID: case FLASH_STATE_ABORT: return OPAL_INTERNAL_ERROR; case FLASH_STATE_READING: return OPAL_BUSY; default: break; } return OPAL_SUCCESS; } /* * Get common marker LID additional data section */ static void *get_adf_sec_data(struct com_marker_adf_sec *adf_sec, uint32_t name) { struct com_marker_adf_header *adf_header; int i; adf_header = (void *)adf_sec->adf_data; for (i = 0; i < be32_to_cpu(adf_sec->adf_cnt); i++) { if (be32_to_cpu(adf_header->name) == name) return adf_header; adf_header = (void *)adf_header + be32_to_cpu(adf_header->size); } return NULL; } /* * Parse common marker LID to get FW version details * * Note: * At present, we are parsing "Service Pack Nomenclature ADF" * section only. If we are adding FW IP support, then we have * to parse "Firmware IP Protection ADF" as well. */ static void parse_marker_lid(uint32_t side) { struct com_marker_header *header; struct com_marker_mi_section *mi_sec; struct com_marker_adf_sec *adf_sec; struct com_marker_adf_sp *adf_sp; header = (void *)lid_data; /* Get MI details */ mi_sec = (void *)header + be32_to_cpu(header->MI_offset); /* * If Marker LID is invalid, then FSP will return a Marker * LID with ASCII zeros for the entire MI keyword. */ if (mi_sec->mi_keyword[0] == '0') return; strncpy(fw_vpd[side].mi_keyword, mi_sec->mi_keyword, MI_KEYWORD_SIZE); fw_vpd[side].mi_keyword[MI_KEYWORD_SIZE - 1] = '\0'; prlog(PR_NOTICE, "CUPD: %s side MI Keyword = %s\n", side == 0x00 ? "P" : "T", fw_vpd[side].mi_keyword); /* Get ML details */ adf_sec = (void *)header + be32_to_cpu(mi_sec->adf_offset); adf_sp = get_adf_sec_data(adf_sec, ADF_NAME_SP); if (!adf_sp) return; strncpy(fw_vpd[side].ext_fw_id, (void *)adf_sp + be32_to_cpu(adf_sp->sp_name_offset), ML_KEYWORD_SIZE); fw_vpd[side].ext_fw_id[ML_KEYWORD_SIZE - 1] = '\0'; prlog(PR_NOTICE, "CUPD: %s side ML Keyword = %s\n", side == 0x00 ? "P" : "T", fw_vpd[side].ext_fw_id); } static void validate_com_marker_lid(void) { if (!strncmp(fw_vpd[ipl_side].mi_keyword, FW_VERSION_UNKNOWN, sizeof(FW_VERSION_UNKNOWN))) { log_simple_error(&e_info(OPAL_RC_CU_MARKER_LID), "CUPD: IPL side Marker LID is not valid\n"); flash_state = FLASH_STATE_INVALID; return; } flash_state = FLASH_STATE_READ; } static void fetch_lid_data_complete(struct fsp_msg *msg) { void *buffer; size_t length, chunk; uint32_t lid_id, offset; uint16_t id; uint8_t flags, status; int rc; status = (msg->resp->word1 >> 8) & 0xff; flags = (msg->data.words[0] >> 16) & 0xff; id = msg->data.words[0] & 0xffff; lid_id = msg->data.words[1]; offset = msg->resp->data.words[1]; length = msg->resp->data.words[2]; prlog(PR_NOTICE, "CUPD: Marker LID id : size : status = " "0x%x : 0x%x : 0x%x\n", msg->data.words[1], msg->resp->data.words[2], status); fsp_freemsg(msg); switch (status) { case FSP_STATUS_SUCCESS: /* Read complete, parse VPD */ parse_marker_lid(lid_id == P_COM_MARKER_LID_ID ? 0 : 1); break; case FSP_STATUS_MORE_DATA: /* More data left */ offset += length; chunk = MARKER_LID_SIZE - offset; if (chunk > 0) { buffer = (void *)PSI_DMA_CODE_UPD + offset; rc = fsp_fetch_data_queue(flags, id, lid_id, offset, buffer, &chunk, fetch_lid_data_complete); /* If queue msg fails, then continue with marker LID * validation hoping that we have at least boot side * information. */ if (rc == OPAL_SUCCESS) return; } break; default: /* Fetch LID call failed */ break; } /* If required, fetch T side marker LID */ if (lid_id == P_COM_MARKER_LID_ID && lid_fetch_side == FETCH_BOTH_SIDE) { length = MARKER_LID_SIZE; rc = fsp_fetch_data_queue(flags, id, T_COM_MARKER_LID_ID, 0, (void *)PSI_DMA_CODE_UPD, &length, fetch_lid_data_complete); /* If queue msg fails, then continue with marker LID * validation hoping that we have at least boot side * information. */ if (rc == OPAL_SUCCESS) return; } lock(&flash_lock); /* Validate marker LID data */ validate_com_marker_lid(); /* TCE unmap */ code_update_tce_unmap(MARKER_LID_SIZE); unlock(&flash_lock); } static void fetch_com_marker_lid(void) { size_t length = MARKER_LID_SIZE; uint32_t lid_id; int rc; /* Read in progress? */ rc = code_update_check_state(); if (rc == OPAL_HARDWARE || rc == OPAL_BUSY) return; if (lid_fetch_side == FETCH_T_SIDE_ONLY) { lid_id = T_COM_MARKER_LID_ID; set_def_fw_version(FW_IPL_SIDE_TEMP); } else if (lid_fetch_side == FETCH_P_SIDE_ONLY) { lid_id = P_COM_MARKER_LID_ID; set_def_fw_version(FW_IPL_SIDE_PERM); } else { lid_id = P_COM_MARKER_LID_ID; set_def_fw_version(FW_IPL_SIDE_PERM); set_def_fw_version(FW_IPL_SIDE_TEMP); } code_update_tce_map(0, lid_data, length); rc = fsp_fetch_data_queue(0x00, 0x05, lid_id, 0, (void *)PSI_DMA_CODE_UPD, &length, fetch_lid_data_complete); if (!rc) flash_state = FLASH_STATE_READING; else flash_state = FLASH_STATE_INVALID; } /* * Add MI and ML keyword details into DT */ #define FW_VER_SIZE 64 static void add_opal_firmware_version(void) { struct dt_node *dt_fw; char buffer[FW_VER_SIZE]; int offset; dt_fw = dt_find_by_path(dt_root, "ibm,opal/firmware"); if (!dt_fw) return; /* MI version */ offset = snprintf(buffer, FW_VER_SIZE, "MI %s %s", fw_vpd[FW_IPL_SIDE_TEMP].mi_keyword, fw_vpd[FW_IPL_SIDE_PERM].mi_keyword); if (ipl_side == FW_IPL_SIDE_TEMP) snprintf(buffer + offset, FW_VER_SIZE - offset, " %s", fw_vpd[FW_IPL_SIDE_TEMP].mi_keyword); else snprintf(buffer + offset, FW_VER_SIZE - offset, " %s", fw_vpd[FW_IPL_SIDE_PERM].mi_keyword); dt_add_property(dt_fw, "mi-version", buffer, strlen(buffer)); /* ML version */ offset = snprintf(buffer, FW_VER_SIZE, "ML %s %s", fw_vpd[FW_IPL_SIDE_TEMP].ext_fw_id, fw_vpd[FW_IPL_SIDE_PERM].ext_fw_id); if (ipl_side == FW_IPL_SIDE_TEMP) snprintf(buffer + offset, FW_VER_SIZE - offset, " %s", fw_vpd[FW_IPL_SIDE_TEMP].ext_fw_id); else snprintf(buffer + offset, FW_VER_SIZE - offset, " %s", fw_vpd[FW_IPL_SIDE_PERM].ext_fw_id); dt_add_property(dt_fw, "ml-version", buffer, strlen(buffer)); } /* * This is called right before starting the payload (Linux) to * ensure the common marker LID read and parsing has happened * before we transfer control. */ void fsp_code_update_wait_vpd(bool is_boot) { int waited = 0; if (!fsp_present()) return; prlog(PR_NOTICE, "CUPD: Waiting read marker LID" " and in flight parsm completion...\n"); lock(&flash_lock); while(true) { if (!(flash_state == FLASH_STATE_READING || in_flight_params)) break; unlock(&flash_lock); time_wait_ms(5); waited+=5; lock(&flash_lock); } unlock(&flash_lock); if (waited) prlog(PR_DEBUG, "CUPD: fsp_code_update_wait_vpd %d\n", waited); if (is_boot) add_opal_firmware_version(); } static int code_update_start(void) { struct fsp_msg *msg; int rc; uint16_t comp = 0x00; /* All components */ uint8_t side = OPAL_COMMIT_TMP_SIDE; /* Temporary side */ msg = fsp_mkmsg(FSP_CMD_FLASH_START, 1, side << 16 | comp); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: CMD_FLASH_START message allocation failed !\n"); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(msg, false)) { fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } rc = (msg->resp->word1 >> 8) & 0xff; fsp_freemsg(msg); return rc; } static int code_update_write_lid(uint32_t lid_id, uint32_t size) { struct fsp_msg *msg; int rc, n_pairs = 1; msg = fsp_mkmsg(FSP_CMD_FLASH_WRITE, 5, lid_id, n_pairs, 0, tce_start, size); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: CMD_FLASH_WRITE message allocation failed !\n"); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(msg, false)) { fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } rc = (msg->resp->word1 >> 8) & 0xff; fsp_freemsg(msg); return rc; } static int code_update_del_lid(uint32_t lid_id) { struct fsp_msg *msg; int rc; msg = fsp_mkmsg(FSP_CMD_FLASH_DEL, 1, lid_id); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: CMD_FLASH_DEL message allocation failed !\n"); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(msg, false)) { fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } rc = (msg->resp->word1 >> 8) & 0xff; fsp_freemsg(msg); return rc; } static int code_update_complete(uint32_t cmd) { struct fsp_msg *msg; int rc; msg = fsp_mkmsg(cmd, 0); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: CUPD COMPLETE message allocation failed !\n"); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(msg, false)) { fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } rc = (msg->resp->word1 >> 8) & 0xff; fsp_freemsg(msg); return rc; } static int code_update_swap_side(void) { struct fsp_msg *msg; int rc; msg = fsp_mkmsg(FSP_CMD_FLASH_SWAP, 0); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: CMD_FLASH_SWAP message allocation failed !\n"); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(msg, false)) { fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } rc = (msg->resp->word1 >> 8) & 0xff; fsp_freemsg(msg); return rc; } static int code_update_set_ipl_side(void) { struct fsp_msg *msg; uint8_t side = FW_IPL_SIDE_TEMP; /* Next IPL side */ int rc; msg = fsp_mkmsg(FSP_CMD_SET_IPL_SIDE, 1, side << 16); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: CMD_SET_IPL_SIDE message allocation failed!\n"); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(msg, false)) { fsp_freemsg(msg); log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: Setting next IPL side failed!\n"); return OPAL_INTERNAL_ERROR; } rc = (msg->resp->word1 >> 8) & 0xff; fsp_freemsg(msg); return rc; } static void code_update_commit_complete(struct fsp_msg *msg) { int rc; uint8_t type; rc = (msg->resp->word1 >> 8) & 0xff; type = (msg->word1 >> 8) & 0xff; fsp_freemsg(msg); if (rc) { log_simple_error(&e_info(OPAL_RC_CU_COMMIT), "CUPD: Code update commit failed, err 0x%x\n", rc); return; } /* Reset cached VPD data */ lock(&flash_lock); /* Find commit type */ if (type == 0x01) { lid_fetch_side = FETCH_P_SIDE_ONLY; } else if (type == 0x02) lid_fetch_side = FETCH_T_SIDE_ONLY; else lid_fetch_side = FETCH_BOTH_SIDE; fetch_com_marker_lid(); unlock(&flash_lock); } static int code_update_commit(uint32_t cmd) { struct fsp_msg *msg; msg = fsp_mkmsg(cmd, 0); if (!msg) { log_simple_error(&e_info(OPAL_RC_CU_MSG), "CUPD: COMMIT message allocation failed !\n"); return OPAL_INTERNAL_ERROR; } if (fsp_queue_msg(msg, code_update_commit_complete)) { log_simple_error(&e_info(OPAL_RC_CU_COMMIT), "CUPD: Failed to queue code update commit message\n"); fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } /* * Inband code update is allowed? */ static int64_t validate_inband_policy(void) { /* Quirk: * If the code update policy is out-of-band, but the system * is not HMC-managed, then inband update is allowed. */ if (hmc_managed != PLATFORM_HMC_MANAGED) return 0; if (update_policy == INBAND_UPDATE_ALLOWED) return 0; return -1; } /* * Validate magic Number */ static int64_t validate_magic_num(uint16_t magic) { if (magic != IMAGE_MAGIC_NUMBER) return -1; return 0; } /* * Compare MI keyword to make sure candidate image * is valid for this platform. */ static int64_t validate_image_version(struct update_image_header *header, uint32_t *result) { struct fw_image_vpd vpd; int t_valid = 0, p_valid = 0, cton_ver = -1, ptot_ver = -1; /* Valid flash image level? */ if (strncmp(fw_vpd[0].mi_keyword, FW_VERSION_UNKNOWN, sizeof(FW_VERSION_UNKNOWN)) != 0) p_valid = 1; if (strncmp(fw_vpd[1].mi_keyword, FW_VERSION_UNKNOWN, sizeof(FW_VERSION_UNKNOWN)) != 0) t_valid = 1; /* Validate with IPL side image */ vpd = fw_vpd[ipl_side]; /* Validate platform identifier (first two char of MI keyword) */ if (strncmp(vpd.mi_keyword, header->mi_keyword_data, 2) != 0) { *result = VALIDATE_INVALID_IMG; return OPAL_SUCCESS; } /* Don't flash different FW series (like P7 image on P8) */ if (vpd.mi_keyword[2] != header->mi_keyword_data[2]) { *result = VALIDATE_INVALID_IMG; return OPAL_SUCCESS; } /* Get current to new version difference */ cton_ver = strncmp(vpd.mi_keyword + 3, header->mi_keyword_data + 3, 6); /* Get P to T version difference */ if (t_valid && p_valid) ptot_ver = strncmp(fw_vpd[0].mi_keyword + 3, fw_vpd[1].mi_keyword + 3, 6); /* Update validation result */ if (ipl_side == FW_IPL_SIDE_TEMP) { if (!ptot_ver && cton_ver > 0) /* downgrade T side */ *result = VALIDATE_TMP_UPDATE_DL; else if (!ptot_ver && cton_ver <= 0) /* upgrade T side */ *result = VALIDATE_TMP_UPDATE; else if (cton_ver > 0) /* Implied commit & downgrade T side */ *result = VALIDATE_TMP_COMMIT_DL; else /* Implied commit & upgrade T side */ *result = VALIDATE_TMP_COMMIT; } else { if (!t_valid) /* Current unknown */ *result = VALIDATE_CUR_UNKNOWN; else if (cton_ver > 0) /* downgrade FW version */ *result = VALIDATE_TMP_UPDATE_DL; else /* upgrade FW version */ *result = VALIDATE_TMP_UPDATE; } return OPAL_SUCCESS; } /* * Validate candidate image */ static int validate_candidate_image(uint64_t buffer, uint32_t size, uint32_t *result) { struct update_image_header *header; int rc = OPAL_PARAMETER; if (size < VALIDATE_BUF_SIZE) goto out; rc = code_update_check_state(); if (rc != OPAL_SUCCESS) goto out; if (validate_inband_policy() != 0) { *result = VALIDATE_FLASH_AUTH; rc = OPAL_SUCCESS; goto out; } memcpy(validate_buf, (void *)buffer, VALIDATE_BUF_SIZE); header = (struct update_image_header *)validate_buf; if (validate_magic_num(be32_to_cpu(header->magic)) != 0) { *result = VALIDATE_INVALID_IMG; rc = OPAL_SUCCESS; goto out; } rc = validate_image_version(header, result); out: return rc; } static int validate_out_buf_mi_data(void *buffer, int offset, uint32_t result) { struct update_image_header *header = (void *)validate_buf; /* Current T & P side MI data */ offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, "MI %s %s\n", fw_vpd[1].mi_keyword, fw_vpd[0].mi_keyword); /* New T & P side MI data */ offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, "MI %s", header->mi_keyword_data); if (result == VALIDATE_TMP_COMMIT_DL || result == VALIDATE_TMP_COMMIT) offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, " %s\n", fw_vpd[1].mi_keyword); else offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, " %s\n", fw_vpd[0].mi_keyword); return offset; } static int validate_out_buf_ml_data(void *buffer, int offset, uint32_t result) { struct update_image_header *header = (void *)validate_buf; /* Candidate image ML data */ char *ext_fw_id = (void *)header->data; /* Current T & P side ML data */ offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, "ML %s %s\n", fw_vpd[1].ext_fw_id, fw_vpd[0].ext_fw_id); /* New T & P side ML data */ offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, "ML %s", ext_fw_id); if (result == VALIDATE_TMP_COMMIT_DL || result == VALIDATE_TMP_COMMIT) offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, " %s\n", fw_vpd[1].ext_fw_id); else offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, " %s\n", fw_vpd[0].ext_fw_id); return offset; } /* * Copy LID data to TCE buffer */ static int get_lid_data(struct opal_sg_list *list, int lid_size, int lid_offset) { struct opal_sg_list *sg; struct opal_sg_entry *entry; int length, num_entries, i, buf_pos = 0; int map_act, map_size; bool last = false; /* Reset TCE start address */ tce_start = 0; for (sg = list; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { length = (be64_to_cpu(sg->length) & ~(SG_LIST_VERSION << 56)) - 16; num_entries = length / sizeof(struct opal_sg_entry); if (num_entries <= 0) return -1; for (i = 0; i < num_entries; i++) { entry = &sg->entry[i]; /* * Continue until we get data block which * contains LID data */ if (lid_offset > be64_to_cpu(entry->length)) { lid_offset -= be64_to_cpu(entry->length); continue; } /* * SG list entry size can be more than 4k. * Map only required pages, instead of * mapping entire entry. */ map_act = be64_to_cpu(entry->length); map_size = be64_to_cpu(entry->length); /* First TCE mapping */ if (!tce_start) { tce_start = PSI_DMA_CODE_UPD + (lid_offset & 0xfff); map_act = be64_to_cpu(entry->length) - lid_offset; lid_offset &= ~0xfff; map_size = be64_to_cpu(entry->length) - lid_offset; } /* Check pending LID size to map */ if (lid_size <= map_act) { /* (map_size - map_act) gives page * start to tce offset difference. * This is required when LID size * is <= 4k. */ map_size = (map_size - map_act) + lid_size; last = true; } /* Ajust remaining size to map */ lid_size -= map_act; /* TCE mapping */ code_update_tce_map(buf_pos, (void*)(be64_to_cpu(entry->data) + lid_offset), map_size); buf_pos += map_size; /* Reset LID offset count */ lid_offset = 0; if (last) return OPAL_SUCCESS; } } /* outer loop */ return -1; } /* * If IPL side is T, then swap P & T sides to add * new fix to T side. */ static int validate_ipl_side(void) { if (ipl_side == FW_IPL_SIDE_PERM) return 0; return code_update_swap_side(); } static int64_t fsp_opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result) { int64_t rc = 0; int offset; lock(&flash_lock); rc = validate_candidate_image(buffer, *size, result); /* Fill output buffer * * Format: * MIcurrent-T-imagecurrent-P-image<0x0A> * MInew-T-imagenew-P-image<0x0A> * MLcurrent-T-imagecurrent-P-image<0x0A> * MLnew-T-imagenew-P-image<0x0A> */ if (!rc && (*result != VALIDATE_FLASH_AUTH && *result != VALIDATE_INVALID_IMG)) { /* Clear output buffer */ memset((void *)buffer, 0, VALIDATE_BUF_SIZE); offset = validate_out_buf_mi_data((void *)buffer, 0, *result); offset += validate_out_buf_ml_data((void *)buffer, offset, *result); *size = offset; } unlock(&flash_lock); return rc; } /* Commit/Reject T side image */ static int64_t fsp_opal_manage_flash(uint8_t op) { uint32_t cmd; int rc; lock(&flash_lock); rc = code_update_check_state(); unlock(&flash_lock); if (rc != OPAL_SUCCESS) return rc; if (op != OPAL_REJECT_TMP_SIDE && op != OPAL_COMMIT_TMP_SIDE) return OPAL_PARAMETER; if ((op == OPAL_COMMIT_TMP_SIDE && ipl_side == FW_IPL_SIDE_PERM) || (op == OPAL_REJECT_TMP_SIDE && ipl_side == FW_IPL_SIDE_TEMP)) return OPAL_ACTIVE_SIDE_ERR; if (op == OPAL_COMMIT_TMP_SIDE) cmd = FSP_CMD_FLASH_NORMAL; else cmd = FSP_CMD_FLASH_REMOVE; return code_update_commit(cmd); } static int fsp_flash_firmware(void) { struct update_image_header *header; struct lid_index_entry *idx_entry; struct opal_sg_list *list; struct opal_sg_entry *entry; int rc, i; /* Make sure no outstanding LID read is in progress */ rc = code_update_check_state(); if (rc == OPAL_BUSY) fsp_code_update_wait_vpd(false); /* Get LID Index */ list = image_data; if (!list) goto out; entry = &list->entry[0]; header = (struct update_image_header *)be64_to_cpu(entry->data); idx_entry = (void *)header + be16_to_cpu(header->lid_index_offset); /* FIXME: * At present we depend on FSP to validate CRC for * individual LIDs. Calculate and validate individual * LID CRC here. */ if (validate_ipl_side() != 0) { log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " "Rename (Swap T and P) failed!\n"); goto out; } /* Set next IPL side */ if (code_update_set_ipl_side() != 0) { log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " "Setting next IPL side failed!\n"); goto out; } /* Start code update process */ if (code_update_start() != 0) { log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " "Code update start failed!\n"); goto out; } /* * Delete T side LIDs before writing. * * Note: * - Applicable for FWv >= 760. * - Current Code Update design is to ignore * any delete lid failure, and continue with * the update. */ rc = code_update_del_lid(DEL_UPD_SIDE_LIDS); if (rc) prlog(PR_TRACE, "CUPD: Failed to delete LIDs (%d). This is okay, continuing..", rc); for (i = 0; i < be16_to_cpu(header->number_lids); i++) { if (be32_to_cpu(idx_entry->size) > LID_MAX_SIZE) { log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: LID" " (0x%x) size 0x%x is > max LID size (0x%x).\n", be32_to_cpu(idx_entry->id), be32_to_cpu(idx_entry->size), LID_MAX_SIZE); goto abort_update; } rc = get_lid_data(list, be32_to_cpu(idx_entry->size), be32_to_cpu(idx_entry->offset)); if (rc) { log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " "Failed to parse LID from firmware image." " (rc : %d).\n", rc); goto abort_update; } rc = code_update_write_lid(be32_to_cpu(idx_entry->id), be32_to_cpu(idx_entry->size)); if (rc) { log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " "Failed to write LID to FSP. (rc : %d).\n", rc); goto abort_update; } /* Unmap TCE */ code_update_tce_unmap(PSI_DMA_CODE_UPD_SIZE); /* Next LID index */ idx_entry = (void *)idx_entry + sizeof(struct lid_index_entry); } /* Code update completed */ rc = code_update_complete(FSP_CMD_FLASH_COMPLETE); return rc; abort_update: rc = code_update_complete(FSP_CMD_FLASH_ABORT); if (rc) log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " "Code update abort command failed. (rc : %d).", rc); out: return -1; } static int64_t validate_sglist(struct opal_sg_list *list) { struct opal_sg_list *sg; struct opal_sg_entry *prev_entry, *entry; int length, num_entries, i; prev_entry = NULL; for (sg = list; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { length = (be64_to_cpu(sg->length) & ~(SG_LIST_VERSION << 56)) - 16; num_entries = length / sizeof(struct opal_sg_entry); if (num_entries <= 0) return -1; for (i = 0; i < num_entries; i++) { entry = &sg->entry[i]; /* All entries must be aligned */ if (((uint64_t)be64_to_cpu(entry->data)) & 0xfff) return OPAL_PARAMETER; /* All non-terminal entries size must be aligned */ if (prev_entry && (prev_entry->length & 0xfff)) return OPAL_PARAMETER; prev_entry = entry; } } return OPAL_SUCCESS; } static int64_t fsp_opal_update_flash(struct opal_sg_list *list) { struct opal_sg_entry *entry; int length, num_entries, result = 0, rc = OPAL_PARAMETER; /* Ensure that the sg list honors our alignment requirements */ rc = validate_sglist(list); if (rc) { log_simple_error(&e_info(OPAL_RC_CU_SG_LIST), "CUPD: sglist fails alignment requirements\n"); return rc; } lock(&flash_lock); if (!list) { /* Cancel update request */ fsp_flash_term_hook = NULL; image_data = NULL; rc = OPAL_SUCCESS; goto out; } length = (be64_to_cpu(list->length) & ~(SG_LIST_VERSION << 56)) - 16; num_entries = length / sizeof(struct opal_sg_entry); if (num_entries <= 0) goto out; /* Validate image header */ entry = &list->entry[0]; rc = validate_candidate_image((uint64_t)be64_to_cpu(entry->data), VALIDATE_BUF_SIZE, &result); if (!rc && (result != VALIDATE_FLASH_AUTH && result != VALIDATE_INVALID_IMG)) { image_data = list; fsp_flash_term_hook = fsp_flash_firmware; goto out; } /* Adjust return code */ if (result == VALIDATE_FLASH_AUTH) rc = OPAL_FLASH_NO_AUTH; else if (result == VALIDATE_INVALID_IMG) rc = OPAL_INVALID_IMAGE; out: unlock(&flash_lock); return rc; } /* * Code Update notifications * * Note: At present we just ACK these notifications. * Reset cached VPD data if we are going to support * concurrent image maint in future. */ static bool code_update_notify(uint32_t cmd_sub_mod, struct fsp_msg *msg) { int rc; uint32_t cmd; switch(cmd_sub_mod) { case FSP_CMD_FLASH_CACHE: cmd = FSP_CMD_FLASH_CACHE_RSP; prlog(PR_NOTICE, "CUPD: Update LID cache event [data = 0x%x]\n", msg->data.words[0]); break; case FSP_CMD_FLASH_OUTC: case FSP_CMD_FLASH_OUTR: case FSP_CMD_FLASH_OUTS: cmd = FSP_CMD_FLASH_OUT_RSP; prlog(PR_NOTICE, "CUPD: Out of band commit notify " "[Type = 0x%x]\n", (msg->word1 >> 8) & 0xff); break; default: log_simple_error(&e_info(OPAL_RC_CU_NOTIFY), "CUPD: Unknown " "notification [cmd = 0x%x]\n", cmd_sub_mod); return false; } rc = fsp_queue_msg(fsp_mkmsg(cmd, 0), fsp_freemsg); if (rc) log_simple_error(&e_info(OPAL_RC_CU_NOTIFY), "CUPD: Failed to " "queue code update notification response :%d\n", rc); return true; } /* * Handle FSP R/R event. * * Note: * If FSP R/R happens during code update, then entire system reboots * and comes up with P side image (and T side image will be invalid). * Hence we don't need to handle R/R during code update. * * Also if FSP R/R happens in init path (while retrieving in_flight_params) * then system fails to continue booting (because we have not yet loaded * all required data/LID from FSP). Hence we don't need to handle R/R * for system params. */ static bool fsp_code_update_rr(uint32_t cmd_sub_mod, struct fsp_msg *msg __unused) { switch (cmd_sub_mod) { case FSP_RESET_START: lock(&flash_lock); if (code_update_check_state() == OPAL_BUSY) flash_state = FLASH_STATE_ABORT; unlock(&flash_lock); return true; case FSP_RELOAD_COMPLETE: lock(&flash_lock); /* Lets try to parse marker LID again, if we failed * to parse marker LID last time. */ if (code_update_check_state() == OPAL_INTERNAL_ERROR) fetch_com_marker_lid(); unlock(&flash_lock); return true; } return false; } static struct fsp_client fsp_cupd_client_rr = { .message = fsp_code_update_rr, }; static struct fsp_client fsp_get_notify = { .message = code_update_notify, }; void fsp_code_update_init(void) { if (!fsp_present()) { flash_state = FLASH_STATE_ABSENT; return; } /* OPAL interface */ opal_register(OPAL_FLASH_VALIDATE, fsp_opal_validate_flash, 3); opal_register(OPAL_FLASH_MANAGE, fsp_opal_manage_flash, 1); opal_register(OPAL_FLASH_UPDATE, fsp_opal_update_flash, 1); /* register Code Update Class D3 */ fsp_register_client(&fsp_get_notify, FSP_MCLASS_CODE_UPDATE); /* Register for Class AA (FSP R/R) */ fsp_register_client(&fsp_cupd_client_rr, FSP_MCLASS_RR_EVENT); /* Flash hook */ fsp_flash_term_hook = NULL; /* Fetch various code update related sys parameters */ get_ipl_side(); get_code_update_policy(); get_platform_hmc_managed(); /* Fetch common marker LID */ lid_data = memalign(TCE_PSIZE, MARKER_LID_SIZE); if (!lid_data) { log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Failed to allocate memory for marker LID\n"); flash_state = FLASH_STATE_ABSENT; return; } fetch_com_marker_lid(); } skiboot-skiboot-5.1.13/hw/fsp/fsp-codeupdate.h000066400000000000000000000135621265204436200212200ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CODEUPDATE_H #define __CODEUPDATE_H /* Flash SG list version */ #define SG_LIST_VERSION (1UL) /* LID size <= 16M */ #define LID_MAX_SIZE 0x1000000 /* Delete all LIDs in */ #define DEL_UPD_SIDE_LIDS 0xFFFFFFFF /* System parameter values used in code update validation */ #define INBAND_UPDATE_ALLOWED 0x01 #define PLATFORM_HMC_MANAGED 0x01 #define FW_LICENSE_ACCEPT 0x01 /* Running image side */ #define FW_IPL_SIDE_TEMP 0x01 #define FW_IPL_SIDE_PERM 0x00 /* Manage operations */ #define OPAL_REJECT_TMP_SIDE 0 #define OPAL_COMMIT_TMP_SIDE 1 /* Validate image size */ #define VALIDATE_BUF_SIZE 4096 /* Code update operation status */ #define OPAL_INVALID_IMAGE -1003 /* Unacceptable image */ #define OPAL_ACTIVE_SIDE_ERR -9001 #define OPAL_FLASH_NO_AUTH -9002 /* Validate image update result tokens */ #define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ #define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ #define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ #define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ /* * Current T side will be committed to P side before being replace with new * image, and the new image is downlevel from current image */ #define VALIDATE_TMP_COMMIT_DL 4 /* * Current T side will be committed to P side before being replaced with new * image */ #define VALIDATE_TMP_COMMIT 5 /* * T side will be updated with a downlevel image */ #define VALIDATE_TMP_UPDATE_DL 6 /* * The candidate image's release date is later than the system's firmware * service entitlement date - service warranty period has expired */ #define VALIDATE_OUT_OF_WRNTY 7 /* default version */ #define FW_VERSION_UNKNOWN "UNKNOWN" /* Actual size of MI & ML keyword including NULL */ #define MI_KEYWORD_SIZE 10 #define ML_KEYWORD_SIZE 9 /* Firmware image VPD data */ struct fw_image_vpd { char mi_keyword[MI_KEYWORD_SIZE]; /* NNSSS_FFF */ char ext_fw_id[ML_KEYWORD_SIZE]; /* FWxxx.yy */ }; /* Master LID header */ struct master_lid_header { char key[3]; /* "MLH" */ uint8_t version; /* 0x02 */ uint16_t header_size; uint16_t entry_size; uint8_t reserved[56]; }; /* LID index entry */ struct lid_index_entry { uint32_t id; uint32_t size; uint32_t offset; uint32_t crc; }; /* SP flags */ #define FW_ONE_OFF_SP 0x80000000 #define FW_EMERGENCY_SP 0x40000000 /* * SP GA date * * sp_flag addr = header->data + header->ext_fw_id_size */ struct update_image_ga_date { uint32_t sp_flag; char sp_ga_date[8]; /* YYYYMMDD */ }; /* Image magic number */ #define IMAGE_MAGIC_NUMBER 0x5549 /* Image header structure */ struct update_image_header { uint16_t magic; uint16_t version; uint32_t package_size; uint32_t crc; uint16_t lid_index_offset; uint16_t number_lids; uint16_t package_flags; uint16_t mi_keyword_size; char mi_keyword_data[40]; uint16_t ext_fw_id_size; /* Rest of the image data including ext fw id, sp flags */ char data[]; }; /* FipS header */ struct fips_header { uint16_t magic; uint16_t version; uint32_t lid_id; uint32_t lid_date; /* YYYYMMDD */ uint16_t lid_time; /* HHMM */ uint16_t lid_class; uint32_t crc; uint32_t lid_size; /* Number of bytes below header */ uint32_t header_size; uint8_t mtd_number; uint8_t valid; /* 1 = valid, 0 = invalid */ uint8_t reserved; uint8_t lid_info_size; char lid_info[64]; /* code level */ uint32_t update_date; /* YYYYMMDD */ uint16_t update_time; /* HHMM */ uint16_t phylum_len; uint8_t lid_phylum[]; }; /* Approximate LID size */ #define MASTER_LID_SIZE 0x5000 /* * Note: * Doc indicates non-SP LIDs size is 0-8MB. However * in reality marker LID size less than 4k. Allocating * 8k to give some breathing space. */ #define MARKER_LID_SIZE 0x00002000 /* Common marker LID no */ #define P_COM_MARKER_LID_ID 0x80A00001 #define T_COM_MARKER_LID_ID (P_COM_MARKER_LID_ID | ADJUST_T_SIDE_LID_NO) /* * Common marker LID structure * * Note that we are populating only required sections, * not all ADF sections in common marker LID. */ struct com_marker_header { uint32_t version; uint32_t MI_offset; /* Offset to MI section */ uint32_t iseries_offset; }; /* MI Keyword section */ struct com_marker_mi_section { uint32_t MI_size; char mi_keyword[40]; /* MI Keyword */ char lst_disrupt_fix_lvl[3]; char skip[21]; /* Skip not interested fields */ uint32_t adf_offset; /* Offset to ADF section */ }; /* Additional Data Fields */ struct com_marker_adf_sec { uint32_t adf_cnt; /* ADF count */ char adf_data[]; /* ADF data */ }; /* ADF common header */ struct com_marker_adf_header { uint32_t size; /* Section size */ uint32_t name; /* Section name */ }; /* * Service Pack Nomenclature ADF * * Service pack release name. */ #define ADF_NAME_SP 0x53504E4D /* SPNM */ struct com_marker_adf_sp { struct com_marker_adf_header header; uint32_t sp_name_offset; /* Offset from start of ADF */ uint32_t sp_name_size; uint32_t skip[4]; /* Skip rest of fields */ }; /* * Firmware IP Protection ADF * * Service Pack flags and GA date. */ #define ADF_NAME_FW_IP 0x46495050 /* FIPP */ struct com_marker_fw_ip { struct com_marker_adf_header header; uint32_t sp_flag_offset; /* Offset from start of ADF */ uint32_t sp_flag_size; uint32_t sp_ga_offset; /* Offset from start of ADF*/ uint32_t sp_ga_size; }; #endif /* __CODEUPDATE_H */ skiboot-skiboot-5.1.13/hw/fsp/fsp-console.c000066400000000000000000000606451265204436200205440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Service Processor serial console handling code */ #include #include #include #include #include #include #include #include #include struct fsp_serbuf_hdr { u16 partition_id; u8 session_id; u8 hmc_id; u16 data_offset; u16 last_valid; u16 ovf_count; u16 next_in; u8 flags; u8 reserved; u16 next_out; u8 data[]; }; #define SER_BUF_DATA_SIZE (0x10000 - sizeof(struct fsp_serbuf_hdr)) struct fsp_serial { bool available; bool open; bool has_part0; bool has_part1; bool log_port; bool out_poke; char loc_code[LOC_CODE_SIZE]; u16 rsrc_id; struct fsp_serbuf_hdr *in_buf; struct fsp_serbuf_hdr *out_buf; struct fsp_msg *poke_msg; }; #define SER_BUFFER_SIZE 0x00040000UL #define MAX_SERIAL 4 static struct fsp_serial fsp_serials[MAX_SERIAL]; static bool got_intf_query; static struct lock fsp_con_lock = LOCK_UNLOCKED; static void* ser_buffer = NULL; static void fsp_console_reinit(void) { int i; void *base; struct fsp_msg *msg; /* Initialize out data structure pointers & TCE maps */ base = ser_buffer; for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *ser = &fsp_serials[i]; ser->in_buf = base; ser->out_buf = base + SER_BUFFER_SIZE/2; base += SER_BUFFER_SIZE; } fsp_tce_map(PSI_DMA_SER0_BASE, ser_buffer, 4 * PSI_DMA_SER0_SIZE); for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; if (fs->rsrc_id == 0xffff) continue; prlog(PR_DEBUG, "FSP: Reassociating HVSI console %d\n", i); msg = fsp_mkmsg(FSP_CMD_ASSOC_SERIAL, 2, (fs->rsrc_id << 16) | 1, i); if (!msg) { prerror("FSPCON: Failed to allocate associate msg\n"); return; } if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("FSPCON: Failed to queue associate msg\n"); return; } } } static void fsp_close_consoles(void) { unsigned int i; for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; if (!fs->available) continue; if (fs->rsrc_id == 0xffff) /* Get clarity from benh */ continue; lock(&fsp_con_lock); if (fs->open) { fs->open = false; fs->out_poke = false; if (fs->poke_msg->state != fsp_msg_unused) fsp_cancelmsg(fs->poke_msg); fsp_freemsg(fs->poke_msg); fs->poke_msg = NULL; } unlock(&fsp_con_lock); } prlog(PR_DEBUG, "FSPCON: Closed consoles due to FSP reset/reload\n"); } static void fsp_pokemsg_reclaim(struct fsp_msg *msg) { struct fsp_serial *fs = msg->user_data; /* * The poke_msg might have been "detached" from the console * in vserial_close, so we need to check whether it's current * before touching the state, otherwise, just free it */ lock(&fsp_con_lock); if (fs->open && fs->poke_msg == msg) { if (fs->out_poke) { if (fsp_queue_msg(fs->poke_msg, fsp_pokemsg_reclaim)) { prerror("FSPCON: failed to queue poke msg\n"); fsp_freemsg(msg); } else { fs->out_poke = false; } } else fs->poke_msg->state = fsp_msg_unused; } else fsp_freemsg(msg); unlock(&fsp_con_lock); } /* Called with the fsp_con_lock held */ static size_t fsp_write_vserial(struct fsp_serial *fs, const char *buf, size_t len) { struct fsp_serbuf_hdr *sb = fs->out_buf; u16 old_nin = sb->next_in; u16 space, chunk; if (!fs->open) return 0; space = (sb->next_out + SER_BUF_DATA_SIZE - old_nin - 1) % SER_BUF_DATA_SIZE; if (space < len) len = space; if (!len) return 0; chunk = SER_BUF_DATA_SIZE - old_nin; if (chunk > len) chunk = len; memcpy(&sb->data[old_nin], buf, chunk); if (chunk < len) memcpy(&sb->data[0], buf + chunk, len - chunk); lwsync(); sb->next_in = (old_nin + len) % SER_BUF_DATA_SIZE; sync(); if (sb->next_out == old_nin && fs->poke_msg) { if (fs->poke_msg->state == fsp_msg_unused) { if (fsp_queue_msg(fs->poke_msg, fsp_pokemsg_reclaim)) prerror("FSPCON: poke msg queuing failed\n"); } else fs->out_poke = true; } #ifndef DISABLE_CON_PENDING_EVT opal_update_pending_evt(OPAL_EVENT_CONSOLE_OUTPUT, OPAL_EVENT_CONSOLE_OUTPUT); #endif return len; } #ifdef DVS_CONSOLE static int fsp_con_port = -1; static bool fsp_con_full; /* * This is called by the code in console.c without the con_lock * held. However it can be called as the result of any printf * thus any other lock might be held including possibly the * FSP lock */ static size_t fsp_con_write(const char *buf, size_t len) { size_t written; if (fsp_con_port < 0) return 0; lock(&fsp_con_lock); written = fsp_write_vserial(&fsp_serials[fsp_con_port], buf, len); fsp_con_full = (written < len); unlock(&fsp_con_lock); return written; } static struct con_ops fsp_con_ops = { .write = fsp_con_write, }; #endif /* DVS_CONSOLE */ static void fsp_open_vserial(struct fsp_msg *msg) { struct fsp_msg *resp; u16 part_id = msg->data.words[0] & 0xffff; u16 sess_id = msg->data.words[1] & 0xffff; u8 hmc_sess = msg->data.bytes[0]; u8 hmc_indx = msg->data.bytes[1]; u8 authority = msg->data.bytes[4]; u32 tce_in, tce_out; struct fsp_serial *fs; prlog(PR_INFO, "FSPCON: Got VSerial Open\n"); prlog(PR_DEBUG, " part_id = 0x%04x\n", part_id); prlog(PR_DEBUG, " sess_id = 0x%04x\n", sess_id); prlog(PR_DEBUG, " hmc_sess = 0x%02x\n", hmc_sess); prlog(PR_DEBUG, " hmc_indx = 0x%02x\n", hmc_indx); prlog(PR_DEBUG, " authority = 0x%02x\n", authority); if (sess_id >= MAX_SERIAL || !fsp_serials[sess_id].available) { prlog(PR_WARNING, "FSPCON: 0x%04x NOT AVAILABLE!\n", sess_id); resp = fsp_mkmsg(FSP_RSP_OPEN_VSERIAL | 0x2f, 0); if (!resp) { prerror("FSPCON: Response allocation failed\n"); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSPCON: Failed to queue response msg\n"); } return; } fs = &fsp_serials[sess_id]; /* Hack ! On blades, the console opened via the mm has partition 1 * while the debug DVS generally has partition 0 (though you can * use what you want really). * We don't want a DVS open/close to crap on the blademm console * thus if it's a raw console, gets an open with partID 1, we * set a flag that ignores the close of partid 0 */ if (fs->rsrc_id == 0xffff) { if (part_id == 0) fs->has_part0 = true; if (part_id == 1) fs->has_part1 = true; } tce_in = PSI_DMA_SER0_BASE + PSI_DMA_SER0_SIZE * sess_id; tce_out = tce_in + SER_BUFFER_SIZE/2; lock(&fsp_con_lock); if (fs->open) { prlog(PR_DEBUG, " already open, skipping init !\n"); unlock(&fsp_con_lock); goto already_open; } fs->open = true; fs->poke_msg = fsp_mkmsg(FSP_CMD_VSERIAL_OUT, 2, msg->data.words[0], msg->data.words[1] & 0xffff); fs->poke_msg->user_data = fs; fs->in_buf->partition_id = fs->out_buf->partition_id = part_id; fs->in_buf->session_id = fs->out_buf->session_id = sess_id; fs->in_buf->hmc_id = fs->out_buf->hmc_id = hmc_indx; fs->in_buf->data_offset = fs->out_buf->data_offset = sizeof(struct fsp_serbuf_hdr); fs->in_buf->last_valid = fs->out_buf->last_valid = SER_BUF_DATA_SIZE - 1; fs->in_buf->ovf_count = fs->out_buf->ovf_count = 0; fs->in_buf->next_in = fs->out_buf->next_in = 0; fs->in_buf->flags = fs->out_buf->flags = 0; fs->in_buf->reserved = fs->out_buf->reserved = 0; fs->in_buf->next_out = fs->out_buf->next_out = 0; unlock(&fsp_con_lock); already_open: resp = fsp_mkmsg(FSP_RSP_OPEN_VSERIAL, 6, msg->data.words[0], msg->data.words[1] & 0xffff, 0, tce_in, 0, tce_out); if (!resp) { prerror("FSPCON: Failed to allocate open msg response\n"); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSPCON: Failed to queue open msg response\n"); return; } #ifdef DVS_CONSOLE prlog(PR_DEBUG, " log_port = %d\n", fs->log_port); if (fs->log_port) { fsp_con_port = sess_id; sync(); /* * We mark the FSP lock as being in the console * path. We do that only once, we never unmark it * (there is really no much point) */ fsp_used_by_console(); fsp_con_lock.in_con_path = true; /* See comment in fsp_used_by_console */ lock(&fsp_con_lock); unlock(&fsp_con_lock); set_console(&fsp_con_ops); } #endif } static void fsp_close_vserial(struct fsp_msg *msg) { u16 part_id = msg->data.words[0] & 0xffff; u16 sess_id = msg->data.words[1] & 0xffff; u8 hmc_sess = msg->data.bytes[0]; u8 hmc_indx = msg->data.bytes[1]; u8 authority = msg->data.bytes[4]; struct fsp_serial *fs; struct fsp_msg *resp; prlog(PR_INFO, "FSPCON: Got VSerial Close\n"); prlog(PR_DEBUG, " part_id = 0x%04x\n", part_id); prlog(PR_DEBUG, " sess_id = 0x%04x\n", sess_id); prlog(PR_DEBUG, " hmc_sess = 0x%02x\n", hmc_sess); prlog(PR_DEBUG, " hmc_indx = 0x%02x\n", hmc_indx); prlog(PR_DEBUG, " authority = 0x%02x\n", authority); if (sess_id >= MAX_SERIAL || !fsp_serials[sess_id].available) { prlog(PR_WARNING, "FSPCON: 0x%04x NOT AVAILABLE!\n", sess_id); goto skip_close; } fs = &fsp_serials[sess_id]; /* See "HACK" comment in open */ if (fs->rsrc_id == 0xffff) { if (part_id == 0) fs->has_part0 = false; if (part_id == 1) fs->has_part1 = false; if (fs->has_part0 || fs->has_part1) { prlog(PR_DEBUG, " skipping close !\n"); goto skip_close; } } #ifdef DVS_CONSOLE if (fs->log_port) { fsp_con_port = -1; set_console(NULL); } #endif lock(&fsp_con_lock); if (fs->open) { fs->open = false; fs->out_poke = false; if (fs->poke_msg && fs->poke_msg->state == fsp_msg_unused) { fsp_freemsg(fs->poke_msg); fs->poke_msg = NULL; } } unlock(&fsp_con_lock); skip_close: resp = fsp_mkmsg(FSP_RSP_CLOSE_VSERIAL, 2, msg->data.words[0], msg->data.words[1] & 0xffff); if (!resp) { prerror("FSPCON: Failed to allocate close msg response\n"); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSPCON: Failed to queue close msg response\n"); } } static bool fsp_con_msg_hmc(u32 cmd_sub_mod, struct fsp_msg *msg) { struct fsp_msg *resp; /* Associate response */ if ((cmd_sub_mod >> 8) == 0xe08a) { prlog(PR_TRACE, "FSPCON: Got associate response, status" " 0x%02x\n", cmd_sub_mod & 0xff); return true; } if ((cmd_sub_mod >> 8) == 0xe08b) { prlog(PR_TRACE, "Got unassociate response, status 0x%02x\n", cmd_sub_mod & 0xff); return true; } switch(cmd_sub_mod) { case FSP_CMD_OPEN_VSERIAL: fsp_open_vserial(msg); return true; case FSP_CMD_CLOSE_VSERIAL: fsp_close_vserial(msg); return true; case FSP_CMD_HMC_INTF_QUERY: prlog(PR_DEBUG, "FSPCON: Got HMC interface query\n"); got_intf_query = true; resp = fsp_mkmsg(FSP_RSP_HMC_INTF_QUERY, 1, msg->data.words[0] & 0x00ffffff); if (!resp) { prerror("FSPCON: Failed to allocate hmc intf response\n"); return true; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSPCON: Failed to queue hmc intf response\n"); } return true; } return false; } static bool fsp_con_msg_vt(u32 cmd_sub_mod, struct fsp_msg *msg) { u16 sess_id = msg->data.words[1] & 0xffff; if (cmd_sub_mod == FSP_CMD_VSERIAL_IN && sess_id < MAX_SERIAL) { struct fsp_serial *fs = &fsp_serials[sess_id]; if (!fs->open) return true; /* FSP is signaling some incoming data. We take the console * lock to avoid racing with a simultaneous read, though we * might want to consider to simplify all that locking into * one single lock that covers the console and the pending * events. */ lock(&fsp_con_lock); opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, OPAL_EVENT_CONSOLE_INPUT); unlock(&fsp_con_lock); } return true; } static bool fsp_con_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg) { assert(msg == NULL); switch (cmd_sub_mod) { case FSP_RESET_START: fsp_close_consoles(); return true; case FSP_RELOAD_COMPLETE: fsp_console_reinit(); return true; } return false; } static struct fsp_client fsp_con_client_hmc = { .message = fsp_con_msg_hmc, }; static struct fsp_client fsp_con_client_vt = { .message = fsp_con_msg_vt, }; static struct fsp_client fsp_con_client_rr = { .message = fsp_con_msg_rr, }; static void fsp_serial_add(int index, u16 rsrc_id, const char *loc_code, bool log_port) { struct fsp_serial *ser; struct fsp_msg *msg; lock(&fsp_con_lock); ser = &fsp_serials[index]; if (ser->available) { unlock(&fsp_con_lock); return; } ser->rsrc_id = rsrc_id; memset(ser->loc_code, 0x00, LOC_CODE_SIZE); strncpy(ser->loc_code, loc_code, LOC_CODE_SIZE - 1); ser->available = true; ser->log_port = log_port; unlock(&fsp_con_lock); /* DVS doesn't have that */ if (rsrc_id != 0xffff) { msg = fsp_mkmsg(FSP_CMD_ASSOC_SERIAL, 2, (rsrc_id << 16) | 1, index); if (!msg) { prerror("FSPCON: Assoc serial alloc failed\n"); return; } if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("FSPCON: Assoc serial queue failed\n"); return; } } } void fsp_console_preinit(void) { int i; void *base; if (!fsp_present()) return; ser_buffer = memalign(TCE_PSIZE, SER_BUFFER_SIZE * MAX_SERIAL); /* Initialize out data structure pointers & TCE maps */ base = ser_buffer; for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *ser = &fsp_serials[i]; ser->in_buf = base; ser->out_buf = base + SER_BUFFER_SIZE/2; base += SER_BUFFER_SIZE; } fsp_tce_map(PSI_DMA_SER0_BASE, ser_buffer, 4 * PSI_DMA_SER0_SIZE); /* Register for class E0 and E1 */ fsp_register_client(&fsp_con_client_hmc, FSP_MCLASS_HMC_INTFMSG); fsp_register_client(&fsp_con_client_vt, FSP_MCLASS_HMC_VT); fsp_register_client(&fsp_con_client_rr, FSP_MCLASS_RR_EVENT); /* Add DVS ports. We currently have session 0 and 3, 0 is for * OS use. 3 is our debug port. We need to add those before * we complete the OPL or we'll potentially miss the * console setup on Firebird blades. */ fsp_serial_add(0, 0xffff, "DVS_OS", false); op_display(OP_LOG, OP_MOD_FSPCON, 0x0001); fsp_serial_add(3, 0xffff, "DVS_FW", true); op_display(OP_LOG, OP_MOD_FSPCON, 0x0002); } static int64_t fsp_console_write(int64_t term_number, int64_t *length, const uint8_t *buffer) { struct fsp_serial *fs; size_t written, requested; if (term_number < 0 || term_number >= MAX_SERIAL) return OPAL_PARAMETER; fs = &fsp_serials[term_number]; if (!fs->available || fs->log_port) return OPAL_PARAMETER; lock(&fsp_con_lock); if (!fs->open) { unlock(&fsp_con_lock); return OPAL_CLOSED; } /* Clamp to a reasonable size */ requested = *length; if (requested > 0x1000) requested = 0x1000; written = fsp_write_vserial(fs, buffer, requested); #ifdef OPAL_DEBUG_CONSOLE_IO prlog(PR_TRACE, "OPAL: console write req=%ld written=%ld" " ni=%d no=%d\n", requested, written, fs->out_buf->next_in, fs->out_buf->next_out); prlog(PR_TRACE, " %02x %02x %02x %02x " "%02x \'%c\' %02x \'%c\' %02x \'%c\'.%02x \'%c\'..\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[4], buffer[5], buffer[5], buffer[6], buffer[6], buffer[7], buffer[7]); #endif /* OPAL_DEBUG_CONSOLE_IO */ *length = written; unlock(&fsp_con_lock); return written ? OPAL_SUCCESS : OPAL_BUSY_EVENT; } static int64_t fsp_console_write_buffer_space(int64_t term_number, int64_t *length) { struct fsp_serial *fs; struct fsp_serbuf_hdr *sb; if (term_number < 0 || term_number >= MAX_SERIAL) return OPAL_PARAMETER; fs = &fsp_serials[term_number]; if (!fs->available || fs->log_port) return OPAL_PARAMETER; lock(&fsp_con_lock); if (!fs->open) { unlock(&fsp_con_lock); return OPAL_CLOSED; } sb = fs->out_buf; *length = (sb->next_out + SER_BUF_DATA_SIZE - sb->next_in - 1) % SER_BUF_DATA_SIZE; unlock(&fsp_con_lock); return OPAL_SUCCESS; } static int64_t fsp_console_read(int64_t term_number, int64_t *length, uint8_t *buffer __unused) { struct fsp_serial *fs; struct fsp_serbuf_hdr *sb; bool pending = false; uint32_t old_nin, n, i, chunk, req = *length; if (term_number < 0 || term_number >= MAX_SERIAL) return OPAL_PARAMETER; fs = &fsp_serials[term_number]; if (!fs->available || fs->log_port) return OPAL_PARAMETER; lock(&fsp_con_lock); if (!fs->open) { unlock(&fsp_con_lock); return OPAL_CLOSED; } sb = fs->in_buf; old_nin = sb->next_in; lwsync(); n = (old_nin + SER_BUF_DATA_SIZE - sb->next_out) % SER_BUF_DATA_SIZE; if (n > req) { pending = true; n = req; } *length = n; chunk = SER_BUF_DATA_SIZE - sb->next_out; if (chunk > n) chunk = n; memcpy(buffer, &sb->data[sb->next_out], chunk); if (chunk < n) memcpy(buffer + chunk, &sb->data[0], n - chunk); sb->next_out = (sb->next_out + n) % SER_BUF_DATA_SIZE; #ifdef OPAL_DEBUG_CONSOLE_IO prlog(PR_TRACE, "OPAL: console read req=%d read=%d ni=%d no=%d\n", req, n, sb->next_in, sb->next_out); prlog(PR_TRACE, " %02x %02x %02x %02x %02x %02x %02x %02x ...\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); #endif /* OPAL_DEBUG_CONSOLE_IO */ /* Might clear the input pending flag */ for (i = 0; i < MAX_SERIAL && !pending; i++) { struct fsp_serial *fs = &fsp_serials[i]; struct fsp_serbuf_hdr *sb = fs->in_buf; if (fs->log_port || !fs->open) continue; if (sb->next_out != sb->next_in) pending = true; } if (!pending) opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0); unlock(&fsp_con_lock); return OPAL_SUCCESS; } void fsp_console_poll(void *data __unused) { #ifdef OPAL_DEBUG_CONSOLE_POLL static int debug; #endif /* * We don't get messages for out buffer being consumed, so we * need to poll. We also defer sending of poke messages from * the sapphire console to avoid a locking nightmare with * beging called from printf() deep into an existing lock nest * stack. */ if (fsp_con_full || (opal_pending_events & OPAL_EVENT_CONSOLE_OUTPUT)) { unsigned int i; bool pending = false; /* We take the console lock. This is somewhat inefficient * but it guarantees we aren't racing with a write, and * thus clearing an event improperly */ lock(&fsp_con_lock); for (i = 0; i < MAX_SERIAL && !pending; i++) { struct fsp_serial *fs = &fsp_serials[i]; struct fsp_serbuf_hdr *sb = fs->out_buf; if (!fs->open) continue; if (sb->next_out == sb->next_in) continue; if (fs->log_port) __flush_console(true); else { #ifdef OPAL_DEBUG_CONSOLE_POLL if (debug < 5) { prlog(PR_DEBUG,"OPAL: %d still pending" " ni=%d no=%d\n", i, sb->next_in, sb->next_out); debug++; } #endif /* OPAL_DEBUG_CONSOLE_POLL */ pending = true; } } if (!pending) { opal_update_pending_evt(OPAL_EVENT_CONSOLE_OUTPUT, 0); #ifdef OPAL_DEBUG_CONSOLE_POLL debug = 0; #endif } unlock(&fsp_con_lock); } } void fsp_console_init(void) { struct dt_node *serials, *ser; int i; if (!fsp_present()) return; opal_register(OPAL_CONSOLE_READ, fsp_console_read, 3); opal_register(OPAL_CONSOLE_WRITE_BUFFER_SPACE, fsp_console_write_buffer_space, 2); opal_register(OPAL_CONSOLE_WRITE, fsp_console_write, 3); /* Wait until we got the intf query before moving on */ while (!got_intf_query) opal_run_pollers(); op_display(OP_LOG, OP_MOD_FSPCON, 0x0000); /* Register poller */ opal_add_poller(fsp_console_poll, NULL); /* Parse serial port data */ serials = dt_find_by_path(dt_root, "ipl-params/fsp-serial"); if (!serials) { prerror("FSPCON: No FSP serial ports in device-tree\n"); return; } i = 1; dt_for_each_child(serials, ser) { u32 rsrc_id = dt_prop_get_u32(ser, "reg"); const void *lc = dt_prop_get(ser, "ibm,loc-code"); prlog(PR_NOTICE, "FSPCON: Serial %d rsrc: %04x loc: %s\n", i, rsrc_id, (const char *)lc); fsp_serial_add(i++, rsrc_id, lc, false); op_display(OP_LOG, OP_MOD_FSPCON, 0x0010 + i); } op_display(OP_LOG, OP_MOD_FSPCON, 0x0005); } static void flush_all_input(void) { unsigned int i; lock(&fsp_con_lock); for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; struct fsp_serbuf_hdr *sb = fs->in_buf; if (fs->log_port) continue; sb->next_out = sb->next_in; } unlock(&fsp_con_lock); } static bool send_all_hvsi_close(void) { unsigned int i; bool has_hvsi = false; static const uint8_t close_packet[] = { 0xfe, 6, 0, 1, 0, 3 }; for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; struct fsp_serbuf_hdr *sb = fs->out_buf; unsigned int space, timeout = 10; if (fs->log_port) continue; if (fs->rsrc_id == 0xffff) continue; has_hvsi = true; /* Do we have room ? Wait a bit if not */ while(timeout--) { space = (sb->next_out + SER_BUF_DATA_SIZE - sb->next_in - 1) % SER_BUF_DATA_SIZE; if (space >= 6) break; time_wait_ms(500); } lock(&fsp_con_lock); fsp_write_vserial(fs, close_packet, 6); unlock(&fsp_con_lock); } return has_hvsi; } static void reopen_all_hvsi(void) { unsigned int i; for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; if (fs->rsrc_id == 0xffff) continue; prlog(PR_NOTICE, "FSP: Deassociating HVSI console %d\n", i); fsp_sync_msg(fsp_mkmsg(FSP_CMD_UNASSOC_SERIAL, 1, (i << 16) | 1), true); } for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; if (fs->rsrc_id == 0xffff) continue; prlog(PR_NOTICE, "FSP: Reassociating HVSI console %d\n", i); fsp_sync_msg(fsp_mkmsg(FSP_CMD_ASSOC_SERIAL, 2, (fs->rsrc_id << 16) | 1, i), true); } } void fsp_console_reset(void) { prlog(PR_NOTICE, "FSP: Console reset !\n"); /* This is called on a fast-reset. To work around issues with HVSI * initial negotiation, before we reboot the kernel, we flush all * input and send an HVSI close packet. */ flush_all_input(); /* Returns false if there is no HVSI console */ if (!send_all_hvsi_close()) return; time_wait_ms(500); flush_all_input(); reopen_all_hvsi(); } void fsp_console_add_nodes(void) { unsigned int i; struct dt_node *consoles; consoles = dt_new(opal_node, "consoles"); dt_add_property_cells(consoles, "#address-cells", 1); dt_add_property_cells(consoles, "#size-cells", 0); for (i = 0; i < MAX_SERIAL; i++) { struct fsp_serial *fs = &fsp_serials[i]; struct dt_node *fs_node; char name[32]; if (fs->log_port || !fs->available) continue; snprintf(name, sizeof(name), "serial@%d", i); fs_node = dt_new(consoles, name); if (fs->rsrc_id == 0xffff) dt_add_property_string(fs_node, "compatible", "ibm,opal-console-raw"); else dt_add_property_string(fs_node, "compatible", "ibm,opal-console-hvsi"); dt_add_property_cells(fs_node, "#write-buffer-size", SER_BUF_DATA_SIZE); dt_add_property_cells(fs_node, "reg", i); dt_add_property_string(fs_node, "device_type", "serial"); } } void fsp_console_select_stdout(void) { bool use_serial = false; if (!fsp_present()) return; /* On P8, we have a sysparam ! yay ! */ if (proc_gen >= proc_gen_p8) { int rc; u8 param; rc = fsp_get_sys_param(SYS_PARAM_CONSOLE_SELECT, ¶m, 1, NULL, NULL); if (rc != 1) prerror("FSPCON: Failed to get console" " sysparam rc %d\n", rc); else { switch(param) { case 0: use_serial = false; break; case 1: use_serial = true; break; default: prerror("FSPCON: Unknown console" " sysparam %d\n", param); } } } else { struct dt_node *iplp; u32 ipl_mode = 0; /* * We hijack the "os-ipl-mode" setting in iplparams to select * out output console. This is the "i5/OS partition mode boot" * setting in ASMI converted to an integer: 0=A, 1=B. */ iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params"); if (iplp) { ipl_mode = dt_prop_get_u32_def(iplp, "os-ipl-mode", 0); use_serial = ipl_mode > 0; /* * Now, if ipl_mode is > 0, we use serial port A else * we use IPMI/SOL/DVS */ } } if (fsp_serials[1].open && use_serial) { dt_add_property_string(dt_chosen, "linux,stdout-path", "/ibm,opal/consoles/serial@1"); prlog(PR_NOTICE, "FSPCON: default console set to serial A\n"); } else { dt_add_property_string(dt_chosen, "linux,stdout-path", "/ibm,opal/consoles/serial@0"); prlog(PR_NOTICE, "FSPCON: default console set to SOL/DVS\n"); } } skiboot-skiboot-5.1.13/hw/fsp/fsp-diag.c000066400000000000000000000030531265204436200177740ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Code for handling FSP_MCLASS_DIAG messages (cmd 0xee) * Receiving a high level ack timeout is likely indicative of a firmware bug */ #include #include #include #include #include #include #include static bool fsp_diag_msg(u32 cmd_sub_mod, struct fsp_msg *msg) { if (cmd_sub_mod == FSP_RSP_DIAG_LINK_ERROR) { printf("FIXME: Unhandled FSP_MCLASS_DIAG Link Error Report\n"); return false; } if (cmd_sub_mod != FSP_RSP_DIAG_ACK_TIMEOUT) { printf("BUG: Unhandled subcommand: 0x%x (New FSP spec?)\n", cmd_sub_mod); return false; } printf("BUG: High Level ACK timeout (FSP_MCLASS_DIAG) for 0x%x\n", msg->data.words[0] & 0xffff0000); return true; } static struct fsp_client fsp_diag = { .message = fsp_diag_msg, }; /* This is called at boot time */ void fsp_init_diag(void) { /* Register for the diag event */ fsp_register_client(&fsp_diag, FSP_MCLASS_DIAG); } skiboot-skiboot-5.1.13/hw/fsp/fsp-dpo.c000066400000000000000000000111771265204436200176600ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Handle FSP DPO (Delayed Power Off) event notification */ #define pr_fmt(fmt) "FSPDPO: " fmt #include #include #include #include #include #include #include #include #define DPO_CMD_SGN_BYTE0 0xf4 /* Byte[0] signature */ #define DPO_CMD_SGN_BYTE1 0x20 /* Byte[1] signature */ #define DPO_TIMEOUT 2700 /* 45 minutes in seconds */ bool fsp_dpo_pending = false; static unsigned long fsp_dpo_init_tb = 0; /* * OPAL DPO interface * * Returns zero if DPO is not active, positive value indicating number * of seconds remaining for a forced system shutdown. This will enable * the host to schedule for shutdown voluntarily before timeout occurs. */ static int64_t fsp_opal_get_dpo_status(int64_t *dpo_timeout) { if (fsp_dpo_init_tb && fsp_dpo_pending) { *dpo_timeout = DPO_TIMEOUT - tb_to_secs(mftb() - fsp_dpo_init_tb); return OPAL_SUCCESS; } else { *dpo_timeout = 0; return OPAL_WRONG_STATE; } } /* Process FSP DPO init message */ static void fsp_process_dpo(struct fsp_msg *msg) { struct fsp_msg *resp; u32 cmd = FSP_RSP_INIT_DPO; int rc; /* DPO message does not have the correct signatures */ if ((msg->data.bytes[0] != DPO_CMD_SGN_BYTE0) || (msg->data.bytes[1] != DPO_CMD_SGN_BYTE1)) { prlog(PR_ERR, "Message signatures did not match\n"); cmd |= FSP_STATUS_INVALID_CMD; resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response " "message\n", __func__); } return; } /* Sapphire is already in "DPO pending" state */ if (fsp_dpo_pending) { prlog(PR_ERR, "OPAL is already in DPO pending state\n"); cmd |= FSP_STATUS_INVALID_DPOSTATE; resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response " "message\n", __func__); } return; } /* Record the DPO init time */ fsp_dpo_init_tb = mftb(); /* Inform the host about DPO */ rc = opal_queue_msg(OPAL_MSG_DPO, NULL, NULL); if (rc) { prlog(PR_ERR, "OPAL message queuing failed\n"); cmd |= FSP_STATUS_GENERIC_ERROR; resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response " "message\n", __func__); } return; } /* Acknowledge the FSP on DPO */ resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response message\n", __func__); } fsp_dpo_pending = true; /* * Sapphire is now in DPO pending state. After first detecting DPO * condition from Sapphire, the host will have 45 minutes to prepare * the system for shutdown. The host must take all necessary actions * required in that regard and at the end shutdown itself. The host * shutdown sequence eventually will make the call OPAL_CEC_POWER_DOWN * which in turn ask the FSP to shutdown the CEC. If the FSP does not * receive the cec power down command from Sapphire within 45 minutes, * it will assume that the host and the Sapphire has processed the DPO * sequence successfully and hence force power off the system. */ } /* Handle DPO sub-command from FSP */ static bool fsp_dpo_message(u32 cmd_sub_mod, struct fsp_msg *msg) { if (cmd_sub_mod == FSP_CMD_INIT_DPO) { prlog(PR_TRACE, "SP initiated Delayed Power Off (DPO)\n"); fsp_process_dpo(msg); return true; } return false; } static struct fsp_client fsp_dpo_client = { .message = fsp_dpo_message, }; void fsp_dpo_init(void) { fsp_register_client(&fsp_dpo_client, FSP_MCLASS_SERVICE); opal_register(OPAL_GET_DPO_STATUS, fsp_opal_get_dpo_status, 1); prlog(PR_TRACE, "FSP DPO support initialized\n"); } skiboot-skiboot-5.1.13/hw/fsp/fsp-dump.c000066400000000000000000000520201265204436200200330ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Dump support: * We get dump notification from different sources: * - During system initialization via HDAT * - During FSP reset/reload (FipS dump) * - Dump available notification MBOX command (0xCE, 0x78, 0x00) * * To avoid complications, we keep list of dumps in a list and fetch * them serially. * * Dump retrieve process: * - Once we get notification from FSP we enqueue the dump ID and notify * Linux via OPAL event notification. * - Linux reads dump info and allocates required memory to fetch the dump * and makes dump read call. * - Sapphire fetches dump data from FSP. * - Linux writes dump to disk and sends acknowledgement. * - Sapphire acknowledges FSP. */ #include #include #include #include #include #include #include /* * Max outstanding dumps to retrieve * * Note: * Dumps are serialized. We don't get notification for second * dump of given type until we acknowledge first one. But we * may get notification for different dump type. And our dump * retrieval code is serialized. Hence we use list to keep * track of outstanding dumps to be retrieved. */ #define MAX_DUMP_RECORD 0x04 /* Max retry */ #define FIPS_DUMP_MAX_RETRY 0x03 /* Dump type */ #define DUMP_TYPE_FSP 0x01 #define DUMP_TYPE_SYS 0x02 #define DUMP_TYPE_SMA 0x03 /* Dump fetch size */ #define DUMP_FETCH_SIZE_FSP 0x500000 #define DUMP_FETCH_SIZE_SYS 0x400000 #define DUMP_FETCH_SIZE_RES 0x200000 /* Params for Fips dump */ #define FSP_DUMP_TOOL_TYPE "SYS " #define FSP_DUMP_CLIENT_ID "SAPPHIRE_CLIENT" enum dump_state { DUMP_STATE_ABSENT, /* No FSP dump */ DUMP_STATE_NONE, /* No dump to retrieve */ DUMP_STATE_NOTIFY, /* Notified Linux */ DUMP_STATE_FETCHING, /* Dump retrieval is in progress */ DUMP_STATE_FETCH, /* Dump retrieve complete */ DUMP_STATE_PARTIAL, /* Partial read */ DUMP_STATE_ABORTING, /* Aborting due to kexec */ }; /* Pending dump list */ struct dump_record { uint8_t type; uint32_t id; uint32_t size; struct list_node link; }; /* List definations */ static LIST_HEAD(dump_pending); static LIST_HEAD(dump_free); /* Dump retrieve state */ static enum dump_state dump_state = DUMP_STATE_NONE; /* Dump buffer SG list */ static struct opal_sg_list *dump_data; static struct dump_record *dump_entry; static int64_t dump_offset; static size_t fetch_remain; /* FipS dump retry count */ static int retry_cnt; /* Protect list and dump retrieve state */ static struct lock dump_lock = LOCK_UNLOCKED; /* Forward declaration */ static int64_t fsp_opal_dump_init(uint8_t dump_type); static int64_t fsp_dump_read(void); DEFINE_LOG_ENTRY(OPAL_RC_DUMP_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_DUMP_LIST, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_DUMP_ACK, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); /* * Helper functions */ static inline void update_dump_state(enum dump_state state) { dump_state = state; } static int64_t check_dump_state(void) { switch (dump_state) { case DUMP_STATE_ABSENT: return OPAL_HARDWARE; case DUMP_STATE_NONE: case DUMP_STATE_NOTIFY: /* During dump fetch, notify is wrong state */ return OPAL_WRONG_STATE; case DUMP_STATE_FETCHING: case DUMP_STATE_ABORTING: return OPAL_BUSY_EVENT; case DUMP_STATE_FETCH: return OPAL_SUCCESS; case DUMP_STATE_PARTIAL: return OPAL_PARTIAL; } return OPAL_SUCCESS; } static inline void dump_tce_map(uint32_t tce_offset, void *buffer, uint32_t size) { uint32_t tlen = ALIGN_UP(size, TCE_PSIZE); fsp_tce_map(PSI_DMA_DUMP_DATA + tce_offset, buffer, tlen); } static inline void dump_tce_unmap(uint32_t size) { fsp_tce_unmap(PSI_DMA_DUMP_DATA, size); } /* * Returns Data set ID for the given dump type */ static inline uint16_t get_dump_data_set_id(uint8_t type) { switch (type) { case DUMP_TYPE_FSP: return FSP_DATASET_SP_DUMP; case DUMP_TYPE_SYS: return FSP_DATASET_HW_DUMP; default: break; } return OPAL_INTERNAL_ERROR; } /* * Returns max data we can fetch from FSP fetch data call */ static inline int64_t get_dump_fetch_max_size(uint8_t type) { switch (type) { case DUMP_TYPE_FSP: return DUMP_FETCH_SIZE_FSP; case DUMP_TYPE_SYS: return DUMP_FETCH_SIZE_SYS; default: break; } return OPAL_INTERNAL_ERROR; } /* * Get dump record from pending list */ static inline struct dump_record *get_dump_rec_from_list(uint32_t id) { struct dump_record *record; list_for_each(&dump_pending, record, link) { if (record->id == id) return record; } return NULL; } /* * New dump available notification to Linux */ static void update_opal_dump_notify(void) { /* * Wait until current dump retrieval to complete * before notifying again. */ if (dump_state != DUMP_STATE_NONE) return; /* More dump's to retrieve */ if (!list_empty(&dump_pending)) { update_dump_state(DUMP_STATE_NOTIFY); opal_update_pending_evt(OPAL_EVENT_DUMP_AVAIL, OPAL_EVENT_DUMP_AVAIL); } } static int64_t remove_dump_id_from_list(uint32_t dump_id) { struct dump_record *record, *nxt_record; int rc = OPAL_SUCCESS; bool found = false; /* Remove record from pending list */ list_for_each_safe(&dump_pending, record, nxt_record, link) { if (record->id != dump_id) continue; found = true; list_del(&record->link); list_add(&dump_free, &record->link); break; } /* * Continue update_opal_dump_notify even if it fails * to remove ID. So that we can resend notification * for the same dump ID to Linux. */ if (!found) { /* List corrupted? */ log_simple_error(&e_info(OPAL_RC_DUMP_LIST), "DUMP: ID 0x%x not found in list!\n", dump_id); rc = OPAL_PARAMETER; } /* Update state */ update_dump_state(DUMP_STATE_NONE); /* Notify next available dump to retrieve */ update_opal_dump_notify(); return rc; } static int64_t add_dump_id_to_list(uint8_t dump_type, uint32_t dump_id, uint32_t dump_size) { struct dump_record *record; int rc = OPAL_SUCCESS; lock(&dump_lock); rc = check_dump_state(); if (rc == OPAL_HARDWARE) goto out; /* List is full ? */ if (list_empty(&dump_free)) { printf("DUMP: Dump ID 0x%x is not queued.\n", dump_id); rc = OPAL_RESOURCE; goto out; } /* Already queued? */ record = get_dump_rec_from_list(dump_id); if (record) { rc = OPAL_SUCCESS; goto out; } /* Add to list */ record = list_pop(&dump_free, struct dump_record, link); record->type = dump_type; record->id = dump_id; record->size = dump_size; list_add_tail(&dump_pending, &record->link); /* OPAL notification */ update_opal_dump_notify(); rc = OPAL_SUCCESS; out: unlock(&dump_lock); return rc; } static void dump_init_complete(struct fsp_msg *msg) { uint8_t status = (msg->resp->word1 >> 8) & 0xff; printf("DUMP: FipS dump init status = 0x%x\n", status); fsp_freemsg(msg); switch (status) { case FSP_STATUS_SUCCESS: printf("DUMP: Initiated FipS dump.\n"); break; case FSP_STATUS_BUSY: /* Retry, if FSP is busy */ if (retry_cnt++ < FIPS_DUMP_MAX_RETRY) if (fsp_opal_dump_init(DUMP_TYPE_FSP) == OPAL_SUCCESS) return; break; default: break; } /* Reset max retry count */ retry_cnt = 0; } /* * Initiate new FipS dump */ static int64_t fsp_opal_dump_init(uint8_t dump_type) { struct fsp_msg *msg; int rc = OPAL_SUCCESS; uint32_t *tool_type = (void *)FSP_DUMP_TOOL_TYPE; uint32_t *client_id = (void *)FSP_DUMP_CLIENT_ID; /* Only FipS dump generate request is supported */ if (dump_type != DUMP_TYPE_FSP) return OPAL_PARAMETER; msg = fsp_mkmsg(FSP_CMD_FSP_DUMP_INIT, 6, *tool_type, sizeof(FSP_DUMP_CLIENT_ID), *client_id, *(client_id + 1), *(client_id + 2), *(client_id + 3)); if (!msg) { log_simple_error(&e_info(OPAL_RC_DUMP_INIT), "DUMP: Message allocation failed.\n"); rc = OPAL_INTERNAL_ERROR; } else if (fsp_queue_msg(msg, dump_init_complete)) { log_simple_error(&e_info(OPAL_RC_DUMP_INIT), "DUMP: Failed to queue FipS dump init request.\n"); fsp_freemsg(msg); rc = OPAL_INTERNAL_ERROR; } return rc; } /* * OPAL interface to send dump information to Linux. */ static int64_t fsp_opal_dump_info2(uint32_t *dump_id, uint32_t *dump_size, uint32_t *dump_type) { struct dump_record *record; int rc = OPAL_SUCCESS; lock(&dump_lock); /* Clear notification */ opal_update_pending_evt(OPAL_EVENT_DUMP_AVAIL, 0); record = list_top(&dump_pending, struct dump_record, link); if (!record) { /* List corrupted? */ update_dump_state(DUMP_STATE_NONE); rc = OPAL_INTERNAL_ERROR; goto out; } *dump_id = record->id; *dump_size = record->size; *dump_type = record->type; out: unlock(&dump_lock); return rc; } static int64_t fsp_opal_dump_info(uint32_t *dump_id, uint32_t *dump_size) { uint32_t dump_type; return fsp_opal_dump_info2(dump_id, dump_size, &dump_type); } static int64_t validate_dump_sglist(struct opal_sg_list *list, int64_t *size) { struct opal_sg_list *sg; struct opal_sg_entry *prev_entry, *entry; int length, num_entries, i; prev_entry = NULL; *size = 0; for (sg = list; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { length = be64_to_cpu(sg->length) - 16; num_entries = length / sizeof(struct opal_sg_entry); if (num_entries <= 0) return OPAL_PARAMETER; for (i = 0; i < num_entries; i++) { entry = &sg->entry[i]; *size += entry->length; /* All entries must be aligned */ if (((uint64_t)be64_to_cpu(entry->data)) & 0xfff) return OPAL_PARAMETER; /* All non-terminal entries size must be aligned */ if (prev_entry && (be64_to_cpu(prev_entry->length) & 0xfff)) return OPAL_PARAMETER; prev_entry = entry; } } return OPAL_SUCCESS; } /* * Map dump buffer to TCE buffer */ static int64_t map_dump_buffer(void) { struct opal_sg_list *sg; struct opal_sg_entry *entry; int64_t fetch_max; int length, num_entries, i; int buf_off, fetch_off, tce_off, sg_off; bool last = false; /* FSP fetch max size */ fetch_max = get_dump_fetch_max_size(dump_entry->type); if (fetch_max > (dump_entry->size - dump_offset)) fetch_remain = dump_entry->size - dump_offset; else fetch_remain = fetch_max; /* offsets */ fetch_off = fetch_remain; tce_off = sg_off = 0; for (sg = dump_data; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { num_entries = (be64_to_cpu(sg->length) - 16) / sizeof(struct opal_sg_entry); if (num_entries <= 0) return OPAL_PARAMETER; for (i = 0; i < num_entries; i++) { entry = &sg->entry[i]; /* Continue until we get offset */ if ((sg_off + be64_to_cpu(entry->length)) < dump_offset) { sg_off += be64_to_cpu(entry->length); continue; } /* * SG list entry size can be more than 4k. * Map only required pages, instead of * mapping entire entry. */ if (!tce_off) { buf_off = (dump_offset - sg_off) & ~0xfff; length = be64_to_cpu(entry->length) - buf_off; } else { buf_off = 0; length = be64_to_cpu(entry->length); } /* Adjust length for last mapping */ if (fetch_off <= length) { length = fetch_off; last = true; } /* Adjust offset */ sg_off += be64_to_cpu(entry->length); fetch_off -= length; /* TCE mapping */ dump_tce_map(tce_off, (void*)(be64_to_cpu(entry->data) + buf_off), length); tce_off += length; /* TCE mapping complete */ if (last) return OPAL_SUCCESS; } } /* outer loop */ return OPAL_PARAMETER; } static void dump_read_complete(struct fsp_msg *msg) { void *buffer; size_t length, offset; int rc; uint32_t dump_id; uint16_t id; uint8_t flags, status; bool compl = false; status = (msg->resp->word1 >> 8) & 0xff; flags = (msg->data.words[0] >> 16) & 0xff; id = msg->data.words[0] & 0xffff; dump_id = msg->data.words[1]; offset = msg->resp->data.words[1]; length = msg->resp->data.words[2]; fsp_freemsg(msg); lock(&dump_lock); if (dump_state == DUMP_STATE_ABORTING) { printf("DUMP: Fetch dump aborted, ID = 0x%x\n", dump_id); dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); update_dump_state(DUMP_STATE_NONE); goto bail; } switch (status) { case FSP_STATUS_SUCCESS: /* Fetch next dump block */ if (dump_offset < dump_entry->size) { dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); rc = fsp_dump_read(); if (rc == OPAL_SUCCESS) goto bail; } else { /* Dump read complete */ compl = true; } break; case FSP_STATUS_MORE_DATA: /* More data to read */ offset += length; buffer = (void *)PSI_DMA_DUMP_DATA + offset; fetch_remain -= length; rc = fsp_fetch_data_queue(flags, id, dump_id, offset, buffer, &fetch_remain, dump_read_complete); if (rc == OPAL_SUCCESS) goto bail; break; default: break; } dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); /* Update state */ if (compl) { printf("DUMP: Fetch dump success. ID = 0x%x\n", dump_id); update_dump_state(DUMP_STATE_FETCH); } else { printf("DUMP: Fetch dump partial. ID = 0x%x\n", dump_id); update_dump_state(DUMP_STATE_PARTIAL); } bail: unlock(&dump_lock); } /* * Fetch dump data from FSP */ static int64_t fsp_dump_read(void) { int64_t rc; uint16_t data_set; uint8_t flags = 0x00; /* Get data set ID */ data_set = get_dump_data_set_id(dump_entry->type); /* Map TCE buffer */ rc = map_dump_buffer(); if (rc != OPAL_SUCCESS) { printf("DUMP: TCE mapping failed\n"); return rc; } printf("DUMP: Fetch Dump. ID = %02x, sub ID = %08x, len = %ld\n", data_set, dump_entry->id, fetch_remain); /* Fetch data */ rc = fsp_fetch_data_queue(flags, data_set, dump_entry->id, dump_offset, (void *)PSI_DMA_DUMP_DATA, &fetch_remain, dump_read_complete); /* Adjust dump fetch offset */ dump_offset += fetch_remain; return rc; } static int64_t fsp_opal_dump_read(uint32_t dump_id, struct opal_sg_list *list) { struct dump_record *record; int64_t rc, size; lock(&dump_lock); /* Check state */ if (dump_state != DUMP_STATE_NOTIFY) { rc = check_dump_state(); goto out; } /* Validate dump ID */ record = get_dump_rec_from_list(dump_id); if (!record) { /* List corrupted? */ rc = OPAL_INTERNAL_ERROR; goto out; } /* Validate dump buffer and size */ rc = validate_dump_sglist(list, &size); if (rc != OPAL_SUCCESS) { printf("DUMP: SG list validation failed\n"); goto out; } if (size < record->size) { /* Insuffient buffer */ printf("DUMP: Insufficient buffer\n"); rc = OPAL_PARAMETER; goto out; } /* Update state */ update_dump_state(DUMP_STATE_FETCHING); /* Fetch dump data */ dump_entry = record; dump_data = list; dump_offset = 0; rc = fsp_dump_read(); if (rc != OPAL_SUCCESS) goto out; /* Check status after initiating fetch data */ rc = check_dump_state(); out: unlock(&dump_lock); return rc; } static void dump_ack_complete(struct fsp_msg *msg) { uint8_t status = (msg->resp->word1 >> 8) & 0xff; if (status) log_simple_error(&e_info(OPAL_RC_DUMP_ACK), "DUMP: ACK failed for ID: 0x%x\n", msg->data.words[0]); else printf("DUMP: ACKed dump ID: 0x%x\n", msg->data.words[0]); fsp_freemsg(msg); } /* * Acknowledge dump */ static int64_t fsp_opal_dump_ack(uint32_t dump_id) { struct dump_record *record; struct fsp_msg *msg; int rc; uint32_t cmd; uint8_t dump_type = 0; /* Get dump type */ lock(&dump_lock); record = get_dump_rec_from_list(dump_id); if (record) dump_type = record->type; /* * Next available dump in pending list will be of different * type. Hence we don't need to wait for ack complete. * * Note: * This allows us to proceed even if we fail to ACK. * In the worst case we may get notification for the * same dump again, which is probably better than * looping forever. */ rc = remove_dump_id_from_list(dump_id); if (rc != OPAL_SUCCESS) /* Invalid dump id */ goto out; /* Adjust mod value */ cmd = FSP_CMD_ACK_DUMP | (dump_type & 0xff); msg = fsp_mkmsg(cmd, 1, dump_id); if (!msg) { log_simple_error(&e_info(OPAL_RC_DUMP_ACK), "DUMP: Message allocation failed.!\n"); rc = OPAL_INTERNAL_ERROR; } else if (fsp_queue_msg(msg, dump_ack_complete)) { log_simple_error(&e_info(OPAL_RC_DUMP_ACK), "DUMP: Failed to queue dump ack message.\n"); fsp_freemsg(msg); rc = OPAL_INTERNAL_ERROR; } out: unlock(&dump_lock); return rc; } /* Resend dump available notification */ static int64_t fsp_opal_dump_resend_notification(void) { lock(&dump_lock); if (dump_state != DUMP_STATE_ABSENT) update_dump_state(DUMP_STATE_NONE); update_opal_dump_notify(); unlock(&dump_lock); return OPAL_SUCCESS; } /* * Handle FSP R/R event. */ static bool fsp_dump_retrieve_rr(uint32_t cmd_sub_mod, struct fsp_msg *msg __unused) { switch (cmd_sub_mod) { case FSP_RESET_START: lock(&dump_lock); /* Reset dump state */ if (dump_state == DUMP_STATE_FETCHING) update_dump_state(DUMP_STATE_ABORTING); unlock(&dump_lock); return true; case FSP_RELOAD_COMPLETE: lock(&dump_lock); /* Reset TCE mapping */ dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); /* Reset dump state */ update_dump_state(DUMP_STATE_NONE); /* * For now keeping R/R handler simple. In the worst case * we may endup resending dump available notification for * same dump ID twice to Linux. */ update_opal_dump_notify(); unlock(&dump_lock); return true; } return false; } /* * Handle host kexec'ing scenarios */ static bool opal_kexec_dump_notify(void *data __unused) { bool ready = true; lock(&dump_lock); /* Dump retrieve is in progress? */ if (dump_state == DUMP_STATE_FETCHING) dump_state = DUMP_STATE_ABORTING; /* Not yet safe to kexec */ if (dump_state == DUMP_STATE_ABORTING) ready = false; unlock(&dump_lock); return ready; } /* * FipS dump notification */ void fsp_fips_dump_notify(uint32_t dump_id, uint32_t dump_size) { printf("DUMP: FipS dump available. ID = 0x%x [size: %d bytes]\n", dump_id, dump_size); add_dump_id_to_list(DUMP_TYPE_FSP, dump_id, dump_size); } /* * System/Platform dump notification */ static bool fsp_sys_dump_notify(uint32_t cmd_sub_mod, struct fsp_msg *msg) { /* * Though spec says mod 00 is deprecated we still * seems to get mod 00 notification (at least on * P7 machine). */ if (cmd_sub_mod != FSP_RSP_SYS_DUMP && cmd_sub_mod != FSP_RSP_SYS_DUMP_OLD) return false; printf("DUMP: Platform dump available. ID = 0x%x [size: %d bytes]\n", msg->data.words[0], msg->data.words[1]); add_dump_id_to_list(DUMP_TYPE_SYS, msg->data.words[0], msg->data.words[1]); return true; } /* * If platform dump available during IPL time, then we * get notification via HDAT. Check for DT for the dump * presence. */ static void check_ipl_sys_dump(void) { struct dt_node *dump_node; uint32_t dump_id, dump_size; dump_node = dt_find_by_path(dt_root, "ipl-params/platform-dump"); if (!dump_node) return; if (!dt_find_property(dump_node, "dump-id")) return; dump_id = dt_prop_get_u32(dump_node, "dump-id"); dump_size = (uint32_t)dt_prop_get_u64(dump_node, "total-size"); printf("DUMP: Platform dump present during IPL.\n"); printf(" ID = 0x%x [size: %d bytes]\n", dump_id, dump_size); add_dump_id_to_list(DUMP_TYPE_SYS, dump_id, dump_size); } /* * Allocate and initialize dump list */ static int init_dump_free_list(void) { struct dump_record *entry; int i; entry = zalloc(sizeof(struct dump_record) * MAX_DUMP_RECORD); if (!entry) { log_simple_error(&e_info(OPAL_RC_DUMP_INIT), "DUMP: Out of memory\n"); return -ENOMEM; } for (i = 0; i < MAX_DUMP_RECORD; i++) { list_add_tail(&dump_free, &entry->link); entry++; } return 0; } static struct fsp_client fsp_sys_dump_client = { .message = fsp_sys_dump_notify, }; static struct fsp_client fsp_dump_client_rr = { .message = fsp_dump_retrieve_rr, }; void fsp_dump_init(void) { if (!fsp_present()) { update_dump_state(DUMP_STATE_ABSENT); return; } /* Initialize list */ if (init_dump_free_list() != 0) { update_dump_state(DUMP_STATE_ABSENT); return; } /* Register for Class CE */ fsp_register_client(&fsp_sys_dump_client, FSP_MCLASS_SERVICE); /* Register for Class AA (FSP R/R) */ fsp_register_client(&fsp_dump_client_rr, FSP_MCLASS_RR_EVENT); /* Register for sync on host reboot call */ opal_add_host_sync_notifier(opal_kexec_dump_notify, NULL); /* OPAL interface */ opal_register(OPAL_DUMP_INIT, fsp_opal_dump_init, 1); opal_register(OPAL_DUMP_INFO, fsp_opal_dump_info, 2); opal_register(OPAL_DUMP_INFO2, fsp_opal_dump_info2, 3); opal_register(OPAL_DUMP_READ, fsp_opal_dump_read, 2); opal_register(OPAL_DUMP_ACK, fsp_opal_dump_ack, 1); opal_register(OPAL_DUMP_RESEND, fsp_opal_dump_resend_notification, 0); /* Check for platform dump presence during IPL time */ check_ipl_sys_dump(); } skiboot-skiboot-5.1.13/hw/fsp/fsp-elog-read.c000066400000000000000000000363571265204436200207440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This code will enable retrieving of error log from fsp->sapphire * in sequence. * Here, FSP would send next log only when sapphire sends a new * log notification response to FSP. On Completion of reading * the log from FSP, OPAL_EVENT_ERROR_LOG_AVAIL is signaled. * This will remain raised until a call to opal_elog_read() * is made and OPAL_SUCCESS is returned, upon which. * the operation is complete and the event is cleared. * This is READ action from FSP. */ /* * Design of READ error log : * When we receive a new error log entry notificatiion from FSP, * we queue it into the "pending" list. * If the "pending" list is not empty, then we start the fetching log from FSP. * * When Linux reads a log entry, we dequeue it from the "pending" list * and enqueue it to another "processed" list. At this point, if the * "pending" list is not empty, we continue to fetch the next log. * * When Linux calls opal_resend_pending_logs(), we fetch the log * corresponding to the head of the pending list and move it to the * processed list, and continue this process this until the pending list is * empty. If the pending list was empty earlier and is currently non-empty, we * initiate an error log fetch. * * When Linux acks an error log, we remove it from processed list. */ #include #include #include #include #include #include #include #include /* * Maximum number of entries that are pre-allocated * to keep track of pending elogs to be fetched. */ #define ELOG_READ_MAX_RECORD 128 /* structure to maintain log-id,log-size, pending and processed list */ struct fsp_log_entry { uint32_t log_id; size_t log_size; struct list_node link; }; static LIST_HEAD(elog_read_pending); static LIST_HEAD(elog_read_processed); static LIST_HEAD(elog_read_free); /* * lock is used to protect overwriting of processed and pending list * and also used while updating state of each log */ static struct lock elog_read_lock = LOCK_UNLOCKED; /* log buffer to copy FSP log for READ */ #define ELOG_READ_BUFFER_SIZE 0x00004000 static void *elog_read_buffer; static uint32_t elog_head_id; /* FSP entry ID */ static size_t elog_head_size; /* actual FSP log size */ static uint32_t elog_read_retries; /* bad response status count */ /* Initialize the state of the log */ static enum elog_head_state elog_read_from_fsp_head_state = ELOG_STATE_NONE; /* Need forward declaration because of Circular dependency */ static void fsp_elog_queue_fetch(void); /* * check the response message for mbox acknowledgment * command send to FSP. */ static void fsp_elog_ack_complete(struct fsp_msg *msg) { uint8_t val; if (!msg->resp) return; val = (msg->resp->word1 >> 8) & 0xff; if (val != 0) prerror("ELOG: Acknowledgment error\n"); fsp_freemsg(msg); } /* send Error Log PHYP Acknowledgment to FSP with entry ID */ static int64_t fsp_send_elog_ack(uint32_t log_id) { struct fsp_msg *ack_msg; ack_msg = fsp_mkmsg(FSP_CMD_ERRLOG_PHYP_ACK, 1, log_id); if (!ack_msg) { prerror("ELOG: Failed to allocate ack message\n"); return OPAL_INTERNAL_ERROR; } if (fsp_queue_msg(ack_msg, fsp_elog_ack_complete)) { fsp_freemsg(ack_msg); ack_msg = NULL; prerror("ELOG: Error queueing elog ack complete\n"); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } /* retrive error log from FSP with TCE for the data transfer */ static void fsp_elog_check_and_fetch_head(void) { lock(&elog_read_lock); if (elog_read_from_fsp_head_state != ELOG_STATE_NONE || list_empty(&elog_read_pending)) { unlock(&elog_read_lock); return; } elog_read_retries = 0; /* Start fetching first entry from the pending list */ fsp_elog_queue_fetch(); unlock(&elog_read_lock); } /* this function should be called with the lock held */ static void fsp_elog_set_head_state(enum elog_head_state state) { enum elog_head_state old_state = elog_read_from_fsp_head_state; elog_read_from_fsp_head_state = state; if (state == ELOG_STATE_FETCHED_DATA && old_state != ELOG_STATE_FETCHED_DATA) opal_update_pending_evt(OPAL_EVENT_ERROR_LOG_AVAIL, OPAL_EVENT_ERROR_LOG_AVAIL); if (state != ELOG_STATE_FETCHED_DATA && old_state == ELOG_STATE_FETCHED_DATA) opal_update_pending_evt(OPAL_EVENT_ERROR_LOG_AVAIL, 0); } /* * when we try maximum time of fetching log from fsp * we call following function to delete log from the * pending list and update the state to fetch next log * * this function should be called with the lock held */ static void fsp_elog_fetch_failure(uint8_t fsp_status) { struct fsp_log_entry *log_data; /* read top list and delete the node */ log_data = list_top(&elog_read_pending, struct fsp_log_entry, link); if (!log_data) { prlog(PR_ERR, "%s: Inconsistent internal list state !\n", __func__); } else { list_del(&log_data->link); list_add(&elog_read_free, &log_data->link); prerror("ELOG: received invalid data: %x FSP status: 0x%x\n", log_data->log_id, fsp_status); } fsp_elog_set_head_state(ELOG_STATE_NONE); } /* Read response value from FSP for fetch sp data mbox command */ static void fsp_elog_read_complete(struct fsp_msg *read_msg) { uint8_t val; /*struct fsp_log_entry *log_data;*/ lock(&elog_read_lock); val = (read_msg->resp->word1 >> 8) & 0xff; fsp_freemsg(read_msg); if (elog_read_from_fsp_head_state == ELOG_STATE_REJECTED) { fsp_elog_set_head_state(ELOG_STATE_NONE); goto elog_read_out; } switch (val) { case FSP_STATUS_SUCCESS: fsp_elog_set_head_state(ELOG_STATE_FETCHED_DATA); break; case FSP_STATUS_DMA_ERROR: if (elog_read_retries++ < MAX_RETRIES) { /* * for a error response value from FSP, we try to * send fetch sp data mbox command again for three * times if response from FSP is still not valid * we send generic error response to fsp. */ fsp_elog_queue_fetch(); break; } fsp_elog_fetch_failure(val); break; default: fsp_elog_fetch_failure(val); } elog_read_out: unlock(&elog_read_lock); /* Check if a new log needs fetching */ fsp_elog_check_and_fetch_head(); } /* read error log from FSP through mbox commands */ static void fsp_elog_queue_fetch(void) { int rc; uint8_t flags = 0; struct fsp_log_entry *entry; entry = list_top(&elog_read_pending, struct fsp_log_entry, link); if (!entry) { prlog(PR_ERR, "%s: Inconsistent internal list state !\n", __func__); fsp_elog_set_head_state(ELOG_STATE_NONE); return; } fsp_elog_set_head_state(ELOG_STATE_FETCHING); elog_head_id = entry->log_id; elog_head_size = entry->log_size; rc = fsp_fetch_data_queue(flags, FSP_DATASET_ERRLOG, elog_head_id, 0, (void *)PSI_DMA_ERRLOG_READ_BUF, &elog_head_size, fsp_elog_read_complete); if (rc) { prerror("ELOG: failed to queue read message: %d\n", rc); fsp_elog_set_head_state(ELOG_STATE_NONE); } } /* opal interface for powernv to read log size and log ID from sapphire */ static int64_t fsp_opal_elog_info(uint64_t *opal_elog_id, uint64_t *opal_elog_size, uint64_t *elog_type) { struct fsp_log_entry *log_data; /* copy type of the error log */ *elog_type = ELOG_TYPE_PEL; /* Check if any OPAL log needs to be reported to the host */ if (opal_elog_info(opal_elog_id, opal_elog_size)) return OPAL_SUCCESS; lock(&elog_read_lock); if (elog_read_from_fsp_head_state != ELOG_STATE_FETCHED_DATA) { unlock(&elog_read_lock); return OPAL_WRONG_STATE; } log_data = list_top(&elog_read_pending, struct fsp_log_entry, link); if (!log_data) { prlog(PR_ERR, "%s: Inconsistent internal list state !\n", __func__); unlock(&elog_read_lock); return OPAL_WRONG_STATE; } *opal_elog_id = log_data->log_id; *opal_elog_size = log_data->log_size; unlock(&elog_read_lock); return OPAL_SUCCESS; } /* opal interface for powernv to read log from sapphire */ static int64_t fsp_opal_elog_read(uint64_t *buffer, uint64_t opal_elog_size, uint64_t opal_elog_id) { int size = opal_elog_size; struct fsp_log_entry *log_data; /* Check if any OPAL log needs to be reported to the host */ if (opal_elog_read(buffer, opal_elog_size, opal_elog_id)) return OPAL_SUCCESS; /* * Read top entry from list. * as we know always top record of the list is fetched from FSP */ lock(&elog_read_lock); if (elog_read_from_fsp_head_state != ELOG_STATE_FETCHED_DATA) { unlock(&elog_read_lock); return OPAL_WRONG_STATE; } log_data = list_top(&elog_read_pending, struct fsp_log_entry, link); if (!log_data) { prlog(PR_ERR, "%s: Inconsistent internal list state !\n", __func__); unlock(&elog_read_lock); return OPAL_WRONG_STATE; } /* Check log ID and then read log from buffer */ if (opal_elog_id != log_data->log_id) { unlock(&elog_read_lock); return OPAL_PARAMETER; } /* Do not copy more than actual log size */ if (opal_elog_size > log_data->log_size) size = log_data->log_size; memset((void *)buffer, 0, opal_elog_size); memcpy((void *)buffer, elog_read_buffer, size); /* * once log is read from linux move record from pending * to processed list and delete record from pending list * and change state of the log to fetch next record */ list_del(&log_data->link); list_add(&elog_read_processed, &log_data->link); fsp_elog_set_head_state(ELOG_STATE_NONE); unlock(&elog_read_lock); /* read error log from FSP */ fsp_elog_check_and_fetch_head(); return OPAL_SUCCESS; } /* set state of the log head before fetching the log */ static void elog_reject_head(void) { if (elog_read_from_fsp_head_state == ELOG_STATE_FETCHING) fsp_elog_set_head_state(ELOG_STATE_REJECTED); if (elog_read_from_fsp_head_state == ELOG_STATE_FETCHED_DATA) fsp_elog_set_head_state(ELOG_STATE_NONE); } /* opal Interface for powernv to send ack to fsp with log ID */ static int64_t fsp_opal_elog_ack(uint64_t ack_id) { int rc = 0; struct fsp_log_entry *record, *next_record; if (opal_elog_ack(ack_id)) return rc; /* Send acknowledgement to FSP */ rc = fsp_send_elog_ack(ack_id); if (rc != OPAL_SUCCESS) { prerror("ELOG: failed to send acknowledgement: %d\n", rc); return rc; } lock(&elog_read_lock); list_for_each_safe(&elog_read_pending, record, next_record, link) { if (record->log_id != ack_id) continue; list_del(&record->link); list_add(&elog_read_free, &record->link); } list_for_each_safe(&elog_read_processed, record, next_record, link) { if (record->log_id != ack_id) continue; list_del(&record->link); list_add(&elog_read_free, &record->link); } unlock(&elog_read_lock); return rc; } /* * once linux kexec's it ask to resend all logs which * are not acknowledged from linux */ static void fsp_opal_resend_pending_logs(void) { struct fsp_log_entry *entry; /* Check if any Sapphire logs are pending */ opal_resend_pending_logs(); lock(&elog_read_lock); /* * If processed list is not empty add all record from * processed list to pending list at head of the list * and delete records from processed list. */ while (!list_empty(&elog_read_processed)) { entry = list_pop(&elog_read_processed, struct fsp_log_entry, link); list_add(&elog_read_pending, &entry->link); } /* * If the current fetched or fetching log doesn't match our * new pending list head, then reject it */ if (!list_empty(&elog_read_pending)) { entry = list_top(&elog_read_pending, struct fsp_log_entry, link); if (entry->log_id != elog_head_id) elog_reject_head(); } unlock(&elog_read_lock); /* Read error log from FSP if needed */ fsp_elog_check_and_fetch_head(); } /* fsp elog notify function */ static bool fsp_elog_msg(uint32_t cmd_sub_mod, struct fsp_msg *msg) { int rc = 0; struct fsp_log_entry *record; uint32_t log_id; uint32_t log_size; if (cmd_sub_mod != FSP_CMD_ERRLOG_NOTIFICATION) return false; log_id = msg->data.words[0]; log_size = msg->data.words[1]; printf("ELOG: Notified of log 0x%08x (size: %d)\n", log_id, log_size); /* Make sure we don't cross read buffer size */ if (log_size > ELOG_READ_BUFFER_SIZE) { log_size = ELOG_READ_BUFFER_SIZE; printf("ELOG: Truncated log (0x%08x) to 0x%x\n", log_id, log_size); } /* take a lock until we take out the node from elog_read_free */ lock(&elog_read_lock); if (!list_empty(&elog_read_free)) { /* Create a new entry in the pending list */ record = list_pop(&elog_read_free, struct fsp_log_entry, link); record->log_id = log_id; record->log_size = log_size; list_add_tail(&elog_read_pending, &record->link); unlock(&elog_read_lock); /* Send response back to FSP for a new elog notify message */ rc = fsp_queue_msg(fsp_mkmsg(FSP_RSP_ERRLOG_NOTIFICATION, 1, log_id), fsp_freemsg); if (rc) prerror("ELOG: Failed to queue errlog notification" " response: %d\n", rc); /* read error log from FSP */ fsp_elog_check_and_fetch_head(); } else { printf("ELOG: Log entry 0x%08x discarded\n", log_id); /* unlock if elog_read_free is empty */ unlock(&elog_read_lock); rc = fsp_queue_msg(fsp_mkmsg(FSP_RSP_ERRLOG_NOTIFICATION, 1, log_id), fsp_freemsg); if (rc) prerror("ELOG: Failed to queue errlog notification" " response: %d\n", rc); /* * if list is full with max record then we * send discarded by phyp (condition full) ack to FSP. * * At some point in the future, we'll get notified again. * This is largely up to FSP as to when they tell us about * the log again. */ rc = fsp_queue_msg(fsp_mkmsg(FSP_CMD_ERRLOG_PHYP_ACK | 0x02, 1, log_id), fsp_freemsg); if (rc) prerror("ELOG: Failed to queue errlog ack" " response: %d\n", rc); } return true; } static struct fsp_client fsp_get_elog_notify = { .message = fsp_elog_msg, }; /* Pre-allocate memory for reading error log from FSP */ static int init_elog_read_free_list(uint32_t num_entries) { struct fsp_log_entry *entry; int i; entry = zalloc(sizeof(struct fsp_log_entry) * num_entries); if (!entry) goto out_err; for (i = 0; i < num_entries; ++i) { list_add_tail(&elog_read_free, &entry->link); entry++; } return 0; out_err: return -ENOMEM; } /* fsp elog read init function */ void fsp_elog_read_init(void) { int val = 0; if (!fsp_present()) return; elog_read_buffer = memalign(TCE_PSIZE, ELOG_READ_BUFFER_SIZE); if (!elog_read_buffer) { prerror("FSP: could not allocate FSP ELOG_READ_BUFFER!\n"); return; } /* Map TCEs */ fsp_tce_map(PSI_DMA_ERRLOG_READ_BUF, elog_read_buffer, PSI_DMA_ERRLOG_READ_BUF_SZ); /* pre allocate memory for 128 record */ val = init_elog_read_free_list(ELOG_READ_MAX_RECORD); if (val != 0) return; /* register Eror log Class D2 */ fsp_register_client(&fsp_get_elog_notify, FSP_MCLASS_ERR_LOG); /* register opal Interface */ opal_register(OPAL_ELOG_READ, fsp_opal_elog_read, 3); opal_register(OPAL_ELOG_ACK, fsp_opal_elog_ack, 1); opal_register(OPAL_ELOG_RESEND, fsp_opal_resend_pending_logs, 0); opal_register(OPAL_ELOG_SIZE, fsp_opal_elog_info, 3); } skiboot-skiboot-5.1.13/hw/fsp/fsp-elog-write.c000066400000000000000000000262041265204436200211510ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This code will enable generation and pushing of error log * from powernv, sapphire to FSP * Critical events from sapphire that needs to be reported * will be pushed on to FSP after converting the * error log to Platform Error Log (PEL) format. * This is termed as WRITE action to FSP. */ #include #include #include #include #include #include #include #include #include #include static LIST_HEAD(elog_write_to_fsp_pending); static LIST_HEAD(elog_write_to_host_pending); static LIST_HEAD(elog_write_to_host_processed); static struct lock elog_write_lock = LOCK_UNLOCKED; static struct lock elog_panic_write_lock = LOCK_UNLOCKED; static struct lock elog_write_to_host_lock = LOCK_UNLOCKED; /* log buffer to copy FSP log for READ */ #define ELOG_WRITE_TO_FSP_BUFFER_SIZE 0x00004000 static void *elog_write_to_fsp_buffer; #define ELOG_PANIC_WRITE_BUFFER_SIZE 0x00004000 static void *elog_panic_write_buffer; #define ELOG_WRITE_TO_HOST_BUFFER_SIZE 0x00004000 static void *elog_write_to_host_buffer; static uint32_t elog_write_retries; /* Manipulate this only with write_lock held */ static uint32_t elog_plid_fsp_commit = -1; static enum elog_head_state elog_write_to_host_head_state = ELOG_STATE_NONE; /* Need forward declaration because of Circular dependency */ static int opal_send_elog_to_fsp(void); static void remove_elog_head_entry(void) { struct errorlog *head, *entry; lock(&elog_write_lock); if (!list_empty(&elog_write_to_fsp_pending)) { head = list_top(&elog_write_to_fsp_pending, struct errorlog, link); if (head->plid == elog_plid_fsp_commit) { entry = list_pop(&elog_write_to_fsp_pending, struct errorlog, link); opal_elog_complete(entry, elog_write_retries < MAX_RETRIES); /* Reset the counter */ elog_plid_fsp_commit = -1; } } elog_write_retries = 0; unlock(&elog_write_lock); } static void opal_fsp_write_complete(struct fsp_msg *read_msg) { uint8_t val; val = (read_msg->resp->word1 >> 8) & 0xff; fsp_freemsg(read_msg); switch (val) { case FSP_STATUS_SUCCESS: remove_elog_head_entry(); break; default: if (elog_write_retries++ >= MAX_RETRIES) { remove_elog_head_entry(); prerror("ELOG: Error in writing to FSP!\n"); } break; } if (opal_send_elog_to_fsp() != OPAL_SUCCESS) prerror("ELOG: Error sending elog to FSP !\n"); } /* write PEL format hex dump of the log to FSP */ static int64_t fsp_opal_elog_write(size_t opal_elog_size) { struct fsp_msg *elog_msg; elog_msg = fsp_mkmsg(FSP_CMD_CREATE_ERRLOG, 3, opal_elog_size, 0, PSI_DMA_ERRLOG_WRITE_BUF); if (!elog_msg) { prerror("ELOG: Failed to create message for WRITE to FSP\n"); return OPAL_INTERNAL_ERROR; } if (fsp_queue_msg(elog_msg, opal_fsp_write_complete)) { fsp_freemsg(elog_msg); elog_msg = NULL; prerror("FSP: Error queueing elog update\n"); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } bool opal_elog_info(uint64_t *opal_elog_id, uint64_t *opal_elog_size) { struct errorlog *head; bool rc = false; lock(&elog_write_to_host_lock); if (elog_write_to_host_head_state == ELOG_STATE_FETCHED_DATA) { head = list_top(&elog_write_to_host_pending, struct errorlog, link); if (!head) { prlog(PR_ERR, "%s: Inconsistent internal list state !\n", __func__); elog_write_to_host_head_state = ELOG_STATE_NONE; } else { *opal_elog_id = head->plid; *opal_elog_size = head->log_size; elog_write_to_host_head_state = ELOG_STATE_FETCHED_INFO; rc = true; } } unlock(&elog_write_to_host_lock); return rc; } static void opal_commit_elog_in_host(void) { struct errorlog *buf; lock(&elog_write_to_host_lock); if (!list_empty(&elog_write_to_host_pending) && (elog_write_to_host_head_state == ELOG_STATE_NONE)) { buf = list_top(&elog_write_to_host_pending, struct errorlog, link); buf->log_size = create_pel_log(buf, (char *)elog_write_to_host_buffer, ELOG_WRITE_TO_HOST_BUFFER_SIZE); elog_write_to_host_head_state = ELOG_STATE_FETCHED_DATA; opal_update_pending_evt(OPAL_EVENT_ERROR_LOG_AVAIL, OPAL_EVENT_ERROR_LOG_AVAIL); } unlock(&elog_write_to_host_lock); } bool opal_elog_read(uint64_t *buffer, uint64_t opal_elog_size, uint64_t opal_elog_id) { struct errorlog *log_data; bool rc = false; lock(&elog_write_to_host_lock); if (elog_write_to_host_head_state == ELOG_STATE_FETCHED_INFO) { log_data = list_top(&elog_write_to_host_pending, struct errorlog, link); if (!log_data) { elog_write_to_host_head_state = ELOG_STATE_NONE; unlock(&elog_write_to_host_lock); return rc; } if ((opal_elog_id != log_data->plid) && (opal_elog_size != log_data->log_size)) { unlock(&elog_write_to_host_lock); return rc; } memcpy((void *)buffer, elog_write_to_host_buffer, opal_elog_size); list_del(&log_data->link); list_add(&elog_write_to_host_processed, &log_data->link); elog_write_to_host_head_state = ELOG_STATE_NONE; rc = true; } unlock(&elog_write_to_host_lock); opal_commit_elog_in_host(); return rc; } bool opal_elog_ack(uint64_t ack_id) { bool rc = false; struct errorlog *log_data; struct errorlog *record, *next_record; lock(&elog_write_to_host_lock); if (!list_empty(&elog_write_to_host_processed)) { list_for_each_safe(&elog_write_to_host_processed, record, next_record, link) { if (record->plid != ack_id) continue; list_del(&record->link); opal_elog_complete(record, true); rc = true; } } if ((!rc) && (!list_empty(&elog_write_to_host_pending))) { log_data = list_top(&elog_write_to_host_pending, struct errorlog, link); if (ack_id == log_data->plid) elog_write_to_host_head_state = ELOG_STATE_NONE; list_for_each_safe(&elog_write_to_host_pending, record, next_record, link) { if (record->plid != ack_id) continue; list_del(&record->link); opal_elog_complete(record, true); rc = true; } } unlock(&elog_write_to_host_lock); return rc; } void opal_resend_pending_logs(void) { struct errorlog *record; lock(&elog_write_to_host_lock); if (list_empty(&elog_write_to_host_processed)) { unlock(&elog_write_to_host_lock); return; } while (!list_empty(&elog_write_to_host_processed)) { record = list_pop(&elog_write_to_host_processed, struct errorlog, link); list_add_tail(&elog_write_to_host_pending, &record->link); } elog_write_to_host_head_state = ELOG_STATE_NONE; unlock(&elog_write_to_host_lock); opal_commit_elog_in_host(); } static int opal_send_elog_to_fsp(void) { struct errorlog *head; int rc = OPAL_SUCCESS; /* Convert entry to PEL * and push it down to FSP. We wait for the ack from * FSP. */ lock(&elog_write_lock); if (!list_empty(&elog_write_to_fsp_pending)) { head = list_top(&elog_write_to_fsp_pending, struct errorlog, link); elog_plid_fsp_commit = head->plid; head->log_size = create_pel_log(head, (char *)elog_write_to_fsp_buffer, ELOG_WRITE_TO_FSP_BUFFER_SIZE); rc = fsp_opal_elog_write(head->log_size); unlock(&elog_write_lock); return rc; } unlock(&elog_write_lock); return rc; } static int opal_push_logs_sync_to_fsp(struct errorlog *buf) { struct fsp_msg *elog_msg; int opal_elog_size = 0; int rc = OPAL_SUCCESS; lock(&elog_panic_write_lock); opal_elog_size = create_pel_log(buf, (char *)elog_panic_write_buffer, ELOG_PANIC_WRITE_BUFFER_SIZE); elog_msg = fsp_mkmsg(FSP_CMD_CREATE_ERRLOG, 3, opal_elog_size, 0, PSI_DMA_ELOG_PANIC_WRITE_BUF); if (!elog_msg) { prerror("ELOG: PLID: 0x%x Failed to create message for WRITE " "to FSP\n", buf->plid); unlock(&elog_panic_write_lock); opal_elog_complete(buf, false); return OPAL_INTERNAL_ERROR; } if (fsp_sync_msg(elog_msg, false)) { fsp_freemsg(elog_msg); rc = OPAL_INTERNAL_ERROR; } else { rc = (elog_msg->resp->word1 >> 8) & 0xff; fsp_freemsg(elog_msg); } unlock(&elog_panic_write_lock); if (rc != OPAL_SUCCESS) opal_elog_complete(buf, false); else opal_elog_complete(buf, true); return rc; } static inline u64 get_elog_timeout(void) { return (mftb() + secs_to_tb(ERRORLOG_TIMEOUT_INTERVAL)); } int elog_fsp_commit(struct errorlog *buf) { int rc = OPAL_SUCCESS; /* Error needs to be committed, update the time out value */ buf->elog_timeout = get_elog_timeout(); if (buf->event_severity == OPAL_ERROR_PANIC) { rc = opal_push_logs_sync_to_fsp(buf); return rc; } lock(&elog_write_lock); if (list_empty(&elog_write_to_fsp_pending)) { list_add_tail(&elog_write_to_fsp_pending, &buf->link); unlock(&elog_write_lock); rc = opal_send_elog_to_fsp(); return rc; } list_add_tail(&elog_write_to_fsp_pending, &buf->link); unlock(&elog_write_lock); return rc; } static void elog_append_write_to_host(struct errorlog *buf) { lock(&elog_write_to_host_lock); if (list_empty(&elog_write_to_host_pending)) { list_add(&elog_write_to_host_pending, &buf->link); unlock(&elog_write_to_host_lock); opal_commit_elog_in_host(); } else { list_add_tail(&elog_write_to_host_pending, &buf->link); unlock(&elog_write_to_host_lock); } } static void elog_timeout_poll(void *data __unused) { uint64_t now; struct errorlog *head, *entry; lock(&elog_write_lock); if (list_empty(&elog_write_to_fsp_pending)) { unlock(&elog_write_lock); return; } else { head = list_top(&elog_write_to_fsp_pending, struct errorlog, link); now = mftb(); if ((tb_compare(now, head->elog_timeout) == TB_AAFTERB) || (tb_compare(now, head->elog_timeout) == TB_AEQUALB)) { entry = list_pop(&elog_write_to_fsp_pending, struct errorlog, link); unlock(&elog_write_lock); elog_append_write_to_host(entry); } else unlock(&elog_write_lock); } } /* fsp elog init function */ void fsp_elog_write_init(void) { if (!fsp_present()) return; elog_panic_write_buffer = memalign(TCE_PSIZE, ELOG_PANIC_WRITE_BUFFER_SIZE); if (!elog_panic_write_buffer) { prerror("FSP: could not allocate ELOG_PANIC_WRITE_BUFFER!\n"); return; } elog_write_to_fsp_buffer = memalign(TCE_PSIZE, ELOG_WRITE_TO_FSP_BUFFER_SIZE); if (!elog_write_to_fsp_buffer) { prerror("FSP: could not allocate ELOG_WRITE_BUFFER!\n"); return; } elog_write_to_host_buffer = memalign(TCE_PSIZE, ELOG_WRITE_TO_HOST_BUFFER_SIZE); if (!elog_write_to_host_buffer) { prerror("FSP: could not allocate ELOG_WRITE_TO_HOST_BUFFER!\n"); return; } /* Map TCEs */ fsp_tce_map(PSI_DMA_ELOG_PANIC_WRITE_BUF, elog_panic_write_buffer, PSI_DMA_ELOG_PANIC_WRITE_BUF_SZ); fsp_tce_map(PSI_DMA_ERRLOG_WRITE_BUF, elog_write_to_fsp_buffer, PSI_DMA_ERRLOG_WRITE_BUF_SZ); elog_init(); /* Add a poller */ opal_add_poller(elog_timeout_poll, NULL); } skiboot-skiboot-5.1.13/hw/fsp/fsp-epow.c000066400000000000000000000123401265204436200200410ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* FSP Environmental and Power Warnings (EPOW) support */ #define pr_fmt(fmt) "FSP-EPOW: " fmt #include #include #include #include #include #include "fsp-epow.h" /* * System EPOW status * * This value is exported to the host. Each individual element in this * array [0...(OPAL_SYSEPOW_MAX-1)] contains bitwise EPOW event info * corresponding to particular defined EPOW sub class. For example. * opal_epow_status[OPAL_SYSEPOW_POWER] will reflect power related EPOW events. */ static int16_t epow_status[OPAL_SYSEPOW_MAX]; /* EPOW lock */ static struct lock epow_lock = LOCK_UNLOCKED; /* Process FSP sent EPOW based information */ static void epow_process_ex1_event(u8 *epow) { memset(epow_status, 0, sizeof(epow_status)); if (epow[4] == EPOW_TMP_INT) { prlog(PR_INFO, "Internal temp above normal\n"); epow_status[OPAL_SYSEPOW_TEMP] = OPAL_SYSTEMP_INT; } else if (epow[4] == EPOW_TMP_AMB) { prlog(PR_INFO, "Ambient temp above normal\n"); epow_status[OPAL_SYSEPOW_TEMP] = OPAL_SYSTEMP_AMB; } else if (epow[4] == EPOW_ON_UPS) { prlog(PR_INFO, "System running on UPS power\n"); epow_status[OPAL_SYSEPOW_POWER] = OPAL_SYSPOWER_UPS; } } /* Process EPOW event */ static void fsp_process_epow(struct fsp_msg *msg, int epow_type) { int rc; u8 epow[8]; bool epow_changed = false; int16_t old_epow_status[OPAL_SYSEPOW_MAX]; /* Basic EPOW signature */ if (msg->data.bytes[0] != 0xF2) { prlog(PR_ERR, "Signature mismatch\n"); return; } lock(&epow_lock); /* Copy over and clear system EPOW status */ memcpy(old_epow_status, epow_status, sizeof(old_epow_status)); switch(epow_type) { case EPOW_NORMAL: case EPOW_EX2: break; case EPOW_EX1: epow[0] = msg->data.bytes[0]; epow[1] = msg->data.bytes[1]; epow[2] = msg->data.bytes[2]; epow[3] = msg->data.bytes[3]; epow[4] = msg->data.bytes[4]; epow_process_ex1_event(epow); break; default: prlog(PR_WARNING, "Unknown EPOW event notification\n"); break; } if (memcmp(epow_status, old_epow_status, sizeof(epow_status))) epow_changed = true; unlock(&epow_lock); /* Send OPAL message notification */ if (epow_changed) { rc = opal_queue_msg(OPAL_MSG_EPOW, NULL, NULL); if (rc) { prlog(PR_ERR, "OPAL EPOW message queuing failed\n"); return; } prlog(PR_INFO, "Notified host about EPOW event\n"); } } /* * EPOW OPAL interface * * The host requests for the system EPOW status through this * OPAl call, where it passes a buffer with a give length. * Sapphire fills the buffer with updated system EPOW status * and then updates the length variable back to reflect the * number of EPOW sub classes it has updated the buffer with. */ static int64_t fsp_opal_get_epow_status(int16_t *out_epow, int16_t *length) { int i; int n_epow_class; /* * There can be situations where the host and the Sapphire versions * don't match with eact other and hence the expected system EPOW status * details. Newer hosts might be expecting status for more number of EPOW * sub classes which Sapphire may not know about and older hosts might be * expecting status for EPOW sub classes which is a subset of what * Sapphire really knows about. Both these situations are handled here. * * (A) Host version >= Sapphire version * * Sapphire sends out EPOW status for sub classes it knows about * and keeps the status. Updates the length variable for the host. * * (B) Host version < Sapphire version * * Sapphire sends out EPOW status for sub classes host knows about * and can interpret correctly. */ if (*length >= OPAL_SYSEPOW_MAX) { n_epow_class = OPAL_SYSEPOW_MAX; *length = OPAL_SYSEPOW_MAX; } else { n_epow_class = *length; } /* Transfer EPOW Status */ for (i = 0; i < n_epow_class; i++) out_epow[i] = epow_status[i]; return OPAL_SUCCESS; } /* Handle EPOW sub-commands from FSP */ static bool fsp_epow_message(u32 cmd_sub_mod, struct fsp_msg *msg) { switch(cmd_sub_mod) { case FSP_CMD_PANELSTATUS: fsp_process_epow(msg, EPOW_NORMAL); return true; case FSP_CMD_PANELSTATUS_EX1: fsp_process_epow(msg, EPOW_EX1); return true; case FSP_CMD_PANELSTATUS_EX2: fsp_process_epow(msg, EPOW_EX2); return true; } return false; } static struct fsp_client fsp_epow_client = { .message = fsp_epow_message, }; void fsp_epow_init(void) { struct dt_node *np; fsp_register_client(&fsp_epow_client, FSP_MCLASS_SERVICE); opal_register(OPAL_GET_EPOW_STATUS, fsp_opal_get_epow_status, 2); np = dt_new(opal_node, "epow"); dt_add_property_strings(np, "compatible", "ibm,opal-v3-epow"); dt_add_property_strings(np, "epow-classes", "power", "temperature", "cooling"); prlog(PR_INFO, "FSP EPOW support initialized\n"); } skiboot-skiboot-5.1.13/hw/fsp/fsp-epow.h000066400000000000000000000020671265204436200200530ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Handle FSP EPOW event notifications */ #ifndef __FSP_EPOW_H #define __FSP_EPOW_H /* FSP based EPOW event notifications */ #define EPOW_NORMAL 0x00 /* panel status normal */ #define EPOW_EX1 0x01 /* panel status extended 1 */ #define EPOW_EX2 0x02 /* Panel status extended 2 */ /* EPOW reason code notifications */ #define EPOW_ON_UPS 1 /* System on UPS */ #define EPOW_TMP_AMB 2 /* Over ambient temperature */ #define EPOW_TMP_INT 3 /* Over internal temperature */ #endif skiboot-skiboot-5.1.13/hw/fsp/fsp-ipmi.c000066400000000000000000000224261265204436200200330ustar00rootroot00000000000000/* Copyright 2014-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include /* * Under the hood, FSP IPMI component implements the KCS (Keyboard Controller * Style) interface * * KCS interface request message format * * BYTE 1 BYTE 2 BYTE 3:N * ------------------------------------- * | NetFn/LUN | Cmd | Data | * ------------------------------------- * * KCS interface response message format * * BYTE 1 BYTE 2 BYTE 3 BYTE 4:N * ------------------------------------------------ * | NetFn/LUN | Cmd | CompCode | Data | * ------------------------------------------------ */ #define FSP_IPMI_REQ_MIN_LEN 2 /* NetFn + Cmd */ #define FSP_IPMI_RESP_MIN_LEN 3 /* NetFn + Cmd + Completion code */ DEFINE_LOG_ENTRY(OPAL_RC_IPMI_REQ, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_IPMI_RESP, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); struct fsp_ipmi_msg { struct list_node link; struct ipmi_msg ipmi_msg; }; static struct fsp_ipmi { struct list_head msg_queue; void *ipmi_req_buf; void *ipmi_resp_buf; /* There can only be one outstanding request whose reference is stored * in 'cur_msg' and the 'lock' protects against the concurrent updates * of it through request and response. The same 'lock' also protects * the list manipulation. */ struct fsp_ipmi_msg *cur_msg; struct lock lock; } fsp_ipmi; static int fsp_ipmi_send_request(void); static void fsp_ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc) { struct fsp_ipmi_msg *fsp_ipmi_msg = fsp_ipmi.cur_msg; lock(&fsp_ipmi.lock); list_del(&fsp_ipmi_msg->link); fsp_ipmi.cur_msg = NULL; unlock(&fsp_ipmi.lock); ipmi_cmd_done(cmd, netfn, cc, &fsp_ipmi_msg->ipmi_msg); } static void fsp_ipmi_req_complete(struct fsp_msg *msg) { uint8_t status = (msg->resp->word1 >> 8) & 0xff; uint32_t length = msg->resp->data.words[0]; struct fsp_ipmi_msg *fsp_ipmi_msg = msg->user_data; struct ipmi_msg *ipmi_msg; fsp_freemsg(msg); if (status != FSP_STATUS_SUCCESS) { assert(fsp_ipmi_msg == fsp_ipmi.cur_msg); ipmi_msg = &fsp_ipmi_msg->ipmi_msg; if (length != (ipmi_msg->req_size + FSP_IPMI_REQ_MIN_LEN)) prlog(PR_DEBUG, "IPMI: Length mismatch in req completion " "(%d, %d)\n", ipmi_msg->req_size, length); log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Request " "failed with status:0x%02x\n", status); /* FSP will not send the response now, so clear the current * outstanding request */ fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); /* Send the next request in the queue */ fsp_ipmi_send_request(); } } static int fsp_ipmi_send_request(void) { uint8_t *req_buf = fsp_ipmi.ipmi_req_buf; struct ipmi_msg *ipmi_msg; struct fsp_msg *msg; int rc; lock(&fsp_ipmi.lock); /* An outstanding request is still pending */ if (fsp_ipmi.cur_msg) { unlock(&fsp_ipmi.lock); return OPAL_SUCCESS; } fsp_ipmi.cur_msg = list_top(&fsp_ipmi.msg_queue, struct fsp_ipmi_msg, link); unlock(&fsp_ipmi.lock); if (!fsp_ipmi.cur_msg) return OPAL_SUCCESS; ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg; prlog(PR_TRACE, "IPMI: Send request, netfn:0x%02x, cmd:0x%02x, " "req_len:%d\n", ipmi_msg->netfn, ipmi_msg->cmd, ipmi_msg->req_size); /* KCS request message format */ *req_buf++ = ipmi_msg->netfn; /* BYTE 1 */ *req_buf++ = ipmi_msg->cmd; /* BYTE 2 */ if (ipmi_msg->req_size) memcpy(req_buf, ipmi_msg->data, ipmi_msg->req_size); msg = fsp_mkmsg(FSP_CMD_FETCH_PLAT_DATA, 5, 0, PSI_DMA_PLAT_REQ_BUF, 0, PSI_DMA_PLAT_RESP_BUF, ipmi_msg->req_size + FSP_IPMI_REQ_MIN_LEN); if (!msg) { log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to " "allocate request message\n"); fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); return OPAL_NO_MEM; } msg->user_data = fsp_ipmi.cur_msg; rc = fsp_queue_msg(msg, fsp_ipmi_req_complete); if (rc) { log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to " "queue request message (%d)\n", rc); fsp_freemsg(msg); fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } static struct ipmi_msg *fsp_ipmi_alloc_msg(size_t req_size, size_t resp_size) { struct fsp_ipmi_msg *fsp_ipmi_msg; struct ipmi_msg *ipmi_msg; fsp_ipmi_msg = zalloc(sizeof(*fsp_ipmi_msg) + MAX(req_size, resp_size)); if (!fsp_ipmi_msg) return NULL; ipmi_msg = &fsp_ipmi_msg->ipmi_msg; ipmi_msg->req_size = req_size; ipmi_msg->resp_size = resp_size; ipmi_msg->data = (uint8_t *)(fsp_ipmi_msg + 1); return ipmi_msg; } static void fsp_ipmi_free_msg(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); free(fsp_ipmi_msg); } static int fsp_ipmi_queue_msg(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); lock(&fsp_ipmi.lock); list_add_tail(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link); unlock(&fsp_ipmi.lock); return fsp_ipmi_send_request(); } static int fsp_ipmi_queue_msg_head(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); lock(&fsp_ipmi.lock); list_add(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link); unlock(&fsp_ipmi.lock); return fsp_ipmi_send_request(); } static int fsp_ipmi_dequeue_msg(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); lock(&fsp_ipmi.lock); list_del_from(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link); unlock(&fsp_ipmi.lock); return 0; } static struct ipmi_backend fsp_ipmi_backend = { .alloc_msg = fsp_ipmi_alloc_msg, .free_msg = fsp_ipmi_free_msg, .queue_msg = fsp_ipmi_queue_msg, .queue_msg_head = fsp_ipmi_queue_msg_head, .dequeue_msg = fsp_ipmi_dequeue_msg, }; static bool fsp_ipmi_send_response(uint32_t cmd) { struct fsp_msg *resp; int rc; resp = fsp_mkmsg(cmd, 0); if (!resp) { log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Failed to " "allocate response message\n"); return false; } rc = fsp_queue_msg(resp, fsp_freemsg); if (rc) { fsp_freemsg(resp); log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Failed to " "queue response message\n"); return false; } return true; } static bool fsp_ipmi_read_response(struct fsp_msg *msg) { uint8_t *resp_buf = fsp_ipmi.ipmi_resp_buf; uint32_t status = msg->data.words[3]; uint32_t length = msg->data.words[2]; struct ipmi_msg *ipmi_msg; uint8_t netfn, cmd, cc; assert(fsp_ipmi.cur_msg); ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg; /* Response TCE token */ assert(msg->data.words[1] == PSI_DMA_PLAT_RESP_BUF); if (status != FSP_STATUS_SUCCESS) { log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Response " "with bad status:0x%02x\n", status); fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); return fsp_ipmi_send_response(FSP_RSP_PLAT_DATA | FSP_STATUS_GENERIC_ERROR); } /* KCS response message format */ netfn = *resp_buf++; cmd = *resp_buf++; cc = *resp_buf++; length -= FSP_IPMI_RESP_MIN_LEN; prlog(PR_TRACE, "IPMI: fsp response received, netfn:0x%02x, cmd:0x%02x," " cc:0x%02x, length:%d\n", netfn, cmd, cc, length); if (length > ipmi_msg->resp_size) { prlog(PR_DEBUG, "IPMI: Length mismatch in response (%d, %d)\n", length, ipmi_msg->resp_size); length = ipmi_msg->resp_size; /* Truncate */ cc = IPMI_ERR_MSG_TRUNCATED; } ipmi_msg->resp_size = length; if (length) memcpy(ipmi_msg->data, resp_buf, length); fsp_ipmi_cmd_done(cmd, netfn, cc); return fsp_ipmi_send_response(FSP_RSP_PLAT_DATA); } static bool fsp_ipmi_response(uint32_t cmd_sub_mod, struct fsp_msg *msg) { bool rc; switch (cmd_sub_mod) { case FSP_CMD_SEND_PLAT_DATA: prlog(PR_TRACE, "FSP_CMD_SEND_PLAT_DATA command received\n"); rc = fsp_ipmi_read_response(msg); break; default: return false; }; /* If response sent successfully, pick the next request */ if (rc == true) fsp_ipmi_send_request(); return rc; } static struct fsp_client fsp_ipmi_client = { .message = fsp_ipmi_response, }; void fsp_ipmi_init(void) { fsp_tce_map(PSI_DMA_PLAT_REQ_BUF, fsp_ipmi.ipmi_req_buf, PSI_DMA_PLAT_REQ_BUF_SIZE); fsp_tce_map(PSI_DMA_PLAT_RESP_BUF, fsp_ipmi.ipmi_resp_buf, PSI_DMA_PLAT_RESP_BUF_SIZE); list_head_init(&fsp_ipmi.msg_queue); init_lock(&fsp_ipmi.lock); fsp_register_client(&fsp_ipmi_client, FSP_MCLASS_FETCH_SPDATA); ipmi_register_backend(&fsp_ipmi_backend); } skiboot-skiboot-5.1.13/hw/fsp/fsp-leds.c000066400000000000000000001433321265204436200200240ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * LED location code and indicator handling */ #define pr_fmt(fmt) "FSPLED: " fmt #include #include #include #include #include #include #include #include #include #include #define buf_write(p, type, val) do { *(type *)(p) = val;\ p += sizeof(type); } while(0) #define buf_read(p, type, addr) do { *addr = *(type *)(p);\ p += sizeof(type); } while(0) /* SPCN replay threshold */ #define SPCN_REPLAY_THRESHOLD 2 /* LED support status */ enum led_support_state { LED_STATE_ABSENT, LED_STATE_READING, LED_STATE_PRESENT, }; static enum led_support_state led_support = LED_STATE_ABSENT; /* * PSI mapped buffer for LED data * * Mapped once and never unmapped. Used for fetching all * available LED information and creating the list. Also * used for setting individual LED state. * */ static void *led_buffer; static u8 *loc_code_list_buffer = NULL; /* Maintain list of all LEDs * * The contents here will be used to cater requests from FSP * async commands and HV initiated OPAL calls. */ static struct list_head cec_ledq; /* CEC LED list */ static struct list_head encl_ledq; /* Enclosure LED list */ static struct list_head spcn_cmdq; /* SPCN command queue */ /* LED lock */ static struct lock led_lock = LOCK_UNLOCKED; static struct lock spcn_cmd_lock = LOCK_UNLOCKED; static struct lock sai_lock = LOCK_UNLOCKED; static bool spcn_cmd_complete = true; /* SPCN command complete */ /* Last SPCN command */ static u32 last_spcn_cmd; static int replay = 0; /* * FSP controls System Attention Indicator. But it expects hypervisor * keep track of the status and serve get LED state request (both from * Linux and FSP itself)! */ static struct sai_data sai_data; /* Forward declaration */ static void fsp_read_leds_data_complete(struct fsp_msg *msg); static int process_led_state_change(void); DEFINE_LOG_ENTRY(OPAL_RC_LED_SPCN, OPAL_PLATFORM_ERR_EVT, OPAL_LED, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LED_BUFF, OPAL_PLATFORM_ERR_EVT, OPAL_LED, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LED_LC, OPAL_PLATFORM_ERR_EVT, OPAL_LED, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LED_STATE, OPAL_PLATFORM_ERR_EVT, OPAL_LED, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LED_SUPPORT, OPAL_PLATFORM_ERR_EVT, OPAL_LED, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); /* Find descendent LED record with CEC location code in CEC list */ static struct fsp_led_data *fsp_find_cec_led(char *loc_code) { struct fsp_led_data *led, *next; list_for_each_safe(&cec_ledq, led, next, link) { if (strcmp(led->loc_code, loc_code)) continue; return led; } return NULL; } /* Find encl LED record with ENCL location code in ENCL list */ static struct fsp_led_data *fsp_find_encl_led(char *loc_code) { struct fsp_led_data *led, *next; list_for_each_safe(&encl_ledq, led, next, link) { if (strcmp(led->loc_code, loc_code)) continue; return led; } return NULL; } /* Find encl LED record with CEC location code in CEC list */ static struct fsp_led_data *fsp_find_encl_cec_led(char *loc_code) { struct fsp_led_data *led, *next; list_for_each_safe(&cec_ledq, led, next, link) { if (strstr(led->loc_code, "-")) continue; if (!strstr(loc_code, led->loc_code)) continue; return led; } return NULL; } /* Find encl LED record with CEC location code in ENCL list */ static struct fsp_led_data *fsp_find_encl_encl_led(char *loc_code) { struct fsp_led_data *led, *next; list_for_each_safe(&encl_ledq, led, next, link) { if (!strstr(loc_code, led->loc_code)) continue; return led; } return NULL; } /* Compute the ENCL LED status in CEC list */ static void compute_encl_status_cec(struct fsp_led_data *encl_led) { struct fsp_led_data *led, *next; encl_led->status &= ~SPCN_LED_IDENTIFY_MASK; encl_led->status &= ~SPCN_LED_FAULT_MASK; list_for_each_safe(&cec_ledq, led, next, link) { if (!strstr(led->loc_code, encl_led->loc_code)) continue; /* Don't count the enclsure LED itself */ if (!strcmp(led->loc_code, encl_led->loc_code)) continue; if (led->status & SPCN_LED_IDENTIFY_MASK) encl_led->status |= SPCN_LED_IDENTIFY_MASK; if (led->status & SPCN_LED_FAULT_MASK) encl_led->status |= SPCN_LED_FAULT_MASK; } } /* Is a enclosure LED */ static bool is_enclosure_led(char *loc_code) { if (strstr(loc_code, "-")) return false; if (!fsp_find_cec_led(loc_code) || !fsp_find_encl_led(loc_code)) return false; return true; } static inline void opal_led_update_complete(u64 async_token, u64 result) { opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, async_token, result); } static inline bool is_sai_loc_code(char *loc_code) { if (!strcmp(sai_data.loc_code, loc_code)) return true; return false; } /* Set/Reset System attention indicator */ static void fsp_set_sai_complete(struct fsp_msg *msg) { int ret = OPAL_SUCCESS; int rc = msg->resp->word1 & 0xff00; struct led_set_cmd *spcn_cmd = (struct led_set_cmd *)msg->user_data; if (rc) { prlog(PR_ERR, "Update SAI cmd failed [rc=%d].\n", rc); ret = OPAL_INTERNAL_ERROR; /* Roll back */ lock(&sai_lock); sai_data.state = spcn_cmd->ckpt_status; unlock(&sai_lock); } if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) opal_led_update_complete(spcn_cmd->async_token, ret); /* free msg and spcn command */ free(spcn_cmd); fsp_freemsg(msg); /* Process pending LED update request */ process_led_state_change(); } static int fsp_set_sai(struct led_set_cmd *spcn_cmd) { int rc = -ENOMEM; uint32_t cmd = FSP_CMD_SA_INDICATOR; struct fsp_msg *msg; /* * FSP does not allow hypervisor to set real SAI, but we can * reset real SAI. Also in our case only host can control * LEDs, not guests. Hence we will set platform virtual SAI * and reset real SAI. */ if (spcn_cmd->state == LED_STATE_ON) cmd |= FSP_LED_SET_PLAT_SAI; else cmd |= FSP_LED_RESET_REAL_SAI; prlog(PR_TRACE, "Update SAI Indicator [cur : 0x%x, new : 0x%x].\n", sai_data.state, spcn_cmd->state); msg = fsp_mkmsg(cmd, 0); if (!msg) { prlog(PR_ERR, "%s: Memory allocation failed.\n", __func__); goto sai_fail; } spcn_cmd->ckpt_status = sai_data.state; msg->user_data = spcn_cmd; rc = fsp_queue_msg(msg, fsp_set_sai_complete); if (rc) { fsp_freemsg(msg); prlog(PR_ERR, "%s: Failed to queue the message\n", __func__); goto sai_fail; } lock(&sai_lock); sai_data.state = spcn_cmd->state; unlock(&sai_lock); return OPAL_SUCCESS; sai_fail: if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) opal_led_update_complete(spcn_cmd->async_token, OPAL_INTERNAL_ERROR); return OPAL_INTERNAL_ERROR; } static void fsp_get_sai_complete(struct fsp_msg *msg) { int rc = msg->resp->word1 & 0xff00; if (rc) { prlog(PR_ERR, "Read real SAI cmd failed [rc = 0x%x].\n", rc); } else { /* Update SAI state */ lock(&sai_lock); sai_data.state = msg->resp->data.words[0] & 0xff; unlock(&sai_lock); prlog(PR_TRACE, "SAI initial state = 0x%x\n", sai_data.state); } fsp_freemsg(msg); } /* Read initial SAI state. */ static void fsp_get_sai(void) { int rc; uint32_t cmd = FSP_CMD_SA_INDICATOR | FSP_LED_READ_REAL_SAI; struct fsp_msg *msg; msg = fsp_mkmsg(cmd, 0); if (!msg) { prlog(PR_ERR, "%s: Memory allocation failed.\n", __func__); return; } rc = fsp_queue_msg(msg, fsp_get_sai_complete); if (rc) { fsp_freemsg(msg); prlog(PR_ERR, "%s: Failed to queue the message\n", __func__); } } static bool sai_update_notification(struct fsp_msg *msg) { uint32_t *state = &msg->data.words[2]; uint32_t param_id = msg->data.words[0]; int len = msg->data.words[1] & 0xffff; if (param_id != SYS_PARAM_REAL_SAI && param_id != SYS_PARAM_PLAT_SAI) return false; if ( len != 4) return false; if (*state != LED_STATE_ON && *state != LED_STATE_OFF) return false; /* Update SAI state */ lock(&sai_lock); sai_data.state = *state; unlock(&sai_lock); prlog(PR_TRACE, "SAI updated. New SAI state = 0x%x\n", *state); return true; } /* * Update both the local LED lists to reflect upon led state changes * occurred with the recent SPCN command. Subsequent LED requests will * be served with these updates changed to the list. */ static void update_led_list(char *loc_code, u32 led_state, u32 excl_bit) { struct fsp_led_data *led = NULL, *encl_led = NULL, *encl_cec_led = NULL; bool is_encl_led = is_enclosure_led(loc_code); /* Enclosure LED in CEC list */ encl_cec_led = fsp_find_encl_cec_led(loc_code); if (!encl_cec_led) { log_simple_error(&e_info(OPAL_RC_LED_LC), "Could not find enclosure LED in CEC LC=%s\n", loc_code); return; } /* Update state */ if (is_encl_led) { /* Enclosure exclusive bit */ encl_cec_led->excl_bit = excl_bit; } else { /* Descendant LED in CEC list */ led = fsp_find_cec_led(loc_code); if (!led) { log_simple_error(&e_info(OPAL_RC_LED_LC), "Could not find descendent LED in \ CEC LC=%s\n", loc_code); return; } led->status = led_state; } /* Enclosure LED in ENCL list */ encl_led = fsp_find_encl_encl_led(loc_code); if (!encl_led) { log_simple_error(&e_info(OPAL_RC_LED_LC), "Could not find enclosure LED in ENCL LC=%s\n", loc_code); return; } /* Compute descendent rolled up status */ compute_encl_status_cec(encl_cec_led); /* Check whether exclussive bits set */ if (encl_cec_led->excl_bit & FSP_LED_EXCL_FAULT) encl_cec_led->status |= SPCN_LED_FAULT_MASK; if (encl_cec_led->excl_bit & FSP_LED_EXCL_IDENTIFY) encl_cec_led->status |= SPCN_LED_IDENTIFY_MASK; /* Copy over */ encl_led->status = encl_cec_led->status; encl_led->excl_bit = encl_cec_led->excl_bit; } static int fsp_set_led_response(uint32_t cmd) { struct fsp_msg *msg; int rc = -1; msg = fsp_mkmsg(cmd, 0); if (!msg) { prerror("Failed to allocate FSP_RSP_SET_LED_STATE [cmd=%x])\n", cmd); } else { rc = fsp_queue_msg(msg, fsp_freemsg); if (rc != OPAL_SUCCESS) { fsp_freemsg(msg); prerror("Failed to queue FSP_RSP_SET_LED_STATE" " [cmd=%x]\n", cmd); } } return rc; } static void fsp_spcn_set_led_completion(struct fsp_msg *msg) { struct fsp_msg *resp = msg->resp; u32 cmd = FSP_RSP_SET_LED_STATE; u8 status = resp->word1 & 0xff00; struct led_set_cmd *spcn_cmd = (struct led_set_cmd *)msg->user_data; lock(&led_lock); /* * LED state update request came as part of FSP async message * FSP_CMD_SET_LED_STATE, we need to send response message. * * Also if SPCN command failed, then roll back changes. */ if (status != FSP_STATUS_SUCCESS) { log_simple_error(&e_info(OPAL_RC_LED_SPCN), "Last SPCN command failed, status=%02x\n", status); cmd |= FSP_STATUS_GENERIC_ERROR; /* Rollback the changes */ update_led_list(spcn_cmd->loc_code, spcn_cmd->ckpt_status, spcn_cmd->ckpt_excl_bit); } /* FSP initiated SPCN command */ if (spcn_cmd->cmd_src == SPCN_SRC_FSP) fsp_set_led_response(cmd); /* OPAL initiated SPCN command */ if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) { if (status != FSP_STATUS_SUCCESS) opal_led_update_complete(spcn_cmd->async_token, OPAL_INTERNAL_ERROR); else opal_led_update_complete(spcn_cmd->async_token, OPAL_SUCCESS); } unlock(&led_lock); /* free msg and spcn command */ free(spcn_cmd); fsp_freemsg(msg); /* Process pending LED update request */ process_led_state_change(); } /* * Set the state of the LED pointed by the location code * * LED command: FAULT state or IDENTIFY state * LED state : OFF (reset) or ON (set) * * SPCN TCE mapped buffer entries for setting LED state * * struct spcn_led_data { * u8 lc_len; * u16 state; * char lc_code[LOC_CODE_SIZE]; *}; */ static int fsp_msg_set_led_state(struct led_set_cmd *spcn_cmd) { struct spcn_led_data sled; struct fsp_msg *msg = NULL; struct fsp_led_data *led = NULL; void *buf = led_buffer; u16 data_len = 0; u32 cmd_hdr = 0; u32 cmd = FSP_RSP_SET_LED_STATE; int rc = -1; sled.lc_len = strlen(spcn_cmd->loc_code); strncpy(sled.lc_code, spcn_cmd->loc_code, sled.lc_len); lock(&led_lock); /* Location code length + Location code + LED control */ data_len = LOC_CODE_LEN + sled.lc_len + LED_CONTROL_LEN; cmd_hdr = SPCN_MOD_SET_LED_CTL_LOC_CODE << 24 | SPCN_CMD_SET << 16 | data_len; /* Fetch the current state of LED */ led = fsp_find_cec_led(spcn_cmd->loc_code); /* LED not present */ if (led == NULL) { if (spcn_cmd->cmd_src == SPCN_SRC_FSP) { cmd |= FSP_STATUS_INVALID_LC; fsp_set_led_response(cmd); } if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) opal_led_update_complete(spcn_cmd->async_token, OPAL_INTERNAL_ERROR); unlock(&led_lock); return rc; } /* * Checkpoint the status here, will use it if the SPCN * command eventually fails. */ spcn_cmd->ckpt_status = led->status; spcn_cmd->ckpt_excl_bit = led->excl_bit; sled.state = led->status; /* Update the exclussive LED bits */ if (is_enclosure_led(spcn_cmd->loc_code)) { if (spcn_cmd->command == LED_COMMAND_FAULT) { if (spcn_cmd->state == LED_STATE_ON) led->excl_bit |= FSP_LED_EXCL_FAULT; if (spcn_cmd->state == LED_STATE_OFF) led->excl_bit &= ~FSP_LED_EXCL_FAULT; } if (spcn_cmd->command == LED_COMMAND_IDENTIFY) { if (spcn_cmd->state == LED_STATE_ON) led->excl_bit |= FSP_LED_EXCL_IDENTIFY; if (spcn_cmd->state == LED_STATE_OFF) led->excl_bit &= ~FSP_LED_EXCL_IDENTIFY; } } /* LED FAULT commad */ if (spcn_cmd->command == LED_COMMAND_FAULT) { if (spcn_cmd->state == LED_STATE_ON) sled.state |= SPCN_LED_FAULT_MASK; if (spcn_cmd->state == LED_STATE_OFF) sled.state &= ~SPCN_LED_FAULT_MASK; } /* LED IDENTIFY command */ if (spcn_cmd->command == LED_COMMAND_IDENTIFY) { if (spcn_cmd->state == LED_STATE_ON) sled.state |= SPCN_LED_IDENTIFY_MASK; if (spcn_cmd->state == LED_STATE_OFF) sled.state &= ~SPCN_LED_IDENTIFY_MASK; } /* Write into SPCN TCE buffer */ buf_write(buf, u8, sled.lc_len); /* Location code length */ strncpy(buf, sled.lc_code, sled.lc_len); /* Location code */ buf += sled.lc_len; buf_write(buf, u16, sled.state); /* LED state */ msg = fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_hdr, 0, PSI_DMA_LED_BUF); if (!msg) { cmd |= FSP_STATUS_GENERIC_ERROR; rc = -1; goto update_fail; } /* * Update the local lists based on the attempted SPCN command to * set/reset an individual led (CEC or ENCL). */ update_led_list(spcn_cmd->loc_code, sled.state, led->excl_bit); msg->user_data = spcn_cmd; rc = fsp_queue_msg(msg, fsp_spcn_set_led_completion); if (rc != OPAL_SUCCESS) { cmd |= FSP_STATUS_GENERIC_ERROR; fsp_freemsg(msg); /* Revert LED state update */ update_led_list(spcn_cmd->loc_code, spcn_cmd->ckpt_status, spcn_cmd->ckpt_excl_bit); } update_fail: if (rc) { log_simple_error(&e_info(OPAL_RC_LED_STATE), "Set led state failed at LC=%s\n", spcn_cmd->loc_code); if (spcn_cmd->cmd_src == SPCN_SRC_FSP) fsp_set_led_response(cmd); if (spcn_cmd->cmd_src == SPCN_SRC_OPAL) opal_led_update_complete(spcn_cmd->async_token, OPAL_INTERNAL_ERROR); } unlock(&led_lock); return rc; } /* * process_led_state_change * * If the command queue is empty, it sets the 'spcn_cmd_complete' as true * and just returns. Else it pops one element from the command queue * and processes the command for the requested LED state change. */ static int process_led_state_change(void) { struct led_set_cmd *spcn_cmd; int rc = 0; /* * The command queue is empty. This will only * happen during the SPCN command callback path * in which case we set 'spcn_cmd_complete' as true. */ lock(&spcn_cmd_lock); if (list_empty(&spcn_cmdq)) { spcn_cmd_complete = true; unlock(&spcn_cmd_lock); return rc; } spcn_cmd = list_pop(&spcn_cmdq, struct led_set_cmd, link); unlock(&spcn_cmd_lock); if (is_sai_loc_code(spcn_cmd->loc_code)) rc = fsp_set_sai(spcn_cmd); else rc = fsp_msg_set_led_state(spcn_cmd); if (rc) { free(spcn_cmd); process_led_state_change(); } return rc; } /* * queue_led_state_change * * FSP async command or OPAL based request for LED state change gets queued * up in the command queue. If no previous SPCN command is pending, then it * immediately pops up one element from the list and processes it. If previous * SPCN commands are still pending then it just queues up and return. When the * SPCN command callback gets to execute, it processes one element from the * list and keeps the chain execution going. At last when there are no elements * in the command queue it sets 'spcn_cmd_complete' as true again. */ static int queue_led_state_change(char *loc_code, u8 command, u8 state, int cmd_src, uint64_t async_token) { struct led_set_cmd *cmd; int rc = 0; /* New request node */ cmd = zalloc(sizeof(struct led_set_cmd)); if (!cmd) { prlog(PR_ERR, "SPCN set command node allocation failed\n"); return -1; } /* Save the request */ strncpy(cmd->loc_code, loc_code, LOC_CODE_SIZE - 1); cmd->command = command; cmd->state = state; cmd->cmd_src = cmd_src; cmd->async_token = async_token; /* Add to the queue */ lock(&spcn_cmd_lock); list_add_tail(&spcn_cmdq, &cmd->link); /* No previous SPCN command pending */ if (spcn_cmd_complete) { spcn_cmd_complete = false; unlock(&spcn_cmd_lock); rc = process_led_state_change(); return rc; } unlock(&spcn_cmd_lock); return rc; } /* * Write single location code information into the TCE outbound buffer * * Data layout * * 2 bytes - Length of location code structure * 4 bytes - CCIN in ASCII * 1 byte - Resource status flag * 1 byte - Indicator state * 1 byte - Raw loc code length * 1 byte - Loc code field size * Field size byte - Null terminated ASCII string padded to 4 byte boundary * */ static u32 fsp_push_data_to_tce(struct fsp_led_data *led, u8 *out_data, u32 total_size) { struct fsp_loc_code_data lcode; /* CCIN value is irrelevant */ lcode.ccin = 0x0; lcode.status = FSP_IND_NOT_IMPLMNTD; if (led->parms & SPCN_LED_IDENTIFY_MASK) lcode.status = FSP_IND_IMPLMNTD; /* LED indicator status */ lcode.ind_state = FSP_IND_INACTIVE; if (led->status & SPCN_LED_IDENTIFY_MASK) lcode.ind_state |= FSP_IND_IDENTIFY_ACTV; if (led->status & SPCN_LED_FAULT_MASK) lcode.ind_state |= FSP_IND_FAULT_ACTV; /* Location code */ memset(lcode.loc_code, 0, LOC_CODE_SIZE); lcode.raw_len = strlen(led->loc_code); strncpy(lcode.loc_code, led->loc_code, lcode.raw_len); lcode.fld_sz = sizeof(lcode.loc_code); /* Rest of the structure */ lcode.size = sizeof(lcode); lcode.status &= 0x0f; /* * Check for outbound buffer overflow. If there are still * more LEDs to be sent across to FSP, don't send, ignore. */ if ((total_size + lcode.size) > PSI_DMA_LOC_COD_BUF_SZ) return 0; /* Copy over to the buffer */ memcpy(out_data, &lcode, sizeof(lcode)); return lcode.size; } /* * Send out LED information structure pointed by "loc_code" * to FSP through the PSI DMA mapping. Buffer layout structure * must be followed. */ static void fsp_ret_loc_code_list(u16 req_type, char *loc_code) { struct fsp_led_data *led, *next; struct fsp_msg *msg; u8 *data; /* Start of TCE mapped buffer */ u8 *out_data; /* Start of location code data */ u32 bytes_sent = 0, total_size = 0; u16 header_size = 0, flags = 0; if (loc_code_list_buffer == NULL) { prerror("No loc_code_list_buffer\n"); return; } /* Init the addresses */ data = loc_code_list_buffer; out_data = NULL; /* Unmapping through FSP_CMD_RET_LOC_BUFFER command */ fsp_tce_map(PSI_DMA_LOC_COD_BUF, (void *)data, PSI_DMA_LOC_COD_BUF_SZ); out_data = data + 8; /* CEC LED list */ list_for_each_safe(&cec_ledq, led, next, link) { /* * When the request type is system wide led list * i.e GET_LC_CMPLT_SYS, send the entire contents * of the CEC list including both all descendents * and all of their enclosures. */ if (req_type == GET_LC_ENCLOSURES) break; if (req_type == GET_LC_ENCL_DESCENDANTS) { if (strstr(led->loc_code, loc_code) == NULL) continue; } if (req_type == GET_LC_SINGLE_LOC_CODE) { if (strcmp(led->loc_code, loc_code)) continue; } /* Push the data into TCE buffer */ bytes_sent = 0; bytes_sent = fsp_push_data_to_tce(led, out_data, total_size); /* Advance the TCE pointer */ out_data += bytes_sent; total_size += bytes_sent; } /* Enclosure LED list */ if (req_type == GET_LC_ENCLOSURES) { list_for_each_safe(&encl_ledq, led, next, link) { /* Push the data into TCE buffer */ bytes_sent = 0; bytes_sent = fsp_push_data_to_tce(led, out_data, total_size); /* Advance the TCE pointer */ out_data += bytes_sent; total_size += bytes_sent; } } /* Count from 'data' instead of 'data_out' */ total_size += 8; memcpy(data, &total_size, sizeof(total_size)); header_size = OUTBUF_HEADER_SIZE; memcpy(data + sizeof(total_size), &header_size, sizeof(header_size)); if (req_type == GET_LC_ENCL_DESCENDANTS) flags = 0x8000; memcpy(data + sizeof(total_size) + sizeof(header_size), &flags, sizeof(flags)); msg = fsp_mkmsg(FSP_RSP_GET_LED_LIST, 3, 0, PSI_DMA_LOC_COD_BUF, total_size); if (!msg) { prerror("Failed to allocate FSP_RSP_GET_LED_LIST.\n"); } else { if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("Failed to queue FSP_RSP_GET_LED_LIST\n"); } } } /* * FSP async command: FSP_CMD_GET_LED_LIST * * (1) FSP sends the list of location codes through inbound buffer * (2) HV sends the status of those location codes through outbound buffer * * Inbound buffer data layout (loc code request structure) * * 2 bytes - Length of entire structure * 2 bytes - Request type * 1 byte - Raw length of location code * 1 byte - Location code field size * `Field size` bytes - NULL terminated ASCII location code string */ static void fsp_get_led_list(struct fsp_msg *msg) { struct fsp_loc_code_req req; u32 tce_token = msg->data.words[1]; void *buf; /* Parse inbound buffer */ buf = fsp_inbound_buf_from_tce(tce_token); if (!buf) { struct fsp_msg *msg; msg = fsp_mkmsg(FSP_RSP_GET_LED_LIST | FSP_STATUS_INVALID_DATA, 0); if (!msg) { prerror("Failed to allocate FSP_RSP_GET_LED_LIST" " | FSP_STATUS_INVALID_DATA\n"); } else { if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("Failed to queue " "FSP_RSP_GET_LED_LIST |" " FSP_STATUS_INVALID_DATA\n"); } } return; } memcpy(&req, buf, sizeof(req)); prlog(PR_TRACE, "Request for loc code list type 0x%04x LC=%s\n", req.req_type, req.loc_code); fsp_ret_loc_code_list(req.req_type, req.loc_code); } /* * FSP async command: FSP_CMD_RET_LOC_BUFFER * * With this command FSP returns ownership of the outbound buffer * used by Sapphire to pass the indicator list previous time. That * way FSP tells Sapphire that it has consumed all the data present * on the outbound buffer and Sapphire can reuse it for next request. */ static void fsp_free_led_list_buf(struct fsp_msg *msg) { u32 tce_token = msg->data.words[1]; u32 cmd = FSP_RSP_RET_LED_BUFFER; struct fsp_msg *resp; /* Token does not point to outbound buffer */ if (tce_token != PSI_DMA_LOC_COD_BUF) { log_simple_error(&e_info(OPAL_RC_LED_BUFF), "Invalid tce token from FSP\n"); cmd |= FSP_STATUS_GENERIC_ERROR; resp = fsp_mkmsg(cmd, 0); if (!resp) { prerror("Failed to allocate FSP_RSP_RET_LED_BUFFER" "| FSP_STATUS_GENERIC_ERROR\n"); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("Failed to queue " "RET_LED_BUFFER|ERROR\n"); } return; } /* Unmap the location code DMA buffer */ fsp_tce_unmap(PSI_DMA_LOC_COD_BUF, PSI_DMA_LOC_COD_BUF_SZ); resp = fsp_mkmsg(cmd, 0); if (!resp) { prerror("Failed to allocate FSP_RSP_RET_LED_BUFFER\n"); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("Failed to queue FSP_RSP_RET_LED_BUFFER\n"); } } static void fsp_ret_led_state(char *loc_code) { bool found = false; u8 ind_state = 0; u32 cmd = FSP_RSP_GET_LED_STATE; struct fsp_led_data *led, *next; struct fsp_msg *msg; if (is_sai_loc_code(loc_code)) { if (sai_data.state & OPAL_SLOT_LED_STATE_ON) ind_state = FSP_IND_FAULT_ACTV; found = true; } else { list_for_each_safe(&cec_ledq, led, next, link) { if (strcmp(loc_code, led->loc_code)) continue; /* Found the location code */ if (led->status & SPCN_LED_IDENTIFY_MASK) ind_state |= FSP_IND_IDENTIFY_ACTV; if (led->status & SPCN_LED_FAULT_MASK) ind_state |= FSP_IND_FAULT_ACTV; found = true; break; } } /* Location code not found */ if (!found) { log_simple_error(&e_info(OPAL_RC_LED_LC), "Could not find the location code LC=%s\n", loc_code); cmd |= FSP_STATUS_INVALID_LC; ind_state = 0xff; } msg = fsp_mkmsg(cmd, 1, ind_state); if (!msg) { prerror("Couldn't alloc FSP_RSP_GET_LED_STATE\n"); return; } if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("Couldn't queue FSP_RSP_GET_LED_STATE\n"); } } /* * FSP async command: FSP_CMD_GET_LED_STATE * * With this command FSP query the state for any given LED */ static void fsp_get_led_state(struct fsp_msg *msg) { struct fsp_get_ind_state_req req; u32 tce_token = msg->data.words[1]; void *buf; /* Parse the inbound buffer */ buf = fsp_inbound_buf_from_tce(tce_token); if (!buf) { struct fsp_msg *msg; msg = fsp_mkmsg(FSP_RSP_GET_LED_STATE | FSP_STATUS_INVALID_DATA, 0); if (!msg) { prerror("Failed to allocate FSP_RSP_GET_LED_STATE" " | FSP_STATUS_INVALID_DATA\n"); return; } if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("Failed to queue FSP_RSP_GET_LED_STATE" " | FSP_STATUS_INVALID_DATA\n"); } return; } memcpy(&req, buf, sizeof(req)); prlog(PR_TRACE, "%s: tce=0x%08x buf=%p rq.sz=%d rq.lc_len=%d" " rq.fld_sz=%d LC: %02x %02x %02x %02x....\n", __func__, tce_token, buf, req.size, req.lc_len, req.fld_sz, req.loc_code[0], req.loc_code[1], req.loc_code[2], req.loc_code[3]); /* Bound check */ if (req.lc_len >= LOC_CODE_SIZE) { log_simple_error(&e_info(OPAL_RC_LED_LC), "Loc code too large in %s: %d bytes\n", __func__, req.lc_len); req.lc_len = LOC_CODE_SIZE - 1; } /* Ensure NULL termination */ req.loc_code[req.lc_len] = 0; /* Do the deed */ fsp_ret_led_state(req.loc_code); } /* * FSP async command: FSP_CMD_SET_LED_STATE * * With this command FSP sets/resets the state for any given LED */ static void fsp_set_led_state(struct fsp_msg *msg) { struct fsp_set_ind_state_req req; struct fsp_led_data *led, *next; u32 tce_token = msg->data.words[1]; bool command, state; void *buf; int rc; /* Parse the inbound buffer */ buf = fsp_inbound_buf_from_tce(tce_token); if (!buf) { fsp_set_led_response(FSP_RSP_SET_LED_STATE | FSP_STATUS_INVALID_DATA); return; } memcpy(&req, buf, sizeof(req)); prlog(PR_TRACE, "%s: tce=0x%08x buf=%p rq.sz=%d rq.typ=0x%04x" " rq.lc_len=%d rq.fld_sz=%d LC: %02x %02x %02x %02x....\n", __func__, tce_token, buf, req.size, req.lc_len, req.fld_sz, req.req_type, req.loc_code[0], req.loc_code[1], req.loc_code[2], req.loc_code[3]); /* Bound check */ if (req.lc_len >= LOC_CODE_SIZE) { log_simple_error(&e_info(OPAL_RC_LED_LC), "Loc code too large in %s: %d bytes\n", __func__, req.lc_len); req.lc_len = LOC_CODE_SIZE - 1; } /* Ensure NULL termination */ req.loc_code[req.lc_len] = 0; /* Decode command */ command = (req.ind_state & LOGICAL_IND_STATE_MASK) ? LED_COMMAND_FAULT : LED_COMMAND_IDENTIFY; state = (req.ind_state & ACTIVE_LED_STATE_MASK) ? LED_STATE_ON : LED_STATE_OFF; /* Handle requests */ switch (req.req_type) { case SET_IND_ENCLOSURE: list_for_each_safe(&cec_ledq, led, next, link) { /* Only descendants of the same enclosure */ if (!strstr(led->loc_code, req.loc_code)) continue; /* Skip the enclosure */ if (!strcmp(led->loc_code, req.loc_code)) continue; rc = queue_led_state_change(led->loc_code, command, state, SPCN_SRC_FSP, 0); if (rc != 0) fsp_set_led_response(FSP_RSP_SET_LED_STATE | FSP_STATUS_GENERIC_ERROR); } break; case SET_IND_SINGLE_LOC_CODE: /* Set led state for single descendent led */ rc = queue_led_state_change(req.loc_code, command, state, SPCN_SRC_FSP, 0); if (rc != 0) fsp_set_led_response(FSP_RSP_SET_LED_STATE | FSP_STATUS_GENERIC_ERROR); break; default: fsp_set_led_response(FSP_RSP_SET_LED_STATE | FSP_STATUS_NOT_SUPPORTED); break; } } /* Handle received indicator message from FSP */ static bool fsp_indicator_message(u32 cmd_sub_mod, struct fsp_msg *msg) { u32 cmd; struct fsp_msg *resp; /* LED support not available yet */ if (led_support != LED_STATE_PRESENT) { log_simple_error(&e_info(OPAL_RC_LED_SUPPORT), "Indicator message while LED support not" " available yet\n"); return false; } switch (cmd_sub_mod) { case FSP_CMD_GET_LED_LIST: prlog(PR_TRACE, "FSP_CMD_GET_LED_LIST command received\n"); fsp_get_led_list(msg); return true; case FSP_CMD_RET_LED_BUFFER: prlog(PR_TRACE, "FSP_CMD_RET_LED_BUFFER command received\n"); fsp_free_led_list_buf(msg); return true; case FSP_CMD_GET_LED_STATE: prlog(PR_TRACE, "FSP_CMD_GET_LED_STATE command received\n"); fsp_get_led_state(msg); return true; case FSP_CMD_SET_LED_STATE: prlog(PR_TRACE, "FSP_CMD_SET_LED_STATE command received\n"); fsp_set_led_state(msg); return true; /* * FSP async sub commands which have not been implemented. * For these async sub commands, print for the log and ack * the field service processor with a generic error. */ case FSP_CMD_GET_MTMS_LIST: prlog(PR_TRACE, "FSP_CMD_GET_MTMS_LIST command received\n"); cmd = FSP_RSP_GET_MTMS_LIST; break; case FSP_CMD_RET_MTMS_BUFFER: prlog(PR_TRACE, "FSP_CMD_RET_MTMS_BUFFER command received\n"); cmd = FSP_RSP_RET_MTMS_BUFFER; break; case FSP_CMD_SET_ENCL_MTMS: prlog(PR_TRACE, "FSP_CMD_SET_MTMS command received\n"); cmd = FSP_RSP_SET_ENCL_MTMS; break; case FSP_CMD_CLR_INCT_ENCL: prlog(PR_TRACE, "FSP_CMD_CLR_INCT_ENCL command received\n"); cmd = FSP_RSP_CLR_INCT_ENCL; break; case FSP_CMD_ENCL_MCODE_INIT: prlog(PR_TRACE, "FSP_CMD_ENCL_MCODE_INIT command received\n"); cmd = FSP_RSP_ENCL_MCODE_INIT; break; case FSP_CMD_ENCL_MCODE_INTR: prlog(PR_TRACE, "FSP_CMD_ENCL_MCODE_INTR command received\n"); cmd = FSP_RSP_ENCL_MCODE_INTR; break; case FSP_CMD_ENCL_POWR_TRACE: prlog(PR_TRACE, "FSP_CMD_ENCL_POWR_TRACE command received\n"); cmd = FSP_RSP_ENCL_POWR_TRACE; break; case FSP_CMD_RET_ENCL_TRACE_BUFFER: prlog(PR_TRACE, "FSP_CMD_RET_ENCL_TRACE_BUFFER command received\n"); cmd = FSP_RSP_RET_ENCL_TRACE_BUFFER; break; case FSP_CMD_GET_SPCN_LOOP_STATUS: prlog(PR_TRACE, "FSP_CMD_GET_SPCN_LOOP_STATUS command received\n"); cmd = FSP_RSP_GET_SPCN_LOOP_STATUS; break; case FSP_CMD_INITIATE_LAMP_TEST: /* XXX: FSP ACK not required for this sub command */ prlog(PR_TRACE, "FSP_CMD_INITIATE_LAMP_TEST command received\n"); return true; default: return false; } cmd |= FSP_STATUS_GENERIC_ERROR; resp = fsp_mkmsg(cmd, 0); if (!resp) { prerror("Failed to allocate FSP_STATUS_GENERIC_ERROR\n"); return false; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("Failed to queue FSP_STATUS_GENERIC_ERROR\n"); return false; } return true; } /* Indicator class client */ static struct fsp_client fsp_indicator_client = { .message = fsp_indicator_message, }; static int fsp_opal_get_sai(u64 *led_mask, u64 *led_value) { *led_mask |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ATTN; if (sai_data.state & OPAL_SLOT_LED_STATE_ON) *led_value |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ATTN; return OPAL_SUCCESS; } static int fsp_opal_set_sai(uint64_t async_token, char *loc_code, const u64 led_mask, const u64 led_value) { int state = LED_STATE_OFF; if (!((led_mask >> OPAL_SLOT_LED_TYPE_ATTN) & OPAL_SLOT_LED_STATE_ON)) return OPAL_PARAMETER; if ((led_value >> OPAL_SLOT_LED_TYPE_ATTN) & OPAL_SLOT_LED_STATE_ON) state = LED_STATE_ON; return queue_led_state_change(loc_code, 0, state, SPCN_SRC_OPAL, async_token); } /* * fsp_opal_leds_get_ind (OPAL_LEDS_GET_INDICATOR) * * Argument Description Updated By * -------- ----------- ---------- * loc_code Location code of the LEDs (Host) * led_mask LED types whose status is available (OPAL) * led_value Status of the available LED types (OPAL) * max_led_type Maximum number of supported LED types (Host/OPAL) * * The host will pass the location code of the LED types (loc_code) and * maximum number of LED types it understands (max_led_type). OPAL will * update the 'led_mask' with set bits pointing to LED types whose status * is available and updates the 'led_value' with actual status. OPAL checks * the 'max_led_type' to understand whether the host is newer or older * compared to itself. In the case where the OPAL is newer compared * to host (OPAL's max_led_type > host's max_led_type), it will update * led_mask and led_value according to max_led_type requested by the host. * When the host is newer compared to the OPAL (host's max_led_type > * OPAL's max_led_type), OPAL updates 'max_led_type' to the maximum * number of LED type it understands and updates 'led_mask', 'led_value' * based on that maximum value of LED types. */ static int64_t fsp_opal_leds_get_ind(char *loc_code, u64 *led_mask, u64 *led_value, u64 *max_led_type) { bool supported = true; int64_t max; int rc; struct fsp_led_data *led; /* FSP not present */ if (!fsp_present()) return OPAL_HARDWARE; /* LED support not available */ if (led_support != LED_STATE_PRESENT) return OPAL_HARDWARE; /* Adjust max LED type */ if (*max_led_type > OPAL_SLOT_LED_TYPE_MAX) { supported = false; *max_led_type = OPAL_SLOT_LED_TYPE_MAX; } /* Invalid parameter */ max = *max_led_type; if (max <= 0) return OPAL_PARAMETER; /* Get System attention indicator state */ if (is_sai_loc_code(loc_code)) { rc = fsp_opal_get_sai(led_mask, led_value); return rc; } /* LED not found */ led = fsp_find_cec_led(loc_code); if (!led) return OPAL_PARAMETER; *led_mask = 0; *led_value = 0; /* Identify LED */ --max; *led_mask |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ID; if (led->status & SPCN_LED_IDENTIFY_MASK) *led_value |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_ID; /* Fault LED */ if (!max) return OPAL_SUCCESS; --max; *led_mask |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_FAULT; if (led->status & SPCN_LED_FAULT_MASK) *led_value |= OPAL_SLOT_LED_STATE_ON << OPAL_SLOT_LED_TYPE_FAULT; /* OPAL doesn't support all the LED type requested by payload */ if (!supported) return OPAL_PARTIAL; return OPAL_SUCCESS; } /* * fsp_opal_leds_set_ind (OPAL_LEDS_SET_INDICATOR) * * Argument Description Updated By * -------- ----------- ---------- * loc_code Location code of the LEDs (Host) * led_mask LED types whose status will be updated (Host) * led_value Requested status of various LED types (Host) * max_led_type Maximum number of supported LED types (Host/OPAL) * * The host will pass the location code of the LED types, mask, value * and maximum number of LED types it understands. OPAL will update * LED status for all the LED types mentioned in the mask with their * value mentioned. OPAL checks the 'max_led_type' to understand * whether the host is newer or older compared to itself. In case where * the OPAL is newer compared to the host (OPAL's max_led_type > * host's max_led_type), it updates LED status based on max_led_type * requested from the host. When the host is newer compared to the OPAL * (host's max_led_type > OPAL's max_led_type), OPAL updates * 'max_led_type' to the maximum number of LED type it understands and * then it updates LED status based on that updated maximum value of LED * types. Host needs to check the returned updated value of max_led_type * to figure out which part of it's request got served and which ones got * ignored. */ static int64_t fsp_opal_leds_set_ind(uint64_t async_token, char *loc_code, const u64 led_mask, const u64 led_value, u64 *max_led_type) { bool supported = true; int command, state, rc = OPAL_SUCCESS; int64_t max; struct fsp_led_data *led; /* FSP not present */ if (!fsp_present()) return OPAL_HARDWARE; /* LED support not available */ if (led_support != LED_STATE_PRESENT) return OPAL_HARDWARE; /* Adjust max LED type */ if (*max_led_type > OPAL_SLOT_LED_TYPE_MAX) { supported = false; *max_led_type = OPAL_SLOT_LED_TYPE_MAX; } max = *max_led_type; /* Invalid parameter */ if (max <= 0) return OPAL_PARAMETER; /* Set System attention indicator state */ if (is_sai_loc_code(loc_code)) { supported = true; rc = fsp_opal_set_sai(async_token, loc_code, led_mask, led_value); goto success; } /* LED not found */ led = fsp_find_cec_led(loc_code); if (!led) return OPAL_PARAMETER; /* Indentify LED mask */ --max; if ((led_mask >> OPAL_SLOT_LED_TYPE_ID) & OPAL_SLOT_LED_STATE_ON) { supported = true; command = LED_COMMAND_IDENTIFY; state = LED_STATE_OFF; if ((led_value >> OPAL_SLOT_LED_TYPE_ID) & OPAL_SLOT_LED_STATE_ON) state = LED_STATE_ON; rc = queue_led_state_change(loc_code, command, state, SPCN_SRC_OPAL, async_token); } if (!max) goto success; /* Fault LED mask */ --max; if ((led_mask >> OPAL_SLOT_LED_TYPE_FAULT) & OPAL_SLOT_LED_STATE_ON) { supported = true; command = LED_COMMAND_FAULT; state = LED_STATE_OFF; if ((led_value >> OPAL_SLOT_LED_TYPE_FAULT) & OPAL_SLOT_LED_STATE_ON) state = LED_STATE_ON; rc = queue_led_state_change(loc_code, command, state, SPCN_SRC_OPAL, async_token); } success: /* Unsupported LED type */ if (!supported) return OPAL_UNSUPPORTED; if (rc == OPAL_SUCCESS) rc = OPAL_ASYNC_COMPLETION; else rc = OPAL_INTERNAL_ERROR; return rc; } /* Get LED node from device tree */ static struct dt_node *dt_get_led_node(void) { struct dt_node *pled; if (!opal_node) { prlog(PR_WARNING, "OPAL parent device node not available\n"); return NULL; } pled = dt_find_by_path(opal_node, DT_PROPERTY_LED_NODE); if (!pled) prlog(PR_WARNING, "Parent device node not available\n"); return pled; } /* Get System attention indicator location code from device tree */ static void dt_get_sai_loc_code(void) { struct dt_node *pled, *child; const char *led_type = NULL; memset(sai_data.loc_code, 0, LOC_CODE_SIZE); pled = dt_get_led_node(); if (!pled) return; list_for_each(&pled->children, child, list) { led_type = dt_prop_get(child, DT_PROPERTY_LED_TYPES); if (!led_type) continue; if (strcmp(led_type, LED_TYPE_ATTENTION)) continue; memcpy(sai_data.loc_code, child->name, LOC_CODE_SIZE - 1); prlog(PR_TRACE, "SAI Location code = %s\n", sai_data.loc_code); return; } } /* * create_led_device_node * * Creates the system parent LED device node and all individual * child LED device nodes under it. This is called right before * starting the payload (Linux) to ensure that the SPCN command * sequence to fetch the LED location code list has been finished * and to have a better chance of creating the deviced nodes. */ void create_led_device_nodes(void) { const char *led_mode = NULL; struct fsp_led_data *led, *next; struct dt_node *pled, *cled; if (!fsp_present()) return; /* Make sure LED list read is completed */ while (led_support == LED_STATE_READING) opal_run_pollers(); if (led_support == LED_STATE_ABSENT) { prlog(PR_WARNING, "LED support not available, \ hence device tree nodes will not be created\n"); return; } /* Get LED node */ pled = dt_get_led_node(); if (!pled) return; dt_add_property_strings(pled, "compatible", DT_PROPERTY_LED_COMPATIBLE); led_mode = dt_prop_get(pled, DT_PROPERTY_LED_MODE); if (!led_mode) { prlog(PR_WARNING, "Unknown LED operating mode\n"); return; } /* LED child nodes */ list_for_each_safe(&cec_ledq, led, next, link) { /* Duplicate LED location code */ if (dt_find_by_path(pled, led->loc_code)) { prlog(PR_WARNING, "duplicate location code %s", led->loc_code); continue; } cled = dt_new(pled, led->loc_code); if (!cled) { prlog(PR_WARNING, "Child device node creation " "failed\n"); continue; } if (!strcmp(led_mode, LED_MODE_LIGHT_PATH)) dt_add_property_strings(cled, DT_PROPERTY_LED_TYPES, LED_TYPE_IDENTIFY, LED_TYPE_FAULT); else dt_add_property_strings(cled, DT_PROPERTY_LED_TYPES, LED_TYPE_IDENTIFY); } } /* * Process the received LED data from SPCN * * Every LED state data is added into the CEC list. If the location * code is a enclosure type, its added into the enclosure list as well. * */ static void fsp_process_leds_data(u16 len) { struct fsp_led_data *led_data = NULL; void *buf = NULL; /* * Process the entire captured data from the last command * * TCE mapped 'led_buffer' contains the fsp_led_data structure * one after the other till the total length 'len'. * */ buf = led_buffer; while (len) { /* Prepare */ led_data = zalloc(sizeof(struct fsp_led_data)); assert(led_data); /* Resource ID */ buf_read(buf, u16, &led_data->rid); len -= sizeof(led_data->rid); /* Location code length */ buf_read(buf, u8, &led_data->lc_len); len -= sizeof(led_data->lc_len); if (led_data->lc_len == 0) { free(led_data); break; } /* Location code */ strncpy(led_data->loc_code, buf, led_data->lc_len); strcat(led_data->loc_code, "\0"); buf += led_data->lc_len; len -= led_data->lc_len; /* Parameters */ buf_read(buf, u16, &led_data->parms); len -= sizeof(led_data->parms); /* Status */ buf_read(buf, u16, &led_data->status); len -= sizeof(led_data->status); /* * This is Enclosure LED's location code, need to go * inside the enclosure LED list as well. */ if (!strstr(led_data->loc_code, "-")) { struct fsp_led_data *encl_led_data = NULL; encl_led_data = zalloc(sizeof(struct fsp_led_data)); assert(encl_led_data); /* copy over the original */ encl_led_data->rid = led_data->rid; encl_led_data->lc_len = led_data->lc_len; strncpy(encl_led_data->loc_code, led_data->loc_code, led_data->lc_len); encl_led_data->loc_code[led_data->lc_len] = '\0'; encl_led_data->parms = led_data->parms; encl_led_data->status = led_data->status; /* Add to the list of enclosure LEDs */ list_add_tail(&encl_ledq, &encl_led_data->link); } /* Push this onto the list */ list_add_tail(&cec_ledq, &led_data->link); } } /* Replay the SPCN command */ static void replay_spcn_cmd(u32 last_spcn_cmd) { u32 cmd_hdr = 0; int rc = -1; /* Reached threshold */ if (replay == SPCN_REPLAY_THRESHOLD) { replay = 0; led_support = LED_STATE_ABSENT; return; } replay++; if (last_spcn_cmd == SPCN_MOD_PRS_LED_DATA_FIRST) { cmd_hdr = SPCN_MOD_PRS_LED_DATA_FIRST << 24 | SPCN_CMD_PRS << 16; rc = fsp_queue_msg(fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_hdr, 0, PSI_DMA_LED_BUF), fsp_read_leds_data_complete); if (rc) prlog(PR_ERR, "Replay SPCN_MOD_PRS_LED_DATA_FIRST" " command could not be queued\n"); } if (last_spcn_cmd == SPCN_MOD_PRS_LED_DATA_SUB) { cmd_hdr = SPCN_MOD_PRS_LED_DATA_SUB << 24 | SPCN_CMD_PRS << 16; rc = fsp_queue_msg(fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_hdr, 0, PSI_DMA_LED_BUF), fsp_read_leds_data_complete); if (rc) prlog(PR_ERR, "Replay SPCN_MOD_PRS_LED_DATA_SUB" " command could not be queued\n"); } /* Failed to queue MBOX message */ if (rc) led_support = LED_STATE_ABSENT; } /* * FSP message response handler for following SPCN LED commands * which are used to fetch all of the LED data from SPCN * * 1. SPCN_MOD_PRS_LED_DATA_FIRST --> First 1KB of LED data * 2. SPCN_MOD_PRS_LED_DATA_SUB --> Subsequent 1KB of LED data * * Once the SPCN_RSP_STATUS_SUCCESS response code has been received * indicating the last batch of 1KB LED data is here, the list addition * process is now complete and we enable LED support for FSP async commands * and for OPAL interface. */ static void fsp_read_leds_data_complete(struct fsp_msg *msg) { struct fsp_led_data *led, *next; struct fsp_msg *resp = msg->resp; u32 cmd_hdr = 0; int rc = 0; u32 msg_status = resp->word1 & 0xff00; u32 led_status = (resp->data.words[1] >> 24) & 0xff; u16 data_len = (u16)(resp->data.words[1] & 0xffff); if (msg_status != FSP_STATUS_SUCCESS) { log_simple_error(&e_info(OPAL_RC_LED_SUPPORT), "FSP returned error %x LED not supported\n", msg_status); /* LED support not available */ led_support = LED_STATE_ABSENT; fsp_freemsg(msg); return; } /* SPCN command status */ switch (led_status) { /* Last 1KB of LED data */ case SPCN_RSP_STATUS_SUCCESS: prlog(PR_DEBUG, "SPCN_RSP_STATUS_SUCCESS: %d bytes received\n", data_len); led_support = LED_STATE_PRESENT; /* Copy data to the local list */ fsp_process_leds_data(data_len); /* LEDs captured on the system */ prlog(PR_DEBUG, "CEC LEDs captured on the system:\n"); list_for_each_safe(&cec_ledq, led, next, link) { prlog(PR_DEBUG, "rid: %x\t" "len: %x " "lcode: %-30s\t" "parms: %04x\t" "status: %04x\n", led->rid, led->lc_len, led->loc_code, led->parms, led->status); } prlog(PR_DEBUG, "ENCL LEDs captured on the system:\n"); list_for_each_safe(&encl_ledq, led, next, link) { prlog(PR_DEBUG, "rid: %x\t" "len: %x " "lcode: %-30s\t" "parms: %04x\t" "status: %04x\n", led->rid, led->lc_len, led->loc_code, led->parms, led->status); } break; /* If more 1KB of LED data present */ case SPCN_RSP_STATUS_COND_SUCCESS: prlog(PR_DEBUG, "SPCN_RSP_STATUS_COND_SUCCESS: %d bytes " " received\n", data_len); /* Copy data to the local list */ fsp_process_leds_data(data_len); /* Fetch the remaining data from SPCN */ last_spcn_cmd = SPCN_MOD_PRS_LED_DATA_SUB; cmd_hdr = SPCN_MOD_PRS_LED_DATA_SUB << 24 | SPCN_CMD_PRS << 16; rc = fsp_queue_msg(fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_hdr, 0, PSI_DMA_LED_BUF), fsp_read_leds_data_complete); if (rc) { prlog(PR_ERR, "SPCN_MOD_PRS_LED_DATA_SUB command" " could not be queued\n"); led_support = LED_STATE_ABSENT; } break; /* Other expected error codes*/ case SPCN_RSP_STATUS_INVALID_RACK: case SPCN_RSP_STATUS_INVALID_SLAVE: case SPCN_RSP_STATUS_INVALID_MOD: case SPCN_RSP_STATUS_STATE_PROHIBIT: case SPCN_RSP_STATUS_UNKNOWN: default: /* Replay the previous SPCN command */ replay_spcn_cmd(last_spcn_cmd); } fsp_freemsg(msg); } /* * Init the LED state * * This is called during the host boot process. This is the place where * we figure out all the LEDs present on the system, their state and then * create structure out of those information and popullate two master lists. * One for all the LEDs on the CEC and one for all the LEDs on the enclosure. * The LED information contained in the lists will cater either to various * FSP initiated async commands or POWERNV initiated OPAL calls. Need to make * sure that this initialization process is complete before allowing any requets * on LED. Also need to be called to re-fetch data from SPCN after any LED state * have been updated. */ static void fsp_leds_query_spcn(void) { struct fsp_led_data *led = NULL; int rc = 0; u32 cmd_hdr = SPCN_MOD_PRS_LED_DATA_FIRST << 24 | SPCN_CMD_PRS << 16; /* Till the last batch of LED data */ last_spcn_cmd = 0; /* Empty the lists */ while (!list_empty(&cec_ledq)) { led = list_pop(&cec_ledq, struct fsp_led_data, link); free(led); } while (!list_empty(&encl_ledq)) { led = list_pop(&encl_ledq, struct fsp_led_data, link); free(led); } /* Allocate buffer with alignment requirements */ if (led_buffer == NULL) { led_buffer = memalign(TCE_PSIZE, PSI_DMA_LED_BUF_SZ); if (!led_buffer) return; } /* TCE mapping - will not unmap */ fsp_tce_map(PSI_DMA_LED_BUF, led_buffer, PSI_DMA_LED_BUF_SZ); /* Request the first 1KB of LED data */ last_spcn_cmd = SPCN_MOD_PRS_LED_DATA_FIRST; rc = fsp_queue_msg(fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_hdr, 0, PSI_DMA_LED_BUF), fsp_read_leds_data_complete); if (rc) prlog(PR_ERR, "SPCN_MOD_PRS_LED_DATA_FIRST command could" " not be queued\n"); else /* Initiated LED list fetch MBOX command */ led_support = LED_STATE_READING; } /* Init the LED subsystem at boot time */ void fsp_led_init(void) { led_buffer = NULL; if (!fsp_present()) return; /* Init the master lists */ list_head_init(&cec_ledq); list_head_init(&encl_ledq); list_head_init(&spcn_cmdq); fsp_leds_query_spcn(); loc_code_list_buffer = memalign(TCE_PSIZE, PSI_DMA_LOC_COD_BUF_SZ); if (loc_code_list_buffer == NULL) prerror("ERROR: Unable to allocate loc_code_list_buffer!\n"); prlog(PR_TRACE, "Init completed\n"); /* Get System attention indicator state */ dt_get_sai_loc_code(); fsp_get_sai(); /* Handle FSP initiated async LED commands */ fsp_register_client(&fsp_indicator_client, FSP_MCLASS_INDICATOR); prlog(PR_TRACE, "FSP async command client registered\n"); /* Register for SAI update notification */ sysparam_add_update_notifier(sai_update_notification); opal_register(OPAL_LEDS_GET_INDICATOR, fsp_opal_leds_get_ind, 4); opal_register(OPAL_LEDS_SET_INDICATOR, fsp_opal_leds_set_ind, 5); prlog(PR_TRACE, "LED OPAL interface registered\n"); } skiboot-skiboot-5.1.13/hw/fsp/fsp-mdst-table.c000066400000000000000000000246301265204436200211300ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Sapphire dump design: * - During initialization we setup Memory Dump Source Table (MDST) table * which contains address, size pair. * - We send MDST table update notification to FSP via MBOX command. * - During Sapphire checkstop: * - FSP retrieves HWDUMP. * - FSP retrieves CEC memory based on MDST table. * - Once Sapphire reboot FSP sends new dump avialable notification via HDAT */ #include #include #include #include #include #include #include /* * Sapphire dump size * This is the maximum memory that FSP can retrieve during checkstop. * * Note: * Presently we are hardcoding this parameter. Eventually we need * new System parameter so that we can get max size dynamically. */ #define MAX_SAPPHIRE_DUMP_SIZE 0x1000000 DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_UPDATE, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_ADD, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_REMOVE, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); static struct dump_mdst_table *mdst_table; static struct dump_mdst_table *dump_mem_region; static int cur_mdst_entry; static int max_mdst_entry; static int cur_dump_size; /* * Presently both sizes are same.. But if someday FSP gives more space * than our TCE mapping then we need this validation.. * * Also once FSP implements MAX_SAPPHIRE_DUMP_SIZE system param, we can * move this validation to separate function. */ static int max_dump_size = MIN(MAX_SAPPHIRE_DUMP_SIZE, PSI_DMA_HYP_DUMP_SIZE); /* Protect MDST table entries */ static struct lock mdst_lock = LOCK_UNLOCKED; /* Not supported on P7 */ static inline bool fsp_mdst_supported(void) { return proc_gen >= proc_gen_p8; } static inline uint32_t get_dump_region_map_size(uint64_t addr, uint32_t size) { uint64_t start, end; start = addr & ~TCE_MASK; end = addr + size; end = (end + (TCE_MASK - 1)) & ~TCE_MASK; return (end - start); } static int dump_region_tce_map(void) { int i; uint32_t t_size = 0, size; uint64_t addr; for (i = 0; i < cur_mdst_entry; i++) { addr = dump_mem_region[i].addr & ~TCE_MASK; size = get_dump_region_map_size(dump_mem_region[i].addr, dump_mem_region[i].size); if (t_size + size > max_dump_size) break; /* TCE mapping */ fsp_tce_map(PSI_DMA_HYP_DUMP + t_size, (void *)addr, size); /* Add entry to MDST table */ mdst_table[i].type = dump_mem_region[i].type; mdst_table[i].size = dump_mem_region[i].size; mdst_table[i].addr = PSI_DMA_HYP_DUMP + t_size; /* TCE alignment adjustment */ mdst_table[i].addr += dump_mem_region[i].addr & 0xfff; t_size += size; } return i; } static inline void dump_region_tce_unmap(void) { fsp_tce_unmap(PSI_DMA_HYP_DUMP, PSI_DMA_HYP_DUMP_SIZE); } static void update_mdst_table_complete(struct fsp_msg *msg) { uint8_t status = (msg->resp->word1 >> 8) & 0xff; if (status) log_simple_error(&e_info(OPAL_RC_DUMP_MDST_UPDATE), "MDST: Update table MBOX command failed: " "0x%x\n", status); else printf("MDST: Table updated.\n"); fsp_freemsg(msg); } /* Send MDST table to FSP */ static int64_t fsp_update_mdst_table(void) { struct fsp_msg *msg; int count; int rc = OPAL_SUCCESS; if (cur_mdst_entry <= 0) { printf("MDST: Table is empty\n"); return OPAL_INTERNAL_ERROR; } lock(&mdst_lock); /* Unmap previous mapping */ dump_region_tce_unmap(); count = dump_region_tce_map(); msg = fsp_mkmsg(FSP_CMD_HYP_MDST_TABLE, 4, 0, PSI_DMA_MDST_TABLE, sizeof(*mdst_table) * count, sizeof(*mdst_table)); unlock(&mdst_lock); if (!msg) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_UPDATE), "MDST: Message allocation failed.!\n"); rc = OPAL_INTERNAL_ERROR; } else if (fsp_queue_msg(msg, update_mdst_table_complete)) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_UPDATE), "MDST: Failed to queue MDST table message.\n"); fsp_freemsg(msg); rc = OPAL_INTERNAL_ERROR; } return rc; } static int dump_region_del_entry(uint32_t id) { int i; uint32_t size; bool found = false; int rc = OPAL_SUCCESS; lock(&mdst_lock); for (i = 0; i < cur_mdst_entry; i++) { if (dump_mem_region[i].type != id) continue; found = true; break; } if (!found) { rc = OPAL_PARAMETER; goto del_out; } /* Adjust current dump size */ size = get_dump_region_map_size(dump_mem_region[i].addr, dump_mem_region[i].size); cur_dump_size -= size; for ( ; i < cur_mdst_entry - 1; i++) dump_mem_region[i] = dump_mem_region[i + 1]; dump_mem_region[i].type = 0; cur_mdst_entry--; del_out: unlock(&mdst_lock); return rc; } /* Add entry to MDST table */ static int __dump_region_add_entry(uint32_t id, uint64_t addr, uint32_t size) { int rc = OPAL_INTERNAL_ERROR; uint32_t act_size; /* Delete function takes lock before modifying table */ dump_region_del_entry(id); lock(&mdst_lock); if (cur_mdst_entry >= max_mdst_entry) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD), "MDST: Table is full.\n"); goto out; } /* TCE alignment adjustment */ act_size = get_dump_region_map_size(addr, size); /* Make sure we don't cross dump size limit */ if (cur_dump_size + act_size > max_dump_size) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD), "MDST: 0x%x is crossing max dump size (0x%x) limit.\n", cur_dump_size + act_size, max_dump_size); goto out; } /* Add entry to dump memory region table */ dump_mem_region[cur_mdst_entry].type = id; dump_mem_region[cur_mdst_entry].addr = addr; dump_mem_region[cur_mdst_entry].size = size; /* Update dump region count and dump size */ cur_mdst_entry++; cur_dump_size += act_size; printf("MDST: Addr = 0x%llx [size : 0x%x bytes] added to MDST table.\n", (uint64_t)addr, size); rc = OPAL_SUCCESS; out: unlock(&mdst_lock); return rc; } static int dump_region_add_entries(void) { int rc; /* Add console buffer */ rc = __dump_region_add_entry(DUMP_REGION_CONSOLE, INMEM_CON_START, INMEM_CON_LEN); if (rc) return rc; /* Add HBRT buffer */ rc = __dump_region_add_entry(DUMP_REGION_HBRT_LOG, HBRT_CON_START, HBRT_CON_LEN); return rc; } static int64_t fsp_opal_register_dump_region(uint32_t id, uint64_t addr, uint64_t size) { int rc = OPAL_SUCCESS; if (!fsp_present()) return OPAL_UNSUPPORTED; if (!fsp_mdst_supported()) { printf("MDST: Not supported\n"); return OPAL_UNSUPPORTED; } /* Validate memory region id */ if (id < DUMP_REGION_HOST_START || id > DUMP_REGION_HOST_END) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD), "MDST: Invalid dump region id : 0x%x\n", id); return OPAL_PARAMETER; } if (size <= 0) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD), "MDST: Invalid size : 0x%llx\n", size); return OPAL_PARAMETER; } rc = __dump_region_add_entry(id, addr, size); if (rc) return rc; /* Send updated MDST to FSP */ rc = fsp_update_mdst_table(); return rc; } static int64_t fsp_opal_unregister_dump_region(uint32_t id) { int rc = OPAL_SUCCESS; if (!fsp_present()) return OPAL_UNSUPPORTED; if (!fsp_mdst_supported()) { printf("MDST: Not supported\n"); return OPAL_UNSUPPORTED; } /* Validate memory region id */ if (id < DUMP_REGION_HOST_START || id > DUMP_REGION_HOST_END) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_REMOVE), "MDST: Invalid dump region id : 0x%x\n", id); return OPAL_PARAMETER; } rc = dump_region_del_entry(id); if (rc) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_REMOVE), "MDST: dump region id : 0x%x not found\n", id); return OPAL_PARAMETER; } /* Send updated MDST to FSP */ rc = fsp_update_mdst_table(); return rc; } /* TCE mapping */ static inline void mdst_table_tce_map(void) { fsp_tce_map(PSI_DMA_MDST_TABLE, mdst_table, PSI_DMA_MDST_TABLE_SIZE); } /* Initialize MDST table */ static int mdst_table_init(void) { dump_mem_region = memalign(TCE_PSIZE, PSI_DMA_MDST_TABLE_SIZE); if (!dump_mem_region) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_INIT), "MDST: Failed to allocate memory for dump " "memory region table.\n"); return -ENOMEM; } memset(dump_mem_region, 0, PSI_DMA_MDST_TABLE_SIZE); mdst_table = memalign(TCE_PSIZE, PSI_DMA_MDST_TABLE_SIZE); if (!mdst_table) { log_simple_error(&e_info(OPAL_RC_DUMP_MDST_INIT), "MDST: Failed to allocate memory for MDST table.\n"); return -ENOMEM; } memset(mdst_table, 0, PSI_DMA_MDST_TABLE_SIZE); mdst_table_tce_map(); max_mdst_entry = PSI_DMA_MDST_TABLE_SIZE / sizeof(*mdst_table); printf("MDST: Max entries in MDST table : %d\n", max_mdst_entry); return OPAL_SUCCESS; } /* * Handle FSP R/R event. */ static bool fsp_mdst_update_rr(uint32_t cmd_sub_mod, struct fsp_msg *msg __unused) { switch (cmd_sub_mod) { case FSP_RESET_START: return true; case FSP_RELOAD_COMPLETE: /* Send MDST to FSP */ fsp_update_mdst_table(); return true; } return false; } static struct fsp_client fsp_mdst_client_rr = { .message = fsp_mdst_update_rr, }; /* Initialize MDST table and send notification to FSP */ void fsp_mdst_table_init(void) { if (!fsp_present()) return; /* OPAL interface */ opal_register(OPAL_REGISTER_DUMP_REGION, fsp_opal_register_dump_region, 3); opal_register(OPAL_UNREGISTER_DUMP_REGION, fsp_opal_unregister_dump_region, 1); if (!fsp_mdst_supported()) return; /* Initiate MDST */ if (mdst_table_init() != OPAL_SUCCESS) return; /* * Ignore return code from mdst_table_add_entries so that * we can atleast capture partial dump. */ dump_region_add_entries(); fsp_update_mdst_table(); /* Register for Class AA (FSP R/R) */ fsp_register_client(&fsp_mdst_client_rr, FSP_MCLASS_RR_EVENT); } skiboot-skiboot-5.1.13/hw/fsp/fsp-mem-err.c000066400000000000000000000275331265204436200204450ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define pr_fmt(fmt) "FSPMEMERR: " fmt #include #include #include #include #include #include /* FSP sends real address of 4K memory page. */ #define MEM_ERR_PAGE_SIZE_4K (1UL << 12) /* maximum number of error event to hold until linux consumes it. */ #define MERR_MAX_RECORD 1024 struct fsp_mem_err_node { struct list_node list; struct OpalMemoryErrorData data; }; static LIST_HEAD(merr_free_list); static LIST_HEAD(mem_error_list); /* * lock is used to protect overwriting of merr_free_list and mem_error_list * list. */ static struct lock mem_err_lock = LOCK_UNLOCKED; DEFINE_LOG_ENTRY(OPAL_RC_MEM_ERR_RES, OPAL_PLATFORM_ERR_EVT, OPAL_MEM_ERR, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_MEM_ERR_DEALLOC, OPAL_PLATFORM_ERR_EVT, OPAL_MEM_ERR, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); static bool send_response_to_fsp(u32 cmd_sub_mod) { struct fsp_msg *rsp; int rc = -ENOMEM; rsp = fsp_mkmsg(cmd_sub_mod, 0); if (rsp) rc = fsp_queue_msg(rsp, fsp_freemsg); if (rc) { fsp_freemsg(rsp); /* XXX Generate error logs */ prerror("Error %d queueing FSP memory error reply\n", rc); return false; } return true; } /* * Queue up the memory error message for delivery. * * queue_event_for_delivery get called from two places. * 1) from queue_mem_err_node when new fsp mem error is available and * 2) from completion callback indicating that linux has consumed an message. * * TODO: * There is a chance that, we may not get a free slot to queue our event * for delivery to linux during both the above invocations. In that case * we end up holding events with us until next fsp memory error comes in. * We need to address this case either here OR fix up messaging infrastructure * to make sure at least one slot will always be available per message type. * * XXX: BenH: I changed the msg infrastructure to attempt an allocation * in that case, at least until we clarify a bit better how * we want to handle things. */ static void queue_event_for_delivery(void *data __unused) { struct fsp_mem_err_node *entry; uint64_t *merr_data; int rc; lock(&mem_err_lock); entry = list_pop(&mem_error_list, struct fsp_mem_err_node, list); unlock(&mem_err_lock); if (!entry) return; /* * struct OpalMemoryErrorData is of (4 * 64 bits) size and well packed * structure. Hence use uint64_t pointer to pass entire structure * using 4 params in generic message format. */ merr_data = (uint64_t *)&entry->data; /* queue up for delivery */ rc = opal_queue_msg(OPAL_MSG_MEM_ERR, NULL, queue_event_for_delivery, merr_data[0], merr_data[1], merr_data[2], merr_data[3]); lock(&mem_err_lock); if (rc) { /* * Failed to queue up the event for delivery. No free slot * available. There is a chance that we are trying to queue * up multiple event at the same time. We may already have * at least one event queued up, in that case we will be * called again through completion callback and we should * be able to grab empty slot then. * * For now, put this node back on mem_error_list. */ list_add(&mem_error_list, &entry->list); } else list_add(&merr_free_list, &entry->list); unlock(&mem_err_lock); } static int queue_mem_err_node(struct OpalMemoryErrorData *merr_evt) { struct fsp_mem_err_node *entry; lock(&mem_err_lock); entry = list_pop(&merr_free_list, struct fsp_mem_err_node, list); if (!entry) { printf("Failed to queue up memory error event.\n"); unlock(&mem_err_lock); return -ENOMEM; } entry->data = *merr_evt; list_add(&mem_error_list, &entry->list); unlock(&mem_err_lock); /* Queue up the event for delivery to OS. */ queue_event_for_delivery(NULL); return 0; } /* Check if memory resilience event for same address already exists. */ static bool is_resilience_event_exist(u64 paddr) { struct fsp_mem_err_node *entry; struct OpalMemoryErrorData *merr_evt; int found = 0; lock(&mem_err_lock); list_for_each(&mem_error_list, entry, list) { merr_evt = &entry->data; if ((merr_evt->type == OPAL_MEM_ERR_TYPE_RESILIENCE) && (merr_evt->u.resilience.physical_address_start == paddr)) { found = 1; break; } } unlock(&mem_err_lock); return !!found; } /* * handle Memory Resilience error message. * Section 28.2 of Hypervisor to FSP Mailbox Interface Specification. * * The flow for Memory Resilence Event is: * 1. PRD component in FSP gets a recoverable attention from hardware when * there is a corretable/uncorrectable memory error to free up a page. * 2. PRD sends Memory Resilence Command to hypervisor with the real address of * the 4K memory page in which the error occurred. * 3. The hypervisor acknowledges with a status immediately. Immediate * acknowledgment doesn’t require the freeing of the page to be completed. */ static bool handle_memory_resilience(u32 cmd_sub_mod, u64 paddr) { int rc = 0; struct OpalMemoryErrorData mem_err_evt; struct errorlog *buf; memset(&mem_err_evt, 0, sizeof(struct OpalMemoryErrorData)); /* Check arguments */ if (paddr == 0) { prerror("memory resilience: Invalid real address.\n"); return send_response_to_fsp(FSP_RSP_MEM_RES | FSP_STATUS_GENERIC_ERROR); } /* Check if event already exist for same address. */ if (is_resilience_event_exist(paddr)) goto send_response; /* Populate an event. */ mem_err_evt.version = OpalMemErr_V1; mem_err_evt.type = OPAL_MEM_ERR_TYPE_RESILIENCE; switch (cmd_sub_mod) { case FSP_CMD_MEM_RES_CE: /* * Should we keep counter for corrected errors in * sapphire OR let linux (PowerNV) handle it? * * For now, send corrected errors to linux and let * linux handle corrected errors thresholding. */ mem_err_evt.flags |= OPAL_MEM_CORRECTED_ERROR; mem_err_evt.u.resilience.resil_err_type = OPAL_MEM_RESILIENCE_CE; break; case FSP_CMD_MEM_RES_UE: mem_err_evt.u.resilience.resil_err_type = OPAL_MEM_RESILIENCE_UE; break; case FSP_CMD_MEM_RES_UE_SCRB: mem_err_evt.u.resilience.resil_err_type = OPAL_MEM_RESILIENCE_UE_SCRUB; break; } mem_err_evt.u.resilience.physical_address_start = paddr; mem_err_evt.u.resilience.physical_address_end = paddr + MEM_ERR_PAGE_SIZE_4K; /* Queue up the event and inform OS about it. */ rc = queue_mem_err_node(&mem_err_evt); send_response: /* Queue up an OK response to the resilience message itself */ if (!rc) return send_response_to_fsp(FSP_RSP_MEM_RES); else { buf = opal_elog_create(&e_info(OPAL_RC_MEM_ERR_RES), 0); log_append_msg(buf, "OPAL_MEM_ERR: Cannot queue up memory " "resilience error event to the OS"); log_add_section(buf, 0x44455350); log_append_data(buf, (char *) &mem_err_evt, sizeof(struct OpalMemoryErrorData)); log_commit(buf); return false; } } /* update existing event entry if match is found. */ static bool update_memory_deallocation_event(u64 paddr_start, u64 paddr_end) { struct fsp_mem_err_node *entry; struct OpalMemoryErrorData *merr_evt; int found = 0; lock(&mem_err_lock); list_for_each(&mem_error_list, entry, list) { merr_evt = &entry->data; if ((merr_evt->type == OPAL_MEM_ERR_TYPE_DYN_DALLOC) && (merr_evt->u.dyn_dealloc.physical_address_start == paddr_start)) { found = 1; if (merr_evt->u.dyn_dealloc.physical_address_end < paddr_end) merr_evt->u.dyn_dealloc.physical_address_end = paddr_end; break; } } unlock(&mem_err_lock); return !!found; } /* * Handle dynamic memory deallocation message. * * When a condition occurs in which we need to do a large scale memory * deallocation, PRD will send a starting and ending address of an area of * memory to Hypervisor. Hypervisor then need to use this to deallocate all * pages between and including the addresses. * */ static bool handle_memory_deallocation(u64 paddr_start, u64 paddr_end) { int rc = 0; u8 err = 0; struct OpalMemoryErrorData mem_err_evt; struct errorlog *buf; memset(&mem_err_evt, 0, sizeof(struct OpalMemoryErrorData)); /* Check arguments */ if ((paddr_start == 0) || (paddr_end == 0)) { prerror("memory deallocation: Invalid " "starting/ending real address.\n"); err = FSP_STATUS_GENERIC_ERROR; } /* If we had an error, send response to fsp and return */ if (err) return send_response_to_fsp(FSP_RSP_MEM_DYN_DEALLOC | err); /* * FSP can send dynamic memory deallocation multiple times for the * same address/address ranges. Hence check and update if we already * have sam event queued. */ if (update_memory_deallocation_event(paddr_start, paddr_end)) goto send_response; /* Populate an new event. */ mem_err_evt.version = OpalMemErr_V1; mem_err_evt.type = OPAL_MEM_ERR_TYPE_DYN_DALLOC; mem_err_evt.u.dyn_dealloc.dyn_err_type = OPAL_MEM_DYNAMIC_DEALLOC; mem_err_evt.u.dyn_dealloc.physical_address_start = paddr_start; mem_err_evt.u.dyn_dealloc.physical_address_end = paddr_end; /* Queue up the event and inform OS about it. */ rc = queue_mem_err_node(&mem_err_evt); send_response: /* Queue up an OK response to the memory deallocation message itself */ if (!rc) return send_response_to_fsp(FSP_RSP_MEM_DYN_DEALLOC); else { buf = opal_elog_create(&e_info(OPAL_RC_MEM_ERR_DEALLOC), 0); log_append_msg(buf, "OPAL_MEM_ERR: Cannot queue up memory " "deallocation error event to the OS"); log_add_section(buf, 0x44455350); log_append_data(buf, (char *)&mem_err_evt, sizeof(struct OpalMemoryErrorData)); log_commit(buf); return false; } } /* Receive a memory error mesages and handle it. */ static bool fsp_mem_err_msg(u32 cmd_sub_mod, struct fsp_msg *msg) { u64 paddr_start, paddr_end; printf("Received 0x%08ux command\n", cmd_sub_mod); switch (cmd_sub_mod) { case FSP_CMD_MEM_RES_CE: case FSP_CMD_MEM_RES_UE: case FSP_CMD_MEM_RES_UE_SCRB: /* * We get the memory relilence command from FSP for * correctable/Uncorrectable/scrub UE errors with real * address of 4K memory page in which the error occurred. */ paddr_start = *((u64 *)&msg->data.words[0]); printf("Got memory resilience error message for " "paddr=0x%016llux\n", paddr_start); return handle_memory_resilience(cmd_sub_mod, paddr_start); case FSP_CMD_MEM_DYN_DEALLOC: paddr_start = *((u64 *)&msg->data.words[0]); paddr_end = *((u64 *)&msg->data.words[2]); printf("Got dynamic memory deallocation message: " "paddr_start=0x%016llux, paddr_end=0x%016llux\n", paddr_start, paddr_end); return handle_memory_deallocation(paddr_start, paddr_end); } return false; } /* * pre allocate memory to hold maximum of 128 memory error event until linux * consumes it. */ static int init_merr_free_list(uint32_t num_entries) { struct fsp_mem_err_node *entry; int i; entry = zalloc(sizeof(struct fsp_mem_err_node) * num_entries); if (!entry) return -ENOMEM; for (i = 0; i < num_entries; ++i, entry++) list_add_tail(&merr_free_list, &entry->list); return 0; } static struct fsp_client fsp_mem_err_client = { .message = fsp_mem_err_msg, }; void fsp_memory_err_init(void) { int rc; printf("Intializing fsp memory handling.\n"); /* If we have an FSP, register for notifications */ if (!fsp_present()) return; /* pre allocate memory for 128 record */ rc = init_merr_free_list(MERR_MAX_RECORD); if (rc < 0) return; fsp_register_client(&fsp_mem_err_client, FSP_MCLASS_MEMORY_ERR); } skiboot-skiboot-5.1.13/hw/fsp/fsp-nvram.c000066400000000000000000000260201265204436200202120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* * The FSP NVRAM API operates in "blocks" of 4K. It is entirely exposed * to the OS via the OPAL APIs. * * In order to avoid dealing with complicated read/modify/write state * machines (and added issues related to FSP failover in the middle) * we keep a memory copy of the entire nvram which we load at boot * time. We save only modified blocks. * * To limit the amount of memory used by the nvram image, we limit * how much nvram we support to NVRAM_SIZE. Additionally, this limit * of 1M is the maximum that the CHRP/PAPR nvram partition format * supports for a partition entry. * * (Q: should we save the whole thing in case of FSP failover ?) * * The nvram is expected to comply with the CHRP/PAPR defined format, * and specifically contain a System partition (ID 0x70) named "common" * with configuration variables for the bootloader and a FW private * partition for future use by skiboot. * * If the partition layout appears broken or lacks one of the above * partitions, we reformat the entire nvram at boot time. * * We do not exploit the ability of the FSP to store a checksum. This * is documented as possibly going away. The CHRP format for nvram * that Linux uses has its own (though weak) checksum mechanism already * */ #define NVRAM_BLKSIZE 0x1000 struct nvram_triplet { uint64_t dma_addr; uint32_t blk_offset; uint32_t blk_count; } __packed; #define NVRAM_FLAG_CLEAR_WPEND 0x80000000 enum nvram_state { NVRAM_STATE_CLOSED, NVRAM_STATE_OPENING, NVRAM_STATE_BROKEN, NVRAM_STATE_OPEN, NVRAM_STATE_ABSENT, }; static void *fsp_nvram_image; static uint32_t fsp_nvram_size; static struct lock fsp_nvram_lock = LOCK_UNLOCKED; static struct fsp_msg *fsp_nvram_msg; static uint32_t fsp_nvram_dirty_start; static uint32_t fsp_nvram_dirty_end; static bool fsp_nvram_was_read; static struct nvram_triplet fsp_nvram_triplet __align(0x1000); static enum nvram_state fsp_nvram_state = NVRAM_STATE_CLOSED; DEFINE_LOG_ENTRY(OPAL_RC_NVRAM_INIT, OPAL_PLATFORM_ERR_EVT , OPAL_NVRAM, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_NVRAM_OPEN, OPAL_PLATFORM_ERR_EVT, OPAL_NVRAM, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_NVRAM_SIZE, OPAL_PLATFORM_ERR_EVT, OPAL_NVRAM, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_NVRAM_READ, OPAL_PLATFORM_ERR_EVT, OPAL_NVRAM, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_NVRAM_WRITE, OPAL_PLATFORM_ERR_EVT, OPAL_NVRAM, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); static void fsp_nvram_send_write(void); static void fsp_nvram_wr_complete(struct fsp_msg *msg) { struct fsp_msg *resp = msg->resp; uint8_t rc; lock(&fsp_nvram_lock); fsp_nvram_msg = NULL; /* Check for various errors. If an error occurred, * we generally assume the nvram is completely dirty * but we won't trigger a new write until we get * either a new attempt at writing, or an FSP reset * reload (TODO) */ if (!resp || resp->state != fsp_msg_response) goto fail_dirty; rc = (msg->word1 >> 8) & 0xff; switch(rc) { case 0: case 0x44: /* Sync to secondary required... XXX */ case 0x45: break; case 0xef: /* Sync to secondary failed, let's ignore that for now, * maybe when (if) we handle redundant FSPs ... */ prerror("FSP: NVRAM sync to secondary failed\n"); break; default: log_simple_error(&e_info(OPAL_RC_NVRAM_WRITE), "FSP: NVRAM write return error 0x%02x\n", rc); goto fail_dirty; } fsp_freemsg(msg); if (fsp_nvram_dirty_start <= fsp_nvram_dirty_end) fsp_nvram_send_write(); unlock(&fsp_nvram_lock); return; fail_dirty: fsp_nvram_dirty_start = 0; fsp_nvram_dirty_end = fsp_nvram_size - 1; fsp_freemsg(msg); unlock(&fsp_nvram_lock); } static void fsp_nvram_send_write(void) { uint32_t start = fsp_nvram_dirty_start; uint32_t end = fsp_nvram_dirty_end; uint32_t count; if (start > end || fsp_nvram_state != NVRAM_STATE_OPEN) return; count = (end - start) / NVRAM_BLKSIZE + 1; fsp_nvram_triplet.dma_addr = PSI_DMA_NVRAM_BODY + start; fsp_nvram_triplet.blk_offset = start / NVRAM_BLKSIZE; fsp_nvram_triplet.blk_count = count; fsp_nvram_msg = fsp_mkmsg(FSP_CMD_WRITE_VNVRAM, 6, 0, PSI_DMA_NVRAM_TRIPL, 1, NVRAM_FLAG_CLEAR_WPEND, 0, 0); if (fsp_queue_msg(fsp_nvram_msg, fsp_nvram_wr_complete)) { fsp_freemsg(fsp_nvram_msg); fsp_nvram_msg = NULL; log_simple_error(&e_info(OPAL_RC_NVRAM_WRITE), "FSP: Error queueing nvram update\n"); return; } fsp_nvram_dirty_start = fsp_nvram_size; fsp_nvram_dirty_end = 0; } static void fsp_nvram_rd_complete(struct fsp_msg *msg) { int64_t rc; lock(&fsp_nvram_lock); /* Read complete, check status. What to do if the read fails ? * * Well, there could be various reasons such as an FSP reboot * at the wrong time, but there is really not much we can do * so for now I'll just mark the nvram as closed, and we'll * attempt a re-open and re-read whenever the OS tries to * access it */ rc = (msg->resp->word1 >> 8) & 0xff; fsp_nvram_msg = NULL; fsp_freemsg(msg); if (rc) { prerror("FSP: NVRAM read failed, will try again later\n"); fsp_nvram_state = NVRAM_STATE_CLOSED; } else { /* nvram was read once, no need to do it ever again */ fsp_nvram_was_read = true; fsp_nvram_state = NVRAM_STATE_OPEN; /* XXX Here we should look for nvram settings that concern * us such as guest kernel arguments etc... */ } unlock(&fsp_nvram_lock); } static void fsp_nvram_send_read(void) { fsp_nvram_msg = fsp_mkmsg(FSP_CMD_READ_VNVRAM, 4, 0, PSI_DMA_NVRAM_BODY, 0, fsp_nvram_size / NVRAM_BLKSIZE); if (fsp_queue_msg(fsp_nvram_msg, fsp_nvram_rd_complete)) { /* If the nvram read fails to queue, we mark ourselves * closed. Shouldn't have happened anyway. Not much else * we can do. */ fsp_nvram_state = NVRAM_STATE_CLOSED; fsp_freemsg(fsp_nvram_msg); fsp_nvram_msg = NULL; log_simple_error(&e_info(OPAL_RC_NVRAM_READ), "FSP: Error queueing nvram read\n"); return; } } static void fsp_nvram_open_complete(struct fsp_msg *msg) { int8_t rc; lock(&fsp_nvram_lock); /* Open complete, check status */ rc = (msg->resp->word1 >> 8) & 0xff; fsp_nvram_msg = NULL; fsp_freemsg(msg); if (rc) { log_simple_error(&e_info(OPAL_RC_NVRAM_OPEN), "FSP: NVRAM open failed, FSP error 0x%02x\n", rc); goto failed; } if (fsp_nvram_was_read) fsp_nvram_state = NVRAM_STATE_OPEN; else fsp_nvram_send_read(); unlock(&fsp_nvram_lock); return; failed: fsp_nvram_state = NVRAM_STATE_CLOSED; unlock(&fsp_nvram_lock); } static void fsp_nvram_send_open(void) { printf("FSP NVRAM: Opening nvram...\n"); fsp_nvram_msg = fsp_mkmsg(FSP_CMD_OPEN_VNVRAM, 1, fsp_nvram_size); assert(fsp_nvram_msg); fsp_nvram_state = NVRAM_STATE_OPENING; if (!fsp_queue_msg(fsp_nvram_msg, fsp_nvram_open_complete)) return; prerror("FSP NVRAM: Failed to queue nvram open message\n"); fsp_freemsg(fsp_nvram_msg); fsp_nvram_msg = NULL; fsp_nvram_state = NVRAM_STATE_CLOSED; } static bool fsp_nvram_get_size(uint32_t *out_size) { struct fsp_msg *msg; int rc, size; msg = fsp_mkmsg(FSP_CMD_GET_VNVRAM_SIZE, 0); assert(msg); rc = fsp_sync_msg(msg, false); size = msg->resp ? msg->resp->data.words[0] : 0; fsp_freemsg(msg); if (rc || size == 0) { log_simple_error(&e_info(OPAL_RC_NVRAM_SIZE), "FSP: Error %d nvram size reported is %d\n", rc, size); fsp_nvram_state = NVRAM_STATE_BROKEN; return false; } printf("FSP: NVRAM file size from FSP is %d bytes\n", size); *out_size = size; return true; } static bool fsp_nvram_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg) { assert(msg == NULL); switch (cmd_sub_mod) { case FSP_RESET_START: printf("FSP: Closing NVRAM on account of FSP Reset\n"); fsp_nvram_state = NVRAM_STATE_CLOSED; return true; case FSP_RELOAD_COMPLETE: printf("FSP: Reopening NVRAM of FSP Reload complete\n"); lock(&fsp_nvram_lock); fsp_nvram_send_open(); unlock(&fsp_nvram_lock); return true; } return false; } static struct fsp_client fsp_nvram_client_rr = { .message = fsp_nvram_msg_rr, }; int fsp_nvram_info(uint32_t *total_size) { if (!fsp_present()) { fsp_nvram_state = NVRAM_STATE_ABSENT; return OPAL_HARDWARE; } if (!fsp_nvram_get_size(total_size)) return OPAL_HARDWARE; return OPAL_SUCCESS; } int fsp_nvram_start_read(void *dst, uint32_t src, uint32_t len) { /* We are currently limited to fully aligned transfers */ assert((((uint64_t)dst) & 0xfff) == 0); assert(dst); /* Currently don't support src!=0 */ assert(src == 0); if (!fsp_present()) return -ENODEV; op_display(OP_LOG, OP_MOD_INIT, 0x0007); lock(&fsp_nvram_lock); /* Store image info */ fsp_nvram_image = dst; fsp_nvram_size = len; /* Mark nvram as not dirty */ fsp_nvram_dirty_start = len; fsp_nvram_dirty_end = 0; /* Map TCEs */ fsp_tce_map(PSI_DMA_NVRAM_TRIPL, &fsp_nvram_triplet, PSI_DMA_NVRAM_TRIPL_SZ); fsp_tce_map(PSI_DMA_NVRAM_BODY, dst, PSI_DMA_NVRAM_BODY_SZ); /* Register for the reset/reload event */ fsp_register_client(&fsp_nvram_client_rr, FSP_MCLASS_RR_EVENT); /* Open and load the nvram from the FSP */ fsp_nvram_send_open(); unlock(&fsp_nvram_lock); return 0; } int fsp_nvram_write(uint32_t offset, void *src, uint32_t size) { uint64_t end = offset + size - 1; /* We only support writing from the original image */ if (src != fsp_nvram_image + offset) return OPAL_HARDWARE; offset &= ~(NVRAM_BLKSIZE - 1); end &= ~(NVRAM_BLKSIZE - 1); lock(&fsp_nvram_lock); /* If the nvram is closed, try re-opening */ if (fsp_nvram_state == NVRAM_STATE_CLOSED) fsp_nvram_send_open(); if (fsp_nvram_dirty_start > offset) fsp_nvram_dirty_start = offset; if (fsp_nvram_dirty_end < end) fsp_nvram_dirty_end = end; if (!fsp_nvram_msg && fsp_nvram_state == NVRAM_STATE_OPEN) fsp_nvram_send_write(); unlock(&fsp_nvram_lock); return 0; } /* This is called right before starting the payload (Linux) to * ensure the initial open & read of nvram has happened before * we transfer control as the guest OS. This is necessary as * Linux will not handle a OPAL_BUSY return properly and treat * it as an error */ void fsp_nvram_wait_open(void) { if (!fsp_present()) return; while(fsp_nvram_state == NVRAM_STATE_OPENING) opal_run_pollers(); if (!fsp_nvram_was_read) { log_simple_error(&e_info(OPAL_RC_NVRAM_INIT), "FSP: NVRAM not read, skipping init\n"); nvram_read_complete(false); return; } nvram_read_complete(true); } skiboot-skiboot-5.1.13/hw/fsp/fsp-op-panel.c000066400000000000000000000152661265204436200206140ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include DEFINE_LOG_ENTRY(OPAL_RC_PANEL_WRITE, OPAL_PLATFORM_ERR_EVT, OPAL_OP_PANEL, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); /* For OPAL OP_PANEL API we can only have one in flight due to TCEs */ static struct fsp_msg *op_req; static uint64_t op_async_token; static struct lock op_lock = LOCK_UNLOCKED; static void fsp_op_display_fatal(uint32_t w0, uint32_t w1) { static struct fsp_msg op_msg_resp; static struct fsp_msg op_msg = { .resp = &op_msg_resp, }; fsp_fillmsg(&op_msg, FSP_CMD_DISP_SRC_DIRECT, 3, 1, w0, w1); fsp_sync_msg(&op_msg, false); } void op_display(enum op_severity sev, enum op_module mod, uint16_t code) { struct fsp_msg *op_msg; uint32_t w0; uint32_t w1; if (!fsp_present()) return; w0 = sev << 16 | mod; w1 = tohex((code >> 12) & 0xf) << 24; w1 |= tohex((code >> 8) & 0xf) << 16; w1 |= tohex((code >> 4) & 0xf) << 8; w1 |= tohex((code ) & 0xf); if (sev == OP_FATAL) { fsp_op_display_fatal(w0, w1); } else { op_msg = fsp_allocmsg(true); if (!op_msg) { prerror("Failed to allocate FSP message for PANEL\n"); return; } fsp_fillmsg(op_msg, FSP_CMD_DISP_SRC_DIRECT, 3, 1, w0, w1); if(fsp_queue_msg(op_msg, fsp_freemsg)) prerror("Failed to queue FSP message for OP PANEL\n"); } } void op_panel_disable_src_echo(void) { struct fsp_msg op_msg_resp; struct fsp_msg op_msg = { .resp = &op_msg_resp, }; if (!fsp_present()) return; fsp_fillmsg(&op_msg, FSP_CMD_DIS_SRC_ECHO, 0); fsp_sync_msg(&op_msg, false); } void op_panel_clear_src(void) { struct fsp_msg op_msg_resp; struct fsp_msg op_msg = { .resp = &op_msg_resp, }; if (!fsp_present()) return; fsp_fillmsg(&op_msg, FSP_CMD_CLEAR_SRC, 0); fsp_sync_msg(&op_msg, false); } /* opal_write_oppanel - Write to the physical op panel. * * Pass in an array of oppanel_line_t structs defining the ASCII characters * to display on each line of the oppanel. If there are two lines on the * physical panel, and you only want to write to the first line, you only * need to pass in one line. If you only want to write to the second line, * you need to pass in both lines, and set the line_len of the first line * to zero. * * This command is asynchronous. If OPAL_SUCCESS is returned, then the * operation was initiated successfully. Subsequent calls will return * OPAL_BUSY until the current operation is complete. */ struct op_src { uint8_t version; #define OP_SRC_VERSION 2 uint8_t flags; uint8_t reserved; uint8_t hex_word_cnt; uint16_t reserved2; uint16_t total_size; uint32_t word2; /* SRC format in low byte */ uint32_t word3; uint32_t word4; uint32_t word5; uint32_t word6; uint32_t word7; uint32_t word8; uint32_t word9; #define OP_SRC_ASCII_LEN 32 uint8_t ascii[OP_SRC_ASCII_LEN]; /* Word 11 */ } __packed __align(4); /* Page align for the sake of TCE mapping */ static struct op_src op_src __align(0x1000); static void __op_panel_write_complete(struct fsp_msg *msg) { fsp_tce_unmap(PSI_DMA_OP_PANEL_MISC, 0x1000); lock(&op_lock); op_req = NULL; unlock(&op_lock); fsp_freemsg(msg); } static void op_panel_write_complete(struct fsp_msg *msg) { uint8_t rc = (msg->resp->word1 >> 8) & 0xff; if (rc) prerror("OPPANEL: Error 0x%02x in display command\n", rc); __op_panel_write_complete(msg); opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, 1, op_async_token); } static int64_t __opal_write_oppanel(oppanel_line_t *lines, uint64_t num_lines, uint64_t async_token) { int64_t rc = OPAL_ASYNC_COMPLETION; int len; int i; if (num_lines < 1 || num_lines > 2) return OPAL_PARAMETER; /* Only one in flight */ lock(&op_lock); if (op_req) { rc = OPAL_BUSY_EVENT; unlock(&op_lock); goto bail; } op_req = fsp_allocmsg(true); if (!op_req) { rc = OPAL_NO_MEM; unlock(&op_lock); goto bail; } unlock(&op_lock); op_async_token = async_token; memset(&op_src, 0, sizeof(op_src)); op_src.version = OP_SRC_VERSION; op_src.flags = 0; op_src.reserved = 0; op_src.hex_word_cnt = 1; /* header word only */ op_src.reserved2 = 0; op_src.total_size = sizeof(op_src); op_src.word2 = 0; /* should be unneeded */ len = be64_to_cpu(lines[0].line_len); if (len > 16) len = 16; memset(op_src.ascii + len, ' ', 16-len); memcpy(op_src.ascii, (void*)be64_to_cpu(lines[0].line), len); if (num_lines > 1) { len = be64_to_cpu(lines[1].line_len); if (len > 16) len = 16; memcpy(op_src.ascii + 16, (void*)be64_to_cpu(lines[1].line), len); memset(op_src.ascii + 16 + len, ' ', 16-len); } for (i = 0; i < sizeof(op_src.ascii); i++) { /* * So, there's this interesting thing if you send * HTML/Javascript through the Operator Panel. * You get to inject it into the ASM web ui! * So we filter out anything suspect here, * at least for the time being. * * Allowed characters: * . / 0-9 : a-z A-Z SPACE */ if (! ((op_src.ascii[i] >= '.' && op_src.ascii[i] <= ':') || (op_src.ascii[i] >= 'a' && op_src.ascii[i] <= 'z') || (op_src.ascii[i] >= 'A' && op_src.ascii[i] <= 'Z') || op_src.ascii[i] == ' ')) { op_src.ascii[i] = '.'; } } fsp_tce_map(PSI_DMA_OP_PANEL_MISC, &op_src, 0x1000); fsp_fillmsg(op_req, FSP_CMD_DISP_SRC_INDIR, 3, 0, PSI_DMA_OP_PANEL_MISC, sizeof(struct op_src)); rc = fsp_queue_msg(op_req, op_panel_write_complete); if (rc) { __op_panel_write_complete(op_req); rc = OPAL_INTERNAL_ERROR; } bail: log_simple_error(&e_info(OPAL_RC_PANEL_WRITE), "FSP: Error updating Op Panel: %lld\n", rc); return rc; } static int64_t opal_write_oppanel_async(uint64_t async_token, oppanel_line_t *lines, uint64_t num_lines) { return __opal_write_oppanel(lines, num_lines, async_token); } void fsp_oppanel_init(void) { struct dt_node *oppanel; if (!fsp_present()) return; opal_register(OPAL_WRITE_OPPANEL_ASYNC, opal_write_oppanel_async, 3); oppanel = dt_new(opal_node, "oppanel"); dt_add_property_cells(oppanel, "#length", 16); dt_add_property_cells(oppanel, "#lines", 2); dt_add_property_string(oppanel, "compatible", "ibm,opal-oppanel"); } skiboot-skiboot-5.1.13/hw/fsp/fsp-rtc.c000066400000000000000000000363601265204436200176670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include /* * Note on how those operate: * * Because the RTC calls can be pretty slow, these functions will shoot * an asynchronous request to the FSP (if none is already pending) * * The requests will return OPAL_BUSY_EVENT as long as the event has * not been completed. * * WARNING: An attempt at doing an RTC write while one is already pending * will simply ignore the new arguments and continue returning * OPAL_BUSY_EVENT. This is to be compatible with existing Linux code. * * Completion of the request will result in an event OPAL_EVENT_RTC * being signaled, which will remain raised until a corresponding call * to opal_rtc_read() or opal_rtc_write() finally returns OPAL_SUCCESS, * at which point the operation is complete and the event cleared. * * If we end up taking longer than rtc_read_timeout_ms millieconds waiting * for the response from a read request, we simply return a cached value (plus * an offset calculated from the timebase. When the read request finally * returns, we update our cache value accordingly. * * There is two separate set of state for reads and writes. If both are * attempted at the same time, the event bit will remain set as long as either * of the two has a pending event to signal. */ #include /* All of the below state is protected by rtc_lock. * It should be held for the shortest amount of time possible. * Certainly not across calls to FSP. */ static struct lock rtc_lock; static enum { RTC_TOD_VALID, RTC_TOD_INVALID, RTC_TOD_PERMANENT_ERROR, } rtc_tod_state = RTC_TOD_INVALID; /* State machine for getting an RTC request. * RTC_{READ/WRITE}_NO_REQUEST -> RTC_{READ/WRITE}_PENDING_REQUEST (one in flight) * RTC_{READ/WRITE}_PENDING_REQUEST -> RTC_{READ/WRITE}_REQUEST_AVAILABLE, * when FSP responds * RTC_{READ/WRITE}_REQUEST_AVAILABLE -> RTC_{READ/WRITE}_NO_REQUEST, * when OS retrieves it */ static enum { RTC_READ_NO_REQUEST, RTC_READ_PENDING_REQUEST, RTC_READ_REQUEST_AVAILABLE, } rtc_read_request_state = RTC_READ_NO_REQUEST; static enum { RTC_WRITE_NO_REQUEST, RTC_WRITE_PENDING_REQUEST, RTC_WRITE_REQUEST_AVAILABLE, } rtc_write_request_state = RTC_WRITE_NO_REQUEST; static bool rtc_tod_cache_dirty = false; /* TODO We'd probably want to export and use this variable declared in fsp.c, * instead of each component individually maintaining the state.. may be for * later optimization */ static bool fsp_in_reset = false; struct opal_tpo_data { uint64_t tpo_async_token; uint32_t *year_month_day; uint32_t *hour_min; }; /* Timebase value when we last initiated a RTC read request */ static unsigned long read_req_tb; /* If a RTC read takes longer than this, we return a value generated * from the cache + timebase */ static const int rtc_read_timeout_ms = 1500; DEFINE_LOG_ENTRY(OPAL_RC_RTC_TOD, OPAL_PLATFORM_ERR_EVT, OPAL_RTC, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_RTC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_RTC, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); static void fsp_tpo_req_complete(struct fsp_msg *read_resp) { struct opal_tpo_data *attr = read_resp->user_data; int val; int rc; val = (read_resp->resp->word1 >> 8) & 0xff; switch (val) { case FSP_STATUS_TOD_RESET: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "RTC TPO in invalid state\n"); rc = OPAL_INTERNAL_ERROR; break; case FSP_STATUS_TOD_PERMANENT_ERROR: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "RTC TPO in permanent error state\n"); rc = OPAL_INTERNAL_ERROR; break; case FSP_STATUS_INVALID_DATA: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "RTC TPO in permanent error state\n"); rc = OPAL_PARAMETER; break; case FSP_STATUS_SUCCESS: /* Save the read TPO value in our cache */ if (attr->year_month_day) *(attr->year_month_day) = read_resp->resp->data.words[0]; if (attr->hour_min) *(attr->hour_min) = read_resp->resp->data.words[1]; rc = OPAL_SUCCESS; break; default: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "TPO read failed: %d\n", val); rc = OPAL_INTERNAL_ERROR; break; } opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, attr->tpo_async_token, rc); free(attr); fsp_freemsg(read_resp); } static void fsp_rtc_process_read(struct fsp_msg *read_resp) { int val = (read_resp->word1 >> 8) & 0xff; struct tm tm; assert(lock_held_by_me(&rtc_lock)); assert(rtc_read_request_state == RTC_READ_PENDING_REQUEST); switch (val) { case FSP_STATUS_TOD_RESET: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "RTC TOD in invalid state\n"); rtc_tod_state = RTC_TOD_INVALID; break; case FSP_STATUS_TOD_PERMANENT_ERROR: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "RTC TOD in permanent error state\n"); rtc_tod_state = RTC_TOD_PERMANENT_ERROR; break; case FSP_STATUS_SUCCESS: /* Save the read RTC value in our cache */ rtc_tod_state = RTC_TOD_VALID; datetime_to_tm(read_resp->data.words[0], (u64) read_resp->data.words[1] << 32, &tm); rtc_cache_update(&tm); prlog(PR_TRACE, "FSP-RTC Got time: %d-%d-%d %d:%d:%d\n", tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); break; default: log_simple_error(&e_info(OPAL_RC_RTC_TOD), "RTC TOD read failed: %d\n", val); rtc_tod_state = RTC_TOD_INVALID; } rtc_read_request_state = RTC_READ_REQUEST_AVAILABLE; } static void opal_rtc_eval_events(bool read_write) { bool request_available; if (read_write) request_available = (rtc_read_request_state == RTC_READ_REQUEST_AVAILABLE); else request_available = (rtc_write_request_state == RTC_WRITE_REQUEST_AVAILABLE); assert(lock_held_by_me(&rtc_lock)); opal_update_pending_evt(OPAL_EVENT_RTC, request_available ? OPAL_EVENT_RTC : 0); } static void fsp_rtc_req_complete(struct fsp_msg *msg) { lock(&rtc_lock); prlog(PR_TRACE, "RTC completion %p\n", msg); if (fsp_msg_cmd(msg) == (FSP_CMD_READ_TOD & 0xffffff)) { fsp_rtc_process_read(msg->resp); opal_rtc_eval_events(true); } else { assert(rtc_write_request_state == RTC_WRITE_PENDING_REQUEST); rtc_write_request_state = RTC_WRITE_REQUEST_AVAILABLE; opal_rtc_eval_events(false); } unlock(&rtc_lock); fsp_freemsg(msg); } static int64_t fsp_rtc_send_read_request(void) { struct fsp_msg *msg; int rc; assert(lock_held_by_me(&rtc_lock)); assert(rtc_read_request_state == RTC_READ_NO_REQUEST); msg = fsp_mkmsg(FSP_CMD_READ_TOD, 0); if (!msg) { log_simple_error(&e_info(OPAL_RC_RTC_READ), "RTC: failed to allocate read message\n"); return OPAL_INTERNAL_ERROR; } rc = fsp_queue_msg(msg, fsp_rtc_req_complete); if (rc) { fsp_freemsg(msg); log_simple_error(&e_info(OPAL_RC_RTC_READ), "RTC: failed to queue read message: %d\n", rc); return OPAL_INTERNAL_ERROR; } rtc_read_request_state = RTC_READ_PENDING_REQUEST; read_req_tb = mftb(); return OPAL_BUSY_EVENT; } static int64_t fsp_opal_rtc_read(uint32_t *year_month_day, uint64_t *hour_minute_second_millisecond) { int64_t rc; if (!year_month_day || !hour_minute_second_millisecond) return OPAL_PARAMETER; lock(&rtc_lock); if (rtc_tod_state == RTC_TOD_PERMANENT_ERROR) { rc = OPAL_HARDWARE; goto out; } /* During R/R of FSP, read cached TOD */ if (fsp_in_reset) { if (rtc_tod_state == RTC_TOD_VALID) { rtc_cache_get_datetime(year_month_day, hour_minute_second_millisecond); rc = OPAL_SUCCESS; } else { rc = OPAL_INTERNAL_ERROR; } goto out; } /* If we don't have a read pending already, fire off a request and * return */ if (rtc_read_request_state == RTC_READ_NO_REQUEST) { prlog(PR_TRACE, "Sending new RTC read request\n"); rc = fsp_rtc_send_read_request(); /* If our pending read is done, clear events and return the time * from the cache */ } else if (rtc_read_request_state == RTC_READ_REQUEST_AVAILABLE) { prlog(PR_TRACE, "RTC read complete, state %d\n", rtc_tod_state); rtc_read_request_state = RTC_READ_NO_REQUEST; opal_rtc_eval_events(true); if (rtc_tod_state == RTC_TOD_VALID) { rtc_cache_get_datetime(year_month_day, hour_minute_second_millisecond); prlog(PR_TRACE,"FSP-RTC Cached datetime: %x %llx\n", *year_month_day, *hour_minute_second_millisecond); rc = OPAL_SUCCESS; } else { rc = OPAL_INTERNAL_ERROR; } /* Timeout: return our cached value (updated from tb), but leave the * read request pending so it will update the cache later */ } else if (mftb() > read_req_tb + msecs_to_tb(rtc_read_timeout_ms)) { prlog(PR_TRACE, "RTC read timed out\n"); if (rtc_tod_state == RTC_TOD_VALID) { rtc_cache_get_datetime(year_month_day, hour_minute_second_millisecond); rc = OPAL_SUCCESS; } else { rc = OPAL_INTERNAL_ERROR; } /* Otherwise, we're still waiting on the read to complete */ } else { assert(rtc_read_request_state == RTC_READ_PENDING_REQUEST); rc = OPAL_BUSY_EVENT; } out: unlock(&rtc_lock); return rc; } static int64_t fsp_rtc_send_write_request(uint32_t year_month_day, uint64_t hour_minute_second_millisecond) { struct fsp_msg *msg; uint32_t w0, w1, w2; struct tm tm; assert(lock_held_by_me(&rtc_lock)); assert(rtc_write_request_state == RTC_WRITE_NO_REQUEST); /* Create a request and send it. Just like for read, we ignore * the "millisecond" field which is probably supposed to be * microseconds and which Linux ignores as well anyway */ w0 = year_month_day; w1 = (hour_minute_second_millisecond >> 32) & 0xffffff00; w2 = 0; msg = fsp_mkmsg(FSP_CMD_WRITE_TOD, 3, w0, w1, w2); if (!msg) { prlog(PR_TRACE, " -> allocation failed !\n"); return OPAL_INTERNAL_ERROR; } prlog(PR_TRACE, " -> req at %p\n", msg); if (fsp_in_reset) { datetime_to_tm(msg->data.words[0], (u64) msg->data.words[1] << 32, &tm); rtc_cache_update(&tm); rtc_tod_cache_dirty = true; fsp_freemsg(msg); return OPAL_SUCCESS; } else if (fsp_queue_msg(msg, fsp_rtc_req_complete)) { prlog(PR_TRACE, " -> queueing failed !\n"); fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } rtc_write_request_state = RTC_WRITE_PENDING_REQUEST; return OPAL_BUSY_EVENT; } static int64_t fsp_opal_rtc_write(uint32_t year_month_day, uint64_t hour_minute_second_millisecond) { int rc; lock(&rtc_lock); if (rtc_tod_state == RTC_TOD_PERMANENT_ERROR) { rc = OPAL_HARDWARE; goto out; } if (rtc_write_request_state == RTC_WRITE_NO_REQUEST) { prlog(PR_TRACE, "Sending new RTC write request\n"); rc = fsp_rtc_send_write_request(year_month_day, hour_minute_second_millisecond); } else if (rtc_write_request_state == RTC_WRITE_PENDING_REQUEST) { rc = OPAL_BUSY_EVENT; } else { assert(rtc_write_request_state == RTC_WRITE_REQUEST_AVAILABLE); rtc_write_request_state = RTC_WRITE_NO_REQUEST; opal_rtc_eval_events(false); rc = OPAL_SUCCESS; } out: unlock(&rtc_lock); return rc; } /* Set timed power on values to fsp */ static int64_t fsp_opal_tpo_write(uint64_t async_token, uint32_t y_m_d, uint32_t hr_min) { static struct opal_tpo_data *attr; struct fsp_msg *msg; if (!fsp_present()) return OPAL_HARDWARE; attr = zalloc(sizeof(struct opal_tpo_data)); if (!attr) return OPAL_NO_MEM; /* Create a request and send it.*/ attr->tpo_async_token = async_token; prlog(PR_TRACE, "Sending TPO write request...\n"); msg = fsp_mkmsg(FSP_CMD_TPO_WRITE, 2, y_m_d, hr_min); if (!msg) { prerror("TPO: Failed to create message for WRITE to FSP\n"); free(attr); return OPAL_INTERNAL_ERROR; } msg->user_data = attr; if (fsp_queue_msg(msg, fsp_tpo_req_complete)) { free(attr); fsp_freemsg(msg); return OPAL_INTERNAL_ERROR; } return OPAL_ASYNC_COMPLETION; } /* Read Timed power on (TPO) from FSP */ static int64_t fsp_opal_tpo_read(uint64_t async_token, uint32_t *y_m_d, uint32_t *hr_min) { static struct opal_tpo_data *attr; struct fsp_msg *msg; int64_t rc; if (!fsp_present()) return OPAL_HARDWARE; if (!y_m_d || !hr_min) return OPAL_PARAMETER; attr = zalloc(sizeof(*attr)); if (!attr) return OPAL_NO_MEM; /* Send read requet to FSP */ attr->tpo_async_token = async_token; attr->year_month_day = y_m_d; attr->hour_min = hr_min; prlog(PR_TRACE, "Sending new TPO read request\n"); msg = fsp_mkmsg(FSP_CMD_TPO_READ, 0); if (!msg) { log_simple_error(&e_info(OPAL_RC_RTC_READ), "TPO: failed to allocate read message\n"); free(attr); return OPAL_INTERNAL_ERROR; } msg->user_data = attr; rc = fsp_queue_msg(msg, fsp_tpo_req_complete); if (rc) { free(attr); fsp_freemsg(msg); log_simple_error(&e_info(OPAL_RC_RTC_READ), "TPO: failed to queue read message: %lld\n", rc); return OPAL_INTERNAL_ERROR; } return OPAL_ASYNC_COMPLETION; } static void rtc_flush_cached_tod(void) { struct fsp_msg *msg; uint64_t h_m_s_m; uint32_t y_m_d; if (rtc_cache_get_datetime(&y_m_d, &h_m_s_m)) return; msg = fsp_mkmsg(FSP_CMD_WRITE_TOD, 3, y_m_d, (h_m_s_m >> 32) & 0xffffff00, 0); if (!msg) { prerror("TPO: %s : Failed to allocate write TOD message\n", __func__); return; } if (fsp_queue_msg(msg, fsp_freemsg)) { fsp_freemsg(msg); prerror("TPO: %s : Failed to queue WRITE_TOD command\n", __func__); return; } } static bool fsp_rtc_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg) { int rc = false; assert(msg == NULL); switch (cmd_sub_mod) { case FSP_RESET_START: lock(&rtc_lock); fsp_in_reset = true; unlock(&rtc_lock); rc = true; break; case FSP_RELOAD_COMPLETE: lock(&rtc_lock); fsp_in_reset = false; if (rtc_tod_cache_dirty) { rtc_flush_cached_tod(); rtc_tod_cache_dirty = false; } unlock(&rtc_lock); rc = true; break; } return rc; } static struct fsp_client fsp_rtc_client_rr = { .message = fsp_rtc_msg_rr, }; void fsp_rtc_init(void) { struct dt_node *np; if (!fsp_present()) { rtc_tod_state = RTC_TOD_PERMANENT_ERROR; return; } opal_register(OPAL_RTC_READ, fsp_opal_rtc_read, 2); opal_register(OPAL_RTC_WRITE, fsp_opal_rtc_write, 2); opal_register(OPAL_WRITE_TPO, fsp_opal_tpo_write, 3); opal_register(OPAL_READ_TPO, fsp_opal_tpo_read, 3); np = dt_new(opal_node, "rtc"); dt_add_property_strings(np, "compatible", "ibm,opal-rtc"); dt_add_property(np, "has-tpo", NULL, 0); /* Register for the reset/reload event */ fsp_register_client(&fsp_rtc_client_rr, FSP_MCLASS_RR_EVENT); prlog(PR_TRACE, "Getting initial RTC TOD\n"); /* We don't wait for RTC response and this is actually okay as * any OPAL callers will wait correctly and if we ever have * internal users then they should check the state properly */ lock(&rtc_lock); fsp_rtc_send_read_request(); unlock(&rtc_lock); } skiboot-skiboot-5.1.13/hw/fsp/fsp-sensor.c000066400000000000000000000534201265204436200204040ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* */ /* * Design note: * This code will enable the 'powernv' to retrieve sensor related data from FSP * using SPCN passthru mailbox commands. * * The OPAL read sensor API in Sapphire is implemented as an 'asynchronous' read * call that returns after queuing the read request. A unique sensor-id is * expected as an argument for OPAL read call which has already been exported * to the device tree during fsp init. The sapphire code decodes this Id to * determine requested attribute and sensor. */ #include #include #include #include #include #include #include #include #include #define INVALID_DATA ((uint32_t)-1) /* Entry size of PRS command modifiers */ #define PRS_STATUS_ENTRY_SZ 0x08 #define SENSOR_PARAM_ENTRY_SZ 0x10 #define SENSOR_DATA_ENTRY_SZ 0x08 #define PROC_JUNC_ENTRY_SZ 0x04 DEFINE_LOG_ENTRY(OPAL_RC_SENSOR_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SENSOR, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_SENSOR_READ, OPAL_PLATFORM_ERR_EVT, OPAL_SENSOR, OPAL_MISC_SUBSYSTEM, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_SENSOR_ASYNC_COMPLETE, OPAL_PLATFORM_ERR_EVT, OPAL_SENSOR, OPAL_MISC_SUBSYSTEM, OPAL_INFO, OPAL_NA); /* FSP response status codes */ enum { SP_RSP_STATUS_VALID_DATA = 0x00, SP_RSP_STATUS_INVALID_DATA = 0x22, SP_RSP_STATUS_SPCN_ERR = 0xA8, SP_RSP_STATUS_DMA_ERR = 0x24, }; enum sensor_state { SENSOR_VALID_DATA, SENSOR_INVALID_DATA, SENSOR_SPCN_ERROR, SENSOR_DMA_ERROR, SENSOR_PERMANENT_ERROR, SENSOR_OPAL_ERROR, }; enum spcn_attr { /* mod 0x01, 0x02 */ SENSOR_PRESENT, SENSOR_FAULTED, SENSOR_AC_FAULTED, SENSOR_ON, SENSOR_ON_SUPPORTED, /* mod 0x10, 0x11 */ SENSOR_THRS, SENSOR_LOCATION, /* mod 0x12, 0x13 */ SENSOR_DATA, /* mod 0x1c */ SENSOR_POWER, SENSOR_MAX, }; /* Parsed sensor attributes, passed through OPAL */ struct opal_sensor_data { uint64_t async_token; /* Asynchronous token */ uint32_t *sensor_data; /* Kernel pointer to copy data */ enum spcn_attr spcn_attr; /* Modifier attribute */ uint16_t rid; /* Sensor RID */ uint8_t frc; /* Sensor resource class */ uint32_t mod_index; /* Modifier index*/ uint32_t offset; /* Offset in sensor buffer */ }; struct spcn_mod_attr { const char *name; enum spcn_attr val; }; struct spcn_mod { uint8_t mod; /* Modifier code */ uint8_t entry_size; /* Size of each entry in response buffer */ uint16_t entry_count; /* Number of entries */ struct spcn_mod_attr *mod_attr; }; static struct spcn_mod_attr prs_status_attrs[] = { {"present", SENSOR_PRESENT}, {"faulted", SENSOR_FAULTED}, {"ac-faulted", SENSOR_AC_FAULTED}, {"on", SENSOR_ON}, {"on-supported", SENSOR_ON_SUPPORTED} }; static struct spcn_mod_attr sensor_param_attrs[] = { {"thrs", SENSOR_THRS}, {"loc", SENSOR_LOCATION} }; static struct spcn_mod_attr sensor_data_attrs[] = { {"data", SENSOR_DATA} }; static struct spcn_mod_attr sensor_power_attrs[] = { {"power", SENSOR_POWER} }; static struct spcn_mod spcn_mod_data[] = { {SPCN_MOD_PRS_STATUS_FIRST, PRS_STATUS_ENTRY_SZ, 0, prs_status_attrs}, {SPCN_MOD_PRS_STATUS_SUBS, PRS_STATUS_ENTRY_SZ, 0, prs_status_attrs}, {SPCN_MOD_SENSOR_PARAM_FIRST, SENSOR_PARAM_ENTRY_SZ, 0, sensor_param_attrs}, {SPCN_MOD_SENSOR_PARAM_SUBS, SENSOR_PARAM_ENTRY_SZ, 0, sensor_param_attrs}, {SPCN_MOD_SENSOR_DATA_FIRST, SENSOR_DATA_ENTRY_SZ, 0, sensor_data_attrs}, {SPCN_MOD_SENSOR_DATA_SUBS, SENSOR_DATA_ENTRY_SZ, 0, sensor_data_attrs}, /* TODO Support this modifier '0x14', if required */ /* {SPCN_MOD_PROC_JUNC_TEMP, PROC_JUNC_ENTRY_SZ, 0, NULL}, */ {SPCN_MOD_SENSOR_POWER, SENSOR_DATA_ENTRY_SZ, 0, sensor_power_attrs}, {SPCN_MOD_LAST, 0xff, 0xffff, NULL} }; /* Frame resource class (FRC) names */ static const char *frc_names[] = { /* 0x00 and 0x01 are reserved */ NULL, NULL, "power-controller", "power-supply", "regulator", "cooling-fan", "cooling-controller", "battery-charger", "battery-pack", "amb-temp", "temp", "vrm", "riser-card", "io-backplane" }; #define SENSOR_MAX_SIZE 0x00100000 static void *sensor_buffer = NULL; static enum sensor_state sensor_state; static bool prev_msg_consumed = true; static struct lock sensor_lock; /* Function prototypes */ static int64_t fsp_sensor_send_read_request(struct opal_sensor_data *attr); static void queue_msg_for_delivery(int rc, struct opal_sensor_data *attr); /* * Power Resource Status (PRS) * Command: 0x42 * * Modifier: 0x01 * -------------------------------------------------------------------------- * | 0 1 2 3 4 5 6 7 | * -------------------------------------------------------------------------- * |Frame resrc class| PRID | SRC | Status | * -------------------------------------------------------------------------- * * * Modifier: 0x10 * -------------------------------------------------------------------------- * | 0 1 2 3 4 5 6 7 | * -------------------------------------------------------------------------- * |Frame resrc class| PRID | Sensor location | * -------------------------------------------------------------------------- * -------------------------------------------------------------------------- * | 8 9 10 11 12 13 14 15 | * -------------------------------------------------------------------------- * | Reserved | Reserved | Threshold | Status | * -------------------------------------------------------------------------- * * * Modifier: 0x12 * -------------------------------------------------------------------------- * | 0 1 2 3 4 5 6 7 | * -------------------------------------------------------------------------- * |Frame resrc class| PRID | Sensor data | Status | * -------------------------------------------------------------------------- * * * Modifier: 0x14 * -------------------------------------------------------------------------- * | 0 1 2 3 | * -------------------------------------------------------------------------- * |Enclosure Tj Avg | Chip Tj Avg | Reserved | Reserved | * -------------------------------------------------------------------------- */ static void fsp_sensor_process_data(struct opal_sensor_data *attr) { uint8_t *sensor_buf_ptr = (uint8_t *)sensor_buffer; uint32_t sensor_data = INVALID_DATA; uint16_t sensor_mod_data[8]; int count, i; uint8_t valid, nr_power; uint32_t power; for (count = 0; count < spcn_mod_data[attr->mod_index].entry_count; count++) { memcpy((void *)sensor_mod_data, sensor_buf_ptr, spcn_mod_data[attr->mod_index].entry_size); if (spcn_mod_data[attr->mod_index].mod == SPCN_MOD_PROC_JUNC_TEMP) { /* TODO Support this modifier '0x14', if required */ } else if (spcn_mod_data[attr->mod_index].mod == SPCN_MOD_SENSOR_POWER) { valid = sensor_buf_ptr[0]; if (valid & 0x80) { nr_power = valid & 0x0f; sensor_data = 0; for (i=0; i < nr_power; i++) { power = *(uint32_t *) &sensor_buf_ptr[2 + i * 5]; prlog(PR_TRACE, "Power[%d]: %d mW\n", i, power); sensor_data += power/1000; } } else { prlog(PR_TRACE, "Power Sensor data not valid\n"); } } else if (sensor_mod_data[0] == attr->frc && sensor_mod_data[1] == attr->rid) { switch (attr->spcn_attr) { /* modifier 0x01, 0x02 */ case SENSOR_PRESENT: prlog(PR_TRACE,"Not exported to device tree\n"); break; case SENSOR_FAULTED: sensor_data = sensor_mod_data[3] & 0x02; break; case SENSOR_AC_FAULTED: case SENSOR_ON: case SENSOR_ON_SUPPORTED: prlog(PR_TRACE,"Not exported to device tree\n"); break; /* modifier 0x10, 0x11 */ case SENSOR_THRS: sensor_data = sensor_mod_data[6]; break; case SENSOR_LOCATION: prlog(PR_TRACE,"Not exported to device tree\n"); break; /* modifier 0x12, 0x13 */ case SENSOR_DATA: sensor_data = sensor_mod_data[2]; break; default: break; } break; } sensor_buf_ptr += spcn_mod_data[attr->mod_index].entry_size; } *(attr->sensor_data) = sensor_data; if (sensor_data == INVALID_DATA) queue_msg_for_delivery(OPAL_PARTIAL, attr); else queue_msg_for_delivery(OPAL_SUCCESS, attr); } static int fsp_sensor_process_read(struct fsp_msg *resp_msg) { uint8_t mbx_rsp_status; uint32_t size = 0; mbx_rsp_status = (resp_msg->word1 >> 8) & 0xff; switch (mbx_rsp_status) { case SP_RSP_STATUS_VALID_DATA: sensor_state = SENSOR_VALID_DATA; size = resp_msg->data.words[1] & 0xffff; break; case SP_RSP_STATUS_INVALID_DATA: log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: %s: Received invalid data\n", __func__); sensor_state = SENSOR_INVALID_DATA; break; case SP_RSP_STATUS_SPCN_ERR: log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: %s: Failure due to SPCN error\n", __func__); sensor_state = SENSOR_SPCN_ERROR; break; case SP_RSP_STATUS_DMA_ERR: log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: %s: Failure due to DMA error\n", __func__); sensor_state = SENSOR_DMA_ERROR; break; default: log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR %s: Read failed, status:0x%02X\n", __func__, mbx_rsp_status); sensor_state = SENSOR_INVALID_DATA; break; } return size; } static void queue_msg_for_delivery(int rc, struct opal_sensor_data *attr) { prlog(PR_INSANE, "%s: rc:%d, data:%d\n", __func__, rc, *(attr->sensor_data)); opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, attr->async_token, rc); spcn_mod_data[attr->mod_index].entry_count = 0; free(attr); prev_msg_consumed = true; } static void fsp_sensor_read_complete(struct fsp_msg *msg) { struct opal_sensor_data *attr = msg->user_data; enum spcn_rsp_status status; int rc, size; prlog(PR_INSANE, "%s()\n", __func__); status = (msg->resp->data.words[1] >> 24) & 0xff; size = fsp_sensor_process_read(msg->resp); fsp_freemsg(msg); lock(&sensor_lock); if (sensor_state == SENSOR_VALID_DATA) { spcn_mod_data[attr->mod_index].entry_count += (size / spcn_mod_data[attr->mod_index].entry_size); attr->offset += size; /* Fetch the subsequent entries of the same modifier type */ if (status == SPCN_RSP_STATUS_COND_SUCCESS) { switch (spcn_mod_data[attr->mod_index].mod) { case SPCN_MOD_PRS_STATUS_FIRST: case SPCN_MOD_SENSOR_PARAM_FIRST: case SPCN_MOD_SENSOR_DATA_FIRST: attr->mod_index++; spcn_mod_data[attr->mod_index].entry_count = spcn_mod_data[attr->mod_index - 1]. entry_count; spcn_mod_data[attr->mod_index - 1].entry_count = 0; break; default: break; } rc = fsp_sensor_send_read_request(attr); if (rc != OPAL_ASYNC_COMPLETION) goto err; } else { /* Notify 'powernv' of read completion */ fsp_sensor_process_data(attr); } } else { rc = OPAL_INTERNAL_ERROR; goto err; } unlock(&sensor_lock); return; err: *(attr->sensor_data) = INVALID_DATA; queue_msg_for_delivery(rc, attr); unlock(&sensor_lock); log_simple_error(&e_info(OPAL_RC_SENSOR_ASYNC_COMPLETE), "SENSOR: %s: Failed to queue the " "read request to fsp\n", __func__); } static int64_t fsp_sensor_send_read_request(struct opal_sensor_data *attr) { int rc; struct fsp_msg *msg; uint32_t *sensor_buf_ptr; uint32_t align; uint32_t cmd_header; prlog(PR_INSANE, "Get the data for modifier [%d]\n", spcn_mod_data[attr->mod_index].mod); if (spcn_mod_data[attr->mod_index].mod == SPCN_MOD_PROC_JUNC_TEMP) { /* TODO Support this modifier '0x14', if required */ align = attr->offset % sizeof(*sensor_buf_ptr); if (align) attr->offset += (sizeof(*sensor_buf_ptr) - align); sensor_buf_ptr = (uint32_t *)((uint8_t *)sensor_buffer + attr->offset); /* TODO Add 8 byte command data required for mod 0x14 */ attr->offset += 8; cmd_header = spcn_mod_data[attr->mod_index].mod << 24 | SPCN_CMD_PRS << 16 | 0x0008; } else { cmd_header = spcn_mod_data[attr->mod_index].mod << 24 | SPCN_CMD_PRS << 16; } msg = fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_header, 0, PSI_DMA_SENSOR_BUF + attr->offset); if (!msg) { log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: Failed " "to allocate read message\n"); return OPAL_INTERNAL_ERROR; } msg->user_data = attr; rc = fsp_queue_msg(msg, fsp_sensor_read_complete); if (rc) { fsp_freemsg(msg); msg = NULL; log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: Failed " "to queue read message (%d)\n", rc); return OPAL_INTERNAL_ERROR; } return OPAL_ASYNC_COMPLETION; } static int64_t parse_sensor_id(uint32_t id, struct opal_sensor_data *attr) { uint32_t mod, index; attr->spcn_attr = id >> 24; if (attr->spcn_attr >= SENSOR_MAX) return OPAL_PARAMETER; if (attr->spcn_attr <= SENSOR_ON_SUPPORTED) mod = SPCN_MOD_PRS_STATUS_FIRST; else if (attr->spcn_attr <= SENSOR_LOCATION) mod = SPCN_MOD_SENSOR_PARAM_FIRST; else if (attr->spcn_attr <= SENSOR_DATA) mod = SPCN_MOD_SENSOR_DATA_FIRST; else if (attr->spcn_attr <= SENSOR_POWER) mod = SPCN_MOD_SENSOR_POWER; else return OPAL_PARAMETER; for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST; index++) { if (spcn_mod_data[index].mod == mod) break; } attr->mod_index = index; attr->frc = (id >> 16) & 0xff; attr->rid = id & 0xffff; return 0; } int64_t fsp_opal_read_sensor(uint32_t sensor_hndl, int token, uint32_t *sensor_data) { struct opal_sensor_data *attr; int64_t rc; prlog(PR_INSANE, "fsp_opal_read_sensor [%08x]\n", sensor_hndl); if (sensor_state == SENSOR_PERMANENT_ERROR) { rc = OPAL_HARDWARE; goto out; } if (!sensor_hndl) { rc = OPAL_PARAMETER; goto out; } lock(&sensor_lock); if (prev_msg_consumed) { attr = zalloc(sizeof(*attr)); if (!attr) { log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: Failed to allocate memory\n"); rc = OPAL_NO_MEM; goto out_lock; } /* Parse the sensor id and store them to the local structure */ rc = parse_sensor_id(sensor_hndl, attr); if (rc) { log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: %s: Failed to parse the sensor " "handle[0x%08x]\n", __func__, sensor_hndl); goto out_free; } /* Kernel buffer pointer to copy the data later when ready */ attr->sensor_data = sensor_data; attr->async_token = token; rc = fsp_sensor_send_read_request(attr); if (rc != OPAL_ASYNC_COMPLETION) { log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: %s: Failed to queue the read " "request to fsp\n", __func__); goto out_free; } prev_msg_consumed = false; } else { rc = OPAL_BUSY_EVENT; } unlock(&sensor_lock); return rc; out_free: free(attr); out_lock: unlock(&sensor_lock); out: return rc; } #define MAX_RIDS 64 #define MAX_NAME 64 static int get_index(uint16_t *prids, uint16_t rid) { int index; for (index = 0; prids[index] && index < MAX_RIDS; index++) if (prids[index] == rid) return index; if (index == MAX_RIDS) return -1; prids[index] = rid; return index; } static void create_sensor_nodes(int index, uint16_t frc, uint16_t rid, uint16_t *prids, struct dt_node *sensors) { char name[MAX_NAME]; struct dt_node *fs_node; uint32_t value; int rid_index; switch (spcn_mod_data[index].mod) { case SPCN_MOD_PRS_STATUS_FIRST: case SPCN_MOD_PRS_STATUS_SUBS: switch (frc) { case SENSOR_FRC_POWER_SUPPLY: case SENSOR_FRC_COOLING_FAN: rid_index = get_index(prids, rid); if (rid_index < 0) break; snprintf(name, MAX_NAME, "%s#%d-%s", frc_names[frc], /* Start enumeration from 1 */ rid_index + 1, spcn_mod_data[index].mod_attr[1].name); fs_node = dt_new(sensors, name); snprintf(name, MAX_NAME, "ibm,opal-sensor-%s", frc_names[frc]); dt_add_property_string(fs_node, "compatible", name); value = spcn_mod_data[index].mod_attr[1].val << 24 | (frc & 0xff) << 16 | rid; dt_add_property_cells(fs_node, "sensor-id", value); break; default: break; } break; case SPCN_MOD_SENSOR_PARAM_FIRST: case SPCN_MOD_SENSOR_PARAM_SUBS: case SPCN_MOD_SENSOR_DATA_FIRST: case SPCN_MOD_SENSOR_DATA_SUBS: switch (frc) { case SENSOR_FRC_POWER_SUPPLY: case SENSOR_FRC_COOLING_FAN: case SENSOR_FRC_AMB_TEMP: rid_index = get_index(prids, rid); if (rid_index < 0) break; snprintf(name, MAX_NAME, "%s#%d-%s", frc_names[frc], /* Start enumeration from 1 */ rid_index + 1, spcn_mod_data[index].mod_attr[0].name); fs_node = dt_new(sensors, name); snprintf(name, MAX_NAME, "ibm,opal-sensor-%s", frc_names[frc]); dt_add_property_string(fs_node, "compatible", name); value = spcn_mod_data[index].mod_attr[0].val << 24 | (frc & 0xff) << 16 | rid; dt_add_property_cells(fs_node, "sensor-id", value); if (spcn_mod_data[index].mod == SPCN_MOD_SENSOR_DATA_FIRST && frc == SENSOR_FRC_AMB_TEMP) dt_add_property_string(fs_node, "label", "Ambient"); break; default: break; } break; case SPCN_MOD_SENSOR_POWER: fs_node = dt_new(sensors, "power#1-data"); dt_add_property_string(fs_node, "compatible", "ibm,opal-sensor-power"); value = spcn_mod_data[index].mod_attr[0].val << 24; dt_add_property_cells(fs_node, "sensor-id", value); break; } } static void add_sensor_ids(struct dt_node *sensors) { uint32_t MAX_FRC_NAMES = sizeof(frc_names) / sizeof(*frc_names); uint8_t *sensor_buf_ptr = (uint8_t *)sensor_buffer; uint16_t *prids[MAX_FRC_NAMES]; uint16_t sensor_frc, power_rid; uint16_t sensor_mod_data[8]; uint32_t index, count; for (index = 0; index < MAX_FRC_NAMES; index++) prids[index] = zalloc(MAX_RIDS * sizeof(**prids)); for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST; index++) { if (spcn_mod_data[index].mod == SPCN_MOD_SENSOR_POWER) { create_sensor_nodes(index, 0, 0, 0, sensors); continue; } for (count = 0; count < spcn_mod_data[index].entry_count; count++) { if (spcn_mod_data[index].mod == SPCN_MOD_PROC_JUNC_TEMP) { /* TODO Support this modifier '0x14', if * required */ } else { memcpy((void *)sensor_mod_data, sensor_buf_ptr, spcn_mod_data[index].entry_size); sensor_frc = sensor_mod_data[0]; power_rid = sensor_mod_data[1]; if (sensor_frc < MAX_FRC_NAMES && frc_names[sensor_frc]) create_sensor_nodes(index, sensor_frc, power_rid, prids[sensor_frc], sensors); } sensor_buf_ptr += spcn_mod_data[index].entry_size; } } for (index = 0; index < MAX_FRC_NAMES; index++) free(prids[index]); } static void add_opal_sensor_node(void) { int index; if (!fsp_present()) return; add_sensor_ids(sensor_node); /* Reset the entry count of each modifier */ for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST; index++) spcn_mod_data[index].entry_count = 0; } void fsp_init_sensor(void) { uint32_t cmd_header, align, size, psi_dma_offset = 0; enum spcn_rsp_status status; uint32_t *sensor_buf_ptr; struct fsp_msg msg, resp; int index, rc; if (!fsp_present()) { sensor_state = SENSOR_PERMANENT_ERROR; return; } sensor_buffer = memalign(TCE_PSIZE, SENSOR_MAX_SIZE); if (!sensor_buffer) { log_simple_error(&e_info(OPAL_RC_SENSOR_INIT), "SENSOR: could " "not allocate sensor_buffer!\n"); return; } /* Map TCE */ fsp_tce_map(PSI_DMA_SENSOR_BUF, sensor_buffer, PSI_DMA_SENSOR_BUF_SZ); msg.resp = &resp; /* Traverse using all the modifiers to know all the sensors available * in the system */ for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST && sensor_state == SENSOR_VALID_DATA;) { prlog(PR_TRACE, "Get the data for modifier [%d]\n", spcn_mod_data[index].mod); if (spcn_mod_data[index].mod == SPCN_MOD_PROC_JUNC_TEMP) { /* TODO Support this modifier 0x14, if required */ align = psi_dma_offset % sizeof(*sensor_buf_ptr); if (align) psi_dma_offset += (sizeof(*sensor_buf_ptr) - align); sensor_buf_ptr = (uint32_t *)((uint8_t *)sensor_buffer + psi_dma_offset); /* TODO Add 8 byte command data required for mod 0x14 */ psi_dma_offset += 8; cmd_header = spcn_mod_data[index].mod << 24 | SPCN_CMD_PRS << 16 | 0x0008; } else { cmd_header = spcn_mod_data[index].mod << 24 | SPCN_CMD_PRS << 16; } fsp_fillmsg(&msg, FSP_CMD_SPCN_PASSTHRU, 4, SPCN_ADDR_MODE_CEC_NODE, cmd_header, 0, PSI_DMA_SENSOR_BUF + psi_dma_offset); rc = fsp_sync_msg(&msg, false); if (rc >= 0) { status = (resp.data.words[1] >> 24) & 0xff; size = fsp_sensor_process_read(&resp); psi_dma_offset += size; spcn_mod_data[index].entry_count += (size / spcn_mod_data[index].entry_size); } else { sensor_state = SENSOR_PERMANENT_ERROR; break; } switch (spcn_mod_data[index].mod) { case SPCN_MOD_PRS_STATUS_FIRST: case SPCN_MOD_SENSOR_PARAM_FIRST: case SPCN_MOD_SENSOR_DATA_FIRST: if (status == SPCN_RSP_STATUS_COND_SUCCESS) index++; else index += 2; break; case SPCN_MOD_PRS_STATUS_SUBS: case SPCN_MOD_SENSOR_PARAM_SUBS: case SPCN_MOD_SENSOR_DATA_SUBS: if (status != SPCN_RSP_STATUS_COND_SUCCESS) index++; break; case SPCN_MOD_SENSOR_POWER: index++; default: break; } } if (sensor_state != SENSOR_VALID_DATA) sensor_state = SENSOR_PERMANENT_ERROR; else add_opal_sensor_node(); } skiboot-skiboot-5.1.13/hw/fsp/fsp-surveillance.c000066400000000000000000000132641265204436200215710ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include static bool fsp_surv_state = false; static bool fsp_surv_ack_pending = false; static u64 surv_timer; static u64 surv_ack_timer; static u32 surv_state_param; static struct lock surv_lock = LOCK_UNLOCKED; #define FSP_SURV_ACK_TIMEOUT 120 /* surv ack timeout in seconds */ DEFINE_LOG_ENTRY(OPAL_RC_SURVE_INIT, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_MISCELLANEOUS_INFO_ONLY); DEFINE_LOG_ENTRY(OPAL_RC_SURVE_STATUS, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_MISCELLANEOUS_INFO_ONLY); DEFINE_LOG_ENTRY(OPAL_RC_SURVE_ACK, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_MISCELLANEOUS_INFO_ONLY); static void fsp_surv_ack(struct fsp_msg *msg) { uint8_t val; if (!msg->resp) return; val = (msg->resp->word1 >> 8) & 0xff; if (val == 0) { /* reset the pending flag */ prlog(PR_DEBUG, "SURV: Received heartbeat acknowledge from FSP\n"); lock(&surv_lock); fsp_surv_ack_pending = false; unlock(&surv_lock); } else prlog(PR_ERR, "SURV: Heartbeat Acknowledgment error from FSP\n"); fsp_freemsg(msg); } static void fsp_surv_check_timeout(void) { u64 now = mftb(); /* * We just checked fsp_surv_ack_pending to be true in fsp_surv_hbeat * and we haven't dropped the surv_lock between then and now. So, we * just go ahead and check timeouts. */ if (tb_compare(now, surv_ack_timer) == TB_AAFTERB) { /* XXX: We should be logging a PEL to the host, assuming * the FSP is dead, pending a R/R. */ log_simple_error(&e_info(OPAL_RC_SURVE_ACK), "SURV: Surv ACK timed out; initiating R/R\n"); /* Reset the pending trigger too */ fsp_surv_ack_pending = false; fsp_trigger_reset(); } return; } /* Send surveillance heartbeat based on a timebase trigger */ static void fsp_surv_hbeat(void) { u64 now = mftb(); struct fsp_msg *msg; /* Check if an ack is pending... if so, don't send the ping just yet */ if (fsp_surv_ack_pending) { fsp_surv_check_timeout(); return; } /* add timebase callbacks */ /* * XXX This packet needs to be pushed to FSP in an interval * less than 120s that's advertised to FSP. * * Verify if the command building format and call is fine. */ if (surv_timer == 0 || (tb_compare(now, surv_timer) == TB_AAFTERB) || (tb_compare(now, surv_timer) == TB_AEQUALB)) { prlog(PR_DEBUG, "SURV: Sending the heartbeat command to FSP\n"); msg = fsp_mkmsg(FSP_CMD_SURV_HBEAT, 1, 120); if (!msg) { prerror("SURV: Failed to allocate heartbeat msg\n"); return; } if (fsp_queue_msg(msg, fsp_surv_ack)) { fsp_freemsg(msg); prerror("SURV: Failed to queue heartbeat msg\n"); } else { fsp_surv_ack_pending = true; surv_timer = now + secs_to_tb(60); surv_ack_timer = now + secs_to_tb(FSP_SURV_ACK_TIMEOUT); } } } static void fsp_surv_poll(void *data __unused) { if (!fsp_surv_state) return; lock(&surv_lock); fsp_surv_hbeat(); unlock(&surv_lock); } static void fsp_surv_got_param(uint32_t param_id __unused, int err_len, void *data __unused) { if (err_len != 4) { log_simple_error(&e_info(OPAL_RC_SURVE_STATUS), "SURV: Error (%d) retrieving surv status; initiating R/R\n", err_len); fsp_trigger_reset(); return; } printf("SURV: Status from FSP: %d\n", surv_state_param); if (!(surv_state_param & 0x01)) return; lock(&surv_lock); fsp_surv_state = true; /* Also send one heartbeat now. The next one will not happen * until we hit the OS. */ fsp_surv_hbeat(); unlock(&surv_lock); } void fsp_surv_query(void) { int rc; printf("SURV: Querying FSP's surveillance status\n"); /* Reset surveillance settings */ lock(&surv_lock); fsp_surv_state = false; surv_timer = 0; surv_ack_timer = 0; unlock(&surv_lock); /* Query FPS for surveillance state */ rc = fsp_get_sys_param(SYS_PARAM_SURV, &surv_state_param, 4, fsp_surv_got_param, NULL); if (rc) { log_simple_error(&e_info(OPAL_RC_SURVE_INIT), "SURV: Error %d queueing param request\n", rc); } } static bool fsp_surv_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg) { assert(msg == NULL); switch (cmd_sub_mod) { case FSP_RESET_START: printf("SURV: Disabling surveillance\n"); lock(&surv_lock); fsp_surv_state = false; fsp_surv_ack_pending = false; unlock(&surv_lock); return true; case FSP_RELOAD_COMPLETE: fsp_surv_query(); return true; } return false; } static struct fsp_client fsp_surv_client_rr = { .message = fsp_surv_msg_rr, }; /* This is called at boot time */ void fsp_init_surveillance(void) { /* Always register the poller, so we don't have to add/remove * it on reset-reload or change of surveillance state. Also the * poller list has no locking so we don't want to play with it * at runtime. */ opal_add_poller(fsp_surv_poll, NULL); /* Register for the reset/reload event */ fsp_register_client(&fsp_surv_client_rr, FSP_MCLASS_RR_EVENT); /* Send query to FSP */ fsp_surv_query(); } skiboot-skiboot-5.1.13/hw/fsp/fsp-sysparam.c000066400000000000000000000304101265204436200207240ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include struct sysparam_comp_data { uint32_t param_len; uint64_t async_token; }; struct sysparam_req { sysparam_compl_t completion; void *comp_data; void *ubuf; uint32_t ulen; struct fsp_msg msg; struct fsp_msg resp; bool done; }; static struct sysparam_attr { const char *name; uint32_t id; uint32_t length; uint8_t perm; } sysparam_attrs[] = { #define _R OPAL_SYSPARAM_READ #define _W OPAL_SYSPARAM_WRITE #define _RW OPAL_SYSPARAM_RW {"surveillance", SYS_PARAM_SURV, 4, _RW}, {"hmc-management", SYS_PARAM_HMC_MANAGED, 4, _R}, {"cupd-policy", SYS_PARAM_FLASH_POLICY, 4, _RW}, {"plat-hmc-managed", SYS_PARAM_NEED_HMC, 4, _RW}, {"fw-license-policy", SYS_PARAM_FW_LICENSE, 4, _RW}, {"world-wide-port-num", SYS_PARAM_WWPN, 12, _W}, {"default-boot-device", SYS_PARAM_DEF_BOOT_DEV, 1, _RW}, {"next-boot-device", SYS_PARAM_NEXT_BOOT_DEV,1, _RW}, {"console-select", SYS_PARAM_CONSOLE_SELECT,1, _RW}, {"boot-device-path", SYS_PARAM_BOOT_DEV_PATH,48, _RW} #undef _R #undef _W #undef _RW }; static int fsp_sysparam_process(struct sysparam_req *r) { u32 param_id, len; int stlen = 0; u8 fstat; /* Snapshot completion before we set the "done" flag */ sysparam_compl_t comp = r->completion; void *cdata = r->comp_data; if (r->msg.state != fsp_msg_done) { prerror("FSP: Request for sysparam 0x%x got FSP failure!\n", r->msg.data.words[0]); stlen = -1; /* XXX Find saner error codes */ goto complete; } param_id = r->resp.data.words[0]; len = r->resp.data.words[1] & 0xffff; /* Check params validity */ if (param_id != r->msg.data.words[0]) { prerror("FSP: Request for sysparam 0x%x got resp. for 0x%x!\n", r->msg.data.words[0], param_id); stlen = -2; /* XXX Sane error codes */ goto complete; } if (len > r->ulen) { prerror("FSP: Request for sysparam 0x%x truncated!\n", param_id); len = r->ulen; } /* Decode the request status */ fstat = (r->msg.resp->word1 >> 8) & 0xff; switch(fstat) { case 0x00: /* XXX Is that even possible ? */ case 0x11: /* Data in request */ memcpy(r->ubuf, &r->resp.data.words[2], len); /* pass through */ case 0x12: /* Data in TCE */ stlen = len; break; default: stlen = -fstat; } complete: /* Call completion if any */ if (comp) comp(r->msg.data.words[0], stlen, cdata); free(r); return stlen; } static void fsp_sysparam_get_complete(struct fsp_msg *msg) { struct sysparam_req *r = container_of(msg, struct sysparam_req, msg); /* If it's an asynchronous request, process it now */ if (r->completion) { fsp_sysparam_process(r); return; } /* Else just set the done flag */ /* Another CPU can be polling on the "done" flag without the * lock held, so let's order the udpates to the structure */ lwsync(); r->done = true; } int fsp_get_sys_param(uint32_t param_id, void *buffer, uint32_t length, sysparam_compl_t async_complete, void *comp_data) { struct sysparam_req *r; uint64_t baddr, tce_token; int rc; if (!fsp_present()) return -ENODEV; /* * XXX FIXME: We currently always allocate the sysparam_req here * however, we want to avoid runtime allocations as much as * possible, so if this is going to be used a lot at runtime, * we probably want to pre-allocate a pool of these */ if (length > 4096) return -EINVAL; r = zalloc(sizeof(struct sysparam_req)); if (!r) return -ENOMEM; r->completion = async_complete; r->comp_data = comp_data; r->done = false; r->ubuf = buffer; r->ulen = length; r->msg.resp = &r->resp; /* Map always 1 page ... easier that way and none of that * is performance critical */ baddr = (uint64_t)buffer; fsp_tce_map(PSI_DMA_GET_SYSPARAM, (void *)(baddr & ~0xffful), 0x1000); tce_token = PSI_DMA_GET_SYSPARAM | (baddr & 0xfff); fsp_fillmsg(&r->msg, FSP_CMD_QUERY_SPARM, 3, param_id, length, tce_token); rc = fsp_queue_msg(&r->msg, fsp_sysparam_get_complete); if (rc) free(r); /* Asynchronous operation or queueing failure, return */ if (rc || async_complete) return rc; /* Synchronous operation requested, spin and process */ while(!r->done) opal_run_pollers(); /* Will free the request */ return fsp_sysparam_process(r); } static void fsp_opal_getparam_complete(uint32_t param_id __unused, int err_len, void *data) { struct sysparam_comp_data *comp_data = data; int rc = OPAL_SUCCESS; if (comp_data->param_len != err_len) rc = OPAL_INTERNAL_ERROR; opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, comp_data->async_token, rc); free(comp_data); } static void fsp_opal_setparam_complete(struct fsp_msg *msg) { struct sysparam_comp_data *comp_data = msg->user_data; u8 fstat; uint32_t param_id; int rc = OPAL_SUCCESS; if (msg->state != fsp_msg_done) { prerror("FSP: Request for set sysparam 0x%x got FSP failure!\n", msg->data.words[0]); rc = OPAL_INTERNAL_ERROR; goto out; } param_id = msg->resp->data.words[0]; if (param_id != msg->data.words[0]) { prerror("FSP: Request for set sysparam 0x%x got resp. for 0x%x!" "\n", msg->data.words[0], param_id); rc = OPAL_INTERNAL_ERROR; goto out; } fstat = (msg->resp->word1 >> 8) & 0xff; switch (fstat) { case 0x00: rc = OPAL_SUCCESS; break; case 0x22: prerror("%s: Response status 0x%x, invalid data\n", __func__, fstat); rc = OPAL_INTERNAL_ERROR; break; case 0x24: prerror("%s: Response status 0x%x, DMA error\n", __func__, fstat); rc = OPAL_INTERNAL_ERROR; break; default: rc = OPAL_INTERNAL_ERROR; break; } out: opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, comp_data->async_token, rc); free(comp_data); fsp_freemsg(msg); } /* OPAL interface for PowerNV to read the system parameter from FSP */ static int64_t fsp_opal_get_param(uint64_t async_token, uint32_t param_id, uint64_t buffer, uint64_t length) { struct sysparam_comp_data *comp_data; int count, rc, i; if (!fsp_present()) return OPAL_HARDWARE; count = ARRAY_SIZE(sysparam_attrs); for (i = 0; i < count; i++) if (sysparam_attrs[i].id == param_id) break; if (i == count) return OPAL_PARAMETER; if (length < sysparam_attrs[i].length) return OPAL_PARAMETER; if (!(sysparam_attrs[i].perm & OPAL_SYSPARAM_READ)) return OPAL_PERMISSION; comp_data = zalloc(sizeof(struct sysparam_comp_data)); if (!comp_data) return OPAL_NO_MEM; comp_data->param_len = sysparam_attrs[i].length; comp_data->async_token = async_token; rc = fsp_get_sys_param(param_id, (void *)buffer, sysparam_attrs[i].length, fsp_opal_getparam_complete, comp_data); if (rc) { free(comp_data); prerror("%s: Error %d queuing param request\n", __func__, rc); return OPAL_INTERNAL_ERROR; } return OPAL_ASYNC_COMPLETION; } /* OPAL interface for PowerNV to update the system parameter to FSP */ static int64_t fsp_opal_set_param(uint64_t async_token, uint32_t param_id, uint64_t buffer, uint64_t length) { struct sysparam_comp_data *comp_data; struct fsp_msg *msg; uint64_t tce_token; int count, rc, i; if (!fsp_present()) return OPAL_HARDWARE; count = ARRAY_SIZE(sysparam_attrs); for (i = 0; i < count; i++) if (sysparam_attrs[i].id == param_id) break; if (i == count) return OPAL_PARAMETER; if (length < sysparam_attrs[i].length) return OPAL_PARAMETER; if (!(sysparam_attrs[i].perm & OPAL_SYSPARAM_WRITE)) return OPAL_PERMISSION; fsp_tce_map(PSI_DMA_SET_SYSPARAM, (void *)(buffer & ~0xffful), 0x1000); tce_token = PSI_DMA_SET_SYSPARAM | (buffer & 0xfff); msg = fsp_mkmsg(FSP_CMD_SET_SPARM_2, 4, param_id, length, tce_token >> 32, tce_token); if (!msg) { prerror("%s: Failed to allocate the message\n", __func__); return OPAL_INTERNAL_ERROR; } comp_data = zalloc(sizeof(struct sysparam_comp_data)); if (!comp_data) { fsp_freemsg(msg); return OPAL_NO_MEM; } comp_data->param_len = length; comp_data->async_token = async_token; msg->user_data = comp_data; rc = fsp_queue_msg(msg, fsp_opal_setparam_complete); if (rc) { free(comp_data); fsp_freemsg(msg); prerror("%s: Failed to queue the message\n", __func__); return OPAL_INTERNAL_ERROR; } return OPAL_ASYNC_COMPLETION; } struct sysparam_notify_entry { struct list_node link; sysparam_update_notify notify; }; static LIST_HEAD(sysparam_update_notifiers); /* Add client to notifier chain */ void sysparam_add_update_notifier(sysparam_update_notify notify) { struct sysparam_notify_entry *entry; entry = zalloc(sizeof(struct sysparam_notify_entry)); assert(entry); entry->notify = notify; list_add_tail(&sysparam_update_notifiers, &entry->link); } /* Remove client from notifier chain */ void sysparam_del_update_notifier(sysparam_update_notify notify) { struct sysparam_notify_entry *entry; list_for_each(&sysparam_update_notifiers, entry, link) { if (entry->notify == notify) { list_del(&entry->link); free(entry); return; } } } /* Update notification chain */ static void sysparam_run_update_notifier(struct fsp_msg *msg) { bool ret; struct sysparam_notify_entry *entry; list_for_each(&sysparam_update_notifiers, entry, link) { ret = entry->notify(msg); if (ret == true) break; } } static bool fsp_sysparam_msg(u32 cmd_sub_mod, struct fsp_msg *msg) { struct fsp_msg *rsp; int rc = -ENOMEM; switch(cmd_sub_mod) { case FSP_CMD_SP_SPARM_UPD_0: case FSP_CMD_SP_SPARM_UPD_1: printf("FSP: Got sysparam update, param ID 0x%x\n", msg->data.words[0]); sysparam_run_update_notifier(msg); rsp = fsp_mkmsg((cmd_sub_mod & 0xffff00) | 0x008000, 0); if (rsp) rc = fsp_queue_msg(rsp, fsp_freemsg); if (rc) { prerror("FSP: Error %d queuing sysparam reply\n", rc); /* What to do here ? R/R ? */ fsp_freemsg(rsp); } return true; } return false; } static struct fsp_client fsp_sysparam_client = { .message = fsp_sysparam_msg, }; static void add_opal_sysparam_node(void) { struct dt_node *sysparams; char *names, *s; uint32_t *ids, *lens; uint8_t *perms; unsigned int i, count, size = 0; if (!fsp_present()) return; sysparams = dt_new(opal_node, "sysparams"); dt_add_property_string(sysparams, "compatible", "ibm,opal-sysparams"); count = ARRAY_SIZE(sysparam_attrs); for (i = 0; i < count; i++) size = size + strlen(sysparam_attrs[i].name) + 1; names = zalloc(size); if (!names) { prerror("%s: Failed to allocate memory for parameter names\n", __func__); return; } ids = zalloc(count * sizeof(*ids)); if (!ids) { prerror("%s: Failed to allocate memory for parameter ids\n", __func__); goto out_free_name; } lens = zalloc(count * sizeof(*lens)); if (!lens) { prerror("%s: Failed to allocate memory for parameter length\n", __func__); goto out_free_id; } perms = zalloc(count * sizeof(*perms)); if (!perms) { prerror("%s: Failed to allocate memory for parameter length\n", __func__); goto out_free_len; } s = names; for (i = 0; i < count; i++) { strcpy(s, sysparam_attrs[i].name); s = s + strlen(sysparam_attrs[i].name) + 1; ids[i] = sysparam_attrs[i].id; lens[i] = sysparam_attrs[i].length; perms[i] = sysparam_attrs[i].perm; } dt_add_property(sysparams, "param-name", names, size); dt_add_property(sysparams, "param-id", ids, count * sizeof(*ids)); dt_add_property(sysparams, "param-len", lens, count * sizeof(*lens)); dt_add_property(sysparams, "param-perm", perms, count * sizeof(*perms)); free(perms); out_free_len: free(lens); out_free_id: free(ids); out_free_name: free(names); } void fsp_sysparam_init(void) { if (!fsp_present()) return; /* Register change notifications */ fsp_register_client(&fsp_sysparam_client, FSP_MCLASS_SERVICE); /* Register OPAL interfaces */ opal_register(OPAL_GET_PARAM, fsp_opal_get_param, 4); opal_register(OPAL_SET_PARAM, fsp_opal_set_param, 4); /* Add device-tree nodes */ add_opal_sysparam_node(); } skiboot-skiboot-5.1.13/hw/fsp/fsp.c000066400000000000000000001776621265204436200171140ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Service Processor handling code * * XXX This mixes PSI and FSP and currently only supports * P7/P7+ PSI and FSP1 * * If we are going to support P8 PSI and FSP2, we probably want * to split the PSI support from the FSP support proper first. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DEFINE_LOG_ENTRY(OPAL_RC_FSP_POLL_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_FSP, OPAL_PLATFORM_FIRMWARE, OPAL_ERROR_PANIC, OPAL_NA); #define FSP_TRACE_MSG #define FSP_TRACE_EVENT #define FSP_MAX_IOPATH 4 enum fsp_path_state { fsp_path_bad, fsp_path_backup, fsp_path_active, }; struct fsp_iopath { enum fsp_path_state state; void *fsp_regs; struct psi *psi; }; enum fsp_mbx_state { fsp_mbx_idle, /* Mailbox ready to send */ fsp_mbx_send, /* Mailbox sent, waiting for ack */ fsp_mbx_crit_op, /* Critical operation in progress */ fsp_mbx_prep_for_reset, /* Prepare for reset sent */ fsp_mbx_hir_seq_done, /* HIR sequence done, link forced down */ fsp_mbx_err, /* Mailbox in error state, waiting for r&r */ fsp_mbx_rr, /* Mailbox in r&r */ }; struct fsp { struct fsp *link; unsigned int index; enum fsp_mbx_state state; struct fsp_msg *pending; unsigned int iopath_count; int active_iopath; /* -1: no active IO path */ struct fsp_iopath iopath[FSP_MAX_IOPATH]; }; enum ipl_state { ipl_initial = 0x00000000, ipl_opl_sent = 0x00000001, ipl_got_continue = 0x00000002, ipl_got_new_role = 0x00000004, ipl_got_caps = 0x00000008, ipl_got_fsp_functional = 0x00000010 }; static enum ipl_state ipl_state = ipl_initial; static struct fsp *first_fsp; static struct fsp *active_fsp; static u16 fsp_curseq = 0x8000; static u64 *fsp_tce_table; #define FSP_INBOUND_SIZE 0x00100000UL static void *fsp_inbound_buf = NULL; static u32 fsp_inbound_off; static struct lock fsp_lock = LOCK_UNLOCKED; static struct lock fsp_poll_lock = LOCK_UNLOCKED; static u64 fsp_cmdclass_resp_bitmask; static u64 timeout_timer; static u64 fsp_hir_timeout; #define FSP_CRITICAL_OP_TIMEOUT 128 #define FSP_DRCR_CLEAR_TIMEOUT 128 /* LID numbers. For now we hijack some of pHyp's own until i figure * out the whole business with the MasterLID */ #define KERNEL_LID_PHYP 0x80a00701 #define KERNEL_LID_OPAL 0x80f00101 #define INITRAMFS_LID_OPAL 0x80f00102 /* * We keep track on last logged values for some things to print only on * value changes, but also to relieve pressure on the tracer which * doesn't do a very good job at detecting repeats when called from * many different CPUs */ static u32 disr_last_print; static u32 drcr_last_print; static u32 hstate_last_print; void fsp_handle_resp(struct fsp_msg *msg); struct fsp_cmdclass { int timeout; bool busy; struct list_head msgq; struct list_head clientq; struct list_head rr_queue; /* To queue up msgs during R/R */ u64 timesent; }; static struct fsp_cmdclass fsp_cmdclass_rr; static struct fsp_cmdclass fsp_cmdclass[FSP_MCLASS_LAST - FSP_MCLASS_FIRST + 1] = { #define DEF_CLASS(_cl, _to) [_cl - FSP_MCLASS_FIRST] = { .timeout = _to } DEF_CLASS(FSP_MCLASS_SERVICE, 16), DEF_CLASS(FSP_MCLASS_PCTRL_MSG, 16), DEF_CLASS(FSP_MCLASS_PCTRL_ABORTS, 16), DEF_CLASS(FSP_MCLASS_ERR_LOG, 16), DEF_CLASS(FSP_MCLASS_CODE_UPDATE, 40), DEF_CLASS(FSP_MCLASS_FETCH_SPDATA, 16), DEF_CLASS(FSP_MCLASS_FETCH_HVDATA, 16), DEF_CLASS(FSP_MCLASS_NVRAM, 16), DEF_CLASS(FSP_MCLASS_MBOX_SURV, 2), DEF_CLASS(FSP_MCLASS_RTC, 16), DEF_CLASS(FSP_MCLASS_SMART_CHIP, 20), DEF_CLASS(FSP_MCLASS_INDICATOR, 180), DEF_CLASS(FSP_MCLASS_HMC_INTFMSG, 16), DEF_CLASS(FSP_MCLASS_HMC_VT, 16), DEF_CLASS(FSP_MCLASS_HMC_BUFFERS, 16), DEF_CLASS(FSP_MCLASS_SHARK, 16), DEF_CLASS(FSP_MCLASS_MEMORY_ERR, 16), DEF_CLASS(FSP_MCLASS_CUOD_EVENT, 16), DEF_CLASS(FSP_MCLASS_HW_MAINT, 16), DEF_CLASS(FSP_MCLASS_VIO, 16), DEF_CLASS(FSP_MCLASS_SRC_MSG, 16), DEF_CLASS(FSP_MCLASS_DATA_COPY, 16), DEF_CLASS(FSP_MCLASS_TONE, 16), DEF_CLASS(FSP_MCLASS_VIRTUAL_NVRAM, 16), DEF_CLASS(FSP_MCLASS_TORRENT, 16), DEF_CLASS(FSP_MCLASS_NODE_PDOWN, 16), DEF_CLASS(FSP_MCLASS_DIAG, 16), DEF_CLASS(FSP_MCLASS_PCIE_LINK_TOPO, 16), DEF_CLASS(FSP_MCLASS_OCC, 16), }; static void fsp_trace_msg(struct fsp_msg *msg, u8 dir __unused) { union trace fsp __unused; #ifdef FSP_TRACE_MSG size_t len = offsetof(struct trace_fsp_msg, data[msg->dlen]); fsp.fsp_msg.dlen = msg->dlen; fsp.fsp_msg.word0 = msg->word0; fsp.fsp_msg.word1 = msg->word1; fsp.fsp_msg.dir = dir; memcpy(fsp.fsp_msg.data, msg->data.bytes, msg->dlen); trace_add(&fsp, TRACE_FSP_MSG, len); #endif /* FSP_TRACE_MSG */ assert(msg->dlen <= sizeof(fsp.fsp_msg.data)); } static struct fsp *fsp_get_active(void) { /* XXX Handle transition between FSPs */ return active_fsp; } static u64 fsp_get_class_bit(u8 class) { /* Alias classes CE and CF as the FSP has a single queue */ if (class == FSP_MCLASS_IPL) class = FSP_MCLASS_SERVICE; return 1ul << (class - FSP_MCLASS_FIRST); } static struct fsp_cmdclass *__fsp_get_cmdclass(u8 class) { struct fsp_cmdclass *ret; /* RR class is special */ if (class == FSP_MCLASS_RR_EVENT) return &fsp_cmdclass_rr; /* Bound check */ if (class < FSP_MCLASS_FIRST || class > FSP_MCLASS_LAST) return NULL; /* Alias classes CE and CF as the FSP has a single queue */ if (class == FSP_MCLASS_IPL) class = FSP_MCLASS_SERVICE; ret = &fsp_cmdclass[class - FSP_MCLASS_FIRST]; /* Unknown class */ if (ret->timeout == 0) return NULL; return ret; } static struct fsp_cmdclass *fsp_get_cmdclass(struct fsp_msg *msg) { u8 c = msg->word0 & 0xff; return __fsp_get_cmdclass(c); } static struct fsp_msg *__fsp_allocmsg(void) { return zalloc(sizeof(struct fsp_msg)); } struct fsp_msg *fsp_allocmsg(bool alloc_response) { struct fsp_msg *msg; msg = __fsp_allocmsg(); if (!msg) return NULL; if (alloc_response) msg->resp = __fsp_allocmsg(); return msg; } void __fsp_freemsg(struct fsp_msg *msg) { free(msg); } void fsp_freemsg(struct fsp_msg *msg) { if (msg && msg->resp) __fsp_freemsg(msg->resp); __fsp_freemsg(msg); } void fsp_cancelmsg(struct fsp_msg *msg) { bool need_unlock = false; struct fsp_cmdclass* cmdclass = fsp_get_cmdclass(msg); struct fsp *fsp = fsp_get_active(); if (fsp->state != fsp_mbx_rr) { prerror("FSP: Message cancel allowed only when" "FSP is in reset\n"); return; } if (!cmdclass) return; /* Recursive locking */ need_unlock = lock_recursive(&fsp_lock); list_del(&msg->link); msg->state = fsp_msg_cancelled; if (need_unlock) unlock(&fsp_lock); } static void fsp_wreg(struct fsp *fsp, u32 reg, u32 val) { struct fsp_iopath *iop; if (fsp->active_iopath < 0) return; iop = &fsp->iopath[fsp->active_iopath]; if (iop->state == fsp_path_bad) return; out_be32(iop->fsp_regs + reg, val); } static u32 fsp_rreg(struct fsp *fsp, u32 reg) { struct fsp_iopath *iop; if (fsp->active_iopath < 0) return 0xffffffff; iop = &fsp->iopath[fsp->active_iopath]; if (iop->state == fsp_path_bad) return 0xffffffff; return in_be32(iop->fsp_regs + reg); } static void fsp_reg_dump(void) { #define FSP_DUMP_ONE(x) \ prlog(PR_DEBUG, " %20s: %x\n", #x, fsp_rreg(fsp, x)); struct fsp *fsp = fsp_get_active(); if (!fsp) return; prlog(PR_DEBUG, "FSP #%d: Register dump (state=%d)\n", fsp->index, fsp->state); FSP_DUMP_ONE(FSP_DRCR_REG); FSP_DUMP_ONE(FSP_DISR_REG); FSP_DUMP_ONE(FSP_MBX1_HCTL_REG); FSP_DUMP_ONE(FSP_MBX1_FCTL_REG); FSP_DUMP_ONE(FSP_MBX2_HCTL_REG); FSP_DUMP_ONE(FSP_MBX2_FCTL_REG); FSP_DUMP_ONE(FSP_SDES_REG); FSP_DUMP_ONE(FSP_HDES_REG); FSP_DUMP_ONE(FSP_HDIR_REG); FSP_DUMP_ONE(FSP_HDIM_SET_REG); FSP_DUMP_ONE(FSP_PDIR_REG); FSP_DUMP_ONE(FSP_PDIM_SET_REG); FSP_DUMP_ONE(FSP_SCRATCH0_REG); FSP_DUMP_ONE(FSP_SCRATCH1_REG); FSP_DUMP_ONE(FSP_SCRATCH2_REG); FSP_DUMP_ONE(FSP_SCRATCH3_REG); } static void fsp_notify_rr_state(u32 state) { struct fsp_client *client, *next; struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(FSP_MCLASS_RR_EVENT); assert(cmdclass); list_for_each_safe(&cmdclass->clientq, client, next, link) client->message(state, NULL); } static void fsp_reset_cmdclass(void) { int i; struct fsp_msg *msg; /* * The FSP is in reset and hence we can't expect any response * to outstanding messages that we've already sent. Clear the * bitmap to reflect that. */ fsp_cmdclass_resp_bitmask = 0; for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) { struct fsp_cmdclass *cmdclass = &fsp_cmdclass[i]; cmdclass->busy = false; cmdclass->timesent = 0; /* Make sure the message queue is empty */ while(!list_empty(&cmdclass->msgq)) { msg = list_pop(&cmdclass->msgq, struct fsp_msg, link); list_add_tail(&cmdclass->rr_queue, &msg->link); } } } static bool fsp_in_hir(struct fsp *fsp) { switch (fsp->state) { case fsp_mbx_crit_op: case fsp_mbx_prep_for_reset: return true; default: return false; } } static bool fsp_in_reset(struct fsp *fsp) { switch (fsp->state) { case fsp_mbx_hir_seq_done: /* FSP reset triggered */ case fsp_mbx_err: /* Will be reset soon */ case fsp_mbx_rr: /* Mbx activity stopped pending reset */ return true; default: return false; } } static bool fsp_hir_state_timeout(void) { u64 now = mftb(); if (tb_compare(now, fsp_hir_timeout) == TB_AAFTERB) return true; return false; } static void fsp_set_hir_timeout(u32 seconds) { u64 now = mftb(); fsp_hir_timeout = now + secs_to_tb(seconds); } static bool fsp_crit_op_in_progress(struct fsp *fsp) { u32 disr = fsp_rreg(fsp, FSP_DISR_REG); if (disr & FSP_DISR_CRIT_OP_IN_PROGRESS) return true; return false; } /* Notify the FSP that it will be reset soon by writing to the DRCR */ static void fsp_prep_for_reset(struct fsp *fsp) { u32 drcr = fsp_rreg(fsp, FSP_DRCR_REG); prlog(PR_TRACE, "FSP: Writing reset to DRCR\n"); drcr_last_print = drcr; fsp_wreg(fsp, FSP_DRCR_REG, (drcr | FSP_PREP_FOR_RESET_CMD)); fsp->state = fsp_mbx_prep_for_reset; fsp_set_hir_timeout(FSP_DRCR_CLEAR_TIMEOUT); } static void fsp_hir_poll(struct fsp *fsp, struct psi *psi) { u32 drcr; switch (fsp->state) { case fsp_mbx_crit_op: if (fsp_crit_op_in_progress(fsp)) { if (fsp_hir_state_timeout()) prerror("FSP: Critical operation timeout\n"); /* XXX What do do next? Check with FSP folks */ } else { fsp_prep_for_reset(fsp); } break; case fsp_mbx_prep_for_reset: drcr = fsp_rreg(fsp, FSP_DRCR_REG); if (drcr != drcr_last_print) { prlog(PR_TRACE, "FSP: DRCR changed, old = %x," " new = %x\n", drcr_last_print, drcr); drcr_last_print = drcr; } if (drcr & FSP_DRCR_ACK_MASK) { if (fsp_hir_state_timeout()) { prerror("FSP: Ack timeout. Triggering reset\n"); psi_reset_fsp(psi); fsp->state = fsp_mbx_hir_seq_done; } } else { prlog(PR_TRACE, "FSP: DRCR ack received." " Triggering reset\n"); psi_reset_fsp(psi); fsp->state = fsp_mbx_hir_seq_done; } break; default: break; } } /* * This is the main entry for the host initiated reset case. * This gets called when: * a. Surveillance ack is not received in 120 seconds * b. A mailbox command doesn't get a response within the stipulated time. */ static void __fsp_trigger_reset(void) { struct fsp *fsp = fsp_get_active(); u32 disr; /* Already in one of the error processing states */ if (fsp_in_hir(fsp) || fsp_in_reset(fsp)) return; prerror("FSP: fsp_trigger_reset() entry\n"); drcr_last_print = 0; /* * Check if we are allowed to reset the FSP. We aren't allowed to * reset the FSP if the FSP_DISR_DBG_IN_PROGRESS is set. */ disr = fsp_rreg(fsp, FSP_DISR_REG); if (disr & FSP_DISR_DBG_IN_PROGRESS) { prerror("FSP: Host initiated reset disabled\n"); return; } /* * Check if some critical operation is in progress as indicated * by FSP_DISR_CRIT_OP_IN_PROGRESS. Timeout is 128 seconds */ if (fsp_crit_op_in_progress(fsp)) { prlog(PR_NOTICE, "FSP: Critical operation in progress\n"); fsp->state = fsp_mbx_crit_op; fsp_set_hir_timeout(FSP_CRITICAL_OP_TIMEOUT); } else fsp_prep_for_reset(fsp); } void fsp_trigger_reset(void) { lock(&fsp_lock); __fsp_trigger_reset(); unlock(&fsp_lock); } /* * Called when we trigger a HIR or when the FSP tells us via the DISR's * RR bit that one is impending. We should therefore stop all mbox activity. */ static void fsp_start_rr(struct fsp *fsp) { struct fsp_iopath *iop; if (fsp->state == fsp_mbx_rr) return; /* We no longer have an active path on that FSP */ if (fsp->active_iopath >= 0) { iop = &fsp->iopath[fsp->active_iopath]; iop->state = fsp_path_bad; fsp->active_iopath = -1; } fsp->state = fsp_mbx_rr; disr_last_print = 0; hstate_last_print = 0; /* * Mark all command classes as non-busy and clear their * timeout, then flush all messages in our staging queue */ fsp_reset_cmdclass(); /* Notify clients. We have to drop the lock here */ unlock(&fsp_lock); fsp_notify_rr_state(FSP_RESET_START); lock(&fsp_lock); /* * Unlike earlier, we don't trigger the PSI link polling * from this point. We wait for the PSI interrupt to tell * us the FSP is really down and then start the polling there. */ } /* * Called on normal/quick shutdown to give up the PSI link */ void fsp_reset_links(void) { struct fsp *fsp = fsp_get_active(); struct fsp_iopath *iop; if (!fsp) return; /* Already in one of the error states? */ if (fsp_in_hir(fsp) || fsp_in_reset(fsp)) return; iop = &fsp->iopath[fsp->active_iopath]; prlog(PR_NOTICE, "FSP #%d: Host initiated shutdown." " Giving up the PSI link\n", fsp->index); psi_disable_link(iop->psi); return; } static void fsp_trace_event(struct fsp *fsp, u32 evt, u32 data0, u32 data1, u32 data2, u32 data3) { union trace tfsp __unused; #ifdef FSP_TRACE_EVENT size_t len = sizeof(struct trace_fsp_event); tfsp.fsp_evt.event = evt; tfsp.fsp_evt.fsp_state = fsp->state; tfsp.fsp_evt.data[0] = data0; tfsp.fsp_evt.data[1] = data1; tfsp.fsp_evt.data[2] = data2; tfsp.fsp_evt.data[3] = data3; trace_add(&tfsp, TRACE_FSP_EVENT, len); #endif /* FSP_TRACE_EVENT */ } static void fsp_handle_errors(struct fsp *fsp) { u32 hstate; struct fsp_iopath *iop; struct psi *psi; u32 disr; if (fsp->active_iopath < 0) { prerror("FSP #%d: fsp_handle_errors() with no active IOP\n", fsp->index); return; } iop = &fsp->iopath[fsp->active_iopath]; if (!iop->psi) { prerror("FSP: Active IOP with no PSI link !\n"); return; } psi = iop->psi; /* * If the link is not up, start R&R immediately, we do call * psi_disable_link() in this case as while the link might * not be up, it might still be enabled and the PSI layer * "active" bit still set */ if (!psi_check_link_active(psi)) { /* Start R&R process */ fsp_trace_event(fsp, TRACE_FSP_EVT_LINK_DOWN, 0, 0, 0, 0); prerror("FSP #%d: Link down, starting R&R\n", fsp->index); fsp_start_rr(fsp); return; } /* Link is up, check for other conditions */ disr = fsp_rreg(fsp, FSP_DISR_REG); /* If in R&R, log values */ if (disr != disr_last_print) { fsp_trace_event(fsp, TRACE_FSP_EVT_DISR_CHG, disr, 0, 0, 0); prlog(PR_TRACE, "FSP #%d: DISR stat change = 0x%08x\n", fsp->index, disr); disr_last_print = disr; } /* On a deferred mbox error, trigger a HIR * Note: We may never get here since the link inactive case is handled * above and the other case is when the iop->psi is NULL, which is * quite rare. */ if (fsp->state == fsp_mbx_err) { prerror("FSP #%d: Triggering HIR on mbx_err\n", fsp->index); fsp_trigger_reset(); return; } /* * If we get here as part of normal flow, the FSP is telling * us that there will be an impending R&R, so we stop all mbox * activity. The actual link down trigger is via a PSI * interrupt that may arrive in due course. */ if (disr & FSP_DISR_FSP_IN_RR) { /* * If we get here with DEBUG_IN_PROGRESS also set, the * FSP is in debug and we should *not* reset it now */ if (disr & FSP_DISR_DBG_IN_PROGRESS) return; /* * When the linux comes back up, we still see that bit * set for a bit, so just move on, nothing to see here */ if (fsp->state == fsp_mbx_rr) return; if (fsp_dpo_pending) { /* * If we are about to process a reset when DPO * is pending, its possible that the host has * gone down, and OPAL is on its way down and * hence will not see the subsequent PSI interrupt. * So, just give up the link here. */ prlog(PR_NOTICE, "FSP #%d: FSP reset with DPO pending." " Giving up PSI link\n", fsp->index); psi_disable_link(psi); } else { prlog(PR_NOTICE, "FSP #%d: FSP in Reset." " Waiting for PSI interrupt\n", fsp->index); } fsp_start_rr(fsp); } /* * However, if any of Unit Check or Runtime Termintated or * Flash Terminated bits is also set, the FSP is asking us * to trigger a HIR so it can try to recover via the DRCR route. */ if (disr & FSP_DISR_HIR_TRIGGER_MASK) { fsp_trace_event(fsp, TRACE_FSP_EVT_SOFT_RR, disr, 0, 0, 0); if (disr & FSP_DISR_FSP_UNIT_CHECK) prlog(PR_DEBUG, "FSP: DISR Unit Check set\n"); else if (disr & FSP_DISR_FSP_RUNTIME_TERM) prlog(PR_DEBUG, "FSP: DISR Runtime Terminate set\n"); else if (disr & FSP_DISR_FSP_FLASH_TERM) prlog(PR_DEBUG, "FSP: DISR Flash Terminate set\n"); prlog(PR_NOTICE, "FSP: Triggering host initiated reset" " sequence\n"); /* Clear all interrupt conditions */ fsp_wreg(fsp, FSP_HDIR_REG, FSP_DBIRQ_ALL); /* Make sure this happened */ fsp_rreg(fsp, FSP_HDIR_REG); fsp_trigger_reset(); return; } /* * We detect an R&R complete indication, acknolwedge it */ if (disr & FSP_DISR_FSP_RR_COMPLETE) { /* * Acking this bit doens't make it go away immediately, so * only do it while still in R&R state */ if (fsp->state == fsp_mbx_rr) { fsp_trace_event(fsp, TRACE_FSP_EVT_RR_COMPL, 0,0,0,0); prlog(PR_NOTICE, "FSP #%d: Detected R&R complete," " acking\n", fsp->index); /* Clear HDATA area */ fsp_wreg(fsp, FSP_MBX1_HDATA_AREA, 0xff); /* Ack it (XDN) and clear HPEND & counts */ fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_PTS | FSP_MBX_CTL_XDN | FSP_MBX_CTL_HPEND | FSP_MBX_CTL_HCSP_MASK | FSP_MBX_CTL_DCSP_MASK); /* * Mark the mbox as usable again so we can process * incoming messages */ fsp->state = fsp_mbx_idle; /* Also clear R&R complete bit in DISR */ fsp_wreg(fsp, FSP_DISR_REG, FSP_DISR_FSP_RR_COMPLETE); psi_enable_fsp_interrupt(psi); } } /* * XXX * * Here we detect a number of errors, should we initiate * and R&R ? */ hstate = fsp_rreg(fsp, FSP_HDES_REG); if (hstate != hstate_last_print) { fsp_trace_event(fsp, TRACE_FSP_EVT_HDES_CHG, hstate, 0, 0, 0); prlog(PR_DEBUG, "FSP #%d: HDES stat change = 0x%08x\n", fsp->index, hstate); hstate_last_print = hstate; } if (hstate == 0xffffffff) return; /* Clear errors */ fsp_wreg(fsp, FSP_HDES_REG, FSP_DBERRSTAT_CLR1); /* * Most of those errors shouldn't have happened, we just clear * the error state and return. In the long run, we might want * to start retrying commands, switching FSPs or links, etc... * * We currently don't set our mailbox to a permanent error state. */ if (hstate & FSP_DBERRSTAT_ILLEGAL1) prerror("FSP #%d: Illegal command error !\n", fsp->index); if (hstate & FSP_DBERRSTAT_WFULL1) prerror("FSP #%d: Write to a full mbox !\n", fsp->index); if (hstate & FSP_DBERRSTAT_REMPTY1) prerror("FSP #%d: Read from an empty mbox !\n", fsp->index); if (hstate & FSP_DBERRSTAT_PAR1) prerror("FSP #%d: Parity error !\n", fsp->index); } /* * This is called by fsp_post_msg() to check if the mbox * is in a state that allows sending of a message * * Due to the various "interesting" contexts fsp_post_msg() * can be called from, including recursive locks from lock * error messages or console code, this should avoid doing * anything more complex than checking a bit of state. * * Specifically, we cannot initiate an R&R and call back into * clients etc... from this function. * * The best we can do is to se the mbox in error state and * handle it later during a poll or interrupts. */ static bool fsp_check_can_send(struct fsp *fsp) { struct fsp_iopath *iop; struct psi *psi; /* Look for FSP in non-idle state */ if (fsp->state != fsp_mbx_idle) return false; /* Look for an active IO path */ if (fsp->active_iopath < 0) goto mbox_error; iop = &fsp->iopath[fsp->active_iopath]; if (!iop->psi) { prerror("FSP: Active IOP with no PSI link !\n"); goto mbox_error; } psi = iop->psi; /* Check if link has gone down. This will be handled later */ if (!psi_check_link_active(psi)) { prerror("FSP #%d: Link seems to be down on send\n", fsp->index); goto mbox_error; } /* XXX Do we want to check for other error conditions ? */ return true; /* * An error of some case occurred, we'll handle it later * from a more normal "poll" context */ mbox_error: fsp->state = fsp_mbx_err; return false; } static bool fsp_post_msg(struct fsp *fsp, struct fsp_msg *msg) { u32 ctl, reg; int i, wlen; prlog(PR_INSANE, "FSP #%d: fsp_post_msg (w0: 0x%08x w1: 0x%08x)\n", fsp->index, msg->word0, msg->word1); /* Note: We used to read HCTL here and only modify some of * the bits in it. This was bogus, because we would write back * the incoming bits as '1' and clear them, causing fsp_poll() * to then miss them. Let's just start with 0, which is how * I suppose the HW intends us to do. */ /* Set ourselves as busy */ fsp->pending = msg; fsp->state = fsp_mbx_send; msg->state = fsp_msg_sent; /* We trace after setting the mailbox state so that if the * tracing recurses, it ends up just queuing the message up */ fsp_trace_msg(msg, TRACE_FSP_MSG_OUT); /* Build the message in the mailbox */ reg = FSP_MBX1_HDATA_AREA; fsp_wreg(fsp, reg, msg->word0); reg += 4; fsp_wreg(fsp, reg, msg->word1); reg += 4; wlen = (msg->dlen + 3) >> 2; for (i = 0; i < wlen; i++) { fsp_wreg(fsp, reg, msg->data.words[i]); reg += 4; } /* Write the header */ fsp_wreg(fsp, FSP_MBX1_HHDR0_REG, (msg->dlen + 8) << 16); /* Write the control register */ ctl = 4 << FSP_MBX_CTL_HCHOST_SHIFT; ctl |= (msg->dlen + 8) << FSP_MBX_CTL_DCHOST_SHIFT; ctl |= FSP_MBX_CTL_PTS | FSP_MBX_CTL_SPPEND; prlog(PR_INSANE, " new ctl: %08x\n", ctl); fsp_wreg(fsp, FSP_MBX1_HCTL_REG, ctl); return true; } static void fsp_poke_queue(struct fsp_cmdclass *cmdclass) { struct fsp *fsp = fsp_get_active(); struct fsp_msg *msg; if (!fsp) return; if (!fsp_check_can_send(fsp)) return; /* From here to the point where fsp_post_msg() sets fsp->state * to !idle we must not cause any re-entrancy (no debug or trace) * in a code path that may hit fsp_post_msg() (it's ok to do so * if we are going to bail out), as we are committed to calling * fsp_post_msg() and so a re-entrancy could cause us to do a * double-send into the mailbox. */ if (cmdclass->busy || list_empty(&cmdclass->msgq)) return; msg = list_top(&cmdclass->msgq, struct fsp_msg, link); assert(msg); cmdclass->busy = true; if (!fsp_post_msg(fsp, msg)) { prerror("FSP #%d: Failed to send message\n", fsp->index); cmdclass->busy = false; return; } } static void __fsp_fillmsg(struct fsp_msg *msg, u32 cmd_sub_mod, u8 add_words, va_list list) { bool response = !!(cmd_sub_mod & 0x1000000); u8 cmd = (cmd_sub_mod >> 16) & 0xff; u8 sub = (cmd_sub_mod >> 8) & 0xff; u8 mod = cmd_sub_mod & 0xff; int i; msg->word0 = cmd & 0xff; msg->word1 = mod << 8 | sub; msg->response = response; msg->dlen = add_words << 2; for (i = 0; i < add_words; i++) msg->data.words[i] = va_arg(list, unsigned int); va_end(list); } void fsp_fillmsg(struct fsp_msg *msg, u32 cmd_sub_mod, u8 add_words, ...) { va_list list; va_start(list, add_words); __fsp_fillmsg(msg, cmd_sub_mod, add_words, list); va_end(list); } struct fsp_msg *fsp_mkmsg(u32 cmd_sub_mod, u8 add_words, ...) { struct fsp_msg *msg = fsp_allocmsg(!!(cmd_sub_mod & 0x1000000)); va_list list; if (!msg) { prerror("FSP: Failed to allocate struct fsp_msg\n"); return NULL; } va_start(list, add_words); __fsp_fillmsg(msg, cmd_sub_mod, add_words, list); va_end(list); return msg; } /* * IMPORTANT NOTE: This is *guaranteed* to not call the completion * routine recusrively for *any* fsp message, either the * queued one or a previous one. Thus it is *ok* to call * this function with a lock held which will itself be * taken by the completion function. * * Any change to this implementation must respect this * rule. This will be especially true of things like * reset/reload and error handling, if we fail to queue * we must just return an error, not call any completion * from the scope of fsp_queue_msg(). */ int fsp_queue_msg(struct fsp_msg *msg, void (*comp)(struct fsp_msg *msg)) { struct fsp_cmdclass *cmdclass; struct fsp *fsp = fsp_get_active(); bool need_unlock; u16 seq; int rc = 0; if (!fsp || !msg) return -1; /* Recursive locking */ need_unlock = lock_recursive(&fsp_lock); /* Grab a new sequence number */ seq = fsp_curseq; fsp_curseq = fsp_curseq + 1; if (fsp_curseq == 0) fsp_curseq = 0x8000; msg->word0 = (msg->word0 & 0xffff) | seq << 16; /* Set completion */ msg->complete = comp; /* Clear response state */ if (msg->resp) msg->resp->state = fsp_msg_unused; /* Queue the message in the appropriate queue */ cmdclass = fsp_get_cmdclass(msg); if (!cmdclass) { prerror("FSP: Invalid msg in fsp_queue_msg w0/1=0x%08x/%08x\n", msg->word0, msg->word1); rc = -1; goto unlock; } msg->state = fsp_msg_queued; /* * If we have initiated or about to initiate a reset/reload operation, * we stash the message on the R&R backup queue. Otherwise, queue it * normally and poke the HW */ if (fsp_in_hir(fsp) || fsp_in_reset(fsp)) list_add_tail(&cmdclass->rr_queue, &msg->link); else { list_add_tail(&cmdclass->msgq, &msg->link); fsp_poke_queue(cmdclass); } unlock: if (need_unlock) unlock(&fsp_lock); return rc; } /* WARNING: This will drop the FSP lock !!! */ static void fsp_complete_msg(struct fsp_msg *msg) { struct fsp_cmdclass *cmdclass = fsp_get_cmdclass(msg); void (*comp)(struct fsp_msg *msg); assert(cmdclass); prlog(PR_INSANE, " completing msg, word0: 0x%08x\n", msg->word0); comp = msg->complete; list_del_from(&cmdclass->msgq, &msg->link); cmdclass->busy = false; msg->state = fsp_msg_done; unlock(&fsp_lock); if (comp) (*comp)(msg); lock(&fsp_lock); } /* WARNING: This will drop the FSP lock !!! */ static void fsp_complete_send(struct fsp *fsp) { struct fsp_msg *msg = fsp->pending; struct fsp_cmdclass *cmdclass = fsp_get_cmdclass(msg); assert(msg); assert(cmdclass); fsp->pending = NULL; prlog(PR_INSANE, " completing send, word0: 0x%08x, resp: %d\n", msg->word0, msg->response); if (msg->response) { u64 setbit = fsp_get_class_bit(msg->word0 & 0xff); msg->state = fsp_msg_wresp; fsp_cmdclass_resp_bitmask |= setbit; cmdclass->timesent = mftb(); } else fsp_complete_msg(msg); } static void fsp_alloc_inbound(struct fsp_msg *msg) { u16 func_id = msg->data.words[0] & 0xffff; u32 len = msg->data.words[1]; u32 tce_token = 0, act_len = 0; u8 rc = 0; void *buf; struct fsp_msg *resp; prlog(PR_DEBUG, "FSP: Allocate inbound buffer func: %04x len: %d\n", func_id, len); lock(&fsp_lock); if ((fsp_inbound_off + len) > FSP_INBOUND_SIZE) { prerror("FSP: Out of space in buffer area !\n"); rc = 0xeb; goto reply; } if (!fsp_inbound_buf) { fsp_inbound_buf = memalign(TCE_PSIZE, FSP_INBOUND_SIZE); if (!fsp_inbound_buf) { prerror("FSP: could not allocate fsp_inbound_buf!\n"); rc = 0xeb; goto reply; } } buf = fsp_inbound_buf + fsp_inbound_off; tce_token = PSI_DMA_INBOUND_BUF + fsp_inbound_off; len = (len + TCE_MASK) & ~TCE_MASK; fsp_inbound_off += len; fsp_tce_map(tce_token, buf, len); prlog(PR_DEBUG, "FSP: -> buffer at 0x%p, TCE: 0x%08x, alen: 0x%x\n", buf, tce_token, len); act_len = len; reply: unlock(&fsp_lock); resp = fsp_mkmsg(FSP_RSP_ALLOC_INBOUND | rc, 3, 0, tce_token, act_len); if (!resp) { prerror("FSP: response message allocation failed\n"); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSP: Failed to queue response message\n"); return; } } void *fsp_inbound_buf_from_tce(u32 tce_token) { u32 offset = tce_token - PSI_DMA_INBOUND_BUF; if (tce_token < PSI_DMA_INBOUND_BUF || offset >= fsp_inbound_off) { prerror("FSP: TCE token 0x%x out of bounds\n", tce_token); return NULL; } return fsp_inbound_buf + offset; } static void fsp_repost_queued_msgs_post_rr(void) { struct fsp_msg *msg; int i; for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) { struct fsp_cmdclass *cmdclass = &fsp_cmdclass[i]; bool poke = false; while(!list_empty(&cmdclass->rr_queue)) { msg = list_pop(&cmdclass->rr_queue, struct fsp_msg, link); list_add_tail(&cmdclass->msgq, &msg->link); poke = true; } if (poke) fsp_poke_queue(cmdclass); } } static bool fsp_local_command(u32 cmd_sub_mod, struct fsp_msg *msg) { u32 cmd = 0; u32 rsp_data = 0; struct fsp_msg *resp; switch(cmd_sub_mod) { case FSP_CMD_CONTINUE_IPL: /* We get a CONTINUE_IPL as a response to OPL */ prlog(PR_NOTICE, "FSP: Got CONTINUE_IPL !\n"); ipl_state |= ipl_got_continue; return true; case FSP_CMD_HV_STATE_CHG: prlog(PR_NOTICE, "FSP: Got HV state change request to %d\n", msg->data.bytes[0]); /* Send response synchronously for now, we might want to * deal with that sort of stuff asynchronously if/when * we add support for auto-freeing of messages */ resp = fsp_mkmsg(FSP_RSP_HV_STATE_CHG, 0); if (!resp) prerror("FSP: Failed to allocate HV state response\n"); else { if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSP: Failed to queue HV state resp\n"); } } return true; case FSP_CMD_SP_NEW_ROLE: /* FSP is assuming a new role */ prlog(PR_INFO, "FSP: FSP assuming new role\n"); resp = fsp_mkmsg(FSP_RSP_SP_NEW_ROLE, 0); if (!resp) prerror("FSP: Failed to allocate SP role response\n"); else { if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSP: Failed to queue SP role resp\n"); } } ipl_state |= ipl_got_new_role; return true; case FSP_CMD_SP_QUERY_CAPS: prlog(PR_INFO, "FSP: FSP query capabilities\n"); /* XXX Do something saner. For now do a synchronous * response and hard code our capabilities */ resp = fsp_mkmsg(FSP_RSP_SP_QUERY_CAPS, 4, 0x3ff80000, 0, 0, 0); if (!resp) prerror("FSP: Failed to allocate CAPS response\n"); else { if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSP: Failed to queue CAPS resp\n"); } } ipl_state |= ipl_got_caps; return true; case FSP_CMD_FSP_FUNCTNAL: prlog(PR_INFO, "FSP: Got FSP Functional\n"); ipl_state |= ipl_got_fsp_functional; return true; case FSP_CMD_ALLOC_INBOUND: fsp_alloc_inbound(msg); return true; case FSP_CMD_SP_RELOAD_COMP: prlog(PR_INFO, "FSP: SP says Reset/Reload complete\n"); if (msg->data.bytes[3] & PPC_BIT8(0)) { fsp_fips_dump_notify(msg->data.words[1], msg->data.words[2]); if (msg->data.bytes[3] & PPC_BIT8(1)) prlog(PR_DEBUG, " PLID is %x\n", msg->data.words[3]); } if (msg->data.bytes[3] & PPC_BIT8(2)) { prlog(PR_DEBUG, " A Reset/Reload was NOT done\n"); } else { /* Notify clients that the FSP is back up */ fsp_notify_rr_state(FSP_RELOAD_COMPLETE); fsp_repost_queued_msgs_post_rr(); } return true; case FSP_CMD_CLOSE_HMC_INTF: /* Close the HMC interface */ /* Though Sapphire does not support a HMC connection, the FSP * sends this message when it is trying to open any new * hypervisor session. So returning an error 0x51. */ cmd = FSP_RSP_CLOSE_HMC_INTF | FSP_STAUS_INVALID_HMC_ID; rsp_data = msg->data.bytes[0] << 24 | msg->data.bytes[1] << 16; rsp_data &= 0xffff0000; resp = fsp_mkmsg(cmd, 1, rsp_data); if (!resp) prerror("FSP: Failed to allocate HMC close response\n"); else { if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSP: Failed to queue HMC close resp\n"); } } return true; } return false; } /* This is called without the FSP lock */ static void fsp_handle_command(struct fsp_msg *msg) { struct fsp_cmdclass *cmdclass = fsp_get_cmdclass(msg); struct fsp_client *client, *next; struct fsp_msg *resp; u32 cmd_sub_mod; if (!cmdclass) { prerror("FSP: Got message for unknown class %x\n", msg->word0 & 0xff); goto free; } cmd_sub_mod = (msg->word0 & 0xff) << 16; cmd_sub_mod |= (msg->word1 & 0xff) << 8; cmd_sub_mod |= (msg->word1 >> 8) & 0xff; /* Some commands are handled locally */ if (fsp_local_command(cmd_sub_mod, msg)) goto free; /* The rest go to clients */ list_for_each_safe(&cmdclass->clientq, client, next, link) { if (client->message(cmd_sub_mod, msg)) goto free; } prerror("FSP: Unhandled message %06x\n", cmd_sub_mod); /* We don't know whether the message expected some kind of * response, so we send one anyway */ resp = fsp_mkmsg((cmd_sub_mod & 0xffff00) | 0x008020, 0); if (!resp) prerror("FSP: Failed to allocate default response\n"); else { if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("FSP: Failed to queue default response\n"); } } free: fsp_freemsg(msg); } static void __fsp_fill_incoming(struct fsp *fsp, struct fsp_msg *msg, int dlen, u32 w0, u32 w1) { unsigned int wlen, i, reg; msg->dlen = dlen - 8; msg->word0 = w0; msg->word1 = w1; wlen = (dlen + 3) >> 2; reg = FSP_MBX1_FDATA_AREA + 8; for (i = 0; i < wlen; i++) { msg->data.words[i] = fsp_rreg(fsp, reg); reg += 4; } /* Ack it (XDN) and clear HPEND & counts */ fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_PTS | FSP_MBX_CTL_XDN | FSP_MBX_CTL_HPEND | FSP_MBX_CTL_HCSP_MASK | FSP_MBX_CTL_DCSP_MASK); fsp_trace_msg(msg, TRACE_FSP_MSG_IN); } static void __fsp_drop_incoming(struct fsp *fsp) { /* Ack it (XDN) and clear HPEND & counts */ fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_PTS | FSP_MBX_CTL_XDN | FSP_MBX_CTL_HPEND | FSP_MBX_CTL_HCSP_MASK | FSP_MBX_CTL_DCSP_MASK); } /* WARNING: This will drop the FSP lock */ static void fsp_handle_incoming(struct fsp *fsp) { struct fsp_msg *msg; u32 h0, w0, w1; unsigned int dlen; bool special_response = false; h0 = fsp_rreg(fsp, FSP_MBX1_FHDR0_REG); dlen = (h0 >> 16) & 0xff; w0 = fsp_rreg(fsp, FSP_MBX1_FDATA_AREA); w1 = fsp_rreg(fsp, FSP_MBX1_FDATA_AREA + 4); prlog(PR_INSANE, " Incoming: w0: 0x%08x, w1: 0x%08x, dlen: %d\n", w0, w1, dlen); /* Some responses are expected out of band */ if ((w0 & 0xff) == FSP_MCLASS_HMC_INTFMSG && ((w1 & 0xff) == 0x8a || ((w1 & 0xff) == 0x8b))) special_response = true; /* Check for response bit */ if (w1 & 0x80 && !special_response) { struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(w0 & 0xff); struct fsp_msg *req; if (!cmdclass) { prerror("FSP: Got response for unknown class %x\n", w0 & 0xff); __fsp_drop_incoming(fsp); return; } if (!cmdclass->busy || list_empty(&cmdclass->msgq)) { prerror("FSP #%d: Got orphan response! w0 = 0x%08x w1 = 0x%08x\n", fsp->index, w0, w1); __fsp_drop_incoming(fsp); return; } req = list_top(&cmdclass->msgq, struct fsp_msg, link); /* Check if the response seems to match the message */ if (req->state != fsp_msg_wresp || (req->word0 & 0xff) != (w0 & 0xff) || (req->word1 & 0xff) != (w1 & 0x7f)) { __fsp_drop_incoming(fsp); prerror("FSP #%d: Response doesn't match pending msg. w0 = 0x%08x w1 = 0x%08x\n", fsp->index, w0, w1); return; } else { u64 resetbit = ~fsp_get_class_bit(req->word0 & 0xff); fsp_cmdclass_resp_bitmask &= resetbit; cmdclass->timesent = 0; } /* Allocate response if needed XXX We need to complete * the original message with some kind of error here ? */ if (!req->resp) { req->resp = __fsp_allocmsg(); if (!req->resp) { __fsp_drop_incoming(fsp); prerror("FSP #%d: Failed to allocate response\n", fsp->index); return; } } /* Populate and complete (will drop the lock) */ req->resp->state = fsp_msg_response; __fsp_fill_incoming(fsp, req->resp, dlen, w0, w1); fsp_complete_msg(req); return; } /* Allocate an incoming message */ msg = __fsp_allocmsg(); if (!msg) { __fsp_drop_incoming(fsp); prerror("FSP #%d: Failed to allocate incoming msg\n", fsp->index); return; } msg->state = fsp_msg_incoming; __fsp_fill_incoming(fsp, msg, dlen, w0, w1); /* Handle FSP commands. This can recurse into fsp_queue_msg etc.. */ unlock(&fsp_lock); fsp_handle_command(msg); lock(&fsp_lock); } static void fsp_check_queues(struct fsp *fsp) { int i; /* XXX In the long run, we might want to have a queue of * classes waiting to be serviced to speed this up, either * that or a bitmap. */ for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) { struct fsp_cmdclass *cmdclass = &fsp_cmdclass[i]; if (fsp->state != fsp_mbx_idle) break; if (cmdclass->busy || list_empty(&cmdclass->msgq)) continue; fsp_poke_queue(cmdclass); } } static void __fsp_poll(bool interrupt) { struct fsp_iopath *iop; struct fsp *fsp = fsp_get_active(); u32 ctl, hdir = 0; bool psi_irq; /* * The tracer isn't terribly efficient at detecting dups * especially when coming from multiple CPUs so we do our * own change-detection locally */ static u32 hdir_last_trace; static u32 ctl_last_trace; static bool psi_irq_last_trace; static bool irq_last_trace; if (!fsp) return; /* Crazy interrupt handling scheme: * * In order to avoid "losing" interrupts when polling the mbox * we only clear interrupt conditions when called as a result of * an interrupt. * * That way, if a poll clears, for example, the HPEND condition, * the interrupt remains, causing a dummy interrupt later on * thus allowing the OS to be notified of a state change (ie it * doesn't need every poll site to monitor every state change). * * However, this scheme is complicated by the fact that we need * to clear the interrupt condition after we have cleared the * original condition in HCTL, and we might have long stale * interrupts which we do need to eventually get rid of. However * clearing interrupts in such a way is racy, so we need to loop * and re-poll HCTL after having done so or we might miss an * event. It's a latency risk, but unlikely and probably worth it. */ again: if (fsp->active_iopath < 0) { /* That should never happen */ if (interrupt && (fsp->state != fsp_mbx_rr)) prerror("FSP: Interrupt with no working IO path\n"); return; } iop = &fsp->iopath[fsp->active_iopath]; /* Handle host initiated resets */ if (fsp_in_hir(fsp)) { fsp_hir_poll(fsp, iop->psi); return; } /* Check for error state and handle R&R completion */ fsp_handle_errors(fsp); /* * The above might have triggered and R&R, check that we * are still functional */ if ((fsp->active_iopath < 0) || fsp_in_hir(fsp)) return; iop = &fsp->iopath[fsp->active_iopath]; /* Read interrupt status (we may or may not use it) */ hdir = fsp_rreg(fsp, FSP_HDIR_REG); /* Read control now as well so we can trace them */ ctl = fsp_rreg(fsp, FSP_MBX1_HCTL_REG); /* Ditto with PSI irq state */ psi_irq = psi_poll_fsp_interrupt(iop->psi); /* Trace it if anything changes */ if (hdir != hdir_last_trace || ctl != ctl_last_trace || interrupt != irq_last_trace || psi_irq != psi_irq_last_trace) { fsp_trace_event(fsp, TRACE_FSP_EVT_POLL_IRQ, interrupt, hdir, ctl, psi_irq); hdir_last_trace = hdir; ctl_last_trace = ctl; irq_last_trace = interrupt; psi_irq_last_trace = psi_irq; } /* * We *MUST* ignore the MBOX2 bits here. While MBOX2 cannot generate * interrupt, it might still latch some bits here (and we found cases * where the MBOX2 XUP would be set). If that happens, clearing HDIR * never works (the bit gets set again immediately) because we don't * clear the condition in HTCL2 and thus we loop forever. */ hdir &= FSP_DBIRQ_MBOX1; /* * Sanity check: If an interrupt is pending and we are in polling * mode, check that the PSI side is also pending. If some bit is * set, just clear and move on. */ if (hdir && !interrupt && !psi_irq) { prerror("FSP: WARNING ! HDIR 0x%08x but no PSI irq !\n", hdir); fsp_wreg(fsp, FSP_HDIR_REG, hdir); } /* * We should never have the mbox in error state here unless it * was fine until some printf inside fsp_handle_errors() caused * the console to poke the FSP which detected a branch new error * in the process. Let's be safe rather than sorry and handle that * here */ if (fsp_in_hir(fsp) || fsp->state == fsp_mbx_err) { prerror("FSP: Late error state detection\n"); goto again; } /* * If we are in an R&R state with an active IO path, we * shouldn't be getting interrupts. If we do, just clear * the condition and print a message */ if (fsp->state == fsp_mbx_rr) { if (interrupt) { prerror("FSP: Interrupt in RR state [HDIR=0x%08x]\n", hdir); fsp_wreg(fsp, FSP_HDIR_REG, hdir); } return; } /* Poll FSP CTL */ if (ctl & (FSP_MBX_CTL_XUP | FSP_MBX_CTL_HPEND)) prlog(PR_INSANE, "FSP #%d: poll, ctl: %x\n", fsp->index, ctl); /* Do we have a pending message waiting to complete ? */ if (ctl & FSP_MBX_CTL_XUP) { fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_XUP); if (fsp->state == fsp_mbx_send) { /* mbox is free */ fsp->state = fsp_mbx_idle; /* Complete message (will break the lock) */ fsp_complete_send(fsp); /* Lock can have been broken, so ctl is now * potentially invalid, let's recheck */ goto again; } else { prerror("FSP #%d: Got XUP with no pending message !\n", fsp->index); } } if (fsp->state == fsp_mbx_send) { /* XXX Handle send timeouts!!! */ } /* Is there an incoming message ? This will break the lock as well */ if (ctl & FSP_MBX_CTL_HPEND) fsp_handle_incoming(fsp); /* Note: Lock may have been broken above, thus ctl might be invalid * now, don't use it any further. */ /* Check for something else to send */ if (fsp->state == fsp_mbx_idle) fsp_check_queues(fsp); /* Clear interrupts, and recheck HCTL if any occurred */ if (interrupt && hdir) { fsp_wreg(fsp, FSP_HDIR_REG, hdir); goto again; } } void fsp_interrupt(void) { lock(&fsp_lock); __fsp_poll(true); unlock(&fsp_lock); } int fsp_sync_msg(struct fsp_msg *msg, bool autofree) { int rc; rc = fsp_queue_msg(msg, NULL); if (rc) goto bail; while(fsp_msg_busy(msg)) { cpu_relax(); opal_run_pollers(); } switch(msg->state) { case fsp_msg_done: rc = 0; break; case fsp_msg_timeout: rc = -1; /* XXX to improve */ break; default: rc = -1; /* Should not happen... (assert ?) */ } if (msg->resp) rc = (msg->resp->word1 >> 8) & 0xff; bail: if (autofree) fsp_freemsg(msg); return rc; } void fsp_register_client(struct fsp_client *client, u8 msgclass) { struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(msgclass); if (!fsp_present()) return; assert(cmdclass); list_add_tail(&cmdclass->clientq, &client->link); } void fsp_unregister_client(struct fsp_client *client, u8 msgclass) { struct fsp_cmdclass *cmdclass = __fsp_get_cmdclass(msgclass); if (!fsp_present()) return; assert(cmdclass); list_del_from(&cmdclass->clientq, &client->link); } static int fsp_init_mbox(struct fsp *fsp) { unsigned int i; u32 reg; /* * Note: The documentation contradicts itself as to * whether the HDIM bits should be set or cleared to * enable interrupts * * This seems to work... */ /* Mask all interrupts */ fsp_wreg(fsp, FSP_HDIM_CLR_REG, FSP_DBIRQ_ALL); /* Clear all errors */ fsp_wreg(fsp, FSP_HDES_REG, FSP_DBERRSTAT_CLR1 | FSP_DBERRSTAT_CLR2); /* Initialize data area as the doco says */ for (i = 0; i < 0x40; i += 4) fsp_wreg(fsp, FSP_MBX1_HDATA_AREA + i, 0); /* * Clear whatever crap may remain in HDCR. Do not write XDN as that * would be interpreted incorrectly as an R&R completion which * we aren't ready to send yet ! */ fsp_wreg(fsp, FSP_MBX1_HCTL_REG, FSP_MBX_CTL_XUP | FSP_MBX_CTL_HPEND | FSP_MBX_CTL_HCSP_MASK | FSP_MBX_CTL_DCSP_MASK | FSP_MBX_CTL_PTS); /* Clear all pending interrupts */ fsp_wreg(fsp, FSP_HDIR_REG, FSP_DBIRQ_ALL); /* Enable all mbox1 interrupts */ fsp_wreg(fsp, FSP_HDIM_SET_REG, FSP_DBIRQ_MBOX1); /* Decode what FSP we are connected to */ reg = fsp_rreg(fsp, FSP_SCRATCH0_REG); if (reg & PPC_BIT32(0)) { /* Is it a valid connection */ if (reg & PPC_BIT32(3)) prlog(PR_INFO, "FSP: Connected to FSP-B\n"); else prlog(PR_INFO, "FSP: Connected to FSP-A\n"); } return 0; } /* We use a single fixed TCE table for all PSI interfaces */ static void fsp_init_tce_table(void) { fsp_tce_table = (u64 *)PSI_TCE_TABLE_BASE; /* Memset the larger table even if we only use the smaller * one on P7 */ memset(fsp_tce_table, 0, PSI_TCE_TABLE_SIZE_P8); } void fsp_tce_map(u32 offset, void *addr, u32 size) { u64 raddr = (u64)addr; assert(!(offset & TCE_MASK)); assert(!(raddr & TCE_MASK)); assert(!(size & TCE_MASK)); size >>= TCE_SHIFT; offset >>= TCE_SHIFT; while(size--) { fsp_tce_table[offset++] = raddr | 0x3; raddr += TCE_PSIZE; } } void fsp_tce_unmap(u32 offset, u32 size) { assert(!(offset & TCE_MASK)); assert(!(size & TCE_MASK)); size >>= TCE_SHIFT; offset >>= TCE_SHIFT; while(size--) fsp_tce_table[offset++] = 0; } static struct fsp *fsp_find_by_index(int index) { struct fsp *fsp = first_fsp; do { if (fsp->index == index) return fsp; } while (fsp->link != first_fsp); return NULL; } static void fsp_init_links(struct dt_node *fsp_node) { const struct dt_property *linksprop; int i, index; struct fsp *fsp; struct fsp_iopath *fiop; linksprop = dt_find_property(fsp_node, "ibm,psi-links"); assert(linksprop); index = dt_prop_get_u32(fsp_node, "reg"); fsp = fsp_find_by_index(index); if (!fsp) { prerror("FSP: FSP with index %d not found\n", index); return; } fsp->state = fsp_mbx_idle; /* Iterate all links */ for (i = 0; i < fsp->iopath_count; i++) { u64 reg; u32 link; link = ((const u32 *)linksprop->prop)[i]; fiop = &fsp->iopath[i]; fiop->psi = psi_find_link(link); if (fiop->psi == NULL) { prerror("FSP #%d: Couldn't find PSI link\n", fsp->index); continue; } prlog(PR_DEBUG, "FSP #%d: Found PSI HB link to chip %d\n", fsp->index, link); psi_fsp_link_in_use(fiop->psi); /* Get the FSP register window */ reg = in_be64(fiop->psi->regs + PSIHB_FSPBAR); fiop->fsp_regs = (void *)(reg | (1ULL << 63) | dt_prop_get_u32(fsp_node, "reg-offset")); } } static void fsp_update_links_states(struct fsp *fsp) { struct fsp_iopath *fiop; unsigned int i; /* Iterate all links */ for (i = 0; i < fsp->iopath_count; i++) { fiop = &fsp->iopath[i]; if (!fiop->psi) continue; if (!fiop->psi->working) fiop->state = fsp_path_bad; else if (fiop->psi->active) { fsp->active_iopath = i; fiop->state = fsp_path_active; } else fiop->state = fsp_path_backup; } if (fsp->active_iopath >= 0) { if (!active_fsp || (active_fsp != fsp)) active_fsp = fsp; fsp_inbound_off = 0; fiop = &fsp->iopath[fsp->active_iopath]; psi_init_for_fsp(fiop->psi); fsp_init_mbox(fsp); } } void fsp_reinit_fsp(void) { struct fsp *fsp; /* Notify all FSPs to check for an updated link state */ for (fsp = first_fsp; fsp; fsp = fsp->link) fsp_update_links_states(fsp); } static void fsp_create_fsp(struct dt_node *fsp_node) { const struct dt_property *linksprop; struct fsp *fsp; int count, index; index = dt_prop_get_u32(fsp_node, "reg"); prlog(PR_INFO, "FSP #%d: Found in device-tree, setting up...\n", index); linksprop = dt_find_property(fsp_node, "ibm,psi-links"); if (!linksprop || linksprop->len < 4) { prerror("FSP #%d: No links !\n", index); return; } fsp = zalloc(sizeof(struct fsp)); if (!fsp) { prerror("FSP #%d: Can't allocate memory !\n", index); return; } fsp->index = index; fsp->active_iopath = -1; count = linksprop->len / 4; prlog(PR_DEBUG, "FSP #%d: Found %d IO PATH\n", index, count); if (count > FSP_MAX_IOPATH) { prerror("FSP #%d: WARNING, limited to %d IO PATH\n", index, FSP_MAX_IOPATH); count = FSP_MAX_IOPATH; } fsp->iopath_count = count; fsp->link = first_fsp; first_fsp = fsp; fsp_init_links(fsp_node); fsp_update_links_states(fsp); if (fsp->active_iopath >= 0) psi_enable_fsp_interrupt(fsp->iopath[fsp->active_iopath].psi); } static void fsp_opal_poll(void *data __unused) { if (try_lock(&fsp_lock)) { __fsp_poll(false); unlock(&fsp_lock); } } static bool fsp_init_one(const char *compat) { struct dt_node *fsp_node; bool inited = false; dt_for_each_compatible(dt_root, fsp_node, compat) { if (!inited) { int i; /* Initialize the per-class msg queues */ for (i = 0; i <= (FSP_MCLASS_LAST - FSP_MCLASS_FIRST); i++) { list_head_init(&fsp_cmdclass[i].msgq); list_head_init(&fsp_cmdclass[i].clientq); list_head_init(&fsp_cmdclass[i].rr_queue); } /* Init the queues for RR notifier cmdclass */ list_head_init(&fsp_cmdclass_rr.msgq); list_head_init(&fsp_cmdclass_rr.clientq); list_head_init(&fsp_cmdclass_rr.rr_queue); /* Register poller */ opal_add_poller(fsp_opal_poll, NULL); inited = true; } /* Create the FSP data structure */ fsp_create_fsp(fsp_node); } return inited; } void fsp_init(void) { prlog(PR_DEBUG, "FSP: Looking for FSP...\n"); fsp_init_tce_table(); if (!fsp_init_one("ibm,fsp1") && !fsp_init_one("ibm,fsp2")) { prlog(PR_DEBUG, "FSP: No FSP on this machine\n"); return; } } bool fsp_present(void) { return first_fsp != NULL; } static void fsp_timeout_poll(void *data __unused) { u64 now = mftb(); u64 timeout_val = 0; u64 cmdclass_resp_bitmask = fsp_cmdclass_resp_bitmask; struct fsp_cmdclass *cmdclass = NULL; struct fsp_msg *req = NULL; u32 index = 0; if (timeout_timer == 0) timeout_timer = now + secs_to_tb(30); /* The lowest granularity for a message timeout is 30 secs. * So every 30secs, check if there is any message * waiting for a response from the FSP */ if (tb_compare(now, timeout_timer) == TB_ABEFOREB) return; if (!try_lock(&fsp_poll_lock)) return; if (tb_compare(now, timeout_timer) == TB_ABEFOREB) { unlock(&fsp_poll_lock); return; } while (cmdclass_resp_bitmask) { u64 time_sent = 0; u64 time_to_comp = 0; if (!(cmdclass_resp_bitmask & 0x1)) goto next_bit; cmdclass = &fsp_cmdclass[index]; timeout_val = secs_to_tb((cmdclass->timeout) * 60); time_sent = cmdclass->timesent; time_to_comp = now - cmdclass->timesent; /* Now check if the response has timed out */ if (tb_compare(time_to_comp, timeout_val) == TB_AAFTERB) { u32 w0, w1; enum fsp_msg_state mstate; /* Take the FSP lock now and re-check */ lock(&fsp_lock); if (!(fsp_cmdclass_resp_bitmask & (1ull << index)) || time_sent != cmdclass->timesent) { unlock(&fsp_lock); goto next_bit; } req = list_top(&cmdclass->msgq, struct fsp_msg, link); if (!req) { printf("FSP: Timeout state mismatch on class %d\n", index); fsp_cmdclass_resp_bitmask &= ~(1ull << index); cmdclass->timesent = 0; unlock(&fsp_lock); goto next_bit; } w0 = req->word0; w1 = req->word1; mstate = req->state; prlog(PR_WARNING, "FSP: Response from FSP timed out," " word0 = %x, word1 = %x state: %d\n", w0, w1, mstate); fsp_reg_dump(); fsp_cmdclass_resp_bitmask &= ~(1ull << index); cmdclass->timesent = 0; if (req->resp) req->resp->state = fsp_msg_timeout; fsp_complete_msg(req); __fsp_trigger_reset(); unlock(&fsp_lock); log_simple_error(&e_info(OPAL_RC_FSP_POLL_TIMEOUT), "FSP: Response from FSP timed out, word0 = %x," "word1 = %x state: %d\n", w0, w1, mstate); } next_bit: cmdclass_resp_bitmask = cmdclass_resp_bitmask >> 1; index++; } unlock(&fsp_poll_lock); } void fsp_opl(void) { struct dt_node *iplp; if (!fsp_present()) return; /* Send OPL */ ipl_state |= ipl_opl_sent; fsp_sync_msg(fsp_mkmsg(FSP_CMD_OPL, 0), true); while(!(ipl_state & ipl_got_continue)) { opal_run_pollers(); cpu_relax(); } /* Send continue ACK */ fsp_sync_msg(fsp_mkmsg(FSP_CMD_CONTINUE_ACK, 0), true); /* Wait for various FSP messages */ prlog(PR_INFO, "INIT: Waiting for FSP to advertize new role...\n"); while(!(ipl_state & ipl_got_new_role)) { cpu_relax(); opal_run_pollers(); } prlog(PR_INFO, "INIT: Waiting for FSP to request capabilities...\n"); while(!(ipl_state & ipl_got_caps)) { cpu_relax(); opal_run_pollers(); } /* Initiate the timeout poller */ opal_add_poller(fsp_timeout_poll, NULL); /* Tell FSP we are in standby */ prlog(PR_INFO, "INIT: Sending HV Functional: Standby...\n"); fsp_sync_msg(fsp_mkmsg(FSP_CMD_HV_FUNCTNAL, 1, 0x01000000), true); /* Wait for FSP functional */ prlog(PR_INFO, "INIT: Waiting for FSP functional\n"); while(!(ipl_state & ipl_got_fsp_functional)) { cpu_relax(); opal_run_pollers(); } /* Tell FSP we are in running state */ prlog(PR_INFO, "INIT: Sending HV Functional: Runtime...\n"); fsp_sync_msg(fsp_mkmsg(FSP_CMD_HV_FUNCTNAL, 1, 0x02000000), true); /* * For the factory reset case, FSP sends us the PCI Bus * Reset request. We don't have to do anything special with * PCI bus numbers here; just send the Power Down message * with modifier 0x02 to FSP. */ iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params"); if (iplp && dt_find_property(iplp, "pci-busno-reset-ipl")) { prlog(PR_DEBUG, "INIT: PCI Bus Reset requested." " Sending Power Down\n"); fsp_sync_msg(fsp_mkmsg(FSP_CMD_POWERDOWN_PCIRS, 0), true); } /* * Tell FSP we are in running state with all partitions. * * This is need otherwise the FSP will not reset it's reboot count * on failures. Ideally we should send that when we know the * OS is up but we don't currently have a very good way to do * that so this will do as a stop-gap */ prlog(PR_NOTICE, "INIT: Sending HV Functional: Runtime all partitions\n"); fsp_sync_msg(fsp_mkmsg(FSP_CMD_HV_FUNCTNAL, 1, 0x04000000), true); } uint32_t fsp_adjust_lid_side(uint32_t lid_no) { struct dt_node *iplp; const char *side = NULL; iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params"); if (iplp) side = dt_prop_get_def(iplp, "cec-ipl-side", NULL); if (!side || !strcmp(side, "temp")) lid_no |= ADJUST_T_SIDE_LID_NO; return lid_no; } struct fsp_fetch_lid_item { enum resource_id id; uint32_t idx; uint32_t lid; uint32_t lid_no; uint64_t bsize; uint32_t offset; void *buffer; size_t *length; size_t remaining; size_t chunk_requested; struct list_node link; int result; }; /* * We have a queue of things to fetch * when fetched, it moves to fsp_fetched_lid until we're asked if it * has been fetched, in which case it's free()d. * * Everything is protected with fsp_fetch_lock. * * We use PSI_DMA_FETCH TCE entry for this fetching queue. If something * is in the fsp_fetch_lid_queue, it means we're using this TCE entry! * * If we add the first entry to fsp_fetch_lid_queue, we trigger fetching! */ static LIST_HEAD(fsp_fetch_lid_queue); static LIST_HEAD(fsp_fetched_lid); static struct lock fsp_fetch_lock = LOCK_UNLOCKED; /* * Asynchronous fsp fetch data call * * Note: * buffer = PSI DMA address space */ int fsp_fetch_data_queue(uint8_t flags, uint16_t id, uint32_t sub_id, uint32_t offset, void *buffer, size_t *length, void (*comp)(struct fsp_msg *msg)) { struct fsp_msg *msg; uint32_t chunk = *length; if (!comp) return OPAL_PARAMETER; msg = fsp_mkmsg(FSP_CMD_FETCH_SP_DATA, 0x6, flags << 16 | id, sub_id, offset, 0, buffer, chunk); if (!msg) { prerror("FSP: allocation failed!\n"); return OPAL_INTERNAL_ERROR; } if (fsp_queue_msg(msg, comp)) { fsp_freemsg(msg); prerror("FSP: Failed to queue fetch data message\n"); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } #define CAPP_IDX_VENICE_DD10 0x100ea #define CAPP_IDX_VENICE_DD20 0x200ea #define CAPP_IDX_MURANO_DD20 0x200ef #define CAPP_IDX_MURANO_DD21 0x201ef static struct { enum resource_id id; uint32_t idx; uint32_t lid_no; } fsp_lid_map[] = { { RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE, KERNEL_LID_OPAL }, { RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE, INITRAMFS_LID_OPAL }, { RESOURCE_ID_CAPP, CAPP_IDX_MURANO_DD20, 0x80a02002 }, { RESOURCE_ID_CAPP, CAPP_IDX_MURANO_DD21, 0x80a02001 }, { RESOURCE_ID_CAPP, CAPP_IDX_VENICE_DD10, 0x80a02003 }, { RESOURCE_ID_CAPP, CAPP_IDX_VENICE_DD20, 0x80a02004 }, }; static void fsp_start_fetching_next_lid(void); static void fsp_fetch_lid_next_chunk(struct fsp_fetch_lid_item *last); static void fsp_fetch_lid_complete(struct fsp_msg *msg) { struct fsp_fetch_lid_item *last; uint32_t woffset, wlen; uint8_t rc; lock(&fsp_fetch_lock); last = list_top(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link); fsp_tce_unmap(PSI_DMA_FETCH, last->bsize); woffset = msg->resp->data.words[1]; wlen = msg->resp->data.words[2]; rc = (msg->resp->word1 >> 8) & 0xff; /* Fall back to a PHYP LID for kernel loads */ if (rc && last->lid_no == KERNEL_LID_OPAL) { const char *ltype = dt_prop_get_def(dt_root, "lid-type", NULL); if (!ltype || strcmp(ltype, "opal")) { prerror("Failed to load in OPAL mode...\n"); last->result = OPAL_PARAMETER; last = list_pop(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link); list_add_tail(&fsp_fetched_lid, &last->link); fsp_start_fetching_next_lid(); unlock(&fsp_fetch_lock); return; } printf("Trying to load as PHYP LID...\n"); last->lid = KERNEL_LID_PHYP; /* Retry with different LID */ fsp_fetch_lid_next_chunk(last); } if (rc !=0 && rc != 2) { last->result = -EIO; last = list_pop(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link); prerror("FSP LID %08x load ERROR %d\n", last->lid_no, rc); list_add_tail(&fsp_fetched_lid, &last->link); fsp_start_fetching_next_lid(); unlock(&fsp_fetch_lock); return; } /* * As per documentation, rc=2 means end of file not reached and * rc=1 means we reached end of file. But it looks like we always * get rc=0 irrespective of whether end of file is reached or not. * The old implementation (fsp_sync_msg) used to rely on * (wlen < chunk) to decide whether we reached end of file. * * Ideally FSP folks should be fix their code as per documentation. * but until they do, adding the old check (hack) here again. * * Without this hack some systems would load partial lid and won't * be able to boot into petitboot kernel. */ if (rc == 0 && (wlen < last->chunk_requested)) last->result = OPAL_SUCCESS; fsp_freemsg(msg); last->remaining -= wlen; *(last->length) += wlen; last->buffer += wlen; last->offset += wlen; prlog(PR_DEBUG, "FSP: LID %x Chunk read -> rc=0x%02x off: %08x" " twritten: %08x\n", last->lid, rc, woffset, wlen); fsp_fetch_lid_next_chunk(last); unlock(&fsp_fetch_lock); } static void fsp_fetch_lid_next_chunk(struct fsp_fetch_lid_item *last) { uint64_t baddr; uint64_t balign, boff; uint32_t chunk; uint32_t taddr; struct fsp_msg *msg; uint8_t flags = 0; uint16_t id = FSP_DATASET_NONSP_LID; uint32_t sub_id; assert(lock_held_by_me(&fsp_fetch_lock)); if (last->remaining == 0 || last->result == OPAL_SUCCESS) { last->result = OPAL_SUCCESS; last = list_pop(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link); list_add_tail(&fsp_fetched_lid, &last->link); fsp_start_fetching_next_lid(); return; } baddr = (uint64_t)last->buffer; balign = baddr & ~TCE_MASK; boff = baddr & TCE_MASK; chunk = last->remaining; if (chunk > (PSI_DMA_FETCH_SIZE - boff)) chunk = PSI_DMA_FETCH_SIZE - boff; last->bsize = ((boff + chunk) + TCE_MASK) & ~TCE_MASK; last->chunk_requested = chunk; prlog(PR_DEBUG, "FSP: LID %08x chunk 0x%08x bytes balign=%llx" " boff=%llx bsize=%llx\n", last->lid_no, chunk, balign, boff, last->bsize); fsp_tce_map(PSI_DMA_FETCH, (void *)balign, last->bsize); taddr = PSI_DMA_FETCH + boff; sub_id = last->lid; msg = fsp_mkmsg(FSP_CMD_FETCH_SP_DATA, 6, flags << 16 | id, sub_id, last->offset, 0, taddr, chunk); if (fsp_queue_msg(msg, fsp_fetch_lid_complete)) { fsp_freemsg(msg); prerror("FSP: Failed to queue fetch data message\n"); last->result = OPAL_INTERNAL_ERROR; last = list_pop(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link); list_add_tail(&fsp_fetched_lid, &last->link); } last->result = OPAL_BUSY; } static void fsp_start_fetching_next_lid(void) { struct fsp_fetch_lid_item *last; assert(lock_held_by_me(&fsp_fetch_lock)); last = list_top(&fsp_fetch_lid_queue, struct fsp_fetch_lid_item, link); if (last == NULL) return; /* If we're not already fetching */ if (last->result == OPAL_EMPTY) fsp_fetch_lid_next_chunk(last); } int fsp_start_preload_resource(enum resource_id id, uint32_t idx, void *buf, size_t *size) { struct fsp_fetch_lid_item *resource; uint32_t lid_no = 0; int i; resource = malloc(sizeof(struct fsp_fetch_lid_item)); assert(resource != NULL); resource->id = id; resource->idx = idx; resource->offset = 0; resource->buffer = buf; resource->remaining = *size; *size = 0; resource->length = size; resource->result = OPAL_EMPTY; for (i = 0; i < ARRAY_SIZE(fsp_lid_map); i++) { if (id != fsp_lid_map[i].id) continue; if (fsp_lid_map[i].idx == idx) { lid_no = fsp_lid_map[i].lid_no; break; } } if (lid_no == 0) return OPAL_PARAMETER; printf("Trying to load OPAL LID %08x...\n", lid_no); resource->lid_no = lid_no; resource->lid = fsp_adjust_lid_side(lid_no); lock(&fsp_fetch_lock); list_add_tail(&fsp_fetch_lid_queue, &resource->link); fsp_start_fetching_next_lid(); unlock(&fsp_fetch_lock); return OPAL_SUCCESS; } int fsp_resource_loaded(enum resource_id id, uint32_t idx) { struct fsp_fetch_lid_item *resource = NULL; struct fsp_fetch_lid_item *r; int rc = OPAL_BUSY; lock(&fsp_fetch_lock); list_for_each(&fsp_fetched_lid, r, link) { if (r->id == id && r->idx == idx) { resource = r; break; } } if (resource) { rc = resource->result; list_del(&resource->link); free(resource); } unlock(&fsp_fetch_lock); return rc; } static int fsp_lid_loaded(uint32_t lid_no) { struct fsp_fetch_lid_item *resource = NULL; struct fsp_fetch_lid_item *r; int rc = OPAL_BUSY; lock(&fsp_fetch_lock); list_for_each(&fsp_fetched_lid, r, link) { if (r->lid_no == lid_no) { resource = r; break; } } if (resource) { rc = resource->result; if (rc == OPAL_SUCCESS) { list_del(&resource->link); free(resource); } } unlock(&fsp_fetch_lock); return rc; } int fsp_preload_lid(uint32_t lid_no, char *buf, size_t *size) { struct fsp_fetch_lid_item *resource; int r = OPAL_SUCCESS; resource = malloc(sizeof(struct fsp_fetch_lid_item)); assert(resource != NULL); resource->id = -1; resource->idx = -1; resource->offset = 0; resource->buffer = buf; resource->remaining = *size; *size = 0; resource->length = size; resource->result = OPAL_EMPTY; if (lid_no == 0) return OPAL_PARAMETER; printf("Trying to load LID %08x from FSP\n", lid_no); resource->lid_no = lid_no; resource->lid = fsp_adjust_lid_side(lid_no); lock(&fsp_fetch_lock); list_add_tail(&fsp_fetch_lid_queue, &resource->link); fsp_start_fetching_next_lid(); unlock(&fsp_fetch_lock); return r; } int fsp_wait_lid_loaded(uint32_t lid_no) { int r; int waited = 0; r = fsp_lid_loaded(lid_no); while(r == OPAL_BUSY) { opal_run_pollers(); time_wait_nopoll(msecs_to_tb(5)); waited+=5; cpu_relax(); r = fsp_lid_loaded(lid_no); } prlog(PR_DEBUG, "FSP: fsp_wait_lid_loaded %x %u ms\n", lid_no, waited); return r; } void fsp_used_by_console(void) { fsp_lock.in_con_path = true; /* * Some other processor might hold it without having * disabled the console locally so let's make sure that * is over by taking/releasing the lock ourselves */ lock(&fsp_lock); unlock(&fsp_lock); } skiboot-skiboot-5.1.13/hw/gx.c000066400000000000000000000071761265204436200161420ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include /* * Note: This file os only used on P7/P7+ */ /* Configuration of the PSI BUID, see the explanation in * interrupts.h */ static int gx_p7_configure_psi_buid(uint32_t chip, uint32_t buid) { uint64_t mode1; int rc; rc = xscom_read(chip, GX_P7_MODE1_REG, &mode1); if (rc) { prerror("GX: XSCOM error %d reading GX MODE1 REG\n", rc); return rc; } mode1 = SETFIELD(GX_P7_MODE1_PSI_BUID, mode1, buid); mode1 &= ~GX_P7_MODE1_PSI_BUID_DISABLE; printf("GX: MODE1_REG set to 0x%llx\n", mode1); rc = xscom_write(chip, GX_P7_MODE1_REG, mode1); if (rc) { prerror("GX: XSCOM error %d writing GX MODE1 REG\n", rc); return rc; } return 0; } static int gx_p7p_configure_psi_buid(uint32_t chip, uint32_t buid) { uint64_t mode4; int rc; rc = xscom_read(chip, GX_P7P_MODE4_REG, &mode4); if (rc) { prerror("GX: XSCOM error %d reading GX MODE1 REG\n", rc); return rc; } mode4 = SETFIELD(GX_P7P_MODE4_PSI_BUID, mode4, buid); mode4 &= ~GX_P7P_MODE4_PSI_BUID_DISABLE; rc = xscom_write(chip, GX_P7P_MODE4_REG, mode4); if (rc) { prerror("GX: XSCOM error %d writing GX MODE1 REG\n", rc); return rc; } return 0; } /* Configure the BUID of the PSI interrupt in the GX * controller. * * @chip: Chip number (0..31) * @buid: 9-bit BUID value */ int gx_configure_psi_buid(uint32_t chip, uint32_t buid) { uint32_t pvr = mfspr(SPR_PVR); printf("GX: PSI BUID for PVR %x (type %x) chip %d BUID 0x%x\n", pvr, PVR_TYPE(pvr), chip, buid); switch(PVR_TYPE(pvr)) { case PVR_TYPE_P7: return gx_p7_configure_psi_buid(chip, buid); case PVR_TYPE_P7P: return gx_p7p_configure_psi_buid(chip, buid); } return -1; } static int gx_p7_configure_tce_bar(uint32_t chip, uint32_t gx, uint64_t addr, uint64_t size) { uint32_t areg, mreg; int rc; switch (gx) { case 0: areg = GX_P7_GX0_TCE_BAR; mreg = GX_P7_GX0_TCE_MASK; break; case 1: areg = GX_P7_GX1_TCE_BAR; mreg = GX_P7_GX1_TCE_MASK; break; default: return -EINVAL; } if (addr) { uint64_t taddr, tmask; /* The address field contains bits 18 to 43 of the address */ taddr = SETFIELD(GX_P7_TCE_BAR_ADDR, 0ul, (addr >> GX_P7_TCE_BAR_ADDR_SHIFT)); taddr |= GX_P7_TCE_BAR_ENABLE; tmask = SETFIELD(GX_P7_TCE_MASK, 0ul, ~((size - 1) >> GX_P7_TCE_BAR_ADDR_SHIFT)); rc = xscom_write(chip, areg, 0); rc |= xscom_write(chip, mreg, tmask); rc |= xscom_write(chip, areg, taddr); } else { rc = xscom_write(chip, areg, 0); } return rc ? -EIO : 0; } /* Configure the TCE BAR of a given GX bus * * @chip: Chip number (0..31) * @gx : GX bus index * @addr: base address of TCE table * @size: size of TCE table */ int gx_configure_tce_bar(uint32_t chip, uint32_t gx, uint64_t addr, uint64_t size) { uint32_t pvr = mfspr(SPR_PVR); printf("GX: TCE BAR for PVR %x (type %x) chip %d gx %d\n", pvr, PVR_TYPE(pvr), chip, gx); /* We only support P7... is there a P7+ with P5IOC2 ? */ switch(PVR_TYPE(pvr)) { case PVR_TYPE_P7: return gx_p7_configure_tce_bar(chip, gx, addr, size); } return -EINVAL; } skiboot-skiboot-5.1.13/hw/homer.c000066400000000000000000000104301265204436200166210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #define PBA_BAR0 0x2013f00 #define PBA_BARMASK0 0x2013f04 static bool read_pba_bar(struct proc_chip *chip, unsigned int bar_no, uint64_t *base, uint64_t *size) { uint64_t bar, mask; int rc; rc = xscom_read(chip->id, PBA_BAR0 + bar_no, &bar); if (rc) { prerror("SLW: Error %d reading PBA BAR%d on chip %d\n", rc, bar_no, chip->id); return false; } rc = xscom_read(chip->id, PBA_BARMASK0 + bar_no, &mask); if (rc) { prerror("SLW: Error %d reading PBA BAR MASK%d on chip %d\n", rc, bar_no, chip->id); return false; } prlog(PR_DEBUG, " PBA BAR%d : 0x%016llx\n", bar_no, bar); prlog(PR_DEBUG, " PBA MASK%d: 0x%016llx\n", bar_no, mask); *base = bar & 0x0ffffffffffffffful; *size = (mask | 0xfffff) + 1; return (*base) != 0; } static void homer_init_chip(struct proc_chip *chip) { uint64_t hbase = 0, hsize = 0; uint64_t sbase, ssize, obase, osize; /* * PBA BARs assigned by HB: * * 0 : Entire HOMER * 1 : OCC to Centaur path (we don't care) * 2 : SLW image * 3 : OCC Common area * * We need to reserve the memory covered by BAR 0 and BAR 3, however * on earlier HBs, BAR0 isn't set so we need BAR 2 instead in that * case to cover SLW (OCC not running). */ if (read_pba_bar(chip, 0, &hbase, &hsize)) { prlog(PR_DEBUG, " HOMER Image at 0x%llx size %lldMB\n", hbase, hsize / 0x100000); if (!mem_range_is_reserved(hbase, hsize)) { prlog(PR_WARNING, "HOMER image is not reserved! Reserving\n"); mem_reserve_hw("ibm,homer-image", hbase, hsize); } chip->homer_base = hbase; chip->homer_size = hsize; } /* * We always read the SLW BAR since we need to grab info about the * SLW image in the struct proc_chip for use by the slw.c code */ if (read_pba_bar(chip, 2, &sbase, &ssize)) { prlog(PR_DEBUG, " SLW Image at 0x%llx size %lldMB\n", sbase, ssize / 0x100000); /* * Only reserve it if we have no homer image or if it * doesn't fit in it (only check the base). */ if ((sbase < hbase || sbase > (hbase + hsize) || (hbase == 0 && sbase > 0)) && !mem_range_is_reserved(sbase, ssize)) { prlog(PR_WARNING, "SLW image is not reserved! Reserving\n"); mem_reserve_hw("ibm,slw-image", sbase, ssize); } chip->slw_base = sbase; chip->slw_bar_size = ssize; chip->slw_image_size = ssize; /* will be adjusted later */ } if (read_pba_bar(chip, 3, &obase, &osize)) { prlog(PR_DEBUG, " OCC Common Area at 0x%llx size %lldMB\n", obase, osize / 0x100000); chip->occ_common_base = obase; chip->occ_common_size = osize; } } void homer_init(void) { struct proc_chip *chip; if (proc_gen != proc_gen_p8 || chip_quirk(QUIRK_NO_PBA)) return; /* * XXX This is temporary, on P8 we look for any configured * SLW/OCC BAR and reserve the memory. Eventually, this will be * done via HostBoot using the device-tree "reserved-ranges" * or we'll load the SLW & OCC images ourselves using Host Services. */ for_each_chip(chip) { prlog(PR_DEBUG, "HOMER: Init chip %d\n", chip->id); homer_init_chip(chip); } /* * Check is PBA BARs are already loaded with HOMER and * skip host services. */ chip = next_chip(NULL); if (chip->homer_base && chip->occ_common_base) { /* Reserve OCC common area from BAR */ if (!mem_range_is_reserved(chip->occ_common_base, chip->occ_common_size)) { prlog(PR_WARNING, "OCC common area is not reserved! Reserving\n"); mem_reserve_hw("ibm,occ-common-area", chip->occ_common_base, chip->occ_common_size); } } else { /* Allocate memory for HOMER and OCC common area */ host_services_occ_base_setup(); } } skiboot-skiboot-5.1.13/hw/ipmi/000077500000000000000000000000001265204436200163035ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hw/ipmi/Makefile.inc000066400000000000000000000003171265204436200205140ustar00rootroot00000000000000SUBDIRS += hw/ipmi IPMI_OBJS = ipmi-rtc.o ipmi-power.o ipmi-opal.o ipmi-fru.o ipmi-sel.o IPMI_OBJS += ipmi-watchdog.o ipmi-sensor.o ipmi-attn.o IPMI = hw/ipmi/built-in.o $(IPMI): $(IPMI_OBJS:%=hw/ipmi/%) skiboot-skiboot-5.1.13/hw/ipmi/ipmi-attn.c000066400000000000000000000046071265204436200203600ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include /* Use same attention SRC for BMC based machine */ DEFINE_LOG_ENTRY(OPAL_RC_ATTN, OPAL_PLATFORM_ERR_EVT, OPAL_ATTN, OPAL_PLATFORM_FIRMWARE, OPAL_ERROR_PANIC, OPAL_ABNORMAL_POWER_OFF); /* Maximum buffer size to capture backtrace and other useful information */ #define IPMI_TI_BUFFER_SIZE (IPMI_MAX_PEL_SIZE - PEL_MIN_SIZE) static char ti_buffer[IPMI_TI_BUFFER_SIZE]; #define STACK_BUF_ENTRIES 20 struct bt_entry bt_buf[STACK_BUF_ENTRIES]; /* Log eSEL event with OPAL backtrace */ static void ipmi_log_terminate_event(const char *msg) { unsigned int bt_entry_cnt = STACK_BUF_ENTRIES; unsigned int ti_len; unsigned int ti_size; struct errorlog *elog_buf; /* Fill OPAL version */ ti_len = snprintf(ti_buffer, IPMI_TI_BUFFER_SIZE, "OPAL version : %s\n", version); /* File information */ ti_len += snprintf(ti_buffer + ti_len, IPMI_TI_BUFFER_SIZE - ti_len, "File info : %s\n", msg); ti_size = IPMI_TI_BUFFER_SIZE - ti_len; /* Backtrace */ __backtrace(bt_buf, &bt_entry_cnt); __print_backtrace(mfspr(SPR_PIR), bt_buf, bt_entry_cnt, ti_buffer + ti_len, &ti_size, true); /* Create eSEL event and commit */ elog_buf = opal_elog_create(&e_info(OPAL_RC_ATTN), 0); log_append_data(elog_buf, (char *)&ti_buffer, ti_len + ti_size); log_commit(elog_buf); } void __attribute__((noreturn)) ipmi_terminate(const char *msg) { /* Terminate called before initializing IPMI (early abort) */ if (!ipmi_present()) { if (platform.cec_reboot()) platform.cec_reboot(); goto out; } /* Log eSEL event */ ipmi_log_terminate_event(msg); /* Reboot call */ if (platform.cec_reboot()) platform.cec_reboot(); out: while (1) time_wait_ms(100); } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-fru.c000066400000000000000000000141171265204436200202030ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include struct product_info { char *manufacturer; char *product; char *part_no; char *version; char *serial_no; char *asset_tag; }; struct common_header { u8 version; u8 internal_offset; u8 chassis_offset; u8 board_offset; u8 product_offset; u8 multirecord_offset; u8 pad; u8 checksum; } __packed; #define min(x,y) ((x) < (y) ? x : y) /* The maximum amount of FRU data we can store. */ #define FRU_DATA_SIZE 256 /* We allocate two bytes at these locations in the data array to track * state. */ #define WRITE_INDEX 256 #define REMAINING 257 /* The ASCII string encoding used only has 5 bits to encode length * hence the maximum is 31 characters. */ #define MAX_STR_LEN 31 static u8 fru_dev_id = 0; static int fru_insert_string(u8 *buf, char *str) { int len = strlen(str); /* The ASCII type/length format only supports a string length * between 2 and 31 characters. Zero characters is ok though * as it indicates no data present. */ if (len == 1 || len > MAX_STR_LEN) return OPAL_PARAMETER; buf[0] = 0xc0 | len; memcpy(&buf[1], str, len); return len + 1; } static u8 fru_checksum(u8 *buf, int len) { int i; u8 checksum = 0; for(i = 0; i < len; i++) { checksum += buf[i]; } checksum = ~checksum + 1; return checksum; } #define FRU_INSERT_STRING(x, y) \ ({ rc = fru_insert_string(x, y); \ if (rc < 1) return OPAL_PARAMETER; rc; }) static int fru_fill_product_info(u8 *buf, struct product_info *info, size_t size) { size_t total_size = 11; int index = 0; int rc; total_size += strlen(info->manufacturer); total_size += strlen(info->product); total_size += strlen(info->part_no); total_size += strlen(info->version); total_size += strlen(info->serial_no); total_size += strlen(info->asset_tag); total_size += (8 - (total_size % 8)) % 8; if (total_size > size) return OPAL_PARAMETER; buf[index++] = 0x1; /* Version */ buf[index++] = total_size / 8; /* Size */ buf[index++] = 0; /* Language code (English) */ index += FRU_INSERT_STRING(&buf[index], info->manufacturer); index += FRU_INSERT_STRING(&buf[index], info->product); index += FRU_INSERT_STRING(&buf[index], info->part_no); index += FRU_INSERT_STRING(&buf[index], info->version); index += FRU_INSERT_STRING(&buf[index], info->serial_no); index += FRU_INSERT_STRING(&buf[index], info->asset_tag); buf[index++] = 0xc1; /* End of data marker */ memset(&buf[index], 0, total_size - index - 1); index += total_size - index - 1; buf[index] = fru_checksum(buf, index); assert(index == total_size - 1); return total_size; } static int fru_add(u8 *buf, int size) { int len; char short_version[MAX_STR_LEN + 1]; struct common_header common_hdr; struct product_info info = { .manufacturer = (char *) "IBM", .product = (char *) "skiboot", .part_no = (char *) "", .serial_no = (char *) "", .asset_tag = (char *) "", }; if (size < sizeof(common_hdr)) return OPAL_PARAMETER; /* We currently only support adding the version number at the * product information offset. We choose an offset of 64 bytes * because that's what the standard recommends. */ common_hdr.version = 1; common_hdr.internal_offset = 0; common_hdr.chassis_offset = 0; common_hdr.board_offset = 0; common_hdr.product_offset = 64/8; common_hdr.multirecord_offset = 0; common_hdr.pad = 0; common_hdr.checksum = fru_checksum((u8 *) &common_hdr, sizeof(common_hdr) - 1); memcpy(buf, &common_hdr, sizeof(common_hdr)); info.version = short_version; if (!strncmp(version, "skiboot-", 8)) strncpy(info.version, &version[8], MAX_STR_LEN + 1); else strncpy(info.version, version, MAX_STR_LEN + 1); if (info.version[MAX_STR_LEN] != '\0') info.version[MAX_STR_LEN - 1] = '+'; info.version[MAX_STR_LEN] = '\0'; len = fru_fill_product_info(&buf[64], &info, size - 64); if (len < 0) return OPAL_PARAMETER; return len + 64; } static void fru_write_complete(struct ipmi_msg *msg) { u8 write_count = msg->data[0]; u16 offset; msg->data[WRITE_INDEX] += write_count; msg->data[REMAINING] -= write_count; if (msg->data[REMAINING] == 0) goto out; offset = msg->data[WRITE_INDEX]; ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU, fru_write_complete, NULL, MIN(msg->data[REMAINING] + 3, IPMI_MAX_REQ_SIZE), 2); memmove(&msg->data[3], &msg->data[offset + 3], msg->req_size - 3); msg->data[0] = fru_dev_id; /* FRU Device ID */ msg->data[1] = offset & 0xff; /* Offset LSB */ msg->data[2] = (offset >> 8) & 0xff; /* Offset MSB */ ipmi_queue_msg(msg); return; out: ipmi_free_msg(msg); } static int fru_write(void) { struct ipmi_msg *msg; int len; /* We allocate FRU_DATA_SIZE + 5 bytes for the message: * - 3 bytes for the the write FRU command header * - FRU_DATA_SIZE bytes for FRU data * - 2 bytes for offset & bytes remaining count */ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU, fru_write_complete, NULL, NULL, FRU_DATA_SIZE + 5, 2); if (!msg) return OPAL_RESOURCE; msg->data[0] = fru_dev_id; /* FRU Device ID */ msg->data[1] = 0x0; /* Offset LSB (we always write a new common header) */ msg->data[2] = 0x0; /* Offset MSB */ len = fru_add(&msg->data[3], FRU_DATA_SIZE); if (len < 0) return len; /* Three bytes for the actual FRU Data Command */ msg->data[WRITE_INDEX] = 0; msg->data[REMAINING] = len; msg->req_size = min(len + 3, IPMI_MAX_REQ_SIZE); return ipmi_queue_msg(msg); } void ipmi_fru_init(u8 dev_id) { fru_dev_id = dev_id; fru_write(); return; } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-opal.c000066400000000000000000000075671265204436200203550ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include static struct lock msgq_lock = LOCK_UNLOCKED; static struct list_head msgq = LIST_HEAD_INIT(msgq); static void opal_send_complete(struct ipmi_msg *msg) { lock(&msgq_lock); list_add_tail(&msgq, &msg->link); opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, ipmi_backend->opal_event_ipmi_recv); unlock(&msgq_lock); } static int64_t opal_ipmi_send(uint64_t interface, struct opal_ipmi_msg *opal_ipmi_msg, uint64_t msg_len) { struct ipmi_msg *msg; if (opal_ipmi_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) { prerror("OPAL IPMI: Incorrect version\n"); return OPAL_UNSUPPORTED; } msg_len -= sizeof(struct opal_ipmi_msg); if (msg_len > IPMI_MAX_REQ_SIZE) { prerror("OPAL IPMI: Invalid request length\n"); return OPAL_PARAMETER; } prlog(PR_DEBUG, "opal_ipmi_send(cmd: 0x%02x netfn: 0x%02x len: 0x%02llx)\n", opal_ipmi_msg->cmd, opal_ipmi_msg->netfn >> 2, msg_len); msg = ipmi_mkmsg(interface, IPMI_CODE(opal_ipmi_msg->netfn >> 2, opal_ipmi_msg->cmd), opal_send_complete, NULL, opal_ipmi_msg->data, msg_len, IPMI_MAX_RESP_SIZE); if (!msg) return OPAL_RESOURCE; msg->complete = opal_send_complete; msg->error = opal_send_complete; return ipmi_queue_msg(msg); } static int64_t opal_ipmi_recv(uint64_t interface, struct opal_ipmi_msg *opal_ipmi_msg, uint64_t *msg_len) { struct ipmi_msg *msg; int64_t rc; lock(&msgq_lock); msg = list_top(&msgq, struct ipmi_msg, link); if (!msg) { rc = OPAL_EMPTY; goto out_unlock; } if (opal_ipmi_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) { prerror("OPAL IPMI: Incorrect version\n"); rc = OPAL_UNSUPPORTED; goto out_del_msg; } if (interface != IPMI_DEFAULT_INTERFACE) { prerror("IPMI: Invalid interface 0x%llx in opal_ipmi_recv\n", interface); rc = OPAL_PARAMETER; goto out_del_msg; } if (*msg_len - sizeof(struct opal_ipmi_msg) < msg->resp_size + 1) { rc = OPAL_RESOURCE; goto out_del_msg; } list_del(&msg->link); if (list_empty(&msgq)) opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, 0); unlock(&msgq_lock); opal_ipmi_msg->cmd = msg->cmd; opal_ipmi_msg->netfn = msg->netfn; opal_ipmi_msg->data[0] = msg->cc; memcpy(&opal_ipmi_msg->data[1], msg->data, msg->resp_size); prlog(PR_DEBUG, "opal_ipmi_recv(cmd: 0x%02x netfn: 0x%02x resp_size: 0x%02x)\n", msg->cmd, msg->netfn >> 2, msg->resp_size); /* Add one as the completion code is returned in the message data */ *msg_len = msg->resp_size + sizeof(struct opal_ipmi_msg) + 1; ipmi_free_msg(msg); return OPAL_SUCCESS; out_del_msg: list_del(&msg->link); if (list_empty(&msgq)) opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, 0); ipmi_free_msg(msg); out_unlock: unlock(&msgq_lock); return rc; } void ipmi_opal_init(void) { struct dt_node *opal_ipmi; opal_ipmi = dt_new(opal_node, "ipmi"); dt_add_property_strings(opal_ipmi, "compatible", "ibm,opal-ipmi"); dt_add_property_cells(opal_ipmi, "ibm,ipmi-interface-id", IPMI_DEFAULT_INTERFACE); dt_add_property_cells(opal_ipmi, "interrupts", ilog2(ipmi_backend->opal_event_ipmi_recv)); opal_register(OPAL_IPMI_SEND, opal_ipmi_send, 3); opal_register(OPAL_IPMI_RECV, opal_ipmi_recv, 3); } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-power.c000066400000000000000000000033021265204436200205350ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include int ipmi_chassis_control(uint8_t request) { struct ipmi_msg *msg; if (!ipmi_present()) return OPAL_CLOSED; if (request > IPMI_CHASSIS_SOFT_SHUTDOWN) return OPAL_PARAMETER; msg = ipmi_mkmsg_simple(IPMI_CHASSIS_CONTROL, &request, sizeof(request)); if (!msg) return OPAL_HARDWARE; prlog(PR_INFO, "IPMI: sending chassis control request 0x%02x\n", request); return ipmi_queue_msg(msg); } int ipmi_set_power_state(uint8_t system, uint8_t device) { struct ipmi_msg *msg; struct { uint8_t system; uint8_t device; } power_state; if (!ipmi_present()) return OPAL_CLOSED; power_state.system = system; power_state.device = device; if (system != IPMI_PWR_NOCHANGE) power_state.system |= 0x80; if (device != IPMI_PWR_NOCHANGE) power_state.device |= 0x80; msg = ipmi_mkmsg_simple(IPMI_SET_POWER_STATE, &power_state, sizeof(power_state)); if (!msg) return OPAL_HARDWARE; prlog(PR_INFO, "IPMI: setting power state: sys %02x, dev %02x\n", power_state.system, power_state.device); return ipmi_queue_msg(msg); } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-rtc.c000066400000000000000000000054031265204436200201750ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include static enum {idle, waiting, updated, error} time_status; static void get_sel_time_error(struct ipmi_msg *msg) { time_status = error; ipmi_free_msg(msg); } static void get_sel_time_complete(struct ipmi_msg *msg) { struct tm tm; uint32_t result; time_t time; memcpy(&result, msg->data, 4); time = le32_to_cpu(result); gmtime_r(&time, &tm); rtc_cache_update(&tm); time_status = updated; ipmi_free_msg(msg); } static int64_t ipmi_get_sel_time(void) { struct ipmi_msg *msg; msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_GET_SEL_TIME, get_sel_time_complete, NULL, NULL, 0, 4); if (!msg) return OPAL_HARDWARE; msg->error = get_sel_time_error; return ipmi_queue_msg(msg); } static int64_t ipmi_set_sel_time(uint32_t tv) { struct ipmi_msg *msg; tv = cpu_to_le32(tv); msg = ipmi_mkmsg_simple(IPMI_SET_SEL_TIME, &tv, sizeof(tv)); if (!msg) return OPAL_HARDWARE; return ipmi_queue_msg(msg); } static int64_t ipmi_opal_rtc_read(uint32_t *y_m_d, uint64_t *h_m_s_m) { int ret = 0; if (!y_m_d || !h_m_s_m) return OPAL_PARAMETER; switch(time_status) { case idle: if (ipmi_get_sel_time() < 0) return OPAL_HARDWARE; time_status = waiting; ret = OPAL_BUSY_EVENT; break; case waiting: ret = OPAL_BUSY_EVENT; break; case updated: rtc_cache_get_datetime(y_m_d, h_m_s_m); time_status = idle; ret = OPAL_SUCCESS; break; case error: time_status = idle; ret = OPAL_HARDWARE; break; } return ret; } static int64_t ipmi_opal_rtc_write(uint32_t year_month_day, uint64_t hour_minute_second_millisecond) { time_t t; struct tm tm; datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm); t = mktime(&tm); if (ipmi_set_sel_time(t)) return OPAL_HARDWARE; return OPAL_SUCCESS; } void ipmi_rtc_init(void) { struct dt_node *np = dt_new(opal_node, "rtc"); dt_add_property_strings(np, "compatible", "ibm,opal-rtc"); opal_register(OPAL_RTC_READ, ipmi_opal_rtc_read, 2); opal_register(OPAL_RTC_WRITE, ipmi_opal_rtc_write, 2); /* Initialise the rtc cache */ ipmi_get_sel_time(); } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-sel.c000066400000000000000000000354251265204436200201770ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define pr_fmt(fmt) "IPMI: " fmt #include #include #include #include #include #include #include #include #include #include /* OEM SEL fields */ #define SEL_OEM_ID_0 0x55 #define SEL_OEM_ID_1 0x55 #define SEL_RECORD_TYPE_OEM 0xC0 #define SEL_RECORD_TYPE_EVENT 0x02 #define SEL_NETFN_IBM 0x3a /* OEM SEL Commands */ #define CMD_AMI_POWER 0x04 #define CMD_AMI_PNOR_ACCESS 0x07 #define CMD_AMI_OCC_RESET 0x0e #define SOFT_OFF 0x00 #define SOFT_REBOOT 0x01 #define RELEASE_PNOR 0x00 #define REQUEST_PNOR 0x01 /* 32.1 SEL Event Records type */ #define SEL_REC_TYPE_SYS_EVENT 0x02 #define SEL_REC_TYPE_AMI_ESEL 0xDF /* OEM SEL generator ID for AMI */ #define SEL_GENERATOR_ID_AMI 0x2000 /* IPMI SEL version */ #define SEL_EVM_VER_1 0x03 #define SEL_EVM_VER_2 0x04 /* * Sensor type for System events * * Sensor information (type, number, etc) is passed to us via * device tree. Currently we are using System Event type to * log OPAL events. */ #define SENSOR_TYPE_SYS_EVENT 0x12 /* * 42.1 Event/Reading Type Codes * * Note that device hotplug and availability related events * are not defined as we are not using those events type. */ #define SEL_EVENT_DIR_TYPE_UNSPECIFIED 0x00 #define SEL_EVENT_DIR_TYPE_THRESHOLD 0x01 #define SEL_EVENT_DIR_TYPE_STATE 0x03 #define SEL_EVENT_DIR_TYPE_PREDICTIVE 0x04 #define SEL_EVENT_DIR_TYPE_LIMIT 0x05 #define SEL_EVENT_DIR_TYPE_PERFORMANCE 0x06 #define SEL_EVENT_DIR_TYPE_TRANSITION 0x07 #define SEL_EVENT_DIR_TYPE_OEM 0x70 /* * 42.1 Event/Reading Type Codes */ #define SEL_DATA1_AMI 0xAA #define SEL_DATA1_DEASSERTED 0x00 #define SEL_DATA1_ASSERTED 0x01 #define SEL_DATA1_OK 0x00 #define SEL_DATA1_NON_CRIT_FROM_OK 0x01 #define SEL_DATA1_CRIT_FROM_LESS_SEV 0x02 #define SEL_DATA1_NON_REC_FROM_LESS_SEV 0x03 #define SEL_DATA1_NON_CRIT 0x04 #define SEL_DATA1_CRITICAL 0x05 #define SEL_DATA1_NON_RECOVERABLE 0X06 #define SEL_DATA1_MONITOR 0x07 #define SEL_DATA1_INFORMATIONAL 0x08 /* SEL Record Entry */ struct sel_record { le16 record_id; uint8_t record_type; le32 timestamp; le16 generator_id; uint8_t evm_ver; uint8_t sensor_type; uint8_t sensor_number; uint8_t event_dir_type; uint8_t event_data1; uint8_t event_data2; uint8_t event_data3; } __packed; static struct sel_record sel_record; struct oem_sel { /* SEL header */ uint8_t id[2]; uint8_t type; uint8_t manuf_id[3]; uint8_t timestamp[4]; /* OEM SEL data (6 bytes) follows */ uint8_t netfun; uint8_t cmd; uint8_t data[4]; }; #define ESEL_HDR_SIZE 7 /* Used for sending PANIC events like abort() path */ struct ipmi_sel_panic_msg { bool busy; struct ipmi_msg *msg; struct lock lock; }; static struct ipmi_sel_panic_msg ipmi_sel_panic_msg; /* Forward declaration */ static void ipmi_elog_poll(struct ipmi_msg *msg); /* * Allocate IPMI message * For normal event, allocate memory using ipmi_mkmsg and for PANIC * event, use pre-allocated buffer. */ static struct ipmi_msg *ipmi_sel_alloc_msg(struct errorlog *elog_buf) { struct ipmi_msg *msg = NULL; if (elog_buf->event_severity == OPAL_ERROR_PANIC) { /* Called before initialization completes */ if (ipmi_sel_panic_msg.msg == NULL) return NULL; if (ipmi_sel_panic_msg.busy == true) return NULL; lock(&ipmi_sel_panic_msg.lock); msg = ipmi_sel_panic_msg.msg; ipmi_sel_panic_msg.busy = true; unlock(&ipmi_sel_panic_msg.lock); ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_RESERVE_SEL, ipmi_elog_poll, elog_buf, IPMI_MAX_REQ_SIZE, 2); } else { msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_RESERVE_SEL, ipmi_elog_poll, elog_buf, NULL, IPMI_MAX_REQ_SIZE, 2); } return msg; } static void ipmi_sel_free_msg(struct ipmi_msg *msg) { if (msg == ipmi_sel_panic_msg.msg) { lock(&ipmi_sel_panic_msg.lock); ipmi_sel_panic_msg.busy = false; unlock(&ipmi_sel_panic_msg.lock); } else { ipmi_free_msg(msg); } msg = NULL; } /* Initialize eSEL record */ static void ipmi_init_esel_record(void) { memset(&sel_record, 0, sizeof(struct sel_record)); sel_record.record_type = SEL_REC_TYPE_AMI_ESEL; sel_record.generator_id = SEL_GENERATOR_ID_AMI; sel_record.evm_ver = SEL_EVM_VER_2; sel_record.sensor_type = SENSOR_TYPE_SYS_EVENT; sel_record.sensor_number = ipmi_get_sensor_number(SENSOR_TYPE_SYS_EVENT); sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_OEM; sel_record.event_data1 = SEL_DATA1_AMI; } /* Update required fields in SEL record */ static void ipmi_update_sel_record(uint8_t event_severity, uint16_t esel_record_id) { sel_record.record_type = SEL_REC_TYPE_SYS_EVENT; sel_record.event_data2 = esel_record_id & 0xff; sel_record.event_data3 = (esel_record_id >> 8) & 0xff; switch (event_severity) { case OPAL_ERROR_PANIC: sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_TRANSITION; sel_record.event_data1 = SEL_DATA1_CRITICAL; break; case OPAL_UNRECOVERABLE_ERR_GENERAL: /* Fall through */ case OPAL_UNRECOVERABLE_ERR_DEGRADE_PERF: case OPAL_UNRECOVERABLE_ERR_LOSS_REDUNDANCY: case OPAL_UNRECOVERABLE_ERR_LOSS_REDUNDANCY_PERF: case OPAL_UNRECOVERABLE_ERR_LOSS_OF_FUNCTION: sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_TRANSITION; sel_record.event_data1 = SEL_DATA1_NON_RECOVERABLE; break; case OPAL_PREDICTIVE_ERR_GENERAL: /* Fall through */ case OPAL_PREDICTIVE_ERR_DEGRADED_PERF: case OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT: case OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_BOOT_DEGRADE_PERF: case OPAL_PREDICTIVE_ERR_LOSS_OF_REDUNDANCY: sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_PREDICTIVE; sel_record.event_data1 = SEL_DATA1_NON_CRIT_FROM_OK; break; case OPAL_RECOVERED_ERR_GENERAL: sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_TRANSITION; sel_record.event_data1 = SEL_DATA1_OK; break; case OPAL_INFO: sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_TRANSITION; sel_record.event_data1 = SEL_DATA1_INFORMATIONAL; break; default: sel_record.event_dir_type = SEL_EVENT_DIR_TYPE_STATE; sel_record.event_data1 = SEL_DATA1_ASSERTED; break; } } static void ipmi_elog_error(struct ipmi_msg *msg) { if (msg->cc == IPMI_LOST_ARBITRATION_ERR) /* Retry due to SEL erase */ ipmi_queue_msg(msg); else { opal_elog_complete(msg->user_data, false); ipmi_sel_free_msg(msg); } } static void ipmi_log_sel_event_error(struct ipmi_msg *msg) { if (msg->cc != IPMI_CC_NO_ERROR) prlog(PR_INFO, "SEL: Failed to log SEL event\n"); ipmi_sel_free_msg(msg); } static void ipmi_log_sel_event_complete(struct ipmi_msg *msg) { prlog(PR_INFO, "SEL: New event logged [ID : %x%x]\n", msg->data[1], msg->data[0]); ipmi_sel_free_msg(msg); } /* Log SEL event with eSEL record ID */ static void ipmi_log_sel_event(struct ipmi_msg *msg, uint8_t event_severity, uint16_t esel_record_id) { /* Fill required SEL event fields */ ipmi_update_sel_record(event_severity, esel_record_id); /* Fill IPMI message */ ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_ADD_SEL_EVENT, ipmi_log_sel_event_complete, NULL, sizeof(struct sel_record), 2); /* Copy SEL data */ memcpy(msg->data, &sel_record, sizeof(struct sel_record)); msg->error = ipmi_log_sel_event_error; ipmi_queue_msg_head(msg); } /* Goes through the required steps to add a complete eSEL: * * 1. Get a reservation * 2. Add eSEL header * 3. Partially add data to the SEL * * Because a reservation is needed we need to ensure eSEL's are added * as a single transaction as concurrent/interleaved adds would cancel * the reservation. We guarantee this by always adding our messages to * the head of the transmission queue, blocking any other messages * being sent until we have completed sending this message. * * There is still a very small chance that we will accidentally * interleave a message if there is another one waiting at the head of * the ipmi queue and another cpu calls the ipmi poller before we * complete. However this should just cause a resevation cancelled * error which we have to deal with anyway (eg. because there may be a * SEL erase in progress) so it shouldn't cause any problems. */ static void ipmi_elog_poll(struct ipmi_msg *msg) { static bool first = false; static char pel_buf[IPMI_MAX_PEL_SIZE]; static size_t pel_size; static size_t esel_size; static int esel_index = 0; int pel_index; static unsigned int reservation_id = 0; static unsigned int record_id = 0; struct errorlog *elog_buf = (struct errorlog *) msg->user_data; size_t req_size; ipmi_init_esel_record(); if (msg->cmd == IPMI_CMD(IPMI_RESERVE_SEL)) { first = true; reservation_id = msg->data[0]; reservation_id |= msg->data[1] << 8; if (!reservation_id) { /* According to specification we should never * get here, but just in case we do we cancel * sending the message. */ prerror("Invalid reservation id"); opal_elog_complete(elog_buf, false); ipmi_sel_free_msg(msg); return; } pel_size = create_pel_log(elog_buf, pel_buf, IPMI_MAX_PEL_SIZE); esel_size = pel_size + sizeof(struct sel_record); esel_index = 0; record_id = 0; } else { record_id = msg->data[0]; record_id |= msg->data[1] << 8; } /* Start or continue the IPMI_PARTIAL_ADD_SEL */ if (esel_index >= esel_size) { /* We're all done. Invalidate the resevation id to * ensure we get an error if we cut in on another eSEL * message. */ reservation_id = 0; esel_index = 0; /* Log SEL event and free ipmi message */ ipmi_log_sel_event(msg, elog_buf->event_severity, record_id); opal_elog_complete(elog_buf, true); return; } if ((esel_size - esel_index) <= (IPMI_MAX_REQ_SIZE - ESEL_HDR_SIZE)) { /* Last data to send */ msg->data[6] = 1; req_size = esel_size - esel_index + ESEL_HDR_SIZE; } else { msg->data[6] = 0; req_size = IPMI_MAX_REQ_SIZE; } ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_PARTIAL_ADD_ESEL, ipmi_elog_poll, elog_buf, req_size, 2); msg->data[0] = reservation_id & 0xff; msg->data[1] = (reservation_id >> 8) & 0xff; msg->data[2] = record_id & 0xff; msg->data[3] = (record_id >> 8) & 0xff; msg->data[4] = esel_index & 0xff; msg->data[5] = (esel_index >> 8) & 0xff; if (first) { first = false; memcpy(&msg->data[ESEL_HDR_SIZE], &sel_record, sizeof(struct sel_record)); esel_index = sizeof(struct sel_record); msg->req_size = esel_index + ESEL_HDR_SIZE; } else { pel_index = esel_index - sizeof(struct sel_record); memcpy(&msg->data[ESEL_HDR_SIZE], &pel_buf[pel_index], msg->req_size - ESEL_HDR_SIZE); esel_index += msg->req_size - ESEL_HDR_SIZE; } ipmi_queue_msg_head(msg); return; } int ipmi_elog_commit(struct errorlog *elog_buf) { struct ipmi_msg *msg; /* Only log events that needs attention */ if (elog_buf->event_severity < OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT || elog_buf->elog_origin != ORG_SAPPHIRE) { prlog(PR_INFO, "dropping non severe PEL event\n"); return 0; } /* We pass a large request size in to mkmsg so that we have a * large enough allocation to reuse the message to pass the * PEL data via a series of partial add commands. */ msg = ipmi_sel_alloc_msg(elog_buf); if (!msg) { opal_elog_complete(elog_buf, false); return OPAL_RESOURCE; } msg->error = ipmi_elog_error; msg->req_size = 0; ipmi_queue_msg(msg); return 0; } #define ACCESS_DENIED 0x00 #define ACCESS_GRANTED 0x01 static void sel_pnor(uint8_t access) { struct ipmi_msg *msg; uint8_t granted = ACCESS_GRANTED; switch (access) { case REQUEST_PNOR: prlog(PR_NOTICE, "PNOR access requested\n"); granted = flash_reserve(); if (granted) occ_pnor_set_owner(PNOR_OWNER_EXTERNAL); /* Ack the request */ msg = ipmi_mkmsg_simple(IPMI_PNOR_ACCESS_STATUS, &granted, 1); ipmi_queue_msg(msg); break; case RELEASE_PNOR: prlog(PR_NOTICE, "PNOR access released\n"); flash_release(); occ_pnor_set_owner(PNOR_OWNER_HOST); break; default: prlog(PR_ERR, "invalid PNOR access requested: %02x\n", access); } } static void sel_power(uint8_t power) { switch (power) { case SOFT_OFF: prlog(PR_NOTICE, "Soft shutdown requested\n"); if (!(debug_descriptor.state_flags & OPAL_BOOT_COMPLETE) && platform.cec_power_down) { prlog(PR_NOTICE, "Host not up, shutting down now\n"); platform.cec_power_down(IPMI_CHASSIS_PWR_DOWN); } else { opal_queue_msg(OPAL_MSG_SHUTDOWN, NULL, NULL, SOFT_OFF); } break; case SOFT_REBOOT: prlog(PR_NOTICE, "Soft reboot requested\n"); if (!(debug_descriptor.state_flags & OPAL_BOOT_COMPLETE) && platform.cec_reboot) { prlog(PR_NOTICE, "Host not up, rebooting now\n"); platform.cec_reboot(); } else { opal_queue_msg(OPAL_MSG_SHUTDOWN, NULL, NULL, SOFT_REBOOT); } break; default: prlog(PR_WARNING, "requested bad power state: %02x\n", power); } } static uint32_t occ_sensor_id_to_chip(uint8_t sensor, uint32_t *chip) { /* todo: lookup sensor ID node in the DT, and map to a chip id */ (void)sensor; *chip = 0; return 0; } static void sel_occ_reset(uint8_t sensor) { uint32_t chip; int rc; rc = occ_sensor_id_to_chip(sensor, &chip); if (rc) { prlog(PR_ERR, "SEL message to reset an unknown OCC " "(sensor ID 0x%02x)\n", sensor); return; } prd_occ_reset(chip); } void ipmi_parse_sel(struct ipmi_msg *msg) { struct oem_sel sel; assert(msg->resp_size <= 16); memcpy(&sel, msg->data, msg->resp_size); /* We do not process system event records */ if (sel.type == SEL_RECORD_TYPE_EVENT) { prlog(PR_INFO, "dropping System Event Record SEL\n"); return; } prlog(PR_DEBUG, "SEL received (%d bytes, netfn %d, cmd %d)\n", msg->resp_size, sel.netfun, sel.cmd); /* Only accept OEM SEL messages */ if (sel.id[0] != SEL_OEM_ID_0 || sel.id[1] != SEL_OEM_ID_1 || sel.type != SEL_RECORD_TYPE_OEM) { prlog(PR_WARNING, "unknown SEL %02x%02x (type %02x)\n", sel.id[0], sel.id[1], sel.type); return; } switch (sel.cmd) { case CMD_AMI_POWER: sel_power(sel.data[0]); break; case CMD_AMI_OCC_RESET: sel_occ_reset(sel.data[0]); break; case CMD_AMI_PNOR_ACCESS: sel_pnor(sel.data[0]); break; default: prlog(PR_WARNING, "unknown OEM SEL command %02x received\n", sel.cmd); } } void ipmi_sel_init(void) { memset(&ipmi_sel_panic_msg, 0, sizeof(struct ipmi_sel_panic_msg)); ipmi_sel_panic_msg.msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_RESERVE_SEL, ipmi_elog_poll, NULL, NULL, IPMI_MAX_REQ_SIZE, 2); } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-sensor.c000066400000000000000000000062221265204436200207160ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define IPMI_WRITE_SENSOR (1 << 0) #define FW_PROGRESS_SENSOR_TYPE 0x0F #define BOOT_COUNT_SENSOR_TYPE 0xC3 static int16_t sensors[255]; struct set_sensor_req { u8 sensor_number; u8 operation; u8 sensor_reading; u8 assertion_mask[2]; u8 deassertion_mask[2]; u8 event_data[3]; }; uint8_t ipmi_get_sensor_number(uint8_t sensor_type) { return sensors[sensor_type]; } int ipmi_set_boot_count(void) { struct set_sensor_req req; struct ipmi_msg *msg; int boot_count_sensor = sensors[BOOT_COUNT_SENSOR_TYPE]; if (!ipmi_present()) return OPAL_CLOSED; if (boot_count_sensor < 0) { prlog(PR_DEBUG, "IPMI: boot count set but not present\n"); return OPAL_HARDWARE; } memset(&req, 0, sizeof(req)); req.sensor_number = boot_count_sensor; req.operation = IPMI_WRITE_SENSOR; req.sensor_reading = 0x00; req.assertion_mask[0] = 0x02; msg = ipmi_mkmsg_simple(IPMI_SET_SENSOR_READING, &req, sizeof(req)); if (!msg) return OPAL_HARDWARE; printf("IPMI: Resetting boot count on successful boot\n"); return ipmi_queue_msg(msg); } int ipmi_set_fw_progress_sensor(uint8_t state) { struct ipmi_msg *msg; struct set_sensor_req request; int fw_sensor_num = sensors[FW_PROGRESS_SENSOR_TYPE]; if (!ipmi_present()) return OPAL_CLOSED; if (fw_sensor_num < 0) { prlog(PR_DEBUG, "IPMI: fw progress set but not present\n"); return OPAL_HARDWARE; } memset(&request, 0, sizeof(request)); request.sensor_number = fw_sensor_num; request.operation = 0xa0; /* Set event data bytes, assertion bits */ request.assertion_mask[0] = 0x04; /* Firmware progress offset */ request.event_data[1] = state; prlog(PR_INFO, "IPMI: setting fw progress sensor %02x to %02x\n", request.sensor_number, request.event_data[1]); msg = ipmi_mkmsg_simple(IPMI_SET_SENSOR_READING, &request, sizeof(request)); if (!msg) return OPAL_HARDWARE; return ipmi_queue_msg(msg); } void ipmi_sensor_init(void) { const struct dt_property *type_prop, *num_prop; uint8_t num, type; struct dt_node *n; memset(sensors, -1, sizeof(sensors)); dt_for_each_compatible(dt_root, n, "ibm,ipmi-sensor") { type_prop = dt_find_property(n, "ipmi-sensor-type"); if (!type_prop) { prerror("IPMI: sensor doesn't have ipmi-sensor-type\n"); continue; } num_prop = dt_find_property(n, "reg"); if (!num_prop) { prerror("IPMI: sensor doesn't have reg property\n"); continue; } num = (uint8_t)dt_property_get_cell(num_prop, 0); type = (uint8_t)dt_property_get_cell(type_prop, 0); sensors[type] = num; } } skiboot-skiboot-5.1.13/hw/ipmi/ipmi-watchdog.c000066400000000000000000000076641265204436200212200ustar00rootroot00000000000000 /* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #define TIMER_USE_DONT_LOG 0x80 #define TIMER_USE_DONT_STOP 0x40 #define TIMER_USE_POST 0x02 /* WDT expiration actions */ #define WDT_PRETIMEOUT_SMI 0x10 #define WDT_POWER_CYCLE_ACTION 0x01 #define WDT_NO_ACTION 0x00 /* How long to set the overall watchdog timeout for. In units of * 100ms. If the timer is not reset within this time the watchdog * expiration action will occur. */ #define WDT_TIMEOUT 600 /* How often to reset the timer using schedule_timer(). Too short and we risk accidentally resetting the system due to opal_run_pollers() not being called in time, too short and we waste time resetting the wdt more frequently than necessary. */ #define WDT_MARGIN 300 static struct timer wdt_timer; static bool wdt_stopped = false; static void ipmi_wdt_complete(struct ipmi_msg *msg) { if (msg->cmd == IPMI_CMD(IPMI_RESET_WDT) && !msg->user_data) schedule_timer(&wdt_timer, msecs_to_tb( (WDT_TIMEOUT - WDT_MARGIN)*100)); ipmi_free_msg(msg); } static void set_wdt(uint8_t action, uint16_t count, uint8_t pretimeout) { struct ipmi_msg *ipmi_msg; ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_SET_WDT, ipmi_wdt_complete, NULL, NULL, 6, 0); if (!ipmi_msg) { prerror("Unable to allocate set wdt message\n"); return; } ipmi_msg->error = ipmi_wdt_complete; ipmi_msg->data[0] = TIMER_USE_POST | TIMER_USE_DONT_LOG; /* Timer Use */ ipmi_msg->data[1] = action; /* Timer Actions */ ipmi_msg->data[2] = pretimeout; /* Pre-timeout Interval */ ipmi_msg->data[3] = 0; /* Timer Use Flags */ ipmi_msg->data[4] = count & 0xff; /* Initial countdown (lsb) */ ipmi_msg->data[5] = (count >> 8) & 0xff; /* Initial countdown (msb) */ ipmi_queue_msg(ipmi_msg); } static struct ipmi_msg *wdt_reset_mkmsg(void) { struct ipmi_msg *ipmi_msg; ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_RESET_WDT, ipmi_wdt_complete, NULL, NULL, 0, 0); if (!ipmi_msg) { prerror("Unable to allocate reset wdt message\n"); return NULL; } return ipmi_msg; } static void sync_reset_wdt(void) { struct ipmi_msg *ipmi_msg; if ((ipmi_msg = wdt_reset_mkmsg())) ipmi_queue_msg_sync(ipmi_msg); } static void reset_wdt(struct timer *t __unused, void *data __unused, uint64_t now __unused) { struct ipmi_msg *ipmi_msg; if ((ipmi_msg = wdt_reset_mkmsg())) ipmi_queue_msg_head(ipmi_msg); } void ipmi_wdt_stop(void) { if (!wdt_stopped) { wdt_stopped = true; set_wdt(WDT_NO_ACTION, 100, 0); } } void ipmi_wdt_final_reset(void) { /* todo: this is disabled while we're waiting on fixed watchdog * behaviour */ #if 0 set_wdt(WDT_POWER_CYCLE_ACTION | WDT_PRETIMEOUT_SMI, WDT_TIMEOUT, WDT_MARGIN/10); reset_wdt(NULL, (void *) 1); #endif set_wdt(WDT_NO_ACTION, 100, 0); ipmi_set_boot_count(); cancel_timer(&wdt_timer); } void ipmi_wdt_init(void) { init_timer(&wdt_timer, reset_wdt, NULL); set_wdt(WDT_POWER_CYCLE_ACTION, WDT_TIMEOUT, 0); /* Start the WDT. We do it synchronously to make sure it has * started before skiboot continues booting. Otherwise we * could crash before the wdt has actually been started. */ sync_reset_wdt(); /* For some reason we have to reset it twice to get it to * actually start the first time. */ sync_reset_wdt(); return; } skiboot-skiboot-5.1.13/hw/ipmi/test/000077500000000000000000000000001265204436200172625ustar00rootroot00000000000000skiboot-skiboot-5.1.13/hw/ipmi/test/Makefile.check000066400000000000000000000015271265204436200220030ustar00rootroot00000000000000# -*-Makefile-*- IPMI_TEST := hw/ipmi/test/run-fru LCOV_EXCLUDE += $(IPMI_TEST:%=%.c) check: $(IPMI_TEST:%=%-check) $(IPMI_TEST:%=%-gcov-run) coverage: $(IPMI_TEST:%=%-gcov-run) $(IPMI_TEST:%=%-gcov-run) : %-run: % $(call Q, TEST-COVERAGE ,$< , $<) $(IPMI_TEST:%=%-check) : %-check: % $(call Q, RUN-TEST ,$(VALGRIND) $<, $<) $(IPMI_TEST) : % : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -o $@ $<, $<) $(IPMI_TEST:%=%-gcov): %-gcov : %.c % $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -O0 -g -I include -I . -I libfdt -lgcov -o $@ $<, $<) $(IPMI_TEST:%=%-gcov): % : $(%.d:-gcov=) -include $(wildcard hw/ipmi/test/*.d) clean: ipmi-test-clean ipmi-test-clean: $(RM) -f hw/ipmi/test/*.[od] $(IPMI_TEST) $(IPMI_TEST:%=%-gcov) $(RM) -f *.gcda *.gcno skiboot.info $(RM) -rf coverage-report skiboot-skiboot-5.1.13/hw/ipmi/test/run-fru.c000066400000000000000000000056761265204436200210420ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "../ipmi-fru.c" int error = 0; const char version[] = "a-too-long-version-test-string-is-here"; void ipmi_free_msg(struct ipmi_msg __unused *msg) { } void ipmi_init_msg(struct ipmi_msg __unused *msg, int __unused interface, uint32_t __unused code, void __unused (*complete)(struct ipmi_msg *), void __unused *user_data, size_t __unused req_size, size_t __unused resp_size) { } struct ipmi_msg *ipmi_mkmsg(int __unused interface, uint32_t __unused code, void __unused (*complete)(struct ipmi_msg *), void __unused *user_data, void __unused *req_data, size_t __unused req_size, size_t __unused resp_size) { return NULL; } int ipmi_queue_msg(struct ipmi_msg __unused *msg) { return 0; } void _prlog(int __unused log_level, const __unused char* fmt, ...) { return; } int main(void) { u8 *buf; int len; struct product_info info = { .manufacturer = (char *) "IBM", .product = (char *) "skiboot", .part_no = (char *) "hello", .version = (char *) "12345", .serial_no = (char *) "12345", .asset_tag = (char *) "abcd", }; struct product_info invalid_info = { .manufacturer = (char *) "I", .product = (char *) "skiboot", .part_no = (char *) "hello", .version = (char *) "12345", .serial_no = (char *) "12345", .asset_tag = (char *) "abcd", }; struct product_info invalid_info2 = { .manufacturer = (char *) "IBM", .product = (char *) "skiboot", .part_no = (char *) "this is a really long string that's more" "than 32 characters, because it turns out that's invalid.", .version = (char *) "12345", .serial_no = (char *) "12345", .asset_tag = (char *) "abcd", }; buf = malloc(256); len = fru_fill_product_info(buf, &info, 40); assert(len > 0); /* Make sure the checksum is right */ assert(!fru_checksum(buf, len)); /* This should fail (not enough space) */ assert(fru_fill_product_info(buf, &info, 39) < 0); memset(buf, 0, 256); len = fru_fill_product_info(buf, &invalid_info, 40); assert(len == OPAL_PARAMETER); memset(buf, 0, 256); len = fru_fill_product_info(buf, &invalid_info2, 256); assert(len == OPAL_PARAMETER); memset(buf, 0, 256); assert(fru_add(buf, 256) > 0); memset(buf, 0, 256); assert(fru_add(buf, 1) == OPAL_PARAMETER); memset(buf, 0, 256); assert(fru_add(buf, 65) == OPAL_PARAMETER); free(buf); return 0; } skiboot-skiboot-5.1.13/hw/lpc-rtc.c000066400000000000000000000123441265204436200170610ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include /* Legacy RTC registers */ #define RTC_REG_SECONDS 0 #define RTC_REG_MINUTES 2 #define RTC_REG_HOURS 4 #define RTC_REG_DAY_OF_WEEK 6 #define RTC_REG_DAY_OF_MONTH 7 #define RTC_REG_MONTH 8 #define RTC_REG_YEAR 9 #define RTC_REG_A 10 #define RTC_REG_A_UIP 0x80 #define RTC_REG_B 11 #define RTC_REG_B_DIS_UPD 0x80 #define RTC_REG_B_PIE 0x40 #define RTC_REG_B_AIE 0x20 #define RTC_REG_B_UIE 0x10 #define RTC_REG_B_SQWE 0x08 #define RTC_REG_B_DM_BINARY 0x04 #define RTC_REG_B_24H 0x02 #define RTC_REG_B_DST_EN 0x01 #define RTC_REG_C 12 #define RTC_REG_D 13 #define RTC_REG_D_VALID 0x80 /* Init value is no interrupts, 24H mode, updates enabled */ #define RTC_REG_B_INIT (RTC_REG_B_24H) static u32 rtc_port; static struct lock rtc_lock = LOCK_UNLOCKED; static uint8_t rtc_read(uint8_t reg) { lpc_outb(reg, rtc_port); return lpc_inb(rtc_port + 1); } static void rtc_write(uint8_t reg, uint8_t val) { lpc_outb(reg, rtc_port); lpc_outb(val, rtc_port + 1); } static bool lpc_rtc_read_tm(struct tm *tm) { struct tm tm2; unsigned int loops = 0; /* Read until two series provide identical values, this * should deal with update races in all practical cases */ for (;;) { tm2 = *tm; tm->tm_sec = rtc_read(RTC_REG_SECONDS); tm->tm_min = rtc_read(RTC_REG_MINUTES); tm->tm_hour = rtc_read(RTC_REG_HOURS); tm->tm_mday = rtc_read(RTC_REG_DAY_OF_MONTH); tm->tm_mon = rtc_read(RTC_REG_MONTH); tm->tm_year = rtc_read(RTC_REG_YEAR); if (loops > 0 && memcmp(&tm2, tm, sizeof(struct tm)) == 0) break; loops++; if (loops > 10) { prerror("RTC: Failed to obtain stable values\n"); return false; } } tm->tm_sec = bcd_byte(tm->tm_sec, 0); tm->tm_min = bcd_byte(tm->tm_min, 0); tm->tm_hour = bcd_byte(tm->tm_hour, 0); tm->tm_mday = bcd_byte(tm->tm_mday, 0); tm->tm_mon = bcd_byte(tm->tm_mon, 0) - 1; tm->tm_year = bcd_byte(tm->tm_year, 0); /* 2000 wrap */ if (tm->tm_year < 69) tm->tm_year += 100; /* Base */ tm->tm_year += 1900; return true; } static void lpc_rtc_write_tm(struct tm *tm __unused) { /* XXX */ } static void lpc_init_time(void) { uint8_t val; struct tm tm; bool valid; lock(&rtc_lock); /* If update is in progress, wait a bit */ val = rtc_read(RTC_REG_A); if (val & RTC_REG_A_UIP) time_wait_ms(10); /* Read from RTC */ valid = lpc_rtc_read_tm(&tm); unlock(&rtc_lock); /* Update cache */ if (valid) rtc_cache_update(&tm); } static void lpc_init_hw(void) { lock(&rtc_lock); /* Set REG B to a suitable default */ rtc_write(RTC_REG_B, RTC_REG_B_INIT); unlock(&rtc_lock); } static int64_t lpc_opal_rtc_read(uint32_t *y_m_d, uint64_t *h_m_s_m) { uint8_t val; int64_t rc = OPAL_SUCCESS; struct tm tm; if (!y_m_d || !h_m_s_m) return OPAL_PARAMETER; /* Return busy if updating. This is somewhat racy, but will * do for now, most RTCs nowadays are smart enough to atomically * update. Alternatively we could just read from the cache... */ lock(&rtc_lock); val = rtc_read(RTC_REG_A); if (val & RTC_REG_A_UIP) { unlock(&rtc_lock); return OPAL_BUSY_EVENT; } /* Read from RTC */ if (lpc_rtc_read_tm(&tm)) rc = OPAL_SUCCESS; else rc = OPAL_HARDWARE; unlock(&rtc_lock); if (rc == OPAL_SUCCESS) { /* Update cache */ rtc_cache_update(&tm); /* Convert to OPAL time */ tm_to_datetime(&tm, y_m_d, h_m_s_m); } return rc; } static int64_t lpc_opal_rtc_write(uint32_t year_month_day, uint64_t hour_minute_second_millisecond) { struct tm tm; /* Convert to struct tm */ datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm); /* Write it out */ lock(&rtc_lock); lpc_rtc_write_tm(&tm); unlock(&rtc_lock); return OPAL_SUCCESS; } void lpc_rtc_init(void) { struct dt_node *rtc_node, *np; if (!lpc_present()) return; /* We support only one */ rtc_node = dt_find_compatible_node(dt_root, NULL, "pnpPNP,b00"); if (!rtc_node) return; /* Get IO base */ rtc_port = dt_prop_get_cell_def(rtc_node, "reg", 1, 0); if (!rtc_port) { prerror("RTC: Can't find reg property\n"); return; } if (dt_prop_get_cell_def(rtc_node, "reg", 0, 0) != OPAL_LPC_IO) { prerror("RTC: Unsupported address type\n"); return; } /* Init the HW */ lpc_init_hw(); /* Create OPAL API node and register OPAL calls */ np = dt_new(opal_node, "rtc"); dt_add_property_strings(np, "compatible", "ibm,opal-rtc"); opal_register(OPAL_RTC_READ, lpc_opal_rtc_read, 2); opal_register(OPAL_RTC_WRITE, lpc_opal_rtc_write, 2); /* Initialise the rtc cache */ lpc_init_time(); } skiboot-skiboot-5.1.13/hw/lpc-uart.c000066400000000000000000000310231265204436200172370ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include DEFINE_LOG_ENTRY(OPAL_RC_UART_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_UART, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); /* UART reg defs */ #define REG_RBR 0 #define REG_THR 0 #define REG_DLL 0 #define REG_IER 1 #define REG_DLM 1 #define REG_FCR 2 #define REG_IIR 2 #define REG_LCR 3 #define REG_MCR 4 #define REG_LSR 5 #define REG_MSR 6 #define REG_SCR 7 #define LSR_DR 0x01 /* Data ready */ #define LSR_OE 0x02 /* Overrun */ #define LSR_PE 0x04 /* Parity error */ #define LSR_FE 0x08 /* Framing error */ #define LSR_BI 0x10 /* Break */ #define LSR_THRE 0x20 /* Xmit holding register empty */ #define LSR_TEMT 0x40 /* Xmitter empty */ #define LSR_ERR 0x80 /* Error */ #define LCR_DLAB 0x80 /* DLL access */ #define IER_RX 0x01 #define IER_THRE 0x02 #define IER_ALL 0x0f static struct lock uart_lock = LOCK_UNLOCKED; static struct dt_node *uart_node; static uint32_t uart_base; static bool has_irq, irq_ok, rx_full, tx_full; static uint8_t tx_room; static uint8_t cached_ier; static void uart_trace(u8 ctx, u8 cnt, u8 irq_state, u8 in_count) { union trace t; t.uart.ctx = ctx; t.uart.cnt = cnt; t.uart.irq_state = irq_state; t.uart.in_count = in_count; trace_add(&t, TRACE_UART, sizeof(struct trace_uart)); } static inline uint8_t uart_read(unsigned int reg) { return lpc_inb(uart_base + reg); } static inline void uart_write(unsigned int reg, uint8_t val) { lpc_outb(val, uart_base + reg); } static void uart_check_tx_room(void) { if (uart_read(REG_LSR) & LSR_THRE) { /* FIFO is 16 entries */ tx_room = 16; tx_full = false; } } static void uart_wait_tx_room(void) { while(!tx_room) { uart_check_tx_room(); if (!tx_room) cpu_relax(); } } static void uart_update_ier(void) { uint8_t ier = 0; if (!has_irq) return; /* If we have never got an interrupt, enable them all, * the first interrupt received will tell us if interrupts * are functional (some boards are missing an EC or FPGA * programming causing LPC interrupts not to work). */ if (!irq_ok) ier = IER_ALL; if (!rx_full) ier |= IER_RX; if (tx_full) ier |= IER_THRE; if (ier != cached_ier) { uart_write(REG_IER, ier); cached_ier = ier; } } /* * Internal console driver (output only) */ static size_t uart_con_write(const char *buf, size_t len) { size_t written = 0; /* If LPC bus is bad, we just swallow data */ if (!lpc_ok()) return written; lock(&uart_lock); while(written < len) { if (tx_room == 0) { uart_wait_tx_room(); if (tx_room == 0) goto bail; } else { uart_write(REG_THR, buf[written++]); tx_room--; } } bail: unlock(&uart_lock); return written; } static int64_t uart_con_flush(void); static struct con_ops uart_con_driver = { .write = uart_con_write, .flush = uart_con_flush }; /* * OPAL console driver */ /* * We implement a simple buffer to buffer input data as some bugs in * Linux make it fail to read fast enough after we get an interrupt. * * We use it on non-interrupt operations as well while at it because * it doesn't cost us much and might help in a few cases where Linux * is calling opal_poll_events() but not actually reading. * * Most of the time I expect we'll flush it completely to Linux into * it's tty flip buffers so I don't bother with a ring buffer. */ #define IN_BUF_SIZE 0x1000 static uint8_t *in_buf; static uint32_t in_count; /* * We implement a ring buffer for output data as well to speed things * up a bit. This allows us to have interrupt driven sends. This is only * for the output data coming from the OPAL API, not the internal one * which is already bufferred. */ #define OUT_BUF_SIZE 0x1000 static uint8_t *out_buf; static uint32_t out_buf_prod; static uint32_t out_buf_cons; /* Asynchronous flush */ static int64_t uart_con_flush(void) { bool tx_was_full = tx_full; uint32_t out_buf_cons_initial = out_buf_cons; while(out_buf_prod != out_buf_cons) { if (tx_room == 0) { /* * If the interrupt is not functional, * we force a full synchronous flush, * otherwise the Linux console isn't * usable (too slow). */ if (irq_ok) uart_check_tx_room(); else uart_wait_tx_room(); } if (tx_room == 0) { tx_full = true; break; } uart_write(REG_THR, out_buf[out_buf_cons++]); out_buf_cons %= OUT_BUF_SIZE; tx_room--; } if (tx_full != tx_was_full) uart_update_ier(); if (out_buf_prod != out_buf_cons) { /* Return busy if nothing was flushed this call */ if (out_buf_cons == out_buf_cons_initial) return OPAL_BUSY; /* Return partial if there's more to flush */ return OPAL_PARTIAL; } return OPAL_SUCCESS; } static uint32_t uart_tx_buf_space(void) { return OUT_BUF_SIZE - 1 - (out_buf_prod + OUT_BUF_SIZE - out_buf_cons) % OUT_BUF_SIZE; } static int64_t uart_opal_write(int64_t term_number, int64_t *length, const uint8_t *buffer) { size_t written = 0, len = *length; if (term_number != 0) return OPAL_PARAMETER; lock(&uart_lock); /* Copy data to out buffer */ while (uart_tx_buf_space() && len--) { out_buf[out_buf_prod++] = *(buffer++); out_buf_prod %= OUT_BUF_SIZE; written++; } /* Flush out buffer again */ uart_con_flush(); unlock(&uart_lock); *length = written; return OPAL_SUCCESS; } static int64_t uart_opal_write_buffer_space(int64_t term_number, int64_t *length) { if (term_number != 0) return OPAL_PARAMETER; lock(&uart_lock); *length = uart_tx_buf_space(); unlock(&uart_lock); return OPAL_SUCCESS; } /* Must be called with UART lock held */ static void uart_read_to_buffer(void) { /* As long as there is room in the buffer */ while(in_count < IN_BUF_SIZE) { /* Read status register */ uint8_t lsr = uart_read(REG_LSR); /* Nothing to read ... */ if ((lsr & LSR_DR) == 0) break; /* Read and add to buffer */ in_buf[in_count++] = uart_read(REG_RBR); } /* If the buffer is full disable the interrupt */ rx_full = (in_count == IN_BUF_SIZE); uart_update_ier(); } static void uart_adjust_opal_event(void) { if (in_count) opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, OPAL_EVENT_CONSOLE_INPUT); else opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT, 0); } /* This is called with the console lock held */ static int64_t uart_opal_read(int64_t term_number, int64_t *length, uint8_t *buffer) { size_t req_count = *length, read_cnt = 0; uint8_t lsr = 0; if (term_number != 0) return OPAL_PARAMETER; if (!in_buf) return OPAL_INTERNAL_ERROR; lock(&uart_lock); /* Read from buffer first */ if (in_count) { read_cnt = in_count; if (req_count < read_cnt) read_cnt = req_count; memcpy(buffer, in_buf, read_cnt); req_count -= read_cnt; if (in_count != read_cnt) memmove(in_buf, in_buf + read_cnt, in_count - read_cnt); in_count -= read_cnt; } /* * If there's still room in the user buffer, read from the UART * directly */ while(req_count) { lsr = uart_read(REG_LSR); if ((lsr & LSR_DR) == 0) break; buffer[read_cnt++] = uart_read(REG_RBR); req_count--; } /* Finally, flush whatever's left in the UART into our buffer */ uart_read_to_buffer(); uart_trace(TRACE_UART_CTX_READ, read_cnt, tx_full, in_count); unlock(&uart_lock); /* Adjust the OPAL event */ uart_adjust_opal_event(); *length = read_cnt; return OPAL_SUCCESS; } static void __uart_do_poll(u8 trace_ctx) { if (!in_buf) return; lock(&uart_lock); uart_read_to_buffer(); uart_con_flush(); uart_trace(trace_ctx, 0, tx_full, in_count); unlock(&uart_lock); uart_adjust_opal_event(); } static void uart_console_poll(void *data __unused) { __uart_do_poll(TRACE_UART_CTX_POLL); } static void uart_irq(uint32_t chip_id __unused, uint32_t irq_mask __unused) { if (!irq_ok) { prlog(PR_DEBUG, "UART: IRQ functional !\n"); irq_ok = true; } __uart_do_poll(TRACE_UART_CTX_IRQ); } /* * Common setup/inits */ void uart_setup_linux_passthrough(void) { char *path; dt_add_property_strings(uart_node, "status", "ok"); path = dt_get_path(uart_node); dt_add_property_string(dt_chosen, "linux,stdout-path", path); free(path); prlog(PR_DEBUG, "UART: Enabled as OS pass-through\n"); } void uart_setup_opal_console(void) { struct dt_node *con, *consoles; /* Create OPAL console node */ consoles = dt_new(opal_node, "consoles"); assert(consoles); dt_add_property_cells(consoles, "#address-cells", 1); dt_add_property_cells(consoles, "#size-cells", 0); con = dt_new_addr(consoles, "serial", 0); assert(con); dt_add_property_string(con, "compatible", "ibm,opal-console-raw"); dt_add_property_cells(con, "#write-buffer-size", INMEM_CON_OUT_LEN); dt_add_property_cells(con, "reg", 0); dt_add_property_string(con, "device_type", "serial"); dt_add_property_string(dt_chosen, "linux,stdout-path", "/ibm,opal/consoles/serial@0"); /* * We mark the UART as reserved since we don't want the * kernel to start using it with its own 8250 driver */ dt_add_property_strings(uart_node, "status", "reserved"); /* * If the interrupt is enabled, turn on RX interrupts (and * only these for now */ tx_full = rx_full = false; uart_update_ier(); /* Allocate an input buffer */ in_buf = zalloc(IN_BUF_SIZE); out_buf = zalloc(OUT_BUF_SIZE); prlog(PR_DEBUG, "UART: Enabled as OS console\n"); /* Register OPAL APIs */ opal_register(OPAL_CONSOLE_READ, uart_opal_read, 3); opal_register(OPAL_CONSOLE_WRITE_BUFFER_SPACE, uart_opal_write_buffer_space, 2); opal_register(OPAL_CONSOLE_WRITE, uart_opal_write, 3); opal_add_poller(uart_console_poll, NULL); } static bool uart_init_hw(unsigned int speed, unsigned int clock) { unsigned int dll = (clock / 16) / speed; /* Clear line control */ uart_write(REG_LCR, 0x00); /* Check if the UART responds */ uart_write(REG_IER, 0x01); if (uart_read(REG_IER) != 0x01) goto detect_fail; uart_write(REG_IER, 0x00); if (uart_read(REG_IER) != 0x00) goto detect_fail; uart_write(REG_LCR, LCR_DLAB); uart_write(REG_DLL, dll & 0xff); uart_write(REG_DLM, dll >> 8); uart_write(REG_LCR, 0x03); /* 8N1 */ uart_write(REG_MCR, 0x03); /* RTS/DTR */ uart_write(REG_FCR, 0x07); /* clear & en. fifos */ return true; detect_fail: prerror("UART: Presence detect failed !\n"); return false; } static struct lpc_client uart_lpc_client = { .interrupt = uart_irq, }; void uart_init(bool use_interrupt) { const struct dt_property *prop; struct dt_node *n; char *path __unused; uint32_t chip_id, irq; if (!lpc_present()) return; /* UART lock is in the console path and thus must block * printf re-entrancy */ uart_lock.in_con_path = true; /* We support only one */ uart_node = n = dt_find_compatible_node(dt_root, NULL, "ns16550"); if (!n) return; /* Get IO base */ prop = dt_find_property(n, "reg"); if (!prop) { log_simple_error(&e_info(OPAL_RC_UART_INIT), "UART: Can't find reg property\n"); return; } if (dt_property_get_cell(prop, 0) != OPAL_LPC_IO) { log_simple_error(&e_info(OPAL_RC_UART_INIT), "UART: Only supports IO addresses\n"); return; } uart_base = dt_property_get_cell(prop, 1); if (!uart_init_hw(dt_prop_get_u32(n, "current-speed"), dt_prop_get_u32(n, "clock-frequency"))) { prerror("UART: Initialization failed\n"); dt_add_property_strings(n, "status", "bad"); return; } chip_id = dt_get_chip_id(uart_node); /* * Mark LPC used by the console (will mark the relevant * locks to avoid deadlocks when flushing the console) */ lpc_used_by_console(); /* Install console backend for printf() */ set_console(&uart_con_driver); /* On Naples, use the SerIRQ, which Linux will have to share with * OPAL as we don't really play the cascaded interrupt game at this * point... */ if (use_interrupt) { irq = dt_prop_get_u32(n, "interrupts"); uart_lpc_client.interrupts = LPC_IRQ(irq); lpc_register_client(chip_id, &uart_lpc_client); has_irq = true; prlog(PR_DEBUG, "UART: Using LPC IRQ %d\n", irq); } else has_irq = false; } skiboot-skiboot-5.1.13/hw/lpc.c000066400000000000000000000515361265204436200163010ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include //#define DBG_IRQ(fmt...) prerror(fmt) #define DBG_IRQ(fmt...) do { } while(0) DEFINE_LOG_ENTRY(OPAL_RC_LPC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_LPC, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LPC_WRITE, OPAL_PLATFORM_ERR_EVT, OPAL_LPC, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); #define ECCB_CTL 0 /* b0020 -> b00200 */ #define ECCB_STAT 2 /* b0022 -> b00210 */ #define ECCB_DATA 3 /* b0023 -> b00218 */ #define ECCB_CTL_MAGIC 0xd000000000000000ul #define ECCB_CTL_DATASZ PPC_BITMASK(4,7) #define ECCB_CTL_READ PPC_BIT(15) #define ECCB_CTL_ADDRLEN PPC_BITMASK(23,25) #define ECCB_ADDRLEN_4B 0x4 #define ECCB_CTL_ADDR PPC_BITMASK(32,63) #define ECCB_STAT_PIB_ERR PPC_BITMASK(0,5) #define ECCB_STAT_RD_DATA PPC_BITMASK(6,37) #define ECCB_STAT_BUSY PPC_BIT(44) #define ECCB_STAT_ERRORS1 PPC_BITMASK(45,51) #define ECCB_STAT_OP_DONE PPC_BIT(52) #define ECCB_STAT_ERRORS2 PPC_BITMASK(53,55) #define ECCB_STAT_ERR_MASK (ECCB_STAT_PIB_ERR | \ ECCB_STAT_ERRORS1 | \ ECCB_STAT_ERRORS2) #define ECCB_TIMEOUT 1000000 /* OPB Master LS registers */ #define OPB_MASTER_LS_IRQ_STAT 0x50 #define OPB_MASTER_LS_IRQ_MASK 0x54 #define OPB_MASTER_LS_IRQ_POL 0x58 #define OPB_MASTER_IRQ_LPC 0x00000800 /* LPC HC registers */ #define LPC_HC_FW_SEG_IDSEL 0x24 #define LPC_HC_FW_RD_ACC_SIZE 0x28 #define LPC_HC_FW_RD_1B 0x00000000 #define LPC_HC_FW_RD_2B 0x01000000 #define LPC_HC_FW_RD_4B 0x02000000 #define LPC_HC_FW_RD_16B 0x04000000 #define LPC_HC_FW_RD_128B 0x07000000 #define LPC_HC_IRQSER_CTRL 0x30 #define LPC_HC_IRQSER_EN 0x80000000 #define LPC_HC_IRQSER_QMODE 0x40000000 #define LPC_HC_IRQSER_START_MASK 0x03000000 #define LPC_HC_IRQSER_START_4CLK 0x00000000 #define LPC_HC_IRQSER_START_6CLK 0x01000000 #define LPC_HC_IRQSER_START_8CLK 0x02000000 #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ #define LPC_HC_IRQSTAT 0x38 #define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ #define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ #define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 #define LPC_HC_IRQ_LRESET 0x00000400 #define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 #define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 #define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 #define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 #define LPC_HC_IRQ_TARG_TAR_ERR 0x00000008 #define LPC_HC_IRQ_BM_TAR_ERR 0x00000004 #define LPC_HC_IRQ_BM0_REQ 0x00000002 #define LPC_HC_IRQ_BM1_REQ 0x00000001 #define LPC_HC_IRQ_BASE_IRQS ( \ LPC_HC_IRQ_LRESET | \ LPC_HC_IRQ_SYNC_ABNORM_ERR | \ LPC_HC_IRQ_SYNC_NORESP_ERR | \ LPC_HC_IRQ_SYNC_NORM_ERR | \ LPC_HC_IRQ_SYNC_TIMEOUT_ERR | \ LPC_HC_IRQ_TARG_TAR_ERR | \ LPC_HC_IRQ_BM_TAR_ERR) #define LPC_HC_ERROR_ADDRESS 0x40 struct lpc_client_entry { struct list_node node; const struct lpc_client *clt; }; /* Default LPC bus */ static int32_t lpc_default_chip_id = -1; /* * These are expected to be the same on all chips and should probably * be read (or configured) dynamically. This is how things are configured * today on Tuletta. */ static uint32_t lpc_io_opb_base = 0xd0010000; static uint32_t lpc_mem_opb_base = 0xe0000000; static uint32_t lpc_fw_opb_base = 0xf0000000; static uint32_t lpc_reg_opb_base = 0xc0012000; static uint32_t opb_master_reg_base = 0xc0010000; static int64_t opb_write(struct proc_chip *chip, uint32_t addr, uint32_t data, uint32_t sz) { uint64_t ctl = ECCB_CTL_MAGIC, stat; int64_t rc, tout; uint64_t data_reg; switch(sz) { case 1: data_reg = ((uint64_t)data) << 56; break; case 2: data_reg = ((uint64_t)data) << 48; break; case 4: data_reg = ((uint64_t)data) << 32; break; default: prerror("LPC: Invalid data size %d\n", sz); return OPAL_PARAMETER; } rc = xscom_write(chip->id, chip->lpc_xbase + ECCB_DATA, data_reg); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: XSCOM write to ECCB DATA error %lld\n", rc); return rc; } ctl = SETFIELD(ECCB_CTL_DATASZ, ctl, sz); ctl = SETFIELD(ECCB_CTL_ADDRLEN, ctl, ECCB_ADDRLEN_4B); ctl = SETFIELD(ECCB_CTL_ADDR, ctl, addr); rc = xscom_write(chip->id, chip->lpc_xbase + ECCB_CTL, ctl); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: XSCOM write to ECCB CTL error %lld\n", rc); return rc; } for (tout = 0; tout < ECCB_TIMEOUT; tout++) { rc = xscom_read(chip->id, chip->lpc_xbase + ECCB_STAT, &stat); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: XSCOM read from ECCB STAT err %lld\n", rc); return rc; } if (stat & ECCB_STAT_OP_DONE) { if (stat & ECCB_STAT_ERR_MASK) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: Error status: 0x%llx\n", stat); return OPAL_HARDWARE; } return OPAL_SUCCESS; } time_wait_nopoll(100); } log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: Write timeout !\n"); return OPAL_HARDWARE; } static int64_t opb_read(struct proc_chip *chip, uint32_t addr, uint32_t *data, uint32_t sz) { uint64_t ctl = ECCB_CTL_MAGIC | ECCB_CTL_READ, stat; int64_t rc, tout; if (sz != 1 && sz != 2 && sz != 4) { prerror("LPC: Invalid data size %d\n", sz); return OPAL_PARAMETER; } ctl = SETFIELD(ECCB_CTL_DATASZ, ctl, sz); ctl = SETFIELD(ECCB_CTL_ADDRLEN, ctl, ECCB_ADDRLEN_4B); ctl = SETFIELD(ECCB_CTL_ADDR, ctl, addr); rc = xscom_write(chip->id, chip->lpc_xbase + ECCB_CTL, ctl); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: XSCOM write to ECCB CTL error %lld\n", rc); return rc; } for (tout = 0; tout < ECCB_TIMEOUT; tout++) { rc = xscom_read(chip->id, chip->lpc_xbase + ECCB_STAT, &stat); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: XSCOM read from ECCB STAT err %lld\n", rc); return rc; } if (stat & ECCB_STAT_OP_DONE) { uint32_t rdata = GETFIELD(ECCB_STAT_RD_DATA, stat); if (stat & ECCB_STAT_ERR_MASK) { log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: Error status: 0x%llx\n", stat); return OPAL_HARDWARE; } switch(sz) { case 1: *data = rdata >> 24; break; case 2: *data = rdata >> 16; break; default: *data = rdata; break; } return 0; } time_wait_nopoll(100); } log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: Read timeout !\n"); return OPAL_HARDWARE; } static int64_t lpc_set_fw_idsel(struct proc_chip *chip, uint8_t idsel) { uint32_t val; int64_t rc; if (idsel == chip->lpc_fw_idsel) return OPAL_SUCCESS; if (idsel > 0xf) return OPAL_PARAMETER; rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_FW_SEG_IDSEL, &val, 4); if (rc) { prerror("LPC: Failed to read HC_FW_SEG_IDSEL register !\n"); return rc; } val = (val & 0xfffffff0) | idsel; rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_FW_SEG_IDSEL, val, 4); if (rc) { prerror("LPC: Failed to write HC_FW_SEG_IDSEL register !\n"); return rc; } chip->lpc_fw_idsel = idsel; return OPAL_SUCCESS; } static int64_t lpc_set_fw_rdsz(struct proc_chip *chip, uint8_t rdsz) { uint32_t val; int64_t rc; if (rdsz == chip->lpc_fw_rdsz) return OPAL_SUCCESS; switch(rdsz) { case 1: val = LPC_HC_FW_RD_1B; break; case 2: val = LPC_HC_FW_RD_2B; break; case 4: val = LPC_HC_FW_RD_4B; break; default: /* * The HW supports 16 and 128 via a buffer/cache * but I have never exprimented with it and am not * sure it works the way we expect so let's leave it * at that for now */ return OPAL_PARAMETER; } rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_FW_RD_ACC_SIZE, val, 4); if (rc) { prerror("LPC: Failed to write LPC_HC_FW_RD_ACC_SIZE !\n"); return rc; } chip->lpc_fw_rdsz = rdsz; return OPAL_SUCCESS; } static int64_t lpc_opb_prepare(struct proc_chip *chip, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t sz, uint32_t *opb_base, bool is_write) { uint32_t top = addr + sz; uint8_t fw_idsel; int64_t rc; /* Address wraparound */ if (top < addr) return OPAL_PARAMETER; /* * Bound check access and get the OPB base address for * the window corresponding to the access type */ switch(addr_type) { case OPAL_LPC_IO: /* IO space is 64K */ if (top > 0x10000) return OPAL_PARAMETER; /* And only supports byte accesses */ if (sz != 1) return OPAL_PARAMETER; *opb_base = lpc_io_opb_base; break; case OPAL_LPC_MEM: /* MEM space is 256M */ if (top > 0x10000000) return OPAL_PARAMETER; /* And only supports byte accesses */ if (sz != 1) return OPAL_PARAMETER; *opb_base = lpc_mem_opb_base; break; case OPAL_LPC_FW: /* * FW space is in segments of 256M controlled * by IDSEL, make sure we don't cross segments */ *opb_base = lpc_fw_opb_base; fw_idsel = (addr >> 28); if (((top - 1) >> 28) != fw_idsel) return OPAL_PARAMETER; /* Set segment */ rc = lpc_set_fw_idsel(chip, fw_idsel); if (rc) return rc; /* Set read access size */ if (!is_write) { rc = lpc_set_fw_rdsz(chip, sz); if (rc) return rc; } break; default: return OPAL_PARAMETER; } return OPAL_SUCCESS; } static int64_t __lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz) { struct proc_chip *chip = get_chip(chip_id); uint32_t opb_base; int64_t rc; if (!chip || !chip->lpc_xbase) return OPAL_PARAMETER; lock(&chip->lpc_lock); /* * Convert to an OPB access and handle LPC HC configuration * for FW accesses (IDSEL) */ rc = lpc_opb_prepare(chip, addr_type, addr, sz, &opb_base, true); if (rc) goto bail; /* Perform OPB access */ rc = opb_write(chip, opb_base + addr, data, sz); /* XXX Add LPC error handling/recovery */ bail: unlock(&chip->lpc_lock); return rc; } int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz) { if (lpc_default_chip_id < 0) return OPAL_PARAMETER; return __lpc_write(lpc_default_chip_id, addr_type, addr, data, sz); } /* * The "OPAL" variant add the emulation of 2 and 4 byte accesses using * byte accesses for IO and MEM space in order to be compatible with * existing Linux expectations */ static int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz) { int64_t rc; if (addr_type == OPAL_LPC_FW || sz == 1) return __lpc_write(chip_id, addr_type, addr, data, sz); while(sz--) { rc = __lpc_write(chip_id, addr_type, addr, data & 0xff, 1); if (rc) return rc; addr++; data >>= 8; } return OPAL_SUCCESS; } static int64_t __lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz) { struct proc_chip *chip = get_chip(chip_id); uint32_t opb_base; int64_t rc; if (!chip || !chip->lpc_xbase) return OPAL_PARAMETER; lock(&chip->lpc_lock); /* * Convert to an OPB access and handle LPC HC configuration * for FW accesses (IDSEL and read size) */ rc = lpc_opb_prepare(chip, addr_type, addr, sz, &opb_base, false); if (rc) goto bail; /* Perform OPB access */ rc = opb_read(chip, opb_base + addr, data, sz); /* XXX Add LPC error handling/recovery */ bail: unlock(&chip->lpc_lock); return rc; } int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz) { if (lpc_default_chip_id < 0) return OPAL_PARAMETER; return __lpc_read(lpc_default_chip_id, addr_type, addr, data, sz); } /* * The "OPAL" variant add the emulation of 2 and 4 byte accesses using * byte accesses for IO and MEM space in order to be compatible with * existing Linux expectations */ static int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz) { int64_t rc; if (addr_type == OPAL_LPC_FW || sz == 1) return __lpc_read(chip_id, addr_type, addr, data, sz); *data = 0; while(sz--) { uint32_t byte; rc = __lpc_read(chip_id, addr_type, addr, &byte, 1); if (rc) return rc; *data = *data | (byte << (8 * sz)); addr++; } return OPAL_SUCCESS; } bool lpc_present(void) { return lpc_default_chip_id >= 0; } /* Called with LPC lock held */ static void lpc_setup_serirq(struct proc_chip *chip) { struct lpc_client_entry *ent; uint32_t mask = LPC_HC_IRQ_BASE_IRQS; int rc; /* Collect serirq enable bits */ list_for_each(&chip->lpc_clients, ent, node) mask |= ent->clt->interrupts & LPC_HC_IRQ_SERIRQ_ALL; rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, mask, 4); if (rc) { prerror("LPC: Failed to update irq mask\n"); return; } DBG_IRQ("LPC: IRQ mask set to 0x%08x\n", mask); /* Enable the LPC interrupt in the OPB Master */ opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_POL, 0, 4); rc = opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_MASK, OPB_MASTER_IRQ_LPC, 4); if (rc) prerror("LPC: Failed to enable IRQs in OPB\n"); /* Check whether we should enable serirq */ if (mask & LPC_HC_IRQ_SERIRQ_ALL) { rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, LPC_HC_IRQSER_EN | LPC_HC_IRQSER_START_4CLK, 4); DBG_IRQ("LPC: SerIRQ enabled\n"); } else { rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, 0, 4); DBG_IRQ("LPC: SerIRQ disabled\n"); } if (rc) prerror("LPC: Failed to configure SerIRQ\n"); { u32 val; rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, &val, 4); DBG_IRQ("LPC: MASK READBACK=%x\n", val); rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, &val, 4); DBG_IRQ("LPC: CTRL READBACK=%x\n", val); } } static void lpc_init_interrupts(struct proc_chip *chip) { int rc; /* First mask them all */ rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4); if (rc) { prerror("LPC: Failed to init interrutps\n"); return; } switch(chip->type) { case PROC_CHIP_P8_MURANO: case PROC_CHIP_P8_VENICE: /* On Murano/Venice, there is no SerIRQ, only enable error * interrupts */ rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQMASK, LPC_HC_IRQ_BASE_IRQS, 4); if (rc) { prerror("LPC: Failed to set interrupt mask\n"); return; } opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, 0, 4); break; case PROC_CHIP_P8_NAPLES: /* On Naples, we support LPC interrupts, enable them based * on what clients requests. This will setup the mask and * enable processing */ lock(&chip->lpc_lock); lpc_setup_serirq(chip); unlock(&chip->lpc_lock); break; default: /* We aren't getting here, are we ? */ return; } } static void lpc_dispatch_reset(struct proc_chip *chip) { struct lpc_client_entry *ent; /* XXX We are going to hit this repeatedly while reset is * asserted which might be sub-optimal. We should instead * detect assertion and start a poller that will wait for * de-assertion. We could notify clients of LPC being * on/off rather than just reset */ prerror("LPC: Got LPC reset !\n"); /* Collect serirq enable bits */ list_for_each(&chip->lpc_clients, ent, node) { if (!ent->clt->reset) continue; unlock(&chip->lpc_lock); ent->clt->reset(chip->id); lock(&chip->lpc_lock); } /* Reconfigure serial interrupts */ if (chip->type == PROC_CHIP_P8_NAPLES) lpc_setup_serirq(chip); } static void lpc_dispatch_err_irqs(struct proc_chip *chip, uint32_t irqs) { int rc; uint32_t err_addr; /* Write back to clear error interrupts, we clear SerIRQ later * as they are handled as level interrupts */ rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSTAT, LPC_HC_IRQ_BASE_IRQS, 4); if (rc) prerror("LPC: Failed to clear IRQ error latches !\n"); if (irqs & LPC_HC_IRQ_LRESET) lpc_dispatch_reset(chip); if (irqs & LPC_HC_IRQ_SYNC_ABNORM_ERR) prerror("LPC: Got SYNC abnormal error\n"); if (irqs & LPC_HC_IRQ_SYNC_NORESP_ERR) prerror("LPC: Got SYNC no-response error\n"); if (irqs & LPC_HC_IRQ_SYNC_NORM_ERR) prerror("LPC: Got SYNC normal error\n"); if (irqs & LPC_HC_IRQ_SYNC_TIMEOUT_ERR) prerror("LPC: Got SYNC timeout error\n"); if (irqs & LPC_HC_IRQ_TARG_TAR_ERR) prerror("LPC: Got abnormal TAR error\n"); if (irqs & LPC_HC_IRQ_BM_TAR_ERR) prerror("LPC: Got bus master TAR error\n"); rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_ERROR_ADDRESS, &err_addr, 4); if (rc) prerror("LPC: Error reading error address register\n"); else prerror("LPC: Error address reg: 0x%08x\n", err_addr); } static void lpc_dispatch_ser_irqs(struct proc_chip *chip, uint32_t irqs, bool clear_latch) { struct lpc_client_entry *ent; uint32_t cirqs; int rc; irqs &= LPC_HC_IRQ_SERIRQ_ALL; /* Collect serirq enable bits */ list_for_each(&chip->lpc_clients, ent, node) { if (!ent->clt->interrupt) continue; cirqs = ent->clt->interrupts & irqs; if (cirqs) { unlock(&chip->lpc_lock); ent->clt->interrupt(chip->id, cirqs); lock(&chip->lpc_lock); } } /* Our SerIRQ are level sensitive, we clear the latch after * we call the handler. */ if (!clear_latch) return; rc = opb_write(chip, lpc_reg_opb_base + LPC_HC_IRQSTAT, irqs, 4); if (rc) prerror("LPC: Failed to clear SerIRQ latches !\n"); } void lpc_interrupt(uint32_t chip_id) { struct proc_chip *chip = get_chip(chip_id); uint32_t irqs, opb_irqs; int rc; /* No initialized LPC controller on that chip */ if (!chip->lpc_xbase) return; lock(&chip->lpc_lock); /* Grab OPB Master LS interrupt status */ rc = opb_read(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT, &opb_irqs, 4); if (rc) { prerror("LPC: Failed to read OPB IRQ state\n"); goto bail; } /* Check if it's an LPC interrupt */ if (!(opb_irqs & OPB_MASTER_IRQ_LPC)) { /* Something we don't support ? Ack it anyway... */ opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT, opb_irqs, 4); goto bail; } /* Handle the lpc interrupt source (errors etc...) */ rc = opb_read(chip, lpc_reg_opb_base + LPC_HC_IRQSTAT, &irqs, 4); if (rc) { prerror("LPC: Failed to read LPC IRQ state\n"); goto bail; } DBG_IRQ("LPC: IRQ on chip 0x%x, irqs=0x%08x\n", chip_id, irqs); /* Handle error interrupts */ if (irqs & LPC_HC_IRQ_BASE_IRQS) lpc_dispatch_err_irqs(chip, irqs); /* Handle SerIRQ interrupts */ if (irqs & LPC_HC_IRQ_SERIRQ_ALL) lpc_dispatch_ser_irqs(chip, irqs, true); /* Ack it at the OPB level */ opb_write(chip, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT, opb_irqs, 4); bail: unlock(&chip->lpc_lock); } void lpc_all_interrupts(uint32_t chip_id) { struct proc_chip *chip = get_chip(chip_id); /* Dispatch all */ lock(&chip->lpc_lock); lpc_dispatch_ser_irqs(chip, LPC_HC_IRQ_SERIRQ_ALL, false); unlock(&chip->lpc_lock); } void lpc_init(void) { struct dt_node *xn; bool has_lpc = false; dt_for_each_compatible(dt_root, xn, "ibm,power8-lpc") { uint32_t gcid = dt_get_chip_id(xn); struct proc_chip *chip; chip = get_chip(gcid); assert(chip); chip->lpc_xbase = dt_get_address(xn, 0, NULL); chip->lpc_fw_idsel = 0xff; chip->lpc_fw_rdsz = 0xff; init_lock(&chip->lpc_lock); if (lpc_default_chip_id < 0 || dt_has_node_property(xn, "primary", NULL)) { lpc_default_chip_id = chip->id; } printf("LPC: Bus on chip %d PCB_Addr=0x%x\n", chip->id, chip->lpc_xbase); has_lpc = true; lpc_init_interrupts(chip); if (chip->type == PROC_CHIP_P8_NAPLES) dt_add_property(xn, "interrupt-controller", NULL, 0); } if (lpc_default_chip_id >= 0) printf("LPC: Default bus on chip %d\n", lpc_default_chip_id); if (has_lpc) { opal_register(OPAL_LPC_WRITE, opal_lpc_write, 5); opal_register(OPAL_LPC_READ, opal_lpc_read, 5); } } void lpc_used_by_console(void) { struct proc_chip *chip; xscom_used_by_console(); for_each_chip(chip) { chip->lpc_lock.in_con_path = true; lock(&chip->lpc_lock); unlock(&chip->lpc_lock); } } bool lpc_ok(void) { struct proc_chip *chip; if (lpc_default_chip_id < 0) return false; if (!xscom_ok()) return false; chip = get_chip(lpc_default_chip_id); return !lock_held_by_me(&chip->lpc_lock); } void lpc_register_client(uint32_t chip_id, const struct lpc_client *clt) { struct lpc_client_entry *ent; struct proc_chip *chip; chip = get_chip(chip_id); assert(chip); ent = malloc(sizeof(*ent)); assert(ent); ent->clt = clt; lock(&chip->lpc_lock); list_add(&chip->lpc_clients, &ent->node); /* Re-evaluate ser irqs on Naples */ if (chip->type == PROC_CHIP_P8_NAPLES) lpc_setup_serirq(chip); unlock(&chip->lpc_lock); } skiboot-skiboot-5.1.13/hw/nx-842.c000066400000000000000000000125521265204436200164560ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* Configuration settings */ #define CFG_842_FC_ENABLE (0x1f) /* enable all 842 functions */ #define CFG_842_ENABLE (1) /* enable 842 engines */ #define DMA_COMPRESS_PREFETCH (1) /* enable prefetching (on P8) */ #define DMA_DECOMPRESS_PREFETCH (1) /* enable prefetching (on P8) */ #define DMA_COMPRESS_MAX_RR (15) /* range 1-15 */ #define DMA_DECOMPRESS_MAX_RR (15) /* range 1-15 */ #define DMA_SPBC (1) /* write SPBC in CPB */ #define DMA_CSB_WR NX_DMA_CSB_WR_CI #define DMA_COMPLETION_MODE NX_DMA_COMPLETION_MODE_CI #define DMA_CPB_WR NX_DMA_CPB_WR_CI_PAD #define DMA_OUTPUT_DATA_WR NX_DMA_OUTPUT_DATA_WR_CI #define EE_1 (1) /* enable engine 842 1 */ #define EE_0 (1) /* enable engine 842 0 */ static int nx_cfg_842(u32 gcid, u64 xcfg) { u64 cfg, ci, ct; int rc, instance = gcid + 1; BUILD_ASSERT(MAX_CHIPS < NX_842_CFG_CI_MAX); rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; ct = GETFIELD(NX_842_CFG_CT, cfg); if (!ct) prlog(PR_INFO, "NX%d: 842 CT set to %u\n", gcid, NX_CT_842); else if (ct == NX_CT_842) prlog(PR_INFO, "NX%d: 842 CT already set to %u\n", gcid, NX_CT_842); else prlog(PR_INFO, "NX%d: 842 CT already set to %u, " "changing to %u\n", gcid, (unsigned int)ct, NX_CT_842); ct = NX_CT_842; cfg = SETFIELD(NX_842_CFG_CT, cfg, ct); /* Coprocessor Instance must be shifted left. * See hw doc Section 5.5.1. */ ci = GETFIELD(NX_842_CFG_CI, cfg) >> NX_842_CFG_CI_LSHIFT; if (!ci) prlog(PR_INFO, "NX%d: 842 CI set to %d\n", gcid, instance); else if (ci == instance) prlog(PR_INFO, "NX%d: 842 CI already set to %u\n", gcid, (unsigned int)ci); else prlog(PR_INFO, "NX%d: 842 CI already set to %u, " "changing to %d\n", gcid, (unsigned int)ci, instance); ci = instance; cfg = SETFIELD(NX_842_CFG_CI, cfg, ci << NX_842_CFG_CI_LSHIFT); /* Enable all functions */ cfg = SETFIELD(NX_842_CFG_FC_ENABLE, cfg, CFG_842_FC_ENABLE); cfg = SETFIELD(NX_842_CFG_ENABLE, cfg, CFG_842_ENABLE); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: 842 CT %u CI %u config failure %d\n", gcid, (unsigned int)ct, (unsigned int)ci, rc); else prlog(PR_DEBUG, "NX%d: 842 Config 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } static int nx_cfg_dma(u32 gcid, u64 xcfg) { u64 cfg; int rc; rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; if (proc_gen == proc_gen_p8) { cfg = SETFIELD(NX_P8_DMA_CFG_842_COMPRESS_PREFETCH, cfg, DMA_COMPRESS_PREFETCH); cfg = SETFIELD(NX_P8_DMA_CFG_842_DECOMPRESS_PREFETCH, cfg, DMA_DECOMPRESS_PREFETCH); } cfg = SETFIELD(NX_DMA_CFG_842_COMPRESS_MAX_RR, cfg, DMA_COMPRESS_MAX_RR); cfg = SETFIELD(NX_DMA_CFG_842_DECOMPRESS_MAX_RR, cfg, DMA_DECOMPRESS_MAX_RR); cfg = SETFIELD(NX_DMA_CFG_842_SPBC, cfg, DMA_SPBC); cfg = SETFIELD(NX_DMA_CFG_842_CSB_WR, cfg, DMA_CSB_WR); cfg = SETFIELD(NX_DMA_CFG_842_COMPLETION_MODE, cfg, DMA_COMPLETION_MODE); cfg = SETFIELD(NX_DMA_CFG_842_CPB_WR, cfg, DMA_CPB_WR); cfg = SETFIELD(NX_DMA_CFG_842_OUTPUT_DATA_WR, cfg, DMA_OUTPUT_DATA_WR); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: DMA config failure %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: DMA 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } static int nx_cfg_ee(u32 gcid, u64 xcfg) { u64 cfg; int rc; rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; cfg = SETFIELD(NX_EE_CFG_CH1, cfg, EE_1); cfg = SETFIELD(NX_EE_CFG_CH0, cfg, EE_0); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: Engine Enable failure %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: Engine Enable 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } void nx_create_842_node(struct dt_node *node) { u32 gcid; u32 pb_base; u64 cfg_dma, cfg_842, cfg_ee; int rc; gcid = dt_get_chip_id(node); pb_base = dt_get_address(node, 0, NULL); prlog(PR_INFO, "NX%d: 842 at 0x%x\n", gcid, pb_base); if (dt_node_is_compatible(node, "ibm,power7-nx")) { cfg_dma = pb_base + NX_P7_DMA_CFG; cfg_842 = pb_base + NX_P7_842_CFG; cfg_ee = pb_base + NX_P7_EE_CFG; } else if (dt_node_is_compatible(node, "ibm,power8-nx")) { cfg_dma = pb_base + NX_P8_DMA_CFG; cfg_842 = pb_base + NX_P8_842_CFG; cfg_ee = pb_base + NX_P8_EE_CFG; } else { prerror("NX%d: ERROR: Unknown NX type!\n", gcid); return; } rc = nx_cfg_dma(gcid, cfg_dma); if (rc) return; rc = nx_cfg_842(gcid, cfg_842); if (rc) return; rc = nx_cfg_ee(gcid, cfg_ee); if (rc) return; prlog(PR_INFO, "NX%d: 842 Coprocessor Enabled\n", gcid); dt_add_property_cells(node, "ibm,842-coprocessor-type", NX_CT_842); dt_add_property_cells(node, "ibm,842-coprocessor-instance", gcid + 1); } skiboot-skiboot-5.1.13/hw/nx-crypto.c000066400000000000000000000202521265204436200174550ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include /* Configuration settings */ #define CFG_SYM_FC_ENABLE (0) /* disable all sym functions */ #define CFG_SYM_ENABLE (0) /* disable sym engines */ #define CFG_ASYM_FC_ENABLE (0) /* disable all asym functions */ #define CFG_ASYM_ENABLE (0) /* disable asym engines */ #define CFG_CRB_IQ_SYM (0) /* don't use any extra input queues */ #define CFG_CRB_IQ_ASYM (0) /* don't use any extra input queues */ #define AES_SHA_MAX_RR (1) /* valid range: 1-8 */ #define AES_SHA_CSB_WR NX_DMA_CSB_WR_PDMA #define AES_SHA_COMPLETION_MODE NX_DMA_COMPLETION_MODE_PDMA #define AES_SHA_CPB_WR NX_DMA_CPB_WR_DMA_NOPAD #define AES_SHA_OUTPUT_DATA_WR NX_DMA_OUTPUT_DATA_WR_DMA #define AMF_MAX_RR (1) /* valid range: 1-8 */ #define AMF_CSB_WR NX_DMA_CSB_WR_PDMA #define AMF_COMPLETION_MODE NX_DMA_COMPLETION_MODE_PDMA #define AMF_CPB_WR (0) /* CPB WR not done with AMF */ #define AMF_OUTPUT_DATA_WR NX_DMA_OUTPUT_DATA_WR_DMA #define EE_CH7 (0) /* disable engine AMF 2(P7) / 3(P8) */ #define EE_CH6 (0) /* disable engine AMF 1(P7) / 2(P8) */ #define EE_CH5 (0) /* disable engine AMF 0(P7) / 1(P8) */ #define EE_CH4 (0) /* disable engine SYM 2(P7) / AMF 0(P8) */ #define EE_CH3 (0) /* disable engine SYM 1 */ #define EE_CH2 (0) /* disable engine SYM 0 */ static int nx_cfg_sym(u32 gcid, u64 xcfg) { u64 cfg, ci, ct; int rc, instance = gcid + 1; BUILD_ASSERT(MAX_CHIPS < NX_SYM_CFG_CI_MAX); rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; ct = GETFIELD(NX_SYM_CFG_CT, cfg); if (!ct) prlog(PR_INFO, "NX%d: SYM CT set to %u\n", gcid, NX_CT_SYM); else if (ct == NX_CT_SYM) prlog(PR_INFO, "NX%d: SYM CT already set to %u\n", gcid, NX_CT_SYM); else prlog(PR_INFO, "NX%d: SYM CT already set to %u, " "changing to %u\n", gcid, (unsigned int)ct, NX_CT_SYM); ct = NX_CT_SYM; cfg = SETFIELD(NX_SYM_CFG_CT, cfg, ct); /* Coprocessor Instance must be shifted left. * See hw doc Section 5.5.1. */ ci = GETFIELD(NX_SYM_CFG_CI, cfg) >> NX_SYM_CFG_CI_LSHIFT; if (!ci) prlog(PR_INFO, "NX%d: SYM CI set to %d\n", gcid, instance); else if (ci == instance) prlog(PR_INFO, "NX%d: SYM CI already set to %u\n", gcid, (unsigned int)ci); else prlog(PR_INFO, "NX%d: SYM CI already set to %u, " "changing to %d\n", gcid, (unsigned int)ci, instance); ci = instance; cfg = SETFIELD(NX_SYM_CFG_CI, cfg, ci << NX_SYM_CFG_CI_LSHIFT); cfg = SETFIELD(NX_SYM_CFG_FC_ENABLE, cfg, CFG_SYM_FC_ENABLE); cfg = SETFIELD(NX_SYM_CFG_ENABLE, cfg, CFG_SYM_ENABLE); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: SYM CT %u CI %u config failure %d\n", gcid, (unsigned int)ct, (unsigned int)ci, rc); else prlog(PR_DEBUG, "NX%d: SYM Config 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } static int nx_cfg_asym(u32 gcid, u64 xcfg) { u64 cfg, ci, ct; int rc, instance = gcid + 1; BUILD_ASSERT(MAX_CHIPS < NX_ASYM_CFG_CI_MAX); rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; ct = GETFIELD(NX_ASYM_CFG_CT, cfg); if (!ct) prlog(PR_INFO, "NX%d: ASYM CT set to %u\n", gcid, NX_CT_ASYM); else if (ct == NX_CT_ASYM) prlog(PR_INFO, "NX%d: ASYM CT already set to %u\n", gcid, NX_CT_ASYM); else prlog(PR_INFO, "NX%d: ASYM CT already set to %u, " "changing to %u\n", gcid, (unsigned int)ct, NX_CT_ASYM); ct = NX_CT_ASYM; cfg = SETFIELD(NX_ASYM_CFG_CT, cfg, ct); /* Coprocessor Instance must be shifted left. * See hw doc Section 5.5.1. */ ci = GETFIELD(NX_ASYM_CFG_CI, cfg) >> NX_ASYM_CFG_CI_LSHIFT; if (!ci) prlog(PR_INFO, "NX%d: ASYM CI set to %d\n", gcid, instance); else if (ci == instance) prlog(PR_INFO, "NX%d: ASYM CI already set to %u\n", gcid, (unsigned int)ci); else prlog(PR_INFO, "NX%d: ASYM CI already set to %u, " "changing to %d\n", gcid, (unsigned int)ci, instance); ci = instance; cfg = SETFIELD(NX_ASYM_CFG_CI, cfg, ci << NX_ASYM_CFG_CI_LSHIFT); cfg = SETFIELD(NX_ASYM_CFG_FC_ENABLE, cfg, CFG_ASYM_FC_ENABLE); cfg = SETFIELD(NX_ASYM_CFG_ENABLE, cfg, CFG_ASYM_ENABLE); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: ASYM CT %u CI %u config failure %d\n", gcid, (unsigned int)ct, (unsigned int)ci, rc); else prlog(PR_DEBUG, "NX%d: ASYM Config 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } static int nx_cfg_dma(u32 gcid, u64 xcfg) { u64 cfg; int rc; rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; cfg = SETFIELD(NX_DMA_CFG_AES_SHA_MAX_RR, cfg, AES_SHA_MAX_RR); cfg = SETFIELD(NX_DMA_CFG_AES_SHA_CSB_WR, cfg, AES_SHA_CSB_WR); cfg = SETFIELD(NX_DMA_CFG_AES_SHA_COMPLETION_MODE, cfg, AES_SHA_COMPLETION_MODE); cfg = SETFIELD(NX_DMA_CFG_AES_SHA_CPB_WR, cfg, AES_SHA_CPB_WR); cfg = SETFIELD(NX_DMA_CFG_AES_SHA_OUTPUT_DATA_WR, cfg, AES_SHA_OUTPUT_DATA_WR); cfg = SETFIELD(NX_DMA_CFG_AMF_MAX_RR, cfg, AMF_MAX_RR); cfg = SETFIELD(NX_DMA_CFG_AMF_CSB_WR, cfg, AMF_CSB_WR); cfg = SETFIELD(NX_DMA_CFG_AMF_COMPLETION_MODE, cfg, AMF_COMPLETION_MODE); cfg = SETFIELD(NX_DMA_CFG_AMF_CPB_WR, cfg, AMF_CPB_WR); cfg = SETFIELD(NX_DMA_CFG_AMF_OUTPUT_DATA_WR, cfg, AMF_OUTPUT_DATA_WR); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: DMA config failure %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: DMA 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } static int nx_cfg_iq(u32 gcid, u64 xcfg) { u64 cfg; int rc; rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; cfg = SETFIELD(NX_CRB_IQ_SYM, cfg, CFG_CRB_IQ_SYM); cfg = SETFIELD(NX_CRB_IQ_ASYM, cfg, CFG_CRB_IQ_ASYM); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: CRB Input Queue failure %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: CRB Input Queue 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } static int nx_cfg_ee(u32 gcid, u64 xcfg) { u64 cfg; int rc; rc = xscom_read(gcid, xcfg, &cfg); if (rc) return rc; cfg = SETFIELD(NX_EE_CFG_CH7, cfg, EE_CH7); cfg = SETFIELD(NX_EE_CFG_CH6, cfg, EE_CH6); cfg = SETFIELD(NX_EE_CFG_CH5, cfg, EE_CH5); cfg = SETFIELD(NX_EE_CFG_CH4, cfg, EE_CH4); cfg = SETFIELD(NX_EE_CFG_CH3, cfg, EE_CH3); cfg = SETFIELD(NX_EE_CFG_CH2, cfg, EE_CH2); rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: Engine Enable failure %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: Engine Enable 0x%016lx\n", gcid, (unsigned long)cfg); return rc; } void nx_create_crypto_node(struct dt_node *node) { u32 gcid; u32 pb_base; u64 cfg_dma, cfg_sym, cfg_asym, cfg_iq, cfg_ee; int rc; gcid = dt_get_chip_id(node); pb_base = dt_get_address(node, 0, NULL); prlog(PR_INFO, "NX%d: Crypto at 0x%x\n", gcid, pb_base); if (dt_node_is_compatible(node, "ibm,power7-nx")) { cfg_dma = pb_base + NX_P7_DMA_CFG; cfg_sym = pb_base + NX_P7_SYM_CFG; cfg_asym = pb_base + NX_P7_ASYM_CFG; cfg_iq = pb_base + NX_P7_CRB_IQ; cfg_ee = pb_base + NX_P7_EE_CFG; } else if (dt_node_is_compatible(node, "ibm,power8-nx")) { cfg_dma = pb_base + NX_P8_DMA_CFG; cfg_sym = pb_base + NX_P8_SYM_CFG; cfg_asym = pb_base + NX_P8_ASYM_CFG; cfg_iq = pb_base + NX_P8_CRB_IQ; cfg_ee = pb_base + NX_P8_EE_CFG; } else { prerror("NX%d: ERROR: Unknown NX type!\n", gcid); return; } rc = nx_cfg_dma(gcid, cfg_dma); if (rc) return; rc = nx_cfg_sym(gcid, cfg_sym); if (rc) return; rc = nx_cfg_asym(gcid, cfg_asym); if (rc) return; rc = nx_cfg_iq(gcid, cfg_iq); if (rc) return; rc = nx_cfg_ee(gcid, cfg_ee); if (rc) return; prlog(PR_INFO, "NX%d: Crypto Coprocessors Disabled (not supported)\n", gcid); } skiboot-skiboot-5.1.13/hw/nx-rng.c000066400000000000000000000052321265204436200167240ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include void nx_create_rng_node(struct dt_node *node) { u64 bar, cfg; u64 xbar, xcfg; u32 pb_base; u32 gcid; u64 rng_addr, rng_len, len, addr_mask; struct dt_node *rng; int rc; gcid = dt_get_chip_id(node); pb_base = dt_get_address(node, 0, NULL); if (dt_node_is_compatible(node, "ibm,power7-nx")) { xbar = pb_base + NX_P7_RNG_BAR; xcfg = pb_base + NX_P7_RNG_CFG; addr_mask = NX_P7_RNG_BAR_ADDR; } else if (dt_node_is_compatible(node, "ibm,power8-nx")) { xbar = pb_base + NX_P8_RNG_BAR; xcfg = pb_base + NX_P8_RNG_CFG; addr_mask = NX_P8_RNG_BAR_ADDR; } else { prerror("NX%d: Unknown NX type!\n", gcid); return; } rc = xscom_read(gcid, xbar, &bar); /* Get RNG BAR */ if (rc) return; /* Hope xscom always prints error message */ rc = xscom_read(gcid, xcfg, &cfg); /* Get RNG CFG */ if (rc) return; /* * We mask in-place rather than using GETFIELD for the base address * as we happen to *know* that it's properly aligned in the register. * * FIXME? Always assusme BAR gets a valid address from FSP */ rng_addr = bar & addr_mask; len = GETFIELD(NX_RNG_BAR_SIZE, bar); if (len > 4) { prerror("NX%d: Corrupted bar size %lld\n", gcid, len); return; } rng_len = (u64[]){ 0x1000, /* 4K */ 0x10000, /* 64K */ 0x400000000, /* 16G*/ 0x100000, /* 1M */ 0x1000000 /* 16M */} [len]; prlog(PR_INFO, "NX%d: RNG BAR set to 0x%016llx..0x%016llx\n", gcid, rng_addr, rng_addr + rng_len - 1); /* RNG must be enabled before MMIO is enabled */ rc = xscom_write(gcid, xcfg, cfg | NX_RNG_CFG_ENABLE); if (rc) return; /* The BAR needs to be enabled too */ rc = xscom_write(gcid, xbar, bar | NX_RNG_BAR_ENABLE); if (rc) return; rng = dt_new_addr(dt_root, "hwrng", rng_addr); if (!rng) return; dt_add_property_strings(rng, "compatible", "ibm,power-rng"); dt_add_property_cells(rng, "reg", hi32(rng_addr), lo32(rng_addr), hi32(rng_len), lo32(rng_len)); dt_add_property_cells(rng, "ibm,chip-id", gcid); } skiboot-skiboot-5.1.13/hw/nx.c000066400000000000000000000015621265204436200161420ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include void nx_init(void) { struct dt_node *node; dt_for_each_compatible(dt_root, node, "ibm,power-nx") { nx_create_rng_node(node); nx_create_crypto_node(node); nx_create_842_node(node); } } skiboot-skiboot-5.1.13/hw/occ.c000066400000000000000000000512501265204436200162600ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* OCC Communication Area for PStates */ #define P8_HOMER_SAPPHIRE_DATA_OFFSET 0x1F8000 #define MAX_PSTATES 256 #define chip_occ_data(chip) \ ((struct occ_pstate_table *)(chip->homer_base + \ P8_HOMER_SAPPHIRE_DATA_OFFSET)) static bool occ_reset; static struct lock occ_lock = LOCK_UNLOCKED; struct occ_pstate_entry { s8 id; u8 flags; u8 vdd; u8 vcs; u32 freq_khz; }; struct occ_pstate_table { u8 valid; u8 version; u8 throttle; s8 pstate_min; s8 pstate_nom; s8 pstate_max; u8 spare1; u8 spare2; u64 reserved; struct occ_pstate_entry pstates[MAX_PSTATES]; }; DEFINE_LOG_ENTRY(OPAL_RC_OCC_LOAD, OPAL_PLATFORM_ERR_EVT, OPAL_OCC, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_OCC_RESET, OPAL_PLATFORM_ERR_EVT, OPAL_OCC, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_OCC_PSTATE_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_OCC, OPAL_CEC_HARDWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_OCC_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_OCC, OPAL_CEC_HARDWARE, OPAL_UNRECOVERABLE_ERR_GENERAL, OPAL_NA); /* Check each chip's HOMER/Sapphire area for PState valid bit */ static bool wait_for_all_occ_init(void) { struct proc_chip *chip; uint64_t occ_data_area; struct occ_pstate_table *occ_data; int tries; uint64_t start_time, end_time; uint32_t timeout = 0; if (platform.occ_timeout) timeout = platform.occ_timeout(); start_time = mftb(); for_each_chip(chip) { /* Check for valid homer address */ if (!chip->homer_base) { prerror("OCC: Chip: %x homer_base is not valid\n", chip->id); return false; } /* Get PState table address */ occ_data_area = chip->homer_base + P8_HOMER_SAPPHIRE_DATA_OFFSET; occ_data = (struct occ_pstate_table *)occ_data_area; /* * Checking for occ_data->valid == 1 is ok because we clear all * homer_base+size before passing memory to host services. * This ensures occ_data->valid == 0 before OCC load */ tries = timeout * 10; while((occ_data->valid != 1) && tries--) { time_wait_ms(100); } if (occ_data->valid != 1) { prerror("OCC: Chip: %x PState table is not valid\n", chip->id); return false; } prlog(PR_DEBUG, "OCC: Chip %02x Data (%016llx) = %016llx\n", chip->id, occ_data_area, *(uint64_t *)occ_data_area); } end_time = mftb(); prlog(PR_NOTICE, "OCC: All Chip Rdy after %lld ms\n", (end_time - start_time) / 512 / 1000); return true; } /* Add device tree properties to describe pstates states */ /* Retrun nominal pstate to set in each core */ static bool add_cpu_pstate_properties(s8 *pstate_nom) { struct proc_chip *chip; uint64_t occ_data_area; struct occ_pstate_table *occ_data; struct dt_node *power_mgt; u8 nr_pstates; /* Arrays for device tree */ u32 *dt_id, *dt_freq; u8 *dt_vdd, *dt_vcs; bool rc; int i; prlog(PR_DEBUG, "OCC: CPU pstate state device tree init\n"); /* Find first chip and core */ chip = next_chip(NULL); /* Extract PState information from OCC */ /* Dump state table */ occ_data_area = chip->homer_base + P8_HOMER_SAPPHIRE_DATA_OFFSET; prlog(PR_DEBUG, "OCC: Data (%16llx) = %16llx %16llx\n", occ_data_area, *(uint64_t *)occ_data_area, *(uint64_t *)(occ_data_area+8)); occ_data = (struct occ_pstate_table *)occ_data_area; if (!occ_data->valid) { prerror("OCC: PState table is not valid\n"); return false; } nr_pstates = occ_data->pstate_max - occ_data->pstate_min + 1; prlog(PR_DEBUG, "OCC: Min %d Nom %d Max %d Nr States %d\n", occ_data->pstate_min, occ_data->pstate_nom, occ_data->pstate_max, nr_pstates); if (nr_pstates <= 1 || nr_pstates > 128) { prerror("OCC: OCC range is not valid\n"); return false; } power_mgt = dt_find_by_path(dt_root, "/ibm,opal/power-mgt"); if (!power_mgt) { prerror("OCC: dt node /ibm,opal/power-mgt not found\n"); return false; } rc = false; /* Setup arrays for device-tree */ /* Allocate memory */ dt_id = (u32 *) malloc(MAX_PSTATES * sizeof(u32)); if (!dt_id) { printf("OCC: dt_id array alloc failure\n"); goto out; } dt_freq = (u32 *) malloc(MAX_PSTATES * sizeof(u32)); if (!dt_freq) { printf("OCC: dt_freq array alloc failure\n"); goto out_free_id; } dt_vdd = (u8 *) malloc(MAX_PSTATES * sizeof(u8)); if (!dt_vdd) { printf("OCC: dt_vdd array alloc failure\n"); goto out_free_freq; } dt_vcs = (u8 *) malloc(MAX_PSTATES * sizeof(u8)); if (!dt_vcs) { printf("OCC: dt_vcs array alloc failure\n"); goto out_free_vdd; } for( i=0; i < nr_pstates; i++) { dt_id[i] = occ_data->pstates[i].id; dt_freq[i] = occ_data->pstates[i].freq_khz/1000; dt_vdd[i] = occ_data->pstates[i].vdd; dt_vcs[i] = occ_data->pstates[i].vcs; } /* Add the device-tree entries */ dt_add_property(power_mgt, "ibm,pstate-ids", dt_id, nr_pstates * 4); dt_add_property(power_mgt, "ibm,pstate-frequencies-mhz", dt_freq, nr_pstates * 4); dt_add_property(power_mgt, "ibm,pstate-vdds", dt_vdd, nr_pstates); dt_add_property(power_mgt, "ibm,pstate-vcss", dt_vcs, nr_pstates); dt_add_property_cells(power_mgt, "ibm,pstate-min", occ_data->pstate_min); dt_add_property_cells(power_mgt, "ibm,pstate-nominal", occ_data->pstate_nom); dt_add_property_cells(power_mgt, "ibm,pstate-max", occ_data->pstate_max); /* Return pstate to set for each core */ *pstate_nom = occ_data->pstate_nom; rc = true; free(dt_vcs); out_free_vdd: free(dt_vdd); out_free_id: free(dt_id); out_free_freq: free(dt_freq); out: return rc; } /* * Prepare chip for pstate transitions */ static bool cpu_pstates_prepare_core(struct proc_chip *chip, struct cpu_thread *c, s8 pstate_nom) { uint32_t core = pir_to_core_id(c->pir); uint64_t tmp, pstate; int rc; /* * Currently Fastsleep init clears EX_PM_SPR_OVERRIDE_EN. * Need to ensure only relevant bits are inited */ /* Init PM GP1 for SCOM based PSTATE control to set nominal freq * * Use the OR SCOM to set the required bits in PM_GP1 register * since the OCC might be mainpulating the PM_GP1 register as well. */ rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SET_GP1), EX_PM_SETUP_GP1_PM_SPR_OVERRIDE_EN); if (rc) { log_simple_error(&e_info(OPAL_RC_OCC_PSTATE_INIT), "OCC: Failed to write PM_GP1 in pstates init\n"); return false; } /* Set new pstate to core */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_PPMCR), &tmp); tmp = tmp & ~0xFFFF000000000000ULL; pstate = ((uint64_t) pstate_nom) & 0xFF; tmp = tmp | (pstate << 56) | (pstate << 48); rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_PPMCR), tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_OCC_PSTATE_INIT), "OCC: Failed to write PM_GP1 in pstates init\n"); return false; } time_wait_ms(1); /* Wait for PState to change */ /* * Init PM GP1 for SPR based PSTATE control. * Once OCC is active EX_PM_SETUP_GP1_DPLL_FREQ_OVERRIDE_EN will be * cleared by OCC. Sapphire need not clear. * However wait for DVFS state machine to become idle after min->nominal * transition initiated above. If not switch over to SPR control could fail. * * Use the AND SCOM to clear the required bits in PM_GP1 register * since the OCC might be mainpulating the PM_GP1 register as well. */ tmp = ~EX_PM_SETUP_GP1_PM_SPR_OVERRIDE_EN; rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_CLEAR_GP1), tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_OCC_PSTATE_INIT), "OCC: Failed to write PM_GP1 in pstates init\n"); return false; } /* Just debug */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_PPMSR), &tmp); prlog(PR_DEBUG, "OCC: Chip %x Core %x PPMSR %016llx\n", chip->id, core, tmp); /* * If PMSR is still in transition at this point due to PState change * initiated above, then the switchover to SPR may not work. * ToDo: Check for DVFS state machine idle before change. */ return true; } static bool occ_opal_msg_outstanding = false; static void occ_msg_consumed(void *data __unused) { lock(&occ_lock); occ_opal_msg_outstanding = false; unlock(&occ_lock); } static void occ_throttle_poll(void *data __unused) { struct proc_chip *chip; struct occ_pstate_table *occ_data; struct opal_occ_msg occ_msg; int rc; if (!try_lock(&occ_lock)) return; if (occ_reset) { int inactive = 0; for_each_chip(chip) { occ_data = chip_occ_data(chip); if (occ_data->valid != 1) { inactive = 1; break; } } if (!inactive) { /* * Queue OCC_THROTTLE with throttle status as 0 to * indicate all OCCs are active after a reset. */ occ_msg.type = OCC_THROTTLE; occ_msg.chip = 0; occ_msg.throttle_status = 0; rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL, 3, (uint64_t *)&occ_msg); if (!rc) occ_reset = false; } } else { if (occ_opal_msg_outstanding) goto done; for_each_chip(chip) { occ_data = chip_occ_data(chip); if ((occ_data->valid == 1) && (chip->throttle != occ_data->throttle) && (occ_data->throttle <= OCC_MAX_THROTTLE_STATUS)) { occ_msg.type = OCC_THROTTLE; occ_msg.chip = chip->id; occ_msg.throttle_status = occ_data->throttle; rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, occ_msg_consumed, 3, (uint64_t *)&occ_msg); if (!rc) { chip->throttle = occ_data->throttle; occ_opal_msg_outstanding = true; break; } } } } done: unlock(&occ_lock); } /* CPU-OCC PState init */ /* Called after OCC init on P8 */ void occ_pstates_init(void) { struct proc_chip *chip; struct cpu_thread *c; s8 pstate_nom; /* OCC is P8 only */ if (proc_gen != proc_gen_p8) return; chip = next_chip(NULL); if (!chip->homer_base) { log_simple_error(&e_info(OPAL_RC_OCC_PSTATE_INIT), "OCC: No HOMER detected, assuming no pstates\n"); return; } /* Wait for all OCC to boot up */ if(!wait_for_all_occ_init()) { log_simple_error(&e_info(OPAL_RC_OCC_TIMEOUT), "OCC: Initialization on all chips did not complete" "(timed out)\n"); return; } /* * Check boundary conditions and add device tree nodes * and return nominal pstate to set for the core */ if (!add_cpu_pstate_properties(&pstate_nom)) { log_simple_error(&e_info(OPAL_RC_OCC_PSTATE_INIT), "Skiping core cpufreq init due to OCC error\n"); return; } /* Setup host based pstates and set nominal frequency */ for_each_chip(chip) { for_each_available_core_in_chip(c, chip->id) { cpu_pstates_prepare_core(chip, c, pstate_nom); } } /* Add opal_poller to poll OCC throttle status of each chip */ for_each_chip(chip) chip->throttle = 0; opal_add_poller(occ_throttle_poll, NULL); } struct occ_load_req { u8 scope; u32 dbob_id; u32 seq_id; struct list_node link; }; static LIST_HEAD(occ_load_req_list); static void occ_queue_load(u8 scope, u32 dbob_id, u32 seq_id) { struct occ_load_req *occ_req; occ_req = zalloc(sizeof(struct occ_load_req)); if (!occ_req) { prerror("OCC: Could not allocate occ_load_req\n"); return; } occ_req->scope = scope; occ_req->dbob_id = dbob_id; occ_req->seq_id = seq_id; list_add_tail(&occ_load_req_list, &occ_req->link); } static void __occ_do_load(u8 scope, u32 dbob_id __unused, u32 seq_id) { struct fsp_msg *stat; int rc = -ENOMEM; int status_word = 0; struct proc_chip *chip = next_chip(NULL); /* Call HBRT... */ rc = host_services_occ_load(); /* Handle fallback to preload */ if (rc == -ENOENT && chip->homer_base) { prlog(PR_INFO, "OCC: Load: Fallback to preloaded image\n"); rc = 0; } else if (!rc) { struct opal_occ_msg occ_msg = { OCC_LOAD, 0, 0 }; rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL, 3, (uint64_t *)&occ_msg); if (rc) prlog(PR_INFO, "OCC: Failed to queue message %d\n", OCC_LOAD); /* Success, start OCC */ rc = host_services_occ_start(); } if (rc) { /* If either of hostservices call fail, send fail to FSP */ /* Find a chip ID to send failure */ for_each_chip(chip) { if (scope == 0x01 && dbob_id != chip->dbob_id) continue; status_word = 0xB500 | (chip->pcid & 0xff); break; } log_simple_error(&e_info(OPAL_RC_OCC_LOAD), "OCC: Error %d in load/start OCC\n", rc); } /* Send a single response for all chips */ stat = fsp_mkmsg(FSP_CMD_LOAD_OCC_STAT, 2, status_word, seq_id); if (stat) rc = fsp_queue_msg(stat, fsp_freemsg); if (rc) { log_simple_error(&e_info(OPAL_RC_OCC_LOAD), "OCC: Error %d queueing FSP OCC LOAD STATUS msg", rc); fsp_freemsg(stat); } } void occ_poke_load_queue(void) { struct occ_load_req *occ_req, *next; if (list_empty(&occ_load_req_list)) return; list_for_each_safe(&occ_load_req_list, occ_req, next, link) { __occ_do_load(occ_req->scope, occ_req->dbob_id, occ_req->seq_id); list_del(&occ_req->link); free(occ_req); } } static void occ_do_load(u8 scope, u32 dbob_id __unused, u32 seq_id) { struct fsp_msg *rsp; int rc = -ENOMEM; u8 err = 0; if (scope != 0x01 && scope != 0x02) { prerror("OCC: Load message with invalid scope 0x%x\n", scope); err = 0x22; } /* First queue up an OK response to the load message itself */ rsp = fsp_mkmsg(FSP_RSP_LOAD_OCC | err, 0); if (rsp) rc = fsp_queue_msg(rsp, fsp_freemsg); if (rc) { log_simple_error(&e_info(OPAL_RC_OCC_LOAD), "OCC: Error %d queueing FSP OCC LOAD reply\n", rc); fsp_freemsg(rsp); return; } if (err) return; /* * Check if hostservices lid caching is complete. If not, queue * the load request. */ if (!hservices_lid_preload_complete()) { occ_queue_load(scope, dbob_id, seq_id); return; } __occ_do_load(scope, dbob_id, seq_id); } static void occ_do_reset(u8 scope, u32 dbob_id, u32 seq_id) { struct fsp_msg *rsp, *stat; struct proc_chip *chip = next_chip(NULL); int rc = -ENOMEM; u8 err = 0; /* Check arguments */ if (scope != 0x01 && scope != 0x02) { prerror("OCC: Reset message with invalid scope 0x%x\n", scope); err = 0x22; } /* First queue up an OK response to the reset message itself */ rsp = fsp_mkmsg(FSP_RSP_RESET_OCC | err, 0); if (rsp) rc = fsp_queue_msg(rsp, fsp_freemsg); if (rc) { fsp_freemsg(rsp); log_simple_error(&e_info(OPAL_RC_OCC_RESET), "OCC: Error %d queueing FSP OCC RESET reply\n", rc); return; } /* If we had an error, return */ if (err) return; /* * Call HBRT to stop OCC and leave it stopped. FSP will send load/start * request subsequently. Also after few runtime restarts (currently 3), * FSP will request OCC to left in stopped state. */ rc = host_services_occ_stop(); /* Handle fallback to preload */ if (rc == -ENOENT && chip->homer_base) { prlog(PR_INFO, "OCC: Reset: Fallback to preloaded image\n"); rc = 0; } if (!rc) { struct opal_occ_msg occ_msg = { OCC_RESET, 0, 0 }; /* Send a single success response for all chips */ stat = fsp_mkmsg(FSP_CMD_RESET_OCC_STAT, 2, 0, seq_id); if (stat) rc = fsp_queue_msg(stat, fsp_freemsg); if (rc) { fsp_freemsg(stat); log_simple_error(&e_info(OPAL_RC_OCC_RESET), "OCC: Error %d queueing FSP OCC RESET" " STATUS message\n", rc); } lock(&occ_lock); rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL, 3, (uint64_t *)&occ_msg); if (rc) prlog(PR_INFO, "OCC: Failed to queue message %d\n", OCC_RESET); /* * Set 'valid' byte of chip_occ_data to 0 since OCC * may not clear this byte on a reset. * OCC will set the 'valid' byte to 1 when it becomes * active again. */ for_each_chip(chip) { struct occ_pstate_table *occ_data; occ_data = chip_occ_data(chip); occ_data->valid = 0; chip->throttle = 0; } occ_reset = true; unlock(&occ_lock); } else { /* * Then send a matching OCC Reset Status message with an 0xFE * (fail) response code as well to the first matching chip */ for_each_chip(chip) { if (scope == 0x01 && dbob_id != chip->dbob_id) continue; rc = -ENOMEM; stat = fsp_mkmsg(FSP_CMD_RESET_OCC_STAT, 2, 0xfe00 | (chip->pcid & 0xff), seq_id); if (stat) rc = fsp_queue_msg(stat, fsp_freemsg); if (rc) { fsp_freemsg(stat); log_simple_error(&e_info(OPAL_RC_OCC_RESET), "OCC: Error %d queueing FSP OCC RESET" " STATUS message\n", rc); } break; } } } #define PV_OCC_GP0 0x01000000 #define PV_OCC_GP0_AND 0x01000004 #define PV_OCC_GP0_OR 0x01000005 #define PV_OCC_GP0_PNOR_OWNER PPC_BIT(18) /* 1 = OCC / Host, 0 = BMC */ static void occ_pnor_set_one_owner(uint32_t chip_id, enum pnor_owner owner) { uint64_t reg, mask; if (owner == PNOR_OWNER_HOST) { reg = PV_OCC_GP0_OR; mask = PV_OCC_GP0_PNOR_OWNER; } else { reg = PV_OCC_GP0_AND; mask = ~PV_OCC_GP0_PNOR_OWNER; } xscom_write(chip_id, reg, mask); } void occ_pnor_set_owner(enum pnor_owner owner) { struct proc_chip *chip; for_each_chip(chip) occ_pnor_set_one_owner(chip->id, owner); } static bool fsp_occ_msg(u32 cmd_sub_mod, struct fsp_msg *msg) { u32 dbob_id, seq_id; u8 scope; switch (cmd_sub_mod) { case FSP_CMD_LOAD_OCC: /* * We get the "Load OCC" command at boot. We don't currently * support loading it ourselves (we don't have the procedures, * they will come with Host Services). For now HostBoot will * have loaded a OCC firmware for us, but we still need to * be nice and respond to OCC. */ scope = msg->data.bytes[3]; dbob_id = msg->data.words[1]; seq_id = msg->data.words[2]; prlog(PR_INFO, "OCC: Got OCC Load message, scope=0x%x" " dbob=0x%x seq=0x%x\n", scope, dbob_id, seq_id); occ_do_load(scope, dbob_id, seq_id); return true; case FSP_CMD_RESET_OCC: /* * We shouldn't be getting this one, but if we do, we have * to reply something sensible or the FSP will get upset */ scope = msg->data.bytes[3]; dbob_id = msg->data.words[1]; seq_id = msg->data.words[2]; prlog(PR_INFO, "OCC: Got OCC Reset message, scope=0x%x" " dbob=0x%x seq=0x%x\n", scope, dbob_id, seq_id); occ_do_reset(scope, dbob_id, seq_id); return true; } return false; } static struct fsp_client fsp_occ_client = { .message = fsp_occ_msg, }; #define OCB_OCI_OCCMISC 0x6a020 #define OCB_OCI_OCCMISC_AND 0x6a021 #define OCB_OCI_OCCMISC_OR 0x6a022 #define OCB_OCI_OCIMISC_IRQ PPC_BIT(0) #define OCB_OCI_OCIMISC_IRQ_TMGT PPC_BIT(1) #define OCB_OCI_OCIMISC_IRQ_SLW_TMR PPC_BIT(14) #define OCB_OCI_OCIMISC_IRQ_OPAL_DUMMY PPC_BIT(15) #define OCB_OCI_OCIMISC_MASK (OCB_OCI_OCIMISC_IRQ_TMGT | \ OCB_OCI_OCIMISC_IRQ_OPAL_DUMMY | \ OCB_OCI_OCIMISC_IRQ_SLW_TMR) void occ_send_dummy_interrupt(void) { struct psi *psi; struct proc_chip *chip = get_chip(this_cpu()->chip_id); /* Emulators and P7 doesn't do this */ if (proc_gen != proc_gen_p8 || chip_quirk(QUIRK_NO_OCC_IRQ)) return; /* Find a functional PSI. This ensures an interrupt even if * the psihb on the current chip is not configured */ if (chip->psi) psi = chip->psi; else psi = psi_find_functional_chip(); if (!psi) { prlog_once(PR_WARNING, "PSI: no functional PSI HB found, " "no self interrupts delivered\n"); return; } xscom_write(psi->chip_id, OCB_OCI_OCCMISC_OR, OCB_OCI_OCIMISC_IRQ | OCB_OCI_OCIMISC_IRQ_OPAL_DUMMY); } void occ_interrupt(uint32_t chip_id) { uint64_t ireg; int64_t rc; /* The OCC interrupt is used to mux up to 15 different sources */ rc = xscom_read(chip_id, OCB_OCI_OCCMISC, &ireg); if (rc) { prerror("OCC: Failed to read interrupt status !\n"); /* Should we mask it in the XIVR ? */ return; } prlog(PR_TRACE, "OCC: IRQ received: %04llx\n", ireg >> 48); /* Clear the bits */ xscom_write(chip_id, OCB_OCI_OCCMISC_AND, ~ireg); /* Dispatch */ if (ireg & OCB_OCI_OCIMISC_IRQ_TMGT) prd_tmgt_interrupt(chip_id); if (ireg & OCB_OCI_OCIMISC_IRQ_SLW_TMR) check_timers(true); /* We may have masked-out OCB_OCI_OCIMISC_IRQ in the previous * OCCMISC_AND write. Check if there are any new source bits set, * and trigger another interrupt if so. */ rc = xscom_read(chip_id, OCB_OCI_OCCMISC, &ireg); if (!rc && (ireg & OCB_OCI_OCIMISC_MASK)) xscom_write(chip_id, OCB_OCI_OCCMISC_OR, OCB_OCI_OCIMISC_IRQ); } void occ_fsp_init(void) { /* OCC is P8 only */ if (proc_gen != proc_gen_p8) return; /* If we have an FSP, register for notifications */ if (fsp_present()) fsp_register_client(&fsp_occ_client, FSP_MCLASS_OCC); } skiboot-skiboot-5.1.13/hw/p5ioc2-phb.c000066400000000000000000001017031265204436200173630ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #define PHBDBG(p, fmt, a...) prlog(PR_DEBUG, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) #define PHBERR(p, fmt, a...) prlog(PR_ERR, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) /* * Lock callbacks. Allows the OPAL API handlers to lock the * PHB around calls such as config space, EEH, etc... */ static void p5ioc2_phb_lock(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); lock(&p->lock); } static void p5ioc2_phb_unlock(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); unlock(&p->lock); } /* * Configuration space access * * The PHB lock is assumed to be already held */ static int64_t p5ioc2_pcicfg_address(struct p5ioc2_phb *p, uint32_t bdfn, uint32_t offset, uint32_t size) { uint32_t addr, sm = size - 1; if (bdfn > 0xffff) return OPAL_PARAMETER; /* XXX Should we enable 4K config space on PCI-X 2.0 ? */ if ((offset > 0xff && !p->is_pcie) || offset > 0xfff) return OPAL_PARAMETER; if (offset & sm) return OPAL_PARAMETER; /* The root bus only has a device at 0 and we get into an * error state if we try to probe beyond that, so let's * avoid that and just return an error to Linux */ if (p->is_pcie && (bdfn >> 8) == 0 && (bdfn & 0xff)) return OPAL_HARDWARE; /* Prevent special operation generation */ if (((bdfn >> 3) & 0x1f) == 0x1f) return OPAL_HARDWARE; /* Check PHB state */ if (p->state == P5IOC2_PHB_STATE_BROKEN) return OPAL_HARDWARE; /* Additionally, should we prevent writes to the PHB own * bus number register ? */ addr = CAP_PCADR_ENABLE; addr = SETFIELD(CAP_PCADR_BDFN, addr, bdfn); addr = SETFIELD(CAP_PCADR_EXTOFF, addr, offset >> 8); addr |= (offset & 0xff); out_le32(p->regs + CAP_PCADR, addr); return OPAL_SUCCESS; } static int64_t p5ioc2_pcicfg_read8(struct phb *phb, uint32_t bdfn, uint32_t offset, uint8_t *data) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); int64_t rc; /* Initialize data in case of error */ *data = 0xff; rc = p5ioc2_pcicfg_address(p, bdfn, offset, 1); if (rc) return rc; *data = in_8(p->regs + CAP_PCDAT + (offset & 3)); return OPAL_SUCCESS; } static int64_t p5ioc2_pcicfg_read16(struct phb *phb, uint32_t bdfn, uint32_t offset, uint16_t *data) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); int64_t rc; /* Initialize data in case of error */ *data = 0xffff; rc = p5ioc2_pcicfg_address(p, bdfn, offset, 2); if (rc) return rc; *data = in_le16(p->regs + CAP_PCDAT + (offset & 3)); return OPAL_SUCCESS; } static int64_t p5ioc2_pcicfg_read32(struct phb *phb, uint32_t bdfn, uint32_t offset, uint32_t *data) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); int64_t rc; /* Initialize data in case of error */ *data = 0xffffffff; rc = p5ioc2_pcicfg_address(p, bdfn, offset, 4); if (rc) return rc; *data = in_le32(p->regs + CAP_PCDAT); return OPAL_SUCCESS; } static int64_t p5ioc2_pcicfg_write8(struct phb *phb, uint32_t bdfn, uint32_t offset, uint8_t data) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); int64_t rc; rc = p5ioc2_pcicfg_address(p, bdfn, offset, 1); if (rc) return rc; out_8(p->regs + CAP_PCDAT + (offset & 3), data); return OPAL_SUCCESS; } static int64_t p5ioc2_pcicfg_write16(struct phb *phb, uint32_t bdfn, uint32_t offset, uint16_t data) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); int64_t rc; rc = p5ioc2_pcicfg_address(p, bdfn, offset, 2); if (rc) return rc; out_le16(p->regs + CAP_PCDAT + (offset & 3), data); return OPAL_SUCCESS; } static int64_t p5ioc2_pcicfg_write32(struct phb *phb, uint32_t bdfn, uint32_t offset, uint32_t data) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); int64_t rc; rc = p5ioc2_pcicfg_address(p, bdfn, offset, 4); if (rc) return rc; out_le32(p->regs + CAP_PCDAT, data); return OPAL_SUCCESS; } static int64_t p5ioc2_presence_detect(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint16_t slotstat; int64_t rc; if (!p->is_pcie) { uint32_t lsr; lsr = in_be32(p->regs + SHPC_LOGICAL_SLOT); if (GETFIELD(SHPC_LOGICAL_SLOT_PRSNT, lsr) != SHPC_SLOT_STATE_EMPTY) return OPAL_SHPC_DEV_PRESENT; else return OPAL_SHPC_DEV_NOT_PRESENT; } rc = p5ioc2_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_SLOTSTAT, &slotstat); if (rc || !(slotstat & PCICAP_EXP_SLOTSTAT_PDETECTST)) return OPAL_SHPC_DEV_NOT_PRESENT; return OPAL_SHPC_DEV_PRESENT; } static int64_t p5ioc2_link_state(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint16_t lstat; int64_t rc; /* XXX Test for PHB in error state ? */ if (!p->is_pcie) return OPAL_SHPC_LINK_UP_x1; rc = p5ioc2_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LSTAT, &lstat); if (rc < 0) { /* Shouldn't happen */ PHBERR(p, "Failed to read link status\n"); return OPAL_HARDWARE; } if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) return OPAL_SHPC_LINK_DOWN; return GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat); } static int64_t p5ioc2_power_state(struct phb *phb __unused) { /* XXX FIXME */ #if 0 struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint64_t reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); /* XXX Test for PHB in error state ? */ if (reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT) return OPAL_SHPC_POWER_ON; return OPAL_SHPC_POWER_OFF; #else return OPAL_SHPC_POWER_ON; #endif } /* p5ioc2_sm_slot_power_off - Slot power off state machine */ static int64_t p5ioc2_sm_slot_power_off(struct p5ioc2_phb *p) { switch(p->state) { default: break; } /* Unknown state, hardware error ? */ return OPAL_HARDWARE; } static int64_t p5ioc2_slot_power_off(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL) return OPAL_BUSY; /* run state machine */ return p5ioc2_sm_slot_power_off(p); } static int64_t p5ioc2_sm_slot_power_on(struct p5ioc2_phb *p __unused) { #if 0 uint64_t reg; uint32_t reg32; uint16_t brctl; switch(p->state) { case P5IOC2_PHB_STATE_FUNCTIONAL: /* Check presence */ reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) { PHBDBG(p, "Slot power on: no device\n"); return OPAL_CLOSED; } /* Adjust UTL interrupt settings to disable various * errors that would interfere with the process */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7e00000000000000); /* If the power is not on, turn it on now */ if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) { reg = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE); reg &= ~(0x8c00000000000000ul); reg |= 0x8400000000000000ul; out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg); p->state = PHB_STATE_SPUP_STABILIZE_DELAY; PHBDBG(p, "Slot power on: powering on...\n"); return p5ioc2_set_sm_timeout(p, secs_to_tb(2)); } /* Power is already on */ power_ok: /* Ensure hot reset is deasserted */ p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); p->retries = 40; p->state = PHB_STATE_SPUP_WAIT_LINK; PHBDBG(p, "Slot power on: waiting for link\n"); /* Fall through */ case PHB_STATE_SPUP_WAIT_LINK: reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); /* Link is up ? Complete */ /* XXX TODO: Check link width problem and if present * go straight to the host reset code path. */ if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) { /* Restore UTL interrupts */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xfe65000000000000); p->state = PHB_STATE_FUNCTIONAL; PHBDBG(p, "Slot power on: up !\n"); return OPAL_SUCCESS; } /* Retries */ p->retries--; if (p->retries == 0) { /* XXX Improve logging */ PHBERR(p,"Slot power on: Timeout waiting for link\n"); goto error; } /* Check time elapsed */ if ((p->retries % 20) != 0) return p5ioc2_set_sm_timeout(p, msecs_to_tb(10)); /* >200ms, time to try a hot reset after clearing the * link status bit (doco says to do so) */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x0080000000000000); /* Mask receiver error status in AER */ p5ioc2_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, ®32); reg32 |= PCIECAP_AER_CE_RECVR_ERR; p5ioc2_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32); /* Turn on host reset */ p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl |= PCI_CFG_BRCTL_SECONDARY_RESET; p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); p->state = PHB_STATE_SPUP_HOT_RESET_DELAY; PHBDBG(p, "Slot power on: soft reset...\n"); return p5ioc2_set_sm_timeout(p, secs_to_tb(1)); case PHB_STATE_SPUP_HOT_RESET_DELAY: /* Turn off host reset */ p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); /* Clear spurious errors */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x00e0000000000000); p5ioc2_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, PCIECAP_AER_CE_RECVR_ERR); /* Unmask receiver error status in AER */ p5ioc2_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, ®32); reg32 &= ~PCIECAP_AER_CE_RECVR_ERR; p5ioc2_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32); /* Go back to waiting for link */ p->state = PHB_STATE_SPUP_WAIT_LINK; PHBDBG(p, "Slot power on: waiting for link (2)\n"); return p5ioc2_set_sm_timeout(p, msecs_to_tb(10)); case PHB_STATE_SPUP_STABILIZE_DELAY: /* Come here after the 2s delay after power up */ p->retries = 1000; p->state = PHB_STATE_SPUP_SLOT_STATUS; PHBDBG(p, "Slot power on: waiting for power\n"); /* Fall through */ case PHB_STATE_SPUP_SLOT_STATUS: reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); /* Doc says to check LED status, but we ignore that, there * no point really and it's easier that way */ if (reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT) goto power_ok; if (p->retries-- == 0) { /* XXX Improve error logging */ PHBERR(p, "Timeout powering up slot\n"); goto error; } return p5ioc2_set_sm_timeout(p, msecs_to_tb(10)); default: break; } /* Unknown state, hardware error ? */ error: p->state = PHB_STATE_FUNCTIONAL; return OPAL_HARDWARE; #else return OPAL_SUCCESS; #endif } static int64_t p5ioc2_slot_power_on(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL) return OPAL_BUSY; /* run state machine */ return p5ioc2_sm_slot_power_on(p); } static int64_t p5ioc2_sm_hot_reset(struct p5ioc2_phb *p) { switch(p->state) { default: break; } /* Unknown state, hardware error ? */ return OPAL_HARDWARE; } static int64_t p5ioc2_hot_reset(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL) return OPAL_BUSY; /* run state machine */ return p5ioc2_sm_hot_reset(p); } static int64_t p5ioc2_sm_freset(struct p5ioc2_phb *p) { switch(p->state) { default: break; } /* XXX Not implemented, return success to make * pci.c happy, otherwise probing of slots will * fail */ return OPAL_SUCCESS; } static int64_t p5ioc2_freset(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL) return OPAL_BUSY; /* run state machine */ return p5ioc2_sm_freset(p); } static int64_t p5ioc2_poll(struct phb *phb) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint64_t now = mftb(); if (p->state == P5IOC2_PHB_STATE_FUNCTIONAL) return OPAL_SUCCESS; /* Check timer */ if (p->delay_tgt_tb && tb_compare(now, p->delay_tgt_tb) == TB_ABEFOREB) return p->delay_tgt_tb - now; /* Expired (or not armed), clear it */ p->delay_tgt_tb = 0; #if 0 /* Dispatch to the right state machine */ switch(p->state) { case PHB_STATE_SPUP_STABILIZE_DELAY: case PHB_STATE_SPUP_SLOT_STATUS: case PHB_STATE_SPUP_WAIT_LINK: case PHB_STATE_SPUP_HOT_RESET_DELAY: return p5ioc2_sm_slot_power_on(p); case PHB_STATE_SPDOWN_STABILIZE_DELAY: case PHB_STATE_SPDOWN_SLOT_STATUS: return p5ioc2_sm_slot_power_off(p); case PHB_STATE_HRESET_DELAY: return p5ioc2_sm_hot_reset(p); default: break; } #endif /* Unknown state, could be a HW error */ return OPAL_HARDWARE; } static int64_t p5ioc2_eeh_freeze_status(struct phb *phb, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint16_t *severity, uint64_t *phb_status __unused) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint32_t cfgrw; /* Defaults: not frozen */ *freeze_state = OPAL_EEH_STOPPED_NOT_FROZEN; *pci_error_type = OPAL_EEH_NO_ERROR; if (severity) *severity = OPAL_EEH_SEV_NO_ERROR; if (pe_number != 0) return OPAL_PARAMETER; /* XXX Handle PHB status */ /* XXX We currently only check for PE freeze, not fence */ cfgrw = in_be32(p->regs + CAP_PCFGRW); if (cfgrw & CAP_PCFGRW_MMIO_FROZEN) *freeze_state |= OPAL_EEH_STOPPED_MMIO_FREEZE; if (cfgrw & CAP_PCFGRW_DMA_FROZEN) *freeze_state |= OPAL_EEH_STOPPED_DMA_FREEZE; if (severity && (cfgrw & (CAP_PCFGRW_MMIO_FROZEN | CAP_PCFGRW_MMIO_FROZEN))) *severity = OPAL_EEH_SEV_PE_ER; /* XXX Don't bother populating pci_error_type */ /* Should read the bits from PLSSR */ return OPAL_SUCCESS; } static int64_t p5ioc2_eeh_next_error(struct phb *phb, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint32_t cfgrw; /* XXX Don't bother */ *pci_error_type = OPAL_EEH_NO_ERROR; *first_frozen_pe = 0; cfgrw = in_be32(p->regs + CAP_PCFGRW); if (cfgrw & (CAP_PCFGRW_MMIO_FROZEN | CAP_PCFGRW_MMIO_FROZEN)) *severity = OPAL_EEH_SEV_PE_ER; return OPAL_SUCCESS; } static int64_t p5ioc2_eeh_freeze_clear(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint32_t cfgrw; if (pe_number != 0) return OPAL_PARAMETER; /* * This sequence isn't very well documented. We play guess * games based on the documentation, what we do on P7IOC, * and common sense. * * Basically we start from the low level (UTL), clear all * error conditions there. Then we clear error conditions * in the PLSSR and DMACSR. * * Once that's done, we unfreeze the PHB * * Note: Should we also clear the error bits in the config * space ? The docs don't say anything... TODO: Check what * OPAL does if possible or ask Milton. */ /* Clear UTL error regs on PCIe */ if (p->is_pcie) { uint32_t err; err = in_be32(p->regs + UTL_SYS_BUS_AGENT_STATUS); out_be32(p->regs + UTL_SYS_BUS_AGENT_STATUS, err); err = in_be32(p->regs + UTL_PCIE_PORT_STATUS); out_be32(p->regs + UTL_PCIE_PORT_STATUS, err); err = in_be32(p->regs + UTL_RC_STATUS); out_be32(p->regs + UTL_RC_STATUS, err); } /* XXX We should probably clear the error regs in the cfg space... */ /* Clear PLSSR and DMACSR */ out_be32(p->regs + CAP_DMACSR, 0); out_be32(p->regs + CAP_PLSSR, 0); /* Clear freeze state as requested */ cfgrw = in_be32(p->regs + CAP_PCFGRW); if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO) { cfgrw &= ~CAP_PCFGRW_MMIO_FROZEN; out_be32(p->regs + CAP_PCFGRW, cfgrw); } if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_DMA) { cfgrw &= ~CAP_PCFGRW_DMA_FROZEN; out_be32(p->regs + CAP_PCFGRW, cfgrw); } return OPAL_SUCCESS; } static int64_t p5ioc2_get_msi_64(struct phb *phb __unused, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint64_t *msi_address, uint32_t *message_data) { if (mve_number > 255 || xive_num > 255 || msi_range != 1) return OPAL_PARAMETER; *msi_address = 0x1000000000000000ul; *message_data = xive_num; return OPAL_SUCCESS; } static uint8_t p5ioc2_choose_bus(struct phb *phb __unused, struct pci_device *bridge __unused, uint8_t candidate, uint8_t *max_bus __unused, bool *use_max) { /* Use standard bus number selection */ *use_max = false; return candidate; } /* p5ioc2_phb_ioda_reset - Reset the IODA tables * * This reset the IODA tables in the PHB. It is called at * initialization time, on PHB reset, and can be called * explicitly from OPAL * * Note: We don't handle EEH on p5ioc2, we use no cache * and thus always purge */ static int64_t p5ioc2_ioda_reset(struct phb *phb, bool purge __unused) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); unsigned int i; /* Init XIVRs */ for (i = 0; i < 16; i++) { p->xive_cache[i] = SETFIELD(CAP_XIVR_PRIO, 0, 0xff); out_be32(p->regs + CAP_XIVRn(i), 0x000000ff); } return OPAL_SUCCESS; } static int64_t p5ioc2_set_phb_tce_memory(struct phb *phb, uint64_t tce_mem_addr, uint64_t tce_mem_size) { struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb); uint64_t tar; uint32_t cfg; printf("PHB%d: set_tce_memory: 0x%016llx 0x%016llx\n", p->index, tce_mem_addr, tce_mem_size); printf("PHB%d: bridge values : 0x%016llx 0x%016llx\n", p->index, p->ioc->tce_base, p->ioc->tce_size); /* First check if it fits in the memory established for * the IO HUB */ if (tce_mem_addr && (tce_mem_addr < p->ioc->tce_base || tce_mem_addr > (p->ioc->tce_base + p->ioc->tce_size) || (tce_mem_addr + tce_mem_size) > (p->ioc->tce_base + p->ioc->tce_size))) { prerror("PHB%d: TCEs not in bridge range\n", p->index); return OPAL_PARAMETER; } /* Supported sizes are power of two's naturally aligned * and between 64K and 8M (p5ioc2 spec) */ if (tce_mem_addr && !is_pow2(tce_mem_size)) { prerror("PHB%d: Size is not a power of 2\n", p->index); return OPAL_PARAMETER; } if (tce_mem_addr & (tce_mem_size - 1)) { prerror("PHB%d: Not naturally aligned\n", p->index); return OPAL_PARAMETER; } if (tce_mem_addr && (tce_mem_size < 0x10000 || tce_mem_size > 0x800000)) { prerror("PHB%d: Size out of range\n", p->index); return OPAL_PARAMETER; } /* First we disable TCEs in the bridge */ cfg = in_be32(p->regs + CAP_PCFGRW); cfg &= ~CAP_PCFGRW_TCE_EN; out_be32(p->regs + CAP_PCFGRW, cfg); /* Now there's a blurb in the spec about all TARm needing * to have the same size.. I will let that as a surprise * for the user ... Linux does it fine and I'd rather not * keep more state to check than I need to */ tar = 0; if (tce_mem_addr) { tar = SETFIELD(CA_TAR_HUBID, 0ul, p->ca ? 4 : 1); tar = SETFIELD(CA_TAR_ALTHUBID, tar, p->ca ? 4 : 1); tar = SETFIELD(CA_TAR_NUM_TCE, tar, ilog2(tce_mem_size) - 16); tar |= tce_mem_addr; /* addr is naturally aligned */ tar |= CA_TAR_VALID; printf("PHB%d: Writing TAR: 0x%016llx\n", p->index, tar); } out_be64(p->ca_regs + CA_TARn(p->index), tar); /* Now set the TCE enable if we set a valid address */ if (tce_mem_addr) { cfg |= CAP_PCFGRW_TCE_EN; out_be32(p->regs + CAP_PCFGRW, cfg); } return OPAL_SUCCESS; } static const struct phb_ops p5ioc2_phb_ops = { .lock = p5ioc2_phb_lock, .unlock = p5ioc2_phb_unlock, .cfg_read8 = p5ioc2_pcicfg_read8, .cfg_read16 = p5ioc2_pcicfg_read16, .cfg_read32 = p5ioc2_pcicfg_read32, .cfg_write8 = p5ioc2_pcicfg_write8, .cfg_write16 = p5ioc2_pcicfg_write16, .cfg_write32 = p5ioc2_pcicfg_write32, .choose_bus = p5ioc2_choose_bus, .eeh_freeze_status = p5ioc2_eeh_freeze_status, .eeh_freeze_clear = p5ioc2_eeh_freeze_clear, .next_error = p5ioc2_eeh_next_error, .get_msi_64 = p5ioc2_get_msi_64, .ioda_reset = p5ioc2_ioda_reset, .set_phb_tce_memory = p5ioc2_set_phb_tce_memory, .presence_detect = p5ioc2_presence_detect, .link_state = p5ioc2_link_state, .power_state = p5ioc2_power_state, .slot_power_off = p5ioc2_slot_power_off, .slot_power_on = p5ioc2_slot_power_on, .hot_reset = p5ioc2_hot_reset, .fundamental_reset = p5ioc2_freset, .poll = p5ioc2_poll, }; /* p5ioc2_phb_get_xive - Interrupt control from OPAL */ static int64_t p5ioc2_phb_get_xive(void *data, uint32_t isn, uint16_t *server, uint8_t *prio) { struct p5ioc2_phb *p = data; uint32_t irq, xivr, fbuid = P7_IRQ_FBUID(isn); if (fbuid != p->buid) return OPAL_PARAMETER; irq = isn & 0xf; xivr = p->xive_cache[irq]; *server = GETFIELD(CAP_XIVR_SERVER, xivr); *prio = GETFIELD(CAP_XIVR_PRIO, xivr); return OPAL_SUCCESS; } /* p5ioc2_phb_set_xive - Interrupt control from OPAL */ static int64_t p5ioc2_phb_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t prio) { struct p5ioc2_phb *p = data; uint32_t irq, xivr, fbuid = P7_IRQ_FBUID(isn); if (fbuid != p->buid) return OPAL_PARAMETER; irq = isn & 0xf; printf("PHB%d: Set XIVE isn %04x (irq=%d) server=%x, prio=%x\n", p->index, isn, irq, server, prio); xivr = SETFIELD(CAP_XIVR_SERVER, 0, server); xivr = SETFIELD(CAP_XIVR_PRIO, xivr, prio); p->xive_cache[irq] = xivr; /* Now we mangle the server and priority */ if (prio == 0xff) { server = 0; prio = 0xff; } else { prio = (prio >> 3) | ((server & 7) << 5); server = server >> 3; } /* We use HRT entry 0 always for now */ xivr = SETFIELD(CAP_XIVR_SERVER, 0, server); xivr = SETFIELD(CAP_XIVR_PRIO, xivr, prio); out_be32(p->regs + CAP_XIVRn(irq), xivr); printf("PHB%d: wrote 0x%08x to XIVR %d\n", p->index, xivr, irq); return OPAL_SUCCESS; } /* IRQ ops for OS interrupts (not internal) */ static const struct irq_source_ops p5ioc2_phb_os_irq_ops = { .get_xive = p5ioc2_phb_get_xive, .set_xive = p5ioc2_phb_set_xive, }; static void p5ioc2_phb_init_utl(struct p5ioc2_phb *p __unused) { /* XXX FIXME */ } static void p5ioc2_phb_init_pcie(struct p5ioc2_phb *p) { int64_t ecap, aercap; ecap = pci_find_cap(&p->phb, 0, PCI_CFG_CAP_ID_EXP); if (ecap < 0) { /* Shouldn't happen */ prerror("P5IOC2: Failed to locate PCI-E cap in bridge\n"); return; } p->ecap = ecap; aercap = pci_find_ecap(&p->phb, 0, PCIECAP_ID_AER, NULL); if (aercap < 0) { /* Shouldn't happen */ prerror("P5IOC2: Failed to locate AER ext cap in bridge\n"); return; } p->aercap = aercap; /* XXX plenty more to do ... */ } static void p5ioc2_phb_hwinit(struct p5ioc2_phb *p) { uint16_t pcicmd; uint32_t phbid; printf("P5IOC2: Initializing PHB HW...\n"); /* Enable PHB and and disable address decoding */ phbid = in_be32(p->ca_regs + CA_PHBIDn(p->index)); phbid |= CA_PHBID_PHB_ENABLE; phbid &= ~CA_PHBID_ADDRSPACE_ENABLE; out_be32(p->ca_regs + CA_PHBIDn(p->index), phbid); /* Set BUID */ out_be32(p->regs + CAP_BUID, SETFIELD(CAP_BUID_MASK, 0, P7_BUID_BASE(p->buid))); out_be32(p->regs + CAP_MSIBASE, P7_BUID_BASE(p->buid) << 16); /* Set IO and Memory mapping */ out_be32(p->regs + CAP_IOAD_H, hi32(p->io_base + IO_PCI_START)); out_be32(p->regs + CAP_IOAD_L, lo32(p->io_base + IO_PCI_START)); out_be32(p->regs + CAP_IOSZ, ~(IO_PCI_SIZE - 1)); out_be32(p->regs + CAP_IO_ST, IO_PCI_START); out_be32(p->regs + CAP_MEM1_H, hi32(p->mm_base + MM_PCI_START)); out_be32(p->regs + CAP_MEM1_L, lo32(p->mm_base + MM_PCI_START)); out_be32(p->regs + CAP_MSZ1, ~(MM_PCI_SIZE - 1)); out_be32(p->regs + CAP_MEM_ST, MM_PCI_START); /* Setup the MODE registers. We captures the values used * by pHyp/OPAL */ out_be32(p->regs + CAP_MODE0, 0x00800010); out_be32(p->regs + CAP_MODE1, 0x00800000); out_be32(p->regs + CAP_MODE3, 0xFFC00050); if (p->is_pcie) out_be32(p->regs + CAP_MODE2, 0x00000400); else out_be32(p->regs + CAP_MODE2, 0x00000408); /* XXX Setup of the arbiter... not sure what to do here, * probably system specific (depends on whow things are * wired on the motherboard). I set things up based on * the values I read on a Juno machine. We setup the BPR * with the various timeouts etc... as well based one * similarly captured values */ if (p->is_pcie) { out_be32(p->regs + CAP_AER, 0x04000000); out_be32(p->regs + CAP_BPR, 0x0000004f); } else { out_be32(p->regs + CAP_AER, 0x84000000); out_be32(p->regs + CAP_BPR, 0x000f00ff); } /* XXX Setup error reporting registers */ /* Clear errors in PLSSR and DMACSR */ out_be32(p->regs + CAP_DMACSR, 0); out_be32(p->regs + CAP_PLSSR, 0); /* Configure MSIs on PCIe only */ if (p->is_pcie) { /* XXX Check that setting ! That's what OPAL uses but * I suspect it might not be correct. We enable a masking * of 3 bits and no offset, which makes me think only * some MSIs will work... not 100% certain. */ out_be32(p->regs + CAP_MVE0, CAP_MVE_VALID | SETFIELD(CAP_MVE_TBL_OFF, 0, 0) | SETFIELD(CAP_MVE_NUM_INT, 0, 0x3)); out_be32(p->regs + CAP_MVE1, 0); } /* Configuration. We keep TCEs disabled */ out_be32(p->regs + CAP_PCFGRW, CAP_PCFGRW_ERR_RECOV_EN | CAP_PCFGRW_FREEZE_EN | CAP_PCFGRW_DAC_DISABLE | (p->is_pcie ? CAP_PCFGRW_MSI_EN : 0)); /* Re-enable address decode */ phbid |= CA_PHBID_ADDRSPACE_ENABLE; out_be32(p->ca_regs + CA_PHBIDn(p->index), phbid); /* PCIe specific inits */ if (p->is_pcie) { p5ioc2_phb_init_utl(p); p5ioc2_phb_init_pcie(p); } /* Take out reset pins on PCI-X. PCI-E will be handled via the hotplug * controller separately */ if (!p->is_pcie) { uint32_t val; /* Setting 1's will deassert the reset signals */ out_be32(p->regs + CAP_CRR, CAP_CRR_RESET1 | CAP_CRR_RESET2); /* Set max sub bus */ p5ioc2_pcicfg_write8(&p->phb, 0, 0x41, 0xff); /* XXX SHPC stuff */ printf("P5IOC2: SHPC Slots available 1 : %08x\n", in_be32(p->regs + 0xb20)); printf("P5IOC2: SHPC Slots available 2 : %08x\n", in_be32(p->regs + 0xb24)); printf("P5IOC2: SHPC Slots config : %08x\n", in_be32(p->regs + 0xb28)); printf("P5IOC2: SHPC Secondary bus conf : %08x\n", in_be32(p->regs + 0xb2c)); p5ioc2_pcicfg_read32(&p->phb, 0, 0, &val); printf("P5IOC2: val0: %08x\n", val); p5ioc2_pcicfg_read32(&p->phb, 0, 4, &val); printf("P5IOC2: val4: %08x\n", val); } /* Enable PCI command/status */ p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_CMD, &pcicmd); pcicmd |= PCI_CFG_CMD_IO_EN | PCI_CFG_CMD_MEM_EN | PCI_CFG_CMD_BUS_MASTER_EN; p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_CMD, pcicmd); p->state = P5IOC2_PHB_STATE_FUNCTIONAL; } static void p5ioc2_pcie_add_node(struct p5ioc2_phb *p) { uint64_t reg[2], mmb, iob; uint32_t lsibase, icsp = get_ics_phandle(); struct dt_node *np; reg[0] = cleanup_addr((uint64_t)p->regs); reg[1] = 0x1000; np = dt_new_addr(p->ioc->dt_node, "pciex", reg[0]); if (!np) return; p->phb.dt_node = np; dt_add_property_strings(np, "compatible", "ibm,p5ioc2-pciex"); dt_add_property_strings(np, "device_type", "pciex"); dt_add_property(np, "reg", reg, sizeof(reg)); dt_add_property_cells(np, "#address-cells", 3); dt_add_property_cells(np, "#size-cells", 2); dt_add_property_cells(np, "#interrupt-cells", 1); dt_add_property_cells(np, "bus-range", 0, 0xff); dt_add_property_cells(np, "clock-frequency", 0x200, 0); /* ??? */ dt_add_property_cells(np, "interrupt-parent", icsp); /* XXX FIXME: add phb own interrupts */ dt_add_property_cells(np, "ibm,opal-num-pes", 1); dt_add_property_cells(np, "ibm,opal-msi-ranges", (p->buid << 4) + 5, 8); /* XXX FIXME: add slot-name */ iob = cleanup_addr(p->io_base + IO_PCI_START); mmb = cleanup_addr(p->mm_base + MM_PCI_START); dt_add_property_cells(np, "ranges", /* IO space */ 0x01000000, 0x00000000, 0x00000000, hi32(iob), lo32(iob), 0, IO_PCI_SIZE, /* M32 space */ 0x02000000, 0x00000000, MM_PCI_START, hi32(mmb), lo32(mmb), 0, MM_PCI_SIZE); /* Add associativity properties */ add_chip_dev_associativity(np); /* The interrupt maps will be generated in the RC node by the * PCI code based on the content of this structure: */ lsibase = p->buid << 4; p->phb.lstate.int_size = 1; p->phb.lstate.int_val[0][0] = lsibase + 1; p->phb.lstate.int_val[1][0] = lsibase + 2; p->phb.lstate.int_val[2][0] = lsibase + 3; p->phb.lstate.int_val[3][0] = lsibase + 4; p->phb.lstate.int_parent[0] = icsp; p->phb.lstate.int_parent[1] = icsp; p->phb.lstate.int_parent[2] = icsp; p->phb.lstate.int_parent[3] = icsp; /* reset clear timestamp... to add if we do a reset and want * to avoid waiting in skiboot */ //dt_property_cells("reset-clear-timestamp",.... } static void p5ioc2_pcix_add_node(struct p5ioc2_phb *p) { uint64_t reg[2], mmb, iob; uint32_t lsibase, icsp = get_ics_phandle(); struct dt_node *np; reg[0] = cleanup_addr((uint64_t)p->regs); reg[1] = 0x1000; np = dt_new_addr(p->ioc->dt_node, "pci", reg[0]); if (!np) return; p->phb.dt_node = np; dt_add_property_strings(np, "compatible", "ibm,p5ioc2-pcix"); dt_add_property_strings(np, "device_type", "pci"); dt_add_property(np, "reg", reg, sizeof(reg)); dt_add_property_cells(np, "#address-cells", 3); dt_add_property_cells(np, "#size-cells", 2); dt_add_property_cells(np, "#interrupt-cells", 1); dt_add_property_cells(np, "bus-range", 0, 0xff); dt_add_property_cells(np, "clock-frequency", 0x200, 0); /* ??? */ //dt_add_property_cells(np, "bus-width", 8); /* Figure out from VPD ? */ dt_add_property_cells(np, "interrupt-parent", icsp); /* XXX FIXME: add phb own interrupts */ dt_add_property_cells(np, "ibm,opal-num-pes", 1); /* XXX FIXME: add slot-name */ iob = cleanup_addr(p->io_base + IO_PCI_START); mmb = cleanup_addr(p->mm_base + MM_PCI_START); dt_add_property_cells(np, "ranges", /* IO space */ 0x01000000, 0x00000000, 0x00000000, hi32(iob), lo32(iob), 0, IO_PCI_SIZE, /* M32 space */ 0x02000000, 0x00000000, MM_PCI_START, hi32(mmb), lo32(mmb), 0, MM_PCI_SIZE); /* Add associativity properties */ add_chip_dev_associativity(np); /* The interrupt maps will be generated in the RC node by the * PCI code based on the content of this structure: */ lsibase = p->buid << 4; p->phb.lstate.int_size = 1; p->phb.lstate.int_val[0][0] = lsibase + 1; p->phb.lstate.int_val[1][0] = lsibase + 2; p->phb.lstate.int_val[2][0] = lsibase + 3; p->phb.lstate.int_val[3][0] = lsibase + 4; p->phb.lstate.int_parent[0] = icsp; p->phb.lstate.int_parent[1] = icsp; p->phb.lstate.int_parent[2] = icsp; p->phb.lstate.int_parent[3] = icsp; /* On PCI-X we need to create an interrupt map here */ pci_std_swizzle_irq_map(np, NULL, &p->phb.lstate, 0); } void p5ioc2_phb_setup(struct p5ioc2 *ioc, struct p5ioc2_phb *p, uint8_t ca, uint8_t index, bool active, uint32_t buid) { uint32_t phbid; p->index = index; p->ca = ca; p->ioc = ioc; p->active = active; p->phb.ops = &p5ioc2_phb_ops; p->buid = buid; p->ca_regs = ca ? ioc->ca1_regs : ioc->ca0_regs; p->regs = p->ca_regs + CA_PHBn_REGS(index); printf("P5IOC2: Initializing PHB %d on CA%d, regs @%p, BUID 0x%04x\n", p->index, p->ca, p->regs, p->buid); /* Memory map: described in p5ioc2.h */ p->mm_base = ca ? ioc->ca1_mm_region : ioc->ca0_mm_region; p->mm_base += MM_WINDOW_SIZE * index; p->io_base = (uint64_t)p->ca_regs; p->io_base += IO_PCI_SIZE * (index + 1); p->state = P5IOC2_PHB_STATE_UNINITIALIZED; /* Query PHB type */ phbid = in_be32(p->ca_regs + CA_PHBIDn(p->index)); switch(GETFIELD(CA_PHBID_PHB_TYPE, phbid)) { case CA_PHBTYPE_PCIX1_0: p->is_pcie = false; p->phb.scan_map = 0x0003; p->phb.phb_type = phb_type_pcix_v1; printf("P5IOC2: PHB is PCI/PCI-X 1.0\n"); break; case CA_PHBTYPE_PCIX2_0: p->is_pcie = false; p->phb.scan_map = 0x0003; p->phb.phb_type = phb_type_pcix_v2; printf("P5IOC2: PHB is PCI/PCI-X 2.0\n"); break; case CA_PHBTYPE_PCIE_G1: p->is_pcie = true; p->phb.scan_map = 0x0001; p->phb.phb_type = phb_type_pcie_v1; printf("P5IOC2: PHB is PCI Express Gen 1\n"); break; case CA_PHBTYPE_PCIE_G2: p->is_pcie = true; p->phb.scan_map = 0x0001; p->phb.phb_type = phb_type_pcie_v2; printf("P5IOC2: PHB is PCI Express Gen 2\n"); break; default: printf("P5IOC2: Unknown PHB type ! phbid=%08x\n", phbid); p->is_pcie = true; p->phb.scan_map = 0x0001; p->phb.phb_type = phb_type_pcie_v1; } /* Find P5IOC2 base location code in IOC */ p->phb.base_loc_code = dt_prop_get_def(ioc->dt_node, "ibm,io-base-loc-code", NULL); if (!p->phb.base_loc_code) prerror("P5IOC2: Base location code not found !\n"); /* Add device nodes */ if (p->is_pcie) p5ioc2_pcie_add_node(p); else p5ioc2_pcix_add_node(p); /* Initialize PHB HW */ p5ioc2_phb_hwinit(p); /* Register all 16 interrupt sources for now as OS visible * * If we ever add some EEH, we might take out the error interrupts * and register them as OPAL internal interrupts instead */ register_irq_source(&p5ioc2_phb_os_irq_ops, p, p->buid << 4, 16); /* We cannot query the PHB type yet as the registers aren't routed * so we'll do that in the inits, at which point we'll establish * the scan map */ /* We register the PHB before we initialize it so we * get a useful OPAL ID for it */ pci_register_phb(&p->phb); /* Platform additional setup */ if (platform.pci_setup_phb) platform.pci_setup_phb(&p->phb, p->index); } skiboot-skiboot-5.1.13/hw/p5ioc2.c000066400000000000000000000212601265204436200166130ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include static int64_t p5ioc2_set_tce_mem(struct io_hub *hub, uint64_t address, uint64_t size) { struct p5ioc2 *ioc = iohub_to_p5ioc2(hub); int64_t rc; printf("P5IOC2: set_tce_mem(0x%016llx size 0x%llx)\n", address, size); /* The address passed must be naturally aligned */ if (address && !is_pow2(size)) return OPAL_PARAMETER; if (address & (size - 1)) return OPAL_PARAMETER; ioc->tce_base = address; ioc->tce_size = size; rc = gx_configure_tce_bar(ioc->host_chip, ioc->gx_bus, address, size); if (rc) return OPAL_INTERNAL_ERROR; return OPAL_SUCCESS; } static int64_t p5ioc2_get_diag_data(struct io_hub *hub __unused, void *diag_buffer __unused, uint64_t diag_buffer_len __unused) { /* XXX Not yet implemented */ return OPAL_UNSUPPORTED; } static const struct io_hub_ops p5ioc2_hub_ops = { .set_tce_mem = p5ioc2_set_tce_mem, .get_diag_data = p5ioc2_get_diag_data, }; static void p5ioc2_inits(struct p5ioc2 *ioc) { uint64_t val; unsigned int p, n; printf("P5IOC2: Initializing hub...\n"); /* * BML base inits */ /* mask off interrupt presentation timeout in FIRMC */ out_be64(ioc->regs + (P5IOC2_FIRMC | P5IOC2_REG_OR), 0x0000080000000000); /* turn off display alter mode */ out_be64(ioc->regs + (P5IOC2_CTL | P5IOC2_REG_AND), 0xffffff7fffffffff); /* setup hub and clustering interrupts BUIDs to 1 and 2 */ out_be64(ioc->regs + P5IOC2_SBUID, 0x0001000200000000); /* setup old style MSI BUID (should be unused but set it up anyway) */ out_be32(ioc->regs + P5IOC2_BUCO, 0xf); /* Set XIXO bit 0 needed for "enhanced" TCEs or else TCE * fetches appear as normal memory reads on GX causing * P7 to checkstop when a TCE DKill collides with them. */ out_be64(ioc->regs + P5IOC2_XIXO, in_be64(ioc->regs + P5IOC2_XIXO) | P5IOC2_XIXO_ENH_TCE); /* Clear routing tables */ for (n = 0; n < 16; n++) { for (p = 0; p < 8; p++) out_be64(ioc->regs + P5IOC2_TxRTE(p,n), 0); } for (n = 0; n < 32; n++) out_be64(ioc->regs + P5IOC2_BUIDRTE(n), 0); /* * Setup routing. We use the same setup that pHyp appears * to do (after inspecting the various registers with SCOM) * * We assume the BARs are already setup by the FSP such * that BAR0 is 128G (8G region size) and BAR6 is * 256M (16M region size). * * The routing is based on what pHyp and BML do, each Calgary * get one slice of BAR6 and two slices of BAR0 */ /* BAR 0 segments 0 & 1 -> CA0 */ out_be64(ioc->regs + P5IOC2_TxRTE(0,0), P5IOC2_TxRTE_VALID | P5IOC2_CA0_RIO_ID); out_be64(ioc->regs + P5IOC2_TxRTE(0,1), P5IOC2_TxRTE_VALID | P5IOC2_CA0_RIO_ID); /* BAR 0 segments 2 & 3 -> CA1 */ out_be64(ioc->regs + P5IOC2_TxRTE(0,2), P5IOC2_TxRTE_VALID | P5IOC2_CA1_RIO_ID); out_be64(ioc->regs + P5IOC2_TxRTE(0,3), P5IOC2_TxRTE_VALID | P5IOC2_CA1_RIO_ID); /* BAR 6 segments 0 -> CA0 */ out_be64(ioc->regs + P5IOC2_TxRTE(6,0), P5IOC2_TxRTE_VALID | P5IOC2_CA0_RIO_ID); /* BAR 6 segments 1 -> CA0 */ out_be64(ioc->regs + P5IOC2_TxRTE(6,1), P5IOC2_TxRTE_VALID | P5IOC2_CA1_RIO_ID); /* * BUID routing, we send entries 1 to CA0 and 2 to CA1 * just like pHyp and make sure the base and mask are * both clear in SID to we route the whole 512 block */ val = in_be64(ioc->regs + P5IOC2_SID); val = SETFIELD(P5IOC2_SID_BUID_BASE, val, 0); val = SETFIELD(P5IOC2_SID_BUID_MASK, val, 0); out_be64(ioc->regs + P5IOC2_SID, val); out_be64(ioc->regs + P5IOC2_BUIDRTE(1), P5IOC2_BUIDRTE_VALID | P5IOC2_BUIDRTE_RR_RET | P5IOC2_CA0_RIO_ID); out_be64(ioc->regs + P5IOC2_BUIDRTE(2), P5IOC2_BUIDRTE_VALID | P5IOC2_BUIDRTE_RR_RET | P5IOC2_CA1_RIO_ID); } static void p5ioc2_ca_init(struct p5ioc2 *ioc, int ca) { void *regs = ca ? ioc->ca1_regs : ioc->ca0_regs; uint64_t val; printf("P5IOC2: Initializing Calgary %d...\n", ca); /* Setup device BUID */ val = SETFIELD(CA_DEVBUID_MASK, 0ul, ca ? P5IOC2_CA1_BUID : P5IOC2_CA0_BUID); out_be32(regs + CA_DEVBUID, val); /* Setup HubID in TARm (and keep TCE clear, Linux will init that) * * BML and pHyp sets the values to 1 for CA0 and 4 for CA1. We * keep the TAR valid bit clear as well. */ val = SETFIELD(CA_TAR_HUBID, 0ul, ca ? 4 : 1); val = SETFIELD(CA_TAR_ALTHUBID, val, ca ? 4 : 1); out_be64(regs + CA_TAR0, val); out_be64(regs + CA_TAR1, val); out_be64(regs + CA_TAR2, val); out_be64(regs + CA_TAR3, val); /* Bridge config register. We set it up to the same value as observed * under pHyp on a Juno machine. The difference from the IPL value is * that TCE buffers are enabled, discard timers are increased and * we disable response status to avoid errors. */ //out_be64(regs + CA_CCR, 0x5045DDDED2000000); // disable memlimit: out_be64(regs + CA_CCR, 0x5005DDDED2000000); /* The system memory base/limit etc... setup will be done when the * user enables TCE via OPAL calls */ } static void p5ioc2_create_hub(struct dt_node *np) { struct p5ioc2 *ioc; unsigned int i, id, irq; char *path; /* Use the BUID extension as ID and add it to device-tree */ id = dt_prop_get_u32(np, "ibm,buid-ext"); path = dt_get_path(np); printf("P5IOC2: Found at %s ID 0x%x\n", path, id); free(path); dt_add_property_cells(np, "ibm,opal-hubid", 0, id); /* Load VPD LID */ vpd_preload(np); vpd_iohub_load(np); ioc = zalloc(sizeof(struct p5ioc2)); if (!ioc) return; ioc->hub.hub_id = id; ioc->hub.ops = &p5ioc2_hub_ops; ioc->dt_node = np; /* We assume SBAR == GX0 + some hard coded offset */ ioc->regs = (void *)dt_get_address(np, 0, NULL); /* For debugging... */ for (i = 0; i < 8; i++) printf("P5IOC2: BAR%d = 0x%016llx M=0x%16llx\n", i, in_be64(ioc->regs + P5IOC2_BAR(i)), in_be64(ioc->regs + P5IOC2_BARM(i))); ioc->host_chip = dt_get_chip_id(np); ioc->gx_bus = dt_prop_get_u32(np, "ibm,gx-index"); /* Rather than reading the BARs in P5IOC2, we "know" that * BAR6 matches GX BAR 1 and BAR0 matches GX BAR 2. This * is a bit fishy but will work for the few machines this * is intended to work on */ ioc->bar6 = dt_prop_get_u64(np, "ibm,gx-bar-1"); ioc->bar0 = dt_prop_get_u64(np, "ibm,gx-bar-2"); printf("DT BAR6 = 0x%016llx\n", ioc->bar6); printf("DT BAR0 = 0x%016llx\n", ioc->bar0); /* We setup the corresponding Calgary register bases and memory * regions. Note: those cannot be used until the routing has * been setup by inits */ ioc->ca0_regs = (void *)ioc->bar6 + P5IOC2_CA0_REG_OFFSET; ioc->ca1_regs = (void *)ioc->bar6 + P5IOC2_CA1_REG_OFFSET; ioc->ca0_mm_region = ioc->bar0 + P5IOC2_CA0_MM_OFFSET; ioc->ca1_mm_region = ioc->bar0 + P5IOC2_CA1_MM_OFFSET; /* Base of our BUIDs, will be refined later */ ioc->buid_base = id << 9; /* Add interrupts: XXX These are the hub interrupts, we should add the * calgary ones as well... but we don't handle any of them currently * anyway. */ irq = (ioc->buid_base + 1) << 4; dt_add_property_cells(np, "interrupts", irq, irq + 1); dt_add_property_cells(np, "interrupt-base", irq); /* Now, we do the bulk of the inits */ p5ioc2_inits(ioc); p5ioc2_ca_init(ioc, 0); p5ioc2_ca_init(ioc, 1); /* So how do we know what PHBs to create ? Let's try all of them * and we'll see if that causes problems. TODO: Use VPD ! */ for (i = 0; i < 4; i++) p5ioc2_phb_setup(ioc, &ioc->ca0_phbs[i], 0, i, true, ioc->buid_base + P5IOC2_CA0_BUID + i + 1); for (i = 0; i < 4; i++) p5ioc2_phb_setup(ioc, &ioc->ca1_phbs[i], 1, i, true, ioc->buid_base + P5IOC2_CA1_BUID + i + 1); /* Reset delay... synchronous, hope we never do that as a * result of an OPAL callback. We shouldn't really need this * here and may fold it in the generic slot init sequence but * it's not like we care much about that p5ioc2 code... * * This is mostly to give devices a chance to settle after * having lifted the reset pin on PCI-X. */ time_wait_ms(1000); printf("P5IOC2: Initialization complete\n"); cec_register(&ioc->hub); } void probe_p5ioc2(void) { struct dt_node *np; dt_for_each_compatible(dt_root, np, "ibm,p5ioc2") p5ioc2_create_hub(np); } skiboot-skiboot-5.1.13/hw/p7ioc-inits.c000066400000000000000000001067341265204436200176710ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This inits are in part auto-generated from tables coming * from the HW guys, then hand updated */ #include #include #include #include #include #include #undef DUMP_CI_ROUTING #undef DUMP_REG_WRITES #ifdef DUMP_REG_WRITES #define REGW(offset, value) do { \ out_be64(ioc->regs + (offset), (value)); \ printf(" REGW: %06lx=%016lx RB: %016llx\n", \ (unsigned long)(offset), \ (unsigned long)(value), \ in_be64(ioc->regs + (offset))); \ in_be64(ioc->regs + (offset)); \ } while(0) #else #define REGW(offset, value) do { \ out_be64(ioc->regs + (offset), (value)); \ in_be64(ioc->regs + (offset)); \ } while(0) #endif #define REGR(offset) in_be64(ioc->regs + (offset)) static void p7ioc_init_BI(struct p7ioc *ioc) { printf("P7IOC: Init BI...\n"); /*** General settings ***/ /* Init_1 and Init_2: Different between P7 and P7+ */ if (PVR_TYPE(mfspr(SPR_PVR)) == PVR_TYPE_P7P) { printf("P7IOC: -> Configured for P7+\n"); /* Chicken switches */ REGW(0x3c00d8, 0x0004000000000600); /* GX config */ REGW(0x3c00a0, 0x9F8929BE00880085); } else { printf("P7IOC: -> Configured for P7\n"); /* P7 setting assumes "early write done" mode is * enabled in the GX controller. It seems to be * the case but maybe we want to check/set it via * xscom ? */ /* Chicken switches */ REGW(0x3c00d8, 0x00040000000004C0); /* GX config */ REGW(0x3c00a0, 0x9C8929BE00880085); } /* * Note: While running skiboot on Firebird-L, I have * to print something or wait for a while. The root * cause wasn't identified yet. */ time_wait_ms(100); /* Init_3: Upbound Credit Config */ REGW(0x3c00c8, 0x0303060403030000); /* Init_4: Credit Init Timer */ REGW(0x3c00e8, 0x00000000000000FF); /* Init_4.1: BI Ack Timing */ REGW(0x3c00e8, 0x0000FC0000000000); /* Init_5: Ordering Override 0*/ REGW(0x3c0200, 0x0000000000000000); /* Init_6: Ordering Override 1*/ REGW(0x3c0208, 0x0000000000000000); /*** Downbound TTYPE table ***/ /* Init_7: Enable sequence / speculation for CI Loads */ REGW(0x3c00a8, 0x0000000000000004); /* Init_8: */ REGW(0x3c00b0, 0x700800C000000000); /* Init_9: Enable sequence / speculation for CI Stores */ REGW(0x3c00a8, 0x0000000000000005); /* Init_10: */ REGW(0x3c00b0, 0x704820C000000000); /* Init_11: Enable speculation for EOI */ REGW(0x3c00a8, 0x000000000000001B); /* Init_12: */ REGW(0x3c00b0, 0x3590204000000000); /* Init_13: ENable speculation for DMA Rd Responses */ REGW(0x3c00a8, 0x0000000000000020); /* Init_14: */ REGW(0x3c00b0, 0x1103C4C000000000); /* Init_15: Enable sequence for DMA RWNITC */ REGW(0x3c00a8, 0x0000000000000001); /* Init_16: */ REGW(0x3c00b0, 0xC000000000000000); /* Init_17: Enable sequence for IOKill */ REGW(0x3c00a8, 0x0000000000000009); /* Init_18: */ REGW(0x3c00b0, 0x4208210000000000); /* Init_19: Enable sequence for IOKill */ REGW(0x3c00a8, 0x000000000000000A); /* Init_20: */ REGW(0x3c00b0, 0x4200210000000000); /* Init_21: Enable sequence for FMTC CI Store w/Kill */ REGW(0x3c00a8, 0x0000000000000021); /*** Timer controls ***/ /* Init_22: */ REGW(0x3c00b0, 0x4200300000000000); /* Init_23: Dnbound timer mask */ REGW(0x3c0190, 0x0040000000000000); /* Init_24: Upbound timer mask 0 */ REGW(0x3c0180, 0x0010001000100010); /* Init_25: Upbound timer mask 1 */ REGW(0x3c0188, 0x0010000000000000); /* Init_26: Credit sync check config */ REGW(0x3c00f0, 0xC102000000000000); /*** Setup trace ***/ /* Init_27: DBG stop trace */ REGW(0x3c0410, 0x4000000000000000); /* Init_28: DBG control */ REGW(0x3c0400, 0x0000000000000000); /* Init_29: DBG Mode */ REGW(0x3c0408, 0xA0000000F0CC3300); /* Init_29a: DBG C0 (Stop on Error) */ REGW(0x3c0418, 0xF4F00FFF00000000); /* Init_30: DBG pre-mux select */ REGW(0x3c0478, 0x0023000000000000); /* Init_31: CA0 mode */ REGW(0x3c04b0, 0x8000000000000000); /* Init_32: CA0 Compression 0 */ REGW(0x3c04b8, 0x0000000000000000); /* Init_33: CA0 Compression 1 */ REGW(0x3c04c0, 0x0000000000000000); /* Init_34: CA0 Pattern A march (cmd1 selected val) */ REGW(0x3c0480, 0x008000007FFFFF00); /* Init_35: CA0 Trigger 0 definition (pattern A) */ REGW(0x3c04a0, 0x8000000000000000); /* Init_36: CA1 mode */ REGW(0x3c0530, 0x8000000000000000); /* Init_37: CA1 Compression 0 */ REGW(0x3c0538, 0x0000000000000000); /* Init_38: CA1 Compression 1 */ REGW(0x3c0540, 0x0000000000000000); /* Init_39: CA2 mode */ REGW(0x3c05b0, 0x8000000000000000); /* Init_40: CA2 Compression 0 */ REGW(0x3c05b8, 0x0000000000000000); /* Init_41: CA2 Compression 1 */ REGW(0x3c05c0, 0x0000000000000000); /* Init_42: CA3 Mode */ REGW(0x3c0630, 0x8000000000000000); /* Init_43: CA3 Compression 0 */ REGW(0x3c0638, 0x0000000000000000); /* Init_44: CA3 Compression 1 */ REGW(0x3c0640, 0x0000000000000000); /* Init_45: CA3 Pattern A match (AIB val) */ REGW(0x3c0600, 0x80000100FFFEFF00); /* Init_46: CA3 Trigger 0 definition (pattern A) */ REGW(0x3c0620, 0x8000000000000000); /* Init_47: DBG unfreeze trace */ REGW(0x3c0410, 0x1000000000000000); /* Init_48: DBG start trace */ REGW(0x3c0410, 0x8000000000000000); /*** AIB Port Config ***/ /* Init_49: AIB Port Information */ REGW(0x3c00d0, 0x0888888800000000); /* Init_50: Port Ordering controls */ REGW(0x3c0200, 0x0000000000000000); /*** LEMs (need to match recov. tables) ***/ /* Init_51: Clear upbound LEM */ REGW(0x3c0000, 0x0000000000000000); /* Init_52: Clear upbound WOF */ REGW(0x3c0040, 0x0000000000000000); /* Init_53: Clear Dnbound LEM */ REGW(0x3c0050, 0x0000000000000000); /* Init_54: Clear Dnbound WOF */ REGW(0x3c0090, 0x0000000000000000); /* Init_55: Clear Fences */ REGW(0x3c0130, 0x0000000000000000); /* Init_56: Clear Erpt latches */ REGW(0x3c0148, 0x0080000000000000); /* Init_57: Set Upbound LEM Action0 */ REGW(0x3c0030, 0x0800000000800000); /* Init_58: Set Upbound LEN Action1 */ REGW(0x3c0038, 0x0000000000000000); /* Init_59: Set Upbound LEM Mask (AND write) */ REGW(0x3c0020, 0x0800000000000000); /* Init_60: Set Dnbound LEM Action0 */ REGW(0x3c0080, 0x2000080CA07FFF40); /* Init_61: Set Dnbound LEM Action1 */ REGW(0x3c0088, 0x0000000000000000); /* Init_62: Set Dnbound LEM Mask (AND write) */ REGW(0x3c0070, 0x00000800200FFE00); /*** Setup Fences (need to match recov. tables) ***/ /* Init_63: Set Upbound Damage Control 0 (GX Err) */ REGW(0x3c0100, 0xF7FFFFFFFF7FFFFF); /* Init_64: Set Upbound Damage Control 1 (AIB Fence) */ REGW(0x3c0108, 0xF7FFFFFFFF7FFFFF); /* Init_65: Set Upbound Damage Control 2 (Drop Pkt) */ REGW(0x3c0110, 0x0010054000000000); /* Init_66: Set Dnbound Damage Control 0 (GX Err) */ REGW(0x3c0118, 0xDFFFF7F35F8000BF); /* Init_67: Set Dnbound Damage Control 1 (AIB Fence) */ REGW(0x3c0120, 0xDFFFF7F35F8000BF); /* Init_68: Set Dnbound Damage Control 2 (Drop Pkt) */ REGW(0x3c0128, 0x0000000C00000000); } static void p7ioc_init_MISC_HSS(struct p7ioc *ioc) { unsigned int i, regbase; printf("P7IOC: Init HSS...\n"); /* Note: These values might need to be tweaked per system and * per physical port depending on electrical characteristics. * * For now we stick to the defaults provided by the spec. */ for (i = 0; i < P7IOC_NUM_PHBS; i++) { regbase = P7IOC_HSS_BASE + i * P7IOC_HSS_STRIDE; if (!p7ioc_phb_enabled(ioc, i)) continue; /* Init_1: HSSn CTL2 */ REGW(regbase + P7IOC_HSSn_CTL2_OFFSET, 0xFFFF6DB6DB000000); /* Init_2: HSSn CTL3 */ REGW(regbase + P7IOC_HSSn_CTL3_OFFSET, 0x1130000320000000); /* Init_3: HSSn CTL8 */ REGW(regbase + P7IOC_HSSn_CTL8_OFFSET, 0xDDDDDDDD00000000); #if 0 /* All these remain set to the values configured by the FSP */ /* Init_4: HSSn CTL9 */ REGW(regbase + P7IOC_HSSn_CTL9_OFFSET, 0x9999999900000000); /* Init_5: HSSn CTL10 */ REGW(regbase + P7IOC_HSSn_CTL10_OFFSET, 0x8888888800000000); /* Init_6: HSSn CTL11 */ REGW(regbase + P7IOC_HSSn_CTL11_OFFSET, 0x4444444400000000); /* Init_7: HSSn CTL12 */ REGW(regbase + P7IOC_HSSn_CTL12_OFFSET, 0x3333333300000000); /* Init_8: HSSn CTL13 */ REGW(regbase + P7IOC_HSSn_CTL13_OFFSET, 0x2222222200000000); /* Init_9: HSSn CTL14 */ REGW(regbase + P7IOC_HSSn_CTL14_OFFSET, 0x1111111100000000); /* Init_10: HSSn CTL15 */ REGW(regbase + P7IOC_HSSn_CTL15_OFFSET, 0x1111111100000000); /* Init_11: HSSn CTL16 */ REGW(regbase + P7IOC_HSSn_CTL16_OFFSET, 0x9999999900000000); /* Init_12: HSSn CTL17 */ REGW(regbase + P7IOC_HSSn_CTL17_OFFSET, 0x8888888800000000); /* Init_13: HSSn CTL18 */ REGW(regbase + P7IOC_HSSn_CTL18_OFFSET, 0xDDDDDDDD00000000); /* Init_14: HSSn CTL19 */ REGW(regbase + P7IOC_HSSn_CTL19_OFFSET, 0xCCCCCCCC00000000); /* Init_15: HSSn CTL20 */ REGW(regbase + P7IOC_HSSn_CTL20_OFFSET, 0xBBBBBBBB00000000); /* Init_16: HSSn CTL21 */ REGW(regbase + P7IOC_HSSn_CTL21_OFFSET, 0x9999999900000000); /* Init_17: HSSn CTL22 */ REGW(regbase + P7IOC_HSSn_CTL22_OFFSET, 0x8888888800000000); /* Init_18: HSSn CTL23 */ REGW(regbase + P7IOC_HSSn_CTL23_OFFSET, 0x7777777700000000); #endif } } static void p7ioc_init_RGC(struct p7ioc *ioc) { unsigned int i; uint64_t val, cfg; printf("P7IOC: Init RGC...\n"); /*** Clear ERPT Macros ***/ /* Init_1: RGC Configuration reg */ cfg = REGR(0x3e1c08); REGW(0x3e1c08, cfg | PPC_BIT(1)); time_wait_ms(1); /* Init_2: RGC Configuration reg */ REGW(0x3e1c08, cfg); /*** Set LEM regs (needs to match recov. code) */ /* Init_3: LEM FIR Accumulator */ REGW(0x3e1e00, 0x0000000000000000); /* Init_4: LEM Action 0 */ REGW(0x3e1e30, 0x0FFF791F0B030000); /* Init_5: LEN Action 1 */ REGW(0x3e1e38, 0x0000000000000000); /* Init_6: LEM WOF */ REGW(0x3e1e40, 0x0000000000000000); /* Init_7: LEM Mask Reg (AND write) */ REGW(0x3e1e20, 0x0FFF001F03030000); /*** Set GEM regs (masks still on, no irpts can occur yet) ***/ /* Init_8: GEM XFIR */ REGW(0x3e0008, 0x0000000000000000); /* Init_9: GEM WOF */ REGW(0x3e0028, 0x0000000000000000); /*** Set Damage Controls (needs to match recov.) ***/ /* Init_10: LDCP */ REGW(0x3e1c18, 0xF00086C0B4FCFFFF); /*** Read status (optional) ***/ /* Init_11: Read status */ val = REGR(0x3e1c10); printf("P7IOC: Init_11 Status: %016llx\n", val); /*** Set running configuration **/ /* Init_12: Configuration reg (modes, values, timers) */ REGW(0x3e1c08, 0x10000077CE100000); /* Init_13: Cmd/Dat Crd Allocation */ REGW(0x3e1c20, 0x00000103000700FF); /* Init_14: GP reg - disable errs, wrap, stop_trc */ REGW(0x3e1018, 0x0000000000000000); /* Init_15: Configuration reg (start init timers) */ cfg = REGR(0x3e1c08); REGW(0x3e1c08, cfg | 0x00003f0000000000); /*** Setup interrupts ***/ /* Init_16: BUID Register * * XXX NOTE: This needs to be clarified. According to the doc * the register contains a 9-bit BUID, which makes sense so far. * * However, the initialization sequence says "depends on which * GX bus) which doesn't since afaik the GX bus number is encoded * in the BUID Extension bit which is right *above* the 9-bit * BUID in the interrupt message. * * So I must be missing something here... For now I'll just * write my 9-bit BUID and we'll see what happens. * */ REGW(0x3e1800, (uint64_t)ioc->rgc_buid << PPC_BITLSHIFT(31)); /* Init_17: Supposed to lock the IODA table but we aren't racing * with anybody so there is little point. * * Note: If/when we support some kind of error recovery that * involves re-initializing the IOC, then we might have * to take some locks but it's assumed that the necessary * lock(s) will be obtained by the caller. */ //REGR(0x3e1840, 0x0000000000000000); /* Init_18: IODA Table Addr: Select IST*/ REGW(0x3e1820, 0x8001000000000000); /* Init_19: IODA Table Data: IRPT 0 */ REGW(0x3e1830, 0x0000000000000000); /* Init_20: IODA Table Data: IRPT 1 */ REGW(0x3e1830, 0x0000000000000000); /* Init_21: IODA Table Addr: Select HRT */ REGW(0x3e1820, 0x8000000000000000); /* Init_22: IODA Table Data: HRT * * XXX Figure out what this actually is and what value should * we use. For now, do like BML and use 0 */ for (i = 0; i < 4; i++) REGW(0x3e1830, 0x0000000000000000); /* Init_23: IODA Table Addr: select XIVT */ REGW(0x3e1820, 0x8002000000000000); /* Init_24: IODA Table Data: Mask all interrupts */ for (i = 0; i < 16; i++) REGW(0x3e1830, 0x000000ff00000000); /* Init_25: Clear table lock if any was stale */ REGW(0x3e1840, 0x0000000000000000); /* Init_32..37: Set the PHB AIB addresses. We configure those * to the values recommended in the p7IOC doc. * * XXX NOTE: I cannot find a documentation for these, I assume * they just take the full 64-bit address, but we may want to * dbl check just in case (it seems to be what BML does but * I'm good at mis-reading Milton's Perl). */ for (i = 0; i < P7IOC_NUM_PHBS; i++) { if (!p7ioc_phb_enabled(ioc, i)) continue; REGW(0x3e1080 + (i << 3), ioc->mmio1_win_start + PHBn_AIB_BASE(i)); } } static void p7ioc_init_ci_routing(struct p7ioc *ioc) { unsigned int i, j = 0; uint64_t rmatch[47]; uint64_t rmask[47]; uint64_t pmask; /* Init_130: clear all matches (except 47 which routes to the RGC) */ for (i = 0; i < 47; i++) { rmatch[i] = REGR(P7IOC_CI_RMATC_REG(i)) & ~(P7IOC_CI_RMATC_ADDR_VALID | P7IOC_CI_RMATC_BUID_VALID | P7IOC_CI_RMATC_TYPE_VALID); rmask[i] = 0; REGW(P7IOC_CI_RMATC_REG(i), rmatch[i]); } /* Init_131...224: configure routing for everything except RGC * * We are using a slightly different routing setup than the * example to make the code easier. We configure all PHB * routing entries by doing all of PHB0 first, then all of PHB1 * etc... * * Then we append everything else except the RGC itself which * remains hard wired at entry 47. So the unused entries live * at 39..46. * * - 0 : PHB0 LSI BUID * - 1 : PHB0 MSI BUID * - 2 : PHB0 AIB Registers * - 3 : PHB0 IO Space * - 4 : PHB0 M32 Space * - 5 : PHB0 M64 Space * - 6..11 : PHB1 * - 12..17 : PHB2 * - 18..23 : PHB3 * - 24..29 : PHB4 * - 30..35 : PHB5 * - 36 : Invalidates broadcast (FMTC) * - 37 : Interrupt response for RGC * - 38 : RGC GEM BUID * - 39..46 : Unused (alternate M64 ?) * - 47 : RGC ASB Registers (catch all) */ /* Helper macro to set a rule */ #define CI_ADD_RULE(p, k, d, m) do { \ rmask[j] = P7IOC_CI_RMATC_ENCODE_##k(m); \ rmatch[j]= P7IOC_CI_RMATC_PORT(p) | \ P7IOC_CI_RMATC_##k##_VALID | \ P7IOC_CI_RMATC_ENCODE_##k(d); \ j++; \ } while (0) pmask = 0; for (i = 0; i < P7IOC_NUM_PHBS; i++) { unsigned int buid_base = ioc->buid_base + PHBn_BUID_BASE(i); if (!p7ioc_phb_enabled(ioc, i)) continue; /* LSI BUIDs, match all 9 bits (1 BUID per PHB) */ CI_ADD_RULE(P7IOC_CI_PHB_PORT(i), BUID, buid_base + PHB_BUID_LSI_OFFSET, 0x1ff); /* MSI BUIDs, match 4 bits (16 BUIDs per PHB) */ CI_ADD_RULE(P7IOC_CI_PHB_PORT(i), BUID, buid_base + PHB_BUID_MSI_OFFSET, 0x1f0); /* AIB reg space */ CI_ADD_RULE(P7IOC_CI_PHB_PORT(i), ADDR, ioc->mmio1_win_start + PHBn_AIB_BASE(i), ~(PHBn_AIB_SIZE - 1)); /* IO space */ CI_ADD_RULE(P7IOC_CI_PHB_PORT(i), ADDR, ioc->mmio1_win_start + PHBn_IO_BASE(i), ~(PHB_IO_SIZE - 1)); /* M32 space */ CI_ADD_RULE(P7IOC_CI_PHB_PORT(i), ADDR, ioc->mmio2_win_start + PHBn_M32_BASE(i), ~(PHB_M32_SIZE - 1)); /* M64 space */ CI_ADD_RULE(P7IOC_CI_PHB_PORT(i), ADDR, ioc->mmio2_win_start + PHBn_M64_BASE(i), ~(PHB_M64_SIZE - 1)); /* For use with invalidate bcasts */ pmask |= P7IOC_CI_PHB_PORT(i); } /* Invalidates broadcast to all PHBs */ CI_ADD_RULE(pmask, TYPE, 0x80, 0xf0); /* Interrupt responses go to RGC */ CI_ADD_RULE(P7IOC_CI_RGC_PORT, TYPE, 0x60, 0xf0); /* RGC GEM BUID (1 BUID) */ CI_ADD_RULE(P7IOC_CI_RGC_PORT, BUID, ioc->rgc_buid, 0x1ff); /* Program the values masks first */ for (i = 0; i < 47; i++) REGW(P7IOC_CI_RMASK_REG(i), rmask[i]); for (i = 0; i < 47; i++) REGW(P7IOC_CI_RMATC_REG(i), rmatch[i]); /* Init_225: CI Match 47 (Configure RGC catch all) */ REGW(P7IOC_CI_RMASK_REG(47), 0x0000000000000000); REGW(P7IOC_CI_RMATC_REG(47), 0x4000800000000000); #ifdef DUMP_CI_ROUTING printf("P7IOC: CI Routing table:\n"); for (i = 0; i < 48; i++) printf(" [%.2d] MTCH: %016llx MSK: %016llx\n", i, REGR(P7IOC_CI_RMATC_REG(i)), REGR(P7IOC_CI_RMASK_REG(i))); #endif /* DUMP_CI_ROUTING */ } static void p7ioc_init_CI(struct p7ioc *ioc) { printf("P7IOC: Init CI...\n"); /*** Clear ERPT macros ***/ /* XXX NOTE: The doc seems to also provide "alternate freq ratio" * settings. Not sure what they are about, let's stick to the * original values for now. */ /* Init_1: CI Port 0 Configuration */ REGW(0x3d0000, 0x420000C0073F0002); /* Init_2: CI Port 0 Configuration */ REGW(0x3d0000, 0x020000C0073F0002); /* Init_3: CI Port 1 Configuration */ REGW(0x3d1000, 0x42000FCF07200002); /* Init_4: CI Port 1 Configuration */ REGW(0x3d1000, 0x02000FCF07200002); /* Init_5: CI Port 2 Configuration */ REGW(0x3d2000, 0x420000C307200002); /* Init_6: CI Port 2 Configuration */ REGW(0x3d2000, 0x020000C307200002); /* Init_7: CI Port 3 Configuration */ REGW(0x3d3000, 0x420000C307200002); /* Init_8: CI Port 3 Configuration */ REGW(0x3d3000, 0x020000C307200002); /* Init_9: CI Port 4 Configuration */ REGW(0x3d4000, 0x420000C307200002); /* Init_10: CI Port 4 Configuration */ REGW(0x3d4000, 0x020000C307200002); /* Init_11: CI Port 5 Configuration */ REGW(0x3d5000, 0x420000C307200002); /* Init_12: CI Port 5 Configuration */ REGW(0x3d5000, 0x020000C307200002); /* Init_13: CI Port 6 Configuration */ REGW(0x3d6000, 0x420000C307200002); /* Init_14: CI Port 6 Configuration */ REGW(0x3d6000, 0x020000C307200002); /* Init_15: CI Port 7 Configuration */ REGW(0x3d7000, 0x420000C307200002); /* Init_16: CI Port 7 Configuration */ REGW(0x3d7000, 0x020000C307200002); /*** Set LEM regs (need to match recov.) ***/ /* Init_17: CI Port 0 LEM FIR Accumulator */ REGW(0x3d0200, 0x0000000000000000); /* Init_18: CI Port 0 LEM Action 0 */ REGW(0x3d0230, 0x0A00000000000000); /* Init_19: CI Port 0 LEM Action 1 */ REGW(0x3d0238, 0x0000000000000000); /* Init_20: CI Port 0 LEM WOF */ REGW(0x3d0240, 0x0000000000000000); /* Init_21: CI Port 0 LEM Mask (AND write) */ REGW(0x3d0220, 0x0200000000000000); /* Init_22: CI Port 1 LEM FIR Accumularor */ REGW(0x3d1200, 0x0000000000000000); /* Init_23: CI Port 1 LEM Action 0 */ REGW(0x3d1230, 0x0000000000000000); /* Init_24: CI Port 1 LEM Action 1 */ REGW(0x3d1238, 0x0000000000000000); /* Init_25: CI Port 1 LEM WOF */ REGW(0x3d1240, 0x0000000000000000); /* Init_26: CI Port 1 LEM Mask (AND write) */ REGW(0x3d1220, 0x0000000000000000); /* Init_27: CI Port 2 LEM FIR Accumulator */ REGW(0x3d2200, 0x0000000000000000); /* Init_28: CI Port 2 LEM Action 0 */ REGW(0x3d2230, 0xA4F4000000000000); /* Init_29: CI Port 2 LEM Action 1 */ REGW(0x3d2238, 0x0000000000000000); /* Init_30: CI Port 2 LEM WOF */ REGW(0x3d2240, 0x0000000000000000); /* Init_31: CI Port 2 LEM Mask (AND write) */ REGW(0x3d2220, 0x0000000000000000); /* Init_32: CI Port 3 LEM FIR Accumulator */ REGW(0x3d3200, 0x0000000000000000); /* Init_33: CI Port 3 LEM Action 0 */ REGW(0x3d3230, 0xA4F4000000000000); /* Init_34: CI Port 3 LEM Action 1 */ REGW(0x3d3238, 0x0000000000000000); /* Init_35: CI Port 3 LEM WOF */ REGW(0x3d3240, 0x0000000000000000); /* Init_36: CI Port 3 LEM Mask (AND write) */ REGW(0x3d3220, 0x0000000000000000); /* Init_37: CI Port 4 LEM FIR Accumulator */ REGW(0x3d4200, 0x0000000000000000); /* Init_38: CI Port 4 Action 0 */ REGW(0x3d4230, 0xA4F4000000000000); /* Init_39: CI Port 4 Action 1 */ REGW(0x3d4238, 0x0000000000000000); /* Init_40: CI Port 4 WOF */ REGW(0x3d4240, 0x0000000000000000); /* Init_41: CI Port 4 Mask (AND write) */ REGW(0x3d4220, 0x0000000000000000); /* Init_42: CI Port 5 LEM FIR Accumulator */ REGW(0x3d5200, 0x0000000000000000); /* Init_43: CI Port 5 Action 0 */ REGW(0x3d5230, 0xA4F4000000000000); /* Init_44: CI Port 5 Action 1 */ REGW(0x3d5238, 0x0000000000000000); /* Init_45: CI Port 4 WOF */ REGW(0x3d5240, 0x0000000000000000); /* Init_46: CI Port 5 Mask (AND write) */ REGW(0x3d5220, 0x0000000000000000); /* Init_47: CI Port 6 LEM FIR Accumulator */ REGW(0x3d6200, 0x0000000000000000); /* Init_48: CI Port 6 Action 0 */ REGW(0x3d6230, 0xA4F4000000000000); /* Init_49: CI Port 6 Action 1 */ REGW(0x3d6238, 0x0000000000000000); /* Init_50: CI Port 6 WOF */ REGW(0x3d6240, 0x0000000000000000); /* Init_51: CI Port 6 Mask (AND write) */ REGW(0x3d6220, 0x0000000000000000); /* Init_52: CI Port 7 LEM FIR Accumulator */ REGW(0x3d7200, 0x0000000000000000); /* Init_53: CI Port 7 Action 0 */ REGW(0x3d7230, 0xA4F4000000000000); /* Init_54: CI Port 7 Action 1 */ REGW(0x3d7238, 0x0000000000000000); /* Init_55: CI Port 7 WOF */ REGW(0x3d7240, 0x0000000000000000); /* Init_56: CI Port 7 Mask (AND write) */ REGW(0x3d7220, 0x0000000000000000); /*** Set Damage Controls (need match recov.) ***/ /* Init_57: CI Port 0 LDCP*/ REGW(0x3d0010, 0x421A0000000075FF); /* Init_58: CI Port 1 LDCP */ REGW(0x3d1010, 0x421A000000007FFF); /* Init_59: CI Port 2 LDCP */ REGW(0x3d2010, 0x421A24F400005B0B); /* Init_60: CI Port 3 LDCP */ REGW(0x3d3010, 0x421A24F400005B0B); /* Init_61: CI Port 4 LDCP */ REGW(0x3d4010, 0x421A24F400005B0B); /* Init_62: CI Port 5 LDCP */ REGW(0x3d5010, 0x421A24F400005B0B); /* Init_63: CI Port 6 LDCP */ REGW(0x3d6010, 0x421A24F400005B0B); /* Init_64: CI Port 7 LDCP */ REGW(0x3d7010, 0x421A24F400005B0B); /*** Setup Trace 0 ***/ /* Init_65: CI Trc 0 DBG - Run/Status (stop trace) */ REGW(0x3d0810, 0x5000000000000000); /* Init_66: CI Trc 0 DBG - Mode (not cross trig CA's) */ REGW(0x3d0808, 0xB0000000F0000000); /* Init_66a: CI Trc 0 DBG - C0 (stop on error) */ REGW(0x3d0818, 0xF4F00FFF00000000); /* Init_67: CI Trc 0 DBG - Select (port 0 mode 2) */ REGW(0x3d0878, 0x0002000000000000); /* Init_68: CI Trc 0 CA0 - Pattern A (RX cmd val) */ REGW(0x3d0880, 0xC0200000DFFFFF00); /* Init_69: CI Trc 0 CA0 - Trigger 0 (Pattern A) */ REGW(0x3d08a0, 0x8000000000000000); /* Init_70: CI Trc 0 - Mode */ REGW(0x3d08b0, 0x8000000000000000); /* Init_71: CI Trc 0 CA1 - Pattern A (TX cmd val) */ REGW(0x3d0900, 0xC0200000DFFFFF00); /* Init_72: CI Trc 0 CA1 - Trigger 0 (Pattern A) */ REGW(0x3d0920, 0x8000000000000000); /* Init_73: CI Trc 0 CA1 - Mode */ REGW(0x3d0930, 0x8000000000000000); /* Init_74: CI Trc 0 DBG - Run/Status (start trace) */ REGW(0x3d0810, 0x8000000000000000); /*** Setup Trace 1 ***/ /* Init_75: CI Trc 1 DBG - Run/Status (stop trace) */ REGW(0x3d0c10, 0x5000000000000000); /* Init_76: CI Trc 1 DBG - Mode (not cross trig CA's) */ REGW(0x3d0c08, 0xB0000000F0000000); /* Init_76a: CI Trc 1 DBG - C0 (stop on error) */ REGW(0x3d0c18, 0xF4F00FFF00000000); /* Init_77: CI Trc 1 DBG - Select (port 1 mode 2) */ REGW(0x3d0c78, 0x0102000000000000); /* Init_78: CI Trc 1 CA0 - Pattern A (RX cmd val) */ REGW(0x3d0c80, 0xC0200000DFFFFF00); /* Init_79: CI Trc 1 CA0 - Trigger 0 (Pattern A) */ REGW(0x3d0ca0, 0x8000000000000000); /* Init_80: CI Trc 1 CA0 - Mode */ REGW(0x3d0cb0, 0x8000000000000000); /* Init_81: CI Trc 1 CA1 - Pattern A (TX cmd val) */ REGW(0x3d0d00, 0xC0200000DFFFFF00); /* Init_82: CI Trc 1 CA1 - Trigger 0 (Pattern A) */ REGW(0x3d0d20, 0x8000000000000000); /* Init_83: CI Trc 1 CA1 - Mode */ REGW(0x3d0d30, 0x8000000000000000); /* Init_84: CI Trc 1 DBG - Run/Status (start trace) */ REGW(0x3d0c10, 0x8000000000000000); /* Init_85...92: * * XXX NOTE: Here we normally read the Port 0 to 7 status regs * which is optional. Eventually we might want to do it to check * if the status matches expectations * * (regs 0x3d0008 to 0x3d7008) */ /*** Set buffer allocations (credits) ***/ /* Init_93: CI Port 0 Rx Cmd Buffer Allocation */ REGW(0x3d0050, 0x0808040400000000); /* Init_94: CI Port 0 Rx Dat Buffer Allocation */ REGW(0x3d0060, 0x0006000200000000); /* Init_95: CI Port 1 Tx Cmd Buffer Allocation */ REGW(0x3d1030, 0x0000040400000000); /* Init_96: CI Port 1 Tx Dat Buffer Allocation */ REGW(0x3d1040, 0x0000004800000000); /* Init_97: CI Port 1 Rx Cmd Buffer Allocation */ REGW(0x3d1050, 0x0008000000000000); /* Init_98: CI Port 1 Rx Dat Buffer Allocation */ REGW(0x3d1060, 0x0048000000000000); /* Init_99: CI Port 2 Tx Cmd Buffer Allocation */ REGW(0x3d2030, 0x0808080800000000); /* Init_100: CI Port 2 Tx Dat Buffer Allocation */ REGW(0x3d2040, 0x0086008200000000); /* Init_101: CI Port 2 Rx Cmd Buffer Allocation */ REGW(0x3d2050, 0x0808080800000000); /* Init_102: CI Port 2 Rx Dat Buffer Allocation */ REGW(0x3d2060, 0x8648000000000000); /* Init_103: CI Port 3 Tx Cmd Buffer Allocation */ REGW(0x3d3030, 0x0808080800000000); /* Init_104: CI Port 3 Tx Dat Buffer Allocation */ REGW(0x3d3040, 0x0086008200000000); /* Init_105: CI Port 3 Rx Cmd Buffer Allocation */ REGW(0x3d3050, 0x0808080800000000); /* Init_106: CI Port 3 Rx Dat Buffer Allocation */ REGW(0x3d3060, 0x8648000000000000); /* Init_107: CI Port 4 Tx Cmd Buffer Allocation */ REGW(0x3d4030, 0x0808080800000000); /* Init_108: CI Port 4 Tx Dat Buffer Allocation */ REGW(0x3d4040, 0x0086008200000000); /* Init_109: CI Port 4 Rx Cmd Buffer Allocation */ REGW(0x3d4050, 0x0808080800000000); /* Init_110: CI Port 4 Rx Dat Buffer Allocation */ REGW(0x3d4060, 0x8648000000000000); /* Init_111: CI Port 5 Tx Cmd Buffer Allocation */ REGW(0x3d5030, 0x0808080800000000); /* Init_112: CI Port 5 Tx Dat Buffer Allocation */ REGW(0x3d5040, 0x0086008200000000); /* Init_113: CI Port 5 Rx Cmd Buffer Allocation */ REGW(0x3d5050, 0x0808080800000000); /* Init_114: CI Port 5 Rx Dat Buffer Allocation */ REGW(0x3d5060, 0x8648000000000000); /* Init_115: CI Port 6 Tx Cmd Buffer Allocation */ REGW(0x3d6030, 0x0808080800000000); /* Init_116: CI Port 6 Tx Dat Buffer Allocation */ REGW(0x3d6040, 0x0086008200000000); /* Init_117: CI Port 6 Rx Cmd Buffer Allocation */ REGW(0x3d6050, 0x0808080800000000); /* Init_118: CI Port 6 Rx Dat Buffer Allocation */ REGW(0x3d6060, 0x8648000000000000); /* Init_119: CI Port 7 Tx Cmd Buffer Allocation */ REGW(0x3d7030, 0x0808080800000000); /* Init_120: CI Port 7 Tx Dat Buffer Allocation */ REGW(0x3d7040, 0x0086008200000000); /* Init_121: CI Port 7 Rx Cmd Buffer Allocation */ REGW(0x3d7050, 0x0808080800000000); /* Init_122: CI Port 6 Rx Dat Buffer Allocation */ REGW(0x3d7060, 0x8648000000000000); /*** Channel ordering ***/ /* Init_123: CI Port 1 Ordering */ REGW(0x3d1070, 0x73D0735E00000000); /* Init_124: CI Port 2 Ordering */ REGW(0x3d2070, 0x73D0735E00000000); /* Init_125: CI Port 3 Ordering */ REGW(0x3d3070, 0x73D0735E00000000); /* Init_126: CI Port 4 Ordering */ REGW(0x3d4070, 0x73D0735E00000000); /* Init_127: CI Port 5 Ordering */ REGW(0x3d5070, 0x73D0735E00000000); /* Init_128: CI Port 6 Ordering */ REGW(0x3d6070, 0x73D0735E00000000); /* Init_129: CI POrt 7 Ordering */ REGW(0x3d7070, 0x73D0735E00000000); /*** Setup routing (port 0 only) */ p7ioc_init_ci_routing(ioc); /*** Set Running Configuration/Crd Init Timers *** * * XXX NOTE: Supposed to only modify bits 8:15 */ /* Init_226: CI Port 1 Configuration */ REGW(0x3d1000, 0x023F0FCF07200002); /* Init_227: CI Port 2 Configuration */ REGW(0x3d2000, 0x023F00C307200002); /* Init_228: CI Port 3 Configuration */ REGW(0x3d3000, 0x023F00C307200002); /* Init_229: CI Port 4 Configuration */ REGW(0x3d4000, 0x023F00C307200002); /* Init_230: CI Port 5 Configuration */ REGW(0x3d5000, 0x023F00C307200002); /* Init_231: CI Port 6 Configuration */ REGW(0x3d6000, 0x023F00C307200002); /* Init_232: CI Port 7 Configuration */ REGW(0x3d7000, 0x023F00C307200002); /* Init_233: CI Port 0 Configuration */ REGW(0x3d0000, 0x023F00C0073F0002); } static void p7ioc_init_PHBs(struct p7ioc *ioc) { unsigned int i; printf("P7IOC: Init PHBs...\n"); /* We use the same reset sequence that we use for * fast reboot for consistency */ for (i = 0; i < P7IOC_NUM_PHBS; i++) { if (p7ioc_phb_enabled(ioc, i)) p7ioc_phb_reset(&ioc->phbs[i].phb); } } static void p7ioc_init_MISC(struct p7ioc *ioc) { printf("P7IOC: Init MISC...\n"); /*** Set LEM regs ***/ /* Init_1: LEM FIR Accumulator */ REGW(0x3ea000, 0x0000000000000000); /* Init_2: LEM Action 0 */ REGW(0x3ea030, 0xFFFFFFFCEE3FFFFF); /* Init_3: LEM Action 1 */ REGW(0x3ea038, 0x0000000001C00000); /* Init_4: LEM WOF */ REGW(0x3ea040, 0x0000000000000000); /* Init_5: LEM Mask (AND write) */ REGW(0x3ea020, 0x000F03F0CD3FFFFF); /* Init_5.1: I2C LEM FIR Accumulator */ REGW(0x3eb000, 0x0000000000000000); /* Init_5.2: I2C LEM Action 0 */ REGW(0x3eb030, 0xEE00000000000000); /* Init_5.3: I2C LEM Action 1 */ REGW(0x3eb038, 0x0000000000000000); /* Init_5.4: I2C LEM WOF */ REGW(0x3eb040, 0x0000000000000000); /* Init_5.5: I2C LEM Mask (AND write) */ REGW(0x3eb020, 0x4600000000000000); /*** Set RGC GP bits (error enables) ***/ /* Init_7: RGC GP0 control (enable umux errors) */ REGW(0x3e1018, 0x8888880000000000); /*** Central Trace Setup *** * * By default trace 4 PHBs Rx/Tx, but this can be changed * for debugging purposes */ /* Init_8: */ REGW(0x3ea810, 0x5000000000000000); /* Init_9: */ REGW(0x3ea800, 0x0000000000000000); /* Init_10: */ REGW(0x3ea808, 0xB0000000F0000000); /* Init_11: */ REGW(0x3ea818, 0xF4F00FFF00000000); /* Init_12: */ REGW(0x3ea820, 0x0000000000000000); /* Init_13: */ REGW(0x3ea828, 0x0000000000000000); /* Init_14: */ REGW(0x3ea830, 0x0000000000000000); /* Init_15: */ REGW(0x3ea838, 0x0000000000000000); /* Init_16: */ REGW(0x3ea840, 0x0000000000000000); /* Init_17: */ REGW(0x3ea878, 0x0300000000000000); /* Init_18: PHB0 mux select (Rx/Tx) */ REGW(0x000F80, 0x0000000000000000); /* Init_19: PHB1 mux select (Rx/Tx) */ REGW(0x010F80, 0x0000000000000000); /* Init_19.0: PHB2 mux select (Rx/Tx) */ REGW(0x020F80, 0x0000000000000000); /* Init_19.1: PHB3 mux select (Rx/Tx) */ REGW(0x030F80, 0x0000000000000000); /* Init_19.2: PHB4 mux select (Rx/Tx) */ REGW(0x040F80, 0x0000000000000000); /* Init_19.3: PHB5 mux select (Rx/Tx) */ REGW(0x050F80, 0x0000000000000000); /* Init_20: */ REGW(0x3ea880, 0x40008000FF7F0000); /* Init_21: */ REGW(0x3ea888, 0x0000000000000000); /* Init_22: */ REGW(0x3ea890, 0x0000000000000000); /* Init_23: */ REGW(0x3ea898, 0x0000000000000000); /* Init_24: */ REGW(0x3ea8a0, 0x8000000000000000); /* Init_25: */ REGW(0x3ea8a8, 0x0000000000000000); /* Init_26: */ REGW(0x3ea8b0, 0x8000000000000000); /* Init_27: */ REGW(0x3ea8b8, 0x0000000000000000); /* Init_28: */ REGW(0x3ea8c0, 0x0000000000000000); /* Init_29: */ REGW(0x3ea900, 0x40008000FF7F0000); /* Init_30: */ REGW(0x3ea908, 0x0000000000000000); /* Init_31: */ REGW(0x3ea910, 0x0000000000000000); /* Init_32: */ REGW(0x3ea918, 0x0000000000000000); /* Init_33: */ REGW(0x3ea920, 0x8000000000000000); /* Init_34: */ REGW(0x3ea928, 0x0000000000000000); /* Init_35: */ REGW(0x3ea930, 0x8000000000000000); /* Init_36: */ REGW(0x3ea938, 0x0000000000000000); /* Init_37: */ REGW(0x3ea940, 0x0000000000000000); /* Init_38: */ REGW(0x3ea980, 0x40008000FF7F0000); /* Init_39: */ REGW(0x3ea988, 0x0000000000000000); /* Init_40: */ REGW(0x3ea990, 0x0000000000000000); /* Init_41: */ REGW(0x3ea998, 0x0000000000000000); /* Init_42: */ REGW(0x3ea9a0, 0x8000000000000000); /* Init_43: */ REGW(0x3ea9a8, 0x0000000000000000); /* Init_44: */ REGW(0x3ea9b0, 0x8000000000000000); /* Init_45: */ REGW(0x3ea9b8, 0x0000000000000000); /* Init_46: */ REGW(0x3ea9c0, 0x0000000000000000); /* Init_47: */ REGW(0x3eaa00, 0x40008000FF7F0000); /* Init_48: */ REGW(0x3eaa08, 0x0000000000000000); /* Init_49: */ REGW(0x3eaa10, 0x0000000000000000); /* Init_50: */ REGW(0x3eaa18, 0x0000000000000000); /* Init_51: */ REGW(0x3eaa20, 0x8000000000000000); /* Init_52: */ REGW(0x3eaa28, 0x0000000000000000); /* Init_53: */ REGW(0x3eaa30, 0x8000000000000000); /* Init_54: */ REGW(0x3eaa38, 0x0000000000000000); /* Init_55: */ REGW(0x3eaa40, 0x0000000000000000); /* Init_56: */ REGW(0x3ea810, 0x1000000000000000); /* Init_57: */ REGW(0x3ea810, 0x8000000000000000); /*** I2C Master init fixup */ /* Init_58: I2C Master Operation Control */ REGW(0x3eb0a8, 0x8100000000000000); } static void p7ioc_init_GEM(struct p7ioc *ioc) { printf("P7IOC: Init GEM...\n"); /*** Check for errors */ /* XXX TODO */ #if 0 /* Init_1: */ REGR(0x3e0008, 0); /* Init_2: */ REGR(0x3e0010, 0); /* Init_3: */ REGR(0x3e0018, 0); #endif /*** Get ready for new errors, allow interrupts *** * * XXX: Need to leave all unused port masked to prevent * invalid errors */ /* Init_4: GEM XFIR */ REGW(0x3e0008, 0x0000000000000000); /* Init_5: GEM Mask (See FIXME) */ REGW(0x3e0020, 0x000F033FFFFFFFFF); /* Init_6: GEM WOF */ REGW(0x3e0028, 0x0000000000000000); } int64_t p7ioc_inits(struct p7ioc *ioc) { p7ioc_init_BI(ioc); p7ioc_init_MISC_HSS(ioc); p7ioc_init_RGC(ioc); p7ioc_init_CI(ioc); p7ioc_init_PHBs(ioc); p7ioc_init_MISC(ioc); p7ioc_init_GEM(ioc); return OPAL_SUCCESS; } void p7ioc_reset(struct io_hub *hub) { struct p7ioc *ioc = iohub_to_p7ioc(hub); unsigned int i; /* We could do a full cold reset of P7IOC but for now, let's * not bother and just try to clean up the interrupts as best * as possible */ /* XXX TODO: RGC interrupts */ printf("P7IOC: Clearing IODA...\n"); /* First clear all IODA tables and wait a bit */ for (i = 0; i < 6; i++) { if (p7ioc_phb_enabled(ioc, i)) p7ioc_phb_reset(&ioc->phbs[i].phb); } } skiboot-skiboot-5.1.13/hw/p7ioc-phb.c000066400000000000000000003231371265204436200173120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #define PHBDBG(p, fmt, a...) prlog(PR_DEBUG, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) #define PHBERR(p, fmt, a...) prlog(PR_ERR, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) /* Helper to select an IODA table entry */ static inline void p7ioc_phb_ioda_sel(struct p7ioc_phb *p, uint32_t table, uint32_t addr, bool autoinc) { out_be64(p->regs + PHB_IODA_ADDR, (autoinc ? PHB_IODA_AD_AUTOINC : 0) | SETFIELD(PHB_IODA_AD_TSEL, 0ul, table) | SETFIELD(PHB_IODA_AD_TADR, 0ul, addr)); } /* Helper to set the state machine timeout */ static inline uint64_t p7ioc_set_sm_timeout(struct p7ioc_phb *p, uint64_t dur) { uint64_t target, now = mftb(); target = now + dur; if (target == 0) target++; p->delay_tgt_tb = target; return dur; } /* * Lock callbacks. Allows the OPAL API handlers to lock the * PHB around calls such as config space, EEH, etc... */ static void p7ioc_phb_lock(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); lock(&p->lock); } static void p7ioc_phb_unlock(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); unlock(&p->lock); } static bool p7ioc_phb_fenced(struct p7ioc_phb *p) { struct p7ioc *ioc = p->ioc; uint64_t fence, fbits; fbits = 0x0003000000000000 >> (p->index * 4); fence = in_be64(ioc->regs + P7IOC_CHIP_FENCE_SHADOW); return (fence & fbits) != 0; } /* * Configuration space access * * The PHB lock is assumed to be already held */ static int64_t p7ioc_pcicfg_check(struct p7ioc_phb *p, uint32_t bdfn, uint32_t offset, uint32_t size) { uint32_t sm = size - 1; if (offset > 0xfff || bdfn > 0xffff) return OPAL_PARAMETER; if (offset & sm) return OPAL_PARAMETER; /* The root bus only has a device at 0 and we get into an * error state if we try to probe beyond that, so let's * avoid that and just return an error to Linux */ if ((bdfn >> 8) == 0 && (bdfn & 0xff)) return OPAL_HARDWARE; /* Check PHB state */ if (p->state == P7IOC_PHB_STATE_BROKEN) return OPAL_HARDWARE; return OPAL_SUCCESS; } #define P7IOC_PCI_CFG_READ(size, type) \ static int64_t p7ioc_pcicfg_read##size(struct phb *phb, uint32_t bdfn, \ uint32_t offset, type *data) \ { \ struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); \ uint64_t addr; \ void *base = p->regs; \ int64_t rc; \ \ /* Initialize data in case of error */ \ *data = (type)0xffffffff; \ \ rc = p7ioc_pcicfg_check(p, bdfn, offset, sizeof(type)); \ if (rc) \ return rc; \ \ if (p7ioc_phb_fenced(p)) { \ if (!(p->flags & P7IOC_PHB_CFG_USE_ASB)) \ return OPAL_HARDWARE; \ \ base = p->regs_asb; \ } else if ((p->flags & P7IOC_PHB_CFG_BLOCKED) && bdfn != 0) { \ return OPAL_HARDWARE; \ } \ \ addr = PHB_CA_ENABLE; \ addr = SETFIELD(PHB_CA_BDFN, addr, bdfn); \ addr = SETFIELD(PHB_CA_REG, addr, offset); \ out_be64(base + PHB_CONFIG_ADDRESS, addr); \ *data = in_le##size(base + PHB_CONFIG_DATA + \ (offset & (4 - sizeof(type)))); \ \ return OPAL_SUCCESS; \ } #define P7IOC_PCI_CFG_WRITE(size, type) \ static int64_t p7ioc_pcicfg_write##size(struct phb *phb, uint32_t bdfn, \ uint32_t offset, type data) \ { \ struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); \ void *base = p->regs; \ uint64_t addr; \ int64_t rc; \ \ rc = p7ioc_pcicfg_check(p, bdfn, offset, sizeof(type)); \ if (rc) \ return rc; \ \ if (p7ioc_phb_fenced(p)) { \ if (!(p->flags & P7IOC_PHB_CFG_USE_ASB)) \ return OPAL_HARDWARE; \ \ base = p->regs_asb; \ } else if ((p->flags & P7IOC_PHB_CFG_BLOCKED) && bdfn != 0) { \ return OPAL_HARDWARE; \ } \ \ addr = PHB_CA_ENABLE; \ addr = SETFIELD(PHB_CA_BDFN, addr, bdfn); \ addr = SETFIELD(PHB_CA_REG, addr, offset); \ out_be64(base + PHB_CONFIG_ADDRESS, addr); \ out_le##size(base + PHB_CONFIG_DATA + \ (offset & (4 - sizeof(type))), data); \ \ return OPAL_SUCCESS; \ } P7IOC_PCI_CFG_READ(8, uint8_t) P7IOC_PCI_CFG_READ(16, uint16_t) P7IOC_PCI_CFG_READ(32, uint32_t) P7IOC_PCI_CFG_WRITE(8, uint8_t) P7IOC_PCI_CFG_WRITE(16, uint16_t) P7IOC_PCI_CFG_WRITE(32, uint32_t) static int64_t p7ioc_presence_detect(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); /* XXX Test for PHB in error state ? */ if (reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT) return OPAL_SHPC_DEV_PRESENT; return OPAL_SHPC_DEV_NOT_PRESENT; } static int64_t p7ioc_link_state(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); uint16_t lstat; int64_t rc; /* XXX Test for PHB in error state ? */ /* Link is up, let's find the actual speed */ if (!(reg & PHB_PCIE_DLP_TC_DL_LINKACT)) return OPAL_SHPC_LINK_DOWN; rc = p7ioc_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LSTAT, &lstat); if (rc < 0) { /* Shouldn't happen */ PHBERR(p, "Failed to read link status\n"); return OPAL_HARDWARE; } if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) return OPAL_SHPC_LINK_DOWN; return GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat); } static int64_t p7ioc_sm_freset(struct p7ioc_phb *p) { uint64_t reg; uint32_t cfg32; uint64_t ci_idx = p->index + 2; switch(p->state) { case P7IOC_PHB_STATE_FUNCTIONAL: /* If the slot isn't present, we needn't do it */ reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) { PHBDBG(p, "Slot freset: no device\n"); return OPAL_CLOSED; } /* Mask PCIE port interrupts and AER receiver error */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7E00000000000000); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, &cfg32); cfg32 |= PCIECAP_AER_CE_RECVR_ERR; p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, cfg32); /* Mask CI port error and clear it */ out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx), 0xa4f4000000000000ul); out_be64(p->regs + PHB_LEM_ERROR_MASK, 0xadb650c9808dd051ul); out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx), 0x0ul); /* Disable link to avoid training issues */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); reg |= PHB_PCIE_DLP_TCTX_DISABLE; out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg); PHBDBG(p, "Slot freset: disable link training\n"); p->state = P7IOC_PHB_STATE_FRESET_DISABLE_LINK; p->retries = 12; return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_FRESET_DISABLE_LINK: reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (reg & PHB_PCIE_DLP_TCRX_DISABLED) { /* Turn on freset */ reg = in_be64(p->regs + PHB_RESET); reg &= ~0x2000000000000000ul; out_be64(p->regs + PHB_RESET, reg); PHBDBG(p, "Slot freset: assert\n"); p->state = P7IOC_PHB_STATE_FRESET_ASSERT_DELAY; return p7ioc_set_sm_timeout(p, secs_to_tb(1)); } if (p->retries-- == 0) { PHBDBG(p, "Slot freset: timeout to disable link training\n"); goto error; } return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_FRESET_ASSERT_DELAY: /* Turn off freset */ reg = in_be64(p->regs + PHB_RESET); reg |= 0x2000000000000000ul; out_be64(p->regs + PHB_RESET, reg); PHBDBG(p, "Slot freset: deassert\n"); p->state = P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY; return p7ioc_set_sm_timeout(p, msecs_to_tb(200)); case P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY: /* Restore link control */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); reg &= ~PHB_PCIE_DLP_TCTX_DISABLE; out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg); PHBDBG(p, "Slot freset: enable link training\n"); p->state = P7IOC_PHB_STATE_FRESET_WAIT_LINK; p->retries = 100; return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_FRESET_WAIT_LINK: reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) { /* * Clear spurious errors and enable PCIE port * interrupts */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x00E0000000000000); out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xFE65000000000000); /* Clear AER receiver error status */ p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, PCIECAP_AER_CE_RECVR_ERR); /* Unmask receiver error status in AER */ p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, &cfg32); cfg32 &= ~PCIECAP_AER_CE_RECVR_ERR; p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, cfg32); /* Clear and Unmask CI port and PHB errors */ out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx), 0x0ul); out_be64(p->regs + PHB_LEM_FIR_ACCUM, 0x0ul); out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK_AND(ci_idx), 0x0ul); out_be64(p->regs + PHB_LEM_ERROR_MASK, 0x1249a1147f500f2cul); PHBDBG(p, "Slot freset: link up!\n"); p->state = P7IOC_PHB_STATE_FUNCTIONAL; p->flags &= ~P7IOC_PHB_CFG_BLOCKED; /* * We might be required to restore bus numbers for PCI bridges * for complete reset */ if (p->flags & P7IOC_RESTORE_BUS_NUM) { p->flags &= ~P7IOC_RESTORE_BUS_NUM; pci_restore_bridge_buses(&p->phb); } return OPAL_SUCCESS; } if (p->retries-- == 0) { uint16_t val; if (p->gen == 1) { PHBDBG(p, "Slot freset: timeout for link up in Gen1 mode!\n"); goto error; } PHBDBG(p, "Slot freset: timeout for link up.\n"); PHBDBG(p, "Slot freset: fallback to Gen1.\n"); p->gen --; /* Limit speed to 2.5G */ p7ioc_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL2, &val); val = SETFIELD(PCICAP_EXP_LCTL2_TLSPD, val, 1); p7ioc_pcicfg_write16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL2, val); /* Retrain */ p7ioc_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, &val); p7ioc_pcicfg_write16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, val | PCICAP_EXP_LCTL_LINK_RETRAIN); /* Enter FRESET_WAIT_LINK, again */ p->state = P7IOC_PHB_STATE_FRESET_WAIT_LINK; p->retries = 100; return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); } return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); default: break; } error: p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_HARDWARE; } static int64_t p7ioc_freset(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); if (p->state != P7IOC_PHB_STATE_FUNCTIONAL) return OPAL_HARDWARE; p->flags |= P7IOC_PHB_CFG_BLOCKED; return p7ioc_sm_freset(p); } static int64_t p7ioc_power_state(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); /* XXX Test for PHB in error state ? */ if (reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT) return OPAL_SHPC_POWER_ON; return OPAL_SHPC_POWER_OFF; } static int64_t p7ioc_sm_slot_power_off(struct p7ioc_phb *p) { uint64_t reg; switch(p->state) { case P7IOC_PHB_STATE_FUNCTIONAL: /* * Check the presence and power status. If be not * be present or power down, we stop here. */ reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) { PHBDBG(p, "Slot power off: no device\n"); return OPAL_CLOSED; } reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) { PHBDBG(p, "Slot power off: already off\n"); p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_SUCCESS; } /* * Mask PCIE port interrupt and turn power off * * We have to set bit 0 and clear it explicitly on PHB * hotplug override register when doing power-off on the * PHB slot. Otherwise, it won't take effect. That's the * similar thing as we did for power-on. */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7e00000000000000); reg = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE); reg &= ~(0x8c00000000000000ul); reg |= 0x8400000000000000ul; out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg); reg &= ~(0x8c00000000000000ul); reg |= 0x0c00000000000000ul; out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg); PHBDBG(p, "Slot power off: powering off...\n"); p->state = P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY; return p7ioc_set_sm_timeout(p, secs_to_tb(2)); case P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY: /* * The link should be stabilized after 2 seconds. * We still need poll registers to make sure the * power is really down every 1ms until limited * 1000 times. */ p->retries = 1000; p->state = P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS; PHBDBG(p, "Slot power off: waiting for power off\n"); case P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS: reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) { /* * We completed the task. Clear link errors * and restore PCIE port interrupts. */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x00E0000000000000ul); out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xFE65000000000000ul); PHBDBG(p, "Slot power off: power off completely\n"); p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_SUCCESS; } if (p->retries-- == 0) { PHBERR(p, "Timeout powering off\n"); goto error; } return p7ioc_set_sm_timeout(p, msecs_to_tb(1)); default: break; } error: p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_HARDWARE; } static int64_t p7ioc_slot_power_off(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); if (p->state != P7IOC_PHB_STATE_FUNCTIONAL) return OPAL_BUSY; /* run state machine */ return p7ioc_sm_slot_power_off(p); } static int64_t p7ioc_sm_slot_power_on(struct p7ioc_phb *p) { uint64_t reg; uint32_t reg32; uint64_t ci_idx = p->index + 2; switch(p->state) { case P7IOC_PHB_STATE_FUNCTIONAL: /* Check presence */ reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) { PHBDBG(p, "Slot power on: no device\n"); return OPAL_CLOSED; } /* Adjust UTL interrupt settings to disable various * errors that would interfere with the process */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7e00000000000000); /* If the power is not on, turn it on now */ if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) { /* * The hotplug override register will not properly * initiate the poweron sequence unless bit 0 * transitions from 0 to 1. Since it can already be * set to 1 as a result of a previous power-on * operation (even if the slot power is now off) * we need to first clear it, then set it to 1 or * nothing will happen */ reg = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE); reg &= ~(0x8c00000000000000ul); out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg); reg |= 0x8400000000000000ul; out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg); p->state = P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY; PHBDBG(p, "Slot power on: powering on...\n"); return p7ioc_set_sm_timeout(p, secs_to_tb(2)); } /* Power is already on */ power_ok: /* Mask AER receiver error */ p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, ®32); reg32 |= PCIECAP_AER_CE_RECVR_ERR; p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32); /* Mask CI port error and clear it */ out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx), 0xa4f4000000000000ul); out_be64(p->regs + PHB_LEM_ERROR_MASK, 0xadb650c9808dd051ul); out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx), 0x0ul); /* Disable link to avoid training issues */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); reg |= PHB_PCIE_DLP_TCTX_DISABLE; out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg); PHBDBG(p, "Slot power on: disable link training\n"); /* Switch to state machine of fundamental reset */ p->state = P7IOC_PHB_STATE_FRESET_DISABLE_LINK; p->retries = 12; return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY: /* Come here after the 2s delay after power up */ p->retries = 1000; p->state = P7IOC_PHB_STATE_SPUP_SLOT_STATUS; PHBDBG(p, "Slot power on: waiting for power\n"); /* Fall through */ case P7IOC_PHB_STATE_SPUP_SLOT_STATUS: reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); /* Doc says to check LED status, but we ignore that, there * no point really and it's easier that way */ if (reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT) goto power_ok; if (p->retries-- == 0) { /* XXX Improve error logging */ PHBERR(p, "Timeout powering up slot\n"); goto error; } return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); default: break; } /* Unknown state, hardware error ? */ error: p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_HARDWARE; } static int64_t p7ioc_slot_power_on(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); if (p->state != P7IOC_PHB_STATE_FUNCTIONAL) return OPAL_BUSY; /* run state machine */ return p7ioc_sm_slot_power_on(p); } /* * The OS is expected to do fundamental reset after complete * reset to make sure the PHB could be recovered from the * fenced state. However, the OS needn't do that explicitly * since fundamental reset will be done automatically while * powering on the PHB. */ static int64_t p7ioc_complete_reset(struct phb *phb, uint8_t assert) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct p7ioc *ioc = p->ioc; uint64_t val64; if (assert == OPAL_ASSERT_RESET) { if (p->state != P7IOC_PHB_STATE_FUNCTIONAL && p->state != P7IOC_PHB_STATE_FENCED) return OPAL_HARDWARE; p->flags |= P7IOC_PHB_CFG_BLOCKED; p7ioc_phb_reset(phb); /* * According to the experiment, we probably still have * the fenced state with the corresponding PHB in the Fence * WOF and we need clear that explicitly. Besides, the RGC * might already have informational error and we should clear * that explicitly as well. Otherwise, RGC XIVE#0 won't issue * interrupt any more. */ val64 = in_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF); val64 &= ~PPC_BIT(15 + p->index * 4); out_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF, val64); /* Clear informational error from RGC */ val64 = in_be64(ioc->regs + P7IOC_RGC_LEM_BASE + P7IOC_LEM_WOF_OFFSET); val64 &= ~PPC_BIT(18); out_be64(ioc->regs + P7IOC_RGC_LEM_BASE + P7IOC_LEM_WOF_OFFSET, val64); val64 = in_be64(ioc->regs + P7IOC_RGC_LEM_BASE + P7IOC_LEM_FIR_OFFSET); val64 &= ~PPC_BIT(18); out_be64(ioc->regs + P7IOC_RGC_LEM_BASE + P7IOC_LEM_FIR_OFFSET, val64); return p7ioc_sm_slot_power_off(p); } else { if (p->state != P7IOC_PHB_STATE_FUNCTIONAL) return OPAL_HARDWARE; /* Restore bus numbers for bridges */ p->flags |= P7IOC_RESTORE_BUS_NUM; return p7ioc_sm_slot_power_on(p); } /* We shouldn't run to here */ return OPAL_PARAMETER; } /* * We have to mask errors prior to disabling link training. * Otherwise it would cause infinite frozen PEs. Also, we * should have some delay after enabling link training. It's * the conclusion from experiment and no document mentioned * it. */ static int64_t p7ioc_sm_hot_reset(struct p7ioc_phb *p) { uint64_t reg; uint32_t cfg32; uint16_t brctl; switch(p->state) { case P7IOC_PHB_STATE_FUNCTIONAL: /* If the slot isn't present, we needn't do it */ reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) { PHBDBG(p, "Slot hot reset: no device\n"); return OPAL_CLOSED; } /* Mask PCIE port interrupts and AER receiver error */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7E00000000000000); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, &cfg32); cfg32 |= PCIECAP_AER_CE_RECVR_ERR; p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, cfg32); /* Disable link to avoid training issues */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); reg |= PHB_PCIE_DLP_TCTX_DISABLE; out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg); PHBDBG(p, "Slot hot reset: disable link training\n"); p->state = P7IOC_PHB_STATE_HRESET_DISABLE_LINK; p->retries = 12; return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_HRESET_DISABLE_LINK: reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (reg & PHB_PCIE_DLP_TCRX_DISABLED) { /* Turn on host reset */ p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl |= PCI_CFG_BRCTL_SECONDARY_RESET; p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); PHBDBG(p, "Slot hot reset: assert reset\n"); p->state = P7IOC_PHB_STATE_HRESET_DELAY; return p7ioc_set_sm_timeout(p, secs_to_tb(1)); } if (p->retries-- == 0) { PHBDBG(p, "Slot hot reset: timeout to disable link training\n"); return OPAL_HARDWARE; } return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_HRESET_DELAY: /* Turn off host reset */ p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); PHBDBG(p, "Slot hot reset: deassert reset\n"); p->state = P7IOC_PHB_STATE_HRESET_ENABLE_LINK; return p7ioc_set_sm_timeout(p, msecs_to_tb(200)); case P7IOC_PHB_STATE_HRESET_ENABLE_LINK: /* Restore link control */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); reg &= ~PHB_PCIE_DLP_TCTX_DISABLE; out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg); PHBDBG(p, "Slot hot reset: enable link training\n"); p->state = P7IOC_PHB_STATE_HRESET_WAIT_LINK; p->retries = 100; return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); case P7IOC_PHB_STATE_HRESET_WAIT_LINK: reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) { /* * Clear spurious errors and enable PCIE port * interrupts */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x00E0000000000000); out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xFE65000000000000); /* Clear AER receiver error status */ p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, PCIECAP_AER_CE_RECVR_ERR); /* Unmask receiver error status in AER */ p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, &cfg32); cfg32 &= ~PCIECAP_AER_CE_RECVR_ERR; p7ioc_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, cfg32); PHBDBG(p, "Slot hot reset: link up!\n"); p->state = P7IOC_PHB_STATE_FUNCTIONAL; p->flags &= ~P7IOC_PHB_CFG_BLOCKED; return OPAL_SUCCESS; } if (p->retries-- == 0) { PHBDBG(p, "Slot hot reset: timeout for link up\n"); goto error; } return p7ioc_set_sm_timeout(p, msecs_to_tb(10)); default: break; } /* Unknown state, hardware error ? */ error: p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_HARDWARE; } static int64_t p7ioc_hot_reset(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); if (p->state != P7IOC_PHB_STATE_FUNCTIONAL) return OPAL_HARDWARE; p->flags |= P7IOC_PHB_CFG_BLOCKED; return p7ioc_sm_hot_reset(p); } static int64_t p7ioc_poll(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t now = mftb(); if (p->state == P7IOC_PHB_STATE_FUNCTIONAL) return OPAL_SUCCESS; /* Check timer */ if (p->delay_tgt_tb && tb_compare(now, p->delay_tgt_tb) == TB_ABEFOREB) return p->delay_tgt_tb - now; /* Expired (or not armed), clear it */ p->delay_tgt_tb = 0; /* Dispatch to the right state machine */ switch(p->state) { case P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY: case P7IOC_PHB_STATE_SPUP_SLOT_STATUS: return p7ioc_sm_slot_power_on(p); case P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY: case P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS: return p7ioc_sm_slot_power_off(p); case P7IOC_PHB_STATE_FRESET_DISABLE_LINK: case P7IOC_PHB_STATE_FRESET_ASSERT_DELAY: case P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY: case P7IOC_PHB_STATE_FRESET_WAIT_LINK: return p7ioc_sm_freset(p); case P7IOC_PHB_STATE_HRESET_DISABLE_LINK: case P7IOC_PHB_STATE_HRESET_ASSERT: case P7IOC_PHB_STATE_HRESET_DELAY: case P7IOC_PHB_STATE_HRESET_ENABLE_LINK: case P7IOC_PHB_STATE_HRESET_WAIT_LINK: return p7ioc_sm_hot_reset(p); default: break; } /* Unknown state, could be a HW error */ return OPAL_HARDWARE; } static void p7ioc_eeh_read_phb_status(struct p7ioc_phb *p, struct OpalIoP7IOCPhbErrorData *stat) { uint16_t tmp16; unsigned int i; memset(stat, 0, sizeof(struct OpalIoP7IOCPhbErrorData)); /* Error data common part */ stat->common.version = OPAL_PHB_ERROR_DATA_VERSION_1; stat->common.ioType = OPAL_PHB_ERROR_DATA_TYPE_P7IOC; stat->common.len = sizeof(struct OpalIoP7IOCPhbErrorData); /* * We read some registers using config space through AIB. * * Get to other registers using ASB when possible to get to them * through a fence if one is present. * * Note that the OpalIoP7IOCPhbErrorData has oddities, such as the * bridge control being 32-bit and the UTL registers being 32-bit * (which they really are, but they use the top 32-bit of a 64-bit * register so we need to be a bit careful). */ /* Use ASB to access PCICFG if the PHB has been fenced */ p->flags |= P7IOC_PHB_CFG_USE_ASB; /* Grab RC bridge control, make it 32-bit */ p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &tmp16); stat->brdgCtl = tmp16; /* Grab UTL status registers */ stat->portStatusReg = hi32(in_be64(p->regs_asb + UTL_PCIE_PORT_STATUS)); stat->rootCmplxStatus = hi32(in_be64(p->regs_asb + UTL_RC_STATUS)); stat->busAgentStatus = hi32(in_be64(p->regs_asb + UTL_SYS_BUS_AGENT_STATUS)); /* * Grab various RC PCIe capability registers. All device, slot * and link status are 16-bit, so we grab the pair control+status * for each of them */ p7ioc_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_DEVCTL, &stat->deviceStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_SLOTCTL, &stat->slotStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, &stat->linkStatus); /* * I assume those are the standard config space header, cmd & status * together makes 32-bit. Secondary status is 16-bit so I'll clear * the top on that one */ p7ioc_pcicfg_read32(&p->phb, 0, PCI_CFG_CMD, &stat->devCmdStatus); p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_SECONDARY_STATUS, &tmp16); stat->devSecStatus = tmp16; /* Grab a bunch of AER regs */ p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_RERR_STA, &stat->rootErrorStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_UE_STATUS, &stat->uncorrErrorStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, &stat->corrErrorStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG0, &stat->tlpHdr1); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG1, &stat->tlpHdr2); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG2, &stat->tlpHdr3); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG3, &stat->tlpHdr4); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_SRCID, &stat->sourceId); /* Restore to AIB */ p->flags &= ~P7IOC_PHB_CFG_USE_ASB; /* * No idea what that that is supposed to be, opal.h says * "Record data about the call to allocate a buffer." * * Let's leave them alone for now... * * uint64_t errorClass; * uint64_t correlator; */ /* P7IOC MMIO Error Regs */ stat->p7iocPlssr = in_be64(p->regs_asb + PHB_CPU_LOADSTORE_STATUS); stat->p7iocCsr = in_be64(p->regs_asb + PHB_DMA_CHAN_STATUS); stat->lemFir = in_be64(p->regs_asb + PHB_LEM_FIR_ACCUM); stat->lemErrorMask = in_be64(p->regs_asb + PHB_LEM_ERROR_MASK); stat->lemWOF = in_be64(p->regs_asb + PHB_LEM_WOF); stat->phbErrorStatus = in_be64(p->regs_asb + PHB_ERR_STATUS); stat->phbFirstErrorStatus = in_be64(p->regs_asb + PHB_ERR1_STATUS); stat->phbErrorLog0 = in_be64(p->regs_asb + PHB_ERR_LOG_0); stat->phbErrorLog1 = in_be64(p->regs_asb + PHB_ERR_LOG_1); stat->mmioErrorStatus = in_be64(p->regs_asb + PHB_OUT_ERR_STATUS); stat->mmioFirstErrorStatus = in_be64(p->regs_asb + PHB_OUT_ERR1_STATUS); stat->mmioErrorLog0 = in_be64(p->regs_asb + PHB_OUT_ERR_LOG_0); stat->mmioErrorLog1 = in_be64(p->regs_asb + PHB_OUT_ERR_LOG_1); stat->dma0ErrorStatus = in_be64(p->regs_asb + PHB_INA_ERR_STATUS); stat->dma0FirstErrorStatus = in_be64(p->regs_asb + PHB_INA_ERR1_STATUS); stat->dma0ErrorLog0 = in_be64(p->regs_asb + PHB_INA_ERR_LOG_0); stat->dma0ErrorLog1 = in_be64(p->regs_asb + PHB_INA_ERR_LOG_1); stat->dma1ErrorStatus = in_be64(p->regs_asb + PHB_INB_ERR_STATUS); stat->dma1FirstErrorStatus = in_be64(p->regs_asb + PHB_INB_ERR1_STATUS); stat->dma1ErrorLog0 = in_be64(p->regs_asb + PHB_INB_ERR_LOG_0); stat->dma1ErrorLog1 = in_be64(p->regs_asb + PHB_INB_ERR_LOG_1); /* Grab PESTA & B content */ p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, 0, true); for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) stat->pestA[i] = in_be64(p->regs_asb + PHB_IODA_DATA0); p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, 0, true); for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) stat->pestB[i] = in_be64(p->regs_asb + PHB_IODA_DATA0); } static int64_t p7ioc_eeh_freeze_status(struct phb *phb, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint16_t *severity, uint64_t *phb_status) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t peev_bit = PPC_BIT(pe_number & 0x3f); uint64_t peev, pesta, pestb; /* Defaults: not frozen */ *freeze_state = OPAL_EEH_STOPPED_NOT_FROZEN; *pci_error_type = OPAL_EEH_NO_ERROR; /* Check dead */ if (p->state == P7IOC_PHB_STATE_BROKEN) { *freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE; *pci_error_type = OPAL_EEH_PHB_ERROR; if (severity) *severity = OPAL_EEH_SEV_PHB_DEAD; goto bail; } /* Check fence */ if (p7ioc_phb_fenced(p)) { /* Should be OPAL_EEH_STOPPED_TEMP_UNAVAIL ? */ *freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE; *pci_error_type = OPAL_EEH_PHB_ERROR; if (severity) *severity = OPAL_EEH_SEV_PHB_FENCED; p->state = P7IOC_PHB_STATE_FENCED; goto bail; } /* Check the PEEV */ p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev = in_be64(p->regs + PHB_IODA_DATA0); if (pe_number > 63) peev = in_be64(p->regs + PHB_IODA_DATA0); if (!(peev & peev_bit)) return OPAL_SUCCESS; /* Indicate that we have an ER pending */ p7ioc_phb_set_err_pending(p, true); if (severity) *severity = OPAL_EEH_SEV_PE_ER; /* Read the PESTA & PESTB */ p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, pe_number, false); pesta = in_be64(p->regs + PHB_IODA_DATA0); p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, pe_number, false); pestb = in_be64(p->regs + PHB_IODA_DATA0); /* Convert them */ if (pesta & IODA_PESTA_MMIO_FROZEN) *freeze_state |= OPAL_EEH_STOPPED_MMIO_FREEZE; if (pestb & IODA_PESTB_DMA_STOPPED) *freeze_state |= OPAL_EEH_STOPPED_DMA_FREEZE; /* XXX Handle more causes */ if (pesta & IODA_PESTA_MMIO_CAUSE) *pci_error_type = OPAL_EEH_PE_MMIO_ERROR; else *pci_error_type = OPAL_EEH_PE_DMA_ERROR; bail: if (phb_status) p7ioc_eeh_read_phb_status(p, (struct OpalIoP7IOCPhbErrorData *) phb_status); return OPAL_SUCCESS; } static int64_t p7ioc_eeh_next_error(struct phb *phb, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct p7ioc *ioc = p->ioc; uint64_t fir, peev0, peev1; uint32_t cfg32, i; /* Check if there're pending errors on the IOC. */ if (p7ioc_err_pending(ioc) && p7ioc_check_LEM(ioc, pci_error_type, severity)) return OPAL_SUCCESS; /* Clear result */ *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; *first_frozen_pe = (uint64_t)-1; /* Check dead */ if (p->state == P7IOC_PHB_STATE_BROKEN) { *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_DEAD; return OPAL_SUCCESS; } /* Check fence */ if (p7ioc_phb_fenced(p)) { /* Should be OPAL_EEH_STOPPED_TEMP_UNAVAIL ? */ *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_FENCED; p->state = P7IOC_PHB_STATE_FENCED; p7ioc_phb_set_err_pending(p, false); return OPAL_SUCCESS; } /* * If we don't have pending errors, which might be moved * from IOC to the PHB, then check if there has any frozen PEs. */ if (!p7ioc_phb_err_pending(p)) { p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); if (peev0 || peev1) { p->err.err_src = P7IOC_ERR_SRC_PHB0 + p->index; p->err.err_class = P7IOC_ERR_CLASS_ER; p->err.err_bit = 0; p7ioc_phb_set_err_pending(p, true); } } /* Check the pending errors, which might come from IOC */ if (p7ioc_phb_err_pending(p)) { /* * If the frozen PE is caused by a malfunctioning TLP, we * need reset the PHB. So convert ER to PHB-fatal error * for the case. */ if (p->err.err_class == P7IOC_ERR_CLASS_ER) { fir = in_be64(p->regs_asb + PHB_LEM_FIR_ACCUM); if (fir & PPC_BIT(60)) { p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_UE_STATUS, &cfg32); if (cfg32 & PCIECAP_AER_UE_MALFORMED_TLP) p->err.err_class = P7IOC_ERR_CLASS_PHB; } } /* * Map P7IOC internal error class to that one OS can handle. * For P7IOC_ERR_CLASS_ER, we also need figure out the frozen * PE. */ switch (p->err.err_class) { case P7IOC_ERR_CLASS_PHB: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_FENCED; p7ioc_phb_set_err_pending(p, false); break; case P7IOC_ERR_CLASS_MAL: case P7IOC_ERR_CLASS_INF: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_INF; p7ioc_phb_set_err_pending(p, false); break; case P7IOC_ERR_CLASS_ER: *pci_error_type = OPAL_EEH_PE_ERROR; *severity = OPAL_EEH_SEV_PE_ER; p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); for (i = 0 ; i < 64; i++) { if (PPC_BIT(i) & peev1) { *first_frozen_pe = i + 64; break; } } for (i = 0 ; *first_frozen_pe == (uint64_t)-1 && i < 64; i++) { if (PPC_BIT(i) & peev0) { *first_frozen_pe = i; break; } } /* No frozen PE? */ if (*first_frozen_pe == (uint64_t)-1) { *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; p7ioc_phb_set_err_pending(p, false); } break; default: *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; p7ioc_phb_set_err_pending(p, false); } } return OPAL_SUCCESS; } static void p7ioc_ER_err_clear(struct p7ioc_phb *p) { u64 err, lem; u32 val; /* Rec 1,2 */ lem = in_be64(p->regs + PHB_LEM_FIR_ACCUM); /* Rec 3,4,5 AER registers (could use cfg space accessors) */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000001c00000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0x10000000); /* Rec 6,7,8 XXX DOC whacks payload & req size ... we don't */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000005000000000ull); val = in_be32(p->regs + PHB_CONFIG_DATA); out_be32(p->regs + PHB_CONFIG_DATA, (val & 0xe0700000) | 0x0f000f00); /* Rec 9,10,11 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000010400000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 12,13,14 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000011000000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 23,24,25 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000013000000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 26,27,28 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000004000000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0x470100f8); /* Rec 29..34 UTL registers */ err = in_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS); out_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS, err); err = in_be64(p->regs + UTL_PCIE_PORT_STATUS); out_be64(p->regs + UTL_PCIE_PORT_STATUS, err); err = in_be64(p->regs + UTL_RC_STATUS); out_be64(p->regs + UTL_RC_STATUS, err); /* PHB error traps registers */ err = in_be64(p->regs + PHB_ERR_STATUS); out_be64(p->regs + PHB_ERR_STATUS, err); out_be64(p->regs + PHB_ERR1_STATUS, 0); out_be64(p->regs + PHB_ERR_LOG_0, 0); out_be64(p->regs + PHB_ERR_LOG_1, 0); err = in_be64(p->regs + PHB_OUT_ERR_STATUS); out_be64(p->regs + PHB_OUT_ERR_STATUS, err); out_be64(p->regs + PHB_OUT_ERR1_STATUS, 0); out_be64(p->regs + PHB_OUT_ERR_LOG_0, 0); out_be64(p->regs + PHB_OUT_ERR_LOG_1, 0); err = in_be64(p->regs + PHB_INA_ERR_STATUS); out_be64(p->regs + PHB_INA_ERR_STATUS, err); out_be64(p->regs + PHB_INA_ERR1_STATUS, 0); out_be64(p->regs + PHB_INA_ERR_LOG_0, 0); out_be64(p->regs + PHB_INA_ERR_LOG_1, 0); err = in_be64(p->regs + PHB_INB_ERR_STATUS); out_be64(p->regs + PHB_INB_ERR_STATUS, err); out_be64(p->regs + PHB_INB_ERR1_STATUS, 0); out_be64(p->regs + PHB_INB_ERR_LOG_0, 0); out_be64(p->regs + PHB_INB_ERR_LOG_1, 0); /* Rec 67, 68 LEM */ out_be64(p->regs + PHB_LEM_FIR_AND_MASK, ~lem); out_be64(p->regs + PHB_LEM_WOF, 0); } static int64_t p7ioc_eeh_freeze_clear(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t peev0, peev1; /* XXX Now this is a heavy hammer, coming roughly from the P7IOC doc * and my old "pseudopal" code. It will need to be refined. In general * error handling will have to be reviewed and probably done properly * "from scratch" based on the description in the p7IOC spec. * * XXX Additionally, when handling interrupts, we might want to consider * masking while processing and/or ack'ing interrupt bits etc... */ u64 err; /* Summary. If nothing, move to clearing the PESTs which can * contain a freeze state from a previous error or simply set * explicitly by the user */ err = in_be64(p->regs + PHB_ETU_ERR_SUMMARY); if (err == 0) goto clear_pest; p7ioc_ER_err_clear(p); clear_pest: /* XXX We just clear the whole PESTA for MMIO clear and PESTB * for DMA clear. We might want to only clear the frozen bit * as to not clobber the rest of the state. However, we expect * the state to have been harvested before the clear operations * so this might not be an issue */ if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, 0); } if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_DMA) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, 0); } /* Update ER pending indication */ p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); if (peev0 || peev1) { p->err.err_src = P7IOC_ERR_SRC_PHB0 + p->index; p->err.err_class = P7IOC_ERR_CLASS_ER; p->err.err_bit = 0; p7ioc_phb_set_err_pending(p, true); } else p7ioc_phb_set_err_pending(p, false); return OPAL_SUCCESS; } static int64_t p7ioc_eeh_freeze_set(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t data; if (pe_number > 127) return OPAL_PARAMETER; if (eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_MMIO && eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_DMA && eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_ALL) return OPAL_PARAMETER; if (eeh_action_token & OPAL_EEH_ACTION_SET_FREEZE_MMIO) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, pe_number, false); data = in_be64(p->regs + PHB_IODA_DATA0); data |= IODA_PESTA_MMIO_FROZEN; out_be64(p->regs + PHB_IODA_DATA0, data); } if (eeh_action_token & OPAL_EEH_ACTION_SET_FREEZE_DMA) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, pe_number, false); data = in_be64(p->regs + PHB_IODA_DATA0); data |= IODA_PESTB_DMA_STOPPED; out_be64(p->regs + PHB_IODA_DATA0, data); } return OPAL_SUCCESS; } static int64_t p7ioc_err_inject_finalize(struct p7ioc_phb *p, uint64_t addr, uint64_t mask, uint64_t ctrl, bool is_write) { if (is_write) ctrl |= PHB_PAPR_ERR_INJ_CTL_WR; else ctrl |= PHB_PAPR_ERR_INJ_CTL_RD; /* HW100549: Take read and write for outbound errors * on DD10 chip */ if (p->rev == P7IOC_REV_DD10) ctrl |= (PHB_PAPR_ERR_INJ_CTL_RD | PHB_PAPR_ERR_INJ_CTL_WR); out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, addr); out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, mask); out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, ctrl); return OPAL_SUCCESS; } static int64_t p7ioc_err_inject_mem32(struct p7ioc_phb *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m, prefer, base; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_OUTB; int32_t index; a = 0x0ull; prefer = 0x0ull; for (index = 0; index < 128; index++) { if (GETFIELD(IODA_XXDT_PE, p->m32d_cache[index]) != pe_no) continue; base = p->m32_base + M32_PCI_START + (M32_PCI_SIZE / 128) * index; /* Update preferred address */ if (!prefer) { prefer = GETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, base); prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, 0x0ull, prefer); } /* The input address matches ? */ if (addr >= base && addr < base + (M32_PCI_SIZE / 128)) { a = addr; break; } } /* Invalid PE number */ if (!prefer) return OPAL_PARAMETER; /* Specified address is out of range */ if (!a) { a = prefer; m = PHB_PAPR_ERR_INJ_MASK_MMIO; } else { m = mask; } return p7ioc_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t p7ioc_err_inject_io32(struct p7ioc_phb *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m, prefer, base; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_OUTB; int32_t index; a = 0x0ull; prefer = 0x0ull; for (index = 0; index < 128; index++) { if (GETFIELD(IODA_XXDT_PE, p->iod_cache[index]) != pe_no) continue; base = p->io_base + (PHB_IO_SIZE / 128) * index; /* Update preferred address */ if (!prefer) { prefer = GETFIELD(PHB_PAPR_ERR_INJ_MASK_IO, base); prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_IO, 0x0ull, prefer); } /* The input address matches ? */ if (addr >= base && addr < base + (PHB_IO_SIZE / 128)) { a = addr; break; } } /* Invalid PE number */ if (!prefer) return OPAL_PARAMETER; /* Specified address is out of range */ if (!a) { a = prefer; m = PHB_PAPR_ERR_INJ_MASK_IO; } else { m = mask; } return p7ioc_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t p7ioc_err_inject_cfg(struct p7ioc_phb *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_CFG; uint8_t v_bits, base, bus_no; /* Looking into PELTM to see if the PCI bus# is owned * by the PE#. Otherwise, we have to figure one out. */ base = GETFIELD(IODA_PELTM_BUS, p->peltm_cache[pe_no]); v_bits = GETFIELD(IODA_PELTM_BUS_VALID, p->peltm_cache[pe_no]); switch (v_bits) { case IODA_BUS_VALID_3_BITS: case IODA_BUS_VALID_4_BITS: case IODA_BUS_VALID_5_BITS: case IODA_BUS_VALID_6_BITS: case IODA_BUS_VALID_7_BITS: case IODA_BUS_VALID_ALL: base = GETFIELD(IODA_PELTM_BUS, p->peltm_cache[pe_no]); base &= (0xff - (((1 << (7 - v_bits)) - 1))); a = SETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, 0x0ul, base); m = PHB_PAPR_ERR_INJ_MASK_CFG; bus_no = GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, addr); bus_no &= (0xff - (((1 << (7 - v_bits)) - 1))); if (base == bus_no) { a = addr; m = mask; } break; case IODA_BUS_VALID_ANY: default: return OPAL_PARAMETER; } return p7ioc_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t p7ioc_err_inject_dma(struct p7ioc_phb *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_INB; int32_t index; /* For DMA, we just pick address from TVT */ for (index = 0; index < 128; index++) { if (GETFIELD(IODA_TVT1_PE_NUM, p->tve_hi_cache[index]) != pe_no) continue; addr = SETFIELD(PHB_PAPR_ERR_INJ_MASK_DMA, 0ul, index); mask = PHB_PAPR_ERR_INJ_MASK_DMA; break; } /* Some PE might not have DMA capability */ if (index >= 128) return OPAL_PARAMETER; return p7ioc_err_inject_finalize(p, addr, mask, ctrl, is_write); } static int64_t p7ioc_err_inject(struct phb *phb, uint32_t pe_no, uint32_t type, uint32_t func, uint64_t addr, uint64_t mask) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); int64_t (*handler)(struct p7ioc_phb *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write); bool is_write; /* To support 64-bits error later */ if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) return OPAL_UNSUPPORTED; /* We can't inject error to the reserved PE#127 */ if (pe_no > 126) return OPAL_PARAMETER; /* Clear the leftover from last time */ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); /* Check if PE number is valid one in PELTM cache */ if (p->peltm_cache[pe_no] == 0x0001f80000000000ull) return OPAL_PARAMETER; /* Clear the leftover from last time */ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); switch (func) { case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_DATA: is_write = false; handler = p7ioc_err_inject_mem32; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_DATA: is_write = true; handler = p7ioc_err_inject_mem32; break; case OPAL_ERR_INJECT_FUNC_IOA_LD_IO_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_IO_DATA: is_write = false; handler = p7ioc_err_inject_io32; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_IO_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_IO_DATA: is_write = true; handler = p7ioc_err_inject_io32; break; case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_DATA: is_write = false; handler = p7ioc_err_inject_cfg; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_DATA: is_write = true; handler = p7ioc_err_inject_cfg; break; case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_DATA: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_MASTER: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_TARGET: is_write = false; handler = p7ioc_err_inject_dma; break; case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_DATA: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_MASTER: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET: is_write = true; handler = p7ioc_err_inject_dma; break; default: return OPAL_PARAMETER; } return handler(p, pe_no, addr, mask, is_write); } static int64_t p7ioc_get_diag_data(struct phb *phb, void *diag_buffer, uint64_t diag_buffer_len) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct OpalIoP7IOCPhbErrorData *diag = diag_buffer; if (diag_buffer_len < sizeof(struct OpalIoP7IOCPhbErrorData)) return OPAL_PARAMETER; /* Specific error data */ p7ioc_eeh_read_phb_status(p, diag); /* * We're running to here probably because of errors (MAL * or INF class) from IOC. For the case, we need clear * the pending errors and mask the error bit for MAL class * error. Fortunately, we shouldn't get MAL class error from * IOC on P7IOC. */ if (p7ioc_phb_err_pending(p) && p->err.err_class == P7IOC_ERR_CLASS_INF && p->err.err_src >= P7IOC_ERR_SRC_PHB0 && p->err.err_src <= P7IOC_ERR_SRC_PHB5) { p7ioc_ER_err_clear(p); p7ioc_phb_set_err_pending(p, false); } return OPAL_SUCCESS; } /* * We don't support address remapping now since all M64 * BARs are sharing on remapping base address. We might * introduce flag to the PHB in order to trace that. The * flag allows to be changed for once. It's something to * do in future. */ static int64_t p7ioc_set_phb_mem_window(struct phb *phb, uint16_t window_type, uint16_t window_num, uint64_t base, uint64_t __unused pci_base, uint64_t size) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t data64; switch (window_type) { case OPAL_IO_WINDOW_TYPE: case OPAL_M32_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16) return OPAL_PARAMETER; /* The base and size should be 16MB aligned */ if (base & 0xFFFFFF || size & 0xFFFFFF) return OPAL_PARAMETER; data64 = p->m64b_cache[window_num]; data64 = SETFIELD(IODA_M64BT_BASE, data64, base >> 24); size = (size >> 24); data64 = SETFIELD(IODA_M64BT_MASK, data64, 0x1000000 - size); break; default: return OPAL_PARAMETER; } /* * If the M64 BAR hasn't enabled yet, we needn't flush * the setting to hardware and just keep it to the cache */ p->m64b_cache[window_num] = data64; if (!(data64 & IODA_M64BT_ENABLE)) return OPAL_SUCCESS; p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, window_num, false); out_be64(p->regs + PHB_IODA_DATA0, data64); return OPAL_SUCCESS; } /* * We can't enable or disable I/O and M32 dynamically, even * unnecessary. So the function only support M64 BARs. */ static int64_t p7ioc_phb_mmio_enable(struct phb *phb, uint16_t window_type, uint16_t window_num, uint16_t enable) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t data64, base, mask; switch (window_type) { case OPAL_IO_WINDOW_TYPE: case OPAL_M32_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16 || enable >= OPAL_ENABLE_M64_NON_SPLIT) return OPAL_PARAMETER; break; default: return OPAL_PARAMETER; } /* * While enabling one specific M64 BAR, we should have * the base/size configured correctly. Otherwise, it * probably incurs fenced AIB. */ data64 = p->m64b_cache[window_num]; if (enable == OPAL_ENABLE_M64_SPLIT) { base = GETFIELD(IODA_M64BT_BASE, data64); base = (base << 24); mask = GETFIELD(IODA_M64BT_MASK, data64); if (base < p->m64_base || mask == 0x0ul) return OPAL_PARTIAL; data64 |= IODA_M64BT_ENABLE; } else if (enable == OPAL_DISABLE_M64) { data64 &= ~IODA_M64BT_ENABLE; } else return OPAL_PARAMETER; p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, window_num, false); out_be64(p->regs + PHB_IODA_DATA0, data64); p->m64b_cache[window_num] = data64; return OPAL_SUCCESS; } static int64_t p7ioc_map_pe_mmio_window(struct phb *phb, uint16_t pe_number, uint16_t window_type, uint16_t window_num, uint16_t segment_num) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t tbl, index; uint64_t *cache; if (pe_number > 127) return OPAL_PARAMETER; switch(window_type) { case OPAL_IO_WINDOW_TYPE: if (window_num != 0 || segment_num > 127) return OPAL_PARAMETER; tbl = IODA_TBL_IODT; index = segment_num; cache = &p->iod_cache[index]; break; case OPAL_M32_WINDOW_TYPE: if (window_num != 0 || segment_num > 127) return OPAL_PARAMETER; tbl = IODA_TBL_M32DT; index = segment_num; cache = &p->m32d_cache[index]; break; case OPAL_M64_WINDOW_TYPE: if (window_num > 15 || segment_num > 7) return OPAL_PARAMETER; tbl = IODA_TBL_M64DT; index = window_num << 3 | segment_num; cache = &p->m64d_cache[index]; break; default: return OPAL_PARAMETER; } p7ioc_phb_ioda_sel(p, tbl, index, false); out_be64(p->regs + PHB_IODA_DATA0, SETFIELD(IODA_XXDT_PE, 0ull, pe_number)); /* Update cache */ *cache = SETFIELD(IODA_XXDT_PE, 0ull, pe_number); return OPAL_SUCCESS; } static int64_t p7ioc_set_pe(struct phb *phb, uint64_t pe_number, uint64_t bdfn, uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, uint8_t pe_action) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t pelt; uint64_t *cache = &p->peltm_cache[pe_number]; if (pe_number > 127 || bdfn > 0xffff) return OPAL_PARAMETER; if (pe_action != OPAL_MAP_PE && pe_action != OPAL_UNMAP_PE) return OPAL_PARAMETER; if (bus_compare > 7) return OPAL_PARAMETER; if (pe_action == OPAL_MAP_PE) { pelt = SETFIELD(IODA_PELTM_BUS, 0ul, bdfn >> 8); pelt |= SETFIELD(IODA_PELTM_DEV, 0ul, (bdfn >> 3) & 0x1f); pelt |= SETFIELD(IODA_PELTM_FUNC, 0ul, bdfn & 0x7); pelt |= SETFIELD(IODA_PELTM_BUS_VALID, 0ul, bus_compare); if (dev_compare) pelt |= IODA_PELTM_DEV_VALID; if (func_compare) pelt |= IODA_PELTM_FUNC_VALID; } else pelt = 0; p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, pelt); /* Update cache */ *cache = pelt; return OPAL_SUCCESS; } static int64_t p7ioc_set_peltv(struct phb *phb, uint32_t parent_pe, uint32_t child_pe, uint8_t state) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint32_t reg; uint64_t mask, peltv; uint64_t *cache; if (parent_pe > 127 || child_pe > 127) return OPAL_PARAMETER; cache = (child_pe >> 6) ? &p->peltv_hi_cache[parent_pe] : &p->peltv_lo_cache[parent_pe]; reg = (child_pe >> 6) ? PHB_IODA_DATA1 : PHB_IODA_DATA0; child_pe &= 0x2f; mask = 1ull << (63 - child_pe); p7ioc_phb_ioda_sel(p, IODA_TBL_PELTV, parent_pe, false); peltv = in_be64(p->regs + reg); if (state) peltv |= mask; else peltv &= ~mask; out_be64(p->regs + reg, peltv); /* Update cache */ *cache = peltv; return OPAL_SUCCESS; } static int64_t p7ioc_map_pe_dma_window(struct phb *phb, uint16_t pe_number, uint16_t window_id, uint16_t tce_levels, uint64_t tce_table_addr, uint64_t tce_table_size, uint64_t tce_page_size) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t tvt0, tvt1, t, pelt; uint64_t dma_window_size; uint64_t *cache_lo, *cache_hi; if (pe_number > 127 || window_id > 127 || tce_levels != 1) return OPAL_PARAMETER; cache_lo = &p->tve_lo_cache[window_id]; cache_hi = &p->tve_hi_cache[window_id]; /* Encode table size */ dma_window_size = tce_page_size * (tce_table_size >> 3); t = ilog2(dma_window_size); if (t < 27) return OPAL_PARAMETER; tvt0 = SETFIELD(IODA_TVT0_TCE_TABLE_SIZE, 0ul, (t - 26)); /* Encode TCE page size */ switch(tce_page_size) { case 0x1000: /* 4K */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 1ul); break; case 0x10000: /* 64K */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 5ul); break; case 0x1000000: /* 16M */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 13ul); break; case 0x400000000: /* 16G */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 23ul); break; default: return OPAL_PARAMETER; } /* XXX Hub number ... leave 0 for now */ /* Shift in the address. The table address is "off by 4 bits" * but since the field is itself shifted by 16, we basically * need to write the address >> 12, which basically boils down * to writing a 4k page address */ tvt0 = SETFIELD(IODA_TVT0_TABLE_ADDR, tvt0, tce_table_addr >> 12); /* Read the PE filter info from the PELT-M */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, pe_number, false); pelt = in_be64(p->regs + PHB_IODA_DATA0); /* Copy in filter bits from PELT */ tvt0 = SETFIELD(IODA_TVT0_BUS_VALID, tvt0, GETFIELD(IODA_PELTM_BUS_VALID, pelt)); tvt0 = SETFIELD(IODA_TVT0_BUS_NUM, tvt0, GETFIELD(IODA_PELTM_BUS, pelt)); tvt1 = SETFIELD(IODA_TVT1_DEV_NUM, tvt1, GETFIELD(IODA_PELTM_DEV, pelt)); tvt1 = SETFIELD(IODA_TVT1_FUNC_NUM, tvt1, GETFIELD(IODA_PELTM_FUNC, pelt)); if (pelt & IODA_PELTM_DEV_VALID) tvt1 |= IODA_TVT1_DEV_VALID; if (pelt & IODA_PELTM_FUNC_VALID) tvt1 |= IODA_TVT1_FUNC_VALID; tvt1 = SETFIELD(IODA_TVT1_PE_NUM, tvt1, pe_number); /* Write the TVE */ p7ioc_phb_ioda_sel(p, IODA_TBL_TVT, window_id, false); out_be64(p->regs + PHB_IODA_DATA1, tvt1); out_be64(p->regs + PHB_IODA_DATA0, tvt0); /* Update cache */ *cache_lo = tvt0; *cache_hi = tvt1; return OPAL_SUCCESS; } static int64_t p7ioc_map_pe_dma_window_real(struct phb *phb __unused, uint16_t pe_number __unused, uint16_t dma_window_num __unused, uint64_t pci_start_addr __unused, uint64_t pci_mem_size __unused) { /* XXX Not yet implemented (not yet used by Linux) */ return OPAL_UNSUPPORTED; } static int64_t p7ioc_set_mve(struct phb *phb, uint32_t mve_number, uint32_t pe_number) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t pelt, mve = 0; uint64_t *cache = &p->mve_cache[mve_number]; if (pe_number > 127 || mve_number > 255) return OPAL_PARAMETER; /* Read the PE filter info from the PELT-M */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, pe_number, false); pelt = in_be64(p->regs + PHB_IODA_DATA0); mve = SETFIELD(IODA_MVT_BUS_VALID, mve, GETFIELD(IODA_PELTM_BUS_VALID, pelt)); mve = SETFIELD(IODA_MVT_BUS_NUM, mve, GETFIELD(IODA_PELTM_BUS, pelt)); mve = SETFIELD(IODA_MVT_DEV_NUM, mve, GETFIELD(IODA_PELTM_DEV, pelt)); mve = SETFIELD(IODA_MVT_FUNC_NUM, mve, GETFIELD(IODA_PELTM_FUNC, pelt)); if (pelt & IODA_PELTM_DEV_VALID) mve |= IODA_MVT_DEV_VALID; if (pelt & IODA_PELTM_FUNC_VALID) mve |= IODA_MVT_FUNC_VALID; mve = SETFIELD(IODA_MVT_PE_NUM, mve, pe_number); p7ioc_phb_ioda_sel(p, IODA_TBL_MVT, mve_number, false); out_be64(p->regs + PHB_IODA_DATA0, mve); /* Update cache */ *cache = mve; return OPAL_SUCCESS; } static int64_t p7ioc_set_mve_enable(struct phb *phb, uint32_t mve_number, uint32_t state) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t mve; uint64_t *cache = &p->mve_cache[mve_number]; if (mve_number > 255) return OPAL_PARAMETER; p7ioc_phb_ioda_sel(p, IODA_TBL_MVT, mve_number, false); mve = in_be64(p->regs + PHB_IODA_DATA0); if (state) mve |= IODA_MVT_VALID; else mve &= ~IODA_MVT_VALID; out_be64(p->regs + PHB_IODA_DATA0, mve); /* Update cache */ *cache = mve; return OPAL_SUCCESS; } static int64_t p7ioc_set_xive_pe(struct phb *phb, uint32_t pe_number, uint32_t xive_num) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t xive; if (pe_number > 127 || xive_num > 255) return OPAL_PARAMETER; /* Update MXIVE cache */ xive = p->mxive_cache[xive_num]; xive = SETFIELD(IODA_XIVT_PENUM, xive, pe_number); p->mxive_cache[xive_num] = xive; /* Update HW */ p7ioc_phb_ioda_sel(p, IODA_TBL_MXIVT, xive_num, false); xive = in_be64(p->regs + PHB_IODA_DATA0); xive = SETFIELD(IODA_XIVT_PENUM, xive, pe_number); out_be64(p->regs + PHB_IODA_DATA0, xive); return OPAL_SUCCESS; } static int64_t p7ioc_get_xive_source(struct phb *phb, uint32_t xive_num, int32_t *interrupt_source_number) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); if (xive_num > 255 || !interrupt_source_number) return OPAL_PARAMETER; *interrupt_source_number = (p->buid_msi << 4) | xive_num; return OPAL_SUCCESS; } static int64_t p7ioc_get_msi_32(struct phb *phb __unused, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint32_t *msi_address, uint32_t *message_data) { if (mve_number > 255 || xive_num > 255 || msi_range != 1) return OPAL_PARAMETER; *msi_address = 0xffff0000 | (mve_number << 4); *message_data = xive_num; return OPAL_SUCCESS; } static int64_t p7ioc_get_msi_64(struct phb *phb __unused, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint64_t *msi_address, uint32_t *message_data) { if (mve_number > 255 || xive_num > 255 || msi_range != 1) return OPAL_PARAMETER; *msi_address = (9ul << 60) | (((u64)mve_number) << 48); *message_data = xive_num; return OPAL_SUCCESS; } static void p7ioc_root_port_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_PERR_RESP); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 |= (PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Mask various unrecoverable errors */ if (!aercap) return; pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, &val32); val32 |= (PCIECAP_AER_UE_MASK_POISON_TLP | PCIECAP_AER_UE_MASK_COMPL_TIMEOUT | PCIECAP_AER_UE_MASK_COMPL_ABORT | PCIECAP_AER_UE_MASK_ECRC); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, val32); /* Report various unrecoverable errors as fatal errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, &val32); val32 |= (PCIECAP_AER_UE_SEVERITY_DLLP | PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN | PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_UNEXP_COMPL | PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW | PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, val32); /* Mask various recoverable errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, &val32); val32 |= PCIECAP_AER_CE_MASK_ADV_NONFATAL; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, val32); /* Enable ECRC check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= (PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); /* Enable all error reporting */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_RERR_CMD, &val32); val32 |= (PCIECAP_AER_RERR_CMD_FE | PCIECAP_AER_RERR_CMD_NFE | PCIECAP_AER_RERR_CMD_CE); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_RERR_CMD, val32); } static void p7ioc_switch_port_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking and disable INTx */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_INTx_DIS); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Disable partity error and enable system error */ pci_cfg_read16(phb, bdfn, PCI_CFG_BRCTL, &val16); val16 &= ~PCI_CFG_BRCTL_PERR_RESP_EN; val16 |= PCI_CFG_BRCTL_SERR_EN; pci_cfg_write16(phb, bdfn, PCI_CFG_BRCTL, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 |= (PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Unmask all unrecoverable errors */ if (!aercap) return; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, 0x0); /* Severity of unrecoverable errors */ if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT) val32 = (PCIECAP_AER_UE_SEVERITY_DLLP | PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN | PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW | PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP | PCIECAP_AER_UE_SEVERITY_INTERNAL); else val32 = (PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_INTERNAL); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, val32); /* Mask various correctable errors */ val32 = PCIECAP_AER_CE_MASK_ADV_NONFATAL; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, val32); /* Enable ECRC generation and disable ECRC check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= PCIECAP_AER_CAPCTL_ECRCG_EN; val32 &= ~PCIECAP_AER_CAPCTL_ECRCC_EN; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } static void p7ioc_endpoint_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 &= ~PCICAP_EXP_DEVCTL_CE_REPORT; val16 |= (PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Enable ECRC generation and check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= (PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } static void p7ioc_device_init(struct phb *phb, struct pci_device *dev) { int ecap = 0; int aercap = 0; /* Figure out AER capability */ if (pci_has_cap(dev, PCI_CFG_CAP_ID_EXP, false)) { ecap = pci_cap(dev, PCI_CFG_CAP_ID_EXP, false); if (!pci_has_cap(dev, PCIECAP_ID_AER, true)) { aercap = pci_find_ecap(phb, dev->bdfn, PCIECAP_ID_AER, NULL); if (aercap > 0) pci_set_cap(dev, PCIECAP_ID_AER, aercap, true); } else { aercap = pci_cap(dev, PCIECAP_ID_AER, true); } } /* Common initialization for the device */ pci_device_init(phb, dev); if (dev->dev_type == PCIE_TYPE_ROOT_PORT) p7ioc_root_port_init(phb, dev, ecap, aercap); else if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT || dev->dev_type == PCIE_TYPE_SWITCH_DNPORT) p7ioc_switch_port_init(phb, dev, ecap, aercap); else p7ioc_endpoint_init(phb, dev, ecap, aercap); } static int64_t p7ioc_pci_reinit(struct phb *phb, uint64_t scope, uint64_t data) { struct pci_device *pd; uint16_t bdfn = data; if (scope != OPAL_REINIT_PCI_DEV) return OPAL_PARAMETER; pd = pci_find_dev(phb, bdfn); if (!pd) return OPAL_PARAMETER; p7ioc_device_init(phb, pd); return OPAL_SUCCESS; } static uint8_t p7ioc_choose_bus(struct phb *phb __unused, struct pci_device *bridge, uint8_t candidate, uint8_t *max_bus, bool *use_max) { uint8_t m, al; int i; /* Bus number selection is nasty on P7IOC. Our EEH HW can only cope * with bus ranges that are naturally aligned powers of two. It also * has "issues" with dealing with more than 32 bus numbers. * * On the other hand we can deal with overlaps to some extent as * the PELT-M entries are ordered. * * We also don't need to bother with the busses between the upstream * and downstream ports of switches. * * For now we apply this simple mechanism which matche what OFW does * under OPAL: * * - Top level bus (PHB to RC) is 0 * - RC to first device is 1..ff * - Then going down, a switch gets (N = parent bus, M = parent max) * * Upstream bridge is N+1, M, use_max = false * * Downstream bridge is closest power of two from 32 down and * * use max * * XXX NOTE: If we have access to HW VPDs, we could know whether * this is a bridge with a single device on it such as IPR and * limit ourselves to a single bus number. */ /* Default use_max is false (legacy) */ *use_max = false; /* If we are the root complex or we are not in PCIe land anymore, just * use legacy algorithm */ if (!bridge || !pci_has_cap(bridge, PCI_CFG_CAP_ID_EXP, false)) return candidate; /* Figure out the bridge type */ switch(bridge->dev_type) { case PCIE_TYPE_PCIX_TO_PCIE: /* PCI-X to PCIE ... hrm, let's not bother too much with that */ return candidate; case PCIE_TYPE_SWITCH_UPPORT: case PCIE_TYPE_ROOT_PORT: /* Upstream port, we use legacy handling as well */ return candidate; case PCIE_TYPE_SWITCH_DNPORT: case PCIE_TYPE_PCIE_TO_PCIX: /* That leaves us with the interesting cases that we handle */ break; default: /* Should not happen, treat as legacy */ prerror("PCI: Device %04x has unsupported type %d in choose_bus\n", bridge->bdfn, bridge->dev_type); return candidate; } /* Ok, let's find a power of two that fits, fallback to 1 */ for (i = 5; i >= 0; i--) { m = (1 << i) - 1; al = (candidate + m) & ~m; if (al <= *max_bus && (al + m) <= *max_bus) break; } if (i < 0) return 0; *use_max = true; *max_bus = al + m; return al; } /* p7ioc_phb_init_ioda_cache - Reset the IODA cache values */ static void p7ioc_phb_init_ioda_cache(struct p7ioc_phb *p) { unsigned int i; for (i = 0; i < 8; i++) p->lxive_cache[i] = SETFIELD(IODA_XIVT_PRIORITY, 0ull, 0xff); for (i = 0; i < 256; i++) { p->mxive_cache[i] = SETFIELD(IODA_XIVT_PRIORITY, 0ull, 0xff); p->mve_cache[i] = 0; } for (i = 0; i < 16; i++) p->m64b_cache[i] = 0; /* * Since there is only one root port under the PHB, * We make all PELTM entries except last one to be * invalid by configuring their RID to 00:00.1. The * last entry is to encompass all RIDs. */ for (i = 0; i < 127; i++) p->peltm_cache[i] = 0x0001f80000000000; p->peltm_cache[127] = 0x0ul; for (i = 0; i < 128; i++) { p->peltv_lo_cache[i] = 0; p->peltv_hi_cache[i] = 0; p->tve_lo_cache[i] = 0; p->tve_hi_cache[i] = 0; p->iod_cache[i] = 0; p->m32d_cache[i] = 0; p->m64d_cache[i] = 0; } } /* p7ioc_phb_ioda_reset - Reset the IODA tables * * @purge: If true, the cache is cleared and the cleared values * are applied to HW. If false, the cached values are * applied to HW * * This reset the IODA tables in the PHB. It is called at * initialization time, on PHB reset, and can be called * explicitly from OPAL */ static int64_t p7ioc_ioda_reset(struct phb *phb, bool purge) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); unsigned int i; uint64_t reg64; uint64_t data64, data64_hi; uint8_t prio; uint16_t server; uint64_t m_server, m_prio; /* If the "purge" argument is set, we clear the table cache */ if (purge) p7ioc_phb_init_ioda_cache(p); /* Init_18..19: Setup the HRT * * XXX NOTE: I still don't completely get that HRT business so * I'll just mimmic BML and put the PHB number + 1 in there */ p7ioc_phb_ioda_sel(p, IODA_TBL_HRT, 0, true); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); /* Init_20..21: Cleanup the LXIVT * * We set the priority to FF (masked) and clear everything * else. That means we leave the HRT index to 0 which is * going to remain unmodified... for now. */ p7ioc_phb_ioda_sel(p, IODA_TBL_LXIVT, 0, true); for (i = 0; i < 8; i++) { data64 = p->lxive_cache[i]; server = GETFIELD(IODA_XIVT_SERVER, data64); prio = GETFIELD(IODA_XIVT_PRIORITY, data64); /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } data64 = SETFIELD(IODA_XIVT_SERVER, data64, m_server); data64 = SETFIELD(IODA_XIVT_PRIORITY, data64, m_prio); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_22..23: Cleanup the MXIVT * * We set the priority to FF (masked) and clear everything * else. That means we leave the HRT index to 0 which is * going to remain unmodified... for now. */ p7ioc_phb_ioda_sel(p, IODA_TBL_MXIVT, 0, true); for (i = 0; i < 256; i++) { data64 = p->mxive_cache[i]; server = GETFIELD(IODA_XIVT_SERVER, data64); prio = GETFIELD(IODA_XIVT_PRIORITY, data64); /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } data64 = SETFIELD(IODA_XIVT_SERVER, data64, m_server); data64 = SETFIELD(IODA_XIVT_PRIORITY, data64, m_prio); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_24..25: Cleanup the MVT */ p7ioc_phb_ioda_sel(p, IODA_TBL_MVT, 0, true); for (i = 0; i < 256; i++) { data64 = p->mve_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_26..27: Cleanup the PELTM * * A completely clear PELTM should make everything match PE 0 */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, 0, true); for (i = 0; i < 127; i++) { data64 = p->peltm_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_28..30: Cleanup the PELTV */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTV, 0, true); for (i = 0; i < 127; i++) { data64 = p->peltv_lo_cache[i]; data64_hi = p->peltv_hi_cache[i]; out_be64(p->regs + PHB_IODA_DATA1, data64_hi); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_31..33: Cleanup the TVT */ p7ioc_phb_ioda_sel(p, IODA_TBL_TVT, 0, true); for (i = 0; i < 127; i++) { data64 = p->tve_lo_cache[i]; data64_hi = p->tve_hi_cache[i]; out_be64(p->regs + PHB_IODA_DATA1, data64_hi); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_34..35: Cleanup the M64BT * * We don't enable M64 BARs by default. However, * we shouldn't purge the hw and cache for it in * future. */ p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, 0, true); for (i = 0; i < 16; i++) out_be64(p->regs + PHB_IODA_DATA0, 0); /* Init_36..37: Cleanup the IODT */ p7ioc_phb_ioda_sel(p, IODA_TBL_IODT, 0, true); for (i = 0; i < 127; i++) { data64 = p->iod_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_38..39: Cleanup the M32DT */ p7ioc_phb_ioda_sel(p, IODA_TBL_M32DT, 0, true); for (i = 0; i < 127; i++) { data64 = p->m32d_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_40..41: Cleanup the M64DT */ p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, 0, true); for (i = 0; i < 16; i++) { data64 = p->m64b_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } p7ioc_phb_ioda_sel(p, IODA_TBL_M64DT, 0, true); for (i = 0; i < 127; i++) { data64 = p->m64d_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Clear up the TCE cache */ reg64 = in_be64(p->regs + PHB_PHB2_CONFIG); reg64 &= ~PHB_PHB2C_64B_TCE_EN; out_be64(p->regs + PHB_PHB2_CONFIG, reg64); reg64 |= PHB_PHB2C_64B_TCE_EN; out_be64(p->regs + PHB_PHB2_CONFIG, reg64); in_be64(p->regs + PHB_PHB2_CONFIG); /* Clear PEST & PEEV */ for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) { uint64_t pesta, pestb; p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, i, false); pesta = in_be64(p->regs + PHB_IODA_DATA0); out_be64(p->regs + PHB_IODA_DATA0, 0); p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, i, false); pestb = in_be64(p->regs + PHB_IODA_DATA0); out_be64(p->regs + PHB_IODA_DATA0, 0); if ((pesta & IODA_PESTA_MMIO_FROZEN) || (pestb & IODA_PESTB_DMA_STOPPED)) PHBDBG(p, "Frozen PE#%d (%s - %s)\n", i, (pestb & IODA_PESTB_DMA_STOPPED) ? "DMA" : "", (pesta & IODA_PESTA_MMIO_FROZEN) ? "MMIO" : ""); } p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); for (i = 0; i < 2; i++) out_be64(p->regs + PHB_IODA_DATA0, 0); return OPAL_SUCCESS; } /* * Clear anything we have in PAPR Error Injection registers. Though * the spec says the PAPR error injection should be one-shot without * the "sticky" bit. However, that's false according to the experiments * I had. So we have to clear it at appropriate point in kernel to * avoid endless frozen PE. */ static int64_t p7ioc_papr_errinjct_reset(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, 0x0ul); out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, 0x0ul); return OPAL_SUCCESS; } static const struct phb_ops p7ioc_phb_ops = { .lock = p7ioc_phb_lock, .unlock = p7ioc_phb_unlock, .cfg_read8 = p7ioc_pcicfg_read8, .cfg_read16 = p7ioc_pcicfg_read16, .cfg_read32 = p7ioc_pcicfg_read32, .cfg_write8 = p7ioc_pcicfg_write8, .cfg_write16 = p7ioc_pcicfg_write16, .cfg_write32 = p7ioc_pcicfg_write32, .choose_bus = p7ioc_choose_bus, .device_init = p7ioc_device_init, .pci_reinit = p7ioc_pci_reinit, .eeh_freeze_status = p7ioc_eeh_freeze_status, .eeh_freeze_clear = p7ioc_eeh_freeze_clear, .eeh_freeze_set = p7ioc_eeh_freeze_set, .err_inject = p7ioc_err_inject, .get_diag_data = NULL, .get_diag_data2 = p7ioc_get_diag_data, .next_error = p7ioc_eeh_next_error, .phb_mmio_enable = p7ioc_phb_mmio_enable, .set_phb_mem_window = p7ioc_set_phb_mem_window, .map_pe_mmio_window = p7ioc_map_pe_mmio_window, .set_pe = p7ioc_set_pe, .set_peltv = p7ioc_set_peltv, .map_pe_dma_window = p7ioc_map_pe_dma_window, .map_pe_dma_window_real = p7ioc_map_pe_dma_window_real, .set_mve = p7ioc_set_mve, .set_mve_enable = p7ioc_set_mve_enable, .set_xive_pe = p7ioc_set_xive_pe, .get_xive_source = p7ioc_get_xive_source, .get_msi_32 = p7ioc_get_msi_32, .get_msi_64 = p7ioc_get_msi_64, .ioda_reset = p7ioc_ioda_reset, .papr_errinjct_reset = p7ioc_papr_errinjct_reset, .presence_detect = p7ioc_presence_detect, .link_state = p7ioc_link_state, .power_state = p7ioc_power_state, .slot_power_off = p7ioc_slot_power_off, .slot_power_on = p7ioc_slot_power_on, .complete_reset = p7ioc_complete_reset, .hot_reset = p7ioc_hot_reset, .fundamental_reset = p7ioc_freset, .poll = p7ioc_poll, }; /* p7ioc_phb_get_xive - Interrupt control from OPAL */ static int64_t p7ioc_msi_get_xive(void *data, uint32_t isn, uint16_t *server, uint8_t *prio) { struct p7ioc_phb *p = data; uint32_t irq, fbuid = P7_IRQ_FBUID(isn); uint64_t xive; if (fbuid < p->buid_msi || fbuid >= (p->buid_msi + 0x10)) return OPAL_PARAMETER; irq = isn & 0xff; xive = p->mxive_cache[irq]; *server = GETFIELD(IODA_XIVT_SERVER, xive); *prio = GETFIELD(IODA_XIVT_PRIORITY, xive); return OPAL_SUCCESS; } /* p7ioc_phb_set_xive - Interrupt control from OPAL */ static int64_t p7ioc_msi_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t prio) { struct p7ioc_phb *p = data; uint32_t irq, fbuid = P7_IRQ_FBUID(isn); uint64_t xive, m_server, m_prio; if (fbuid < p->buid_msi || fbuid >= (p->buid_msi + 0x10)) return OPAL_PARAMETER; /* We cache the arguments because we have to mangle * it in order to hijack 3 bits of priority to extend * the server number */ irq = isn & 0xff; xive = p->mxive_cache[irq]; xive = SETFIELD(IODA_XIVT_SERVER, xive, server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, prio); p->mxive_cache[irq] = xive; /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } /* We use HRT entry 0 always for now */ p7ioc_phb_ioda_sel(p, IODA_TBL_MXIVT, irq, false); xive = in_be64(p->regs + PHB_IODA_DATA0); xive = SETFIELD(IODA_XIVT_SERVER, xive, m_server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, m_prio); out_be64(p->regs + PHB_IODA_DATA0, xive); return OPAL_SUCCESS; } /* p7ioc_phb_get_xive - Interrupt control from OPAL */ static int64_t p7ioc_lsi_get_xive(void *data, uint32_t isn, uint16_t *server, uint8_t *prio) { struct p7ioc_phb *p = data; uint32_t irq = (isn & 0x7); uint32_t fbuid = P7_IRQ_FBUID(isn); uint64_t xive; if (fbuid != p->buid_lsi) return OPAL_PARAMETER; xive = p->lxive_cache[irq]; *server = GETFIELD(IODA_XIVT_SERVER, xive); *prio = GETFIELD(IODA_XIVT_PRIORITY, xive); return OPAL_SUCCESS; } /* p7ioc_phb_set_xive - Interrupt control from OPAL */ static int64_t p7ioc_lsi_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t prio) { struct p7ioc_phb *p = data; uint32_t irq = (isn & 0x7); uint32_t fbuid = P7_IRQ_FBUID(isn); uint64_t xive, m_server, m_prio; if (fbuid != p->buid_lsi) return OPAL_PARAMETER; xive = SETFIELD(IODA_XIVT_SERVER, 0ull, server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, prio); /* * We cache the arguments because we have to mangle * it in order to hijack 3 bits of priority to extend * the server number */ p->lxive_cache[irq] = xive; /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } /* We use HRT entry 0 always for now */ p7ioc_phb_ioda_sel(p, IODA_TBL_LXIVT, irq, false); xive = in_be64(p->regs + PHB_IODA_DATA0); xive = SETFIELD(IODA_XIVT_SERVER, xive, m_server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, m_prio); out_be64(p->regs + PHB_IODA_DATA0, xive); return OPAL_SUCCESS; } static void p7ioc_phb_err_interrupt(void *data, uint32_t isn) { struct p7ioc_phb *p = data; uint64_t peev0, peev1; PHBDBG(p, "Got interrupt 0x%04x\n", isn); opal_pci_eeh_set_evt(p->phb.opal_id); /* If the PHB is broken, go away */ if (p->state == P7IOC_PHB_STATE_BROKEN) return; /* * Check if there's an error pending and update PHB fence * state and return, the ER error is drowned at this point */ lock(&p->lock); if (p7ioc_phb_fenced(p)) { p->state = P7IOC_PHB_STATE_FENCED; PHBERR(p, "ER error ignored, PHB fenced\n"); unlock(&p->lock); return; } /* * If we already had pending errors, which might be * moved from IOC, then we needn't check PEEV to avoid * overwriting the errors from IOC. */ if (!p7ioc_phb_err_pending(p)) { unlock(&p->lock); return; } /* * We don't have pending errors from IOC, it's safe * to check PEEV for frozen PEs. */ p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); if (peev0 || peev1) { p->err.err_src = P7IOC_ERR_SRC_PHB0 + p->index; p->err.err_class = P7IOC_ERR_CLASS_ER; p->err.err_bit = 0; p7ioc_phb_set_err_pending(p, true); } unlock(&p->lock); } /* MSIs (OS owned) */ static const struct irq_source_ops p7ioc_msi_irq_ops = { .get_xive = p7ioc_msi_get_xive, .set_xive = p7ioc_msi_set_xive, }; /* LSIs (OS owned) */ static const struct irq_source_ops p7ioc_lsi_irq_ops = { .get_xive = p7ioc_lsi_get_xive, .set_xive = p7ioc_lsi_set_xive, }; /* PHB Errors (Ski owned) */ static const struct irq_source_ops p7ioc_phb_err_irq_ops = { .get_xive = p7ioc_lsi_get_xive, .set_xive = p7ioc_lsi_set_xive, .interrupt = p7ioc_phb_err_interrupt, }; static void p7ioc_pcie_add_node(struct p7ioc_phb *p) { uint64_t reg[2], iob, m32b, m64b, tkill; uint32_t lsibase, icsp = get_ics_phandle(); struct dt_node *np; reg[0] = cleanup_addr((uint64_t)p->regs); reg[1] = 0x100000; np = dt_new_addr(p->ioc->dt_node, "pciex", reg[0]); if (!np) return; p->phb.dt_node = np; dt_add_property_strings(np, "compatible", "ibm,p7ioc-pciex", "ibm,ioda-phb"); dt_add_property_strings(np, "device_type", "pciex"); dt_add_property(np, "reg", reg, sizeof(reg)); dt_add_property_cells(np, "#address-cells", 3); dt_add_property_cells(np, "#size-cells", 2); dt_add_property_cells(np, "#interrupt-cells", 1); dt_add_property_cells(np, "bus-range", 0, 0xff); dt_add_property_cells(np, "clock-frequency", 0x200, 0); /* ??? */ dt_add_property_cells(np, "interrupt-parent", icsp); /* XXX FIXME: add slot-name */ //dt_property_cell("bus-width", 8); /* Figure it out from VPD ? */ /* "ranges", we only expose IO and M32 * * Note: The kernel expects us to have chopped of 64k from the * M32 size (for the 32-bit MSIs). If we don't do that, it will * get confused (OPAL does it) */ iob = cleanup_addr(p->io_base); m32b = cleanup_addr(p->m32_base + M32_PCI_START); dt_add_property_cells(np, "ranges", /* IO space */ 0x01000000, 0x00000000, 0x00000000, hi32(iob), lo32(iob), 0, PHB_IO_SIZE, /* M32 space */ 0x02000000, 0x00000000, M32_PCI_START, hi32(m32b), lo32(m32b), 0,M32_PCI_SIZE - 0x10000); /* XXX FIXME: add opal-memwin32, dmawins, etc... */ m64b = cleanup_addr(p->m64_base); dt_add_property_cells(np, "ibm,opal-m64-window", hi32(m64b), lo32(m64b), hi32(m64b), lo32(m64b), hi32(PHB_M64_SIZE), lo32(PHB_M64_SIZE)); dt_add_property_cells(np, "ibm,opal-msi-ports", 256); dt_add_property_cells(np, "ibm,opal-num-pes", 128); dt_add_property_cells(np, "ibm,opal-reserved-pe", 127); dt_add_property_cells(np, "ibm,opal-msi-ranges", p->buid_msi << 4, 0x100); tkill = reg[0] + PHB_TCE_KILL; dt_add_property_cells(np, "ibm,opal-tce-kill", hi32(tkill), lo32(tkill)); /* Add associativity properties */ add_chip_dev_associativity(np); /* The interrupt maps will be generated in the RC node by the * PCI code based on the content of this structure: */ lsibase = p->buid_lsi << 4; p->phb.lstate.int_size = 1; p->phb.lstate.int_val[0][0] = lsibase + PHB_LSI_PCIE_INTA; p->phb.lstate.int_val[1][0] = lsibase + PHB_LSI_PCIE_INTB; p->phb.lstate.int_val[2][0] = lsibase + PHB_LSI_PCIE_INTC; p->phb.lstate.int_val[3][0] = lsibase + PHB_LSI_PCIE_INTD; p->phb.lstate.int_parent[0] = icsp; p->phb.lstate.int_parent[1] = icsp; p->phb.lstate.int_parent[2] = icsp; p->phb.lstate.int_parent[3] = icsp; } /* p7ioc_phb_setup - Setup a p7ioc_phb data structure * * WARNING: This is called before the AIB register routing is * established. If this wants to access PHB registers, it must * use the ASB hard coded variant (slower) */ void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index) { struct p7ioc_phb *p = &ioc->phbs[index]; unsigned int buid_base = ioc->buid_base + PHBn_BUID_BASE(index); p->index = index; p->ioc = ioc; p->gen = 2; /* Operate in Gen2 mode by default */ p->phb.ops = &p7ioc_phb_ops; p->phb.phb_type = phb_type_pcie_v2; p->regs_asb = ioc->regs + PHBn_ASB_BASE(index); p->regs = ioc->regs + PHBn_AIB_BASE(index); p->buid_lsi = buid_base + PHB_BUID_LSI_OFFSET; p->buid_msi = buid_base + PHB_BUID_MSI_OFFSET; p->io_base = ioc->mmio1_win_start + PHBn_IO_BASE(index); p->m32_base = ioc->mmio2_win_start + PHBn_M32_BASE(index); p->m64_base = ioc->mmio2_win_start + PHBn_M64_BASE(index); p->state = P7IOC_PHB_STATE_UNINITIALIZED; p->phb.scan_map = 0x1; /* Only device 0 to scan */ /* Find P7IOC base location code in IOC */ p->phb.base_loc_code = dt_prop_get_def(ioc->dt_node, "ibm,io-base-loc-code", NULL); if (!p->phb.base_loc_code) prerror("P7IOC: Base location code not found !\n"); /* Create device node for PHB */ p7ioc_pcie_add_node(p); /* Register OS interrupt sources */ register_irq_source(&p7ioc_msi_irq_ops, p, p->buid_msi << 4, 256); register_irq_source(&p7ioc_lsi_irq_ops, p, p->buid_lsi << 4, 4); /* Register internal interrupt source (LSI 7) */ register_irq_source(&p7ioc_phb_err_irq_ops, p, (p->buid_lsi << 4) + PHB_LSI_PCIE_ERROR, 1); /* Initialize IODA table caches */ p7ioc_phb_init_ioda_cache(p); /* We register the PHB before we initialize it so we * get a useful OPAL ID for it */ pci_register_phb(&p->phb); /* Platform additional setup */ if (platform.pci_setup_phb) platform.pci_setup_phb(&p->phb, p->index); } static bool p7ioc_phb_wait_dlp_reset(struct p7ioc_phb *p) { unsigned int i; uint64_t val; /* * Firmware cannot access the UTL core regs or PCI config space * until the cores are out of DL_PGRESET. * DL_PGRESET should be polled until it is inactive with a value * of '0'. The recommended polling frequency is once every 1ms. * Firmware should poll at least 200 attempts before giving up. * MMIO Stores to the link are silently dropped by the UTL core if * the link is down. * MMIO Loads to the link will be dropped by the UTL core and will * eventually time-out and will return an all ones response if the * link is down. */ #define DLP_RESET_ATTEMPTS 400 printf("P7IOC: Waiting for DLP PG reset to complete...\n"); for (i = 0; i < DLP_RESET_ATTEMPTS; i++) { val = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (!(val & PHB_PCIE_DLP_TC_DL_PGRESET)) break; time_wait_ms(1); } if (val & PHB_PCIE_DLP_TC_DL_PGRESET) { PHBERR(p, "Timeout waiting for DLP PG reset !\n"); return false; } return true; } /* p7ioc_phb_init_rc - Initialize the Root Complex config space */ static bool p7ioc_phb_init_rc_cfg(struct p7ioc_phb *p) { int64_t ecap, aercap; /* XXX Handle errors ? */ /* Init_51..51: * * Set primary bus to 0, secondary to 1 and subordinate to 0xff */ p7ioc_pcicfg_write32(&p->phb, 0, PCI_CFG_PRIMARY_BUS, 0x00ff0100); /* Init_52..57 * * IO and Memory base & limits are set to base > limit, which * allows all inbounds. * * XXX This has the potential of confusing the OS which might * think that nothing is forwarded downstream. We probably need * to fix this to match the IO and M32 PHB windows */ p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_IO_BASE, 0x0010); p7ioc_pcicfg_write32(&p->phb, 0, PCI_CFG_MEM_BASE, 0x00000010); p7ioc_pcicfg_write32(&p->phb, 0, PCI_CFG_PREF_MEM_BASE, 0x00000010); /* Init_58..: Setup bridge control to enable forwarding of CORR, FATAL, * and NONFATAL errors */ p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, PCI_CFG_BRCTL_SERR_EN); /* Init_60..61 * * PCIE Device control/status, enable error reporting, disable relaxed * ordering, set MPS to 128 (see note), clear errors. * * Note: The doc recommends to set MPS to 4K. This has proved to have * some issues as it requires specific claming of MRSS on devices and * we've found devices in the field that misbehave when doing that. * * We currently leave it all to 128 bytes (minimum setting) at init * time. The generic PCIe probing later on might apply a different * value, or the kernel will, but we play it safe at early init */ if (p->ecap <= 0) { ecap = pci_find_cap(&p->phb, 0, PCI_CFG_CAP_ID_EXP); if (ecap < 0) { PHBERR(p, "Can't locate PCI-E capability\n"); return false; } p->ecap = ecap; } else { ecap = p->ecap; } p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DEVSTAT, PCICAP_EXP_DEVSTAT_CE | PCICAP_EXP_DEVSTAT_NFE | PCICAP_EXP_DEVSTAT_FE | PCICAP_EXP_DEVSTAT_UE); p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DEVCTL, PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT | SETFIELD(PCICAP_EXP_DEVCTL_MPS, 0, PCIE_MPS_128B)); /* Init_62..63 * * Root Control Register. Enable error reporting * * Note: Added CRS visibility. */ p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_RC, PCICAP_EXP_RC_SYSERR_ON_CE | PCICAP_EXP_RC_SYSERR_ON_NFE | PCICAP_EXP_RC_SYSERR_ON_FE | PCICAP_EXP_RC_CRS_VISIBLE); /* Init_64..65 * * Device Control 2. Enable ARI fwd, set timer */ p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DCTL2, SETFIELD(PCICAP_EXP_DCTL2_CMPTOUT, 0, 2) | PCICAP_EXP_DCTL2_ARI_FWD); /* Init_66..81 * * AER inits */ aercap = pci_find_ecap(&p->phb, 0, PCIECAP_ID_AER, NULL); if (aercap < 0) { /* Shouldn't happen */ PHBERR(p, "Failed to locate AER capability in bridge\n"); return false; } p->aercap = aercap; /* Clear all UE status */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_STATUS, 0xffffffff); /* Disable some error reporting as per the P7IOC spec */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_MASK, PCIECAP_AER_UE_POISON_TLP | PCIECAP_AER_UE_COMPL_TIMEOUT | PCIECAP_AER_UE_COMPL_ABORT | PCIECAP_AER_UE_ECRC); /* Report some errors as fatal */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_SEVERITY, PCIECAP_AER_UE_DLP | PCIECAP_AER_UE_SURPRISE_DOWN | PCIECAP_AER_UE_FLOW_CTL_PROT | PCIECAP_AER_UE_UNEXP_COMPL | PCIECAP_AER_UE_RECV_OVFLOW | PCIECAP_AER_UE_MALFORMED_TLP); /* Clear all CE status */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CE_STATUS, 0xffffffff); /* Disable some error reporting as per the P7IOC spec */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CE_MASK, PCIECAP_AER_CE_ADV_NONFATAL); /* Enable ECRC generation & checking */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CAPCTL, PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); /* Enable reporting in root error control */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_RERR_CMD, PCIECAP_AER_RERR_CMD_FE | PCIECAP_AER_RERR_CMD_NFE | PCIECAP_AER_RERR_CMD_CE); /* Clear root error status */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_RERR_STA, 0xffffffff); return true; } static void p7ioc_phb_init_utl(struct p7ioc_phb *p) { /* Init_82..84: Clear spurious errors and assign errors to the * right "interrupt" signal */ out_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS, 0xffffffffffffffff); out_be64(p->regs + UTL_SYS_BUS_AGENT_ERR_SEVERITY, 0x0000000000000000); out_be64(p->regs + UTL_SYS_BUS_AGENT_IRQ_EN, 0xac80000000000000); /* Init_85..89: Setup buffer allocations */ out_be64(p->regs + UTL_OUT_POST_DAT_BUF_ALLOC, 0x0400000000000000); out_be64(p->regs + UTL_IN_POST_HDR_BUF_ALLOC, 0x1000000000000000); out_be64(p->regs + UTL_IN_POST_DAT_BUF_ALLOC, 0x4000000000000000); out_be64(p->regs + UTL_PCIE_TAGS_ALLOC, 0x0800000000000000); out_be64(p->regs + UTL_GBIF_READ_TAGS_ALLOC, 0x0800000000000000); /* Init_90: PCI Express port control */ out_be64(p->regs + UTL_PCIE_PORT_CONTROL, 0x8480000000000000); /* Init_91..93: Clean & setup port errors */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xff7fffffffffffff); out_be64(p->regs + UTL_PCIE_PORT_ERROR_SEV, 0x00e0000000000000); out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7e65000000000000); /* Init_94 : Cleanup RC errors */ out_be64(p->regs + UTL_RC_STATUS, 0xffffffffffffffff); } static void p7ioc_phb_init_errors(struct p7ioc_phb *p) { /* Init_98: LEM Error Mask : Temporarily disable error interrupts */ out_be64(p->regs + PHB_LEM_ERROR_MASK, 0xffffffffffffffff); /* Init_99..107: Configure main error traps & clear old state */ out_be64(p->regs + PHB_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_ERR_LEM_ENABLE, 0xffffffffefffffff); out_be64(p->regs + PHB_ERR_FREEZE_ENABLE, 0x0000000061c00000); out_be64(p->regs + PHB_ERR_AIB_FENCE_ENABLE, 0xffffffc58c000000); out_be64(p->regs + PHB_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_108_116: Configure MMIO error traps & clear old state */ out_be64(p->regs + PHB_OUT_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_OUT_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR_LEM_ENABLE, 0xffffffffffffffff); out_be64(p->regs + PHB_OUT_ERR_FREEZE_ENABLE, 0x0000430803000000); out_be64(p->regs + PHB_OUT_ERR_AIB_FENCE_ENABLE, 0x9df3bc00f0f0700f); out_be64(p->regs + PHB_OUT_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_117_125: Configure DMA_A error traps & clear old state */ out_be64(p->regs + PHB_INA_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_INA_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR_LEM_ENABLE, 0xffffffffffffffff); out_be64(p->regs + PHB_INA_ERR_FREEZE_ENABLE, 0xc00003ff01006000); out_be64(p->regs + PHB_INA_ERR_AIB_FENCE_ENABLE, 0x3fff50007e559fd8); out_be64(p->regs + PHB_INA_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_126_134: Configure DMA_B error traps & clear old state */ out_be64(p->regs + PHB_INB_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_INB_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_LEM_ENABLE, 0xffffffffffffffff); out_be64(p->regs + PHB_INB_ERR_FREEZE_ENABLE, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_AIB_FENCE_ENABLE, 0x18ff80ffff7f0000); out_be64(p->regs + PHB_INB_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_135..138: Cleanup & configure LEM */ out_be64(p->regs + PHB_LEM_FIR_ACCUM, 0x0000000000000000); out_be64(p->regs + PHB_LEM_ACTION0, 0xffffffffffffffff); out_be64(p->regs + PHB_LEM_ACTION1, 0x0000000000000000); out_be64(p->regs + PHB_LEM_WOF, 0x0000000000000000); } /* p7ioc_phb_init - Initialize the PHB hardware * * This is currently only called at boot time. It will eventually * be called at runtime, for example in some cases of error recovery * after a PHB reset in which case we might need locks etc... */ int64_t p7ioc_phb_init(struct p7ioc_phb *p) { uint64_t val; PHBDBG(p, "Initializing PHB %d...\n", p->index); p->state = P7IOC_PHB_STATE_INITIALIZING; /* For some reason, the doc wants us to read the version * register, so let's do it. We shoud probably check that * the value makes sense... */ val = in_be64(p->regs_asb + PHB_VERSION); p->rev = ((val >> 16) & 0xffff) | (val & 0xffff); PHBDBG(p, "PHB version: %08x\n", p->rev); /* * Configure AIB operations * * This register maps upbound commands to AIB channels. * DMA Write=0, DMA Read=2, MMIO Load Response=1, * Interrupt Request=1, TCE Read=3. */ /* Init_1: AIB TX Channel Mapping */ out_be64(p->regs_asb + PHB_AIB_TX_CHAN_MAPPING, 0x0211300000000000); /* * This group of steps initializes the AIB RX credits for * the CI block’s port that is attached to this PHB. * * Channel 0 (Dkill): 32 command credits, 0 data credits * (effectively infinite command credits) * Channel 1 (DMA/TCE Read Responses): 32 command credits, 32 data * credits (effectively infinite * command and data credits) * Channel 2 (Interrupt Reissue/Return): 32 command, 0 data credits * (effectively infinite * command credits) * Channel 3 (MMIO Load/Stores, EOIs): 1 command, 1 data credit */ /* Init_2: AIB RX Command Credit */ out_be64(p->regs_asb + PHB_AIB_RX_CMD_CRED, 0x0020002000200001); /* Init_3: AIB RX Data Credit */ out_be64(p->regs_asb + PHB_AIB_RX_DATA_CRED, 0x0000002000000001); /* Init_4: AXIB RX Credit Init Timer */ out_be64(p->regs_asb + PHB_AIB_RX_CRED_INIT_TIMER, 0xFF00000000000000); /* * Enable all 32 AIB and TCE tags. * * AIB tags are used for DMA read requests. * TCE tags are used for every internal transaction as well as TCE * read requests. */ /* Init_5: PHB - AIB Tag Enable Register */ out_be64(p->regs_asb + PHB_AIB_TAG_ENABLE, 0xFFFFFFFF00000000); /* Init_6: PHB – TCE Tag Enable Register */ out_be64(p->regs_asb + PHB_TCE_TAG_ENABLE, 0xFFFFFFFF00000000); /* Init_7: PCIE - System Configuration Register * * This is the default value out of reset. This register can be * modified to change the following fields if needed: * * bits 04:09 - SYS_EC0C_MAXLINKWIDTH[5:0] * The default link width is x8. This can be reduced * to x1 or x4, if needed. * * bits 10:12 - SYS_EC04_MAX_PAYLOAD[2:0] * * The default max payload size is 4KB. This can be * reduced to the allowed ranges from 128B * to 2KB if needed. */ out_be64(p->regs + PHB_PCIE_SYSTEM_CONFIG, 0x422800FC20000000); /* Init_8: PHB - PCI-E Reset Register * * This will deassert reset for the PCI-E cores, including the * PHY and HSS macros. The TLDLP core will begin link training * shortly after this register is written. * This will also assert reset for the internal scan-only error * report macros. The error report macro reset will be deasserted * in a later step. * Firmware will verify in a later step whether the PCI-E link * has been established. * * NOTE: We perform a PERST at the end of the init sequence so * we could probably skip that link training. */ out_be64(p->regs + PHB_RESET, 0xE800000000000000); /* Init_9: BUID * * Only the top 5 bit of the MSI field are implemented, the bottom * are always 0. Our buid_msi value should also be a multiple of * 16 so it should all fit well */ val = SETFIELD(PHB_BUID_LSI, 0ul, P7_BUID_BASE(p->buid_lsi)); val |= SETFIELD(PHB_BUID_MSI, 0ul, P7_BUID_BASE(p->buid_msi)); out_be64(p->regs + PHB_BUID, val); /* Init_10..12: IO Space */ out_be64(p->regs + PHB_IO_BASE_ADDR, p->io_base); out_be64(p->regs + PHB_IO_BASE_MASK, ~(PHB_IO_SIZE - 1)); out_be64(p->regs + PHB_IO_START_ADDR, 0); /* Init_13..15: M32 Space */ out_be64(p->regs + PHB_M32_BASE_ADDR, p->m32_base + M32_PCI_START); out_be64(p->regs + PHB_M32_BASE_MASK, ~(M32_PCI_SIZE - 1)); out_be64(p->regs + PHB_M32_START_ADDR, M32_PCI_START); /* Init_16: PCIE-E Outbound Request Upper Address */ out_be64(p->regs + PHB_M64_UPPER_BITS, 0); /* Init_17: PCIE-E PHB2 Configuration * * We enable IO, M32, 32-bit MSI and 64-bit MSI */ out_be64(p->regs + PHB_PHB2_CONFIG, PHB_PHB2C_32BIT_MSI_EN | PHB_PHB2C_IO_EN | PHB_PHB2C_64BIT_MSI_EN | PHB_PHB2C_M32_EN | PHB_PHB2C_64B_TCE_EN); /* Init_18..xx: Reset all IODA tables */ p7ioc_ioda_reset(&p->phb, false); /* Init_42..47: Clear UTL & DLP error log regs */ out_be64(p->regs + PHB_PCIE_UTL_ERRLOG1, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG2, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG3, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG4, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_DLP_ERRLOG1, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_DLP_ERRLOG2, 0xffffffffffffffff); /* Init_48: Wait for DLP core to be out of reset */ if (!p7ioc_phb_wait_dlp_reset(p)) goto failed; /* Init_49 - Clear port status */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xffffffffffffffff); /* Init_50..81: Init root complex config space */ if (!p7ioc_phb_init_rc_cfg(p)) goto failed; /* Init_82..94 : Init UTL */ p7ioc_phb_init_utl(p); /* Init_95: PCI-E Reset, deassert reset for internal error macros */ out_be64(p->regs + PHB_RESET, 0xe000000000000000); /* Init_96: PHB Control register. Various PHB settings: * * - Enable ECC for various internal RAMs * - Enable all TCAM entries * - Set failed DMA read requests to return Completer Abort on error */ out_be64(p->regs + PHB_CONTROL, 0x7f38000000000000); /* Init_97: Legacy Control register * * The spec sets bit 0 to enable DKill to flush the TCEs. We do not * use that mechanism however, we require the OS to directly access * the TCE Kill register, so we leave that bit set to 0 */ out_be64(p->regs + PHB_LEGACY_CTRL, 0x0000000000000000); /* Init_98..138 : Setup error registers */ p7ioc_phb_init_errors(p); /* Init_139: Read error summary */ val = in_be64(p->regs + PHB_ETU_ERR_SUMMARY); if (val) { PHBERR(p, "Errors detected during PHB init: 0x%16llx\n", val); goto failed; } /* Steps Init_140..142 have been removed from the spec. */ /* Init_143..144: Enable IO, MMIO, Bus master etc... and clear * status bits */ p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_STAT, PCI_CFG_STAT_SENT_TABORT | PCI_CFG_STAT_RECV_TABORT | PCI_CFG_STAT_RECV_MABORT | PCI_CFG_STAT_SENT_SERR | PCI_CFG_STAT_RECV_PERR); p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_CMD, PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_BUS_MASTER_EN | PCI_CFG_CMD_MEM_EN | PCI_CFG_CMD_IO_EN); /* At this point, the spec suggests doing a bus walk. However we * haven't powered up the slots with the SHCP controller. We'll * deal with that and link training issues later, for now, let's * enable the full range of error detection */ /* Init_145..149: Enable error interrupts and LEM */ out_be64(p->regs + PHB_ERR_IRQ_ENABLE, 0x0000000061c00000); out_be64(p->regs + PHB_OUT_ERR_IRQ_ENABLE, 0x0000430803000000); out_be64(p->regs + PHB_INA_ERR_IRQ_ENABLE, 0xc00003ff01006000); out_be64(p->regs + PHB_INB_ERR_IRQ_ENABLE, 0x0000000000000000); out_be64(p->regs + PHB_LEM_ERROR_MASK, 0x1249a1147f500f2c); /* Init_150: Enable DMA read/write TLP address speculation */ out_be64(p->regs + PHB_TCE_PREFETCH, 0x0000c00000000000); /* Init_151..152: Set various timeouts */ out_be64(p->regs + PHB_TIMEOUT_CTRL1, 0x1611112010200000); out_be64(p->regs + PHB_TIMEOUT_CTRL2, 0x0000561300000000); /* Mark the PHB as functional which enables all the various sequences */ p->state = P7IOC_PHB_STATE_FUNCTIONAL; return OPAL_SUCCESS; failed: PHBERR(p, "Initialization failed\n"); p->state = P7IOC_PHB_STATE_BROKEN; return OPAL_HARDWARE; } void p7ioc_phb_reset(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct p7ioc *ioc = p->ioc; uint64_t ci_idx, rreg; unsigned int i; bool fenced; /* Check our fence status. The fence bits we care about are * two bits per PHB at IBM bit location 14 and 15 + 4*phb */ fenced = p7ioc_phb_fenced(p); PHBDBG(p, "PHB reset... (fenced: %d)\n", (int)fenced); /* * If not fenced and already functional, let's do an IODA reset * to clear pending DMAs and wait a bit for thing to settle. It's * notable that the IODA table cache won't be emptied so that we * can restore them during error recovery. */ if (p->state == P7IOC_PHB_STATE_FUNCTIONAL && !fenced) { PHBDBG(p, " ioda reset ...\n"); p7ioc_ioda_reset(&p->phb, false); time_wait_ms(100); } /* CI port index */ ci_idx = p->index + 2; /* Reset register bits for this PHB */ rreg = 0;/*PPC_BIT(8 + ci_idx * 2);*/ /* CI port config reset */ rreg |= PPC_BIT(9 + ci_idx * 2); /* CI port func reset */ rreg |= PPC_BIT(32 + p->index); /* PHBn config reset */ /* Mask various errors during reset and clear pending errors */ out_be64(ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx), 0xa4f4000000000000ul); out_be64(p->regs_asb + PHB_LEM_ERROR_MASK, 0xadb650c9808dd051ul); out_be64(ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx), 0); /* We need to retry in case the fence doesn't lift due to a * problem with lost credits (HW guys). How many times ? */ #define MAX_PHB_RESET_RETRIES 5 for (i = 0; i < MAX_PHB_RESET_RETRIES; i++) { PHBDBG(p, " reset try %d...\n", i); /* Apply reset */ out_be64(ioc->regs + P7IOC_CCRR, rreg); time_wait_ms(1); out_be64(ioc->regs + P7IOC_CCRR, 0); /* Check if fence lifed */ fenced = p7ioc_phb_fenced(p); PHBDBG(p, " fenced: %d...\n", (int)fenced); if (!fenced) break; } /* Reset failed, not much to do, maybe add an error return */ if (fenced) { PHBERR(p, "Reset failed, fence still set !\n"); p->state = P7IOC_PHB_STATE_BROKEN; return; } /* Wait a bit */ time_wait_ms(100); /* Re-initialize the PHB */ p7ioc_phb_init(p); /* Restore the CI error mask */ out_be64(ioc->regs + P7IOC_CIn_LEM_ERR_MASK_AND(ci_idx), 0); } skiboot-skiboot-5.1.13/hw/p7ioc.c000066400000000000000000000467431265204436200165500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include /* * Determine the base address of LEM registers according to * the indicated error source. */ static void *p7ioc_LEM_base(struct p7ioc *ioc, uint32_t err_src) { uint32_t index; void *base = NULL; switch (err_src) { case P7IOC_ERR_SRC_RGC: base = ioc->regs + P7IOC_RGC_LEM_BASE; break; case P7IOC_ERR_SRC_BI_UP: base = ioc->regs + P7IOC_BI_UP_LEM_BASE; break; case P7IOC_ERR_SRC_BI_DOWN: base = ioc->regs + P7IOC_BI_DOWN_LEM_BASE; break; case P7IOC_ERR_SRC_CI_P0: case P7IOC_ERR_SRC_CI_P1: case P7IOC_ERR_SRC_CI_P2: case P7IOC_ERR_SRC_CI_P3: case P7IOC_ERR_SRC_CI_P4: case P7IOC_ERR_SRC_CI_P5: case P7IOC_ERR_SRC_CI_P6: case P7IOC_ERR_SRC_CI_P7: index = err_src - P7IOC_ERR_SRC_CI_P0; base = ioc->regs + P7IOC_CI_PORTn_LEM_BASE(index); break; case P7IOC_ERR_SRC_PHB0: case P7IOC_ERR_SRC_PHB1: case P7IOC_ERR_SRC_PHB2: case P7IOC_ERR_SRC_PHB3: case P7IOC_ERR_SRC_PHB4: case P7IOC_ERR_SRC_PHB5: index = err_src - P7IOC_ERR_SRC_PHB0; base = ioc->regs + P7IOC_PHBn_LEM_BASE(index); break; case P7IOC_ERR_SRC_MISC: base = ioc->regs + P7IOC_MISC_LEM_BASE; break; case P7IOC_ERR_SRC_I2C: base = ioc->regs + P7IOC_I2C_LEM_BASE; break; default: prerror("%s: Unknown error source %d\n", __func__, err_src); } return base; } static void p7ioc_get_diag_common(struct p7ioc *ioc, void *base, struct OpalIoP7IOCErrorData *data) { /* GEM */ data->gemXfir = in_be64(ioc->regs + P7IOC_GEM_XFIR); data->gemRfir = in_be64(ioc->regs + P7IOC_GEM_RFIR); data->gemRirqfir = in_be64(ioc->regs + P7IOC_GEM_RIRQFIR); data->gemMask = in_be64(ioc->regs + P7IOC_GEM_MASK); data->gemRwof = in_be64(ioc->regs + P7IOC_GEM_RWOF); /* LEM */ data->lemFir = in_be64(base + P7IOC_LEM_FIR_OFFSET); data->lemErrMask = in_be64(base + P7IOC_LEM_ERR_MASK_OFFSET); data->lemAction0 = in_be64(base + P7IOC_LEM_ACTION_0_OFFSET); data->lemAction1 = in_be64(base + P7IOC_LEM_ACTION_1_OFFSET); data->lemWof = in_be64(base + P7IOC_LEM_WOF_OFFSET); } static int64_t p7ioc_get_diag_data(struct io_hub *hub, void *diag_buffer, uint64_t diag_buffer_len) { struct p7ioc *ioc = iohub_to_p7ioc(hub); struct OpalIoP7IOCErrorData *data = diag_buffer; void *base; /* Make sure we have enough buffer */ if (diag_buffer_len < sizeof(struct OpalIoP7IOCErrorData)) return OPAL_PARAMETER; /* We need do nothing if there're no pending errors */ if (!p7ioc_err_pending(ioc)) return OPAL_CLOSED; /* * We needn't collect diag-data for CI Port{2, ..., 7} * and PHB{0, ..., 5} since their errors (except GXE) * have been cached to the specific PHB. */ base = p7ioc_LEM_base(ioc, ioc->err.err_src); if (!base) { p7ioc_set_err_pending(ioc, false); return OPAL_INTERNAL_ERROR; } switch (ioc->err.err_src) { case P7IOC_ERR_SRC_RGC: data->type = OPAL_P7IOC_DIAG_TYPE_RGC; p7ioc_get_diag_common(ioc, base, data); data->rgc.rgcStatus = in_be64(ioc->regs + 0x3E1C10); data->rgc.rgcLdcp = in_be64(ioc->regs + 0x3E1C18); break; case P7IOC_ERR_SRC_BI_UP: data->type = OPAL_P7IOC_DIAG_TYPE_BI; data->bi.biDownbound = 0; p7ioc_get_diag_common(ioc, base, data); data->bi.biLdcp0 = in_be64(ioc->regs + 0x3C0100); data->bi.biLdcp1 = in_be64(ioc->regs + 0x3C0108); data->bi.biLdcp2 = in_be64(ioc->regs + 0x3C0110); data->bi.biFenceStatus = in_be64(ioc->regs + 0x3C0130); break; case P7IOC_ERR_SRC_BI_DOWN: data->type = OPAL_P7IOC_DIAG_TYPE_BI; data->bi.biDownbound = 1; p7ioc_get_diag_common(ioc, base, data); data->bi.biLdcp0 = in_be64(ioc->regs + 0x3C0118); data->bi.biLdcp1 = in_be64(ioc->regs + 0x3C0120); data->bi.biLdcp2 = in_be64(ioc->regs + 0x3C0128); data->bi.biFenceStatus = in_be64(ioc->regs + 0x3C0130); break; case P7IOC_ERR_SRC_CI_P0: case P7IOC_ERR_SRC_CI_P1: data->type = OPAL_P7IOC_DIAG_TYPE_CI; data->ci.ciPort = ioc->err.err_src - P7IOC_ERR_SRC_CI_P0; p7ioc_get_diag_common(ioc, base, data); data->ci.ciPortStatus = in_be64(base + 0x008); data->ci.ciPortLdcp = in_be64(base + 0x010); break; case P7IOC_ERR_SRC_MISC: data->type = OPAL_P7IOC_DIAG_TYPE_MISC; p7ioc_get_diag_common(ioc, base, data); break; case P7IOC_ERR_SRC_I2C: data->type = OPAL_P7IOC_DIAG_TYPE_I2C; p7ioc_get_diag_common(ioc, base, data); break; default: p7ioc_set_err_pending(ioc, false); return OPAL_CLOSED; } /* For errors of MAL class, we need mask it */ if (ioc->err.err_class == P7IOC_ERR_CLASS_MAL) out_be64(base + P7IOC_LEM_ERR_MASK_OR_OFFSET, PPC_BIT(63 - ioc->err.err_bit)); p7ioc_set_err_pending(ioc, false); return OPAL_SUCCESS; } static const struct io_hub_ops p7ioc_hub_ops = { .set_tce_mem = NULL, /* No set_tce_mem for p7ioc, we use FMTC */ .get_diag_data = p7ioc_get_diag_data, .reset = p7ioc_reset, }; static int64_t p7ioc_rgc_get_xive(void *data, uint32_t isn, uint16_t *server, uint8_t *prio) { struct p7ioc *ioc = data; uint32_t irq = (isn & 0xf); uint32_t fbuid = P7_IRQ_FBUID(isn); uint64_t xive; if (fbuid != ioc->rgc_buid) return OPAL_PARAMETER; xive = ioc->xive_cache[irq]; *server = GETFIELD(IODA_XIVT_SERVER, xive); *prio = GETFIELD(IODA_XIVT_PRIORITY, xive); return OPAL_SUCCESS; } static int64_t p7ioc_rgc_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t prio) { struct p7ioc *ioc = data; uint32_t irq = (isn & 0xf); uint32_t fbuid = P7_IRQ_FBUID(isn); uint64_t xive; uint64_t m_server, m_prio; if (fbuid != ioc->rgc_buid) return OPAL_PARAMETER; xive = SETFIELD(IODA_XIVT_SERVER, 0ull, server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, prio); ioc->xive_cache[irq] = xive; /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } /* Update the XIVE. Don't care HRT entry on P7IOC */ out_be64(ioc->regs + 0x3e1820, (0x0002000000000000 | irq)); xive = in_be64(ioc->regs + 0x3e1830); xive = SETFIELD(IODA_XIVT_SERVER, xive, m_server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, m_prio); out_be64(ioc->regs + 0x3e1830, xive); return OPAL_SUCCESS; } /* * The function is used to figure out the error class and error * bit according to LEM WOF. * * The bits of WOF register have been classified according to * the error severity. Of course, we should process those errors * with higher priority. For example, there have 2 errors (GXE, INF) * pending, we should process GXE, and INF is meaningless in face * of GXE. */ static bool p7ioc_err_bit(struct p7ioc *ioc, uint64_t wof) { uint64_t val, severity[P7IOC_ERR_CLASS_LAST]; int32_t class, bit, err_bit = -1; /* Clear severity array */ memset(severity, 0, sizeof(uint64_t) * P7IOC_ERR_CLASS_LAST); /* * The severity array has fixed values. However, it depends * on the damage settings for individual components. We're * using fixed values based on the assumption that damage settings * are fixed for now. If we change it some day, we also need * change the severity array accordingly. Anyway, it's something * to improve in future so that we can figure out the severity * array from hardware registers. */ switch (ioc->err.err_src) { case P7IOC_ERR_SRC_EI: /* EI won't create interrupt yet */ break; case P7IOC_ERR_SRC_RGC: severity[P7IOC_ERR_CLASS_GXE] = 0xF00086E0F4FCFFFF; severity[P7IOC_ERR_CLASS_RGA] = 0x0000010000000000; severity[P7IOC_ERR_CLASS_INF] = 0x0FFF781F0B030000; break; case P7IOC_ERR_SRC_BI_UP: severity[P7IOC_ERR_CLASS_GXE] = 0xF7FFFFFF7FFFFFFF; severity[P7IOC_ERR_CLASS_INF] = 0x0800000080000000; break; case P7IOC_ERR_SRC_BI_DOWN: severity[P7IOC_ERR_CLASS_GXE] = 0xDFFFF7F35F8000BF; severity[P7IOC_ERR_CLASS_INF] = 0x2000080CA07FFF40; break; case P7IOC_ERR_SRC_CI_P0: severity[P7IOC_ERR_CLASS_GXE] = 0xF5FF000000000000; severity[P7IOC_ERR_CLASS_INF] = 0x0200FFFFFFFFFFFF; severity[P7IOC_ERR_CLASS_MAL] = 0x0800000000000000; break; case P7IOC_ERR_SRC_CI_P1: severity[P7IOC_ERR_CLASS_GXE] = 0xFFFF000000000000; severity[P7IOC_ERR_CLASS_INF] = 0x0000FFFFFFFFFFFF; break; case P7IOC_ERR_SRC_CI_P2: case P7IOC_ERR_SRC_CI_P3: case P7IOC_ERR_SRC_CI_P4: case P7IOC_ERR_SRC_CI_P5: case P7IOC_ERR_SRC_CI_P6: case P7IOC_ERR_SRC_CI_P7: severity[P7IOC_ERR_CLASS_GXE] = 0x5B0B000000000000; severity[P7IOC_ERR_CLASS_PHB] = 0xA4F4000000000000; severity[P7IOC_ERR_CLASS_INF] = 0x0000FFFFFFFFFFFF; break; case P7IOC_ERR_SRC_MISC: severity[P7IOC_ERR_CLASS_GXE] = 0x0000000310000000; severity[P7IOC_ERR_CLASS_PLL] = 0x0000000001C00000; severity[P7IOC_ERR_CLASS_INF] = 0x555FFFF0EE3FFFFF; severity[P7IOC_ERR_CLASS_MAL] = 0xAAA0000C00000000; break; case P7IOC_ERR_SRC_I2C: severity[P7IOC_ERR_CLASS_GXE] = 0x1100000000000000; severity[P7IOC_ERR_CLASS_INF] = 0xEEFFFFFFFFFFFFFF; break; case P7IOC_ERR_SRC_PHB0: case P7IOC_ERR_SRC_PHB1: case P7IOC_ERR_SRC_PHB2: case P7IOC_ERR_SRC_PHB3: case P7IOC_ERR_SRC_PHB4: case P7IOC_ERR_SRC_PHB5: severity[P7IOC_ERR_CLASS_PHB] = 0xADB650CB808DD051; severity[P7IOC_ERR_CLASS_ER] = 0x0000A0147F50092C; severity[P7IOC_ERR_CLASS_INF] = 0x52490F2000222682; break; } /* * The error class (ERR_CLASS) has been defined based on * their severity. The priority of those errors out of same * class should be defined based on the position of corresponding * bit in LEM (Local Error Macro) register. */ for (class = P7IOC_ERR_CLASS_NONE + 1; err_bit < 0 && class < P7IOC_ERR_CLASS_LAST; class++) { val = wof & severity[class]; if (!val) continue; for (bit = 0; bit < 64; bit++) { if (val & PPC_BIT(bit)) { err_bit = 63 - bit; break; } } } /* If we don't find the error bit, we needn't go on. */ if (err_bit < 0) return false; ioc->err.err_class = class - 1; ioc->err.err_bit = err_bit; return true; } /* * Check LEM to determine the detailed error information. * The function is expected to be called while OS calls * to OPAL API opal_pci_next_error(). Eventually, the errors * from CI Port{2, ..., 7} or PHB{0, ..., 5} would be cached * to the specific PHB, the left errors would be cached to * the IOC. */ bool p7ioc_check_LEM(struct p7ioc *ioc, uint16_t *pci_error_type, uint16_t *severity) { void *base; uint64_t fir, wof, mask; struct p7ioc_phb *p; int32_t index; bool ret; /* Make sure we have error pending on IOC */ if (!p7ioc_err_pending(ioc)) return false; /* * The IOC probably has been put to fatal error * state (GXE) because of failure on reading on * GEM FIR. */ if (ioc->err.err_src == P7IOC_ERR_SRC_NONE && ioc->err.err_class != P7IOC_ERR_CLASS_NONE) goto err; /* * Get the base address of LEM registers according * to the error source. If we failed to get that, * the error pending flag would be cleared. */ base = p7ioc_LEM_base(ioc, ioc->err.err_src); if (!base) { p7ioc_set_err_pending(ioc, false); return false; } /* IOC would be broken upon broken FIR */ fir = in_be64(base + P7IOC_LEM_FIR_OFFSET); if (fir == 0xffffffffffffffff) { ioc->err.err_src = P7IOC_ERR_SRC_NONE; ioc->err.err_class = P7IOC_ERR_CLASS_GXE; goto err; } /* Read on ERR_MASK and WOF. However, we needn't do for PHBn */ wof = in_be64(base + P7IOC_LEM_WOF_OFFSET); if (ioc->err.err_src >= P7IOC_ERR_SRC_PHB0 && ioc->err.err_src <= P7IOC_ERR_SRC_PHB5) { mask = 0x0ull; } else { mask = in_be64(base + P7IOC_LEM_ERR_MASK_OFFSET); in_be64(base + P7IOC_LEM_ACTION_0_OFFSET); in_be64(base + P7IOC_LEM_ACTION_1_OFFSET); } /* * We need process those unmasked error first. If we're * failing to get the error bit, we needn't proceed. */ if (wof & ~mask) wof &= ~mask; if (!wof) { p7ioc_set_err_pending(ioc, false); return false; } if (!p7ioc_err_bit(ioc, wof)) { p7ioc_set_err_pending(ioc, false); return false; } err: /* * We run into here because of valid error. Those errors * from CI Port{2, ..., 7} and PHB{0, ..., 5} will be cached * to the specific PHB. However, we will cache the global * errors (e.g. GXE) to IOC directly. For the left errors, * they will be cached to IOC. */ if (((ioc->err.err_src >= P7IOC_ERR_SRC_CI_P2 && ioc->err.err_src <= P7IOC_ERR_SRC_CI_P7) || (ioc->err.err_src >= P7IOC_ERR_SRC_PHB0 && ioc->err.err_src <= P7IOC_ERR_SRC_PHB5)) && ioc->err.err_class != P7IOC_ERR_CLASS_GXE) { index = (ioc->err.err_src >= P7IOC_ERR_SRC_PHB0 && ioc->err.err_src <= P7IOC_ERR_SRC_PHB5) ? (ioc->err.err_src - P7IOC_ERR_SRC_PHB0) : (ioc->err.err_src - P7IOC_ERR_SRC_CI_P2); p = &ioc->phbs[index]; if (p7ioc_phb_enabled(ioc, index)) { p->err.err_src = ioc->err.err_src; p->err.err_class = ioc->err.err_class; p->err.err_bit = ioc->err.err_bit; p7ioc_phb_set_err_pending(p, true); p7ioc_set_err_pending(ioc, false); return false; } } /* * Map the internal error class to that OS can recognize. * Errors from PHB or the associated CI port would be * GXE, PHB-fatal, ER, or INF. For the case, GXE will be * cached to IOC and the left classes will be cached to * the specific PHB. */ switch (ioc->err.err_class) { case P7IOC_ERR_CLASS_GXE: case P7IOC_ERR_CLASS_PLL: case P7IOC_ERR_CLASS_RGA: *pci_error_type = OPAL_EEH_IOC_ERROR; *severity = OPAL_EEH_SEV_IOC_DEAD; ret = true; break; case P7IOC_ERR_CLASS_INF: case P7IOC_ERR_CLASS_MAL: *pci_error_type = OPAL_EEH_IOC_ERROR; *severity = OPAL_EEH_SEV_INF; ret = false; break; default: p7ioc_set_err_pending(ioc, false); ret = false; } return ret; } /* * Check GEM to see if there has any problematic components. * The function is expected to be called in RGC interrupt * handler. Also, it's notable that failure on reading on * XFIR will cause GXE directly. */ static bool p7ioc_check_GEM(struct p7ioc *ioc) { uint64_t xfir, rwof; /* * Recov_5: Read GEM Xfir * Recov_6: go to GXE recovery? */ xfir = in_be64(ioc->regs + P7IOC_GEM_XFIR); if (xfir == 0xffffffffffffffff) { ioc->err.err_src = P7IOC_ERR_SRC_NONE; ioc->err.err_class = P7IOC_ERR_CLASS_GXE; p7ioc_set_err_pending(ioc, true); return true; } /* * Recov_7: Read GEM Rfir * Recov_8: Read GEM RIRQfir * Recov_9: Read GEM RWOF * Recov_10: Read Fence Shadow * Recov_11: Read Fence Shadow WOF */ in_be64(ioc->regs + P7IOC_GEM_RFIR); in_be64(ioc->regs + P7IOC_GEM_RIRQFIR); rwof = in_be64(ioc->regs + P7IOC_GEM_RWOF); in_be64(ioc->regs + P7IOC_CHIP_FENCE_SHADOW); in_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF); /* * Check GEM RWOF to see which component has been * put into problematic state. */ ioc->err.err_src = P7IOC_ERR_SRC_NONE; if (rwof & PPC_BIT(1)) ioc->err.err_src = P7IOC_ERR_SRC_RGC; else if (rwof & PPC_BIT(2)) ioc->err.err_src = P7IOC_ERR_SRC_BI_UP; else if (rwof & PPC_BIT(3)) ioc->err.err_src = P7IOC_ERR_SRC_BI_DOWN; else if (rwof & PPC_BIT(4)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P0; else if (rwof & PPC_BIT(5)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P1; else if (rwof & PPC_BIT(6)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P2; else if (rwof & PPC_BIT(7)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P3; else if (rwof & PPC_BIT(8)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P4; else if (rwof & PPC_BIT(9)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P5; else if (rwof & PPC_BIT(10)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P6; else if (rwof & PPC_BIT(11)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P7; else if (rwof & PPC_BIT(16)) ioc->err.err_src = P7IOC_ERR_SRC_PHB0; else if (rwof & PPC_BIT(17)) ioc->err.err_src = P7IOC_ERR_SRC_PHB1; else if (rwof & PPC_BIT(18)) ioc->err.err_src = P7IOC_ERR_SRC_PHB2; else if (rwof & PPC_BIT(19)) ioc->err.err_src = P7IOC_ERR_SRC_PHB3; else if (rwof & PPC_BIT(20)) ioc->err.err_src = P7IOC_ERR_SRC_PHB4; else if (rwof & PPC_BIT(21)) ioc->err.err_src = P7IOC_ERR_SRC_PHB5; else if (rwof & PPC_BIT(24)) ioc->err.err_src = P7IOC_ERR_SRC_MISC; else if (rwof & PPC_BIT(25)) ioc->err.err_src = P7IOC_ERR_SRC_I2C; /* * If we detect any problematic components, the OS is * expected to poll that for more details through OPAL * interface. */ if (ioc->err.err_src != P7IOC_ERR_SRC_NONE) { p7ioc_set_err_pending(ioc, true); return true; } return false; } static void p7ioc_rgc_interrupt(void *data, uint32_t isn) { struct p7ioc *ioc = data; printf("Got RGC interrupt 0x%04x\n", isn); /* We will notify OS while getting error from GEM */ if (p7ioc_check_GEM(ioc)) /* This is a bit hacky but works - we raise the event on a downstream phb as the OS needs to call opal_pci_next_error for all phbs to ensure all events are cleared anyway. */ opal_pci_eeh_set_evt(ioc->phbs[0].phb.opal_id); } static const struct irq_source_ops p7ioc_rgc_irq_ops = { .get_xive = p7ioc_rgc_get_xive, .set_xive = p7ioc_rgc_set_xive, .interrupt = p7ioc_rgc_interrupt, }; static void p7ioc_create_hub(struct dt_node *np) { struct p7ioc *ioc; unsigned int i, id; u64 bar1, bar2; u32 pdt; char *path; /* Use the BUID extension as ID and add it to device-tree */ id = dt_prop_get_u32(np, "ibm,buid-ext"); path = dt_get_path(np); printf("P7IOC: Found at %s ID 0x%x\n", path, id); free(path); /* Load VPD LID */ vpd_preload(np); vpd_iohub_load(np); ioc = zalloc(sizeof(struct p7ioc)); if (!ioc) return; ioc->hub.hub_id = id; ioc->hub.ops = &p7ioc_hub_ops; ioc->dt_node = np; bar1 = dt_prop_get_u64(np, "ibm,gx-bar-1"); bar2 = dt_prop_get_u64(np, "ibm,gx-bar-2"); ioc->regs = (void *)bar1; ioc->mmio1_win_start = bar1; ioc->mmio1_win_size = MWIN1_SIZE; ioc->mmio2_win_start = bar2; ioc->mmio2_win_size = MWIN2_SIZE; ioc->buid_base = id << 9; ioc->rgc_buid = ioc->buid_base + RGC_BUID_OFFSET; /* Add some DT properties */ dt_add_property_cells(np, "ibm,opal-hubid", 0, id); /* XXX Fixme: how many RGC interrupts ? */ dt_add_property_cells(np, "interrupts", ioc->rgc_buid << 4); dt_add_property_cells(np, "interrupt-base", ioc->rgc_buid << 4); /* XXX What about ibm,opal-mmio-real ? */ /* Clear the RGC XIVE cache */ for (i = 0; i < 16; i++) ioc->xive_cache[i] = SETFIELD(IODA_XIVT_PRIORITY, 0ull, 0xff); /* * Register RGC interrupts * * For now I assume only 0 is... to verify with Greg or HW guys, * we support all 16 */ register_irq_source(&p7ioc_rgc_irq_ops, ioc, ioc->rgc_buid << 4, 1); /* Check for presence detect from HDAT, we use only BR1 on P7IOC */ pdt = dt_prop_get_u32_def(np, "ibm,br1-presence-detect", 0xffffffff); if (pdt != 0xffffffff) printf("P7IOC: Presence detect from HDAT : 0x%02x\n", pdt); else { } ioc->phb_pdt = pdt & 0xff; /* Setup PHB structures (no HW access yet) */ for (i = 0; i < P7IOC_NUM_PHBS; i++) { if (p7ioc_phb_enabled(ioc, i)) p7ioc_phb_setup(ioc, i); else ioc->phbs[i].state = P7IOC_PHB_STATE_OFF; } /* Now, we do the bulk of the inits */ p7ioc_inits(ioc); printf("P7IOC: Initialization complete\n"); cec_register(&ioc->hub); } void probe_p7ioc(void) { struct dt_node *np; dt_for_each_compatible(dt_root, np, "ibm,p7ioc") p7ioc_create_hub(np); } skiboot-skiboot-5.1.13/hw/p8-i2c.c000066400000000000000000001133571265204436200165250ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #undef DEBUG #include #include #include #include #include #include #include #include #include #include #include #include /* XXX SRC's will be moved to errorlog.h and then remove fsp-elog.h */ #include DEFINE_LOG_ENTRY(OPAL_RC_I2C_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_I2C, OPAL_IO_SUBSYSTEM, OPAL_PREDICTIVE_ERR_DEGRADED_PERF, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_I2C_START_REQ, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_I2C_TIMEOUT, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_I2C_TRANSFER, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_I2C_RESET, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA); #ifdef DEBUG #define DBG(fmt...) prlog(PR_ERR, "I2C: " fmt) #define I2C_TIMEOUT_IRQ_MS 100 /* 100ms/byte timeout */ #define I2C_TIMEOUT_POLL_MS 4000 /* 4s/byte timeout */ #else #define DBG(fmt...) prlog(PR_TRACE, "I2C: " fmt) #define I2C_TIMEOUT_IRQ_MS 1 /* 1ms/byte timeout */ #define I2C_TIMEOUT_POLL_MS 4000 /* 4s/byte timeout */ #endif /* How long to keep the sensor cache disabled after an access * in milliseconds */ #define SENSOR_CACHE_EN_DELAY 10 #define USEC_PER_SEC 1000000 #define USEC_PER_MSEC 1000 #define I2C_RESET_DELAY_MS 5 /* 5 msecs */ #define I2C_FIFO_HI_LVL 4 #define I2C_FIFO_LO_LVL 4 /* * I2C registers set. * Below is the offset of registers from base which is stored in the * 'struct p8_i2c_master' */ /* I2C FIFO register */ #define I2C_FIFO_REG 0x4 #define I2C_FIFO PPC_BITMASK(0, 7) /* I2C command register */ #define I2C_CMD_REG 0x5 #define I2C_CMD_WITH_START PPC_BIT(0) #define I2C_CMD_WITH_ADDR PPC_BIT(1) #define I2C_CMD_READ_CONT PPC_BIT(2) #define I2C_CMD_WITH_STOP PPC_BIT(3) #define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) #define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) #define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) #define I2C_MAX_TFR_LEN 0xfff0ull /* I2C mode register */ #define I2C_MODE_REG 0x6 #define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) #define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) #define I2C_MODE_ENHANCED PPC_BIT(28) #define I2C_MODE_DIAGNOSTIC PPC_BIT(29) #define I2C_MODE_PACING_ALLOW PPC_BIT(30) #define I2C_MODE_WRAP PPC_BIT(31) /* I2C watermark register */ #define I2C_WATERMARK_REG 0x7 #define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) #define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) /* I2C interrupt mask, condition and interrupt registers */ #define I2C_INTR_MASK_REG 0x8 #define I2C_INTR_COND_REG 0x9 #define I2C_INTR_REG 0xa #define I2C_INTR_ALL PPC_BITMASK(16, 31) #define I2C_INTR_INVALID_CMD PPC_BIT(16) #define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) #define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) #define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) #define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) #define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) #define I2C_INTR_DATA_REQ PPC_BIT(22) #define I2C_INTR_CMD_COMP PPC_BIT(23) #define I2C_INTR_STOP_ERR PPC_BIT(24) #define I2C_INTR_I2C_BUSY PPC_BIT(25) #define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) #define I2C_INTR_SCL_EQ_1 PPC_BIT(28) #define I2C_INTR_SCL_EQ_0 PPC_BIT(29) #define I2C_INTR_SDA_EQ_1 PPC_BIT(30) #define I2C_INTR_SDA_EQ_0 PPC_BIT(31) /* I2C status register */ #define I2C_RESET_I2C_REG 0xb #define I2C_RESET_ERRORS 0xc #define I2C_STAT_REG 0xb #define I2C_STAT_INVALID_CMD PPC_BIT(0) #define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) #define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) #define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) #define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) #define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) #define I2C_STAT_DATA_REQ PPC_BIT(6) #define I2C_STAT_CMD_COMP PPC_BIT(7) #define I2C_STAT_STOP_ERR PPC_BIT(8) #define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) #define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) #define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) #define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) #define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) #define I2C_STAT_PORT_BUSY PPC_BIT(22) #define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) #define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) #define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ I2C_STAT_BKEND_OVERRUN_ERR | \ I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) /* I2C extended status register */ #define I2C_EXTD_STAT_REG 0xc #define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) #define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) #define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) #define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) #define I2C_EXTD_STAT_S_SCL PPC_BIT(18) #define I2C_EXTD_STAT_S_SDA PPC_BIT(19) #define I2C_EXTD_STAT_M_SCL PPC_BIT(20) #define I2C_EXTD_STAT_M_SDA PPC_BIT(21) #define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) #define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) #define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) #define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) #define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) /* I2C residual front end/back end length */ #define I2C_RESIDUAL_LEN_REG 0xd #define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) #define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) /* Port busy register */ #define I2C_PORT_BUYS_REG 0xe enum p8_i2c_master_type { I2C_POWER8, I2C_CENTAUR, MAX_I2C_TYPE, }; struct p8_i2c_master { struct lock lock; /* Lock to guard the members */ enum p8_i2c_master_type type; /* P8 vs. Centaur */ uint64_t poll_interval; /* Polling interval */ uint64_t byte_timeout; /* Timeout per byte */ uint64_t xscom_base; /* xscom base of i2cm */ uint32_t fifo_size; /* Maximum size of FIFO */ uint32_t chip_id; /* Chip the i2cm sits on */ uint32_t engine_id; /* Engine# on chip */ uint8_t obuf[4]; /* Offset buffer */ uint32_t bytes_sent; bool irq_ok; /* Interrupt working ? */ bool occ_cache_dis; /* I have disabled the cache */ enum request_state { state_idle, state_occache_dis, state_offset, state_data, state_error, state_recovery, } state; struct list_head req_list; /* Request queue head */ struct timer poller; struct timer timeout; struct timer recovery; struct timer sensor_cache; uint8_t recovery_pass; struct list_node link; }; struct p8_i2c_master_port { struct i2c_bus bus; /* Abstract bus struct for the client */ struct p8_i2c_master *master; uint32_t port_num; uint32_t bit_rate_div; /* Divisor to set bus speed*/ }; struct p8_i2c_request { struct i2c_request req; uint32_t port_num; uint64_t timeout; }; static void p8_i2c_print_debug_info(struct p8_i2c_master_port *port, struct i2c_request *req) { struct p8_i2c_master *master = port->master; uint64_t cmd, mode, stat, estat, intr; int rc; /* Print master and request structure bits */ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Chip %08x Eng. %d Port %d--\n" " xscom_base=0x%016llx\tstate=%d\tbytes_sent=%d\n", master->chip_id, master->engine_id, port->port_num, master->xscom_base, master->state, master->bytes_sent); log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Request info--\n" " addr=0x%04x\toffset_bytes=%d\toffset=%d\tlen=%d\n", req->dev_addr, req->offset_bytes, req->offset, req->rw_len); /* Dump the current state of i2c registers */ rc = xscom_read(master->chip_id, master->xscom_base + I2C_CMD_REG, &cmd); if (rc) { prlog(PR_DEBUG, "I2C: Failed to read CMD_REG\n"); cmd = 0ull; } rc = xscom_read(master->chip_id, master->xscom_base + I2C_MODE_REG, &mode); if (rc) { prlog(PR_DEBUG, "I2C: Failed to read MODE_REG\n"); mode = 0ull; } rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG, &stat); if (rc) { prlog(PR_DEBUG, "I2C: Failed to read STAT_REG\n"); stat = 0ull; } rc = xscom_read(master->chip_id, master->xscom_base + I2C_EXTD_STAT_REG, &estat); if (rc) { prlog(PR_DEBUG, "I2C: Failed to read EXTD_STAT_REG\n"); estat = 0ull; } rc = xscom_read(master->chip_id, master->xscom_base + I2C_INTR_MASK_REG, &intr); if (rc) { prlog(PR_DEBUG, "I2C: Failed to read INTR_MASK_REG\n"); intr = 0ull; } log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Register dump--\n" "cmd:0x%016llx\tmode:0x%016llx\tstat:0x%016llx\n" "estat:0x%016llx\tintr:0x%016llx\n", cmd, mode, stat, estat, intr); } static bool p8_i2c_has_irqs(struct p8_i2c_master *master) { struct proc_chip *chip; /* Centaur I2C doesn't have interrupts */ if (master->type == I2C_CENTAUR) return false; chip = get_chip(master->chip_id); /* The i2c interrurpt was only added to Murano DD2.1 and Venice * DD2.0. When operating without interrupts, we need to bump the * timeouts as we rely solely on the polls from Linux which can * be up to 2s apart ! * * Also we don't have interrupts for the Centaur i2c. */ switch (chip->type) { case PROC_CHIP_P8_MURANO: return chip->ec_level >= 0x21; case PROC_CHIP_P8_VENICE: return chip->ec_level >= 0x20; case PROC_CHIP_P8_NAPLES: return true; default: return false; } } static int p8_i2c_enable_irqs(struct p8_i2c_master *master) { int rc; /* Enable the interrupts */ rc = xscom_write(master->chip_id, master->xscom_base + I2C_INTR_COND_REG, I2C_STAT_ANY_ERR >> 16 | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ); if (rc) prlog(PR_ERR, "I2C: Failed to enable the interrupts\n"); return rc; } static int p8_i2c_prog_watermark(struct p8_i2c_master *master) { uint64_t watermark; int rc; rc = xscom_read(master->chip_id, master->xscom_base + I2C_WATERMARK_REG, &watermark); if (rc) { prlog(PR_ERR, "I2C: Failed to read the WATERMARK_REG\n"); return rc; } /* Set the high/low watermark */ watermark = SETFIELD(I2C_WATERMARK_HIGH, watermark, I2C_FIFO_HI_LVL); watermark = SETFIELD(I2C_WATERMARK_LOW, watermark, I2C_FIFO_LO_LVL); rc = xscom_write(master->chip_id, master->xscom_base + I2C_WATERMARK_REG, watermark); if (rc) prlog(PR_ERR, "I2C: Failed to set high/low watermark level\n"); return rc; } static int p8_i2c_prog_mode(struct p8_i2c_master_port *port, bool enhanced_mode) { struct p8_i2c_master *master = port->master; struct i2c_request *req = list_top(&master->req_list, struct i2c_request, link); struct p8_i2c_request *request = container_of(req, struct p8_i2c_request, req); uint64_t mode, omode; int rc; rc = xscom_read(master->chip_id, master->xscom_base + I2C_MODE_REG, &mode); if (rc) { prlog(PR_ERR, "I2C: Failed to read the MODE_REG\n"); return rc; } omode = mode; mode = SETFIELD(I2C_MODE_PORT_NUM, mode, request->port_num); mode = SETFIELD(I2C_MODE_BIT_RATE_DIV, mode, port->bit_rate_div); if (enhanced_mode) mode |= I2C_MODE_ENHANCED; else mode &= ~I2C_MODE_ENHANCED; if (mode == omode) return 0; rc = xscom_write(master->chip_id, master->xscom_base + I2C_MODE_REG, mode); if (rc) prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n"); return rc; } static void p8_i2c_complete_request(struct p8_i2c_master *master, struct i2c_request *req, int ret) { /* We only complete the current top level request */ assert(req == list_top(&master->req_list, struct i2c_request, link)); cancel_timer_async(&master->timeout); list_del(&req->link); master->state = state_idle; req->result = ret; /* Schedule re-enabling of sensor cache */ if (master->occ_cache_dis) schedule_timer(&master->sensor_cache, msecs_to_tb(SENSOR_CACHE_EN_DELAY)); unlock(&master->lock); if (req->completion) req->completion(ret, req); /* req might have been freed at this point */ lock(&master->lock); } static int p8_i2c_engine_reset(struct p8_i2c_master_port *port) { struct p8_i2c_master *master = port->master; int rc; /* Reset the i2c engine */ rc = xscom_write(master->chip_id, master->xscom_base + I2C_RESET_I2C_REG, 0); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed " "to reset the i2c engine\n"); return rc; } /* Reprogram the watermark and mode */ rc = p8_i2c_prog_watermark(port->master); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed to" "program the WATERMARK_REG\n"); return rc; } rc = p8_i2c_prog_mode(port, false); if (rc) log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed to" "program the MODE_REG\n"); return rc; } static void p8_i2c_translate_error(struct i2c_request *req, uint64_t status) { /* Assuming there are not more than one type of error simultaneously */ if (status & I2C_STAT_NACK_RCVD_ERR) req->result = OPAL_I2C_NACK_RCVD; else if (status & I2C_STAT_INVALID_CMD) req->result = OPAL_I2C_INVALID_CMD; else if (status & I2C_STAT_LBUS_PARITY_ERR) req->result = OPAL_I2C_LBUS_PARITY; else if (status & I2C_STAT_BKEND_OVERRUN_ERR) req->result = OPAL_I2C_BKEND_OVERRUN; else if (status & I2C_STAT_BKEND_ACCESS_ERR) req->result = OPAL_I2C_BKEND_ACCESS; else if (status & I2C_STAT_ARBT_LOST_ERR) req->result = OPAL_I2C_ARBT_LOST; else if (status & I2C_STAT_STOP_ERR) req->result = OPAL_I2C_STOP_ERR; } static void p8_i2c_status_error(struct p8_i2c_master_port *port, struct i2c_request *req, uint64_t status) { struct p8_i2c_master *master = port->master; int rc; /* Display any error other than I2C_INTR_NACK_RCVD_ERR since * getting NACK's is normal if Linux is probing the bus */ if (!(status & I2C_STAT_NACK_RCVD_ERR)) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Transfer error occurred\n"); p8_i2c_print_debug_info(port, req); } p8_i2c_translate_error(req, status); rc = p8_i2c_engine_reset(port); if (rc) goto exit; if (status & (I2C_STAT_LBUS_PARITY_ERR | I2C_STAT_ARBT_LOST_ERR | I2C_STAT_STOP_ERR)) { /* * Don't bother issuing a STOP command for those errors * just get rid of the current request and start off with * the fresh one in the list */ p8_i2c_complete_request(master, req, req->result); } else { /* * Reset the bus by issuing a STOP command to slave. * * Reprogram the mode register with 'enhanced bit' set */ rc = p8_i2c_prog_mode(port, true); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: " "Failed to program the MODE_REG\n"); goto exit; } /* Enable the interrupt */ p8_i2c_enable_irqs(master); /* Send an immediate stop */ master->state = state_error; rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG, I2C_CMD_WITH_STOP); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: " "Failed to issue immediate STOP\n"); goto exit; } } return; exit: p8_i2c_complete_request(master, req, req->result); } static int p8_i2c_fifo_read(struct p8_i2c_master *master, uint8_t *buf, uint32_t count) { uint64_t fifo; uint32_t i; int rc = 0; for (i = 0; i < count; i++, buf++) { rc = xscom_read(master->chip_id, master->xscom_base + I2C_FIFO_REG, &fifo); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed to read the fifo\n"); break; } *buf = GETFIELD(I2C_FIFO, fifo); } return rc; } static int p8_i2c_fifo_write(struct p8_i2c_master *master, uint8_t *buf, uint32_t count) { uint64_t fifo; uint32_t i; int rc = 0; for (i = 0; i < count; i++, buf++) { fifo = SETFIELD(I2C_FIFO, 0ull, *buf); rc = xscom_write(master->chip_id, master->xscom_base + I2C_FIFO_REG, fifo); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed to write the fifo\n"); break; } } return rc; } static void p8_i2c_status_data_request(struct p8_i2c_master *master, struct i2c_request *req, uint64_t status) { uint32_t fifo_count, fifo_free, count; uint8_t *buf; int rc = 0; fifo_count = GETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, status); fifo_free = master->fifo_size - fifo_count; DBG("Data request, state=%d fifo_count=%d/%d bytes_sent=%d\n", master->state, fifo_count, master->fifo_size, master->bytes_sent); switch(master->state) { case state_offset: /* We assume the offset can always be written in one go */ if (fifo_free < req->offset_bytes) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Fifo too small for offset !\n"); rc = OPAL_HARDWARE; } else { rc = p8_i2c_fifo_write(master, master->obuf, req->offset_bytes); } /* For writes, transition to data phase now */ if (rc == 0 && req->op == SMBUS_WRITE) master->state = state_data; break; case state_data: /* Sanity check */ if (master->bytes_sent >= req->rw_len) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: " "Data req with no data to send sent=%d " "req=%d\n", master->bytes_sent, req->rw_len); rc = OPAL_HARDWARE; break; } /* Get next chunk */ buf = req->rw_buf + master->bytes_sent; count = req->rw_len - master->bytes_sent; /* Check direction */ if (req->op == I2C_READ || req->op == SMBUS_READ) { if (count > fifo_count) count = fifo_count; rc = p8_i2c_fifo_read(master, buf, count); } else { if (count > fifo_free) count = fifo_free; rc = p8_i2c_fifo_write(master, buf, count); } if (rc == 0) master->bytes_sent += count; break; default: log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Invalid " "state %d in data req !\n", master->state); rc = OPAL_WRONG_STATE; } if (rc) p8_i2c_complete_request(master, req, rc); else p8_i2c_enable_irqs(master); } static void p8_i2c_complete_offset(struct p8_i2c_master *master, struct i2c_request *req) { uint64_t cmd; int rc = 0; DBG("Completing offset phase\n"); /* If it's a write, we should only get here for empty * write commands */ if (req->op == SMBUS_WRITE && req->rw_len != 0) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Write " "completion in offset state !\n"); rc = OPAL_HARDWARE; goto complete; } /* Switch to data phase */ master->state = state_data; /* If it's not a read command, or there are no data to read, * then we complete the command */ if (req->op != SMBUS_READ || req->rw_len == 0) goto complete; /* Otherwise, let's start the data phase */ cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE; cmd = SETFIELD(I2C_CMD_DEV_ADDR, cmd, req->dev_addr); cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len); DBG("Command: %016llx, state: %d\n", cmd, master->state); /* Send command */ rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG, cmd); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed " "to write the CMD_REG\n"); goto complete; } /* Enable the interrupts */ p8_i2c_enable_irqs(master); return; complete: p8_i2c_complete_request(master, req, rc); } static void p8_i2c_status_cmd_completion(struct p8_i2c_master *master, struct i2c_request *req) { int rc; DBG("Command completion, state=%d bytes_sent=%d\n", master->state, master->bytes_sent); /* If we complete an offset, we probably need to transition * do a data read, check if that all makes sense */ if (master->state == state_offset) { p8_i2c_complete_offset(master, req); return; } /* If we are not already in error state, check if we have * completed our data transfer properly */ if (master->state != state_error && master->bytes_sent != req->rw_len) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Request " "complete with residual data req=%d done=%d\n", req->rw_len, master->bytes_sent); /* Should we error out here ? */ } rc = master->state == state_error ? req->result : OPAL_SUCCESS; p8_i2c_complete_request(master, req, rc); } static void p8_i2c_check_status(struct p8_i2c_master *master) { struct p8_i2c_master_port *port; struct i2c_request *req; uint64_t status; int rc; /* If we are idle, just return, we'll catch error conditions * when we next try to enqueue a request */ if (master->state == state_idle) return; /* Read status register */ rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG, &status); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed " "to read the STAT_REG\n"); return; } /* Nothing happened ? Go back */ if (!(status & (I2C_STAT_ANY_ERR | I2C_STAT_DATA_REQ | I2C_STAT_CMD_COMP))) return; DBG("Non-0 status: %016llx\n", status); /* Mask the interrupts for this engine */ rc = xscom_write(master->chip_id, master->xscom_base + I2C_INTR_REG, ~I2C_INTR_ALL); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed " "to disable the interrupts\n"); return; } /* No request ? That's not normal ! Bail out without re-enabling * the interrupt */ req = list_top(&master->req_list, struct i2c_request, link); if (req == NULL) { log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Interrupt with no request" ", status=0x%016llx\n", status); return; } /* Get port for current request */ port = container_of(req->bus, struct p8_i2c_master_port, bus); /* Handle the status in that order: errors, data requests and * command completion. */ if (status & I2C_STAT_ANY_ERR) p8_i2c_status_error(port, req, status); else if (status & I2C_STAT_DATA_REQ) p8_i2c_status_data_request(master, req, status); else if (status & I2C_STAT_CMD_COMP) p8_i2c_status_cmd_completion(master, req); } static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port) { struct p8_i2c_master *master = port->master; uint64_t status, estat; int rc; master->recovery_pass++; /* Read status register */ rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG, &status); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " "to read the STAT_REG\n"); return rc; } rc = xscom_read(master->chip_id, master->xscom_base + I2C_EXTD_STAT_REG, &estat); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " "to read the EXTD_STAT_REG\n"); return rc; } if (estat & (I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY)) { DBG("Initial estat busy ! %016llx\n", estat); /* Just a warning for now */ } /* Nothing happened ? Go back */ if (status & I2C_STAT_ANY_ERR) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: " "Initial error status 0x%016llx\n", status); if (master->recovery_pass > 1) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: " "Error stuck, aborting !!\n"); return OPAL_HARDWARE; } /* Mark state as "recovery" to block any other activity */ master->state = state_recovery; /* Reset the engine */ p8_i2c_engine_reset(port); /* Delay 5ms for bus to settle */ schedule_timer(&master->recovery, msecs_to_tb(5)); unlock(&master->lock); return OPAL_BUSY; } /* Still busy ? */ if (!(status & I2C_STAT_CMD_COMP)) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Initial " "command complete not set\n"); if (master->recovery_pass > 5) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: " "Command stuck, aborting !!\n"); return OPAL_HARDWARE; } master->state = state_recovery; /* Delay 5ms for bus to settle */ schedule_timer(&master->recovery, msecs_to_tb(5)); unlock(&master->lock); return OPAL_BUSY; } master->recovery_pass = 0; return 0; } static int p8_i2c_start_request(struct p8_i2c_master *master, struct i2c_request *req) { struct p8_i2c_master_port *port; struct p8_i2c_request *request = container_of(req, struct p8_i2c_request, req); uint64_t cmd, now; int64_t rc, tbytes; DBG("Starting req %d len=%d addr=%02x (offset=%x)\n", req->op, req->rw_len, req->dev_addr, req->offset); /* Get port */ port = container_of(req->bus, struct p8_i2c_master_port, bus); /* Check if we need to disable the OCC cache first */ if (master->type == I2C_CENTAUR && !master->occ_cache_dis) { DBG("Disabling OCC cache...\n"); rc = centaur_disable_sensor_cache(master->chip_id); if (rc < 0) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " "to disable the sensor cache\n"); return rc; } master->occ_cache_dis = true; /* Do we need to wait ? */ if (rc > 0) { DBG("Waiting %lld\n", rc); master->state = state_occache_dis; schedule_timer(&master->recovery, rc); return 0; } } /* Convert the offset if needed */ if (req->offset_bytes) { int i; for (i = 0; i < req->offset_bytes; i++) { uint8_t b; b = req->offset >> (8 * (req->offset_bytes - i - 1)); master->obuf[i] = b; } DBG("Offset %d bytes: %02x %02x %02x %02x\n", req->offset_bytes, master->obuf[0], master->obuf[1], master->obuf[2], master->obuf[3]); } /* Program mode register */ rc = p8_i2c_prog_mode(port, false); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " "to program the MODE_REG\n"); return rc; } /* Check status */ rc = p8_i2c_check_initial_status(port); if (rc != OPAL_BUSY) master->recovery_pass = 0; if (rc) return rc; /* Initialize bytes_sent */ master->bytes_sent = 0; /* Set up the command register */ cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR; cmd = SETFIELD(I2C_CMD_DEV_ADDR, cmd, req->dev_addr); switch (req->op) { case I2C_READ: cmd |= I2C_CMD_READ_NOT_WRITE; /* Fall through */ case I2C_WRITE: cmd |= I2C_CMD_WITH_STOP; cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len); master->state = state_data; break; case SMBUS_READ: cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->offset_bytes); master->state = state_offset; break; case SMBUS_WRITE: cmd |= I2C_CMD_WITH_STOP; cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len + req->offset_bytes); master->state = state_offset; break; default: return OPAL_PARAMETER; } DBG("Command: %016llx, state: %d\n", cmd, master->state); /* Send command */ rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG, cmd); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed " "to write the CMD_REG\n"); return rc; } /* Enable the interrupts */ p8_i2c_enable_irqs(master); /* Run a poll timer for boot cases or non-working interrupts * cases */ now = schedule_timer(&master->poller, master->poll_interval); /* Calculate and start timeout */ tbytes = req->rw_len + req->offset_bytes + 2; request->timeout = now + tbytes * master->byte_timeout; /* Start the timeout */ schedule_timer_at(&master->timeout, request->timeout); return OPAL_SUCCESS; } static void p8_i2c_check_work(struct p8_i2c_master *master) { struct i2c_request *req; int rc; while (master->state == state_idle && !list_empty(&master->req_list)) { req = list_top(&master->req_list, struct i2c_request, link); rc = p8_i2c_start_request(master, req); if (rc && rc != OPAL_BUSY) p8_i2c_complete_request(master, req, rc); } } static int p8_i2c_queue_request(struct i2c_request *req) { struct i2c_bus *bus = req->bus; struct p8_i2c_master_port *port = container_of(bus, struct p8_i2c_master_port, bus); struct p8_i2c_master *master = port->master; int rc = 0; /* Parameter check */ if (req->rw_len > I2C_MAX_TFR_LEN) { prlog(PR_ERR, "I2C: Too large transfer %d bytes\n", req->rw_len); return OPAL_PARAMETER; } if (req->offset_bytes > 4) { prlog(PR_ERR, "I2C: Invalid offset size %d\n", req->offset_bytes); return OPAL_PARAMETER; } lock(&master->lock); list_add_tail(&master->req_list, &req->link); p8_i2c_check_work(master); unlock(&master->lock); return rc; } static struct i2c_request *p8_i2c_alloc_request(struct i2c_bus *bus) { struct p8_i2c_master_port *port = container_of(bus, struct p8_i2c_master_port, bus); struct p8_i2c_request *request; request = zalloc(sizeof(*request)); if (!request) { prlog(PR_ERR, "I2C: Failed to allocate i2c request\n"); return NULL; } request->port_num = port->port_num; request->req.bus = bus; return &request->req; } static void p8_i2c_free_request(struct i2c_request *req) { struct p8_i2c_request *request = container_of(req, struct p8_i2c_request, req); free(request); } static inline uint32_t p8_i2c_get_bit_rate_divisor(uint32_t lb_freq, uint32_t bus_speed) { return (((lb_freq / bus_speed) - 1) / 4); } static inline uint64_t p8_i2c_get_poll_interval(uint32_t bus_speed) { uint64_t usec; /* Polling Interval = 8 * (1/bus_speed) * (1/10) -> convert to uSec */ usec = ((8 * USEC_PER_SEC) / (10 * bus_speed)); return usecs_to_tb(usec); } static void p8_i2c_timeout(struct timer *t __unused, void *data, uint64_t now) { struct p8_i2c_master_port *port; struct p8_i2c_master *master = data; struct p8_i2c_request *request; struct i2c_request *req; lock(&master->lock); /* This could be spurrious ... */ if (master->state == state_idle) { DBG("I2C: Timeout in idle state\n"); goto exit; } /* We might still be spurrious timer, we need to ensure that the * head request is indeed old enough to be the one timing out */ req = list_top(&master->req_list, struct i2c_request, link); if (req == NULL) { DBG("I2C: Timeout with no" " pending request state=%d\n", master->state); goto exit; } request = container_of(req, struct p8_i2c_request, req); if (tb_compare(now, request->timeout) == TB_ABEFOREB) { DBG("I2C: Timeout with request not expired\n"); goto exit; } port = container_of(req->bus, struct p8_i2c_master_port, bus); /* Allright, we have a request and it has timed out ... */ log_simple_error(&e_info(OPAL_RC_I2C_TIMEOUT), "I2C: Request timeout !\n"); p8_i2c_print_debug_info(port, req); /* Reset the engine */ p8_i2c_engine_reset(port); /* Should we send a stop ? For now just complete */ p8_i2c_complete_request(master, req, OPAL_I2C_TIMEOUT); exit: unlock(&master->lock); } static void p8_i2c_recover(struct timer *t __unused, void *data, uint64_t now __unused) { struct p8_i2c_master *master = data; lock(&master->lock); assert(master->state == state_recovery || master->state == state_occache_dis); master->state = state_idle; /* We may or may not still have work pending, re-enable the sensor cache * immediately if we don't (we just waited the recovery time so there is * little point waiting longer). */ if (master->occ_cache_dis && list_empty(&master->req_list)) { DBG("Re-enabling OCC cache after recovery\n"); centaur_enable_sensor_cache(master->chip_id); master->occ_cache_dis = false; } /* Re-check for new work */ p8_i2c_check_work(master); unlock(&master->lock); } static void p8_i2c_enable_scache(struct timer *t __unused, void *data, uint64_t now __unused) { struct p8_i2c_master *master = data; lock(&master->lock); /* Check if we are still idle */ if (master->state == state_idle && master->occ_cache_dis) { DBG("Re-enabling OCC cache\n"); centaur_enable_sensor_cache(master->chip_id); master->occ_cache_dis = false; } unlock(&master->lock); } static void p8_i2c_poll(struct timer *t __unused, void *data, uint64_t now) { struct p8_i2c_master *master = data; /* * This is called when the interrupt isn't functional or * generally from the opal pollers, so fast while booting * and slowly when Linux is up. */ /* Lockless fast bailout */ if (master->state == state_idle) return; lock(&master->lock); p8_i2c_check_status(master); if (master->state != state_idle) schedule_timer_at(&master->poller, now + master->poll_interval); p8_i2c_check_work(master); unlock(&master->lock); } void p8_i2c_interrupt(uint32_t chip_id) { struct proc_chip *chip = get_chip(chip_id); struct p8_i2c_master *master = NULL; assert(chip); list_for_each(&chip->i2cms, master, link) { /* Lockless fast bailout (shared interrupt) */ if (master->state == state_idle) continue; lock(&master->lock); /* Run the state machine */ p8_i2c_check_status(master); /* Check for new work */ p8_i2c_check_work(master); unlock(&master->lock); } } static const char *compat[] = { "ibm,power8-i2cm", "ibm,centaur-i2cm" }; static void p8_i2c_add_bus_prop(struct p8_i2c_master_port *port) { const struct dt_property *c, *p; struct dt_node *np = port->bus.dt_node; char name[32]; c = dt_find_property(np, "compatible"); p = dt_find_property(np, "ibm,port-name"); if (!c) { if (port->master->type == I2C_POWER8) dt_add_property_strings(np, "compatible", "ibm,power8-i2c-port", "ibm,opal-i2c"); else if (port->master->type == I2C_CENTAUR) dt_add_property_strings(np, "compatible", "ibm,centaur-i2c-port", "ibm,opal-i2c"); } if (!p) { if (port->master->type == I2C_POWER8) snprintf(name, sizeof(name), "p8_%08x_e%dp%d", port->master->chip_id, port->master->engine_id, port->port_num); else if (port->master->type == I2C_CENTAUR) snprintf(name, sizeof(name), "cen_%08x_e%dp%d", port->master->chip_id, port->master->engine_id, port->port_num); dt_add_property_string(np, "ibm,port-name", name); } } static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type) { struct p8_i2c_master_port *port; uint32_t lb_freq, count, max_bus_speed; struct dt_node *i2cm_port; struct p8_i2c_master *master; struct list_head *chip_list; uint64_t ex_stat; static bool irq_printed; int64_t rc; master = zalloc(sizeof(*master)); if (!master) { log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: Failed to allocate master " "structure\n"); return; } master->type = type; /* Local bus speed in Hz */ lb_freq = dt_prop_get_u32(i2cm, "clock-frequency"); /* XXX HACK for bad HB value */ if (lb_freq == 600000000) { prlog(PR_ERR, "I2C: Fixing up HB bad clock freq !\n"); lb_freq = 50000000; } /* Initialise the i2c master structure */ master->state = state_idle; master->chip_id = dt_get_chip_id(i2cm); master->engine_id = dt_prop_get_u32(i2cm, "chip-engine#"); master->xscom_base = dt_get_address(i2cm, 0, NULL); if (master->type == I2C_CENTAUR) { struct centaur_chip *centaur = get_centaur(master->chip_id); assert(centaur); chip_list = ¢aur->i2cms; /* Detect bad device-tree from HostBoot giving us bogus * i2c masters */ if (master->engine_id > 0) { prlog(PR_ERR, "I2C: Skipping Centaur Master #1\n"); free(master); return; } } else { struct proc_chip *chip = get_chip(master->chip_id); assert(chip); chip_list = &chip->i2cms; } init_timer(&master->timeout, p8_i2c_timeout, master); init_timer(&master->poller, p8_i2c_poll, master); init_timer(&master->recovery, p8_i2c_recover, master); init_timer(&master->sensor_cache, p8_i2c_enable_scache, master); prlog(PR_INFO, "I2C: Chip %08x Eng. %d\n", master->chip_id, master->engine_id); /* Disable OCC cache during inits */ if (master->type == I2C_CENTAUR) { rc = centaur_disable_sensor_cache(master->chip_id); if (rc < 0) { log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: " "Error %lld disabling sensor cache\n", rc); /* Ignore error and move on ... */ } else time_wait(rc); } rc = xscom_read(master->chip_id, master->xscom_base + I2C_EXTD_STAT_REG, &ex_stat); if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: " "Failed to read EXTD_STAT_REG\n"); if (master->type == I2C_CENTAUR) centaur_enable_sensor_cache(master->chip_id); free(master); return; } master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat); list_head_init(&master->req_list); /* Check if interrupt is usable */ master->irq_ok = p8_i2c_has_irqs(master); if (!irq_printed) { irq_printed = true; prlog(PR_INFO, "I2C: Interrupts %sfunctional\n", master->irq_ok ? "" : "non-"); } /* Program the watermark register */ rc = p8_i2c_prog_watermark(master); /* Re-enable the sensor cache, we aren't touching HW anymore */ if (master->type == I2C_CENTAUR) centaur_enable_sensor_cache(master->chip_id); /* Handle errors from p8_i2c_prog_watermark */ if (rc) { log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: Failed to program the " "WATERMARK_REG\n"); free(master); return; } /* Allocate ports driven by this master */ count = 0; dt_for_each_child(i2cm, i2cm_port) count++; port = zalloc(sizeof(*port) * count); if (!port) { log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: Insufficient memory\n"); free(master); return; } /* Add master to chip's list */ list_add_tail(chip_list, &master->link); max_bus_speed = 0; dt_for_each_child(i2cm, i2cm_port) { uint32_t speed; port->port_num = dt_prop_get_u32(i2cm_port, "reg"); port->master = master; speed = dt_prop_get_u32(i2cm_port, "bus-frequency"); if (speed > max_bus_speed) max_bus_speed = speed; port->bit_rate_div = p8_i2c_get_bit_rate_divisor(lb_freq, speed); port->bus.dt_node = i2cm_port; port->bus.queue_req = p8_i2c_queue_request; port->bus.alloc_req = p8_i2c_alloc_request; port->bus.free_req = p8_i2c_free_request; i2c_add_bus(&port->bus); /* Add OPAL properties to the bus node */ p8_i2c_add_bus_prop(port); prlog(PR_INFO, " P%d: <%s> %d kHz\n", port->port_num, (char *)dt_prop_get(i2cm_port, "ibm,port-name"), speed/1000); port++; } /* If we have no interrupt, calculate a poll interval, * otherwise just use a TIMER_POLL timer which will tick * on OPAL pollers only (which allows us to operate * during boot before interrupts are functional etc... */ if (master->irq_ok) master->poll_interval = TIMER_POLL; else master->poll_interval = p8_i2c_get_poll_interval(max_bus_speed); master->byte_timeout = master->irq_ok ? msecs_to_tb(I2C_TIMEOUT_IRQ_MS) : msecs_to_tb(I2C_TIMEOUT_POLL_MS); } void p8_i2c_init(void) { struct dt_node *i2cm; int i; for (i = 0; i < MAX_I2C_TYPE; i++) { dt_for_each_compatible(dt_root, i2cm, compat[i]) p8_i2c_init_one(i2cm, i); } } skiboot-skiboot-5.1.13/hw/phb3.c000066400000000000000000004110441265204436200163510ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * PHB3 support * */ /* * * FIXME: * More stuff for EEH support: * - PBCQ error reporting interrupt * - I2C-based power management (replacing SHPC) * - Directly detect fenced PHB through one dedicated HW reg */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Enable this to disable error interrupts for debug purposes */ #undef DISABLE_ERR_INTS static void phb3_init_hw(struct phb3 *p, bool first_init); #define PHBDBG(p, fmt, a...) prlog(PR_DEBUG, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) #define PHBINF(p, fmt, a...) prlog(PR_INFO, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) #define PHBERR(p, fmt, a...) prlog(PR_ERR, "PHB%d: " fmt, \ (p)->phb.opal_id, ## a) /* * Lock callbacks. Allows the OPAL API handlers to lock the * PHB around calls such as config space, EEH, etc... */ static void phb3_lock(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); lock(&p->lock); } static void phb3_unlock(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); unlock(&p->lock); } /* Helper to select an IODA table entry */ static inline void phb3_ioda_sel(struct phb3 *p, uint32_t table, uint32_t addr, bool autoinc) { out_be64(p->regs + PHB_IODA_ADDR, (autoinc ? PHB_IODA_AD_AUTOINC : 0) | SETFIELD(PHB_IODA_AD_TSEL, 0ul, table) | SETFIELD(PHB_IODA_AD_TADR, 0ul, addr)); } /* Helper to set the state machine timeout */ static inline uint64_t phb3_set_sm_timeout(struct phb3 *p, uint64_t dur) { uint64_t target, now = mftb(); target = now + dur; if (target == 0) target++; p->delay_tgt_tb = target; return dur; } /* Check if AIB is fenced via PBCQ NFIR */ static bool phb3_fenced(struct phb3 *p) { uint64_t nfir; /* We still probably has crazy xscom */ xscom_read(p->chip_id, p->pe_xscom + 0x0, &nfir); if (nfir & PPC_BIT(16)) { p->flags |= PHB3_AIB_FENCED; p->state = PHB3_STATE_FENCED; return true; } return false; } /* * Configuration space access * * The PHB lock is assumed to be already held */ static int64_t phb3_pcicfg_check(struct phb3 *p, uint32_t bdfn, uint32_t offset, uint32_t size, uint8_t *pe) { uint32_t sm = size - 1; if (offset > 0xfff || bdfn > 0xffff) return OPAL_PARAMETER; if (offset & sm) return OPAL_PARAMETER; /* The root bus only has a device at 0 and we get into an * error state if we try to probe beyond that, so let's * avoid that and just return an error to Linux */ if ((bdfn >> 8) == 0 && (bdfn & 0xff)) return OPAL_HARDWARE; /* Check PHB state */ if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; /* Fetch the PE# from cache */ *pe = p->rte_cache[bdfn]; return OPAL_SUCCESS; } #define PHB3_PCI_CFG_READ(size, type) \ static int64_t phb3_pcicfg_read##size(struct phb *phb, uint32_t bdfn, \ uint32_t offset, type *data) \ { \ struct phb3 *p = phb_to_phb3(phb); \ uint64_t addr, val64; \ int64_t rc; \ uint8_t pe; \ bool use_asb = false; \ \ /* Initialize data in case of error */ \ *data = (type)0xffffffff; \ \ rc = phb3_pcicfg_check(p, bdfn, offset, sizeof(type), &pe); \ if (rc) \ return rc; \ \ if (p->flags & PHB3_AIB_FENCED) { \ if (!(p->flags & PHB3_CFG_USE_ASB)) \ return OPAL_HARDWARE; \ use_asb = true; \ } else if ((p->flags & PHB3_CFG_BLOCKED) && bdfn != 0) { \ return OPAL_HARDWARE; \ } \ \ addr = PHB_CA_ENABLE; \ addr = SETFIELD(PHB_CA_BDFN, addr, bdfn); \ addr = SETFIELD(PHB_CA_REG, addr, offset); \ addr = SETFIELD(PHB_CA_PE, addr, pe); \ if (use_asb) { \ phb3_write_reg_asb(p, PHB_CONFIG_ADDRESS, addr); \ sync(); \ val64 = bswap_64(phb3_read_reg_asb(p, PHB_CONFIG_DATA)); \ *data = (type)(val64 >> (8 * (offset & (4 - sizeof(type))))); \ } else { \ out_be64(p->regs + PHB_CONFIG_ADDRESS, addr); \ *data = in_le##size(p->regs + PHB_CONFIG_DATA + \ (offset & (4 - sizeof(type)))); \ } \ \ return OPAL_SUCCESS; \ } #define PHB3_PCI_CFG_WRITE(size, type) \ static int64_t phb3_pcicfg_write##size(struct phb *phb, uint32_t bdfn, \ uint32_t offset, type data) \ { \ struct phb3 *p = phb_to_phb3(phb); \ uint64_t addr, val64 = 0; \ int64_t rc; \ uint8_t pe; \ bool use_asb = false; \ \ rc = phb3_pcicfg_check(p, bdfn, offset, sizeof(type), &pe); \ if (rc) \ return rc; \ \ if (p->flags & PHB3_AIB_FENCED) { \ if (!(p->flags & PHB3_CFG_USE_ASB)) \ return OPAL_HARDWARE; \ use_asb = true; \ } else if ((p->flags & PHB3_CFG_BLOCKED) && bdfn != 0) { \ return OPAL_HARDWARE; \ } \ \ addr = PHB_CA_ENABLE; \ addr = SETFIELD(PHB_CA_BDFN, addr, bdfn); \ addr = SETFIELD(PHB_CA_REG, addr, offset); \ addr = SETFIELD(PHB_CA_PE, addr, pe); \ if (use_asb) { \ val64 = data; \ val64 = bswap_64(val64 << 8 * (offset & (4 - sizeof(type)))); \ phb3_write_reg_asb(p, PHB_CONFIG_ADDRESS, addr); \ sync(); \ phb3_write_reg_asb(p, PHB_CONFIG_DATA, val64); \ } else { \ out_be64(p->regs + PHB_CONFIG_ADDRESS, addr); \ out_le##size(p->regs + PHB_CONFIG_DATA + \ (offset & (4 - sizeof(type))), data); \ } \ \ return OPAL_SUCCESS; \ } PHB3_PCI_CFG_READ(8, u8) PHB3_PCI_CFG_READ(16, u16) PHB3_PCI_CFG_READ(32, u32) PHB3_PCI_CFG_WRITE(8, u8) PHB3_PCI_CFG_WRITE(16, u16) PHB3_PCI_CFG_WRITE(32, u32) static uint8_t phb3_choose_bus(struct phb *phb __unused, struct pci_device *bridge __unused, uint8_t candidate, uint8_t *max_bus __unused, bool *use_max) { /* Use standard bus number selection */ *use_max = false; return candidate; } static void phb3_root_port_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_PERR_RESP); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 |= (PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); if (!aercap) return; /* Mask various unrecoverable errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, &val32); val32 |= (PCIECAP_AER_UE_MASK_POISON_TLP | PCIECAP_AER_UE_MASK_COMPL_TIMEOUT | PCIECAP_AER_UE_MASK_COMPL_ABORT | PCIECAP_AER_UE_MASK_ECRC); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, val32); /* Report various unrecoverable errors as fatal errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, &val32); val32 |= (PCIECAP_AER_UE_SEVERITY_DLLP | PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN | PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_UNEXP_COMPL | PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW | PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, val32); /* Mask various recoverable errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, &val32); val32 |= PCIECAP_AER_CE_MASK_ADV_NONFATAL; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, val32); /* Enable ECRC check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= (PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); /* Enable all error reporting */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_RERR_CMD, &val32); val32 |= (PCIECAP_AER_RERR_CMD_FE | PCIECAP_AER_RERR_CMD_NFE | PCIECAP_AER_RERR_CMD_CE); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_RERR_CMD, val32); } static void phb3_switch_port_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { struct phb3 *p = phb_to_phb3(phb); uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking and disable INTx */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_INTx_DIS); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Disable partity error and enable system error */ pci_cfg_read16(phb, bdfn, PCI_CFG_BRCTL, &val16); val16 &= ~PCI_CFG_BRCTL_PERR_RESP_EN; val16 |= PCI_CFG_BRCTL_SERR_EN; pci_cfg_write16(phb, bdfn, PCI_CFG_BRCTL, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 |= (PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT); /* HW279570 - Disable reporting of correctable errors */ val16 &= ~PCICAP_EXP_DEVCTL_CE_REPORT; pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Unmask all unrecoverable errors */ if (!aercap) return; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, 0x0); /* Severity of unrecoverable errors */ if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT) val32 = (PCIECAP_AER_UE_SEVERITY_DLLP | PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN | PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW | PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP | PCIECAP_AER_UE_SEVERITY_INTERNAL); else val32 = (PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_INTERNAL); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, val32); /* * Mask various correctable errors * * On Murano and Venice DD1.0 we disable emission of corrected * error messages to the PHB completely to workaround errata * HW257476 causing the loss of tags. */ if (p->rev < PHB3_REV_MURANO_DD20) val32 = 0xffffffff; else val32 = PCIECAP_AER_CE_MASK_ADV_NONFATAL; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, val32); /* Enable ECRC generation and disable ECRC check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= PCIECAP_AER_CAPCTL_ECRCG_EN; val32 &= ~PCIECAP_AER_CAPCTL_ECRCC_EN; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } static void phb3_endpoint_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { struct phb3 *p = phb_to_phb3(phb); uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 &= ~PCICAP_EXP_DEVCTL_CE_REPORT; val16 |= (PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT); /* HW279570 - Disable reporting of correctable errors */ val16 &= ~PCICAP_EXP_DEVCTL_CE_REPORT; pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* * On Murano and Venice DD1.0 we disable emission of corrected * error messages to the PHB completely to workaround errata * HW257476 causing the loss of tags. */ if (p->rev < PHB3_REV_MURANO_DD20) pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, 0xffffffff); /* Enable ECRC generation and check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= (PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } static void phb3_check_device_quirks(struct phb *phb, struct pci_device *dev) { struct phb3 *p = phb_to_phb3(phb); u64 modectl; u32 vdid; u16 vendor, device; /* For these adapters, if they are directly under the PHB, we * adjust some settings for performances */ xscom_read(p->chip_id, p->pe_xscom + 0x0b, &modectl); pci_cfg_read32(phb, dev->bdfn, 0, &vdid); vendor = vdid & 0xffff; device = vdid >> 16; if (vendor == 0x15b3 && (device == 0x1003 || /* Travis3-EN (CX3) */ device == 0x1011 || /* HydePark (ConnectIB) */ device == 0x1013)) { /* GlacierPark (CX4) */ /* Set disable_wr_scope_group bit */ modectl |= PPC_BIT(14); } else { /* Clear disable_wr_scope_group bit */ modectl &= ~PPC_BIT(14); } xscom_write(p->chip_id, p->pe_xscom + 0x0b, modectl); } static void phb3_device_init(struct phb *phb, struct pci_device *dev) { int ecap = 0; int aercap = 0; /* Some special adapter tweaks for devices directly under the PHB */ if (dev->primary_bus == 1) phb3_check_device_quirks(phb, dev); /* Figure out PCIe & AER capability */ if (pci_has_cap(dev, PCI_CFG_CAP_ID_EXP, false)) { ecap = pci_cap(dev, PCI_CFG_CAP_ID_EXP, false); if (!pci_has_cap(dev, PCIECAP_ID_AER, true)) { aercap = pci_find_ecap(phb, dev->bdfn, PCIECAP_ID_AER, NULL); if (aercap > 0) pci_set_cap(dev, PCIECAP_ID_AER, aercap, true); } else { aercap = pci_cap(dev, PCIECAP_ID_AER, true); } } /* Common initialization for the device */ pci_device_init(phb, dev); if (dev->dev_type == PCIE_TYPE_ROOT_PORT) phb3_root_port_init(phb, dev, ecap, aercap); else if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT || dev->dev_type == PCIE_TYPE_SWITCH_DNPORT) phb3_switch_port_init(phb, dev, ecap, aercap); else phb3_endpoint_init(phb, dev, ecap, aercap); } static int64_t phb3_pci_reinit(struct phb *phb, uint64_t scope, uint64_t data) { struct pci_device *pd; uint16_t bdfn = data; if (scope != OPAL_REINIT_PCI_DEV) return OPAL_PARAMETER; pd = pci_find_dev(phb, bdfn); if (!pd) return OPAL_PARAMETER; phb3_device_init(phb, pd); return OPAL_SUCCESS; } static int64_t phb3_presence_detect(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); uint64_t hp_override; /* Test for PHB in error state ? */ if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; /* XXX Check bifurcation stuff ? */ /* Read hotplug override */ hp_override = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE); PHBDBG(p, "hp_override: 0x%016llx\n", hp_override); /* * On P8, the slot status isn't wired up properly, we have to * use the hotplug override A/B bits. */ if ((hp_override & PHB_HPOVR_PRESENCE_A) && (hp_override & PHB_HPOVR_PRESENCE_B)) return OPAL_SHPC_DEV_NOT_PRESENT; /* * Anything else, we assume device present, the link state * machine will perform an early bail out if no electrical * signaling is established after a second. */ return OPAL_SHPC_DEV_PRESENT; } /* Clear IODA cache tables */ static void phb3_init_ioda_cache(struct phb3 *p) { uint32_t i; uint64_t *data64; /* * RTT and PELTV. RTE should be 0xFF's to indicate * invalid PE# for the corresponding RID. * * Note: Instead we set all RTE entries to 0x00 to * work around a problem where PE lookups might be * done before Linux has established valid PE's * (during PCI probing). We can revisit that once/if * Linux has been fixed to always setup valid PEs. * * The value 0x00 corresponds to the default PE# Linux * uses to check for config space freezes before it * has assigned PE# to busses. * * WARNING: Additionally, we need to be careful, there's * a HW issue, if we get an MSI on an RTT entry that is * FF, things will go bad. We need to ensure we don't * ever let a live FF RTT even temporarily when resetting * for EEH etc... (HW278969). */ for (i = 0; i < ARRAY_SIZE(p->rte_cache); i++) p->rte_cache[i] = PHB3_RESERVED_PE_NUM; memset(p->peltv_cache, 0x0, sizeof(p->peltv_cache)); /* Disable all LSI */ for (i = 0; i < ARRAY_SIZE(p->lxive_cache); i++) { data64 = &p->lxive_cache[i]; *data64 = SETFIELD(IODA2_LXIVT_PRIORITY, 0ul, 0xff); *data64 = SETFIELD(IODA2_LXIVT_SERVER, *data64, 0x0); } /* Diable all MSI */ for (i = 0; i < ARRAY_SIZE(p->ive_cache); i++) { data64 = &p->ive_cache[i]; *data64 = SETFIELD(IODA2_IVT_PRIORITY, 0ul, 0xff); *data64 = SETFIELD(IODA2_IVT_SERVER, *data64, 0x0); } /* Clear TVT */ memset(p->tve_cache, 0x0, sizeof(p->tve_cache)); /* Clear M32 domain */ memset(p->m32d_cache, 0x0, sizeof(p->m32d_cache)); /* Clear M64 domain */ memset(p->m64b_cache, 0x0, sizeof(p->m64b_cache)); } /* phb3_ioda_reset - Reset the IODA tables * * @purge: If true, the cache is cleared and the cleared values * are applied to HW. If false, the cached values are * applied to HW * * This reset the IODA tables in the PHB. It is called at * initialization time, on PHB reset, and can be called * explicitly from OPAL */ static int64_t phb3_ioda_reset(struct phb *phb, bool purge) { struct phb3 *p = phb_to_phb3(phb); uint64_t server, prio; uint64_t *pdata64, data64; uint32_t i; if (purge) { prlog(PR_DEBUG, "PHB%d: Purging all IODA tables...\n", p->phb.opal_id); phb3_init_ioda_cache(p); } /* Init_27..28 - LIXVT */ phb3_ioda_sel(p, IODA2_TBL_LXIVT, 0, true); for (i = 0; i < ARRAY_SIZE(p->lxive_cache); i++) { data64 = p->lxive_cache[i]; server = GETFIELD(IODA2_LXIVT_SERVER, data64); prio = GETFIELD(IODA2_LXIVT_PRIORITY, data64); data64 = SETFIELD(IODA2_LXIVT_SERVER, data64, server); data64 = SETFIELD(IODA2_LXIVT_PRIORITY, data64, prio); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_29..30 - MRT */ phb3_ioda_sel(p, IODA2_TBL_MRT, 0, true); for (i = 0; i < 8; i++) out_be64(p->regs + PHB_IODA_DATA0, 0); /* Init_31..32 - TVT */ phb3_ioda_sel(p, IODA2_TBL_TVT, 0, true); for (i = 0; i < ARRAY_SIZE(p->tve_cache); i++) out_be64(p->regs + PHB_IODA_DATA0, p->tve_cache[i]); /* Init_33..34 - M64BT */ phb3_ioda_sel(p, IODA2_TBL_M64BT, 0, true); for (i = 0; i < ARRAY_SIZE(p->m64b_cache); i++) out_be64(p->regs + PHB_IODA_DATA0, p->m64b_cache[i]); /* Init_35..36 - M32DT */ phb3_ioda_sel(p, IODA2_TBL_M32DT, 0, true); for (i = 0; i < ARRAY_SIZE(p->m32d_cache); i++) out_be64(p->regs + PHB_IODA_DATA0, p->m32d_cache[i]); /* Load RTE, PELTV */ if (p->tbl_rtt) memcpy((void *)p->tbl_rtt, p->rte_cache, RTT_TABLE_SIZE); if (p->tbl_peltv) memcpy((void *)p->tbl_peltv, p->peltv_cache, PELTV_TABLE_SIZE); /* Load IVT */ if (p->tbl_ivt) { pdata64 = (uint64_t *)p->tbl_ivt; for (i = 0; i < IVT_TABLE_ENTRIES; i++) pdata64[i * IVT_TABLE_STRIDE] = p->ive_cache[i]; } /* Invalidate RTE, IVE, TCE cache */ out_be64(p->regs + PHB_RTC_INVALIDATE, PHB_RTC_INVALIDATE_ALL); out_be64(p->regs + PHB_IVC_INVALIDATE, PHB_IVC_INVALIDATE_ALL); out_be64(p->regs + PHB_TCE_KILL, PHB_TCE_KILL_ALL); /* Clear RBA */ if (p->rev >= PHB3_REV_MURANO_DD20) { phb3_ioda_sel(p, IODA2_TBL_RBA, 0, true); for (i = 0; i < 32; i++) out_be64(p->regs + PHB_IODA_DATA0, 0x0ul); } /* Clear PEST & PEEV */ for (i = 0; i < PHB3_MAX_PE_NUM; i++) { uint64_t pesta, pestb; phb3_ioda_sel(p, IODA2_TBL_PESTA, i, false); pesta = in_be64(p->regs + PHB_IODA_DATA0); out_be64(p->regs + PHB_IODA_DATA0, 0); phb3_ioda_sel(p, IODA2_TBL_PESTB, i, false); pestb = in_be64(p->regs + PHB_IODA_DATA0); out_be64(p->regs + PHB_IODA_DATA0, 0); if ((pesta & IODA2_PESTA_MMIO_FROZEN) || (pestb & IODA2_PESTB_DMA_STOPPED)) PHBDBG(p, "Frozen PE#%d (%s - %s)\n", i, (pesta & IODA2_PESTA_MMIO_FROZEN) ? "DMA" : "", (pestb & IODA2_PESTB_DMA_STOPPED) ? "MMIO" : ""); } phb3_ioda_sel(p, IODA2_TBL_PEEV, 0, true); for (i = 0; i < 4; i++) out_be64(p->regs + PHB_IODA_DATA0, 0); return OPAL_SUCCESS; } /* * Clear anything we have in PAPR Error Injection registers. Though * the spec says the PAPR error injection should be one-shot without * the "sticky" bit. However, that's false according to the experiments * I had. So we have to clear it at appropriate point in kernel to * avoid endless frozen PE. */ static int64_t phb3_papr_errinjct_reset(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, 0x0ul); out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, 0x0ul); return OPAL_SUCCESS; } static int64_t phb3_set_phb_mem_window(struct phb *phb, uint16_t window_type, uint16_t window_num, uint64_t addr, uint64_t __unused pci_addr, uint64_t size) { struct phb3 *p = phb_to_phb3(phb); uint64_t data64; /* * By design, PHB3 doesn't support IODT any more. * Besides, we can't enable M32 BAR as well. So * the function is used to do M64 mapping and each * BAR is supposed to be shared by all PEs. */ switch (window_type) { case OPAL_IO_WINDOW_TYPE: case OPAL_M32_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16) return OPAL_PARAMETER; data64 = p->m64b_cache[window_num]; if (data64 & IODA2_M64BT_SINGLE_PE) { if ((addr & 0x1FFFFFFul) || (size & 0x1FFFFFFul)) return OPAL_PARAMETER; } else { if ((addr & 0xFFFFFul) || (size & 0xFFFFFul)) return OPAL_PARAMETER; } /* size should be 2^N */ if (!size || size & (size-1)) return OPAL_PARAMETER; /* address should be size aligned */ if (addr & (size - 1)) return OPAL_PARAMETER; break; default: return OPAL_PARAMETER; } if (data64 & IODA2_M64BT_SINGLE_PE) { data64 = SETFIELD(IODA2_M64BT_SINGLE_BASE, data64, addr >> 25); data64 = SETFIELD(IODA2_M64BT_SINGLE_MASK, data64, 0x20000000 - (size >> 25)); } else { data64 = SETFIELD(IODA2_M64BT_BASE, data64, addr >> 20); data64 = SETFIELD(IODA2_M64BT_MASK, data64, 0x40000000 - (size >> 20)); } p->m64b_cache[window_num] = data64; return OPAL_SUCCESS; } /* * For one specific M64 BAR, it can be shared by all PEs, * or owned by single PE exclusively. */ static int64_t phb3_phb_mmio_enable(struct phb *phb, uint16_t window_type, uint16_t window_num, uint16_t enable) { struct phb3 *p = phb_to_phb3(phb); uint64_t data64, base, mask; /* * By design, PHB3 doesn't support IODT any more. * Besides, we can't enable M32 BAR as well. So * the function is used to do M64 mapping and each * BAR is supposed to be shared by all PEs. */ switch (window_type) { case OPAL_IO_WINDOW_TYPE: case OPAL_M32_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16 || enable > OPAL_ENABLE_M64_NON_SPLIT) return OPAL_PARAMETER; break; default: return OPAL_PARAMETER; } /* * We need check the base/mask while enabling * the M64 BAR. Otherwise, invalid base/mask * might cause fenced AIB unintentionally */ data64 = p->m64b_cache[window_num]; switch (enable) { case OPAL_DISABLE_M64: data64 &= ~IODA2_M64BT_SINGLE_PE; data64 &= ~IODA2_M64BT_ENABLE; break; case OPAL_ENABLE_M64_SPLIT: if (data64 & IODA2_M64BT_SINGLE_PE) return OPAL_PARAMETER; base = GETFIELD(IODA2_M64BT_BASE, data64); base = (base << 20); mask = GETFIELD(IODA2_M64BT_MASK, data64); if (base < p->mm0_base || !mask) return OPAL_PARTIAL; data64 |= IODA2_M64BT_ENABLE; break; case OPAL_ENABLE_M64_NON_SPLIT: if (!(data64 & IODA2_M64BT_SINGLE_PE)) return OPAL_PARAMETER; base = GETFIELD(IODA2_M64BT_SINGLE_BASE, data64); base = (base << 25); mask = GETFIELD(IODA2_M64BT_SINGLE_MASK, data64); if (base < p->mm0_base || !mask) return OPAL_PARTIAL; data64 |= IODA2_M64BT_SINGLE_PE; data64 |= IODA2_M64BT_ENABLE; break; } /* Update HW and cache */ phb3_ioda_sel(p, IODA2_TBL_M64BT, window_num, false); out_be64(p->regs + PHB_IODA_DATA0, data64); p->m64b_cache[window_num] = data64; return OPAL_SUCCESS; } static int64_t phb3_map_pe_mmio_window(struct phb *phb, uint16_t pe_num, uint16_t window_type, uint16_t window_num, uint16_t segment_num) { struct phb3 *p = phb_to_phb3(phb); uint64_t data64, *cache; if (pe_num >= PHB3_MAX_PE_NUM) return OPAL_PARAMETER; /* * PHB3 doesn't support IODT any more. On the other * hand, PHB3 support M64DT with much more flexibility. * we need figure it out later. At least, we never use * M64DT in kernel. */ switch(window_type) { case OPAL_IO_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M32_WINDOW_TYPE: if (window_num != 0 || segment_num >= PHB3_MAX_PE_NUM) return OPAL_PARAMETER; cache = &p->m32d_cache[segment_num]; phb3_ioda_sel(p, IODA2_TBL_M32DT, segment_num, false); out_be64(p->regs + PHB_IODA_DATA0, SETFIELD(IODA2_M32DT_PE, 0ull, pe_num)); *cache = SETFIELD(IODA2_M32DT_PE, 0ull, pe_num); break; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16) return OPAL_PARAMETER; cache = &p->m64b_cache[window_num]; data64 = *cache; /* The BAR shouldn't be enabled yet */ if (data64 & IODA2_M64BT_ENABLE) return OPAL_PARTIAL; data64 |= IODA2_M64BT_SINGLE_PE; data64 = SETFIELD(IODA2_M64BT_PE_HI, data64, pe_num >> 5); data64 = SETFIELD(IODA2_M64BT_PE_LOW, data64, pe_num); *cache = data64; break; default: return OPAL_PARAMETER; } return OPAL_SUCCESS; } static int64_t phb3_map_pe_dma_window(struct phb *phb, uint16_t pe_num, uint16_t window_id, uint16_t tce_levels, uint64_t tce_table_addr, uint64_t tce_table_size, uint64_t tce_page_size) { struct phb3 *p = phb_to_phb3(phb); uint64_t tts_encoded; uint64_t data64 = 0; /* * Sanity check. We currently only support "2 window per PE" mode * ie, only bit 59 of the PCI address is used to select the window */ if (pe_num >= PHB3_MAX_PE_NUM || (window_id >> 1) != pe_num) return OPAL_PARAMETER; /* * tce_table_size == 0 is used to disable an entry, in this case * we ignore other arguments */ if (tce_table_size == 0) { phb3_ioda_sel(p, IODA2_TBL_TVT, window_id, false); out_be64(p->regs + PHB_IODA_DATA0, 0); p->tve_cache[window_id] = 0; return OPAL_SUCCESS; } /* Additional arguments validation */ if (tce_levels < 1 || tce_levels > 5 || !is_pow2(tce_table_size) || tce_table_size < 0x1000) return OPAL_PARAMETER; /* Encode TCE table size */ data64 = SETFIELD(IODA2_TVT_TABLE_ADDR, 0ul, tce_table_addr >> 12); tts_encoded = ilog2(tce_table_size) - 11; if (tts_encoded > 31) return OPAL_PARAMETER; data64 = SETFIELD(IODA2_TVT_TCE_TABLE_SIZE, data64, tts_encoded); /* Encode TCE page size */ switch (tce_page_size) { case 0x1000: /* 4K */ data64 = SETFIELD(IODA2_TVT_IO_PSIZE, data64, 1); break; case 0x10000: /* 64K */ data64 = SETFIELD(IODA2_TVT_IO_PSIZE, data64, 5); break; case 0x1000000: /* 16M */ data64 = SETFIELD(IODA2_TVT_IO_PSIZE, data64, 13); break; case 0x10000000: /* 256M */ data64 = SETFIELD(IODA2_TVT_IO_PSIZE, data64, 17); break; default: return OPAL_PARAMETER; } /* Encode number of levels */ data64 = SETFIELD(IODA2_TVT_NUM_LEVELS, data64, tce_levels - 1); phb3_ioda_sel(p, IODA2_TBL_TVT, window_id, false); out_be64(p->regs + PHB_IODA_DATA0, data64); p->tve_cache[window_id] = data64; return OPAL_SUCCESS; } static int64_t phb3_map_pe_dma_window_real(struct phb *phb, uint16_t pe_num, uint16_t window_id, uint64_t pci_start_addr, uint64_t pci_mem_size) { struct phb3 *p = phb_to_phb3(phb); uint64_t end = pci_start_addr + pci_mem_size; uint64_t tve; if (pe_num >= PHB3_MAX_PE_NUM || (window_id >> 1) != pe_num) return OPAL_PARAMETER; if (pci_mem_size) { /* Enable */ /* * Check that the start address has the right TVE index, * we only support the 1 bit mode where each PE has 2 * TVEs */ if ((pci_start_addr >> 59) != (window_id & 1)) return OPAL_PARAMETER; pci_start_addr &= ((1ull << 59) - 1); end = pci_start_addr + pci_mem_size; /* We have to be 16M aligned */ if ((pci_start_addr & 0x00ffffff) || (pci_mem_size & 0x00ffffff)) return OPAL_PARAMETER; /* * It *looks* like this is the max we can support (we need * to verify this. Also we are not checking for rollover, * but then we aren't trying too hard to protect ourselves * againt a completely broken OS. */ if (end > 0x0003ffffffffffffull) return OPAL_PARAMETER; /* * Put start address bits 49:24 into TVE[52:53]||[0:23] * and end address bits 49:24 into TVE[54:55]||[24:47] * and set TVE[51] */ tve = (pci_start_addr << 16) & (0xffffffull << 48); tve |= (pci_start_addr >> 38) & (3ull << 10); tve |= (end >> 8) & (0xfffffful << 16); tve |= (end >> 40) & (3ull << 8); tve |= PPC_BIT(51); } else { /* Disable */ tve = 0; } phb3_ioda_sel(p, IODA2_TBL_TVT, window_id, false); out_be64(p->regs + PHB_IODA_DATA0, tve); p->tve_cache[window_id] = tve; return OPAL_SUCCESS; } static bool phb3_pci_msi_check_q(struct phb3 *p, uint32_t ive_num) { uint64_t ive, ivc, ffi, state; uint8_t *q_byte; /* Each IVE has 16-bytes or 128-bytes */ ive = p->tbl_ivt + (ive_num * IVT_TABLE_STRIDE * 8); q_byte = (uint8_t *)(ive + 5); /* * Handle Q bit. If the Q bit doesn't show up, * we would have CI load to make that. */ if (!(*q_byte & 0x1)) { /* Read from random PHB reg to force flush */ in_be64(p->regs + PHB_IVC_UPDATE); /* Order with subsequent read of Q */ sync(); /* Q still not set, bail out */ if (!(*q_byte & 0x1)) return false; } /* Lock FFI and send interrupt */ while (1) { state = in_be64(p->regs + PHB_FFI_LOCK); if (!state) break; if (state == ~0ULL) /* PHB Fenced */ return false; } /* Clear Q bit and update IVC */ *q_byte = 0; ivc = SETFIELD(PHB_IVC_UPDATE_SID, 0ul, ive_num) | PHB_IVC_UPDATE_ENABLE_Q; out_be64(p->regs + PHB_IVC_UPDATE, ivc); /* * Resend interrupt. Note the lock clear bit isn't documented in * the PHB3 spec and thus is probably unnecessary but it's in * IODA2 so let's be safe here, it won't hurt to set it */ ffi = SETFIELD(PHB_FFI_REQUEST_ISN, 0ul, ive_num) | PHB_FFI_LOCK_CLEAR; out_be64(p->regs + PHB_FFI_REQUEST, ffi); return true; } static void phb3_pci_msi_flush_ive(struct phb3 *p, uint32_t ive_num) { asm volatile("dcbf %0,%1" : : "b" (p->tbl_ivt), "r" (ive_num * IVT_TABLE_STRIDE * 8) : "memory"); } static int64_t phb3_pci_msi_eoi(struct phb *phb, uint32_t hwirq) { struct phb3 *p = phb_to_phb3(phb); uint32_t ive_num = PHB3_IRQ_NUM(hwirq); uint64_t ive, ivc; uint8_t *p_byte, gp, gen; /* OS might not configure IVT yet */ if (!p->tbl_ivt) return OPAL_HARDWARE; /* Each IVE has 16-bytes or 128-bytes */ ive = p->tbl_ivt + (ive_num * IVT_TABLE_STRIDE * 8); p_byte = (uint8_t *)(ive + 4); /* Read generation and P */ gp = *p_byte; gen = gp >> 1; /* Increment generation count and clear P */ *p_byte = ((gen + 1) << 1) & 0x7; /* Update the IVC with a match against the old gen count */ ivc = SETFIELD(PHB_IVC_UPDATE_SID, 0ul, ive_num) | PHB_IVC_UPDATE_ENABLE_P | PHB_IVC_UPDATE_ENABLE_GEN | SETFIELD(PHB_IVC_UPDATE_GEN_MATCH, 0ul, gen); out_be64(p->regs + PHB_IVC_UPDATE, ivc); /* Handle Q bit */ phb3_pci_msi_check_q(p, ive_num); phb3_pci_msi_flush_ive(p, ive_num); return OPAL_SUCCESS; } static int64_t phb3_set_ive_pe(struct phb *phb, uint32_t pe_num, uint32_t ive_num) { struct phb3 *p = phb_to_phb3(phb); uint64_t *cache, ivep, data64; uint16_t *pe_word; /* OS should enable the BAR in advance */ if (!p->tbl_ivt) return OPAL_HARDWARE; /* Each IVE reserves 128 bytes */ if (pe_num >= PHB3_MAX_PE_NUM || ive_num >= IVT_TABLE_ENTRIES) return OPAL_PARAMETER; /* Update IVE cache */ cache = &p->ive_cache[ive_num]; *cache = SETFIELD(IODA2_IVT_PE, *cache, pe_num); /* Update in-memory IVE without clobbering P and Q */ ivep = p->tbl_ivt + (ive_num * IVT_TABLE_STRIDE * 8); pe_word = (uint16_t *)(ivep + 6); *pe_word = pe_num; /* Invalidate IVC */ data64 = SETFIELD(PHB_IVC_INVALIDATE_SID, 0ul, ive_num); out_be64(p->regs + PHB_IVC_INVALIDATE, data64); return OPAL_SUCCESS; } static int64_t phb3_get_msi_32(struct phb *phb __unused, uint32_t pe_num, uint32_t ive_num, uint8_t msi_range, uint32_t *msi_address, uint32_t *message_data) { /* * Sanity check. We needn't check on mve_number (PE#) * on PHB3 since the interrupt source is purely determined * by its DMA address and data, but the check isn't * harmful. */ if (pe_num >= PHB3_MAX_PE_NUM || ive_num >= IVT_TABLE_ENTRIES || msi_range != 1 || !msi_address|| !message_data) return OPAL_PARAMETER; /* * DMA address and data will form the IVE index. * For more details, please refer to IODA2 spec. */ *msi_address = 0xFFFF0000 | ((ive_num << 4) & 0xFFFFFE0F); *message_data = ive_num & 0x1F; return OPAL_SUCCESS; } static int64_t phb3_get_msi_64(struct phb *phb __unused, uint32_t pe_num, uint32_t ive_num, uint8_t msi_range, uint64_t *msi_address, uint32_t *message_data) { /* Sanity check */ if (pe_num >= PHB3_MAX_PE_NUM || ive_num >= IVT_TABLE_ENTRIES || msi_range != 1 || !msi_address || !message_data) return OPAL_PARAMETER; /* * DMA address and data will form the IVE index. * For more details, please refer to IODA2 spec. */ *msi_address = (0x1ul << 60) | ((ive_num << 4) & 0xFFFFFFFFFFFFFE0Ful); *message_data = ive_num & 0x1F; return OPAL_SUCCESS; } static bool phb3_err_check_pbcq(struct phb3 *p) { uint64_t nfir, mask, wof, val64; int32_t class, bit; uint64_t severity[PHB3_ERR_CLASS_LAST] = { 0x0000000000000000, /* NONE */ 0x018000F800000000, /* DEAD */ 0x7E7DC70000000000, /* FENCED */ 0x0000000000000000, /* ER */ 0x0000000000000000 /* INF */ }; /* * Read on NFIR to see if XSCOM is working properly. * If XSCOM doesn't work well, we need take the PHB * into account any more. */ xscom_read(p->chip_id, p->pe_xscom + 0x0, &nfir); if (nfir == 0xffffffffffffffff) { p->err.err_src = PHB3_ERR_SRC_NONE; p->err.err_class = PHB3_ERR_CLASS_DEAD; phb3_set_err_pending(p, true); return true; } /* * Check WOF. We need handle unmasked errors firstly. * We probably run into the situation (on simulator) * where we have asserted FIR bits, but WOF has nothing. * For that case, we should check FIR as well. */ xscom_read(p->chip_id, p->pe_xscom + 0x3, &mask); xscom_read(p->chip_id, p->pe_xscom + 0x8, &wof); if (wof & ~mask) wof &= ~mask; if (!wof) { if (nfir & ~mask) nfir &= ~mask; if (!nfir) return false; wof = nfir; } /* We shouldn't hit class PHB3_ERR_CLASS_NONE */ for (class = PHB3_ERR_CLASS_NONE; class < PHB3_ERR_CLASS_LAST; class++) { val64 = wof & severity[class]; if (!val64) continue; for (bit = 0; bit < 64; bit++) { if (val64 & PPC_BIT(bit)) { p->err.err_src = PHB3_ERR_SRC_PBCQ; p->err.err_class = class; p->err.err_bit = 63 - bit; phb3_set_err_pending(p, true); return true; } } } return false; } static bool phb3_err_check_lem(struct phb3 *p) { uint64_t fir, wof, mask, val64; int32_t class, bit; uint64_t severity[PHB3_ERR_CLASS_LAST] = { 0x0000000000000000, /* NONE */ 0x0000000000000000, /* DEAD */ 0xADB670C980ADD151, /* FENCED */ 0x000800107F500A2C, /* ER */ 0x42018E2200002482 /* INF */ }; /* * Read FIR. If XSCOM or ASB is frozen, we needn't * go forward and just mark the PHB with dead state */ fir = phb3_read_reg_asb(p, PHB_LEM_FIR_ACCUM); if (fir == 0xffffffffffffffff) { p->err.err_src = PHB3_ERR_SRC_PHB; p->err.err_class = PHB3_ERR_CLASS_DEAD; phb3_set_err_pending(p, true); return true; } /* * Check on WOF for the unmasked errors firstly. Under * some situation where we run skiboot on simulator, * we already had FIR bits asserted, but WOF is still zero. * For that case, we check FIR directly. */ wof = phb3_read_reg_asb(p, PHB_LEM_WOF); mask = phb3_read_reg_asb(p, PHB_LEM_ERROR_MASK); if (wof & ~mask) wof &= ~mask; if (!wof) { if (fir & ~mask) fir &= ~mask; if (!fir) return false; wof = fir; } /* We shouldn't hit PHB3_ERR_CLASS_NONE */ for (class = PHB3_ERR_CLASS_NONE; class < PHB3_ERR_CLASS_LAST; class++) { val64 = wof & severity[class]; if (!val64) continue; for (bit = 0; bit < 64; bit++) { if (val64 & PPC_BIT(bit)) { p->err.err_src = PHB3_ERR_SRC_PHB; p->err.err_class = class; p->err.err_bit = 63 - bit; phb3_set_err_pending(p, true); return true; } } } return false; } /* * The function can be called during error recovery for INF * and ER class. For INF case, it's expected to be called * when grabbing the error log. We will call it explicitly * when clearing frozen PE state for ER case. */ static void phb3_err_ER_clear(struct phb3 *p) { uint32_t val32; uint64_t val64; uint64_t fir = in_be64(p->regs + PHB_LEM_FIR_ACCUM); /* Rec 1: Grab the PCI config lock */ /* Removed... unnecessary. We have our own lock here */ /* Rec 2/3/4: Take all inbound transactions */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000001c00000000ul); out_be32(p->regs + PHB_CONFIG_DATA, 0x10000000); /* Rec 5/6/7: Clear pending non-fatal errors */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000005000000000ul); val32 = in_be32(p->regs + PHB_CONFIG_DATA); out_be32(p->regs + PHB_CONFIG_DATA, (val32 & 0xe0700000) | 0x0f000f00); /* Rec 8/9/10: Clear pending fatal errors for AER */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000010400000000ul); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 11/12/13: Clear pending non-fatal errors for AER */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000011000000000ul); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 22/23/24: Clear root port errors */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000013000000000ul); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 25/26/27: Enable IO and MMIO bar */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000004000000000ul); out_be32(p->regs + PHB_CONFIG_DATA, 0x470100f8); /* Rec 28: Release the PCI config lock */ /* Removed... unnecessary. We have our own lock here */ /* Rec 29...34: Clear UTL errors */ val64 = in_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS); out_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS, val64); val64 = in_be64(p->regs + UTL_PCIE_PORT_STATUS); out_be64(p->regs + UTL_PCIE_PORT_STATUS, val64); val64 = in_be64(p->regs + UTL_RC_STATUS); out_be64(p->regs + UTL_RC_STATUS, val64); /* Rec 39...66: Clear PHB error trap */ val64 = in_be64(p->regs + PHB_ERR_STATUS); out_be64(p->regs + PHB_ERR_STATUS, val64); out_be64(p->regs + PHB_ERR1_STATUS, 0x0ul); out_be64(p->regs + PHB_ERR_LOG_0, 0x0ul); out_be64(p->regs + PHB_ERR_LOG_1, 0x0ul); val64 = in_be64(p->regs + PHB_OUT_ERR_STATUS); out_be64(p->regs + PHB_OUT_ERR_STATUS, val64); out_be64(p->regs + PHB_OUT_ERR1_STATUS, 0x0ul); out_be64(p->regs + PHB_OUT_ERR_LOG_0, 0x0ul); out_be64(p->regs + PHB_OUT_ERR_LOG_1, 0x0ul); val64 = in_be64(p->regs + PHB_INA_ERR_STATUS); out_be64(p->regs + PHB_INA_ERR_STATUS, val64); out_be64(p->regs + PHB_INA_ERR1_STATUS, 0x0ul); out_be64(p->regs + PHB_INA_ERR_LOG_0, 0x0ul); out_be64(p->regs + PHB_INA_ERR_LOG_1, 0x0ul); val64 = in_be64(p->regs + PHB_INB_ERR_STATUS); out_be64(p->regs + PHB_INB_ERR_STATUS, val64); out_be64(p->regs + PHB_INB_ERR1_STATUS, 0x0ul); out_be64(p->regs + PHB_INB_ERR_LOG_0, 0x0ul); out_be64(p->regs + PHB_INB_ERR_LOG_1, 0x0ul); /* Rec 67/68: Clear FIR/WOF */ out_be64(p->regs + PHB_LEM_FIR_AND_MASK, ~fir); out_be64(p->regs + PHB_LEM_WOF, 0x0ul); } static void phb3_read_phb_status(struct phb3 *p, struct OpalIoPhb3ErrorData *stat) { uint16_t val; uint64_t *pPEST; uint64_t val64 = 0; uint32_t i; memset(stat, 0, sizeof(struct OpalIoPhb3ErrorData)); /* Error data common part */ stat->common.version = OPAL_PHB_ERROR_DATA_VERSION_1; stat->common.ioType = OPAL_PHB_ERROR_DATA_TYPE_PHB3; stat->common.len = sizeof(struct OpalIoPhb3ErrorData); /* * We read some registers using config space through AIB. * * Get to other registers using ASB when possible to get to them * through a fence if one is present. */ /* Use ASB to access PCICFG if the PHB has been fenced */ p->flags |= PHB3_CFG_USE_ASB; /* Grab RC bridge control, make it 32-bit */ phb3_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &val); stat->brdgCtl = val; /* Grab UTL status registers */ stat->portStatusReg = hi32(phb3_read_reg_asb(p, UTL_PCIE_PORT_STATUS)); stat->rootCmplxStatus = hi32(phb3_read_reg_asb(p, UTL_RC_STATUS)); stat->busAgentStatus = hi32(phb3_read_reg_asb(p, UTL_SYS_BUS_AGENT_STATUS)); /* * Grab various RC PCIe capability registers. All device, slot * and link status are 16-bit, so we grab the pair control+status * for each of them */ phb3_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_DEVCTL, &stat->deviceStatus); phb3_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_SLOTCTL, &stat->slotStatus); phb3_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, &stat->linkStatus); /* * I assume those are the standard config space header, cmd & status * together makes 32-bit. Secondary status is 16-bit so I'll clear * the top on that one */ phb3_pcicfg_read32(&p->phb, 0, PCI_CFG_CMD, &stat->devCmdStatus); phb3_pcicfg_read16(&p->phb, 0, PCI_CFG_SECONDARY_STATUS, &val); stat->devSecStatus = val; /* Grab a bunch of AER regs */ phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_RERR_STA, &stat->rootErrorStatus); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_UE_STATUS, &stat->uncorrErrorStatus); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, &stat->corrErrorStatus); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG0, &stat->tlpHdr1); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG1, &stat->tlpHdr2); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG2, &stat->tlpHdr3); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG3, &stat->tlpHdr4); phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_SRCID, &stat->sourceId); /* Restore to AIB */ p->flags &= ~PHB3_CFG_USE_ASB; /* PEC NFIR */ xscom_read(p->chip_id, p->pe_xscom + 0x0, &stat->nFir); xscom_read(p->chip_id, p->pe_xscom + 0x3, &stat->nFirMask); xscom_read(p->chip_id, p->pe_xscom + 0x8, &stat->nFirWOF); /* PHB3 inbound and outbound error Regs */ stat->phbPlssr = phb3_read_reg_asb(p, PHB_CPU_LOADSTORE_STATUS); stat->phbCsr = phb3_read_reg_asb(p, PHB_DMA_CHAN_STATUS); stat->lemFir = phb3_read_reg_asb(p, PHB_LEM_FIR_ACCUM); stat->lemErrorMask = phb3_read_reg_asb(p, PHB_LEM_ERROR_MASK); stat->lemWOF = phb3_read_reg_asb(p, PHB_LEM_WOF); stat->phbErrorStatus = phb3_read_reg_asb(p, PHB_ERR_STATUS); stat->phbFirstErrorStatus = phb3_read_reg_asb(p, PHB_ERR1_STATUS); stat->phbErrorLog0 = phb3_read_reg_asb(p, PHB_ERR_LOG_0); stat->phbErrorLog1 = phb3_read_reg_asb(p, PHB_ERR_LOG_1); stat->mmioErrorStatus = phb3_read_reg_asb(p, PHB_OUT_ERR_STATUS); stat->mmioFirstErrorStatus = phb3_read_reg_asb(p, PHB_OUT_ERR1_STATUS); stat->mmioErrorLog0 = phb3_read_reg_asb(p, PHB_OUT_ERR_LOG_0); stat->mmioErrorLog1 = phb3_read_reg_asb(p, PHB_OUT_ERR_LOG_1); stat->dma0ErrorStatus = phb3_read_reg_asb(p, PHB_INA_ERR_STATUS); stat->dma0FirstErrorStatus = phb3_read_reg_asb(p, PHB_INA_ERR1_STATUS); stat->dma0ErrorLog0 = phb3_read_reg_asb(p, PHB_INA_ERR_LOG_0); stat->dma0ErrorLog1 = phb3_read_reg_asb(p, PHB_INA_ERR_LOG_1); stat->dma1ErrorStatus = phb3_read_reg_asb(p, PHB_INB_ERR_STATUS); stat->dma1FirstErrorStatus = phb3_read_reg_asb(p, PHB_INB_ERR1_STATUS); stat->dma1ErrorLog0 = phb3_read_reg_asb(p, PHB_INB_ERR_LOG_0); stat->dma1ErrorLog1 = phb3_read_reg_asb(p, PHB_INB_ERR_LOG_1); /* * Grab PESTA & B content. The error bit (bit#0) should * be fetched from IODA and the left content from memory * resident tables. */ pPEST = (uint64_t *)p->tbl_pest; val64 = PHB_IODA_AD_AUTOINC; val64 = SETFIELD(PHB_IODA_AD_TSEL, val64, IODA2_TBL_PESTA); phb3_write_reg_asb(p, PHB_IODA_ADDR, val64); for (i = 0; i < OPAL_PHB3_NUM_PEST_REGS; i++) { stat->pestA[i] = phb3_read_reg_asb(p, PHB_IODA_DATA0); stat->pestA[i] |= pPEST[2 * i]; } val64 = PHB_IODA_AD_AUTOINC; val64 = SETFIELD(PHB_IODA_AD_TSEL, val64, IODA2_TBL_PESTB); phb3_write_reg_asb(p, PHB_IODA_ADDR, val64); for (i = 0; i < OPAL_PHB3_NUM_PEST_REGS; i++) { stat->pestB[i] = phb3_read_reg_asb(p, PHB_IODA_DATA0); stat->pestB[i] |= pPEST[2 * i + 1]; } } static int64_t phb3_msi_get_xive(void *data, uint32_t isn, uint16_t *server, uint8_t *prio) { struct phb3 *p = data; uint32_t chip, index, irq; uint64_t ive; chip = P8_IRQ_TO_CHIP(isn); index = P8_IRQ_TO_PHB(isn); irq = PHB3_IRQ_NUM(isn); if (chip != p->chip_id || index != p->index || irq > PHB3_MSI_IRQ_MAX) return OPAL_PARAMETER; /* * Each IVE has 16 bytes in cache. Note that the kernel * should strip the link bits from server field. */ ive = p->ive_cache[irq]; *server = GETFIELD(IODA2_IVT_SERVER, ive); *prio = GETFIELD(IODA2_IVT_PRIORITY, ive); return OPAL_SUCCESS; } static int64_t phb3_msi_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t prio) { struct phb3 *p = data; uint32_t chip, index; uint64_t *cache, ive_num, data64, m_server, m_prio; uint32_t *ive; chip = P8_IRQ_TO_CHIP(isn); index = P8_IRQ_TO_PHB(isn); ive_num = PHB3_IRQ_NUM(isn); if (p->state == PHB3_STATE_BROKEN || !p->tbl_rtt) return OPAL_HARDWARE; if (chip != p->chip_id || index != p->index || ive_num > PHB3_MSI_IRQ_MAX) return OPAL_PARAMETER; /* * We need strip the link from server. As Milton told * me, the server is assigned as follows and the left * bits unused: node/chip/core/thread/link = 2/3/4/3/2 * * Note: the server has added the link bits to server. */ m_server = server; m_prio = prio; cache = &p->ive_cache[ive_num]; *cache = SETFIELD(IODA2_IVT_SERVER, *cache, m_server); *cache = SETFIELD(IODA2_IVT_PRIORITY, *cache, m_prio); /* * Update IVT and IVC. We need use IVC update register * to do that. Each IVE in the table has 128 bytes */ ive = (uint32_t *)(p->tbl_ivt + ive_num * IVT_TABLE_STRIDE * 8); data64 = PHB_IVC_UPDATE_ENABLE_SERVER | PHB_IVC_UPDATE_ENABLE_PRI; data64 = SETFIELD(PHB_IVC_UPDATE_SID, data64, ive_num); data64 = SETFIELD(PHB_IVC_UPDATE_SERVER, data64, m_server); data64 = SETFIELD(PHB_IVC_UPDATE_PRI, data64, m_prio); /* * We don't use SETFIELD because we are doing a 32-bit access * in order to avoid touching the P and Q bits */ *ive = (m_server << 8) | m_prio; out_be64(p->regs + PHB_IVC_UPDATE, data64); /* * Handle Q bit if we're going to enable the interrupt. * The OS should make sure the interrupt handler has * been installed already. */ if (prio != 0xff) { if (phb3_pci_msi_check_q(p, ive_num)) phb3_pci_msi_flush_ive(p, ive_num); } return OPAL_SUCCESS; } static int64_t phb3_lsi_get_xive(void *data, uint32_t isn, uint16_t *server, uint8_t *prio) { struct phb3 *p = data; uint32_t chip, index, irq; uint64_t lxive; chip = P8_IRQ_TO_CHIP(isn); index = P8_IRQ_TO_PHB(isn); irq = PHB3_IRQ_NUM(isn); if (chip != p->chip_id || index != p->index || irq < PHB3_LSI_IRQ_MIN || irq > PHB3_LSI_IRQ_MAX) return OPAL_PARAMETER; lxive = p->lxive_cache[irq - PHB3_LSI_IRQ_MIN]; *server = GETFIELD(IODA2_LXIVT_SERVER, lxive); *prio = GETFIELD(IODA2_LXIVT_PRIORITY, lxive); return OPAL_SUCCESS; } static int64_t phb3_lsi_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t prio) { struct phb3 *p = data; uint32_t chip, index, irq, entry; uint64_t lxive; chip = P8_IRQ_TO_CHIP(isn); index = P8_IRQ_TO_PHB(isn); irq = PHB3_IRQ_NUM(isn); if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; if (chip != p->chip_id || index != p->index || irq < PHB3_LSI_IRQ_MIN || irq > PHB3_LSI_IRQ_MAX) return OPAL_PARAMETER; lxive = SETFIELD(IODA2_LXIVT_SERVER, 0ul, server); lxive = SETFIELD(IODA2_LXIVT_PRIORITY, lxive, prio); /* * We cache the arguments because we have to mangle * it in order to hijack 3 bits of priority to extend * the server number */ entry = irq - PHB3_LSI_IRQ_MIN; p->lxive_cache[entry] = lxive; /* We use HRT entry 0 always for now */ phb3_ioda_sel(p, IODA2_TBL_LXIVT, entry, false); lxive = in_be64(p->regs + PHB_IODA_DATA0); lxive = SETFIELD(IODA2_LXIVT_SERVER, lxive, server); lxive = SETFIELD(IODA2_LXIVT_PRIORITY, lxive, prio); out_be64(p->regs + PHB_IODA_DATA0, lxive); return OPAL_SUCCESS; } static void phb3_err_interrupt(void *data, uint32_t isn) { struct phb3 *p = data; PHBDBG(p, "Got interrupt 0x%08x\n", isn); /* Update pending event */ opal_update_pending_evt(OPAL_EVENT_PCI_ERROR, OPAL_EVENT_PCI_ERROR); /* If the PHB is broken, go away */ if (p->state == PHB3_STATE_BROKEN) return; /* * Mark the PHB has pending error so that the OS * can handle it at late point. */ phb3_set_err_pending(p, true); } /* MSIs (OS owned) */ static const struct irq_source_ops phb3_msi_irq_ops = { .get_xive = phb3_msi_get_xive, .set_xive = phb3_msi_set_xive, }; /* LSIs (OS owned) */ static const struct irq_source_ops phb3_lsi_irq_ops = { .get_xive = phb3_lsi_get_xive, .set_xive = phb3_lsi_set_xive, }; /* Error LSIs (skiboot owned) */ static const struct irq_source_ops phb3_err_lsi_irq_ops = { .get_xive = phb3_lsi_get_xive, .set_xive = phb3_lsi_set_xive, .interrupt = phb3_err_interrupt, }; static int64_t phb3_set_pe(struct phb *phb, uint64_t pe_num, uint64_t bdfn, uint8_t bcompare, uint8_t dcompare, uint8_t fcompare, uint8_t action) { struct phb3 *p = phb_to_phb3(phb); uint64_t mask, val, tmp, idx; int32_t all = 0; uint16_t *rte; /* Sanity check */ if (!p->tbl_rtt) return OPAL_HARDWARE; if (action != OPAL_MAP_PE && action != OPAL_UNMAP_PE) return OPAL_PARAMETER; if (pe_num >= PHB3_MAX_PE_NUM || bdfn > 0xffff || bcompare > OpalPciBusAll || dcompare > OPAL_COMPARE_RID_DEVICE_NUMBER || fcompare > OPAL_COMPARE_RID_FUNCTION_NUMBER) return OPAL_PARAMETER; /* Figure out the RID range */ if (bcompare == OpalPciBusAny) { mask = 0x0; val = 0x0; all = 0x1; } else { tmp = ((0x1 << (bcompare + 1)) - 1) << (15 - bcompare); mask = tmp; val = bdfn & tmp; } if (dcompare == OPAL_IGNORE_RID_DEVICE_NUMBER) all = (all << 1) | 0x1; else { mask |= 0xf8; val |= (bdfn & 0xf8); } if (fcompare == OPAL_IGNORE_RID_FUNCTION_NUMBER) all = (all << 1) | 0x1; else { mask |= 0x7; val |= (bdfn & 0x7); } /* Map or unmap the RTT range */ if (all == 0x7) { if (action == OPAL_MAP_PE) { for (idx = 0; idx < RTT_TABLE_ENTRIES; idx++) p->rte_cache[idx] = pe_num; } else { for ( idx = 0; idx < ARRAY_SIZE(p->rte_cache); idx++) p->rte_cache[idx] = PHB3_RESERVED_PE_NUM; } memcpy((void *)p->tbl_rtt, p->rte_cache, RTT_TABLE_SIZE); } else { rte = (uint16_t *)p->tbl_rtt; for (idx = 0; idx < RTT_TABLE_ENTRIES; idx++, rte++) { if ((idx & mask) != val) continue; if (action == OPAL_MAP_PE) p->rte_cache[idx] = pe_num; else p->rte_cache[idx] = PHB3_RESERVED_PE_NUM; *rte = p->rte_cache[idx]; } } /* Invalidate the entire RTC */ out_be64(p->regs + PHB_RTC_INVALIDATE, PHB_RTC_INVALIDATE_ALL); return OPAL_SUCCESS; } static int64_t phb3_set_peltv(struct phb *phb, uint32_t parent_pe, uint32_t child_pe, uint8_t state) { struct phb3 *p = phb_to_phb3(phb); uint8_t *peltv; uint32_t idx, mask; /* Sanity check */ if (!p->tbl_peltv) return OPAL_HARDWARE; if (parent_pe >= PHB3_MAX_PE_NUM || child_pe >= PHB3_MAX_PE_NUM) return OPAL_PARAMETER; /* Find index for parent PE */ idx = parent_pe * (PHB3_MAX_PE_NUM / 8); idx += (child_pe / 8); mask = 0x1 << (7 - (child_pe % 8)); peltv = (uint8_t *)p->tbl_peltv; peltv += idx; if (state) { *peltv |= mask; p->peltv_cache[idx] |= mask; } else { *peltv &= ~mask; p->peltv_cache[idx] &= ~mask; } return OPAL_SUCCESS; } static int64_t phb3_link_state(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); uint64_t reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); uint16_t lstat; int64_t rc; /* XXX Test for PHB in error state ? */ /* Link is up, let's find the actual speed */ if (!(reg & PHB_PCIE_DLP_TC_DL_LINKACT)) return OPAL_SHPC_LINK_DOWN; rc = phb3_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LSTAT, &lstat); if (rc < 0) { /* Shouldn't happen */ PHBERR(p, "Failed to read link status\n"); return OPAL_HARDWARE; } if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) return OPAL_SHPC_LINK_DOWN; return GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat); } static int64_t phb3_power_state(struct phb __unused *phb) { /* XXX Test for PHB in error state ? */ /* XXX TODO - External power control ? */ return OPAL_SHPC_POWER_ON; } static int64_t phb3_slot_power_off(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; if (p->state != PHB3_STATE_FUNCTIONAL) return OPAL_BUSY; /* XXX TODO - External power control ? */ return OPAL_SUCCESS; } static int64_t phb3_slot_power_on(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; if (p->state != PHB3_STATE_FUNCTIONAL) return OPAL_BUSY; /* XXX TODO - External power control ? */ return OPAL_SUCCESS; } static void phb3_setup_for_link_down(struct phb3 *p) { uint32_t reg32; /* Mark link down */ p->has_link = false; /* Mask PCIE port interrupts */ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad42800000000000); /* Mask AER receiver error */ phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, ®32); reg32 |= PCIECAP_AER_CE_RECVR_ERR; phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32); } static void phb3_setup_for_link_up(struct phb3 *p) { uint32_t reg32; /* Clear AER receiver error status */ phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, PCIECAP_AER_CE_RECVR_ERR); /* Unmask receiver error status in AER */ phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, ®32); reg32 &= ~PCIECAP_AER_CE_RECVR_ERR; phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32); /* Clear spurrious errors and enable PCIE port interrupts */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xffdfffffffffffff); out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad5a800000000000); /* Mark link up */ p->has_link = true; /* Don't block PCI-CFG */ p->flags &= ~PHB3_CFG_BLOCKED; /* * For complete reset, we might be required to restore * bus numbers for PCI bridges. */ if (p->flags & PHB3_RESTORE_BUS_NUM) { p->flags &= ~PHB3_RESTORE_BUS_NUM; pci_restore_bridge_buses(&p->phb); } } static int64_t phb3_retry_state(struct phb3 *p) { if (p->retry_state <= PHB3_STATE_FUNCTIONAL) return OPAL_WRONG_STATE; p->delay_tgt_tb = 0; p->state = p->retry_state; return p->phb.ops->poll(&p->phb); } static int64_t phb3_sm_link_poll(struct phb3 *p) { int64_t rc; uint64_t reg; /* This is the state machine to wait for the link to come * up. Currently we just wait until we timeout, eventually * we want to add retries and fallback to Gen1. */ switch(p->state) { case PHB3_STATE_WAIT_LINK_ELECTRICAL: /* Wait for the link electrical connection to be * established (shorter timeout). This allows us to * workaround spurrious presence detect on some machines * without waiting 10s each time * * Note: We *also* check for the full link up bit here * because simics doesn't seem to implement the electrical * link bit at all */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (reg & (PHB_PCIE_DLP_INBAND_PRESENCE | PHB_PCIE_DLP_TC_DL_LINKACT)) { PHBDBG(p, "Electrical link detected...\n"); p->state = PHB3_STATE_WAIT_LINK; p->retries = PHB3_LINK_WAIT_RETRIES; } else if (p->retries-- == 0) { PHBDBG(p, "Timeout waiting for electrical link\n"); PHBDBG(p, "DLP train control: 0x%016llx\n", reg); rc = phb3_retry_state(p); if (rc >= OPAL_SUCCESS) return rc; /* No link, we still mark the PHB as functional */ p->state = PHB3_STATE_FUNCTIONAL; return OPAL_SUCCESS; } return phb3_set_sm_timeout(p, msecs_to_tb(100)); case PHB3_STATE_WAIT_LINK: /* XXX I used the PHB_PCIE_LINK_MANAGEMENT register here but * simics doesn't seem to give me anything, so I've switched * to PCIE_DLP_TRAIN_CTL which appears more reliable */ reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) { /* Setup PHB for link up */ phb3_setup_for_link_up(p); PHBDBG(p, "Link is up!\n"); p->state = PHB3_STATE_FUNCTIONAL; return OPAL_SUCCESS; } if (p->retries-- == 0) { PHBDBG(p, "Timeout waiting for link up\n"); PHBDBG(p, "DLP train control: 0x%016llx\n", reg); rc = phb3_retry_state(p); if (rc >= OPAL_SUCCESS) return rc; /* No link, we still mark the PHB as functional */ p->state = PHB3_STATE_FUNCTIONAL; return OPAL_SUCCESS; } return phb3_set_sm_timeout(p, msecs_to_tb(100)); default: /* How did we get here ? */ assert(false); } return OPAL_HARDWARE; } static int64_t phb3_start_link_poll(struct phb3 *p) { /* * Wait for link up to 10s. However, we give up after * only two seconds if the electrical connection isn't * stablished according to the DLP link control register */ p->retries = PHB3_LINK_ELECTRICAL_RETRIES; p->state = PHB3_STATE_WAIT_LINK_ELECTRICAL; return phb3_set_sm_timeout(p, msecs_to_tb(100)); } static int64_t phb3_sm_hot_reset(struct phb3 *p) { uint16_t brctl; switch (p->state) { case PHB3_STATE_FUNCTIONAL: /* We need do nothing with available slot */ if (phb3_presence_detect(&p->phb) != OPAL_SHPC_DEV_PRESENT) { PHBDBG(p, "Slot hreset: no device\n"); return OPAL_CLOSED; } /* Prepare for link going down */ phb3_setup_for_link_down(p); /* Turn on hot reset */ phb3_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl |= PCI_CFG_BRCTL_SECONDARY_RESET; phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); PHBDBG(p, "Slot hreset: assert reset\n"); p->state = PHB3_STATE_HRESET_DELAY; return phb3_set_sm_timeout(p, secs_to_tb(1)); case PHB3_STATE_HRESET_DELAY: /* Turn off hot reset */ phb3_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl); brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl); PHBDBG(p, "Slot hreset: deassert reset\n"); /* * Due to some oddball adapters bouncing the link * training a couple of times, we wait for a full second * before we start checking the link status, otherwise * we can get a spurrious link down interrupt which * causes us to EEH immediately. */ p->state = PHB3_STATE_HRESET_DELAY2; return phb3_set_sm_timeout(p, secs_to_tb(1)); case PHB3_STATE_HRESET_DELAY2: return phb3_start_link_poll(p); default: PHBDBG(p, "Slot hreset: wrong state %d\n", p->state); break; } p->state = PHB3_STATE_FUNCTIONAL; return OPAL_HARDWARE; } static int64_t phb3_hot_reset(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); if (p->state != PHB3_STATE_FUNCTIONAL) { PHBDBG(p, "phb3_hot_reset: wrong state %d\n", p->state); return OPAL_HARDWARE; } p->flags |= PHB3_CFG_BLOCKED; return phb3_sm_hot_reset(p); } static int64_t phb3_sm_fundamental_reset(struct phb3 *p) { uint64_t reg; /* * Check if there's something connected. We do that here * instead of the switch case below because we want to do * that before we test the skip_perst */ if (p->state == PHB3_STATE_FUNCTIONAL && phb3_presence_detect(&p->phb) != OPAL_SHPC_DEV_PRESENT) { PHBDBG(p, "Slot freset: no device\n"); return OPAL_CLOSED; } /* Handle boot time skipping of reset */ if (p->skip_perst && p->state == PHB3_STATE_FUNCTIONAL) { PHBDBG(p, "Cold boot, skipping PERST assertion\n"); p->state = PHB3_STATE_FRESET_ASSERT_DELAY; /* PERST skipping happens only once */ p->skip_perst = false; } switch(p->state) { case PHB3_STATE_FUNCTIONAL: /* Prepare for link going down */ phb3_setup_for_link_down(p); /* Fall-through */ case PHB3_STATE_FRESET_START: if (p->state == PHB3_STATE_FRESET_START) { PHBDBG(p, "Slot freset: Retrying\n"); p->retry_state = 0; } /* Assert PERST */ reg = in_be64(p->regs + PHB_RESET); reg &= ~0x2000000000000000ul; out_be64(p->regs + PHB_RESET, reg); PHBDBG(p, "Slot freset: Asserting PERST\n"); /* XXX Check delay for PERST... doing 1s for now */ p->state = PHB3_STATE_FRESET_ASSERT_DELAY; return phb3_set_sm_timeout(p, secs_to_tb(1)); case PHB3_STATE_FRESET_ASSERT_DELAY: /* Deassert PERST */ reg = in_be64(p->regs + PHB_RESET); reg |= 0x2000000000000000ul; out_be64(p->regs + PHB_RESET, reg); PHBDBG(p, "Slot freset: Deasserting PERST\n"); p->state = PHB3_STATE_FRESET_DEASSERT_DELAY; /* CAPP fpga requires 1s to flash before polling link */ return phb3_set_sm_timeout(p, secs_to_tb(1)); case PHB3_STATE_FRESET_DEASSERT_DELAY: /* Switch to generic link poll state machine */ return phb3_start_link_poll(p); default: PHBDBG(p, "Slot freset: wrong state %d\n", p->state); break; } p->state = PHB3_STATE_FUNCTIONAL; return OPAL_HARDWARE; } static int64_t phb3_fundamental_reset(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); if (p->state != PHB3_STATE_FUNCTIONAL) { PHBDBG(p, "phb3_fundamental_reset: wrong state %d\n", p->state); return OPAL_HARDWARE; } /* Allow to retry fundamental reset */ p->retry_state = PHB3_STATE_FRESET_START; p->flags |= PHB3_CFG_BLOCKED; return phb3_sm_fundamental_reset(p); } struct lock capi_lock = LOCK_UNLOCKED; static struct { uint32_t ec_level; struct capp_lid_hdr *lid; size_t size; int load_result; } capp_ucode_info = { 0, NULL, 0, false }; #define CAPP_UCODE_MAX_SIZE 0x20000 static int64_t capp_lid_download(void) { int64_t ret; if (capp_ucode_info.load_result != OPAL_EMPTY) return capp_ucode_info.load_result; capp_ucode_info.load_result = wait_for_resource_loaded( RESOURCE_ID_CAPP, capp_ucode_info.ec_level); if (capp_ucode_info.load_result != OPAL_SUCCESS) { prerror("CAPP: Error loading ucode lid. index=%x\n", capp_ucode_info.ec_level); ret = OPAL_RESOURCE; free(capp_ucode_info.lid); capp_ucode_info.lid = NULL; goto end; } ret = OPAL_SUCCESS; end: return ret; } static int64_t capp_load_ucode(struct phb3 *p) { struct proc_chip *chip = get_chip(p->chip_id); struct capp_ucode_lid *ucode; struct capp_ucode_data *data; struct capp_lid_hdr *lid; uint64_t rc, val, addr; uint32_t chunk_count, offset; int i; if (chip->capp_ucode_loaded) return OPAL_SUCCESS; rc = capp_lid_download(); if (rc) return rc; prlog(PR_INFO, "CHIP%i: CAPP ucode lid loaded at %p\n", p->chip_id, capp_ucode_info.lid); lid = capp_ucode_info.lid; /* * If lid header is present (on FSP machines), it'll tell us where to * find the ucode. Otherwise this is the ucode. */ ucode = (struct capp_ucode_lid *)lid; if (be64_to_cpu(lid->eyecatcher) == 0x434150504c494448) { if (be64_to_cpu(lid->version) != 0x1) { PHBERR(p, "capi ucode lid header invalid\n"); return OPAL_HARDWARE; } ucode = (struct capp_ucode_lid *) ((char *)ucode + be64_to_cpu(lid->ucode_offset)); } if ((be64_to_cpu(ucode->eyecatcher) != 0x43415050554C4944) || (ucode->version != 1)) { PHBERR(p, "CAPP: ucode header invalid\n"); return OPAL_HARDWARE; } offset = 0; while (offset < be64_to_cpu(ucode->data_size)) { data = (struct capp_ucode_data *) ((char *)&ucode->data + offset); chunk_count = be32_to_cpu(data->hdr.chunk_count); offset += sizeof(struct capp_ucode_data_hdr) + chunk_count * 8; if (be64_to_cpu(data->hdr.eyecatcher) != 0x4341505055434F44) { PHBERR(p, "CAPP: ucode data header invalid:%i\n", offset); return OPAL_HARDWARE; } switch (data->hdr.reg) { case apc_master_cresp: xscom_write(p->chip_id, CAPP_APC_MASTER_ARRAY_ADDR_REG, 0); addr = CAPP_APC_MASTER_ARRAY_WRITE_REG; break; case apc_master_uop_table: xscom_write(p->chip_id, CAPP_APC_MASTER_ARRAY_ADDR_REG, 0x180ULL << 52); addr = CAPP_APC_MASTER_ARRAY_WRITE_REG; break; case snp_ttype: xscom_write(p->chip_id, CAPP_SNP_ARRAY_ADDR_REG, 0x5000ULL << 48); addr = CAPP_SNP_ARRAY_WRITE_REG; break; case snp_uop_table: xscom_write(p->chip_id, CAPP_SNP_ARRAY_ADDR_REG, 0x4000ULL << 48); addr = CAPP_SNP_ARRAY_WRITE_REG; break; default: continue; } for (i = 0; i < chunk_count; i++) { val = be64_to_cpu(data->data[i]); xscom_write(p->chip_id, addr, val); } } chip->capp_ucode_loaded = true; return OPAL_SUCCESS; } static void do_capp_recovery_scoms(struct phb3 *p) { uint64_t reg; PHBDBG(p, "Doing CAPP recovery scoms\n"); xscom_write(p->chip_id, SNOOP_CAPI_CONFIG, 0); /* disable snoops */ capp_load_ucode(p); xscom_write(p->chip_id, CAPP_ERR_RPT_CLR, 0); /* clear err rpt reg*/ xscom_write(p->chip_id, CAPP_FIR, 0); /* clear capp fir */ xscom_read(p->chip_id, CAPP_ERR_STATUS_CTRL, ®); reg &= ~(PPC_BIT(0) | PPC_BIT(1)); xscom_write(p->chip_id, CAPP_ERR_STATUS_CTRL, reg); } /* * The OS is expected to do fundamental reset after complete * reset to make sure the PHB could be recovered from the * fenced state. However, the OS needn't do that explicitly * since fundamental reset will be done automatically while * powering on the PHB. * * * Usually, we need power off/on the PHB. That includes the * fundamental reset. However, we don't know how to control * the power stuff yet. So skip that and do fundamental reset * directly after reinitialization the hardware. */ static int64_t phb3_sm_complete_reset(struct phb3 *p) { uint64_t cqsts, val; switch (p->state) { case PHB3_STATE_FENCED: case PHB3_STATE_FUNCTIONAL: /* do steps 3-5 of capp recovery procedure */ if (p->flags & PHB3_CAPP_RECOVERY) do_capp_recovery_scoms(p); /* * The users might be doing error injection through PBCQ * Error Inject Control Register. Without clearing that, * we will get recrusive error during recovery and it will * fail eventually. */ xscom_write(p->chip_id, p->pe_xscom + 0xa, 0x0ul); /* * We might have escalated frozen state on non-existing PE * to fenced PHB. For the case, the PHB isn't fenced in the * hardware level and it's not safe to do ETU reset. So we * have to force fenced PHB prior to ETU reset. */ if (!phb3_fenced(p)) xscom_write(p->chip_id, p->pe_xscom + 0x2, 0x000000f000000000ull); /* Clear errors in NFIR and raise ETU reset */ xscom_read(p->chip_id, p->pe_xscom + 0x0, &p->nfir_cache); xscom_read(p->chip_id, p->spci_xscom + 1, &val);/* HW275117 */ xscom_write(p->chip_id, p->pci_xscom + 0xa, 0x8000000000000000); p->state = PHB3_STATE_CRESET_WAIT_CQ; p->retries = 500; return phb3_set_sm_timeout(p, msecs_to_tb(10)); case PHB3_STATE_CRESET_WAIT_CQ: xscom_read(p->chip_id, p->pe_xscom + 0x1c, &val); xscom_read(p->chip_id, p->pe_xscom + 0x1d, &val); xscom_read(p->chip_id, p->pe_xscom + 0x1e, &val); xscom_read(p->chip_id, p->pe_xscom + 0xf, &cqsts); if (!(cqsts & 0xC000000000000000)) { xscom_write(p->chip_id, p->pe_xscom + 0x1, ~p->nfir_cache); p->state = PHB3_STATE_CRESET_REINIT; return phb3_set_sm_timeout(p, msecs_to_tb(100)); } if (p->retries-- == 0) { PHBERR(p, "Timeout waiting for pending transaction\n"); goto error; } return phb3_set_sm_timeout(p, msecs_to_tb(10)); case PHB3_STATE_CRESET_REINIT: p->flags &= ~PHB3_AIB_FENCED; p->flags &= ~PHB3_CAPP_RECOVERY; phb3_init_hw(p, false); p->state = PHB3_STATE_CRESET_FRESET; return phb3_set_sm_timeout(p, msecs_to_tb(100)); case PHB3_STATE_CRESET_FRESET: p->state = PHB3_STATE_FUNCTIONAL; p->flags |= PHB3_CFG_BLOCKED; return phb3_sm_fundamental_reset(p); default: assert(false); } /* Mark the PHB as dead and expect it to be removed */ error: p->state = PHB3_STATE_BROKEN; return OPAL_PARAMETER; } static int64_t phb3_complete_reset(struct phb *phb, uint8_t assert) { struct phb3 *p = phb_to_phb3(phb); if ((assert == OPAL_ASSERT_RESET && p->state != PHB3_STATE_FUNCTIONAL && p->state != PHB3_STATE_FENCED) || (assert == OPAL_DEASSERT_RESET && p->state != PHB3_STATE_FUNCTIONAL)) { PHBERR(p, "phb3_creset: wrong state %d\n", p->state); return OPAL_HARDWARE; } /* Block PCI-CFG access */ p->flags |= PHB3_CFG_BLOCKED; if (assert == OPAL_ASSERT_RESET) { PHBINF(p, "Starting PHB reset sequence\n"); return phb3_sm_complete_reset(p); } else { /* Restore bus numbers for bridges */ p->flags |= PHB3_RESTORE_BUS_NUM; return phb3_sm_hot_reset(p); } } static int64_t phb3_poll(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); uint64_t now = mftb(); if (p->state == PHB3_STATE_FUNCTIONAL) return OPAL_SUCCESS; /* Check timer */ if (p->delay_tgt_tb && tb_compare(now, p->delay_tgt_tb) == TB_ABEFOREB) return p->delay_tgt_tb - now; /* Expired (or not armed), clear it */ p->delay_tgt_tb = 0; /* Dispatch to the right state machine */ switch(p->state) { case PHB3_STATE_HRESET_DELAY: case PHB3_STATE_HRESET_DELAY2: return phb3_sm_hot_reset(p); case PHB3_STATE_FRESET_START: case PHB3_STATE_FRESET_ASSERT_DELAY: case PHB3_STATE_FRESET_DEASSERT_DELAY: return phb3_sm_fundamental_reset(p); case PHB3_STATE_CRESET_WAIT_CQ: case PHB3_STATE_CRESET_REINIT: case PHB3_STATE_CRESET_FRESET: return phb3_sm_complete_reset(p); case PHB3_STATE_WAIT_LINK_ELECTRICAL: case PHB3_STATE_WAIT_LINK: return phb3_sm_link_poll(p); default: PHBDBG(p, "phb3_poll: wrong state %d\n", p->state); break; } /* Unknown state, could be a HW error */ return OPAL_HARDWARE; } static int64_t phb3_eeh_freeze_status(struct phb *phb, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint16_t *severity, uint64_t *phb_status) { struct phb3 *p = phb_to_phb3(phb); uint64_t peev_bit = PPC_BIT(pe_number & 0x3f); uint64_t peev, pesta, pestb; /* Defaults: not frozen */ *freeze_state = OPAL_EEH_STOPPED_NOT_FROZEN; *pci_error_type = OPAL_EEH_NO_ERROR; /* Check dead */ if (p->state == PHB3_STATE_BROKEN) { *freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE; *pci_error_type = OPAL_EEH_PHB_ERROR; if (severity) *severity = OPAL_EEH_SEV_PHB_DEAD; return OPAL_HARDWARE; } /* Check fence and CAPP recovery */ if (phb3_fenced(p) || (p->flags & PHB3_CAPP_RECOVERY)) { *freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE; *pci_error_type = OPAL_EEH_PHB_ERROR; if (severity) *severity = OPAL_EEH_SEV_PHB_FENCED; goto bail; } /* Check the PEEV */ phb3_ioda_sel(p, IODA2_TBL_PEEV, pe_number / 64, false); peev = in_be64(p->regs + PHB_IODA_DATA0); if (!(peev & peev_bit)) return OPAL_SUCCESS; /* Indicate that we have an ER pending */ phb3_set_err_pending(p, true); if (severity) *severity = OPAL_EEH_SEV_PE_ER; /* Read the PESTA & PESTB */ phb3_ioda_sel(p, IODA2_TBL_PESTA, pe_number, false); pesta = in_be64(p->regs + PHB_IODA_DATA0); phb3_ioda_sel(p, IODA2_TBL_PESTB, pe_number, false); pestb = in_be64(p->regs + PHB_IODA_DATA0); /* Convert them */ if (pesta & IODA2_PESTA_MMIO_FROZEN) *freeze_state |= OPAL_EEH_STOPPED_MMIO_FREEZE; if (pestb & IODA2_PESTB_DMA_STOPPED) *freeze_state |= OPAL_EEH_STOPPED_DMA_FREEZE; bail: if (phb_status) phb3_read_phb_status(p, (struct OpalIoPhb3ErrorData *)phb_status); return OPAL_SUCCESS; } static int64_t phb3_eeh_freeze_clear(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct phb3 *p = phb_to_phb3(phb); uint64_t err, peev[4]; int32_t i; bool frozen_pe = false; if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; /* Summary. If nothing, move to clearing the PESTs which can * contain a freeze state from a previous error or simply set * explicitely by the user */ err = in_be64(p->regs + PHB_ETU_ERR_SUMMARY); if (err == 0xffffffffffffffff) { if (phb3_fenced(p)) { PHBERR(p, "eeh_freeze_clear on fenced PHB\n"); return OPAL_HARDWARE; } } if (err != 0) phb3_err_ER_clear(p); /* * We have PEEV in system memory. It would give more performance * to access that directly. */ if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO) { phb3_ioda_sel(p, IODA2_TBL_PESTA, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, 0); } if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_DMA) { phb3_ioda_sel(p, IODA2_TBL_PESTB, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, 0); } /* Update ER pending indication */ phb3_ioda_sel(p, IODA2_TBL_PEEV, 0, true); for (i = 0; i < ARRAY_SIZE(peev); i++) { peev[i] = in_be64(p->regs + PHB_IODA_DATA0); if (peev[i]) { frozen_pe = true; break; } } if (frozen_pe) { p->err.err_src = PHB3_ERR_SRC_PHB; p->err.err_class = PHB3_ERR_CLASS_ER; p->err.err_bit = -1; phb3_set_err_pending(p, true); } else phb3_set_err_pending(p, false); return OPAL_SUCCESS; } static int64_t phb3_eeh_freeze_set(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct phb3 *p = phb_to_phb3(phb); uint64_t data; if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; if (pe_number >= PHB3_MAX_PE_NUM) return OPAL_PARAMETER; if (eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_MMIO && eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_DMA && eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_ALL) return OPAL_PARAMETER; if (eeh_action_token & OPAL_EEH_ACTION_SET_FREEZE_MMIO) { phb3_ioda_sel(p, IODA2_TBL_PESTA, pe_number, false); data = in_be64(p->regs + PHB_IODA_DATA0); data |= IODA2_PESTA_MMIO_FROZEN; out_be64(p->regs + PHB_IODA_DATA0, data); } if (eeh_action_token & OPAL_EEH_ACTION_SET_FREEZE_DMA) { phb3_ioda_sel(p, IODA2_TBL_PESTB, pe_number, false); data = in_be64(p->regs + PHB_IODA_DATA0); data |= IODA2_PESTB_DMA_STOPPED; out_be64(p->regs + PHB_IODA_DATA0, data); } return OPAL_SUCCESS; } static int64_t phb3_eeh_next_error(struct phb *phb, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity) { struct phb3 *p = phb_to_phb3(phb); uint64_t fir, peev[4]; uint32_t cfg32; int32_t i, j; /* If the PHB is broken, we needn't go forward */ if (p->state == PHB3_STATE_BROKEN) { *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_DEAD; return OPAL_SUCCESS; } if ((p->flags & PHB3_CAPP_RECOVERY)) { *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_FENCED; return OPAL_SUCCESS; } /* * Check if we already have pending errors. If that's * the case, then to get more information about the * pending errors. Here we try PBCQ prior to PHB. */ if (phb3_err_pending(p) && !phb3_err_check_pbcq(p) && !phb3_err_check_lem(p)) phb3_set_err_pending(p, false); /* Clear result */ *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; *first_frozen_pe = (uint64_t)-1; /* Check frozen PEs */ if (!phb3_err_pending(p)) { phb3_ioda_sel(p, IODA2_TBL_PEEV, 0, true); for (i = 0; i < ARRAY_SIZE(peev); i++) { peev[i] = in_be64(p->regs + PHB_IODA_DATA0); if (peev[i]) { p->err.err_src = PHB3_ERR_SRC_PHB; p->err.err_class = PHB3_ERR_CLASS_ER; p->err.err_bit = -1; phb3_set_err_pending(p, true); break; } } } /* Mapping errors */ if (phb3_err_pending(p)) { /* * If the frozen PE is caused by a malfunctioning TLP, we * need reset the PHB. So convert ER to PHB-fatal error * for the case. */ if (p->err.err_class == PHB3_ERR_CLASS_ER) { fir = phb3_read_reg_asb(p, PHB_LEM_FIR_ACCUM); if (fir & PPC_BIT(60)) { phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_UE_STATUS, &cfg32); if (cfg32 & PCIECAP_AER_UE_MALFORMED_TLP) p->err.err_class = PHB3_ERR_CLASS_FENCED; } } switch (p->err.err_class) { case PHB3_ERR_CLASS_DEAD: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_DEAD; break; case PHB3_ERR_CLASS_FENCED: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_FENCED; break; case PHB3_ERR_CLASS_ER: *pci_error_type = OPAL_EEH_PE_ERROR; *severity = OPAL_EEH_SEV_PE_ER; phb3_ioda_sel(p, IODA2_TBL_PEEV, 0, true); for (i = 0; i < ARRAY_SIZE(peev); i++) peev[i] = in_be64(p->regs + PHB_IODA_DATA0); for (i = ARRAY_SIZE(peev) - 1; i >= 0; i--) { for (j = 0; j < 64; j++) { if (peev[i] & PPC_BIT(j)) { *first_frozen_pe = i * 64 + j; break; } } if (*first_frozen_pe != (uint64_t)(-1)) break; } /* No frozen PE ? */ if (*first_frozen_pe == (uint64_t)-1) { *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; phb3_set_err_pending(p, false); } break; case PHB3_ERR_CLASS_INF: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_INF; break; default: *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; phb3_set_err_pending(p, false); } } return OPAL_SUCCESS; } static int64_t phb3_err_inject_finalize(struct phb3 *p, uint64_t addr, uint64_t mask, uint64_t ctrl, bool is_write) { if (is_write) ctrl |= PHB_PAPR_ERR_INJ_CTL_WR; else ctrl |= PHB_PAPR_ERR_INJ_CTL_RD; out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, addr); out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, mask); out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, ctrl); return OPAL_SUCCESS; } static int64_t phb3_err_inject_mem32(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t base, len, segstart, segsize; uint64_t a, m; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_OUTB; uint32_t index; segsize = (M32_PCI_SIZE / PHB3_MAX_PE_NUM); a = base = len = 0x0ull; for (index = 0; index < PHB3_MAX_PE_NUM; index++) { if (GETFIELD(IODA2_M32DT_PE, p->m32d_cache[index]) != pe_no) continue; /* Obviously, we can't support discontiguous segments. * We have to pick the first batch of contiguous segments * for that case */ segstart = p->mm1_base + segsize * index; if (!len) { base = segstart; len = segsize; } else if ((base + len) == segstart) { len += segsize; } /* Check the specified address is valid one */ if (addr >= segstart && addr < (segstart + segsize)) { a = addr; break; } } /* No MM32 segments assigned to the PE */ if (!len) return OPAL_PARAMETER; /* Specified address is out of range */ if (!a) { a = base; len = len & ~(len - 1); m = ~(len - 1); } else { m = mask; } a = SETFIELD(PHB_PAPR_ERR_INJ_ADDR_MMIO, 0x0ull, a); m = SETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, 0x0ull, m); return phb3_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t phb3_err_inject_mem64(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t base, len, segstart, segsize; uint64_t cache, a, m; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_OUTB; uint32_t index, s_index, e_index; /* By default, the PE is PCI device dependent one */ s_index = 0; e_index = ARRAY_SIZE(p->m64b_cache) - 2; for (index = 0; index < RTT_TABLE_ENTRIES; index++) { if (p->rte_cache[index] != pe_no) continue; if (index + 8 >= RTT_TABLE_ENTRIES) break; /* PCI bus dependent PE */ if (p->rte_cache[index + 8] == pe_no) { s_index = e_index = ARRAY_SIZE(p->m64b_cache) - 1; break; } } a = base = len = 0x0ull; for (index = s_index; !len && index <= e_index; index++) { cache = p->m64b_cache[index]; if (!(cache & IODA2_M64BT_ENABLE)) continue; if (cache & IODA2_M64BT_SINGLE_PE) { if (GETFIELD(IODA2_M64BT_PE_HI, cache) != (pe_no >> 5) || GETFIELD(IODA2_M64BT_PE_LOW, cache) != (pe_no & 0x1f)) continue; segstart = GETFIELD(IODA2_M64BT_SINGLE_BASE, cache); segstart <<= 25; /* 32MB aligned */ segsize = GETFIELD(IODA2_M64BT_SINGLE_MASK, cache); segsize = (0x2000000ull - segsize) << 25; } else { segstart = GETFIELD(IODA2_M64BT_BASE, cache); segstart <<= 20; /* 1MB aligned */ segsize = GETFIELD(IODA2_M64BT_MASK, cache); segsize = (0x40000000ull - segsize) << 20; segsize /= PHB3_MAX_PE_NUM; segstart = segstart + segsize * pe_no; } /* First window always wins based on the ascending * searching priority the 16 BARs have. We're using * the feature to assign resource for SRIOV VFs. */ if (!len) { base = segstart; len = segsize; } /* Specified address is valid one */ if (addr >= segstart && addr < (segstart + segsize)) { a = addr; } } /* No MM64 segments assigned to the PE */ if (!len) return OPAL_PARAMETER; /* Address specified or calculated */ if (!a) { a = base; len = len & ~(len - 1); m = ~(len - 1); } else { m = mask; } a = SETFIELD(PHB_PAPR_ERR_INJ_ADDR_MMIO, 0x0ull, a); m = SETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, 0x0ull, m); return phb3_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t phb3_err_inject_cfg(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m, prefer; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_CFG; int bdfn; bool is_bus_pe; a = 0xffffull; prefer = 0xffffull; m = PHB_PAPR_ERR_INJ_MASK_CFG_ALL; for (bdfn = 0; bdfn < RTT_TABLE_ENTRIES; bdfn++) { if (p->rte_cache[bdfn] != pe_no) continue; /* The PE can be associated with PCI bus or device */ is_bus_pe = false; if ((bdfn + 8) < RTT_TABLE_ENTRIES && p->rte_cache[bdfn + 8] == pe_no) is_bus_pe = true; /* Figure out the PCI config address */ if (prefer == 0xffffull) { if (is_bus_pe) { m = PHB_PAPR_ERR_INJ_MASK_CFG; prefer = SETFIELD(m, 0x0ull, (bdfn >> 8)); } else { m = PHB_PAPR_ERR_INJ_MASK_CFG_ALL; prefer = SETFIELD(m, 0x0ull, bdfn); } } /* Check the input address is valid or not */ if (!is_bus_pe && GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG_ALL, addr) == bdfn) { a = addr; break; } if (is_bus_pe && GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, addr) == (bdfn >> 8)) { a = addr; break; } } /* Invalid PE number */ if (prefer == 0xffffull) return OPAL_PARAMETER; /* Specified address is out of range */ if (a == 0xffffull) a = prefer; else m = mask; return phb3_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t phb3_err_inject_dma(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write, bool is_64bits) { uint32_t index, page_size; uint64_t tve, table_entries; uint64_t base, start, end, len, a, m; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_INB; /* TVE index and base address */ if (!is_64bits) { index = (pe_no << 1); base = 0x0ull; } else { index = ((pe_no << 1) + 1); base = (0x1ull << 59); } /* Raw data of table entries and page size */ tve = p->tve_cache[index]; table_entries = GETFIELD(IODA2_TVT_TCE_TABLE_SIZE, tve); table_entries = (0x1ull << (table_entries + 8)); page_size = GETFIELD(IODA2_TVT_IO_PSIZE, tve); if (!page_size && !(tve & PPC_BIT(51))) return OPAL_UNSUPPORTED; /* Check the page size */ switch (page_size) { case 0: /* bypass */ start = ((tve & (0x3ull << 10)) << 14) | ((tve & (0xffffffull << 40)) >> 40); end = ((tve & (0x3ull << 8)) << 16) | ((tve & (0xffffffull << 16)) >> 16); /* 16MB aligned size */ len = (end - start) << 24; break; case 5: /* 64KB */ len = table_entries * 0x10000ull; break; case 13: /* 16MB */ len = table_entries * 0x1000000ull; break; case 17: /* 256MB */ len = table_entries * 0x10000000ull; break; case 1: /* 4KB */ default: len = table_entries * 0x1000ull; } /* The specified address is in range */ if (addr && addr >= base && addr < (base + len)) { a = addr; m = mask; } else { a = base; len = len & ~(len - 1); m = ~(len - 1); } return phb3_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t phb3_err_inject_dma32(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { return phb3_err_inject_dma(p, pe_no, addr, mask, is_write, false); } static int64_t phb3_err_inject_dma64(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write) { return phb3_err_inject_dma(p, pe_no, addr, mask, is_write, true); } static int64_t phb3_err_inject(struct phb *phb, uint32_t pe_no, uint32_t type, uint32_t func, uint64_t addr, uint64_t mask) { struct phb3 *p = phb_to_phb3(phb); int64_t (*handler)(struct phb3 *p, uint32_t pe_no, uint64_t addr, uint64_t mask, bool is_write); bool is_write; /* How could we get here without valid RTT? */ if (!p->tbl_rtt) return OPAL_HARDWARE; /* We can't inject error to the reserved PE */ if (pe_no == PHB3_RESERVED_PE_NUM || pe_no >= PHB3_MAX_PE_NUM) return OPAL_PARAMETER; /* Clear leftover from last time */ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); switch (func) { case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_DATA: is_write = false; if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) handler = phb3_err_inject_mem64; else handler = phb3_err_inject_mem32; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_DATA: is_write = true; if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) handler = phb3_err_inject_mem64; else handler = phb3_err_inject_mem32; break; case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_DATA: is_write = false; handler = phb3_err_inject_cfg; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_DATA: is_write = true; handler = phb3_err_inject_cfg; break; case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_DATA: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_MASTER: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_TARGET: is_write = false; if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) handler = phb3_err_inject_dma64; else handler = phb3_err_inject_dma32; break; case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_DATA: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_MASTER: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET: is_write = true; if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) handler = phb3_err_inject_dma64; else handler = phb3_err_inject_dma32; break; default: return OPAL_PARAMETER; } return handler(p, pe_no, addr, mask, is_write); } static int64_t phb3_get_diag_data(struct phb *phb, void *diag_buffer, uint64_t diag_buffer_len) { struct phb3 *p = phb_to_phb3(phb); struct OpalIoPhb3ErrorData *data = diag_buffer; if (diag_buffer_len < sizeof(struct OpalIoPhb3ErrorData)) return OPAL_PARAMETER; if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; /* * Dummy check for fence so that phb3_read_phb_status knows * whether to use ASB or AIB */ phb3_fenced(p); phb3_read_phb_status(p, data); /* * We're running to here probably because of errors * (INF class). For that case, we need clear the error * explicitly. */ if (phb3_err_pending(p) && p->err.err_class == PHB3_ERR_CLASS_INF && p->err.err_src == PHB3_ERR_SRC_PHB) { phb3_err_ER_clear(p); phb3_set_err_pending(p, false); } return OPAL_SUCCESS; } static void phb3_init_capp_regs(struct phb3 *p) { uint64_t reg; xscom_read(p->chip_id, APC_MASTER_PB_CTRL, ®); reg |= PPC_BIT(3); xscom_write(p->chip_id, APC_MASTER_PB_CTRL, reg); /* Dynamically workout which PHB to connect to port 0 of the CAPP. * Here is the table from the CAPP workbook: * APC_MASTER CAPP CAPP * bits 1:3 port0 port1 * 000 disabled disabled * * 001 PHB2 disabled * * 010 PHB1 disabled * 011 PHB1 PHB2 * * 100 PHB0 disabled * 101 PHB0 PHB2 * 110 PHB0 PHB1 * * We don't use port1 so only those starred above are used. * Hence reduce table to: * PHB0 -> APC MASTER(bits 1:3) = 0b100 * PHB1 -> APC MASTER(bits 1:3) = 0b010 * PHB2 -> APC MASTER(bits 1:3) = 0b001 */ reg = 0x4000000000000000ULL >> p->index; reg |= 0x0070000000000000; xscom_write(p->chip_id, APC_MASTER_CAPI_CTRL,reg); PHBINF(p, "CAPP: port attached\n"); /* tlb and mmio */ xscom_write(p->chip_id, TRANSPORT_CONTROL, 0x4028000104000000); xscom_write(p->chip_id, CANNED_PRESP_MAP0, 0); xscom_write(p->chip_id, CANNED_PRESP_MAP1, 0xFFFFFFFF00000000); xscom_write(p->chip_id, CANNED_PRESP_MAP2, 0); /* error recovery */ xscom_write(p->chip_id, CAPP_ERR_STATUS_CTRL, 0); xscom_write(p->chip_id, FLUSH_SUE_STATE_MAP, 0x1DC20B6600000000); xscom_write(p->chip_id, CAPP_EPOCH_TIMER_CTRL, 0xC0000000FFF0FFE0); xscom_write(p->chip_id, FLUSH_UOP_CONFIG1, 0xB188280728000000); xscom_write(p->chip_id, FLUSH_UOP_CONFIG2, 0xB188400F00000000); xscom_write(p->chip_id, SNOOP_CAPI_CONFIG, 0xA1F0000000000000); } /* override some inits with CAPI defaults */ static void phb3_init_capp_errors(struct phb3 *p) { out_be64(p->regs + PHB_ERR_AIB_FENCE_ENABLE, 0xffffffdd0c80ffc0); out_be64(p->regs + PHB_OUT_ERR_AIB_FENCE_ENABLE, 0x9cf3fe08f8dc700f); out_be64(p->regs + PHB_INA_ERR_AIB_FENCE_ENABLE, 0xffff57fbff01ffde); out_be64(p->regs + PHB_INB_ERR_AIB_FENCE_ENABLE, 0xfcffe0fbff7ff0ec); } static int64_t phb3_set_capi_mode(struct phb *phb, uint64_t mode, uint64_t pe_number) { struct phb3 *p = phb_to_phb3(phb); struct proc_chip *chip = get_chip(p->chip_id); uint64_t reg; int i; u8 mask; if (!chip->capp_ucode_loaded) { PHBERR(p, "CAPP: ucode not loaded\n"); return OPAL_RESOURCE; } /* * Check if CAPP port is being used by any another PHB. * Check and set chip->capp_phb3_attached_mask atomically incase * two phb3_set_capi_mode() calls race. */ lock(&capi_lock); mask = ~(1 << p->index); if (chip->capp_phb3_attached_mask & mask) { PHBERR(p, "CAPP: port already in use by another PHB:%x\n", chip->capp_phb3_attached_mask); unlock(&capi_lock); return false; } chip->capp_phb3_attached_mask = 1 << p->index; unlock(&capi_lock); xscom_read(p->chip_id, CAPP_ERR_STATUS_CTRL, ®); if ((reg & PPC_BIT(5))) { PHBERR(p, "CAPP: recovery failed (%016llx)\n", reg); return OPAL_HARDWARE; } else if ((reg & PPC_BIT(0)) && (!(reg & PPC_BIT(1)))) { PHBDBG(p, "CAPP: recovery in progress\n"); return OPAL_BUSY; } xscom_read(p->chip_id, CAPP_ERR_STATUS_CTRL, ®); if ((reg & PPC_BIT(5))) { PHBERR(p, "CAPP: recovery failed (%016llx)\n", reg); return OPAL_HARDWARE; } else if ((reg & PPC_BIT(0)) && (!(reg & PPC_BIT(1)))) { PHBDBG(p, "CAPP: recovery in progress\n"); return OPAL_BUSY; } if (mode == OPAL_PHB_CAPI_MODE_PCIE) return OPAL_UNSUPPORTED; if (mode == OPAL_PHB_CAPI_MODE_SNOOP_OFF) { xscom_write(p->chip_id, SNOOP_CAPI_CONFIG, 0x0000000000000000); return OPAL_SUCCESS; } if (mode == OPAL_PHB_CAPI_MODE_SNOOP_ON) { xscom_write(p->chip_id, CAPP_ERR_STATUS_CTRL, 0x0000000000000000); xscom_write(p->chip_id, SNOOP_CAPI_CONFIG, 0xA1F0000000000000); return OPAL_SUCCESS; } if (mode != OPAL_PHB_CAPI_MODE_CAPI) return OPAL_UNSUPPORTED; xscom_read(p->chip_id, 0x9013c03, ®); if (reg & PPC_BIT(0)) { PHBDBG(p, "Already in CAPP mode\n"); } /* poll cqstat */ for (i = 0; i < 500000; i++) { xscom_read(p->chip_id, p->pe_xscom + 0xf, ®); if (!(reg & 0xC000000000000000)) break; time_wait_us(10); } if (reg & 0xC000000000000000) { PHBERR(p, "CAPP: Timeout waiting for pending transaction\n"); return OPAL_HARDWARE; } xscom_write(p->chip_id, p->spci_xscom + 0x3, 0x8000000000000000ull); /* FIXME security timer bar xscom_write(p->chip_id, p->spci_xscom + 0x4, 0x8000000000000000ull); */ /* aib mode */ xscom_read(p->chip_id, p->pci_xscom + 0xf, ®); reg &= ~PPC_BITMASK(6,7); reg |= PPC_BIT(8); reg |= PPC_BITMASK(40, 41); reg &= ~PPC_BIT(42); xscom_write(p->chip_id, p->pci_xscom + 0xf, reg); /* pci hwconf0 */ xscom_read(p->chip_id, p->pe_xscom + 0x18, ®); reg |= PPC_BIT(14); reg &= ~PPC_BIT(15); xscom_write(p->chip_id, p->pe_xscom + 0x18, reg); /* pci hwconf1 */ xscom_read(p->chip_id, p->pe_xscom + 0x19, ®); reg &= ~PPC_BITMASK(17,18); xscom_write(p->chip_id, p->pe_xscom + 0x19, reg); /* aib tx cmd cred */ xscom_read(p->chip_id, p->pci_xscom + 0xd, ®); reg &= ~PPC_BITMASK(42,46); reg |= PPC_BIT(47); xscom_write(p->chip_id, p->pci_xscom + 0xd, reg); xscom_write(p->chip_id, p->pci_xscom + 0xc, 0xff00000000000000ull); /* pci mode ctl */ xscom_read(p->chip_id, p->pe_xscom + 0xb, ®); reg |= PPC_BIT(25); xscom_write(p->chip_id, p->pe_xscom + 0xb, reg); /* set tve no translate mode allow mmio window */ memset(p->tve_cache, 0x0, sizeof(p->tve_cache)); /* Allow address range 0x0002000000000000: 0x0002FFFFFFFFFFF */ p->tve_cache[pe_number * 2] = 0x000000FFFFFF0a00ULL; phb3_ioda_sel(p, IODA2_TBL_TVT, 0, true); for (i = 0; i < ARRAY_SIZE(p->tve_cache); i++) out_be64(p->regs + PHB_IODA_DATA0, p->tve_cache[i]); /* set m64 bar to pass mmio window */ memset(p->m64b_cache, 0x0, sizeof(p->m64b_cache)); p->m64b_cache[0] = PPC_BIT(0); /*enable*/ p->m64b_cache[0] |= PPC_BIT(1); /*single pe*/ p->m64b_cache[0] |= (p->mm0_base << 12) | ((pe_number & 0x3e0) << 27); /*base and upper pe*/ p->m64b_cache[0] |= 0x3fffc000 | (pe_number & 0x1f); /*mask and lower pe*/ p->m64b_cache[1] = PPC_BIT(0); /*enable*/ p->m64b_cache[1] |= PPC_BIT(1); /*single pe*/ p->m64b_cache[1] |= (0x0002000000000000ULL << 12) | ((pe_number & 0x3e0) << 27); /*base and upper pe*/ p->m64b_cache[1] |= 0x3f000000 | (pe_number & 0x1f); /*mask and lower pe*/ phb3_ioda_sel(p, IODA2_TBL_M64BT, 0, true); for (i = 0; i < ARRAY_SIZE(p->m64b_cache); i++) out_be64(p->regs + PHB_IODA_DATA0, p->m64b_cache[i]); out_be64(p->regs + PHB_PHB3_CONFIG, PHB_PHB3C_64B_TCE_EN); out_be64(p->regs + PHB_PHB3_CONFIG, PHB_PHB3C_64BIT_MSI_EN); phb3_init_capp_errors(p); phb3_init_capp_regs(p); if (!chiptod_capp_timebase_sync(p->chip_id)) { PHBERR(p, "CAPP: Failed to sync timebase\n"); return OPAL_HARDWARE; } return OPAL_SUCCESS; } static int64_t phb3_set_capp_recovery(struct phb *phb) { struct phb3 *p = phb_to_phb3(phb); if (p->flags & PHB3_CAPP_RECOVERY) return 0; /* set opal event flag to indicate eeh condition */ opal_update_pending_evt(OPAL_EVENT_PCI_ERROR, OPAL_EVENT_PCI_ERROR); p->flags |= PHB3_CAPP_RECOVERY; return 0; } static const struct phb_ops phb3_ops = { .lock = phb3_lock, .unlock = phb3_unlock, .cfg_read8 = phb3_pcicfg_read8, .cfg_read16 = phb3_pcicfg_read16, .cfg_read32 = phb3_pcicfg_read32, .cfg_write8 = phb3_pcicfg_write8, .cfg_write16 = phb3_pcicfg_write16, .cfg_write32 = phb3_pcicfg_write32, .choose_bus = phb3_choose_bus, .device_init = phb3_device_init, .presence_detect = phb3_presence_detect, .ioda_reset = phb3_ioda_reset, .papr_errinjct_reset = phb3_papr_errinjct_reset, .pci_reinit = phb3_pci_reinit, .set_phb_mem_window = phb3_set_phb_mem_window, .phb_mmio_enable = phb3_phb_mmio_enable, .map_pe_mmio_window = phb3_map_pe_mmio_window, .map_pe_dma_window = phb3_map_pe_dma_window, .map_pe_dma_window_real = phb3_map_pe_dma_window_real, .pci_msi_eoi = phb3_pci_msi_eoi, .set_xive_pe = phb3_set_ive_pe, .get_msi_32 = phb3_get_msi_32, .get_msi_64 = phb3_get_msi_64, .set_pe = phb3_set_pe, .set_peltv = phb3_set_peltv, .link_state = phb3_link_state, .power_state = phb3_power_state, .slot_power_off = phb3_slot_power_off, .slot_power_on = phb3_slot_power_on, .hot_reset = phb3_hot_reset, .fundamental_reset = phb3_fundamental_reset, .complete_reset = phb3_complete_reset, .poll = phb3_poll, .eeh_freeze_status = phb3_eeh_freeze_status, .eeh_freeze_clear = phb3_eeh_freeze_clear, .eeh_freeze_set = phb3_eeh_freeze_set, .next_error = phb3_eeh_next_error, .err_inject = phb3_err_inject, .get_diag_data = NULL, .get_diag_data2 = phb3_get_diag_data, .set_capi_mode = phb3_set_capi_mode, .set_capp_recovery = phb3_set_capp_recovery, }; /* * We should access those registers at the stage since the * AIB isn't ready yet. */ static void phb3_setup_aib(struct phb3 *p) { /* Init_2 - AIB TX Channel Mapping Register */ phb3_write_reg_asb(p, PHB_AIB_TX_CHAN_MAPPING, 0x0211230000000000); /* Init_3 - AIB RX command credit register */ if (p->rev >= PHB3_REV_VENICE_DD20) phb3_write_reg_asb(p, PHB_AIB_RX_CMD_CRED, 0x0020000100020001); else phb3_write_reg_asb(p, PHB_AIB_RX_CMD_CRED, 0x0020000100010001); /* Init_4 - AIB rx data credit register */ if (p->rev >= PHB3_REV_VENICE_DD20) phb3_write_reg_asb(p, PHB_AIB_RX_DATA_CRED, 0x0020002000010001); else phb3_write_reg_asb(p, PHB_AIB_RX_DATA_CRED, 0x0020002000000001); /* Init_5 - AIB rx credit init timer register */ phb3_write_reg_asb(p, PHB_AIB_RX_CRED_INIT_TIMER, 0x0f00000000000000); /* Init_6 - AIB Tag Enable register */ phb3_write_reg_asb(p, PHB_AIB_TAG_ENABLE, 0xffffffff00000000); /* Init_7 - TCE Tag Enable register */ phb3_write_reg_asb(p, PHB_TCE_TAG_ENABLE, 0xffffffff00000000); } static void phb3_init_ioda2(struct phb3 *p) { /* Init_14 - LSI Source ID */ out_be64(p->regs + PHB_LSI_SOURCE_ID, SETFIELD(PHB_LSI_SRC_ID, 0ul, 0xff)); /* Init_15 - IVT BAR / Length * Init_16 - RBA BAR * - RTT BAR * Init_17 - PELT-V BAR */ out_be64(p->regs + PHB_RTT_BAR, p->tbl_rtt | PHB_RTT_BAR_ENABLE); out_be64(p->regs + PHB_PELTV_BAR, p->tbl_peltv | PHB_PELTV_BAR_ENABLE); out_be64(p->regs + PHB_IVT_BAR, p->tbl_ivt | 0x800 | PHB_IVT_BAR_ENABLE); /* DD2.0 or the subsequent chips don't have memory * resident RBA. */ if (p->rev >= PHB3_REV_MURANO_DD20) out_be64(p->regs + PHB_RBA_BAR, 0x0ul); else out_be64(p->regs + PHB_RBA_BAR, p->tbl_rba | PHB_RBA_BAR_ENABLE); /* Init_18..21 - Setup M32 */ out_be64(p->regs + PHB_M32_BASE_ADDR, p->mm1_base); out_be64(p->regs + PHB_M32_BASE_MASK, ~(M32_PCI_SIZE - 1)); out_be64(p->regs + PHB_M32_START_ADDR, M32_PCI_START); /* Init_22 - Setup PEST BAR */ out_be64(p->regs + PHB_PEST_BAR, p->tbl_pest | PHB_PEST_BAR_ENABLE); /* Init_23 - PCIE Outbound upper address */ out_be64(p->regs + PHB_M64_UPPER_BITS, 0); /* Init_24 - Interrupt represent timers * The register doesn't take effect on Murano DD1.0 */ if (p->rev >= PHB3_REV_NAPLES_DD10) out_be64(p->regs + PHB_INTREP_TIMER, 0x0014000000000000); else if (p->rev >= PHB3_REV_MURANO_DD20) out_be64(p->regs + PHB_INTREP_TIMER, 0x0004000000000000); else out_be64(p->regs + PHB_INTREP_TIMER, 0); /* Init_25 - PHB3 Configuration Register. Clear TCE cache then * configure the PHB */ out_be64(p->regs + PHB_PHB3_CONFIG, PHB_PHB3C_64B_TCE_EN); out_be64(p->regs + PHB_PHB3_CONFIG, PHB_PHB3C_M32_EN | PHB_PHB3C_32BIT_MSI_EN | PHB_PHB3C_64BIT_MSI_EN); /* Init_26 - At least 512ns delay according to spec */ time_wait_us(2); /* Init_27..36 - On-chip IODA tables init */ phb3_ioda_reset(&p->phb, false); } static bool phb3_wait_dlp_reset(struct phb3 *p) { unsigned int i; uint64_t val; /* * Firmware cannot access the UTL core regs or PCI config space * until the cores are out of DL_PGRESET. * DL_PGRESET should be polled until it is inactive with a value * of '0'. The recommended polling frequency is once every 1ms. * Firmware should poll at least 200 attempts before giving up. * MMIO Stores to the link are silently dropped by the UTL core if * the link is down. * MMIO Loads to the link will be dropped by the UTL core and will * eventually time-out and will return an all ones response if the * link is down. */ #define DLP_RESET_ATTEMPTS 40000 PHBDBG(p, "Waiting for DLP PG reset to complete...\n"); for (i = 0; i < DLP_RESET_ATTEMPTS; i++) { val = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (!(val & PHB_PCIE_DLP_TC_DL_PGRESET)) break; time_wait_us(10); } if (val & PHB_PCIE_DLP_TC_DL_PGRESET) { PHBERR(p, "Timeout waiting for DLP PG reset !\n"); return false; } return true; } /* phb3_init_rc - Initialize the Root Complex config space */ static bool phb3_init_rc_cfg(struct phb3 *p) { int64_t ecap, aercap; /* XXX Handle errors ? */ /* Init_45..46: * * Set primary bus to 0, secondary to 1 and subordinate to 0xff */ phb3_pcicfg_write32(&p->phb, 0, PCI_CFG_PRIMARY_BUS, 0x00ff0100); /* Init_47..52 * * IO and Memory base & limits are set to base > limit, which * allows all inbounds. * * XXX This has the potential of confusing the OS which might * think that nothing is forwarded downstream. We probably need * to fix this to match the IO and M32 PHB windows */ phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_IO_BASE, 0x0010); phb3_pcicfg_write32(&p->phb, 0, PCI_CFG_MEM_BASE, 0x00000010); phb3_pcicfg_write32(&p->phb, 0, PCI_CFG_PREF_MEM_BASE, 0x00000010); /* Init_53..54 - Setup bridge control enable forwarding of CORR, FATAL, * and NONFATAL errors */ phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, PCI_CFG_BRCTL_SERR_EN); /* Init_55..56 * * PCIE Device control/status, enable error reporting, disable relaxed * ordering, set MPS to 128 (see note), clear errors. * * Note: The doc recommends to set MPS to 4K. This has proved to have * some issues as it requires specific claming of MRSS on devices and * we've found devices in the field that misbehave when doing that. * * We currently leave it all to 128 bytes (minimum setting) at init * time. The generic PCIe probing later on might apply a different * value, or the kernel will, but we play it safe at early init */ if (p->ecap <= 0) { ecap = pci_find_cap(&p->phb, 0, PCI_CFG_CAP_ID_EXP); if (ecap < 0) { PHBERR(p, "Can't locate PCI-E capability\n"); return false; } p->ecap = ecap; } else { ecap = p->ecap; } phb3_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DEVSTAT, PCICAP_EXP_DEVSTAT_CE | PCICAP_EXP_DEVSTAT_NFE | PCICAP_EXP_DEVSTAT_FE | PCICAP_EXP_DEVSTAT_UE); phb3_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DEVCTL, PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT | SETFIELD(PCICAP_EXP_DEVCTL_MPS, 0, PCIE_MPS_128B)); /* Init_57..58 * * Root Control Register. Enable error reporting * * Note: Added CRS visibility. */ phb3_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_RC, PCICAP_EXP_RC_SYSERR_ON_CE | PCICAP_EXP_RC_SYSERR_ON_NFE | PCICAP_EXP_RC_SYSERR_ON_FE | PCICAP_EXP_RC_CRS_VISIBLE); /* Init_59..60 * * Device Control 2. Enable ARI fwd, set timer to RTOS timer */ phb3_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DCTL2, SETFIELD(PCICAP_EXP_DCTL2_CMPTOUT, 0, 0xf) | PCICAP_EXP_DCTL2_ARI_FWD); /* Init_61..76 * * AER inits */ aercap = pci_find_ecap(&p->phb, 0, PCIECAP_ID_AER, NULL); if (aercap < 0) { /* Shouldn't happen */ PHBERR(p, "Failed to locate AER Ecapability in bridge\n"); return false; } p->aercap = aercap; /* Clear all UE status */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_STATUS, 0xffffffff); /* Disable some error reporting as per the PHB3 spec */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_MASK, PCIECAP_AER_UE_POISON_TLP | PCIECAP_AER_UE_COMPL_TIMEOUT | PCIECAP_AER_UE_COMPL_ABORT | PCIECAP_AER_UE_ECRC); /* Report some errors as fatal */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_SEVERITY, PCIECAP_AER_UE_DLP | PCIECAP_AER_UE_SURPRISE_DOWN | PCIECAP_AER_UE_FLOW_CTL_PROT | PCIECAP_AER_UE_UNEXP_COMPL | PCIECAP_AER_UE_RECV_OVFLOW | PCIECAP_AER_UE_MALFORMED_TLP); /* Clear all CE status */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CE_STATUS, 0xffffffff); /* Disable some error reporting as per the PHB3 spec */ /* Note: When link down, also disable rcvr errors */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CE_MASK, PCIECAP_AER_CE_ADV_NONFATAL | (p->has_link ? 0 : PCIECAP_AER_CE_RECVR_ERR)); /* Enable ECRC generation & checking */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CAPCTL, PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); /* Enable reporting in root error control */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_RERR_CMD, PCIECAP_AER_RERR_CMD_FE | PCIECAP_AER_RERR_CMD_NFE | PCIECAP_AER_RERR_CMD_CE); /* Clear root error status */ phb3_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_RERR_STA, 0xffffffff); return true; } static void phb3_init_utl(struct phb3 *p) { /* Init_77..79: Clear spurrious errors and assign errors to the * right "interrupt" signal */ out_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS, 0xffffffffffffffff); out_be64(p->regs + UTL_SYS_BUS_AGENT_ERR_SEVERITY, 0x5000000000000000); out_be64(p->regs + UTL_SYS_BUS_AGENT_IRQ_EN, 0xfcc0000000000000); /* Init_80..81: Setup tag allocations * * Stick to HW defaults. May differs between PHB implementations */ /* Init_82: PCI Express port control * SW283991: Set Outbound Non-Posted request timeout to 16ms (RTOS). */ out_be64(p->regs + UTL_PCIE_PORT_CONTROL, 0x8588007000000000); /* Init_83..85: Clean & setup port errors */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xffdfffffffffffff); out_be64(p->regs + UTL_PCIE_PORT_ERROR_SEV, 0x5039000000000000); if (p->has_link) out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad5a800000000000); else out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad42800000000000); /* Init_86 : Cleanup RC errors */ out_be64(p->regs + UTL_RC_STATUS, 0xffffffffffffffff); } static void phb3_init_errors(struct phb3 *p) { /* Init_88: LEM Error Mask : Temporarily disable error interrupts */ out_be64(p->regs + PHB_LEM_ERROR_MASK, 0xffffffffffffffff); /* Init_89..97: Disable all error interrupts until end of init */ out_be64(p->regs + PHB_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_ERR_LEM_ENABLE, 0xffffffffffffffff); out_be64(p->regs + PHB_ERR_FREEZE_ENABLE, 0x0000000080800000); out_be64(p->regs + PHB_ERR_AIB_FENCE_ENABLE, 0xffffffdd0c00ffc0); out_be64(p->regs + PHB_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_98_106: Configure MMIO error traps & clear old state * * Don't enable BAR multi-hit detection in bit 41. */ out_be64(p->regs + PHB_OUT_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_OUT_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR_LEM_ENABLE, 0xfdffffffffbfffff); out_be64(p->regs + PHB_OUT_ERR_FREEZE_ENABLE, 0x0000420800000000); out_be64(p->regs + PHB_OUT_ERR_AIB_FENCE_ENABLE, 0x9cf3bc00f89c700f); out_be64(p->regs + PHB_OUT_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_OUT_ERR_STATUS_MASK, 0x0000000000400000); out_be64(p->regs + PHB_OUT_ERR1_STATUS_MASK, 0x0000000000400000); /* Init_107_115: Configure DMA_A error traps & clear old state */ out_be64(p->regs + PHB_INA_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_INA_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR_LEM_ENABLE, 0xffffffffffffffff); out_be64(p->regs + PHB_INA_ERR_FREEZE_ENABLE, 0xc00003a901006000); out_be64(p->regs + PHB_INA_ERR_AIB_FENCE_ENABLE, 0x3fff5452fe019fde); out_be64(p->regs + PHB_INA_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_INA_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_116_124: Configure DMA_B error traps & clear old state */ out_be64(p->regs + PHB_INB_ERR_STATUS, 0xffffffffffffffff); out_be64(p->regs + PHB_INB_ERR1_STATUS, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_LEM_ENABLE, 0xffffffffffffffff); /* * Workaround for errata HW257476, turn correctable messages into * ER freezes on Murano and Venice DD1.0 */ if (p->rev < PHB3_REV_MURANO_DD20) out_be64(p->regs + PHB_INB_ERR_FREEZE_ENABLE, 0x0000600000000070); else out_be64(p->regs + PHB_INB_ERR_FREEZE_ENABLE, 0x0000600000000060); out_be64(p->regs + PHB_INB_ERR_AIB_FENCE_ENABLE, 0xfcff80fbff7ff08c); out_be64(p->regs + PHB_INB_ERR_LOG_0, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_LOG_1, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR_STATUS_MASK, 0x0000000000000000); out_be64(p->regs + PHB_INB_ERR1_STATUS_MASK, 0x0000000000000000); /* Init_125..128: Cleanup & configure LEM */ out_be64(p->regs + PHB_LEM_FIR_ACCUM, 0x0000000000000000); out_be64(p->regs + PHB_LEM_ACTION0, 0xffffffffffffffff); out_be64(p->regs + PHB_LEM_ACTION1, 0xffffffffffffffff); out_be64(p->regs + PHB_LEM_WOF, 0x0000000000000000); } static int64_t phb3_fixup_pec_inits(struct phb3 *p) { int64_t rc; uint64_t val; /* These fixups handle some timer updates that HB doesn't yet do * to work around problems with some adapters or external drawers * (SW283991) */ /* PCI Hardware Configuration 0 Register */ rc = xscom_read(p->chip_id, p->pe_xscom + 0x18, &val); if (rc) { PHBERR(p, "Can't read CS0 !\n"); return rc; } val = val & 0x0f0fffffffffffffull; val = val | 0x1010000000000000ull; rc = xscom_write(p->chip_id, p->pe_xscom + 0x18, val); if (rc) { PHBERR(p, "Can't write CS0 !\n"); return rc; } return 0; } static void phb3_init_hw(struct phb3 *p, bool first_init) { uint64_t val; PHBDBG(p, "Initializing PHB...\n"); /* Fixups for PEC inits */ if (phb3_fixup_pec_inits(p)) { PHBERR(p, "Failed to init PEC, PHB appears broken\n"); goto failed; } /* Lift reset */ xscom_read(p->chip_id, p->spci_xscom + 1, &val);/* HW275117 */ xscom_write(p->chip_id, p->pci_xscom + 0xa, 0); /* XXX FIXME, turn that into a state machine or a worker thread */ time_wait_ms(100); /* Grab version and fit it in an int */ val = phb3_read_reg_asb(p, PHB_VERSION); if (val == 0 || val == 0xffffffffffffffff) { PHBERR(p, "Failed to read version, PHB appears broken\n"); goto failed; } p->rev = ((val >> 16) & 0x00ff0000) | (val & 0xffff); PHBDBG(p, "Core revision 0x%x\n", p->rev); /* Setup AIB credits etc... */ phb3_setup_aib(p); /* Init_8 - PCIE System Configuration Register * * Use default values, clear bit 15 (SYS_EC00_SLOT) to avoid incorrect * slot power limit message and adjust max speed based on system * config. Don't hard wire default value as some bits are different * between implementations. */ val = in_be64(p->regs + PHB_PCIE_SYSTEM_CONFIG); PHBDBG(p, "Default system config: 0x%016llx\n", val); val = SETFIELD(PHB_PCIE_SCONF_SLOT, val, 0); val = SETFIELD(PHB_PCIE_SCONF_MAXLINKSPEED, val, p->max_link_speed); out_be64(p->regs + PHB_PCIE_SYSTEM_CONFIG, val); PHBDBG(p, "New system config : 0x%016llx\n", in_be64(p->regs + PHB_PCIE_SYSTEM_CONFIG)); /* Init_9..12 - PCIE DLP Lane EQ control */ if (p->lane_eq) { out_be64(p->regs + PHB_PCIE_LANE_EQ_CNTL0, be64_to_cpu(p->lane_eq[0])); out_be64(p->regs + PHB_PCIE_LANE_EQ_CNTL1, be64_to_cpu(p->lane_eq[1])); out_be64(p->regs + PHB_PCIE_LANE_EQ_CNTL2, be64_to_cpu(p->lane_eq[2])); out_be64(p->regs + PHB_PCIE_LANE_EQ_CNTL3, be64_to_cpu(p->lane_eq[3])); } /* Init_XX - (PHB2 errata) * * Set proper credits, needs adjustment due to wrong defaults * on PHB2 before we lift the reset. This only applies to Murano * and Venice */ if (p->index == 2 && p->rev < PHB3_REV_NAPLES_DD10) out_be64(p->regs + PHB_PCIE_SYS_LINK_INIT, 0x9008133332120000); /* Init_13 - PCIE Reset */ /* * Lift the PHB resets but not PERST, this will be lifted * later by the initial PERST state machine */ PHBDBG(p, "PHB_RESET is 0x%016llx\n", in_be64(p->regs + PHB_RESET)); out_be64(p->regs + PHB_RESET, 0xd000000000000000); /* Architected IODA2 inits */ phb3_init_ioda2(p); /* Init_37..42 - Clear UTL & DLP error logs */ out_be64(p->regs + PHB_PCIE_UTL_ERRLOG1, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG2, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG3, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG4, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_DLP_ERRLOG1, 0xffffffffffffffff); out_be64(p->regs + PHB_PCIE_DLP_ERRLOG2, 0xffffffffffffffff); /* Init_43 - Wait for UTL core to come out of reset */ if (!phb3_wait_dlp_reset(p)) goto failed; /* Init_44 - Clear port status */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xffffffffffffffff); /* Init_45..76: Init root complex config space */ if (!phb3_init_rc_cfg(p)) goto failed; /* Init_77..86 : Init UTL */ phb3_init_utl(p); /* * Init_87: PHB Control register. Various PHB settings * Enable IVC for Murano DD2.0 or later one */ #ifdef IVT_TABLE_IVE_16B val = 0xf3a80e4b00000000; #else val = 0xf3a80ecb00000000; #endif if (p->rev >= PHB3_REV_MURANO_DD20) val |= 0x0000010000000000; if (first_init && p->rev >= PHB3_REV_NAPLES_DD10) { /* Enable 32-bit bypass support on Naples and tell the OS * about it */ val |= 0x0010000000000000; dt_add_property(p->phb.dt_node, "ibm,32-bit-bypass-supported", NULL, 0); } out_be64(p->regs + PHB_CONTROL, val); /* Init_88..128 : Setup error registers */ phb3_init_errors(p); /* Init_129: Read error summary */ val = in_be64(p->regs + PHB_ETU_ERR_SUMMARY); if (val) { PHBERR(p, "Errors detected during PHB init: 0x%16llx\n", val); goto failed; } /* NOTE: At this point the spec waits for the link to come up. We * don't bother as we are doing a PERST soon. */ /* XXX I don't know why the spec does this now and not earlier, so * to be sure to get it right we might want to move it to the freset * state machine, though the generic PCI layer will probably do * this anyway (ie, enable MEM, etc... in the RC) * * Note:The spec enables IO but PHB3 doesn't do IO space .... so we * leave that clear. */ phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_CMD, PCI_CFG_CMD_MEM_EN | PCI_CFG_CMD_BUS_MASTER_EN | PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN); /* Clear errors */ phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_STAT, PCI_CFG_STAT_SENT_TABORT | PCI_CFG_STAT_RECV_TABORT | PCI_CFG_STAT_RECV_MABORT | PCI_CFG_STAT_SENT_SERR | PCI_CFG_STAT_RECV_PERR); /* Init_136 - Re-enable error interrupts */ /* TBD: Should we mask any of these for PERST ? */ out_be64(p->regs + PHB_ERR_IRQ_ENABLE, 0x0000002280b80000); out_be64(p->regs + PHB_OUT_ERR_IRQ_ENABLE, 0x600c42fc042080f0); out_be64(p->regs + PHB_INA_ERR_IRQ_ENABLE, 0xc000a3a901826020); out_be64(p->regs + PHB_INB_ERR_IRQ_ENABLE, 0x0000600000800070); out_be64(p->regs + PHB_LEM_ERROR_MASK, 0x42498e327f502eae); /* * Init_141 - Enable DMA address speculation * * Errata#20131017: Disable speculation until Murano DD2.0 * * Note: We keep IVT speculation disabled (bit 4). It should work with * Murano DD2.0 and later but lacks sufficient testing. We will re-enable * it once that has been done. */ if (p->rev >= PHB3_REV_MURANO_DD20) out_be64(p->regs + PHB_TCE_SPEC_CTL, 0xf000000000000000); else out_be64(p->regs + PHB_TCE_SPEC_CTL, 0x0ul); /* Errata#20131017: avoid TCE queue overflow */ if (p->rev == PHB3_REV_MURANO_DD20) phb3_write_reg_asb(p, PHB_TCE_WATERMARK, 0x0003000000030302); /* Init_142 - PHB3 - Timeout Control Register 1 * SW283991: Increase timeouts */ out_be64(p->regs + PHB_TIMEOUT_CTRL1, 0x1715152016200000); /* Init_143 - PHB3 - Timeout Control Register 2 */ out_be64(p->regs + PHB_TIMEOUT_CTRL2, 0x2320d71600000000); /* Mark the PHB as functional which enables all the various sequences */ p->state = PHB3_STATE_FUNCTIONAL; PHBDBG(p, "Initialization complete\n"); return; failed: PHBERR(p, "Initialization failed\n"); p->state = PHB3_STATE_BROKEN; } static void phb3_allocate_tables(struct phb3 *p) { uint16_t *rte; uint32_t i; /* XXX Our current memalign implementation sucks, * * It will do the job, however it doesn't support freeing * the memory and wastes space by always allocating twice * as much as requested (size + alignment) */ p->tbl_rtt = (uint64_t)local_alloc(p->chip_id, RTT_TABLE_SIZE, RTT_TABLE_SIZE); assert(p->tbl_rtt); rte = (uint16_t *)(p->tbl_rtt); for (i = 0; i < RTT_TABLE_ENTRIES; i++, rte++) *rte = PHB3_RESERVED_PE_NUM; p->tbl_peltv = (uint64_t)local_alloc(p->chip_id, PELTV_TABLE_SIZE, PELTV_TABLE_SIZE); assert(p->tbl_peltv); memset((void *)p->tbl_peltv, 0, PELTV_TABLE_SIZE); p->tbl_pest = (uint64_t)local_alloc(p->chip_id, PEST_TABLE_SIZE, PEST_TABLE_SIZE); assert(p->tbl_pest); memset((void *)p->tbl_pest, 0, PEST_TABLE_SIZE); p->tbl_ivt = (uint64_t)local_alloc(p->chip_id, IVT_TABLE_SIZE, IVT_TABLE_SIZE); assert(p->tbl_ivt); memset((void *)p->tbl_ivt, 0, IVT_TABLE_SIZE); p->tbl_rba = (uint64_t)local_alloc(p->chip_id, RBA_TABLE_SIZE, RBA_TABLE_SIZE); assert(p->tbl_rba); memset((void *)p->tbl_rba, 0, RBA_TABLE_SIZE); } static void phb3_add_properties(struct phb3 *p) { struct dt_node *np = p->phb.dt_node; uint32_t lsibase, icsp = get_ics_phandle(); uint64_t m32b, m64b, m64s, reg, tkill; reg = cleanup_addr((uint64_t)p->regs); /* Add various properties that HB doesn't have to * add, some of them simply because they result from * policy decisions made in skiboot rather than in HB * such as the MMIO windows going to PCI, interrupts, * etc... */ dt_add_property_cells(np, "#address-cells", 3); dt_add_property_cells(np, "#size-cells", 2); dt_add_property_cells(np, "#interrupt-cells", 1); dt_add_property_cells(np, "bus-range", 0, 0xff); dt_add_property_cells(np, "clock-frequency", 0x200, 0); /* ??? */ dt_add_property_cells(np, "interrupt-parent", icsp); /* XXX FIXME: add slot-name */ //dt_property_cell("bus-width", 8); /* Figure it out from VPD ? */ /* "ranges", we only expose M32 (PHB3 doesn't do IO) * * Note: The kernel expects us to have chopped of 64k from the * M32 size (for the 32-bit MSIs). If we don't do that, it will * get confused (OPAL does it) */ m32b = cleanup_addr(p->mm1_base); m64b = cleanup_addr(p->mm0_base); m64s = p->mm0_size; dt_add_property_cells(np, "ranges", /* M32 space */ 0x02000000, 0x00000000, M32_PCI_START, hi32(m32b), lo32(m32b), 0, M32_PCI_SIZE - 0x10000); /* XXX FIXME: add opal-memwin32, dmawins, etc... */ dt_add_property_cells(np, "ibm,opal-m64-window", hi32(m64b), lo32(m64b), hi32(m64b), lo32(m64b), hi32(m64s), lo32(m64s)); dt_add_property(np, "ibm,opal-single-pe", NULL, 0); //dt_add_property_cells(np, "ibm,opal-msi-ports", 2048); dt_add_property_cells(np, "ibm,opal-num-pes", 256); dt_add_property_cells(np, "ibm,opal-reserved-pe", PHB3_RESERVED_PE_NUM); dt_add_property_cells(np, "ibm,opal-msi-ranges", p->base_msi, PHB3_MSI_IRQ_COUNT); tkill = reg + PHB_TCE_KILL; dt_add_property_cells(np, "ibm,opal-tce-kill", hi32(tkill), lo32(tkill)); /* * Indicate to Linux that the architected IODA2 MSI EOI method * is supported */ dt_add_property_string(np, "ibm,msi-eoi-method", "ioda2"); /* Indicate to Linux that CAPP timebase sync is supported */ dt_add_property_string(np, "ibm,capp-timebase-sync", NULL); /* The interrupt maps will be generated in the RC node by the * PCI code based on the content of this structure: */ lsibase = p->base_lsi; p->phb.lstate.int_size = 1; p->phb.lstate.int_val[0][0] = lsibase + PHB3_LSI_PCIE_INTA; p->phb.lstate.int_val[1][0] = lsibase + PHB3_LSI_PCIE_INTB; p->phb.lstate.int_val[2][0] = lsibase + PHB3_LSI_PCIE_INTC; p->phb.lstate.int_val[3][0] = lsibase + PHB3_LSI_PCIE_INTD; p->phb.lstate.int_parent[0] = icsp; p->phb.lstate.int_parent[1] = icsp; p->phb.lstate.int_parent[2] = icsp; p->phb.lstate.int_parent[3] = icsp; /* Indicators for variable tables */ dt_add_property_cells(np, "ibm,opal-rtt-table", hi32(p->tbl_rtt), lo32(p->tbl_rtt), RTT_TABLE_SIZE); dt_add_property_cells(np, "ibm,opal-peltv-table", hi32(p->tbl_peltv), lo32(p->tbl_peltv), PELTV_TABLE_SIZE); dt_add_property_cells(np, "ibm,opal-pest-table", hi32(p->tbl_pest), lo32(p->tbl_pest), PEST_TABLE_SIZE); dt_add_property_cells(np, "ibm,opal-ivt-table", hi32(p->tbl_ivt), lo32(p->tbl_ivt), IVT_TABLE_SIZE); dt_add_property_cells(np, "ibm,opal-ive-stride", IVT_TABLE_STRIDE); dt_add_property_cells(np, "ibm,opal-rba-table", hi32(p->tbl_rba), lo32(p->tbl_rba), RBA_TABLE_SIZE); } static bool phb3_calculate_windows(struct phb3 *p) { const struct dt_property *prop; /* Get PBCQ MMIO windows from device-tree */ prop = dt_require_property(p->phb.dt_node, "ibm,mmio-window", -1); assert(prop->len >= (2 * sizeof(uint64_t))); p->mm0_base = ((const uint64_t *)prop->prop)[0]; p->mm0_size = ((const uint64_t *)prop->prop)[1]; if (prop->len > 16) { p->mm1_base = ((const uint64_t *)prop->prop)[2]; p->mm1_size = ((const uint64_t *)prop->prop)[3]; } /* Sort them so that 0 is big and 1 is small */ if (p->mm1_size && p->mm1_size > p->mm0_size) { uint64_t b = p->mm0_base; uint64_t s = p->mm0_size; p->mm0_base = p->mm1_base; p->mm0_size = p->mm1_size; p->mm1_base = b; p->mm1_size = s; } /* If 1 is too small, ditch it */ if (p->mm1_size < M32_PCI_SIZE) p->mm1_size = 0; /* If 1 doesn't exist, carve it out of 0 */ if (p->mm1_size == 0) { p->mm0_size /= 2; p->mm1_base = p->mm0_base + p->mm0_size; p->mm1_size = p->mm0_size; } /* Crop mm1 to our desired size */ if (p->mm1_size > M32_PCI_SIZE) p->mm1_size = M32_PCI_SIZE; return true; } static void phb3_create(struct dt_node *np) { const struct dt_property *prop; struct phb3 *p = zalloc(sizeof(struct phb3)); size_t lane_eq_len; struct dt_node *iplp; char *path; assert(p); /* Populate base stuff */ p->index = dt_prop_get_u32(np, "ibm,phb-index"); p->chip_id = dt_prop_get_u32(np, "ibm,chip-id"); p->regs = (void *)dt_get_address(np, 0, NULL); p->base_msi = PHB3_MSI_IRQ_BASE(p->chip_id, p->index); p->base_lsi = PHB3_LSI_IRQ_BASE(p->chip_id, p->index); p->phb.dt_node = np; p->phb.ops = &phb3_ops; p->phb.phb_type = phb_type_pcie_v3; p->phb.scan_map = 0x1; /* Only device 0 to scan */ p->max_link_speed = dt_prop_get_u32_def(np, "ibm,max-link-speed", 3); p->state = PHB3_STATE_UNINITIALIZED; if (!phb3_calculate_windows(p)) return; /* Get the various XSCOM register bases from the device-tree */ prop = dt_require_property(np, "ibm,xscom-bases", 3 * sizeof(uint32_t)); p->pe_xscom = ((const uint32_t *)prop->prop)[0]; p->spci_xscom = ((const uint32_t *)prop->prop)[1]; p->pci_xscom = ((const uint32_t *)prop->prop)[2]; /* * We skip the initial PERST assertion requested by the generic code * when doing a cold boot because we are coming out of cold boot already * so we save boot time that way. The PERST state machine will still * handle waiting for the link to come up, it will just avoid actually * asserting & deasserting the PERST output * * For a hot IPL, we still do a PERST * * Note: In absence of property (ie, FSP-less), we stick to the old * behaviour and set skip_perst to true */ p->skip_perst = true; /* Default */ iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params"); if (iplp) { const char *ipl_type = dt_prop_get_def(iplp, "cec-major-type", NULL); if (ipl_type && (!strcmp(ipl_type, "hot"))) p->skip_perst = false; } /* By default link is assumed down */ p->has_link = false; /* We register the PHB before we initialize it so we * get a useful OPAL ID for it */ pci_register_phb(&p->phb); /* Hello ! */ path = dt_get_path(np); PHBINF(p, "Found %s @%p\n", path, p->regs); PHBINF(p, " M32 [0x%016llx..0x%016llx]\n", p->mm1_base, p->mm1_base + p->mm1_size - 1); PHBINF(p, " M64 [0x%016llx..0x%016llx]\n", p->mm0_base, p->mm0_base + p->mm0_size - 1); free(path); /* Find base location code from root node */ p->phb.base_loc_code = dt_prop_get_def(dt_root, "ibm,io-base-loc-code", NULL); if (!p->phb.base_loc_code) PHBERR(p, "Base location code not found !\n"); /* Check for lane equalization values from HB or HDAT */ p->lane_eq = dt_prop_get_def_size(np, "ibm,lane-eq", NULL, &lane_eq_len); if (p->lane_eq && lane_eq_len != (8 * 4)) { PHBERR(p, "Device-tree has ibm,lane-eq with wrong len %ld\n", lane_eq_len); p->lane_eq = NULL; } if (p->lane_eq) { PHBDBG(p, "Override lane equalization settings:\n"); PHBDBG(p, " 0x%016llx 0x%016llx\n", be64_to_cpu(p->lane_eq[0]), be64_to_cpu(p->lane_eq[1])); PHBDBG(p, " 0x%016llx 0x%016llx\n", be64_to_cpu(p->lane_eq[2]), be64_to_cpu(p->lane_eq[3])); } /* * Grab CEC IO VPD load info from the root of the device-tree, * on P8 there's a single such VPD for the whole machine */ prop = dt_find_property(dt_root, "ibm,io-vpd"); if (!prop) { /* LX VPD Lid not already loaded */ vpd_iohub_load(dt_root); } /* Allocate the SkiBoot internal in-memory tables for the PHB */ phb3_allocate_tables(p); phb3_add_properties(p); /* Clear IODA2 cache */ phb3_init_ioda_cache(p); /* Register interrupt sources */ register_irq_source(&phb3_msi_irq_ops, p, p->base_msi, PHB3_MSI_IRQ_COUNT); register_irq_source(&phb3_lsi_irq_ops, p, p->base_lsi, 4); #ifndef DISABLE_ERR_INTS register_irq_source(&phb3_err_lsi_irq_ops, p, p->base_lsi + PHB3_LSI_PCIE_INF, 2); #endif /* Get the HW up and running */ phb3_init_hw(p, true); /* Load capp microcode into capp unit */ capp_load_ucode(p); /* Platform additional setup */ if (platform.pci_setup_phb) platform.pci_setup_phb(&p->phb, p->index); } static void phb3_probe_pbcq(struct dt_node *pbcq) { uint32_t spci_xscom, pci_xscom, pe_xscom, gcid, pno; uint64_t val, phb_bar, bar_en; uint64_t mmio0_bar, mmio0_bmask, mmio0_sz; uint64_t mmio1_bar, mmio1_bmask, mmio1_sz; uint64_t reg[2]; uint64_t mmio_win[4]; unsigned int mmio_win_sz; struct dt_node *np; char *path; uint64_t capp_ucode_base; unsigned int max_link_speed; gcid = dt_get_chip_id(pbcq); pno = dt_prop_get_u32(pbcq, "ibm,phb-index"); path = dt_get_path(pbcq); prlog(PR_NOTICE, "Chip %d Found PBCQ%d at %s\n", gcid, pno, path); free(path); pe_xscom = dt_get_address(pbcq, 0, NULL); pci_xscom = dt_get_address(pbcq, 1, NULL); spci_xscom = dt_get_address(pbcq, 2, NULL); prlog(PR_DEBUG, "PHB3[%d:%d]: X[PE]=0x%08x X[PCI]=0x%08x" " X[SPCI]=0x%08x\n", gcid, pno, pe_xscom, pci_xscom, spci_xscom); /* Check if CAPP mode */ if (xscom_read(gcid, spci_xscom + 0x03, &val)) { prerror("PHB3[%d:%d]: Cannot read AIB CAPP ENABLE\n", gcid, pno); return; } if (val >> 63) { prerror("PHB3[%d:%d]: Ignoring bridge in CAPP mode\n", gcid, pno); return; } /* Get PE BARs, assume only 0 and 2 are used for now */ xscom_read(gcid, pe_xscom + 0x42, &phb_bar); phb_bar >>= 14; prlog(PR_DEBUG, "PHB3[%d:%d] REGS = 0x%016llx [4k]\n", gcid, pno, phb_bar); if (phb_bar == 0) { prerror("PHB3[%d:%d]: No PHB BAR set !\n", gcid, pno); return; } /* Dbl check PHB BAR */ xscom_read(gcid, spci_xscom + 1, &val);/* HW275117 */ xscom_read(gcid, pci_xscom + 0x0b, &val); val >>= 14; prlog(PR_DEBUG, "PHB3[%d:%d] PCIBAR = 0x%016llx\n", gcid, pno, val); if (phb_bar != val) { prerror("PHB3[%d:%d] PCIBAR invalid, fixing up...\n", gcid, pno); xscom_read(gcid, spci_xscom + 1, &val);/* HW275117 */ xscom_write(gcid, pci_xscom + 0x0b, phb_bar << 14); } /* Check MMIO BARs */ xscom_read(gcid, pe_xscom + 0x40, &mmio0_bar); xscom_read(gcid, pe_xscom + 0x43, &mmio0_bmask); mmio0_bmask &= 0xffffffffc0000000ull; mmio0_sz = ((~mmio0_bmask) >> 14) + 1; mmio0_bar >>= 14; prlog(PR_DEBUG, "PHB3[%d:%d] MMIO0 = 0x%016llx [0x%016llx]\n", gcid, pno, mmio0_bar, mmio0_sz); xscom_read(gcid, pe_xscom + 0x41, &mmio1_bar); xscom_read(gcid, pe_xscom + 0x44, &mmio1_bmask); mmio1_bmask &= 0xffffffffc0000000ull; mmio1_sz = ((~mmio1_bmask) >> 14) + 1; mmio1_bar >>= 14; prlog(PR_DEBUG, "PHB3[%d:%d] MMIO1 = 0x%016llx [0x%016llx]\n", gcid, pno, mmio1_bar, mmio1_sz); /* Check BAR enable * * XXX BAR aren't always enabled by HB, we'll make assumptions * that BARs are valid if they value is non-0 */ xscom_read(gcid, pe_xscom + 0x45, &bar_en); prlog(PR_DEBUG, "PHB3[%d:%d] BAREN = 0x%016llx\n", gcid, pno, bar_en); /* Always enable PHB BAR */ bar_en |= 0x2000000000000000ull; /* Build MMIO windows list */ mmio_win_sz = 0; if (mmio0_bar) { mmio_win[mmio_win_sz++] = mmio0_bar; mmio_win[mmio_win_sz++] = mmio0_sz; bar_en |= 0x8000000000000000ul; } if (mmio1_bar) { mmio_win[mmio_win_sz++] = mmio1_bar; mmio_win[mmio_win_sz++] = mmio1_sz; bar_en |= 0x4000000000000000ul; } /* No MMIO windows ? Barf ! */ if (mmio_win_sz == 0) { prerror("PHB3[%d:%d]: No MMIO windows enabled !\n", gcid, pno); return; } /* Set the interrupt routing stuff, 8 relevant bits in mask * (11 bits per PHB) */ val = P8_CHIP_IRQ_PHB_BASE(gcid, pno); val = (val << 45); xscom_write(gcid, pe_xscom + 0x1a, val); xscom_write(gcid, pe_xscom + 0x1b, 0xff00000000000000ul); /* Configure LSI location to the top of the map */ xscom_write(gcid, pe_xscom + 0x1f, 0xff00000000000000ul); /* Now add IRSN message bits to BAR enable and write it */ bar_en |= 0x1800000000000000ul; xscom_write(gcid, pe_xscom + 0x45, bar_en); prlog(PR_DEBUG, "PHB3[%d:%d] NEWBAREN = 0x%016llx\n", gcid, pno, bar_en); xscom_read(gcid, pe_xscom + 0x1a, &val); prlog(PR_DEBUG, "PHB3[%d:%d] IRSNC = 0x%016llx\n", gcid, pno, val); xscom_read(gcid, pe_xscom + 0x1b, &val); prlog(PR_DEBUG, "PHB3[%d:%d] IRSNM = 0x%016llx\n", gcid, pno, val); prlog(PR_DEBUG, "PHB3[%d:%d] LSI = 0x%016llx\n", gcid, pno, val); /* Create PHB node */ reg[0] = phb_bar; reg[1] = 0x1000; np = dt_new_addr(dt_root, "pciex", reg[0]); if (!np) return; dt_add_property_strings(np, "compatible", "ibm,power8-pciex", "ibm,ioda2-phb"); dt_add_property_strings(np, "device_type", "pciex"); dt_add_property(np, "reg", reg, sizeof(reg)); /* Everything else is handled later by skiboot, we just * stick a few hints here */ dt_add_property_cells(np, "ibm,xscom-bases", pe_xscom, spci_xscom, pci_xscom); dt_add_property(np, "ibm,mmio-window", mmio_win, 8 * mmio_win_sz); dt_add_property_cells(np, "ibm,phb-index", pno); dt_add_property_cells(np, "ibm,pbcq", pbcq->phandle); dt_add_property_cells(np, "ibm,chip-id", gcid); if (dt_has_node_property(pbcq, "ibm,use-ab-detect", NULL)) dt_add_property(np, "ibm,use-ab-detect", NULL, 0); if (dt_has_node_property(pbcq, "ibm,hub-id", NULL)) dt_add_property_cells(np, "ibm,hub-id", dt_prop_get_u32(pbcq, "ibm,hub-id")); if (dt_has_node_property(pbcq, "ibm,loc-code", NULL)) { const char *lc = dt_prop_get(pbcq, "ibm,loc-code"); dt_add_property_string(np, "ibm,loc-code", lc); } if (dt_has_node_property(pbcq, "ibm,lane-eq", NULL)) { size_t leq_size; const void *leq = dt_prop_get_def_size(pbcq, "ibm,lane-eq", NULL, &leq_size); if (leq != NULL && leq_size == 4 * 8) dt_add_property(np, "ibm,lane-eq", leq, leq_size); } if (dt_has_node_property(pbcq, "ibm,capp-ucode", NULL)) { capp_ucode_base = dt_prop_get_u32(pbcq, "ibm,capp-ucode"); dt_add_property_cells(np, "ibm,capp-ucode", capp_ucode_base); } max_link_speed = dt_prop_get_u32_def(pbcq, "ibm,max-link-speed", 3); dt_add_property_cells(np, "ibm,max-link-speed", max_link_speed); dt_add_property_cells(np, "ibm,capi-flags", OPAL_PHB_CAPI_FLAG_SNOOP_CONTROL); add_chip_dev_associativity(np); } int phb3_preload_capp_ucode(void) { struct dt_node *p; struct proc_chip *chip; uint32_t index; uint64_t rc; int ret; p = dt_find_compatible_node(dt_root, NULL, "ibm,power8-pbcq"); if (!p) { printf("CAPI: WARNING: no compat thing found\n"); return OPAL_SUCCESS; } chip = get_chip(dt_get_chip_id(p)); rc = xscom_read_cfam_chipid(chip->id, &index); if (rc) { prerror("CAPP: Error reading cfam chip-id\n"); ret = OPAL_HARDWARE; return ret; } /* Keep ChipID and Major/Minor EC. Mask out the Location Code. */ index = index & 0xf0fff; /* Assert that we're preloading */ assert(capp_ucode_info.lid == NULL); capp_ucode_info.load_result = OPAL_EMPTY; capp_ucode_info.ec_level = index; /* Is the ucode preloaded like for BML? */ if (dt_has_node_property(p, "ibm,capp-ucode", NULL)) { capp_ucode_info.lid = (struct capp_lid_hdr *)(u64) dt_prop_get_u32(p, "ibm,capp-ucode"); ret = OPAL_SUCCESS; goto end; } /* If we successfully download the ucode, we leave it around forever */ capp_ucode_info.size = CAPP_UCODE_MAX_SIZE; capp_ucode_info.lid = malloc(CAPP_UCODE_MAX_SIZE); if (!capp_ucode_info.lid) { prerror("CAPP: Can't allocate space for ucode lid\n"); ret = OPAL_NO_MEM; goto end; } printf("CAPI: Preloading ucode %x\n", capp_ucode_info.ec_level); ret = start_preload_resource(RESOURCE_ID_CAPP, index, capp_ucode_info.lid, &capp_ucode_info.size); if (ret != OPAL_SUCCESS) prerror("CAPI: Failed to preload resource %d\n", ret); end: return ret; } void phb3_preload_vpd(void) { const struct dt_property *prop; prop = dt_find_property(dt_root, "ibm,io-vpd"); if (!prop) { /* LX VPD Lid not already loaded */ vpd_preload(dt_root); } } void probe_phb3(void) { struct dt_node *np; /* Look for PBCQ XSCOM nodes */ dt_for_each_compatible(dt_root, np, "ibm,power8-pbcq") phb3_probe_pbcq(np); /* Look for newly created PHB nodes */ dt_for_each_compatible(dt_root, np, "ibm,power8-pciex") phb3_create(np); } skiboot-skiboot-5.1.13/hw/prd.c000066400000000000000000000206151265204436200163020ustar00rootroot00000000000000/* Copyright 2014-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * imitations under the License. */ #include #include #include #include #include #include #include #include enum events { EVENT_ATTN = 1 << 0, EVENT_OCC_ERROR = 1 << 1, EVENT_OCC_RESET = 1 << 2, }; static uint8_t events[MAX_CHIPS]; static uint64_t ipoll_status[MAX_CHIPS]; static struct opal_prd_msg prd_msg; static bool prd_msg_inuse, prd_active; struct dt_node *prd_node; /* Locking: * * The events lock serialises access to the events, ipoll_status, * prd_msg_inuse, and prd_active variables. * * The ipoll_lock protects against concurrent updates to the ipoll registers. * * The ipoll_lock may be acquired with events_lock held. This order must * be preserved. */ static struct lock events_lock = LOCK_UNLOCKED; static struct lock ipoll_lock = LOCK_UNLOCKED; /* PRD registers */ #define PRD_IPOLL_REG_MASK 0x01020013 #define PRD_IPOLL_REG_STATUS 0x01020014 #define PRD_IPOLL_XSTOP PPC_BIT(0) /* Xstop for host/core/millicode */ #define PRD_IPOLL_RECOV PPC_BIT(1) /* Recoverable */ #define PRD_IPOLL_SPEC_ATTN PPC_BIT(2) /* Special attention */ #define PRD_IPOLL_HOST_ATTN PPC_BIT(3) /* Host attention */ #define PRD_IPOLL_MASK PPC_BITMASK(0, 3) static int queue_prd_msg_hbrt(struct opal_prd_msg *msg, void (*consumed)(void *data)) { uint64_t *buf; BUILD_ASSERT(sizeof(*msg) / sizeof(uint64_t) == 4); buf = (uint64_t *)msg; return _opal_queue_msg(OPAL_MSG_PRD, msg, consumed, 4, buf); } static int queue_prd_msg_nop(struct opal_prd_msg *msg, void (*consumed)(void *data)) { (void)msg; (void)consumed; return OPAL_UNSUPPORTED; } static int (*queue_prd_msg)(struct opal_prd_msg *msg, void (*consumed)(void *data)) = queue_prd_msg_nop; static void send_next_pending_event(void); static void prd_msg_consumed(void *data) { struct opal_prd_msg *msg = data; uint32_t proc; uint8_t event = 0; lock(&events_lock); switch (msg->hdr.type) { case OPAL_PRD_MSG_TYPE_ATTN: proc = msg->attn.proc; /* If other ipoll events have been received in the time * between prd_msg creation and consumption, we'll need to * raise a separate ATTN message for those. So, we only * clear the event if we don't have any further ipoll_status * bits. */ ipoll_status[proc] &= ~msg->attn.ipoll_status; if (!ipoll_status[proc]) event = EVENT_ATTN; break; case OPAL_PRD_MSG_TYPE_OCC_ERROR: proc = msg->occ_error.chip; event = EVENT_OCC_ERROR; break; case OPAL_PRD_MSG_TYPE_OCC_RESET: proc = msg->occ_reset.chip; event = EVENT_OCC_RESET; break; default: prlog(PR_ERR, "PRD: invalid msg consumed, type: 0x%x\n", msg->hdr.type); } if (event) events[proc] &= ~event; prd_msg_inuse = false; send_next_pending_event(); unlock(&events_lock); } static int populate_ipoll_msg(struct opal_prd_msg *msg, uint32_t proc) { uint64_t ipoll_mask; int rc; lock(&ipoll_lock); rc = xscom_read(proc, PRD_IPOLL_REG_MASK, &ipoll_mask); unlock(&ipoll_lock); if (rc) { prlog(PR_ERR, "PRD: Unable to read ipoll status (chip %d)!\n", proc); return -1; } msg->attn.proc = proc; msg->attn.ipoll_status = ipoll_status[proc]; msg->attn.ipoll_mask = ipoll_mask; return 0; } static void send_next_pending_event(void) { struct proc_chip *chip; uint32_t proc; uint8_t event; assert(!prd_msg_inuse); if (!prd_active) return; event = 0; for_each_chip(chip) { proc = chip->id; if (events[proc]) { event = events[proc]; break; } } if (!event) return; prd_msg_inuse = true; prd_msg.token = 0; prd_msg.hdr.size = sizeof(prd_msg); if (event & EVENT_ATTN) { prd_msg.hdr.type = OPAL_PRD_MSG_TYPE_ATTN; populate_ipoll_msg(&prd_msg, proc); } else if (event & EVENT_OCC_ERROR) { prd_msg.hdr.type = OPAL_PRD_MSG_TYPE_OCC_ERROR; prd_msg.occ_error.chip = proc; } else if (event & EVENT_OCC_RESET) { prd_msg.hdr.type = OPAL_PRD_MSG_TYPE_OCC_RESET; prd_msg.occ_reset.chip = proc; } queue_prd_msg(&prd_msg, prd_msg_consumed); } static void __prd_event(uint32_t proc, uint8_t event) { events[proc] |= event; if (!prd_msg_inuse) send_next_pending_event(); } static void prd_event(uint32_t proc, uint8_t event) { lock(&events_lock); __prd_event(proc, event); unlock(&events_lock); } static int __ipoll_update_mask(uint32_t proc, bool set, uint64_t bits) { uint64_t mask; int rc; rc = xscom_read(proc, PRD_IPOLL_REG_MASK, &mask); if (rc) return rc; if (set) mask |= bits; else mask &= ~bits; return xscom_write(proc, PRD_IPOLL_REG_MASK, mask); } static int ipoll_record_and_mask_pending(uint32_t proc) { uint64_t status; int rc; lock(&ipoll_lock); rc = xscom_read(proc, PRD_IPOLL_REG_STATUS, &status); status &= PRD_IPOLL_MASK; if (!rc) __ipoll_update_mask(proc, true, status); unlock(&ipoll_lock); if (!rc) ipoll_status[proc] |= status; return rc; } /* Entry point for interrupts */ void prd_psi_interrupt(uint32_t proc) { int rc; lock(&events_lock); rc = ipoll_record_and_mask_pending(proc); if (rc) prlog(PR_ERR, "PRD: Failed to update IPOLL mask\n"); __prd_event(proc, EVENT_ATTN); unlock(&events_lock); } void prd_tmgt_interrupt(uint32_t proc) { prd_event(proc, EVENT_OCC_ERROR); } void prd_occ_reset(uint32_t proc) { prd_event(proc, EVENT_OCC_RESET); } /* incoming message handlers */ static int prd_msg_handle_attn_ack(struct opal_prd_msg *msg) { int rc; lock(&ipoll_lock); rc = __ipoll_update_mask(msg->attn_ack.proc, false, msg->attn_ack.ipoll_ack & PRD_IPOLL_MASK); unlock(&ipoll_lock); if (rc) prlog(PR_ERR, "PRD: Unable to unmask ipoll!\n"); return rc; } static int prd_msg_handle_init(struct opal_prd_msg *msg) { struct proc_chip *chip; lock(&ipoll_lock); for_each_chip(chip) { __ipoll_update_mask(chip->id, false, msg->init.ipoll & PRD_IPOLL_MASK); } unlock(&ipoll_lock); /* we're transitioning from inactive to active; send any pending tmgt * interrupts */ lock(&events_lock); prd_active = true; if (!prd_msg_inuse) send_next_pending_event(); unlock(&events_lock); return OPAL_SUCCESS; } static int prd_msg_handle_fini(void) { struct proc_chip *chip; lock(&events_lock); prd_active = false; unlock(&events_lock); lock(&ipoll_lock); for_each_chip(chip) { __ipoll_update_mask(chip->id, true, PRD_IPOLL_MASK); } unlock(&ipoll_lock); return OPAL_SUCCESS; } /* Entry from the host above */ static int64_t opal_prd_msg(struct opal_prd_msg *msg) { int rc; /* fini is a little special: the kernel (which may not have the entire * opal_prd_msg definition) can send a FINI message, so we don't check * the full size */ if (msg->hdr.size >= sizeof(struct opal_prd_msg_header) && msg->hdr.type == OPAL_PRD_MSG_TYPE_FINI) return prd_msg_handle_fini(); if (msg->hdr.size != sizeof(*msg)) return OPAL_PARAMETER; switch (msg->hdr.type) { case OPAL_PRD_MSG_TYPE_INIT: rc = prd_msg_handle_init(msg); break; case OPAL_PRD_MSG_TYPE_ATTN_ACK: rc = prd_msg_handle_attn_ack(msg); break; default: rc = OPAL_UNSUPPORTED; } return rc; } void prd_init(void) { struct proc_chip *chip; /* mask everything */ lock(&ipoll_lock); for_each_chip(chip) { __ipoll_update_mask(chip->id, true, PRD_IPOLL_MASK); } unlock(&ipoll_lock); if (fsp_present()) { /* todo: FSP implementation */ queue_prd_msg = queue_prd_msg_nop; } else { queue_prd_msg = queue_prd_msg_hbrt; opal_register(OPAL_PRD_MSG, opal_prd_msg, 1); } prd_node = dt_new(opal_node, "diagnostics"); dt_add_property_strings(prd_node, "compatible", "ibm,opal-prd"); } void prd_register_reserved_memory(void) { struct mem_region *region; if (!prd_node) return; lock(&mem_region_lock); for (region = mem_region_next(NULL); region; region = mem_region_next(region)) { if (region->type != REGION_HW_RESERVED) continue; if (!region->node) continue; if (!dt_find_property(region->node, "ibm,prd-label")) { dt_add_property_string(region->node, "ibm,prd-label", region->name); } } unlock(&mem_region_lock); } skiboot-skiboot-5.1.13/hw/psi.c000066400000000000000000000544371265204436200163210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Service Processor serial console handling code */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static LIST_HEAD(psis); static u64 psi_link_timer; static u64 psi_link_timeout; static bool psi_link_poll_active; static bool psi_ext_irq_policy = EXTERNAL_IRQ_POLICY_LINUX; static void psi_register_interrupts(struct psi *psi); static void psi_activate_phb(struct psi *psi); static struct lock psi_lock = LOCK_UNLOCKED; DEFINE_LOG_ENTRY(OPAL_RC_PSI_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_PSI, OPAL_PLATFORM_FIRMWARE, OPAL_UNRECOVERABLE_ERR_LOSS_OF_FUNCTION, OPAL_NA); void psi_set_link_polling(bool active) { printf("PSI: %sing link polling\n", active ? "start" : "stopp"); psi_link_poll_active = active; } void psi_disable_link(struct psi *psi) { lock(&psi_lock); /* * Note: This can be called with the link already down but * not detected as such yet by this layer since psi_check_link_active() * operates locklessly and thus won't update the PSI structure. This * is a non-issue, the only consequence is the messages in the log * mentioning first the link having gone down then being disabled. */ if (psi->active) { u64 reg; psi->active = false; /* Mask errors in SEMR */ reg = in_be64(psi->regs + PSIHB_SEMR); reg = ((0xfffull << 36) | (0xfffull << 20)); out_be64(psi->regs + PSIHB_SEMR, reg); printf("PSI: SEMR set to %llx\n", reg); /* Reset all the error bits in PSIHB_CR and * disable FSP interrupts */ reg = in_be64(psi->regs + PSIHB_CR); reg &= ~(0x7ffull << 20); reg &= ~PSIHB_CR_PSI_LINK_ENABLE; /* flip link enable */ /* * Ensure no commands/spurious interrupts reach * the processor, by flipping the command enable. */ reg &= ~PSIHB_CR_FSP_CMD_ENABLE; reg &= ~PSIHB_CR_FSP_IRQ_ENABLE; reg &= ~PSIHB_CR_FSP_IRQ; /* Clear interrupt state too */ printf("PSI[0x%03x]: Disabling link!\n", psi->chip_id); out_be64(psi->regs + PSIHB_CR, reg); printf("PSI: PSIHB_CR (error bits) set to %llx\n", in_be64(psi->regs + PSIHB_CR)); psi_set_link_polling(true); } unlock(&psi_lock); } /* * Resetting the FSP is a multi step sequence: * 1. Read the PSIHBCR * 2. Set the PSIHBCR[6] -- write register back. * 3. Read PSIHBCR again * 4. Reset PSIHBCR[6] -- write register back. */ void psi_reset_fsp(struct psi *psi) { lock(&psi_lock); if (psi->active) { u64 reg; printf("PSI: Driving FSP reset via PSI\n"); reg = in_be64(psi->regs + PSIHB_CR); reg &= ~(0xfffull << 20); /* Reset error bits */ reg |= PSIHB_CR_FSP_RESET; /* FSP reset trigger start */ out_be64(psi->regs + PSIHB_CR, reg); printf("PSI[0x%03x]: FSP reset start PSIHBCR set to %llx\n", psi->chip_id, in_be64(psi->regs + PSIHB_CR)); reg = in_be64(psi->regs + PSIHB_CR); reg &= ~PSIHB_CR_FSP_RESET; /* Clear FSP reset bit */ out_be64(psi->regs + PSIHB_CR, reg); /* Complete reset */ printf("PSI[0x%03x]: FSP reset complete. PSIHBCR set to %llx\n", psi->chip_id, in_be64(psi->regs + PSIHB_CR)); } unlock(&psi_lock); /* Now bring down the PSI link too... */ psi_disable_link(psi); } bool psi_check_link_active(struct psi *psi) { u64 val = in_be64(psi->regs + PSIHB_CR); /* * Unlocked, used during fsp_poke_msg so we really want * to avoid fancy link re-entrancy and deadlocks here */ if (!psi->active) return false; return (val & PSIHB_CR_PSI_LINK_ENABLE) && (val & PSIHB_CR_FSP_LINK_ACTIVE); } struct psi *psi_find_link(uint32_t chip_id) { struct psi *psi; list_for_each(&psis, psi, list) { if (psi->chip_id == chip_id) return psi; } return NULL; } #define PSI_LINK_CHECK_INTERVAL 10 /* Interval in secs */ #define PSI_LINK_RECOVERY_TIMEOUT 1800 /* 30 minutes */ static void psi_link_poll(void *data __unused) { struct psi *psi; u64 now; if (!psi_link_poll_active) return; now = mftb(); if (psi_link_timer == 0 || (tb_compare(now, psi_link_timer) == TB_AAFTERB) || (tb_compare(now, psi_link_timer) == TB_AEQUALB)) { lock(&psi_lock); list_for_each(&psis, psi, list) { u64 val; if (psi->active || !psi->working) continue; val = in_be64(psi->regs + PSIHB_CR); printf("PSI[0x%03x]: Poll CR=0x%016llx\n", psi->chip_id, val); if ((val & PSIHB_CR_PSI_LINK_ENABLE) && (val & PSIHB_CR_FSP_LINK_ACTIVE)) { printf("PSI[0x%03x]: Found active link!\n", psi->chip_id); psi_link_timeout = 0; psi->active = true; psi_activate_phb(psi); psi_set_link_polling(false); unlock(&psi_lock); fsp_reinit_fsp(); return; } } if (!psi_link_timeout) psi_link_timeout = now + secs_to_tb(PSI_LINK_RECOVERY_TIMEOUT); if (tb_compare(now, psi_link_timeout) == TB_AAFTERB) { log_simple_error(&e_info(OPAL_RC_PSI_TIMEOUT), "PSI: Link timeout -- loss of FSP\n"); /* Reset the link timeout and continue looking */ psi_link_timeout = 0; } /* Poll every 10 seconds */ psi_link_timer = now + secs_to_tb(PSI_LINK_CHECK_INTERVAL); unlock(&psi_lock); } } void psi_enable_fsp_interrupt(struct psi *psi) { if (!psi->working) return; /* Enable FSP interrupts in the GXHB */ lock(&psi_lock); out_be64(psi->regs + PSIHB_CR, in_be64(psi->regs + PSIHB_CR) | PSIHB_CR_FSP_IRQ_ENABLE); unlock(&psi_lock); } /* Multiple bits can be set on errors */ static void decode_psihb_error(u64 val) { if (val & PSIHB_CR_PSI_ERROR) printf("PSI: PSI Reported Error\n"); if (val & PSIHB_CR_PSI_LINK_INACTIVE) printf("PSI: PSI Link Inactive Transition\n"); if (val & PSIHB_CR_FSP_ACK_TIMEOUT) printf("PSI: FSP Ack Timeout\n"); if (val & PSIHB_CR_MMIO_LOAD_TIMEOUT) printf("PSI: MMIO Load Timeout\n"); if (val & PSIHB_CR_MMIO_LENGTH_ERROR) printf("PSI: MMIO Length Error\n"); if (val & PSIHB_CR_MMIO_ADDRESS_ERROR) printf("PSI: MMIO Address Error\n"); if (val & PSIHB_CR_MMIO_TYPE_ERROR) printf("PSI: MMIO Type Error\n"); if (val & PSIHB_CR_UE) printf("PSI: UE Detected\n"); if (val & PSIHB_CR_PARITY_ERROR) printf("PSI: Internal Parity Error\n"); if (val & PSIHB_CR_SYNC_ERR_ALERT1) printf("PSI: Sync Error Alert1\n"); if (val & PSIHB_CR_SYNC_ERR_ALERT2) printf("PSI: Sync Error Alert2\n"); if (val & PSIHB_CR_FSP_COMMAND_ERROR) printf("PSI: FSP Command Error\n"); } static void handle_psi_interrupt(struct psi *psi, u64 val) { printf("PSI[0x%03x]: PSI mgmnt interrupt CR=0x%016llx\n", psi->chip_id, val); if (val & (0xfffull << 20)) { decode_psihb_error(val); psi_disable_link(psi); } else if (val & (0x1full << 11)) printf("PSI: FSP error detected\n"); } /* TODO: Determine which of these needs to be handled by powernv */ static void handle_extra_interrupt(struct psi *psi) { u64 val; val = in_be64(psi->regs + PSIHB_IRQ_STATUS); /* * Decode interrupt type, call appropriate handlers * when available. */ if (val & PSIHB_IRQ_STAT_OCC) occ_interrupt(psi->chip_id); if (val & PSIHB_IRQ_STAT_FSI) printf("PSI: FSI irq received\n"); if (val & PSIHB_IRQ_STAT_LPC) { lpc_interrupt(psi->chip_id); /* * i2c interrupts are ORed with the LPC ones on * Murano DD2.1 and Venice DD2.0 */ p8_i2c_interrupt(psi->chip_id); } if (val & PSIHB_IRQ_STAT_LOCAL_ERR) prd_psi_interrupt(psi->chip_id); if (val & PSIHB_IRQ_STAT_HOST_ERR) { if (platform.external_irq) platform.external_irq(psi->chip_id); } else { u64 xivr; /* * The way our FPGA "pulses" the external interrupt * on BMC machines means we might not see it in the * status register anymore, so look at the latch in * the XIVR */ xivr = in_be64(psi->regs + PSIHB_XIVR_HOST_ERR); if (xivr & PPC_BIT(39) && platform.external_irq) platform.external_irq(psi->chip_id); } /* * TODO: Per Vicente Chung, CRESPs don't generate interrupts, * and are just informational. Need to define the policy * to handle them. */ } static void psi_spurious_fsp_irq(struct psi *psi) { u64 reg, bit; prerror("PSI: Spurious interrupt, attempting clear\n"); if (proc_gen == proc_gen_p8) { reg = PSIHB_XSCOM_P8_HBCSR_CLR; bit = PSIHB_XSCOM_P8_HBSCR_FSP_IRQ; } else { reg = PSIHB_XSCOM_P7_HBCSR_CLR; bit = PSIHB_XSCOM_P7_HBSCR_FSP_IRQ; } xscom_write(psi->chip_id, psi->xscom_base + reg, bit); } bool psi_poll_fsp_interrupt(struct psi *psi) { return !!(in_be64(psi->regs + PSIHB_CR) & PSIHB_CR_FSP_IRQ); } static void psi_interrupt(void *data, uint32_t isn __unused) { struct psi *psi = data; u64 val; val = in_be64(psi->regs + PSIHB_CR); if (psi_link_poll_active) { printf("PSI[0x%03x]: PSI interrupt CR=0x%016llx (A=%d)\n", psi->chip_id, val, psi->active); } /* Handle PSI interrupts first in case it's a link down */ if (val & PSIHB_CR_PSI_IRQ) { handle_psi_interrupt(psi, val); /* * If the link went down, re-read PSIHB_CR as * the FSP interrupt might have been cleared. */ if (!psi->active) val = in_be64(psi->regs + PSIHB_CR); } /* * We avoid forwarding FSP interrupts if the link isn't * active. They should be masked anyway but it looks * like the CR bit can remain set. */ if (val & PSIHB_CR_FSP_IRQ) { /* * We have a case a flood with FSP mailbox interrupts * when the link is down, see if we manage to clear * the condition */ if (!psi->active) psi_spurious_fsp_irq(psi); else fsp_interrupt(); } /* P8 additional interrupt? */ if (proc_gen == proc_gen_p8) handle_extra_interrupt(psi); /* Poll the console buffers on any interrupt since we don't * get send notifications */ fsp_console_poll(NULL); } static int64_t psi_p7_set_xive(void *data, uint32_t isn __unused, uint16_t server, uint8_t priority) { struct psi *psi = data; uint64_t xivr; if (!psi->working) return OPAL_HARDWARE; /* Populate the XIVR */ xivr = (uint64_t)server << 40; xivr |= (uint64_t)priority << 32; xivr |= P7_IRQ_BUID(psi->interrupt) << 16; out_be64(psi->regs + PSIHB_XIVR, xivr); return OPAL_SUCCESS; } static int64_t psi_p7_get_xive(void *data, uint32_t isn __unused, uint16_t *server, uint8_t *priority) { struct psi *psi = data; uint64_t xivr; if (!psi->working) return OPAL_HARDWARE; /* Read & decode the XIVR */ xivr = in_be64(psi->regs + PSIHB_XIVR); *server = (xivr >> 40) & 0x7ff; *priority = (xivr >> 32) & 0xff; return OPAL_SUCCESS; } static int64_t psi_p8_set_xive(void *data, uint32_t isn, uint16_t server, uint8_t priority) { struct psi *psi = data; uint64_t xivr_p, xivr; switch(isn & 7) { case P8_IRQ_PSI_FSP: xivr_p = PSIHB_XIVR_FSP; break; case P8_IRQ_PSI_OCC: xivr_p = PSIHB_XIVR_OCC; break; case P8_IRQ_PSI_FSI: xivr_p = PSIHB_XIVR_FSI; break; case P8_IRQ_PSI_LPC: xivr_p = PSIHB_XIVR_LPC; break; case P8_IRQ_PSI_LOCAL_ERR: xivr_p = PSIHB_XIVR_LOCAL_ERR; break; case P8_IRQ_PSI_HOST_ERR: xivr_p = PSIHB_XIVR_HOST_ERR; break; default: return OPAL_PARAMETER; } /* Populate the XIVR */ xivr = (uint64_t)server << 40; xivr |= (uint64_t)priority << 32; xivr |= (uint64_t)(isn & 7) << 29; out_be64(psi->regs + xivr_p, xivr); return OPAL_SUCCESS; } static int64_t psi_p8_get_xive(void *data, uint32_t isn __unused, uint16_t *server, uint8_t *priority) { struct psi *psi = data; uint64_t xivr_p, xivr; switch(isn & 7) { case P8_IRQ_PSI_FSP: xivr_p = PSIHB_XIVR_FSP; break; case P8_IRQ_PSI_OCC: xivr_p = PSIHB_XIVR_OCC; break; case P8_IRQ_PSI_FSI: xivr_p = PSIHB_XIVR_FSI; break; case P8_IRQ_PSI_LPC: xivr_p = PSIHB_XIVR_LPC; break; case P8_IRQ_PSI_LOCAL_ERR: xivr_p = PSIHB_XIVR_LOCAL_ERR; break; case P8_IRQ_PSI_HOST_ERR: xivr_p = PSIHB_XIVR_HOST_ERR; break; default: return OPAL_PARAMETER; } /* Read & decode the XIVR */ xivr = in_be64(psi->regs + xivr_p); *server = (xivr >> 40) & 0xffff; *priority = (xivr >> 32) & 0xff; return OPAL_SUCCESS; } /* Called on a fast reset, make sure we aren't stuck with * an accepted and never EOId PSI interrupt */ void psi_irq_reset(void) { struct psi *psi; uint64_t xivr; printf("PSI: Hot reset!\n"); assert(proc_gen == proc_gen_p7); list_for_each(&psis, psi, list) { /* Mask the interrupt & clean the XIVR */ xivr = 0x000000ff00000000; xivr |= P7_IRQ_BUID(psi->interrupt) << 16; out_be64(psi->regs + PSIHB_XIVR, xivr); #if 0 /* Seems to checkstop ... */ /* * Maybe not anymore; we were just blindly sending * this on all iopaths, not just the active one; * We don't even know if those psis are even correct. */ /* Send a dummy EOI to make sure the ICP is clear */ icp_send_eoi(psi->interrupt); #endif } } static const struct irq_source_ops psi_p7_irq_ops = { .get_xive = psi_p7_get_xive, .set_xive = psi_p7_set_xive, .interrupt = psi_interrupt, }; static const struct irq_source_ops psi_p8_irq_ops = { .get_xive = psi_p8_get_xive, .set_xive = psi_p8_set_xive, .interrupt = psi_interrupt, }; static const struct irq_source_ops psi_p8_host_err_ops = { .get_xive = psi_p8_get_xive, .set_xive = psi_p8_set_xive, }; static void psi_tce_enable(struct psi *psi, bool enable) { void *addr; u64 val; switch (proc_gen) { case proc_gen_p7: addr = psi->regs + PSIHB_CR; break; case proc_gen_p8: addr = psi->regs + PSIHB_PHBSCR; break; default: prerror("%s: Unknown CPU type\n", __func__); return; } val = in_be64(addr); if (enable) val |= PSIHB_CR_TCE_ENABLE; else val &= ~PSIHB_CR_TCE_ENABLE; out_be64(addr, val); } /* * Configure the PSI interface for communicating with * an FSP, such as enabling the TCEs, FSP commands, * etc... */ void psi_init_for_fsp(struct psi *psi) { uint64_t reg; bool enable_tce = true; lock(&psi_lock); /* Disable and setup TCE base address */ psi_tce_enable(psi, false); switch (proc_gen) { case proc_gen_p7: out_be64(psi->regs + PSIHB_TAR, PSI_TCE_TABLE_BASE | PSIHB_TAR_16K_ENTRIES); break; case proc_gen_p8: out_be64(psi->regs + PSIHB_TAR, PSI_TCE_TABLE_BASE | PSIHB_TAR_256K_ENTRIES); break; default: enable_tce = false; }; /* Enable various other configuration register bits based * on what pHyp does. We keep interrupts disabled until * after the mailbox has been properly configured. We assume * basic stuff such as PSI link enable is already there. * * - FSP CMD Enable * - FSP MMIO Enable * - TCE Enable * - Error response enable * * Clear all other error bits */ if (!psi->active) { prerror("PSI: psi_init_for_fsp() called on inactive link!\n"); unlock(&psi_lock); return; } reg = in_be64(psi->regs + PSIHB_CR); reg |= PSIHB_CR_FSP_CMD_ENABLE; reg |= PSIHB_CR_FSP_MMIO_ENABLE; reg |= PSIHB_CR_FSP_ERR_RSP_ENABLE; reg &= ~0x00000000ffffffffull; out_be64(psi->regs + PSIHB_CR, reg); psi_tce_enable(psi, enable_tce); unlock(&psi_lock); } void psi_set_external_irq_policy(bool policy) { psi_ext_irq_policy = policy; } /* * Register interrupt sources for all working links, not just the active ones. * This is a one time activity. */ static void psi_register_interrupts(struct psi *psi) { /* Configure the interrupt BUID and mask it */ switch (proc_gen) { case proc_gen_p7: /* On P7, we get a single interrupt */ out_be64(psi->regs + PSIHB_XIVR, P7_IRQ_BUID(psi->interrupt) << 16 | 0xffull << 32); /* Configure it in the GX controller as well */ gx_configure_psi_buid(psi->chip_id, P7_IRQ_BUID(psi->interrupt)); /* Register the IRQ source */ register_irq_source(&psi_p7_irq_ops, psi, psi->interrupt, 1); break; case proc_gen_p8: /* On P8 we get a block of 8, set up the base/mask * and mask all the sources for now */ out_be64(psi->regs + PSIHB_IRSN, SETFIELD(PSIHB_IRSN_COMP, 0ul, psi->interrupt) | SETFIELD(PSIHB_IRSN_MASK, 0ul, 0x7fff8ul) | PSIHB_IRSN_DOWNSTREAM_EN | PSIHB_IRSN_UPSTREAM_EN); out_be64(psi->regs + PSIHB_XIVR_FSP, (0xffull << 32) | (P8_IRQ_PSI_FSP << 29)); out_be64(psi->regs + PSIHB_XIVR_OCC, (0xffull << 32) | (P8_IRQ_PSI_OCC << 29)); out_be64(psi->regs + PSIHB_XIVR_FSI, (0xffull << 32) | (P8_IRQ_PSI_FSI << 29)); out_be64(psi->regs + PSIHB_XIVR_LPC, (0xffull << 32) | (P8_IRQ_PSI_LPC << 29)); out_be64(psi->regs + PSIHB_XIVR_LOCAL_ERR, (0xffull << 32) | (P8_IRQ_PSI_LOCAL_ERR << 29)); out_be64(psi->regs + PSIHB_XIVR_HOST_ERR, (0xffull << 32) | (P8_IRQ_PSI_HOST_ERR << 29)); /* * Register the IRQ sources FSP, OCC, FSI, LPC * and Local Error. Host Error is actually the * external interrupt and the policy for that comes * from the platform */ if (psi_ext_irq_policy == EXTERNAL_IRQ_POLICY_SKIBOOT) { register_irq_source(&psi_p8_irq_ops, psi, psi->interrupt + P8_IRQ_PSI_SKIBOOT_BASE, P8_IRQ_PSI_ALL_COUNT); } else { register_irq_source(&psi_p8_irq_ops, psi, psi->interrupt + P8_IRQ_PSI_SKIBOOT_BASE, P8_IRQ_PSI_LOCAL_COUNT); /* * Host Error is handled by powernv; host error * is at offset 5 from the PSI base. */ register_irq_source(&psi_p8_host_err_ops, psi, psi->interrupt + P8_IRQ_PSI_LINUX_BASE, P8_IRQ_PSI_LINUX_COUNT); } break; default: /* Unknown: just no interrupts */ prerror("PSI: Unknown interrupt type\n"); } } static void psi_activate_phb(struct psi *psi) { u64 reg; /* * Disable interrupt emission in the control register, * it will be re-enabled later, after the mailbox one * will have been enabled. */ reg = in_be64(psi->regs + PSIHB_CR); reg &= ~PSIHB_CR_FSP_IRQ_ENABLE; out_be64(psi->regs + PSIHB_CR, reg); /* Enable interrupts in the mask register. We enable everything * except for bit "FSP command error detected" which the doc * (P7 BookIV) says should be masked for normal ops. It also * seems to be masked under OPAL. */ reg = 0x0000010000100000ull; out_be64(psi->regs + PSIHB_SEMR, reg); #if 0 /* Dump the GXHB registers */ printf(" PSIHB_BBAR : %llx\n", in_be64(psi->regs + PSIHB_BBAR)); printf(" PSIHB_FSPBAR : %llx\n", in_be64(psi->regs + PSIHB_FSPBAR)); printf(" PSIHB_FSPMMR : %llx\n", in_be64(psi->regs + PSIHB_FSPMMR)); printf(" PSIHB_TAR : %llx\n", in_be64(psi->regs + PSIHB_TAR)); printf(" PSIHB_CR : %llx\n", in_be64(psi->regs + PSIHB_CR)); printf(" PSIHB_SEMR : %llx\n", in_be64(psi->regs + PSIHB_SEMR)); printf(" PSIHB_XIVR : %llx\n", in_be64(psi->regs + PSIHB_XIVR)); #endif } static void psi_create_mm_dtnode(struct psi *psi) { struct dt_node *np; uint64_t addr = (uint64_t)psi->regs; np = dt_new_addr(dt_root, "psi", addr); if (!np) return; /* Hard wire size to 4G */ dt_add_property_cells(np, "reg", hi32(addr), lo32(addr), 1, 0); switch (proc_gen) { case proc_gen_p7: dt_add_property_strings(np, "compatible", "ibm,psi", "ibm,power7-psi"); break; case proc_gen_p8: dt_add_property_strings(np, "compatible", "ibm,psi", "ibm,power8-psi"); break; default: dt_add_property_strings(np, "compatible", "ibm,psi"); } dt_add_property_cells(np, "interrupt-parent", get_ics_phandle()); dt_add_property_cells(np, "interrupts", psi->interrupt); dt_add_property_cells(np, "ibm,chip-id", psi->chip_id); } static struct psi *alloc_psi(uint64_t base) { struct psi *psi; psi = zalloc(sizeof(struct psi)); if (!psi) { prerror("PSI: Could not allocate memory\n"); return NULL; } psi->xscom_base = base; return psi; } static struct psi *psi_probe_p7(struct proc_chip *chip, u64 base) { struct psi *psi = NULL; uint64_t rc, val; rc = xscom_read(chip->id, base + PSIHB_XSCOM_P7_HBBAR, &val); if (rc) { prerror("PSI: Error %llx reading PSIHB BAR on chip %d\n", rc, chip->id); return NULL; } if (val & PSIHB_XSCOM_P7_HBBAR_EN) { psi = alloc_psi(base); if (!psi) return NULL; psi->working = true; rc = val >> 36; /* Bits 0:1 = 0x00; 2:27 Bridge BAR... */ rc <<= 20; /* ... corresponds to bits 18:43 of base addr */ psi->regs = (void *)rc; } else printf("PSI[0x%03x]: Working link not found\n", chip->id); return psi; } static struct psi *psi_probe_p8(struct proc_chip *chip, u64 base) { struct psi *psi = NULL; uint64_t rc, val; rc = xscom_read(chip->id, base + PSIHB_XSCOM_P8_BASE, &val); if (rc) { prerror("PSI[0x%03x]: Error %llx reading PSIHB BAR\n", chip->id, rc); return NULL; } if (val & PSIHB_XSCOM_P8_HBBAR_EN) { psi = alloc_psi(base); if (!psi) return NULL; psi->working = true; psi->regs = (void *)(val & ~PSIHB_XSCOM_P8_HBBAR_EN); } else printf("PSI[0x%03x]: Working chip not found\n", chip->id); return psi; } static bool psi_init_psihb(struct dt_node *psihb) { uint32_t chip_id = dt_get_chip_id(psihb); struct proc_chip *chip = get_chip(chip_id); struct psi *psi = NULL; u64 base, val; if (!chip) { prerror("PSI: Can't find chip!\n"); return false; } base = dt_get_address(psihb, 0, NULL); if (dt_node_is_compatible(psihb, "ibm,power7-psihb-x")) psi = psi_probe_p7(chip, base); else if (dt_node_is_compatible(psihb, "ibm,power8-psihb-x")) psi = psi_probe_p8(chip, base); else { prerror("PSI: Unknown processor type\n"); return false; } if (!psi) return false; list_add(&psis, &psi->list); val = in_be64(psi->regs + PSIHB_CR); if (val & PSIHB_CR_FSP_LINK_ACTIVE) { lock(&psi_lock); psi->active = true; unlock(&psi_lock); } psi->chip_id = chip->id; psi->interrupt = get_psi_interrupt(chip->id); chip->psi = psi; psi_create_mm_dtnode(psi); psi_register_interrupts(psi); psi_activate_phb(psi); printf("PSI[0x%03x]: Found PSI bridge [working=%d, active=%d]\n", psi->chip_id, psi->working, psi->active); return true; } void psi_fsp_link_in_use(struct psi *psi __unused) { static bool poller_created = false; /* Do this once only */ if (!poller_created) { poller_created = true; opal_add_poller(psi_link_poll, NULL); } } struct psi *psi_find_functional_chip(void) { return list_top(&psis, struct psi, list); } void psi_init(void) { struct dt_node *np; dt_for_each_compatible(dt_root, np, "ibm,psihb-x") psi_init_psihb(np); } skiboot-skiboot-5.1.13/hw/sfc-ctrl.c000066400000000000000000000307531265204436200172360ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include /* Offset of SFC registers in FW space */ #define SFC_CMDREG_OFFSET 0x00000c00 /* Offset of SFC command buffer in FW space */ #define SFC_CMDBUF_OFFSET 0x00000d00 /* Offset of flash MMIO mapping in FW space */ #define SFC_MMIO_OFFSET 0x0c000000 /* * Register definitions */ #define SFC_REG_CONF 0x10 /* CONF: Direct Access Configuration */ #define SFC_REG_CONF_FRZE (1 << 3) #define SFC_REG_CONF_ECCEN (1 << 2) #define SFC_REG_CONF_DRCD (1 << 1) #define SFC_REG_CONF_FLRLD (1 << 0) #define SFC_REG_STATUS 0x0C /* STATUS : Status Reg */ #define SFC_REG_STATUS_NX_ON_SHFT 28 #define SFC_REG_STATUS_RWP (1 << 27) #define SFC_REG_STATUS_FOURBYTEAD (1 << 26) #define SFC_REG_STATUS_ILLEGAL (1 << 4) #define SFC_REG_STATUS_ECCERRCNTN (1 << 3) #define SFC_REG_STATUS_ECCUEN (1 << 2) #define SFC_REG_STATUS_DONE (1 << 0) #define SFC_REG_CMD 0x40 /* CMD : Command */ #define SFC_REG_CMD_OPCODE_SHFT 9 #define SFC_REG_CMD_LENGTH_SHFT 0 #define SFC_REG_SPICLK 0x3C /* SPICLK: SPI clock rate config */ #define SFC_REG_SPICLK_OUTDLY_SHFT 24 #define SFC_REG_SPICLK_INSAMPDLY_SHFT 16 #define SFC_REG_SPICLK_CLKHI_SHFT 8 #define SFC_REG_SPICLK_CLKLO_SHFT 0 #define SFC_REG_ADR 0x44 /* ADR : Address */ #define SFC_REG_ERASMS 0x48 /* ERASMS : Small Erase Block Size */ #define SFC_REG_ERASLGS 0x4C /* ERALGS : Large Erase Block Size */ #define SFC_REG_CONF4 0x54 /* CONF4 : SPI Op Code for Small Erase */ #define SFC_REG_CONF5 0x58 /* CONF5 : Small Erase Size config reg */ #define SFC_REG_CONF8 0x64 /* CONF8 : Read Command */ #define SFC_REG_CONF8_CSINACTIVERD_SHFT 18 #define SFC_REG_CONF8_DUMMY_SHFT 8 #define SFC_REG_CONF8_READOP_SHFT 0 #define SFC_REG_ADRCBF 0x80 /* ADRCBF : First Intf NOR Addr Offset */ #define SFC_REG_ADRCMF 0x84 /* ADRCMF : First Intf NOR Allocation */ #define SFC_REG_ADRCBS 0x88 /* ADRCBS : Second Intf NOR Addr Offset */ #define SFC_REG_ADRCMS 0x8C /* ADRCMS : Second Intf NOR Allocation */ #define SFC_REG_OADRNB 0x90 /* OADRNB : Direct Access OBP Window Base Address */ #define SFC_REG_OADRNS 0x94 /* OADRNS : DIrect Access OPB Window Size */ #define SFC_REG_CHIPIDCONF 0x9C /* CHIPIDCONF : config ChipId CMD */ #define SFC_REG_CHIPIDCONF_OPCODE_SHFT 24 #define SFC_REG_CHIPIDCONF_READ (1 << 23) #define SFC_REG_CHIPIDCONF_WRITE (1 << 22) #define SFC_REG_CHIPIDCONF_USE_ADDR (1 << 21) #define SFC_REG_CHIPIDCONF_DUMMY_SHFT 16 #define SFC_REG_CHIPIDCONF_LEN_SHFT 0 /* * SFC Opcodes */ #define SFC_OP_READRAW 0x03 /* Read Raw */ #define SFC_OP_WRITERAW 0x02 /* Write Raw */ #define SFC_OP_ERASM 0x32 /* Erase Small */ #define SFC_OP_ERALG 0x34 /* Erase Large */ #define SFC_OP_ENWRITPROT 0x53 /* Enable WRite Protect */ #define SFC_OP_CHIPID 0x1F /* Get Chip ID */ #define SFC_OP_STATUS 0x05 /* Get Status */ #define SFC_OP_TURNOFF 0x5E /* Turn Off */ #define SFC_OP_TURNON 0x50 /* Turn On */ #define SFC_OP_ABORT 0x6F /* Super-Abort */ #define SFC_OP_START4BA 0x37 /* Start 4BA */ #define SFC_OP_END4BA 0x69 /* End 4BA */ /* Command buffer size */ #define SFC_CMDBUF_SIZE 256 struct sfc_ctrl { /* Erase sizes */ uint32_t small_er_size; uint32_t large_er_size; /* Current 4b mode */ bool mode_4b; /* Callbacks */ struct spi_flash_ctrl ops; }; /* Command register support */ static inline int sfc_reg_read(uint8_t reg, uint32_t *val) { uint32_t tmp; int rc; *val = 0xffffffff; rc = lpc_fw_read32(&tmp, SFC_CMDREG_OFFSET + reg); if (rc) return rc; *val = be32_to_cpu(tmp); return 0; } static inline int sfc_reg_write(uint8_t reg, uint32_t val) { return lpc_fw_write32(cpu_to_be32(val), SFC_CMDREG_OFFSET + reg); } static int sfc_buf_write(uint32_t len, const void *data) { uint32_t tmp, off = 0; int rc; if (len > SFC_CMDBUF_SIZE) return FLASH_ERR_PARM_ERROR; while (len >= 4) { tmp = *(const uint32_t *)data; rc = lpc_fw_write32(tmp, SFC_CMDBUF_OFFSET + off); if (rc) return rc; off += 4; len -= 4; data += 4; } if (!len) return 0; /* lpc_fw_write operates on BE values so that's what we layout * in memory with memcpy. The swap in the register on LE doesn't * matter, the result in memory will be in the right order. */ tmp = -1; memcpy(&tmp, data, len); return lpc_fw_write32(tmp, SFC_CMDBUF_OFFSET + off); } static int sfc_buf_read(uint32_t len, void *data) { uint32_t tmp, off = 0; int rc; if (len > SFC_CMDBUF_SIZE) return FLASH_ERR_PARM_ERROR; while (len >= 4) { rc = lpc_fw_read32(data, SFC_CMDBUF_OFFSET + off); if (rc) return rc; off += 4; len -= 4; data += 4; } if (!len) return 0; rc = lpc_fw_read32(&tmp, SFC_CMDBUF_OFFSET + off); if (rc) return rc; /* We know tmp contains a big endian value, so memcpy is * our friend here */ memcpy(data, &tmp, len); return 0; } /* Polls until SFC indicates command is complete */ static int sfc_poll_complete(void) { uint32_t status, timeout; struct timespec ts; /* * A full 256 bytes read/write command will take at least * 126us. Smaller commands are faster but we use less of * them. So let's sleep in increments of 100us */ ts.tv_sec = 0; ts.tv_nsec = 100000; /* * Use a 1s timeout which should be sufficient for the * commands we use */ timeout = 10000; do { int rc; rc = sfc_reg_read(SFC_REG_STATUS, &status); if (rc) return rc; if (status & SFC_REG_STATUS_DONE) break; if (--timeout == 0) return FLASH_ERR_CTRL_TIMEOUT; nanosleep(&ts, NULL); } while (true); return 0; } static int sfc_exec_command(uint8_t opcode, uint32_t length) { int rc = 0; uint32_t cmd_reg = 0; if (opcode > 0x7f || length > 0x1ff) return FLASH_ERR_PARM_ERROR; /* Write command register to start execution */ cmd_reg |= (opcode << SFC_REG_CMD_OPCODE_SHFT); cmd_reg |= (length << SFC_REG_CMD_LENGTH_SHFT); rc = sfc_reg_write(SFC_REG_CMD, cmd_reg); if (rc) return rc; /* Wait for command to complete */ return sfc_poll_complete(); } static int sfc_chip_id(struct spi_flash_ctrl *ctrl, uint8_t *id_buf, uint32_t *id_size) { uint32_t idconf; int rc; (void)ctrl; if ((*id_size) < 3) return FLASH_ERR_PARM_ERROR; /* * XXX This will not work in locked down mode but we assume that * in this case, the chip ID command is already properly programmed * and the SFC will ignore this. However I haven't verified... */ idconf = ((uint64_t)CMD_RDID) << SFC_REG_CHIPIDCONF_OPCODE_SHFT; idconf |= SFC_REG_CHIPIDCONF_READ; idconf |= (3ul << SFC_REG_CHIPIDCONF_LEN_SHFT); (void)sfc_reg_write(SFC_REG_CHIPIDCONF, idconf); /* Perform command */ rc = sfc_exec_command(SFC_OP_CHIPID, 0); if (rc) return rc; /* Read chip ID */ rc = sfc_buf_read(3, id_buf); if (rc) return rc; *id_size = 3; return 0; } static int sfc_read(struct spi_flash_ctrl *ctrl, uint32_t pos, void *buf, uint32_t len) { (void)ctrl; while(len) { uint32_t chunk = len; int rc; if (chunk > SFC_CMDBUF_SIZE) chunk = SFC_CMDBUF_SIZE; rc = sfc_reg_write(SFC_REG_ADR, pos); if (rc) return rc; rc = sfc_exec_command(SFC_OP_READRAW, chunk); if (rc) return rc; rc = sfc_buf_read(chunk, buf); if (rc) return rc; len -= chunk; pos += chunk; buf += chunk; } return 0; } static int sfc_write(struct spi_flash_ctrl *ctrl, uint32_t addr, const void *buf, uint32_t size) { uint32_t chunk; int rc; (void)ctrl; while(size) { /* We shall not cross a page boundary */ chunk = 0x100 - (addr & 0xff); if (chunk > size) chunk = size; /* Write to SFC write buffer */ rc = sfc_buf_write(chunk, buf); if (rc) return rc; /* Program address */ rc = sfc_reg_write(SFC_REG_ADR, addr); if (rc) return rc; /* Send command */ rc = sfc_exec_command(SFC_OP_WRITERAW, chunk); if (rc) return rc; addr += chunk; buf += chunk; size -= chunk; } return 0; } static int sfc_erase(struct spi_flash_ctrl *ctrl, uint32_t addr, uint32_t size) { struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops); uint32_t sm_mask = ct->small_er_size - 1; uint32_t lg_mask = ct->large_er_size - 1; uint32_t chunk; uint8_t cmd; int rc; while(size) { /* Choose erase size for this chunk */ if (((addr | size) & lg_mask) == 0) { chunk = ct->large_er_size; cmd = SFC_OP_ERALG; } else if (((addr | size) & sm_mask) == 0) { chunk = ct->small_er_size; cmd = SFC_OP_ERASM; } else return FLASH_ERR_ERASE_BOUNDARY; rc = sfc_reg_write(SFC_REG_ADR, addr); if (rc) return rc; rc = sfc_exec_command(cmd, 0); if (rc) return rc; addr += chunk; size -= chunk; } return 0; } static int sfc_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize) { struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops); struct flash_info *info = ctrl->finfo; uint32_t er_flags; (void)tsize; /* Keep non-erase related flags */ er_flags = ~FL_ERASE_ALL; /* Add supported erase sizes */ if (ct->small_er_size == 0x1000 || ct->large_er_size == 0x1000) er_flags |= FL_ERASE_4K; if (ct->small_er_size == 0x8000 || ct->large_er_size == 0x8000) er_flags |= FL_ERASE_32K; if (ct->small_er_size == 0x10000 || ct->large_er_size == 0x10000) er_flags |= FL_ERASE_64K; /* Mask the flags out */ info->flags &= er_flags; return 0; } static int sfc_set_4b(struct spi_flash_ctrl *ctrl, bool enable) { struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops); int rc; rc = sfc_exec_command(enable ? SFC_OP_START4BA : SFC_OP_END4BA, 0); if (rc) return rc; ct->mode_4b = enable; return 0; } static void sfc_validate_er_size(uint32_t *size) { if (*size == 0) return; /* We only support 4k, 32k and 64k */ if (*size != 0x1000 && *size != 0x8000 && *size != 0x10000) { FL_ERR("SFC: Erase size %d bytes unsupported\n", *size); *size = 0; } } static int sfc_init(struct sfc_ctrl *ct) { int rc; uint32_t status; /* * Assumptions: The controller has been fully initialized * by an earlier FW layer setting the chip ID command, the * erase sizes, and configuring the timings for reads and * writes. * * This driver is meant to be usable if the configuration * is in lock down. * * If that wasn't the case, we could configure some sane * defaults here and tuned values in setup() after the * chip has been identified. */ /* Read erase sizes from flash */ rc = sfc_reg_read(SFC_REG_ERASMS, &ct->small_er_size); if (rc) return rc; sfc_validate_er_size(&ct->small_er_size); rc = sfc_reg_read(SFC_REG_ERASLGS, &ct->large_er_size); if (rc) return rc; sfc_validate_er_size(&ct->large_er_size); /* No erase sizes we can cope with ? Ouch... */ if ((ct->small_er_size == 0 && ct->large_er_size == 0) || (ct->large_er_size && (ct->small_er_size > ct->large_er_size))) { FL_ERR("SFC: No supported erase sizes !\n"); return FLASH_ERR_CTRL_CONFIG_MISMATCH; } FL_INF("SFC: Suppored erase sizes:"); if (ct->small_er_size) FL_INF(" %dKB", ct->small_er_size >> 10); if (ct->large_er_size) FL_INF(" %dKB", ct->large_er_size >> 10); FL_INF("\n"); /* Read current state of 4 byte addressing */ rc = sfc_reg_read(SFC_REG_STATUS, &status); if (rc) return rc; ct->mode_4b = !!(status & SFC_REG_STATUS_FOURBYTEAD); return 0; } int sfc_open(struct spi_flash_ctrl **ctrl) { struct sfc_ctrl *ct; int rc; *ctrl = NULL; ct = malloc(sizeof(*ct)); if (!ct) { FL_ERR("SFC: Failed to allocate\n"); return FLASH_ERR_MALLOC_FAILED; } memset(ct, 0, sizeof(*ct)); ct->ops.chip_id = sfc_chip_id; ct->ops.setup = sfc_setup; ct->ops.set_4b = sfc_set_4b; ct->ops.read = sfc_read; ct->ops.write = sfc_write; ct->ops.erase = sfc_erase; rc = sfc_init(ct); if (rc) goto fail; *ctrl = &ct->ops; return 0; fail: free(ct); return rc; } void sfc_close(struct spi_flash_ctrl *ctrl) { struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops); /* Free the whole lot */ free(ct); } skiboot-skiboot-5.1.13/hw/slw.c000066400000000000000000000771711265204436200163330ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Handle ChipTOD chip & configure core timebases */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __HAVE_LIBPORE__ #include #include #define MAX_RESET_PATCH_SIZE 64 static uint32_t slw_saved_reset[MAX_RESET_PATCH_SIZE]; static bool slw_current_le = false; #endif /* __HAVE_LIBPORE__ */ /* SLW timer related stuff */ static bool slw_has_timer; static uint64_t slw_timer_inc; static uint64_t slw_timer_target; static uint32_t slw_timer_chip; static uint64_t slw_last_gen; static uint64_t slw_last_gen_stamp; /* Assembly in head.S */ extern void enter_rvwinkle(void); DEFINE_LOG_ENTRY(OPAL_RC_SLW_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SLW, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_SLW_SET, OPAL_PLATFORM_ERR_EVT, OPAL_SLW, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_SLW_GET, OPAL_PLATFORM_ERR_EVT, OPAL_SLW, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_SLW_REG, OPAL_PLATFORM_ERR_EVT, OPAL_SLW, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); #ifdef __HAVE_LIBPORE__ static void slw_do_rvwinkle(void *data) { struct cpu_thread *cpu = this_cpu(); struct cpu_thread *master = data; uint64_t lpcr = mfspr(SPR_LPCR); struct proc_chip *chip; /* Setup our ICP to receive IPIs */ icp_prep_for_rvwinkle(); /* Setup LPCR to wakeup on external interrupts only */ mtspr(SPR_LPCR, ((lpcr & ~SPR_LPCR_P8_PECE) | SPR_LPCR_P8_PECE2)); prlog(PR_DEBUG, "SLW: CPU PIR 0x%04x goint to rvwinkle...\n", cpu->pir); /* Tell that we got it */ cpu->state = cpu_state_rvwinkle; enter_rvwinkle(); /* Ok, it's ours again */ cpu->state = cpu_state_active; prlog(PR_DEBUG, "SLW: CPU PIR 0x%04x woken up !\n", cpu->pir); /* Cleanup our ICP */ reset_cpu_icp(); /* Resync timebase */ chiptod_wakeup_resync(); /* Restore LPCR */ mtspr(SPR_LPCR, lpcr); /* If we are passed a master pointer we are the designated * waker, let's proceed. If not, return, we are finished. */ if (!master) return; prlog(PR_DEBUG, "SLW: CPU PIR 0x%04x waiting for master...\n", cpu->pir); /* Allriiiight... now wait for master to go down */ while(master->state != cpu_state_rvwinkle) sync(); /* XXX Wait one second ! (should check xscom state ? ) */ time_wait_ms(1000); for_each_chip(chip) { struct cpu_thread *c; uint64_t tmp; for_each_available_core_in_chip(c, chip->id) { xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(pir_to_core_id(c->pir), EX_PM_IDLE_STATE_HISTORY_PHYP), &tmp); prlog(PR_TRACE, "SLW: core %x:%x" " history: 0x%016llx (mid2)\n", chip->id, pir_to_core_id(c->pir), tmp); } } prlog(PR_DEBUG, "SLW: Waking master (PIR 0x%04x)...\n", master->pir); /* Now poke all the secondary threads on the master's core */ for_each_cpu(cpu) { if (!cpu_is_sibling(cpu, master) || (cpu == master)) continue; icp_kick_cpu(cpu); /* Wait for it to claim to be back (XXX ADD TIMEOUT) */ while(cpu->state != cpu_state_active) sync(); } /* Now poke the master and be gone */ icp_kick_cpu(master); } static void slw_patch_reset(void) { extern uint32_t rvwinkle_patch_start; extern uint32_t rvwinkle_patch_end; uint32_t *src, *dst, *sav; BUILD_ASSERT((&rvwinkle_patch_end - &rvwinkle_patch_start) <= MAX_RESET_PATCH_SIZE); src = &rvwinkle_patch_start; dst = (uint32_t *)0x100; sav = slw_saved_reset; while(src < &rvwinkle_patch_end) { *(sav++) = *(dst); *(dst++) = *(src++); } sync_icache(); } static void slw_unpatch_reset(void) { extern uint32_t rvwinkle_patch_start; extern uint32_t rvwinkle_patch_end; uint32_t *src, *dst, *sav; src = &rvwinkle_patch_start; dst = (uint32_t *)0x100; sav = slw_saved_reset; while(src < &rvwinkle_patch_end) { *(dst++) = *(sav++); src++; } sync_icache(); } #endif /* __HAVE_LIBPORE__ */ static bool slw_general_init(struct proc_chip *chip, struct cpu_thread *c) { uint32_t core = pir_to_core_id(c->pir); uint64_t tmp; int rc; /* PowerManagement GP0 clear PM_DISABLE */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Failed to read PM_GP0\n"); return false; } tmp = tmp & ~0x8000000000000000ULL; rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Failed to write PM_GP0\n"); return false; } prlog(PR_TRACE, "SLW: PMGP0 set to 0x%016llx\n", tmp); /* Read back for debug */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), &tmp); if (rc) log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Failed to re-read PM_GP0. Continuing...\n"); prlog(PR_TRACE, "SLW: PMGP0 read 0x%016llx\n", tmp); /* Set CORE and ECO PFET Vret to select zero */ rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_CORE_PFET_VRET), 0); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Failed to write PM_CORE_PFET_VRET\n"); return false; } rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_CORE_ECO_VRET), 0); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Failed to write PM_CORE_ECO_VRET\n"); return false; } return true; } static bool slw_set_overrides(struct proc_chip *chip, struct cpu_thread *c) { uint32_t core = pir_to_core_id(c->pir); uint64_t tmp; int rc; /* * Set ENABLE_IGNORE_RECOV_ERRORS in OHA_MODE_REG * * XXX FIXME: This should be only done for "forced" winkle such as * when doing repairs or LE transition, and we should restore the * original value when done */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX(core, PM_OHA_MODE_REG), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to read PM_OHA_MODE_REG\n"); return false; } tmp = tmp | 0x8000000000000000ULL; rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX(core, PM_OHA_MODE_REG), tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to write PM_OHA_MODE_REG\n"); return false; } prlog(PR_TRACE, "SLW: PM_OHA_MODE_REG set to 0x%016llx\n", tmp); /* Read back for debug */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX(core, PM_OHA_MODE_REG),&tmp); prlog(PR_TRACE, "SLW: PM_OHA_MODE_REG read 0x%016llx\n", tmp); /* * Clear special wakeup bits that could hold power mgt * * XXX FIXME: See above */ rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SPECIAL_WAKEUP_FSP), 0); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to write PM_SPECIAL_WAKEUP_FSP\n"); return false; } rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SPECIAL_WAKEUP_OCC), 0); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to write PM_SPECIAL_WAKEUP_OCC\n"); return false; } rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SPECIAL_WAKEUP_PHYP), 0); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to write PM_SPECIAL_WAKEUP_PHYP\n"); return false; } return true; } #ifdef __HAVE_LIBPORE__ static bool slw_unset_overrides(struct proc_chip *chip, struct cpu_thread *c) { uint32_t core = pir_to_core_id(c->pir); /* XXX FIXME: Save and restore the overrides */ prlog(PR_DEBUG, "SLW: slw_unset_overrides %x:%x\n", chip->id, core); return true; } #endif /* __HAVE_LIBPORE__ */ static bool slw_set_idle_mode(struct proc_chip *chip, struct cpu_thread *c) { uint32_t core = pir_to_core_id(c->pir); uint64_t tmp; int rc; /* * PM GP1 allows fast/deep mode to be selected independently for sleep * and winkle. Init PM GP1 so that sleep happens in fast mode and * winkle happens in deep mode. * Make use of the OR XSCOM for this since the OCC might be manipulating * the PM_GP1 register as well. Before doing this ensure that the bits * managing idle states are cleared so as to override any bits set at * init time. */ tmp = ~EX_PM_GP1_SLEEP_WINKLE_MASK; rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_CLEAR_GP1), tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to write PM_GP1\n"); return false; } rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SET_GP1), EX_PM_SETUP_GP1_FAST_SLEEP_DEEP_WINKLE); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to write PM_GP1\n"); return false; } /* Read back for debug */ xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP1), &tmp); prlog(PR_TRACE, "SLW: PMGP1 read 0x%016llx\n", tmp); return true; } static bool slw_get_idle_state_history(struct proc_chip *chip, struct cpu_thread *c) { uint32_t core = pir_to_core_id(c->pir); uint64_t tmp; int rc; /* Cleanup history */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_IDLE_STATE_HISTORY_PHYP), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_GET), "SLW: Failed to read PM_IDLE_STATE_HISTORY\n"); return false; } prlog(PR_TRACE, "SLW: core %x:%x history: 0x%016llx (old1)\n", chip->id, core, tmp); rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_IDLE_STATE_HISTORY_PHYP), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_GET), "SLW: Failed to read PM_IDLE_STATE_HISTORY\n"); return false; } prlog(PR_TRACE, "SLW: core %x:%x history: 0x%016llx (old2)\n", chip->id, core, tmp); return true; } static bool idle_prepare_core(struct proc_chip *chip, struct cpu_thread *c) { prlog(PR_TRACE, "FASTSLEEP: Prepare core %x:%x\n", chip->id, pir_to_core_id(c->pir)); if(!slw_general_init(chip, c)) return false; if(!slw_set_overrides(chip, c)) return false; if(!slw_set_idle_mode(chip, c)) return false; if(!slw_get_idle_state_history(chip, c)) return false; return true; } /* Define device-tree fields */ #define MAX_NAME_LEN 16 struct cpu_idle_states { char name[MAX_NAME_LEN]; u32 latency_ns; u32 residency_ns; u32 flags; u64 pmicr; u64 pmicr_mask; }; /* Flag definitions */ /* Set bits to avoid misinterpretation even if kernel has endian bugs */ #define IDLE_DEC_STOP 0x00000001 /* Decrementer would stop */ #define IDLE_TB_STOP 0x00000002 /* Timebase would stop */ #define IDLE_LOSE_USER_CONTEXT 0x00001000 /* Restore GPRs like nap */ #define IDLE_LOSE_HYP_CONTEXT 0x00002000 /* Restore hypervisor resource from PACA pointer */ #define IDLE_LOSE_FULL_CONTEXT 0x00004000 /* Restore hypervisor resource by searching PACA */ #define IDLE_USE_PMICR 0x00800000 /* Use SPR PMICR instruction */ #define IDLE_FASTSLEEP_PMICR 0x0000002000000000 #define IDLE_DEEPSLEEP_PMICR 0x0000003000000000 #define IDLE_SLEEP_PMICR_MASK 0x0000003000000000 #define IDLE_FASTWINKLE_PMICR 0x0000000000200000 #define IDLE_DEEPWINKLE_PMICR 0x0000000000300000 #define IDLE_WINKLE_PMICR_MASK 0x0000000000300000 static struct cpu_idle_states power7_cpu_idle_states[] = { { /* nap */ .name = "nap", .latency_ns = 4000, .residency_ns = 100000, .flags = 0*IDLE_DEC_STOP \ | 0*IDLE_TB_STOP \ | 1*IDLE_LOSE_USER_CONTEXT \ | 0*IDLE_LOSE_HYP_CONTEXT \ | 0*IDLE_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_NAP_ENABLED \ | 0*OPAL_PM_SLEEP_ENABLED \ | 0*OPAL_PM_WINKLE_ENABLED \ | 0*IDLE_USE_PMICR, .pmicr = 0, .pmicr_mask = 0 }, }; static struct cpu_idle_states power8_cpu_idle_states[] = { { /* nap */ .name = "nap", .latency_ns = 4000, .residency_ns = 100000, .flags = 0*IDLE_DEC_STOP \ | 0*IDLE_TB_STOP \ | 1*IDLE_LOSE_USER_CONTEXT \ | 0*IDLE_LOSE_HYP_CONTEXT \ | 0*IDLE_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_NAP_ENABLED \ | 0*IDLE_USE_PMICR, .pmicr = 0, .pmicr_mask = 0 }, { /* fast sleep (with workaround) */ .name = "fastsleep_", .latency_ns = 40000, .residency_ns = 300000000, .flags = 1*IDLE_DEC_STOP \ | 1*IDLE_TB_STOP \ | 1*IDLE_LOSE_USER_CONTEXT \ | 0*IDLE_LOSE_HYP_CONTEXT \ | 0*IDLE_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_SLEEP_ENABLED_ER1 \ | 0*IDLE_USE_PMICR, /* Not enabled until deep states are available */ .pmicr = IDLE_FASTSLEEP_PMICR, .pmicr_mask = IDLE_SLEEP_PMICR_MASK }, { /* Winkle */ .name = "winkle", .latency_ns = 10000000, .residency_ns = 1000000000, /* Educated guess (not measured). * Winkle is not currently used by * linux cpuidle subsystem so we * don't have real world user. * However, this should be roughly * accurate for when linux does * use it. */ .flags = 1*IDLE_DEC_STOP \ | 1*IDLE_TB_STOP \ | 1*IDLE_LOSE_USER_CONTEXT \ | 1*IDLE_LOSE_HYP_CONTEXT \ | 1*IDLE_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_WINKLE_ENABLED \ | 0*IDLE_USE_PMICR, /* Currently choosing deep vs fast via EX_PM_GP1 reg */ .pmicr = 0, .pmicr_mask = 0 }, }; /* Add device tree properties to describe idle states */ static void add_cpu_idle_state_properties(void) { struct dt_node *power_mgt; struct cpu_idle_states *states; struct proc_chip *chip; int nr_states; bool can_sleep = true, can_winkle = true; u8 i; /* Buffers to hold idle state properties */ char *name_buf; u32 *latency_ns_buf; u32 *residency_ns_buf; u32 *flags_buf; u64 *pmicr_buf; u64 *pmicr_mask_buf; /* Variables to track buffer length */ u8 name_buf_len; u8 num_supported_idle_states; prlog(PR_DEBUG, "CPU idle state device tree init\n"); /* Create /ibm,opal/power-mgt */ power_mgt = dt_new(opal_node, "power-mgt"); if (!power_mgt) { prlog(PR_ERR, "creating dt node /ibm,opal/power-mgt failed\n"); return; } /* Mambo currently misbehaves in nap mode vs. timebase, so let's * disable idle states */ if (chip_quirk(QUIRK_DISABLE_NAP)) return; /* * Chose the right state table for the chip * * XXX We use the first chip version, we should probably look * for the smaller of all chips instead.. */ chip = next_chip(NULL); assert(chip); if (chip->type == PROC_CHIP_P8_MURANO || chip->type == PROC_CHIP_P8_VENICE || chip->type == PROC_CHIP_P8_NAPLES) { const struct dt_property *p; p = dt_find_property(dt_root, "ibm,enabled-idle-states"); if (p) prlog(PR_WARNING, "SLW: HB-provided idle states property found\n"); states = power8_cpu_idle_states; nr_states = ARRAY_SIZE(power8_cpu_idle_states); /* Check if hostboot say we can sleep */ if (!p || !dt_prop_find_string(p, "fast-sleep")) { prlog(PR_NOTICE, "SLW: Sleep not enabled by HB" " on this platform\n"); can_sleep = false; } /* Clip to NAP only on Murano and Venice DD1.x */ if ((chip->type == PROC_CHIP_P8_MURANO || chip->type == PROC_CHIP_P8_VENICE) && chip->ec_level < 0x20) { prlog(PR_NOTICE, "SLW: Sleep not enabled on P8 DD1.x\n"); can_sleep = false; } } else { states = power7_cpu_idle_states; nr_states = ARRAY_SIZE(power7_cpu_idle_states); } /* Enable winkle only if slw image is intact */ can_winkle = (chip->slw_base && chip->slw_bar_size && chip->slw_image_size); /* * Currently we can't append strings and cells to dt properties. * So create buffers to which you can append values, then create * dt properties with this buffer content. */ /* Allocate memory to idle state property buffers. */ name_buf = (char *) malloc(nr_states * sizeof(char) * MAX_NAME_LEN); latency_ns_buf = (u32 *) malloc(nr_states * sizeof(u32)); residency_ns_buf= (u32 *) malloc(nr_states * sizeof(u32)); flags_buf = (u32 *) malloc(nr_states * sizeof(u32)); pmicr_buf = (u64 *) malloc(nr_states * sizeof(u64)); pmicr_mask_buf = (u64 *) malloc(nr_states * sizeof(u64)); name_buf_len = 0; num_supported_idle_states = 0; for (i = 0; i < nr_states; i++) { /* For each state, check if it is one of the supported states. */ if( (states[i].flags & OPAL_PM_NAP_ENABLED) || ((states[i].flags & OPAL_PM_SLEEP_ENABLED) && can_sleep) || ((states[i].flags & OPAL_PM_SLEEP_ENABLED_ER1) && can_sleep) || ((states[i].flags & OPAL_PM_WINKLE_ENABLED) && can_winkle) ) { /* * If a state is supported add each of its property * to its corresponding property buffer. */ strcpy(name_buf, states[i].name); name_buf = name_buf + strlen(states[i].name) + 1; *latency_ns_buf = cpu_to_fdt32(states[i].latency_ns); latency_ns_buf++; *residency_ns_buf = cpu_to_fdt32(states[i].residency_ns); residency_ns_buf++; *flags_buf = cpu_to_fdt32(states[i].flags); flags_buf++; *pmicr_buf = cpu_to_fdt64(states[i].pmicr); pmicr_buf++; *pmicr_mask_buf = cpu_to_fdt64(states[i].pmicr); pmicr_mask_buf++; /* Increment buffer length trackers */ name_buf_len += strlen(states[i].name) + 1; num_supported_idle_states++; } } /* Point buffer pointers back to beginning of the buffer */ name_buf -= name_buf_len; latency_ns_buf -= num_supported_idle_states; residency_ns_buf -= num_supported_idle_states; flags_buf -= num_supported_idle_states; pmicr_buf -= num_supported_idle_states; pmicr_mask_buf -= num_supported_idle_states; /* Create dt properties with the buffer content */ dt_add_property(power_mgt, "ibm,cpu-idle-state-names", name_buf, name_buf_len* sizeof(char)); dt_add_property(power_mgt, "ibm,cpu-idle-state-latencies-ns", latency_ns_buf, num_supported_idle_states * sizeof(u32)); dt_add_property(power_mgt, "ibm,cpu-idle-state-residency-ns", residency_ns_buf, num_supported_idle_states * sizeof(u32)); dt_add_property(power_mgt, "ibm,cpu-idle-state-flags", flags_buf, num_supported_idle_states * sizeof(u32)); dt_add_property(power_mgt, "ibm,cpu-idle-state-pmicr", pmicr_buf, num_supported_idle_states * sizeof(u64)); dt_add_property(power_mgt, "ibm,cpu-idle-state-pmicr-mask", pmicr_mask_buf, num_supported_idle_states * sizeof(u64)); free(name_buf); free(latency_ns_buf); free(residency_ns_buf); free(flags_buf); free(pmicr_buf); free(pmicr_mask_buf); } #ifdef __HAVE_LIBPORE__ static void slw_cleanup_core(struct proc_chip *chip, struct cpu_thread *c) { uint64_t tmp; int rc; /* Display history to check transition */ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(pir_to_core_id(c->pir), EX_PM_IDLE_STATE_HISTORY_PHYP), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_GET), "SLW: Failed to read PM_IDLE_STATE_HISTORY\n"); /* XXX error handling ? return false; */ } prlog(PR_DEBUG, "SLW: core %x:%x history: 0x%016llx (new1)\n", chip->id, pir_to_core_id(c->pir), tmp); rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(pir_to_core_id(c->pir), EX_PM_IDLE_STATE_HISTORY_PHYP), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_GET), "SLW: Failed to read PM_IDLE_STATE_HISTORY\n"); /* XXX error handling ? return false; */ } prlog(PR_DEBUG, "SLW: core %x:%x history: 0x%016llx (new2)\n", chip->id, pir_to_core_id(c->pir), tmp); /* * XXX FIXME: Error out if the transition didn't reach rvwinkle ? */ /* * XXX FIXME: We should restore a bunch of the EX bits we * overwrite to sane values here */ slw_unset_overrides(chip, c); } static void slw_cleanup_chip(struct proc_chip *chip) { struct cpu_thread *c; for_each_available_core_in_chip(c, chip->id) slw_cleanup_core(chip, c); } static void slw_patch_scans(struct proc_chip *chip, bool le_mode) { int64_t rc; uint64_t old_val, new_val; rc = sbe_xip_get_scalar((void *)chip->slw_base, "skip_ex_override_ring_scans", &old_val); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to read scan override on chip %d\n", chip->id); return; } new_val = le_mode ? 0 : 1; prlog(PR_TRACE, "SLW: Chip %d, LE value was: %lld, setting to %lld\n", chip->id, old_val, new_val); rc = sbe_xip_set_scalar((void *)chip->slw_base, "skip_ex_override_ring_scans", new_val); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to set LE mode on chip %d\n", chip->id); return; } } #else static inline void slw_patch_scans(struct proc_chip *chip __unused, bool le_mode __unused ) { } #endif /* __HAVE_LIBPORE__ */ #ifndef __HAVE_LIBPORE__ int64_t __attrconst slw_reinit(uint64_t flags) { (void)flags; return OPAL_UNSUPPORTED; } #else int64_t slw_reinit(uint64_t flags) { struct proc_chip *chip; struct cpu_thread *cpu; bool has_waker = false; bool target_le = slw_current_le; if (proc_gen < proc_gen_p8) return OPAL_UNSUPPORTED; if (flags & OPAL_REINIT_CPUS_HILE_BE) target_le = false; if (flags & OPAL_REINIT_CPUS_HILE_LE) target_le = true; prlog(PR_TRACE, "SLW Reinit from CPU PIR 0x%04x," " HILE set to %s endian...\n", this_cpu()->pir, target_le ? "little" : "big"); /* Prepare chips/cores for rvwinkle */ for_each_chip(chip) { if (!chip->slw_base) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Not found on chip %d\n", chip->id); return OPAL_HARDWARE; } slw_patch_scans(chip, target_le); } slw_current_le = target_le; /* XXX Save HIDs ? Or do that in head.S ... */ slw_patch_reset(); /* rvwinkle everybody and pick one to wake me once I rvwinkle myself */ for_each_available_cpu(cpu) { struct cpu_thread *master = NULL; if (cpu == this_cpu()) continue; /* Pick up a waker for myself: it must not be a sibling of * the current CPU and must be a thread 0 (so it gets to * sync its timebase before doing time_wait_ms() */ if (!has_waker && !cpu_is_sibling(cpu, this_cpu()) && cpu_is_thread0(cpu)) { has_waker = true; master = this_cpu(); } __cpu_queue_job(cpu, "slw_do_rvwinkle", slw_do_rvwinkle, master, true); /* Wait for it to claim to be down */ while(cpu->state != cpu_state_rvwinkle) sync(); } /* XXX Wait one second ! (should check xscom state ? ) */ prlog(PR_TRACE, "SLW: Waiting one second...\n"); time_wait_ms(1000); prlog(PR_TRACE, "SLW: Done.\n"); for_each_chip(chip) { struct cpu_thread *c; uint64_t tmp; for_each_available_core_in_chip(c, chip->id) { xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(pir_to_core_id(c->pir), EX_PM_IDLE_STATE_HISTORY_PHYP), &tmp); prlog(PR_DEBUG, "SLW: core %x:%x" " history: 0x%016llx (mid)\n", chip->id, pir_to_core_id(c->pir), tmp); } } /* Wake everybody except on my core */ for_each_cpu(cpu) { if (cpu->state != cpu_state_rvwinkle || cpu_is_sibling(cpu, this_cpu())) continue; icp_kick_cpu(cpu); /* Wait for it to claim to be back (XXX ADD TIMEOUT) */ while(cpu->state != cpu_state_active) sync(); } /* Did we find a waker ? If we didn't, that means we had no * other core in the system, we can't do it */ if (!has_waker) { prlog(PR_TRACE, "SLW: No candidate waker, giving up !\n"); return OPAL_HARDWARE; } /* Our siblings are rvwinkling, and our waker is waiting for us * so let's just go down now */ slw_do_rvwinkle(NULL); slw_unpatch_reset(); for_each_chip(chip) slw_cleanup_chip(chip); prlog(PR_TRACE, "SLW Reinit complete !\n"); return OPAL_SUCCESS; } #endif /* __HAVE_LIBPORE__ */ #ifdef __HAVE_LIBPORE__ static void slw_patch_regs(struct proc_chip *chip) { struct cpu_thread *c; void *image = (void *)chip->slw_base; int rc; for_each_available_cpu(c) { if (c->chip_id != chip->id) continue; /* Clear HRMOR */ rc = p8_pore_gen_cpureg_fixed(image, P8_SLW_MODEBUILD_SRAM, P8_SPR_HRMOR, 0, cpu_get_core_index(c), cpu_get_thread_index(c)); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to set HRMOR for CPU %x\n", c->pir); } /* XXX Add HIDs etc... */ } } #endif /* __HAVE_LIBPORE__ */ static void slw_init_chip(struct proc_chip *chip) { int64_t rc; struct cpu_thread *c; prlog(PR_DEBUG, "SLW: Init chip 0x%x\n", chip->id); if (!chip->slw_base) { prerror("SLW: No image found !\n"); return; } #ifdef __HAVE_LIBPORE__ /* Check actual image size */ rc = sbe_xip_get_scalar((void *)chip->slw_base, "image_size", &chip->slw_image_size); if (rc != 0) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Error %lld reading SLW image size\n", rc); /* XXX Panic ? */ chip->slw_base = 0; chip->slw_bar_size = 0; chip->slw_image_size = 0; return; } prlog(PR_DEBUG, "SLW: Image size from image: 0x%llx\n", chip->slw_image_size); if (chip->slw_image_size > chip->slw_bar_size) { log_simple_error(&e_info(OPAL_RC_SLW_INIT), "SLW: Built-in image size larger than BAR size !\n"); /* XXX Panic ? */ } /* Patch SLW image */ slw_patch_regs(chip); #endif /* __HAVE_LIBPORE__ */ /* At power ON setup inits for fast-sleep */ for_each_available_core_in_chip(c, chip->id) { idle_prepare_core(chip, c); } } /* Workarounds while entering fast-sleep */ static void fast_sleep_enter(void) { uint32_t core = pir_to_core_id(this_cpu()->pir); uint32_t chip_id = this_cpu()->chip_id; struct cpu_thread *primary_thread; uint64_t tmp; int rc; primary_thread = this_cpu()->primary; rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1), &tmp); if (rc) { prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(1):" " rc=%d chip_id=%d core=%d\n", rc, chip_id, core); return; } primary_thread->save_l2_fir_action1 = tmp; tmp = tmp & ~0x0200000000000000ULL; rc = xscom_write(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1), tmp); if (rc) { prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(2):" " rc=%d chip_id=%d core=%d\n", rc, chip_id, core); return; } rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1), &tmp); if (rc) { prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(3):" " rc=%d chip_id=%d core=%d\n", rc, chip_id, core); return; } } /* Workarounds while exiting fast-sleep */ static void fast_sleep_exit(void) { uint32_t core = pir_to_core_id(this_cpu()->pir); uint32_t chip_id = this_cpu()->chip_id; struct cpu_thread *primary_thread; int rc; primary_thread = this_cpu()->primary; rc = xscom_write(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1), primary_thread->save_l2_fir_action1); if (rc) { prlog(PR_WARNING, "fast_sleep_exit XSCOM failed:" " rc=%d chip_id=%d core=%d\n", rc, chip_id, core); return; } } /* * Setup and cleanup method for fast-sleep workarounds * state = 1 fast-sleep * enter = 1 Enter state * exit = 0 Exit state */ static int64_t opal_config_cpu_idle_state(uint64_t state, uint64_t enter) { /* Only fast-sleep for now */ if (state != 1) return OPAL_PARAMETER; switch(enter) { case 1: fast_sleep_enter(); break; case 0: fast_sleep_exit(); break; default: return OPAL_PARAMETER; } return OPAL_SUCCESS; } opal_call(OPAL_CONFIG_CPU_IDLE_STATE, opal_config_cpu_idle_state, 2); #ifdef __HAVE_LIBPORE__ static int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val) { struct cpu_thread *c = find_cpu_by_pir(cpu_pir); struct proc_chip *chip = get_chip(c->chip_id); void *image = (void *) chip->slw_base; int rc; int i; int spr_is_supported = 0; /* Check of the SPR is supported by libpore */ for ( i=0; i < SLW_SPR_REGS_SIZE ; i++) { if (sprn == SLW_SPR_REGS[i].value) { spr_is_supported = 1; break; } } if (!spr_is_supported) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Trying to set unsupported spr for CPU %x\n", c->pir); return OPAL_UNSUPPORTED; } rc = p8_pore_gen_cpureg_fixed(image, P8_SLW_MODEBUILD_SRAM, sprn, val, cpu_get_core_index(c), cpu_get_thread_index(c)); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to set spr for CPU %x\n", c->pir); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } opal_call(OPAL_SLW_SET_REG, opal_slw_set_reg, 3); #endif /* __HAVE_LIBPORE__ */ static void slw_dump_timer_ffdc(void) { uint64_t i, val; int64_t rc; static const uint32_t dump_regs[] = { 0xe0000, 0xe0001, 0xe0002, 0xe0003, 0xe0004, 0xe0005, 0xe0006, 0xe0007, 0xe0008, 0xe0009, 0xe000a, 0xe000b, 0xe000c, 0xe000d, 0xe000e, 0xe000f, 0xe0010, 0xe0011, 0xe0012, 0xe0013, 0xe0014, 0xe0015, 0xe0016, 0xe0017, 0xe0018, 0xe0019, 0x5001c, 0x50038, 0x50039, 0x5003a, 0x5003b }; prlog(PR_ERR, "SLW: Register state:\n"); for (i = 0; i < ARRAY_SIZE(dump_regs); i++) { uint32_t reg = dump_regs[i]; rc = xscom_read(slw_timer_chip, reg, &val); if (rc) { prlog(PR_ERR, "SLW: XSCOM error %lld reading" " reg 0x%x\n", rc, reg); break; } prlog(PR_ERR, "SLW: %5x = %016llx\n", reg, val); } } /* This is called with the timer lock held, so there is no * issue with re-entrancy or concurrence */ void slw_update_timer_expiry(uint64_t new_target) { uint64_t count, gen, gen2, req, now = mftb(); int64_t rc; if (!slw_has_timer || new_target == slw_timer_target) return; slw_timer_target = new_target; /* Calculate how many increments from now, rounded up */ if (now < new_target) count = (new_target - now + slw_timer_inc - 1) / slw_timer_inc; else count = 1; /* Max counter is 24-bit */ if (count > 0xffffff) count = 0xffffff; /* Fabricate update request */ req = (1ull << 63) | (count << 32); prlog(PR_TRACE, "SLW: TMR expiry: 0x%llx, req: %016llx\n", count, req); do { /* Grab generation and spin if odd */ for (;;) { rc = xscom_read(slw_timer_chip, 0xE0006, &gen); if (rc) { prerror("SLW: Error %lld reading tmr gen " " count\n", rc); return; } if (!(gen & 1)) break; if (tb_compare(now + msecs_to_tb(1), mftb()) == TB_ABEFOREB) { prerror("SLW: Stuck with odd generation !\n"); slw_has_timer = false; slw_dump_timer_ffdc(); return; } } rc = xscom_write(slw_timer_chip, 0x5003A, req); if (rc) { prerror("SLW: Error %lld writing tmr request\n", rc); return; } /* Re-check gen count */ rc = xscom_read(slw_timer_chip, 0xE0006, &gen2); if (rc) { prerror("SLW: Error %lld re-reading tmr gen " " count\n", rc); return; } } while(gen != gen2); /* Check if the timer is working. If at least 1ms has elapsed * since the last call to this function, check that the gen * count has changed */ if (tb_compare(slw_last_gen_stamp + msecs_to_tb(1), now) == TB_ABEFOREB) { if (slw_last_gen == gen) { prlog(PR_ERR, "SLW: Timer appears to not be running !\n"); slw_has_timer = false; slw_dump_timer_ffdc(); } slw_last_gen = gen; slw_last_gen_stamp = mftb(); } prlog(PR_TRACE, "SLW: gen: %llx\n", gen); } bool slw_timer_ok(void) { return slw_has_timer; } static void slw_init_timer(void) { struct dt_node *np; int64_t rc; uint32_t tick_us; np = dt_find_compatible_node(dt_root, NULL, "ibm,power8-sbe-timer"); if (!np) return; slw_timer_chip = dt_get_chip_id(np); tick_us = dt_prop_get_u32(np, "tick-time-us"); slw_timer_inc = usecs_to_tb(tick_us); slw_timer_target = ~0ull; rc = xscom_read(slw_timer_chip, 0xE0006, &slw_last_gen); if (rc) { prerror("SLW: Error %lld reading tmr gen count\n", rc); return; } slw_last_gen_stamp = mftb(); prlog(PR_INFO, "SLW: Timer facility on chip %d, resolution %dus\n", slw_timer_chip, tick_us); slw_has_timer = true; } void slw_init(void) { struct proc_chip *chip; if (proc_gen != proc_gen_p8) return; for_each_chip(chip) slw_init_chip(chip); add_cpu_idle_state_properties(); slw_init_timer(); } skiboot-skiboot-5.1.13/hw/xscom.c000066400000000000000000000320031265204436200166400ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include /* Mask of bits to clear in HMER before an access */ #define HMER_CLR_MASK (~(SPR_HMER_XSCOM_FAIL | \ SPR_HMER_XSCOM_DONE | \ SPR_HMER_XSCOM_STATUS)) DEFINE_LOG_ENTRY(OPAL_RC_XSCOM_RW, OPAL_PLATFORM_ERR_EVT, OPAL_XSCOM, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_XSCOM_INDIRECT_RW, OPAL_PLATFORM_ERR_EVT, OPAL_XSCOM, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_XSCOM_RESET, OPAL_PLATFORM_ERR_EVT, OPAL_XSCOM, OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); /* xscom details to trigger xstop */ static struct { uint64_t addr; uint64_t fir_bit; } xstop_xscom; /* * Locking notes: * * We used to have a per-target lock. However due to errata HW822317 * we can have issues on the issuer side if multiple threads try to * send XSCOMs simultaneously (HMER responses get mixed up), so just * use a global lock instead */ static struct lock xscom_lock = LOCK_UNLOCKED; static inline void *xscom_addr(uint32_t gcid, uint32_t pcb_addr) { struct proc_chip *chip = get_chip(gcid); uint64_t addr; assert(chip); addr = chip->xscom_base; addr |= ((uint64_t)pcb_addr << 4) & ~0xfful; addr |= (pcb_addr << 3) & 0x78; return (void *)addr; } static uint64_t xscom_wait_done(void) { uint64_t hmer; do hmer = mfspr(SPR_HMER); while(!(hmer & SPR_HMER_XSCOM_DONE)); /* * HW822317: We need to read a second time as the actual * status can be delayed by 1 cycle after DONE */ return mfspr(SPR_HMER); } static void xscom_reset(uint32_t gcid) { u64 hmer; /* Clear errors in HMER */ mtspr(SPR_HMER, HMER_CLR_MASK); /* First we need to write 0 to a register on our chip */ out_be64(xscom_addr(this_cpu()->chip_id, 0x202000f), 0); hmer = xscom_wait_done(); if (hmer & SPR_HMER_XSCOM_FAIL) goto fail; /* Then we need to clear those two other registers on the target */ out_be64(xscom_addr(gcid, 0x2020007), 0); hmer = xscom_wait_done(); if (hmer & SPR_HMER_XSCOM_FAIL) goto fail; out_be64(xscom_addr(gcid, 0x2020009), 0); hmer = xscom_wait_done(); if (hmer & SPR_HMER_XSCOM_FAIL) goto fail; return; fail: /* Fatal error resetting XSCOM */ log_simple_error(&e_info(OPAL_RC_XSCOM_RESET), "XSCOM: Fatal error resetting engine after failed access !\n"); /* XXX Generate error log ? attn ? panic ? * If we decide to panic, change the above severity to PANIC */ } static bool xscom_handle_error(uint64_t hmer, uint32_t gcid, uint32_t pcb_addr, bool is_write) { unsigned int stat = GETFIELD(SPR_HMER_XSCOM_STATUS, hmer); /* XXX Figure out error codes from doc and error * recovery procedures */ switch(stat) { /* XSCOM blocked, just retry */ case 1: return true; } /* XXX: Create error log entry ? */ log_simple_error(&e_info(OPAL_RC_XSCOM_RW), "XSCOM: %s error gcid=0x%x pcb_addr=0x%x stat=0x%x\n", is_write ? "write" : "read", gcid, pcb_addr, stat); /* We need to reset the XSCOM or we'll hang on the next access */ xscom_reset(gcid); /* Non recovered ... just fail */ return false; } static void xscom_handle_ind_error(uint64_t data, uint32_t gcid, uint64_t pcb_addr, bool is_write) { unsigned int stat = GETFIELD(XSCOM_DATA_IND_ERR, data); bool timeout = !(data & XSCOM_DATA_IND_COMPLETE); /* XXX: Create error log entry ? */ if (timeout) log_simple_error(&e_info(OPAL_RC_XSCOM_INDIRECT_RW), "XSCOM: indirect %s timeout, gcid=0x%x pcb_addr=0x%llx" " stat=0x%x\n", is_write ? "write" : "read", gcid, pcb_addr, stat); else log_simple_error(&e_info(OPAL_RC_XSCOM_INDIRECT_RW), "XSCOM: indirect %s error, gcid=0x%x pcb_addr=0x%llx" " stat=0x%x\n", is_write ? "write" : "read", gcid, pcb_addr, stat); } static bool xscom_gcid_ok(uint32_t gcid) { return get_chip(gcid) != NULL; } /* * Low level XSCOM access functions, perform a single direct xscom * access via MMIO */ static int __xscom_read(uint32_t gcid, uint32_t pcb_addr, uint64_t *val) { uint64_t hmer; if (!xscom_gcid_ok(gcid)) { prerror("%s: invalid XSCOM gcid 0x%x\n", __func__, gcid); return OPAL_PARAMETER; } for (;;) { /* Clear status bits in HMER (HMER is special * writing to it *ands* bits */ mtspr(SPR_HMER, HMER_CLR_MASK); /* Read value from SCOM */ *val = in_be64(xscom_addr(gcid, pcb_addr)); /* Wait for done bit */ hmer = xscom_wait_done(); /* Check for error */ if (!(hmer & SPR_HMER_XSCOM_FAIL)) break; /* Handle error and eventually retry */ if (!xscom_handle_error(hmer, gcid, pcb_addr, false)) return OPAL_HARDWARE; } return 0; } static int __xscom_write(uint32_t gcid, uint32_t pcb_addr, uint64_t val) { uint64_t hmer; if (!xscom_gcid_ok(gcid)) { prerror("%s: invalid XSCOM gcid 0x%x\n", __func__, gcid); return OPAL_PARAMETER; } for (;;) { /* Clear status bits in HMER (HMER is special * writing to it *ands* bits */ mtspr(SPR_HMER, HMER_CLR_MASK); /* Write value to SCOM */ out_be64(xscom_addr(gcid, pcb_addr), val); /* Wait for done bit */ hmer = xscom_wait_done(); /* Check for error */ if (!(hmer & SPR_HMER_XSCOM_FAIL)) break; /* Handle error and eventually retry */ if (!xscom_handle_error(hmer, gcid, pcb_addr, true)) return OPAL_HARDWARE; } return 0; } /* * Indirect XSCOM access functions */ static int xscom_indirect_read(uint32_t gcid, uint64_t pcb_addr, uint64_t *val) { uint32_t addr; uint64_t data; int rc, retries; if (proc_gen != proc_gen_p8) { *val = (uint64_t)-1; return OPAL_UNSUPPORTED; } /* Write indirect address */ addr = pcb_addr & 0x7fffffff; data = XSCOM_DATA_IND_READ | (pcb_addr & XSCOM_ADDR_IND_ADDR); rc = __xscom_write(gcid, addr, data); if (rc) goto bail; /* Wait for completion */ for (retries = 0; retries < XSCOM_IND_MAX_RETRIES; retries++) { rc = __xscom_read(gcid, addr, &data); if (rc) goto bail; if ((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) { *val = data & XSCOM_DATA_IND_DATA; break; } if ((data & XSCOM_DATA_IND_COMPLETE) || (retries >= XSCOM_IND_MAX_RETRIES)) { xscom_handle_ind_error(data, gcid, pcb_addr, false); rc = OPAL_HARDWARE; goto bail; } } bail: if (rc) *val = (uint64_t)-1; return rc; } static int xscom_indirect_write(uint32_t gcid, uint64_t pcb_addr, uint64_t val) { uint32_t addr; uint64_t data; int rc, retries; if (proc_gen != proc_gen_p8) return OPAL_UNSUPPORTED; /* Write indirect address & data */ addr = pcb_addr & 0x7fffffff; data = pcb_addr & XSCOM_ADDR_IND_ADDR; data |= val & XSCOM_ADDR_IND_DATA; rc = __xscom_write(gcid, addr, data); if (rc) goto bail; /* Wait for completion */ for (retries = 0; retries < XSCOM_IND_MAX_RETRIES; retries++) { rc = __xscom_read(gcid, addr, &data); if (rc) goto bail; if ((data & XSCOM_DATA_IND_COMPLETE) && ((data & XSCOM_DATA_IND_ERR) == 0)) break; if ((data & XSCOM_DATA_IND_COMPLETE) || (retries >= XSCOM_IND_MAX_RETRIES)) { xscom_handle_ind_error(data, gcid, pcb_addr, true); rc = OPAL_HARDWARE; goto bail; } } bail: return rc; } static uint32_t xscom_decode_chiplet(uint32_t partid, uint64_t *pcb_addr) { uint32_t gcid = (partid & 0x0fffffff) >> 4; uint32_t core = partid & 0xf; *pcb_addr |= P8_EX_PCB_SLAVE_BASE; *pcb_addr |= core << 24; return gcid; } /* * External API */ int xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val) { uint32_t gcid; int rc; /* Handle part ID decoding */ switch(partid >> 28) { case 0: /* Normal processor chip */ gcid = partid; break; case 8: /* Centaur */ return centaur_xscom_read(partid, pcb_addr, val); case 4: /* EX chiplet */ gcid = xscom_decode_chiplet(partid, &pcb_addr); break; default: return OPAL_PARAMETER; } /* HW822317 requires us to do global locking */ lock(&xscom_lock); /* Direct vs indirect access */ if (pcb_addr & XSCOM_ADDR_IND_FLAG) rc = xscom_indirect_read(gcid, pcb_addr, val); else rc = __xscom_read(gcid, pcb_addr & 0x7fffffff, val); /* Unlock it */ unlock(&xscom_lock); return rc; } opal_call(OPAL_XSCOM_READ, xscom_read, 3); int xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val) { uint32_t gcid; int rc; /* Handle part ID decoding */ switch(partid >> 28) { case 0: /* Normal processor chip */ gcid = partid; break; case 8: /* Centaur */ return centaur_xscom_write(partid, pcb_addr, val); case 4: /* EX chiplet */ gcid = xscom_decode_chiplet(partid, &pcb_addr); break; default: return OPAL_PARAMETER; } /* HW822317 requires us to do global locking */ lock(&xscom_lock); /* Direct vs indirect access */ if (pcb_addr & XSCOM_ADDR_IND_FLAG) rc = xscom_indirect_write(gcid, pcb_addr, val); else rc = __xscom_write(gcid, pcb_addr & 0x7fffffff, val); /* Unlock it */ unlock(&xscom_lock); return rc; } opal_call(OPAL_XSCOM_WRITE, xscom_write, 3); int xscom_readme(uint64_t pcb_addr, uint64_t *val) { return xscom_read(this_cpu()->chip_id, pcb_addr, val); } int xscom_writeme(uint64_t pcb_addr, uint64_t val) { return xscom_write(this_cpu()->chip_id, pcb_addr, val); } int64_t xscom_read_cfam_chipid(uint32_t partid, uint32_t *chip_id) { uint64_t val; int64_t rc = OPAL_SUCCESS; /* Mambo chip model lacks the f000f register, just make * something up (Murano DD2.1) */ if (chip_quirk(QUIRK_NO_F000F)) val = 0x221EF04980000000; else rc = xscom_read(partid, 0xf000f, &val); /* Extract CFAM id */ if (rc == OPAL_SUCCESS) *chip_id = (uint32_t)(val >> 44); return rc; } static void xscom_init_chip_info(struct proc_chip *chip) { uint32_t val; int64_t rc; rc = xscom_read_cfam_chipid(chip->id, &val); if (rc) { prerror("XSCOM: Error %lld reading 0xf000f register\n", rc); /* We leave chip type to UNKNOWN */ return; } /* Identify chip */ switch(val & 0xff) { case 0xf9: chip->type = PROC_CHIP_P7; assert(proc_gen == proc_gen_p7); break; case 0xe8: chip->type = PROC_CHIP_P7P; assert(proc_gen == proc_gen_p7); break; case 0xef: chip->type = PROC_CHIP_P8_MURANO; assert(proc_gen == proc_gen_p8); break; case 0xea: chip->type = PROC_CHIP_P8_VENICE; assert(proc_gen == proc_gen_p8); break; case 0xd3: chip->type = PROC_CHIP_P8_NAPLES; assert(proc_gen == proc_gen_p8); break; default: printf("CHIP: Unknown chip type 0x%02x !!!\n", (unsigned char)(val & 0xff)); } /* Get EC level from CFAM ID */ chip->ec_level = ((val >> 16) & 0xf) << 4; chip->ec_level |= (val >> 8) & 0xf; } /* * This function triggers xstop by writing to XSCOM. * Machine would enter xstop state post completion of this. */ int64_t xscom_trigger_xstop(void) { int rc = OPAL_UNSUPPORTED; if (xstop_xscom.addr) rc = xscom_writeme(xstop_xscom.addr, PPC_BIT(xstop_xscom.fir_bit)); return rc; } void xscom_init(void) { struct dt_node *xn; const struct dt_property *p; dt_for_each_compatible(dt_root, xn, "ibm,xscom") { uint32_t gcid = dt_get_chip_id(xn); const struct dt_property *reg; struct proc_chip *chip; const char *chip_name; static const char *chip_names[] = { "UNKNOWN", "P7", "P7+", "P8E", "P8", "P8NVL", }; chip = get_chip(gcid); assert(chip); /* XXX We need a proper address parsing. For now, we just * "know" that we are looking at a u64 */ reg = dt_find_property(xn, "reg"); assert(reg); chip->xscom_base = dt_translate_address(xn, 0, NULL); /* Grab processor type and EC level */ xscom_init_chip_info(chip); if (chip->type >= ARRAY_SIZE(chip_names)) chip_name = "INVALID"; else chip_name = chip_names[chip->type]; printf("XSCOM: chip 0x%x at 0x%llx [%s DD%x.%x]\n", gcid, chip->xscom_base, chip_name, chip->ec_level >> 4, chip->ec_level & 0xf); } /* Collect details to trigger xstop via XSCOM write */ p = dt_find_property(dt_root, "ibm,sw-checkstop-fir"); if (p) { xstop_xscom.addr = dt_property_get_cell(p, 0); xstop_xscom.fir_bit = dt_property_get_cell(p, 1); prlog(PR_INFO, "XSTOP: XSCOM addr = 0x%llx, FIR bit = %lld\n", xstop_xscom.addr, xstop_xscom.fir_bit); } else prlog(PR_INFO, "XSTOP: ibm,sw-checkstop-fir prop not found\n"); } void xscom_used_by_console(void) { xscom_lock.in_con_path = true; /* * Some other processor might hold it without having * disabled the console locally so let's make sure that * is over by taking/releasing the lock ourselves */ lock(&xscom_lock); unlock(&xscom_lock); } bool xscom_ok(void) { return !lock_held_by_me(&xscom_lock); } skiboot-skiboot-5.1.13/include/000077500000000000000000000000001265204436200163525ustar00rootroot00000000000000skiboot-skiboot-5.1.13/include/affinity.h000066400000000000000000000017271265204436200203430ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * * All functions in charge of generating the associativity/affinity * properties in the device-tree */ #ifndef __AFFINITY_H #define __AFFINITY_H struct dt_node; struct cpu_thread; extern void add_associativity_ref_point(void); extern void add_chip_dev_associativity(struct dt_node *dev); extern void add_core_associativity(struct cpu_thread *cpu); #endif /* __AFFINITY_H */ skiboot-skiboot-5.1.13/include/asm-utils.h000066400000000000000000000025021265204436200204400ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ASM_UTILS_H #define __ASM_UTILS_H /* * Do NOT use the immediate load helpers with symbols * only with constants. Symbols will _not_ be resolved * by the linker since we are building -pie, and will * instead generate relocs of a type our little built-in * relocator can't handle */ /* Load an immediate 64-bit value into a register */ #define LOAD_IMM64(r, e) \ lis r,(e)@highest; \ ori r,r,(e)@higher; \ rldicr r,r, 32, 31; \ oris r,r, (e)@h; \ ori r,r, (e)@l; /* Load an immediate 32-bit value into a register */ #define LOAD_IMM32(r, e) \ lis r,(e)@h; \ ori r,r,(e)@l; /* Load an address via the TOC */ #define LOAD_ADDR_FROM_TOC(r, e) ld r,e@got(%r2) #endif /* __ASM_UTILS_H */ skiboot-skiboot-5.1.13/include/ast.h000066400000000000000000000052561265204436200173220ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __AST_H #define __AST_H /* * AHB bus registers */ /* SPI Flash controller #1 (BMC) */ #define BMC_SPI_FCTL_BASE 0x1E620000 #define BMC_SPI_FCTL_CTRL (BMC_SPI_FCTL_BASE + 0x10) #define BMC_SPI_FREAD_TIMING (BMC_SPI_FCTL_BASE + 0x94) #define BMC_FLASH_BASE 0x20000000 /* SPI Flash controller #2 (PNOR) */ #define PNOR_SPI_FCTL_BASE 0x1E630000 #define PNOR_SPI_FCTL_CONF (PNOR_SPI_FCTL_BASE + 0x00) #define PNOR_SPI_FCTL_CTRL (PNOR_SPI_FCTL_BASE + 0x04) #define PNOR_SPI_FREAD_TIMING (PNOR_SPI_FCTL_BASE + 0x14) #define PNOR_FLASH_BASE 0x30000000 /* LPC registers */ #define LPC_BASE 0x1e789000 #define LPC_HICR6 (LPC_BASE + 0x80) #define LPC_HICR7 (LPC_BASE + 0x88) #define LPC_HICR8 (LPC_BASE + 0x8c) #define LPC_iBTCR0 (LPC_BASE + 0x140) /* VUART1 */ #define VUART1_BASE 0x1e787000 #define VUART1_GCTRLA (VUART1_BASE + 0x20) #define VUART1_GCTRLB (VUART1_BASE + 0x24) #define VUART1_ADDRL (VUART1_BASE + 0x28) #define VUART1_ADDRH (VUART1_BASE + 0x2c) /* SCU registers */ #define SCU_BASE 0x1e6e2000 #define SCU_HW_STRAPPING (SCU_BASE + 0x70) /* * AHB Accessors */ #ifndef __SKIBOOT__ #include "io.h" #else /* * Register accessors, return byteswapped values * (IE. LE registers) */ void ast_ahb_writel(uint32_t val, uint32_t reg); uint32_t ast_ahb_readl(uint32_t reg); /* * copy to/from accessors. Cannot cross IDSEL boundaries (256M) */ int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len); int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len); void ast_io_init(void); bool ast_is_ahb_lpc_pnor(void); /* UART configuration */ bool ast_is_vuart1_enabled(void); void ast_setup_vuart1(uint16_t io_base, uint8_t irq); void ast_setup_sio_uart1(uint16_t io_base, uint8_t irq); void ast_disable_sio_uart1(void); /* BT configuration */ void ast_setup_ibt(uint16_t io_base, uint8_t irq); #endif /* __SKIBOOT__ */ /* * SPI Flash controllers */ #define AST_SF_TYPE_PNOR 0 #define AST_SF_TYPE_BMC 1 #define AST_SF_TYPE_MEM 2 struct spi_flash_ctrl; int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl); void ast_sf_close(struct spi_flash_ctrl *ctrl); #endif /* __AST_H */ skiboot-skiboot-5.1.13/include/bitutils.h000066400000000000000000000032051265204436200203620ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __BITUTILS_H #define __BITUTILS_H /* PPC bit number conversion */ #ifdef __ASSEMBLY__ #define PPC_BIT(bit) (0x8000000000000000 >> (bit)) #define PPC_BIT32(bit) (0x80000000 >> (bit)) #define PPC_BIT8(bit) (0x80 >> (bit)) #else #define PPC_BIT(bit) (0x8000000000000000UL >> (bit)) #define PPC_BIT32(bit) (0x80000000UL >> (bit)) #define PPC_BIT8(bit) (0x80UL >> (bit)) #endif #define PPC_BITMASK(bs,be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) #define PPC_BITMASK32(bs,be) ((PPC_BIT32(bs) - PPC_BIT32(be))|PPC_BIT32(bs)) #define PPC_BITLSHIFT(be) (63 - (be)) #define PPC_BITLSHIFT32(be) (31 - (be)) /* * PPC bitmask field manipulation */ /* Find left shift from first set bit in mask */ #define MASK_TO_LSH(m) (__builtin_ffsl(m) - 1) /* Extract field fname from val */ #define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) /* Set field fname of oval to fval * NOTE: oval isn't modified, the combined result is returned */ #define SETFIELD(m, v, val) \ (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) #endif /* __BITUTILS_H */ skiboot-skiboot-5.1.13/include/bt.h000066400000000000000000000012611265204436200171300ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __BT_H #define __BT_H /* Initialise the BT interface */ void bt_init(void); #endif skiboot-skiboot-5.1.13/include/capp.h000066400000000000000000000044771265204436200174620ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ struct capp_lid_hdr { be64 eyecatcher; /* 'CAPPLIDH' in ASCII */ be64 version; be64 lid_no; be64 pad; be64 ucode_offset; be64 total_size; }; struct capp_ucode_data_hdr { be64 eyecatcher; /* 'CAPPUCOD' in ASCII */ u8 version; u8 reg; u8 reserved[2]; be32 chunk_count; /* Num of 8-byte chunks that follow */ }; struct capp_ucode_data { struct capp_ucode_data_hdr hdr; be64 data[]; }; struct capp_ucode_lid { be64 eyecatcher; /* 'CAPPULID' in ASCII */ be64 version; be64 data_size; /* Total size of all capp microcode data */ u8 reserved[40]; struct capp_ucode_data data; /* This repeats */ }; enum capp_reg { apc_master_cresp = 0x1, apc_master_uop_table = 0x2, snp_ttype = 0x3, snp_uop_table = 0x4, apt_master_capi_ctrl = 0x5, snoop_capi_cnfg = 0x6, canned_presp_map0 = 0x7, canned_presp_map1 = 0x8, canned_presp_map2 = 0x9, flush_sue_state_map = 0xA, apc_master_powerbus_ctrl = 0xB }; #define CAPP_SNP_ARRAY_ADDR_REG 0x2013028 #define CAPP_APC_MASTER_ARRAY_ADDR_REG 0x201302A #define CAPP_SNP_ARRAY_WRITE_REG 0x2013801 #define CAPP_APC_MASTER_ARRAY_WRITE_REG 0x2013802 #define CAPP_FIR 0x2013000 #define CAPP_ERR_RPT_CLR 0x2013013 #define APC_MASTER_PB_CTRL 0x2013018 #define APC_MASTER_CAPI_CTRL 0x2013019 #define TRANSPORT_CONTROL 0x201301C #define CANNED_PRESP_MAP0 0x201301D #define CANNED_PRESP_MAP1 0x201301E #define CANNED_PRESP_MAP2 0x201301F #define CAPP_ERR_STATUS_CTRL 0x201300E #define FLUSH_SUE_STATE_MAP 0x201300F #define CAPP_TB 0x2013026 #define CAPP_TFMR 0x2013027 #define CAPP_EPOCH_TIMER_CTRL 0x201302C #define FLUSH_UOP_CONFIG1 0x2013803 #define FLUSH_UOP_CONFIG2 0x2013804 #define SNOOP_CAPI_CONFIG 0x201301A skiboot-skiboot-5.1.13/include/cec.h000066400000000000000000000025451265204436200172630ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CEC_H #define __CEC_H #include /* This represent an IO Hub and contains the function pointers * for the IO Hub related OPAL ops and other internal functions */ struct io_hub; struct io_hub_ops { /* OPAL_PCI_SET_HUB_TCE_MEMORY (p5ioc2 only) */ int64_t (*set_tce_mem)(struct io_hub *hub, uint64_t address, uint64_t size); /* OPAL_PCI_GET_HUB_DIAG_DATA */ int64_t (*get_diag_data)(struct io_hub *hub, void *diag_buffer, uint64_t diag_buffer_len); /* Called on fast reset */ void (*reset)(struct io_hub *hub); }; struct io_hub { uint32_t hub_id; const struct io_hub_ops *ops; }; extern struct io_hub *cec_get_hub_by_id(uint32_t hub_id); extern void cec_reset(void); extern void cec_register(struct io_hub *hub); #endif /* __CEC_H */ skiboot-skiboot-5.1.13/include/centaur.h000066400000000000000000000026641265204436200201740ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CENTAUR_H #define __CENTAUR_H #include #include #include struct centaur_chip { bool valid; uint8_t ec_level; uint32_t part_id; uint32_t fsi_master_chip_id; uint32_t fsi_master_port; uint32_t fsi_master_engine; uint32_t scache_disable_count; bool scache_was_enabled; struct lock lock; /* Used by hw/p8-i2c.c */ struct list_head i2cms; }; extern int64_t centaur_disable_sensor_cache(uint32_t part_id); extern int64_t centaur_enable_sensor_cache(uint32_t part_id); extern int64_t centaur_xscom_read(uint32_t id, uint64_t pcb_addr, uint64_t *val) __warn_unused_result; extern int64_t centaur_xscom_write(uint32_t id, uint64_t pcb_addr, uint64_t val) __warn_unused_result; extern void centaur_init(void); extern struct centaur_chip *get_centaur(uint32_t part_id); #endif /* __CENTAUR_H */ skiboot-skiboot-5.1.13/include/chip.h000066400000000000000000000106011265204436200174440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHIP_H #define __CHIP_H #include #include #include /* * Note on chip IDs: * * We carry a "chip_id" around, in the cpu_thread, but also as * ibm,chip-id properties. * * This ID is the HW fabric ID of a chip based on the XSCOM numbering, * also known as "GCID" (Global Chip ID). * * The format of this number is different between P7 and P8 and care must * be taken when trying to convert between this chip ID and some other * representation such as PIR values, interrupt-server numbers etc... : * * P7 GCID * ------- * * Global chip ID is a 6 bit number: * * NodeID T ChipID * | | | | * |___|___|___|___|___|___| * * Where T is the "torrent" bit and is 0 for P7 chips and 1 for * directly XSCOM'able IO chips such as Torrent * * This macro converts a PIR to a GCID */ #define P7_PIR2GCID(pir) ({ \ uint32_t _pir = pir; \ ((_pir >> 4) & 0x38) | ((_pir >> 5) & 0x3); }) #define P7_PIR2COREID(pir) (((pir) >> 2) & 0x7) #define P7_PIR2THREADID(pir) ((pir) & 0x3) /* * P8 GCID * ------- * * Global chip ID is a 6 bit number: * * NodeID ChipID * | | | * |___|___|___|___|___|___| * * The difference with P7 is the absence of T bit, the ChipID * is 3 bits long. The GCID is thus the same as the high bits * if the PIR */ #define P8_PIR2GCID(pir) (((pir) >> 7) & 0x3f) #define P8_PIR2COREID(pir) (((pir) >> 3) & 0xf) #define P8_PIR2THREADID(pir) ((pir) & 0x7) struct dt_node; struct centaur_chip; struct mfsi; /* Chip type */ enum proc_chip_type { PROC_CHIP_UNKNOWN, PROC_CHIP_P7, PROC_CHIP_P7P, PROC_CHIP_P8_MURANO, PROC_CHIP_P8_VENICE, PROC_CHIP_P8_NAPLES, }; /* Simulator quirks */ enum proc_chip_quirks { QUIRK_NO_CHIPTOD = 0x00000001, QUIRK_MAMBO_CALLOUTS = 0x00000002, QUIRK_NO_F000F = 0x00000004, QUIRK_NO_PBA = 0x00000008, QUIRK_NO_OCC_IRQ = 0x00000010, QUIRK_DISABLE_NAP = 0x00000020, } proc_chip_quirks; static inline bool chip_quirk(unsigned int q) { return !!(proc_chip_quirks & q); } #define MAX_CHIPS (1 << 6) /* 6-bit chip ID */ /* * For each chip in the system, we maintain this structure * * This contains fields used by different modules including * modules in hw/ but is handy to keep per-chip data */ struct proc_chip { uint32_t id; /* HW Chip ID (GCID) */ struct dt_node *devnode; /* "xscom" chip node */ /* These are only initialized after xcom_init */ enum proc_chip_type type; uint32_t ec_level; /* 0xMm (DD1.0 = 0x10) */ /* Those two values are only populated on machines with an FSP * dbob_id = Drawer/Block/Octant/Blade (DBOBID) * pcid = HDAT processor_chip_id */ uint32_t dbob_id; uint32_t pcid; /* Used by hw/xscom.c */ uint64_t xscom_base; /* Used by hw/lpc.c */ uint32_t lpc_xbase; struct lock lpc_lock; uint8_t lpc_fw_idsel; uint8_t lpc_fw_rdsz; struct list_head lpc_clients; /* Used by hw/slw.c */ uint64_t slw_base; uint64_t slw_bar_size; uint64_t slw_image_size; /* Used by hw/homer.c */ uint64_t homer_base; uint64_t homer_size; uint64_t occ_common_base; uint64_t occ_common_size; u8 throttle; /* Must hold capi_lock to change */ u8 capp_phb3_attached_mask; bool capp_ucode_loaded; /* Used by hw/centaur.c */ struct centaur_chip *centaurs; /* Used by hw/p8-i2c.c */ struct list_head i2cms; /* Used by hw/psi.c */ struct psi *psi; /* Used by hw/fsi-master.c */ struct mfsi *fsi_masters; }; extern uint32_t pir_to_chip_id(uint32_t pir); extern uint32_t pir_to_core_id(uint32_t pir); extern uint32_t pir_to_thread_id(uint32_t pir); extern struct proc_chip *next_chip(struct proc_chip *chip); #define for_each_chip(__c) for (__c=next_chip(NULL); __c; __c=next_chip(__c)) extern struct proc_chip *get_chip(uint32_t chip_id); extern void init_chips(void); #endif /* __CHIP_H */ skiboot-skiboot-5.1.13/include/chiptod.h000066400000000000000000000022151265204436200201550ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHIPTOD_H #define __CHIPTOD_H /* The ChipTOD is the HW facility that maintains a synchronized * time base across the fabric. */ enum chiptod_topology { chiptod_topo_unknown = -1, chiptod_topo_primary = 0, chiptod_topo_secondary = 1, }; extern void chiptod_init(void); extern bool chiptod_wakeup_resync(void); extern int chiptod_recover_tb_errors(void); extern void chiptod_reset_tb(void); extern bool chiptod_adjust_topology(enum chiptod_topology topo, bool enable); extern bool chiptod_capp_timebase_sync(uint32_t chip_id); #endif /* __CHIPTOD_H */ skiboot-skiboot-5.1.13/include/compiler.h000066400000000000000000000030301265204436200203310ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __COMPILER_H #define __COMPILER_H #ifndef __ASSEMBLY__ #include /* Macros for various compiler bits and pieces */ #define __packed __attribute__((packed)) #define __align(x) __attribute__((__aligned__(x))) #define __unused __attribute__((unused)) #define __used __attribute__((used)) #define __section(x) __attribute__((__section__(x))) #define __noreturn __attribute__((noreturn)) /* not __const as this has a different meaning (const) */ #define __attrconst __attribute__((const)) #define __warn_unused_result __attribute__((warn_unused_result)) #if 0 /* Provided by gcc stddef.h */ #define offsetof(type,m) __builtin_offsetof(type,m) #endif #define __nomcount __attribute__((no_instrument_function)) /* Compiler barrier */ static inline void barrier(void) { asm volatile("" : : : "memory"); } #endif /* __ASSEMBLY__ */ /* Stringification macro */ #define __tostr(x) #x #define tostr(x) __tostr(x) #endif /* __COMPILER_H */ skiboot-skiboot-5.1.13/include/config.h000066400000000000000000000055361265204436200200010ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CONFIG_H #define __CONFIG_H #define HAVE_TYPEOF 1 #define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 /* Keep -Wundef happy by defining whatever isn't on commandline to 0 */ #if defined(HAVE_LITTLE_ENDIAN) && HAVE_LITTLE_ENDIAN #define HAVE_BIG_ENDIAN 0 #endif #if defined(HAVE_BIG_ENDIAN) && HAVE_BIG_ENDIAN #define HAVE_LITTLE_ENDIAN 0 #endif /* We don't have a byteswap.h, and thus no bswap_64 */ #define HAVE_BYTESWAP_H 0 #define HAVE_BSWAP_64 0 /* * Build options. */ /* Enable lock debugging */ #define DEBUG_LOCKS 1 /* Enable malloc debugging */ #define DEBUG_MALLOC 1 /* Enable OPAL entry point tracing */ //#define OPAL_TRACE_ENTRY 1 /* Enable tracing of event state change */ //#define OPAL_TRACE_EVT_CHG 1 /* Enable various levels of OPAL_console debug */ //#define OPAL_DEBUG_CONSOLE_IO 1 //#define OPAL_DEBUG_CONSOLE_POLL 1 /* Enable this to force all writes to the in-memory console to * be mirrored on the mambo console */ //#define MAMBO_DEBUG_CONSOLE 1 /* Enable this to hookup SkiBoot log to the DVS console */ #define DVS_CONSOLE 1 /* Enable this to force the dummy console to the kernel. * (ie, an OPAL console that injects into skiboot own console) * Where possible, leave this undefined and enable it dynamically using * the chosen->sapphire,enable-dummy-console in the device tree. * * Note: This only gets enabled if there is no FSP console. If there * is one it always takes over for now. This also cause the LPC UART * node to be marked "reserved" so Linux doesn't instanciate a 8250 * driver for it. */ //#define FORCE_DUMMY_CONSOLE 1 /* Enable this to do fast resets. Currently unreliable... */ //#define ENABLE_FAST_RESET 1 /* Enable this to make fast reboot clear memory */ //#define FAST_REBOOT_CLEARS_MEMORY 1 /* Enable this to disable setting of the output pending event when * sending things on the console. The FSP is very slow to consume * and older kernels wait after each character during early boot so * things get very slow. Eventually, we may want to create an OPAL * API for the kernel to activate or deactivate that functionality */ #define DISABLE_CON_PENDING_EVT 1 /* Configure this to provide some additional kernel command line * arguments to the bootloader */ //#define KERNEL_COMMAND_LINE "debug" #endif /* __CONFIG_H */ skiboot-skiboot-5.1.13/include/console.h000066400000000000000000000041321265204436200201650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CONSOLE_H #define __CONSOLE_H #include "unistd.h" #include /* * Our internal console uses the format of BML new-style in-memory * console and supports input for setups without a physical console * facility or FSP. * * (This is v3 of the format, the previous one sucked) */ struct memcons { uint64_t magic; #define MEMCONS_MAGIC 0x6630696567726173LL uint64_t obuf_phys; uint64_t ibuf_phys; uint32_t obuf_size; uint32_t ibuf_size; uint32_t out_pos; #define MEMCONS_OUT_POS_WRAP 0x80000000u #define MEMCONS_OUT_POS_MASK 0x00ffffffu uint32_t in_prod; uint32_t in_cons; }; extern struct memcons memcons; #define INMEM_CON_IN_LEN 16 #define INMEM_CON_OUT_LEN (INMEM_CON_LEN - INMEM_CON_IN_LEN) /* Console driver */ struct con_ops { size_t (*write)(const char *buf, size_t len); size_t (*read)(char *buf, size_t len); bool (*poll_read)(void); int64_t (*flush)(void); }; extern struct lock con_lock; extern bool dummy_console_enabled(void); extern void force_dummy_console(void); extern bool flush_console(void); extern bool __flush_console(bool flush_to_drivers); extern void set_console(struct con_ops *driver); extern void console_complete_flush(void); extern int mambo_read(void); extern void mambo_write(const char *buf, size_t count); extern void enable_mambo_console(void); ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count); extern void clear_console(void); extern void memcons_add_properties(void); extern void dummy_console_add_nodes(void); #endif /* __CONSOLE_H */ skiboot-skiboot-5.1.13/include/cpu.h000066400000000000000000000162411265204436200173160ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CPU_H #define __CPU_H #include #include #include #include #include #include /* * cpu_thread is our internal structure representing each * thread in the system */ enum cpu_thread_state { cpu_state_no_cpu = 0, /* Nothing there */ cpu_state_unknown, /* In PACA, not called in yet */ cpu_state_unavailable, /* Not available */ cpu_state_present, /* Assumed to spin in asm entry */ cpu_state_active, /* Secondary called in */ cpu_state_os, /* Under OS control */ cpu_state_disabled, /* Disabled by us due to error */ cpu_state_rvwinkle, /* Doing an rvwinkle cycle */ }; struct cpu_job; struct cpu_thread { uint32_t pir; uint32_t server_no; uint32_t chip_id; bool is_secondary; bool current_hile; struct cpu_thread *primary; enum cpu_thread_state state; struct dt_node *node; struct trace_info *trace; uint64_t save_r1; void *icp_regs; uint32_t lock_depth; uint32_t con_suspend; bool con_need_flush; bool in_mcount; bool in_poller; uint32_t hbrt_spec_wakeup; /* primary only */ uint64_t save_l2_fir_action1; uint64_t current_token; #ifdef STACK_CHECK_ENABLED int64_t stack_bot_mark; uint64_t stack_bot_pc; uint64_t stack_bot_tok; #define CPU_BACKTRACE_SIZE 60 struct bt_entry stack_bot_bt[CPU_BACKTRACE_SIZE]; unsigned int stack_bot_bt_count; #endif struct lock job_lock; struct list_head job_queue; /* * Per-core mask tracking for threads in HMI handler and * a cleanup done bit. * [D][TTTTTTTT] * * The member 'core_hmi_state' is primary only. * The 'core_hmi_state_ptr' member from all secondry cpus will point * to 'core_hmi_state' member in primary cpu. */ uint32_t core_hmi_state; /* primary only */ uint32_t *core_hmi_state_ptr; /* Mask to indicate thread id in core. */ uint8_t thread_mask; bool tb_invalid; }; /* This global is set to 1 to allow secondaries to callin, * typically set after the primary has allocated the cpu_thread * array and stacks */ extern unsigned long cpu_secondary_start; /* Max PIR in the system */ extern unsigned int cpu_max_pir; /* Max # of threads per core */ extern unsigned int cpu_thread_count; /* Boot CPU. */ extern struct cpu_thread *boot_cpu; static inline void __nomcount cpu_relax(void) { /* Relax a bit to give sibling threads some breathing space */ smt_low(); smt_very_low(); asm volatile("nop; nop; nop; nop;\n" "nop; nop; nop; nop;\n" "nop; nop; nop; nop;\n" "nop; nop; nop; nop;\n"); smt_medium(); barrier(); } /* Initialize CPUs */ void pre_init_boot_cpu(void); void init_boot_cpu(void); void init_all_cpus(void); /* This brings up our secondaries */ extern void cpu_bringup(void); /* This is called by secondaries as they call in */ extern void cpu_callin(struct cpu_thread *cpu); /* For cpus which fail to call in. */ extern void cpu_remove_node(const struct cpu_thread *t); /* Find CPUs using different methods */ extern struct cpu_thread *find_cpu_by_chip_id(u32 chip_id); extern struct cpu_thread *find_cpu_by_node(struct dt_node *cpu); extern struct cpu_thread *find_cpu_by_server(u32 server_no); extern struct cpu_thread *find_cpu_by_pir(u32 pir); extern struct dt_node *get_cpu_node(u32 pir); /* Iterator */ extern struct cpu_thread *first_cpu(void); extern struct cpu_thread *next_cpu(struct cpu_thread *cpu); /* WARNING: CPUs that have been picked up by the OS are no longer * appearing as available and can not have jobs scheduled * on them. Essentially that means that after the OS is * fully started, all CPUs are seen as unavailable from * this API standpoint. */ static inline bool cpu_is_available(struct cpu_thread *cpu) { return cpu->state == cpu_state_active || cpu->state == cpu_state_rvwinkle; } extern struct cpu_thread *first_available_cpu(void); extern struct cpu_thread *next_available_cpu(struct cpu_thread *cpu); #define for_each_cpu(cpu) \ for (cpu = first_cpu(); cpu; cpu = next_cpu(cpu)) #define for_each_available_cpu(cpu) \ for (cpu = first_available_cpu(); cpu; cpu = next_available_cpu(cpu)) extern struct cpu_thread *first_available_core_in_chip(u32 chip_id); extern struct cpu_thread *next_available_core_in_chip(struct cpu_thread *cpu, u32 chip_id); #define for_each_available_core_in_chip(core, chip_id) \ for (core = first_available_core_in_chip(chip_id); core; \ core = next_available_core_in_chip(core, chip_id)) /* Return the caller CPU (only after init_cpu_threads) */ register struct cpu_thread *__this_cpu asm("r13"); static inline __nomcount struct cpu_thread *this_cpu(void) { return __this_cpu; } /* Get the thread # of a cpu within the core */ static inline uint32_t cpu_get_thread_index(struct cpu_thread *cpu) { return cpu->pir - cpu->primary->pir; } /* Get the core # of a cpu within the core */ extern uint32_t cpu_get_core_index(struct cpu_thread *cpu); /* Get the PIR of thread 0 of the same core */ static inline uint32_t cpu_get_thread0(struct cpu_thread *cpu) { return cpu->primary->pir; } static inline bool cpu_is_thread0(struct cpu_thread *cpu) { return cpu->primary == cpu; } static inline bool cpu_is_sibling(struct cpu_thread *cpu1, struct cpu_thread *cpu2) { return cpu1->primary == cpu2->primary; } /* Called when some error condition requires disabling a core */ void cpu_disable_all_threads(struct cpu_thread *cpu); /* Allocate & queue a job on target CPU */ extern struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu, const char *name, void (*func)(void *data), void *data, bool no_return); static inline struct cpu_job *cpu_queue_job(struct cpu_thread *cpu, const char *name, void (*func)(void *data), void *data) { return __cpu_queue_job(cpu, name, func, data, false); } /* Poll job status, returns true if completed */ extern bool cpu_poll_job(struct cpu_job *job); /* Synchronously wait for a job to complete, this will * continue handling the FSP mailbox if called from the * boot CPU. Set free_it to free it automatically. */ extern void cpu_wait_job(struct cpu_job *job, bool free_it); /* Free a CPU job, only call on a completed job */ extern void cpu_free_job(struct cpu_job *job); /* Called by init to process jobs */ extern void cpu_process_jobs(void); /* Fallback to running jobs synchronously for global jobs */ extern void cpu_process_local_jobs(void); static inline void cpu_give_self_os(void) { __this_cpu->state = cpu_state_os; } extern unsigned long __attrconst cpu_stack_bottom(unsigned int pir); extern unsigned long __attrconst cpu_stack_top(unsigned int pir); #endif /* __CPU_H */ skiboot-skiboot-5.1.13/include/device.h000066400000000000000000000206051265204436200177650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __DEVICE_H #define __DEVICE_H #include #include #include /* Any property or node with this prefix will not be passed to the kernel. */ #define DT_PRIVATE "skiboot," /* * An in-memory representation of a node in the device tree. * * This is trivially flattened into an fdt. * * Note that the add_* routines will make a copy of the name if it's not * a read-only string (ie. usually a string literal). */ struct dt_property { struct list_node list; const char *name; size_t len; char prop[/* len */]; }; struct dt_node { const char *name; struct list_node list; struct list_head properties; struct list_head children; struct dt_node *parent; u32 phandle; }; /* This is shared with device_tree.c .. make it static when * the latter is gone (hopefully soon) */ extern u32 last_phandle; extern struct dt_node *dt_root; extern struct dt_node *dt_chosen; /* Create a root node: ie. a parentless one. */ struct dt_node *dt_new_root(const char *name); /* Graft a root node into this tree. */ bool dt_attach_root(struct dt_node *parent, struct dt_node *root); /* Add a child node. */ struct dt_node *dt_new(struct dt_node *parent, const char *name); struct dt_node *dt_new_addr(struct dt_node *parent, const char *name, uint64_t unit_addr); struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name, uint64_t unit_addr0, uint64_t unit_addr1); /* Copy node to new parent, including properties and subnodes */ struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent); /* Add a property node, various forms. */ struct dt_property *dt_add_property(struct dt_node *node, const char *name, const void *val, size_t size); struct dt_property *dt_add_property_string(struct dt_node *node, const char *name, const char *value); struct dt_property *dt_add_property_nstr(struct dt_node *node, const char *name, const char *value, unsigned int vlen); /* Given out enough GCC extensions, we will achieve enlightenment! */ #define dt_add_property_strings(node, name, ...) \ __dt_add_property_strings((node), ((name)), \ sizeof((const char *[]) { __VA_ARGS__ })/sizeof(const char *), \ __VA_ARGS__) struct dt_property *__dt_add_property_strings(struct dt_node *node, const char *name, int count, ...); /* Given out enough GCC extensions, we will achieve enlightenment! */ #define dt_add_property_cells(node, name, ...) \ __dt_add_property_cells((node), ((name)), \ sizeof((u32[]) { __VA_ARGS__ })/sizeof(u32), \ __VA_ARGS__) struct dt_property *__dt_add_property_cells(struct dt_node *node, const char *name, int count, ...); #define dt_add_property_u64s(node, name, ...) \ __dt_add_property_u64s((node), ((name)), \ sizeof((u64[]) { __VA_ARGS__ })/sizeof(u64), \ __VA_ARGS__) struct dt_property *__dt_add_property_u64s(struct dt_node *node, const char *name, int count, ...); static inline struct dt_property *dt_add_property_u64(struct dt_node *node, const char *name, u64 val) { return dt_add_property_cells(node, name, (u32)(val >> 32), (u32)val); } void dt_del_property(struct dt_node *node, struct dt_property *prop); /* Warning: moves *prop! */ void dt_resize_property(struct dt_property **prop, size_t len); u32 dt_property_get_cell(const struct dt_property *prop, u32 index); /* First child of this node. */ struct dt_node *dt_first(const struct dt_node *root); /* Return next node, or NULL. */ struct dt_node *dt_next(const struct dt_node *root, const struct dt_node *prev); /* Iterate nodes */ #define dt_for_each_node(root, node) \ for (node = dt_first(root); node; node = dt_next(root, node)) #define dt_for_each_child(parent, node) \ list_for_each(&parent->children, node, list) /* Find a string in a string list */ bool dt_prop_find_string(const struct dt_property *p, const char *s); /* Check a compatible property */ bool dt_node_is_compatible(const struct dt_node *node, const char *compat); /* Find a node based on compatible property */ struct dt_node *dt_find_compatible_node(struct dt_node *root, struct dt_node *prev, const char *compat); #define dt_for_each_compatible(root, node, compat) \ for (node = NULL; \ (node = dt_find_compatible_node(root, node, compat)) != NULL;) struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root, struct dt_node *prev, const char *compat, uint32_t chip_id); #define dt_for_each_compatible_on_chip(root, node, compat, chip_id) \ for (node = NULL; \ (node = dt_find_compatible_node_on_chip(root, node,\ compat, chip_id)) != NULL;) /* Build the full path for a node. Return a new block of memory, caller * shall free() it */ char *dt_get_path(const struct dt_node *node); /* Find a node by path */ struct dt_node *dt_find_by_path(struct dt_node *root, const char *path); /* Find a child node by name */ struct dt_node *dt_find_by_name(struct dt_node *root, const char *name); /* Find a node by phandle */ struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle); /* Find a property by name. */ const struct dt_property *dt_find_property(const struct dt_node *node,\ const char *name); const struct dt_property *dt_require_property(const struct dt_node *node, const char *name, int wanted_len); /* non-const variant */ struct dt_property *__dt_find_property(struct dt_node *node, const char *name); /* Find a property by name, check if it's the same as val. */ bool dt_has_node_property(const struct dt_node *node, const char *name, const char *val); /* Free a node (and any children). */ void dt_free(struct dt_node *node); /* Parse an initial fdt */ void dt_expand(const void *fdt); int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node) __warn_unused_result; /* Simplified accessors */ u64 dt_prop_get_u64(const struct dt_node *node, const char *prop); u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def); u32 dt_prop_get_u32(const struct dt_node *node, const char *prop); u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def); const void *dt_prop_get(const struct dt_node *node, const char *prop); const void *dt_prop_get_def(const struct dt_node *node, const char *prop, void *def); const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop, void *def, size_t *len); u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell); u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop, u32 cell, u32 def); /* Parsing helpers */ u32 dt_n_address_cells(const struct dt_node *node); u32 dt_n_size_cells(const struct dt_node *node); u64 dt_get_number(const void *pdata, unsigned int cells); /* Find an ibm,chip-id property in this node; if not found, walk up the parent * nodes. Returns -1 if no chip-id property exists. */ u32 dt_get_chip_id(const struct dt_node *node); /* Address accessors ("reg" properties parsing). No translation, * only support "simple" address forms (1 or 2 cells). Asserts * if address doesn't exist */ u64 dt_get_address(const struct dt_node *node, unsigned int index, u64 *out_size); /* Count "reg" property entries */ unsigned int dt_count_addresses(const struct dt_node *node); /* Address translation * * WARNING: Current implementation is simplified and will not * handle complex address formats with address space indicators * nor will it handle "ranges" translations yet... (XX TODO) */ u64 dt_translate_address(const struct dt_node *node, unsigned int index, u64 *out_size); /* compare function used to sort child nodes by name when added to the * tree. This is mainly here for testing. */ int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b); #endif /* __DEVICE_H */ skiboot-skiboot-5.1.13/include/device_tree.h000066400000000000000000000023371265204436200210060ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __DEVICE_TREE_H #define __DEVICE_TREE_H #include /* Note: Device tree creation has no locks. It's assumed to be done * by a single processor in a non racy way */ void *create_dtb(const struct dt_node *root); /* Helpers to cache errors in fdt; use this instead of fdt_* */ uint32_t dt_begin_node(const char *name); /* returns phandle */ void dt_property_string(const char *name, const char *value); void dt_property_cell(const char *name, u32 cell); void dt_property_cells(const char *name, int count, ...); void dt_property(const char *name, const void *val, size_t size); void dt_end_node(void); #endif /* __DEVICE_TREE_H */ skiboot-skiboot-5.1.13/include/dts.h000066400000000000000000000014521265204436200173170ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __DTS_H #define __DTS_H #include extern int64_t dts_sensor_read(uint32_t sensor_hndl, uint32_t *sensor_data); extern bool dts_sensor_create_nodes(struct dt_node *sensors); #endif /* __DTS_H */ skiboot-skiboot-5.1.13/include/ec/000077500000000000000000000000001265204436200167415ustar00rootroot00000000000000skiboot-skiboot-5.1.13/include/ec/config.h000066400000000000000000000044021265204436200203570ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file config.H * * @brief Definitions for EC configuration values. * */ #ifndef __EC_CONFIG_H_ #define __EC_CONFIG_H_ #include #define EC_RTC_PORT_BASE (0x70) // RTC/CMOS LPC base address #define EC_RTC_BLOCK_SIZE (512) // Size of addressable data in RTC #define EC_RTC_CENTURY (1) // 1 if century format is enabled #if EC_RTC_CENTURY #define EC_RTC_BBRAM_OFFSET (0x33) // Offset of NV data (= size of calendar) #else #define EC_RTC_BBRAM_OFFSET (0x0E) // Offset of NV data (= size of calendar) #endif // #if EC_RTC_CENTURY #define EC_RTCDD_READ_TRIES (2) // Times to try the RTC if updating #define EC_RTCDD_RETRY_DELAY (300000) // Delay between RTC read retries in ns // based on update time of 244 + 30.5 µs #define EC_GPIO_INDEX 0x200 #define EC_GPIO_DATA 0x201 #define EC_GPIO_NUM_PORTS 17 #define EC_GPIO_PORT_SKIP 4 #define EC_GPIO_DATA_OFFSET 0x0 #define EC_GPIO_DDR_OFFSET 0x1 #define EC_GPIO_PIN_OFFSET 0x2 #define EC_GPIO_PUP_OFFSET 0x3 typedef enum EcGpioPort { EC_GPIO_PORT_A = 0, EC_GPIO_PORT_B = 1, EC_GPIO_PORT_C = 2, EC_GPIO_PORT_D = 3, EC_GPIO_PORT_E = 4, EC_GPIO_PORT_F = 5, EC_GPIO_PORT_G = 6, EC_GPIO_PORT_H = 7, // skip port I EC_GPIO_PORT_J = 8, EC_GPIO_PORT_K = 9, EC_GPIO_PORT_L = 10, EC_GPIO_PORT_M = 11, EC_GPIO_PORT_N = 12, // skip port O EC_GPIO_PORT_P = 13, EC_GPIO_PORT_Q = 14, EC_GPIO_PORT_R = 15, EC_GPIO_PORT_S = 16, } EcGpioPort; #ifdef __cplusplus extern "C" { #endif void ec_outb(uint16_t, uint8_t); uint8_t ec_inb(uint16_t); #ifdef __cplusplus } #endif #endif // __EC_CONFIG_H_ skiboot-skiboot-5.1.13/include/ec/gpio.h000066400000000000000000000026061265204436200200540ustar00rootroot00000000000000/* Copyright 2013-2014 Google Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file gpio.h * * @brief Public interface of the EC GPIO device driver * */ #ifndef __EC_GPIO_H_ #define __EC_GPIO_H_ #ifdef __cplusplus extern "C" { #endif #define EC_GPIO_INPUT 0 #define EC_GPIO_OUTPUT 1 #define EC_GPIO_PULLUP_DISABLE 0 #define EC_GPIO_PULLUP_ENABLE 1 // Sets up a GPIO as output or input. // Returns: <0 on error int ec_gpio_setup(EcGpioPort port, uint8_t pin, int is_output, int pullup_enable); // Reads the current value of an input GPIO. // Returns: GPIO value (0,1) or <0 on error. int ec_gpio_read(EcGpioPort port, uint8_t pin); // Sets the driving value of an output GPIO. Port should already be set // to output mode. // Returns: <0 on error int ec_gpio_set(EcGpioPort port, uint8_t pin, int val); #ifdef __cplusplus } #endif #endif // __EC_GPIO_H_ skiboot-skiboot-5.1.13/include/elf.h000066400000000000000000000060661265204436200173010ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ELF_H #define __ELF_H #include /* Generic ELF header */ struct elf_hdr { uint32_t ei_ident; #define ELF_IDENT 0x7F454C46 uint8_t ei_class; #define ELF_CLASS_32 1 #define ELF_CLASS_64 2 uint8_t ei_data; #define ELF_DATA_LSB 1 #define ELF_DATA_MSB 2 uint8_t ei_version; uint8_t ei_pad[9]; uint16_t e_type; uint16_t e_machine; #define ELF_MACH_PPC32 0x14 #define ELF_MACH_PPC64 0x15 uint32_t e_version; }; /* 64-bit ELF header */ struct elf64_hdr { uint32_t ei_ident; uint8_t ei_class; uint8_t ei_data; uint8_t ei_version; uint8_t ei_pad[9]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; uint64_t e_entry; uint64_t e_phoff; uint64_t e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; }; /* 64-bit ELF program header */ struct elf64_phdr { uint32_t p_type; #define ELF_PTYPE_LOAD 1 uint32_t p_flags; #define ELF_PFLAGS_R 0x4 #define ELF_PFLAGS_W 0x2 #define ELF_PFLAGS_X 0x1 uint64_t p_offset; uint64_t p_vaddr; uint64_t p_paddr; uint64_t p_filesz; uint64_t p_memsz; uint64_t p_align; }; /* 64-bit ELF section header */ struct elf64_shdr { uint32_t sh_name; uint32_t sh_type; uint64_t sh_flags; #define ELF_SFLAGS_X 0x4 #define ELF_SFLAGS_A 0x2 #define ELF_SFLAGS_W 0x1 uint64_t sh_addr; uint64_t sh_offset; uint64_t sh_size; uint32_t sh_link; uint32_t sh_info; uint64_t sh_addralign; int64_t sh_entsize; }; /* Some relocation related stuff used in relocate.c */ struct elf64_dyn { int64_t d_tag; #define DT_NULL 0 #define DT_RELA 7 #define DT_RELASZ 8 #define DT_RELAENT 9 #define DT_RELACOUNT 0x6ffffff9 uint64_t d_val; }; struct elf64_rela { uint64_t r_offset; uint64_t r_info; #define ELF64_R_TYPE(info) ((info) & 0xffffffffu) int64_t r_addend; }; /* relocs we support */ #define R_PPC64_RELATIVE 22 /* 32-bit ELF header */ struct elf32_hdr { uint32_t ei_ident; uint8_t ei_class; uint8_t ei_data; uint8_t ei_version; uint8_t ei_pad[9]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; uint32_t e_entry; uint32_t e_phoff; uint32_t e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; }; /* 32-bit ELF program header*/ struct elf32_phdr { uint32_t p_type; uint32_t p_offset; uint32_t p_vaddr; uint32_t p_paddr; uint32_t p_filesz; uint32_t p_memsz; uint32_t p_flags; uint32_t p_align; }; #endif /* __ELF_H */ skiboot-skiboot-5.1.13/include/errorlog.h000066400000000000000000000263241265204436200203650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ERRORLOG_H #define __ERRORLOG_H #include #include #include #include /* Classification of error/events type reported on OPAL */ /* Platform Events/Errors: Report Machine Check Interrupt */ #define OPAL_PLATFORM_ERR_EVT 0x01 /* INPUT_OUTPUT: Report all I/O related events/errors */ #define OPAL_INPUT_OUTPUT_ERR_EVT 0x02 /* RESOURCE_DEALLOC: Hotplug events and errors */ #define OPAL_RESOURCE_DEALLOC_ERR_EVT 0x03 /* MISC: Miscellaneous error */ #define OPAL_MISC_ERR_EVT 0x04 /* OPAL Subsystem IDs listed for reporting events/errors */ #define OPAL_PROCESSOR_SUBSYSTEM 0x10 #define OPAL_MEMORY_SUBSYSTEM 0x20 #define OPAL_IO_SUBSYSTEM 0x30 #define OPAL_IO_DEVICES 0x40 #define OPAL_CEC_HARDWARE 0x50 #define OPAL_POWER_COOLING 0x60 #define OPAL_MISC_SUBSYSTEM 0x70 #define OPAL_SURVEILLANCE_ERR 0x7A #define OPAL_PLATFORM_FIRMWARE 0x80 #define OPAL_SOFTWARE 0x90 #define OPAL_EXTERNAL_ENV 0xA0 /* * During reporting an event/error the following represents * how serious the logged event/error is. (Severity) */ #define OPAL_INFO 0x00 #define OPAL_RECOVERED_ERR_GENERAL 0x10 /* 0x2X series is to denote set of Predictive Error */ /* 0x20 Generic predictive error */ #define OPAL_PREDICTIVE_ERR_GENERAL 0x20 /* 0x21 Predictive error, degraded performance */ #define OPAL_PREDICTIVE_ERR_DEGRADED_PERF 0x21 /* 0x22 Predictive error, fault may be corrected after reboot */ #define OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT 0x22 /* * 0x23 Predictive error, fault may be corrected after reboot, * degraded performance */ #define OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_BOOT_DEGRADE_PERF 0x23 /* 0x24 Predictive error, loss of redundancy */ #define OPAL_PREDICTIVE_ERR_LOSS_OF_REDUNDANCY 0x24 /* 0x4X series for Unrecoverable Error */ /* 0x40 Generic Unrecoverable error */ #define OPAL_UNRECOVERABLE_ERR_GENERAL 0x40 /* 0x41 Unrecoverable error bypassed with degraded performance */ #define OPAL_UNRECOVERABLE_ERR_DEGRADE_PERF 0x41 /* 0x44 Unrecoverable error bypassed with loss of redundancy */ #define OPAL_UNRECOVERABLE_ERR_LOSS_REDUNDANCY 0x44 /* 0x45 Unrecoverable error bypassed with loss of redundancy and performance */ #define OPAL_UNRECOVERABLE_ERR_LOSS_REDUNDANCY_PERF 0x45 /* 0x48 Unrecoverable error bypassed with loss of function */ #define OPAL_UNRECOVERABLE_ERR_LOSS_OF_FUNCTION 0x48 /* 0x50 In case of PANIC */ #define OPAL_ERROR_PANIC 0x50 /* * OPAL Event Sub-type * This field provides additional information on the non-error * event type */ #define OPAL_NA 0x00 #define OPAL_MISCELLANEOUS_INFO_ONLY 0x01 #define OPAL_PREV_REPORTED_ERR_RECTIFIED 0x10 #define OPAL_SYS_RESOURCES_DECONFIG_BY_USER 0x20 #define OPAL_SYS_RESOURCE_DECONFIG_PRIOR_ERR 0x21 #define OPAL_RESOURCE_DEALLOC_EVENT_NOTIFY 0x22 #define OPAL_CONCURRENT_MAINTENANCE_EVENT 0x40 #define OPAL_CAPACITY_UPGRADE_EVENT 0x60 #define OPAL_RESOURCE_SPARING_EVENT 0x70 #define OPAL_DYNAMIC_RECONFIG_EVENT 0x80 #define OPAL_NORMAL_SYS_PLATFORM_SHUTDOWN 0xD0 #define OPAL_ABNORMAL_POWER_OFF 0xE0 /* Max user dump size is 14K */ #define OPAL_LOG_MAX_DUMP 14336 /* Origin of error, elog_origin */ #define ORG_SAPPHIRE 1 #define ORG_POWERNV 2 /* Multiple user data sections */ struct __attribute__((__packed__))elog_user_data_section { uint32_t tag; uint16_t size; uint16_t component_id; char data_dump[1]; }; /* * All the information regarding an error/event to be reported * needs to populate this structure using pre-defined interfaces * only */ struct __attribute__((__packed__)) errorlog { uint16_t component_id; uint8_t error_event_type; uint8_t subsystem_id; uint8_t event_severity; uint8_t event_subtype; uint8_t user_section_count; uint8_t elog_origin; uint32_t user_section_size; uint32_t reason_code; uint32_t additional_info[4]; uint32_t plid; uint32_t log_size; uint64_t elog_timeout; char user_data_dump[OPAL_LOG_MAX_DUMP]; struct list_node link; }; struct opal_err_info { uint32_t reason_code; uint8_t err_type; uint16_t cmp_id; uint8_t subsystem; uint8_t sev; uint8_t event_subtype; }; /* Component IDs */ /* In PEL error log format, Creator ID is hypervisor * But we can have various component ID to distinguish * which component in hypervisor is reporting the error * This is 2 bytes long, * first byte corresponds to Component IDs * Second byte is reserved for the Reason code. * Component ID is mapped to readable 4-digit ascii * character name in FSP and displayed. */ /* SAPPHIRE components */ #define OPAL_CODEUPDATE 0x4355 /* CU */ #define OPAL_CONSOLE 0x434E /* CN */ #define OPAL_CEC 0x4345 /* CE */ #define OPAL_CHIP 0x4348 /* CH */ #define OPAL_ELOG 0x454C /* EL */ #define OPAL_NVRAM 0x4E56 /* NV */ #define OPAL_RTC 0x5254 /* RT */ #define OPAL_SURVEILLANCE 0x5355 /* SU */ #define OPAL_SYSPARAM 0x5350 /* SP */ #define OPAL_LPC 0x4C50 /* LP */ #define OPAL_UART 0x5541 /* UA */ #define OPAL_OCC 0x4F43 /* OC */ #define OPAL_OP_PANEL 0x4F50 /* OP */ #define OPAL_PHB3 0x5048 /* PH */ #define OPAL_PSI 0x5053 /* PS */ #define OPAL_VPD 0x5650 /* VP */ #define OPAL_XSCOM 0x5853 /* XS */ #define OPAL_PCI 0x5043 /* PC */ #define OPAL_MISC 0x4D49 /* MI */ #define OPAL_ATTN 0x4154 /* AT */ #define OPAL_MEM_ERR 0x4D45 /* ME */ #define OPAL_CENTAUR 0x4354 /* CT */ #define OPAL_MFSI 0x4D46 /* MF */ #define OPAL_DUMP 0x4455 /* DU */ #define OPAL_LED 0x4C45 /* LE */ #define OPAL_SENSOR 0x5345 /* SE */ #define OPAL_SLW 0x534C /* SL */ #define OPAL_FSP 0x4650 /* FP */ #define OPAL_I2C 0x4943 /* IC */ #define OPAL_IPMI 0x4950 /* IP */ /* SAPPHIRE SRC componenet ID*/ #define OPAL_CU 0x1000 #define OPAL_CN 0x2000 #define OPAL_CE 0x3000 #define OPAL_CH 0x4000 #define OPAL_EL 0x5000 #define OPAL_NV 0x6000 #define OPAL_RT 0x7000 #define OPAL_SU 0x8000 #define OPAL_SP 0x9000 #define OPAL_LP 0xa000 #define OPAL_UA 0xb000 #define OPAL_OC 0xc000 #define OPAL_OP 0xd000 #define OPAL_PH 0xe000 #define OPAL_PS 0xf000 #define OPAL_VP 0x1000 #define OPAL_XS 0x1100 #define OPAL_PC 0x1200 #define OPAL_MI 0x1300 #define OPAL_AT 0x1400 #define OPAL_ME 0x1500 #define OPAL_CT 0x1600 #define OPAL_MF 0x1700 #define OPAL_DU 0x1800 #define OPAL_LE 0x1900 #define OPAL_SE 0x2000 #define OPAL_SL 0x2100 #define OPAL_FP 0x2200 #define OPAL_IC 0x2300 #define OPAL_IP 0x2400 enum opal_reasoncode { /* code update */ OPAL_RC_CU_FLASH = OPAL_CU | 0x10, OPAL_RC_CU_INIT = OPAL_CU | 0x11, OPAL_RC_CU_SG_LIST = OPAL_CU | 0x12, OPAL_RC_CU_COMMIT = OPAL_CU | 0x13, OPAL_RC_CU_MSG = OPAL_CU | 0x14, OPAL_RC_CU_NOTIFY = OPAL_CU | 0x15, OPAL_RC_CU_MARKER_LID = OPAL_CU | 0x16, /* NVRAM */ OPAL_RC_NVRAM_INIT = OPAL_NV | 0x10, OPAL_RC_NVRAM_OPEN = OPAL_NV | 0x11, OPAL_RC_NVRAM_SIZE = OPAL_NV | 0x12, OPAL_RC_NVRAM_WRITE = OPAL_NV | 0x13, OPAL_RC_NVRAM_READ = OPAL_NV | 0x14, /* CENTAUR */ OPAL_RC_CENTAUR_INIT = OPAL_CT | 0x10, OPAL_RC_CENTAUR_RW_ERR = OPAL_CT | 0x11, /* MFSI */ OPAL_RC_MFSI_RW_ERR = OPAL_MF | 0x10, /* UART */ OPAL_RC_UART_INIT = OPAL_UA | 0x10, /* OCC */ OPAL_RC_OCC_RESET = OPAL_OC | 0x10, OPAL_RC_OCC_LOAD = OPAL_OC | 0x11, OPAL_RC_OCC_PSTATE_INIT = OPAL_OC | 0x12, OPAL_RC_OCC_TIMEOUT = OPAL_OC | 0x13, /* RTC */ OPAL_RC_RTC_READ = OPAL_RT | 0x10, OPAL_RC_RTC_TOD = OPAL_RT | 0x11, /* SURVEILLANCE */ OPAL_RC_SURVE_INIT = OPAL_SU | 0x10, OPAL_RC_SURVE_STATUS = OPAL_SU | 0x11, OPAL_RC_SURVE_ACK = OPAL_SU | 0x12, /* SYSPARAM */ OPAL_RC_SYSPARM_INIT = OPAL_SP | 0x10, OPAL_RC_SYSPARM_MSG = OPAL_SP | 0x11, /* LPC */ OPAL_RC_LPC_READ = OPAL_LP | 0x10, OPAL_RC_LPC_WRITE = OPAL_LP | 0x11, /* OP_PANEL */ OPAL_RC_PANEL_WRITE = OPAL_OP | 0x10, /* PSI */ OPAL_RC_PSI_INIT = OPAL_PS | 0x10, OPAL_RC_PSI_IRQ_RESET = OPAL_PS | 0x11, OPAL_RC_PSI_TIMEOUT = OPAL_PS | 0X12, /* XSCOM */ OPAL_RC_XSCOM_RW = OPAL_XS | 0x10, OPAL_RC_XSCOM_INDIRECT_RW = OPAL_XS | 0x11, OPAL_RC_XSCOM_RESET = OPAL_XS | 0x12, /* PCI */ OPAL_RC_PCI_INIT_SLOT = OPAL_PC | 0x10, OPAL_RC_PCI_ADD_SLOT = OPAL_PC | 0x11, OPAL_RC_PCI_SCAN = OPAL_PC | 0x12, OPAL_RC_PCI_RESET_PHB = OPAL_PC | 0x10, /* ATTN */ OPAL_RC_ATTN = OPAL_AT | 0x10, /* MEM_ERR */ OPAL_RC_MEM_ERR_RES = OPAL_ME | 0x10, OPAL_RC_MEM_ERR_DEALLOC = OPAL_ME | 0x11, /* DUMP */ OPAL_RC_DUMP_INIT = OPAL_DU | 0x10, OPAL_RC_DUMP_LIST = OPAL_DU | 0x11, OPAL_RC_DUMP_ACK = OPAL_DU | 0x12, OPAL_RC_DUMP_MDST_INIT = OPAL_DU | 0x13, OPAL_RC_DUMP_MDST_UPDATE= OPAL_DU | 0x14, OPAL_RC_DUMP_MDST_ADD = OPAL_DU | 0x15, OPAL_RC_DUMP_MDST_REMOVE= OPAL_DU | 0x16, /* LED */ OPAL_RC_LED_SPCN = OPAL_LE | 0x10, OPAL_RC_LED_BUFF = OPAL_LE | 0x11, OPAL_RC_LED_LC = OPAL_LE | 0x12, OPAL_RC_LED_STATE = OPAL_LE | 0x13, OPAL_RC_LED_SUPPORT = OPAL_LE | 0x14, /* SENSOR */ OPAL_RC_SENSOR_INIT = OPAL_SE | 0x10, OPAL_RC_SENSOR_READ = OPAL_SE | 0x11, OPAL_RC_SENSOR_ASYNC_COMPLETE = OPAL_SE | 0x12, /* SLW */ OPAL_RC_SLW_INIT = OPAL_SL | 0x10, OPAL_RC_SLW_SET = OPAL_SL | 0x11, OPAL_RC_SLW_GET = OPAL_SL | 0x12, OPAL_RC_SLW_REG = OPAL_SL | 0x13, /* FSP */ OPAL_RC_FSP_POLL_TIMEOUT = OPAL_FP | 0x10, /* I2C */ OPAL_RC_I2C_INIT = OPAL_IC | 0X10, OPAL_RC_I2C_START_REQ = OPAL_IC | 0X11, OPAL_RC_I2C_TIMEOUT = OPAL_IC | 0x12, OPAL_RC_I2C_TRANSFER = OPAL_IC | 0x13, OPAL_RC_I2C_RESET = OPAL_IC | 0x14, /* IPMI */ OPAL_RC_IPMI_REQ = OPAL_IP | 0x10, OPAL_RC_IPMI_RESP = OPAL_IP | 0x11, /* Platform error */ OPAL_RC_ABNORMAL_REBOOT = OPAL_CE | 0x10, }; #define DEFINE_LOG_ENTRY(reason, type, id, subsys, \ severity, subtype) static struct opal_err_info err_##reason = \ { .reason_code = reason, .err_type = type, .cmp_id = id, \ .subsystem = subsys, .sev = severity, .event_subtype = subtype } /* This is wrapper around the error log function, which creates * and commits the error to FSP. * Used for simple error logging */ void log_simple_error(struct opal_err_info *e_info, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); #define e_info(reason_code) err_##reason_code struct errorlog *opal_elog_create(struct opal_err_info *e_info, uint32_t tag) __warn_unused_result; void log_add_section(struct errorlog *buf, uint32_t tag); void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size); void log_append_msg(struct errorlog *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void log_commit(struct errorlog *elog); /* Called by the backend after an error has been logged by the * backend. If the error could not be logged successfully success is * set to false. */ void opal_elog_complete(struct errorlog *elog, bool success); int elog_init(void); #endif /* __ERRORLOG_H */ skiboot-skiboot-5.1.13/include/fsi-master.h000066400000000000000000000020111265204436200205670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __FSI_MASTER_H #define __FSI_MASTER_H /* * Definition of the MFSI masters */ #define MFSI_cMFSI0 0 #define MFSI_cMFSI1 1 #define MFSI_hMFSI0 2 extern int64_t mfsi_read(uint32_t chip, uint32_t mfsi, uint32_t port, uint32_t fsi_addr, uint32_t *data); extern int64_t mfsi_write(uint32_t chip, uint32_t mfsi, uint32_t port, uint32_t fsi_addr, uint32_t data); extern void mfsi_init(void); #endif /* __FSI_MASTER_H */ skiboot-skiboot-5.1.13/include/fsp-attn.h000066400000000000000000000077741265204436200202760ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __FSP_ATTN_H #define __FSP_ATTN_H /* Per spec attn area can go up to 0x400 bytes */ #define ATTN_AREA_SZ 0x400 extern struct sp_attn_area cpu_ctl_sp_attn_area1; extern struct sp_attn_area cpu_ctl_sp_attn_area2; struct spat_entry { /* Virtual address */ __be64 vaddr; /* Partition id */ __be16 id; uint8_t reserved[6]; } __packed; /* SP Address Table: Structure is not used as of today, defined to * keep in sync with the spec */ struct sp_addr_table { /* Index of last valid spat entry */ __be32 idx; /* Number of SPAT entries allocated = 31 for SP */ __be32 count; uint8_t reserved1[8]; /* SPAT entries */ struct spat_entry spat_entry[31]; } __packed; /* SP Attention Areas */ struct sp_attn_area { /* Processor Number */ uint8_t processor; /* Attention command */ uint8_t attn_cmd; __be16 data_len; uint8_t data[]; } __packed __align(ATTN_AREA_SZ); #define SRC_WORD_COUNT 8 #define SRC_LEN 32 /* Max limit of user data size is 940 (due to attention area size) */ #define TI_MSG_LEN 940 /* Maximum sapphire version length (approx) */ #define VERSION_LEN 80 /* Up to 10 frames each of length 40 bytes + header = 430 bytes */ #define BT_FRAME_LEN 430 /* File info length : Use the rest of the memory for file details */ #define FILE_INFO_LEN (TI_MSG_LEN - VERSION_LEN - BT_FRAME_LEN) struct user_data { char version[VERSION_LEN]; char bt_buf[BT_FRAME_LEN]; char file_info[FILE_INFO_LEN]; } __packed; /* Terminate Immediate Attention */ struct ti_attn { /* Command valid */ uint8_t cmd_valid; /* Attention command */ uint8_t attn_cmd; __be16 data_len; /* Controls dump actions */ uint8_t dump_ctrl; uint8_t reserved1; /* Hardware dump type */ __be16 dump_type; /* SRC format */ uint8_t src_fmt; /* SRC flags */ uint8_t src_flags; /* Number of ASCII words */ uint8_t ascii_cnt; /* Number of HEX words */ uint8_t hex_cnt; __be16 reserved2; /* SRC length */ __be16 src_len; __be32 src_word[SRC_WORD_COUNT]; /* ASCII data */ char src[SRC_LEN]; uint32_t msg_len; /* User data: Debug details */ struct user_data msg; } __packed __align(ATTN_AREA_SZ); /* Hypervisor Service Routine Data area: Structure is not used as of today, * defined to keep in sync with the spec */ struct hsr_data_area { /* MS Address Compare Address */ __be64 ms_cmp_addr; /* MS Address Compare Op (set/reset) */ __be16 ms_cmp_op; /* MS Address Compare Length */ __be16 ms_cmp_len; /* MS Address Compare Data */ __be32 ms_cmp_data; /* MS Address Compare Service Routine */ __be64 ms_cmp_sr; /* Pointer to MS Display / Alter HSR */ __be64 ms_display; __be64 reserved1; /* MS Dump HSR */ __be64 ms_dump_hsr; /* Pointer to Real Address Validation HSR */ __be64 hsr_raddr; /* Effective Address Field */ __be64 eaddr; /* Pointer to CPU Spin HSR */ __be64 hsr_cpu_spin; /* Pointer to SP Glue HSR */ __be64 hsr_sp_glue; uint8_t reserved2[19]; /* Time Base Flags * bit 7 (0x01) = 0b1: hardware time base disabled * other bits reserved */ uint8_t time_flags; uint8_t reserved3[12]; /* TDE Addr Parm */ __be64 tde_addr; /* SDR1 Shared Processor HSR */ __be64 hsr_sdr1_proc; __be64 partition_id; uint8_t reserved4[12]; /* Address Type for Compare * 1 = real address * 2 = effective address * 3 = virtual address */ __be16 ms_addr_type; uint8_t reserved5[10]; /* Cache Flush Service Routine Pointer */ __be64 cfsr; uint8_t reserved6[88]; } __packed; #endif /* __FSP_ATTN_H */ skiboot-skiboot-5.1.13/include/fsp-elog.h000066400000000000000000000035071265204436200202440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #ifndef __ELOG_H #define __ELOG_H #define ELOG_TYPE_PEL 0 #define MAX_RETRIES 3 /* Following variables are used to indicate state of the * head log entry which is being fetched from FSP/OPAL and * these variables are not overwritten until next log is * retrieved from FSP/OPAL. */ enum elog_head_state { ELOG_STATE_FETCHING, /*In the process of reading log from FSP. */ ELOG_STATE_FETCHED_INFO,/* Indicates reading log info is completed */ ELOG_STATE_FETCHED_DATA,/* Indicates reading log is completed */ ELOG_STATE_NONE, /* Indicates to fetch next log */ ELOG_STATE_REJECTED, /* resend all pending logs to linux */ }; /* Generate src from opal reason code (src_comp) */ #define generate_src_from_comp(src_comp) (OPAL_SRC_TYPE_ERROR << 24 | \ OPAL_FAILING_SUBSYSTEM << 16 | src_comp) int elog_fsp_commit(struct errorlog *buf) __warn_unused_result; bool opal_elog_info(uint64_t *opal_elog_id, uint64_t *opal_elog_size) __warn_unused_result; bool opal_elog_read(uint64_t *buffer, uint64_t opal_elog_size, uint64_t opal_elog_id) __warn_unused_result; bool opal_elog_ack(uint64_t ack_id) __warn_unused_result; void opal_resend_pending_logs(void); #endif /* __ELOG_H */ skiboot-skiboot-5.1.13/include/fsp-leds.h000066400000000000000000000104041265204436200202370ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* */ /* * SPCN based LED location code and other information */ #ifndef __FSP_LEDS_H #define __FSP_LEDS_H /* Supported FSP response codes */ #define FSP_IND_NOT_IMPLMNTD 0x00 /* Indicator not implemented */ #define FSP_IND_IMPLMNTD 0x04 /* Indicator implemented */ #define FSP_IND_IMPL_UNKNOWN 0x08 /* Implementation unknown */ #define FSP_IND_INACTIVE 0x00 /* Indicator not active */ #define FSP_IND_IDENTIFY_ACTV 0x01 /* Identify state active */ #define FSP_IND_FAULT_ACTV 0x02 /* Fault state active */ #define FSP_IND_STATE_UNKNOWN 0xff /* Indicator state unknown */ #define FSP_RSRC_NOT_PRESENT 0x00 /* Resource not present */ #define FSP_RSRC_PRESENT 0x40 /* Resource present */ #define FSP_RSRC_PRSNC_UNKNOWN 0x80 /* Resource presence unknown */ /* LED exclusive bits */ #define FSP_LED_EXCL_FAULT 1UL << 0 #define FSP_LED_EXCL_IDENTIFY 1UL << 1 /* LED update message source */ enum spcn_cmd_src { SPCN_SRC_FSP = 0, SPCN_SRC_OPAL = 1, SPCN_SRC_MAX = 2 }; /* SPCN set LED */ struct spcn_led_data { u8 lc_len; u16 state; char lc_code[LOC_CODE_SIZE]; }; /* LED data */ struct fsp_led_data { u16 rid; /* Resource ID */ u8 lc_len; /* Location code len */ char loc_code[LOC_CODE_SIZE]; u16 parms; /* Parameters */ u16 status; /* Status */ u16 excl_bit; /* Exclusive LED bit */ struct list_node link; }; /* FSP location code request */ struct fsp_loc_code_req { u16 len; u16 req_type; u8 raw_len; u8 lc_sz; char loc_code[LOC_CODE_SIZE]; }; /* FSP location code data */ struct fsp_loc_code_data { u16 size; u32 ccin; u8 status; u8 ind_state; u8 raw_len; u8 fld_sz; /* The size below must include the padding to * make the whole structure aligned to a * multiple of 4 bytes */ char loc_code[LOC_CODE_SIZE + 2]; /* 82 */ /* We need to pack the structure otherwise the * compiler adds additional alignment to make * it 8 bytes aligned */ } __packed; /* Get indicator state request */ struct fsp_get_ind_state_req { u16 size; u8 lc_len; u8 fld_sz; char loc_code[LOC_CODE_SIZE]; }; /* Set indicator state request */ struct fsp_set_ind_state_req { u16 size; u16 req_type; u8 reserved[3]; u8 ind_state; u8 lc_len; u8 fld_sz; char loc_code[LOC_CODE_SIZE]; }; /* LED set SPCN command */ struct led_set_cmd { char loc_code[LOC_CODE_SIZE]; u8 command; u8 state; u16 ckpt_status; /* Checkpointed status */ u16 ckpt_excl_bit; /* Checkpointed exclusive status */ u64 async_token; /* OPAL async token */ enum spcn_cmd_src cmd_src; /* OPAL or FSP based */ struct list_node link; }; /* System Attention Indicator */ struct sai_data { uint8_t state; char loc_code[LOC_CODE_SIZE]; }; /* LED commands and state */ #define LED_COMMAND_FAULT 1 #define LED_COMMAND_IDENTIFY 0 #define LED_STATE_ON 1 #define LED_STATE_OFF 0 /* FSP get loc-code list command request type */ #define GET_LC_CMPLT_SYS 0x8000 #define GET_LC_ENCLOSURES 0x4000 #define GET_LC_ENCL_DESCENDANTS 0x2000 #define GET_LC_SINGLE_LOC_CODE 0x0100 /* FSP set indicator command request type */ #define SET_IND_ENCLOSURE 0x4000 #define SET_IND_SINGLE_LOC_CODE 0x0001 /* Response buffer */ #define OUTBUF_HEADER_SIZE 8 /* LED miscellaneous */ #define LOC_CODE_LEN 1 #define LED_CONTROL_LEN 2 #define FSP_LC_STRUCT_FIXED_SZ 0x0a /* LED Device tree property names */ #define DT_PROPERTY_LED_COMPATIBLE "ibm,opal-v3-led" #define DT_PROPERTY_LED_NODE "leds" #define DT_PROPERTY_LED_MODE "led-mode" #define DT_PROPERTY_LED_TYPES "led-types" /* LED Mode */ #define LED_MODE_LIGHT_PATH "lightpath" #define LED_MODE_GUIDING_LIGHT "guidinglight" /* LED type */ #define LED_TYPE_IDENTIFY "identify" #define LED_TYPE_FAULT "fault" #define LED_TYPE_ATTENTION "attention" #endif skiboot-skiboot-5.1.13/include/fsp-mdst-table.h000066400000000000000000000023231265204436200213450ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __FSPMDST_H #define __FSPMDST_H /* * Dump region ids * * 0x01 - 0x7F : OPAL * 0x80 - 0xFF : Kernel * */ #define DUMP_REGION_OPAL_START 0x01 #define DUMP_REGION_OPAL_END 0x7F #define DUMP_REGION_HOST_START OPAL_DUMP_REGION_HOST_START #define DUMP_REGION_HOST_END OPAL_DUMP_REGION_HOST_END #define DUMP_REGION_CONSOLE 0x01 #define DUMP_REGION_HBRT_LOG 0x02 /* * Sapphire Memory Dump Source Table * * Format of this table is same as Memory Dump Source Table (MDST) * defined in HDAT spec. */ struct dump_mdst_table { uint64_t addr; uint32_t type; /* DUMP_SECTION_* */ uint32_t size; }; #endif /* __FSPMDST_H */ skiboot-skiboot-5.1.13/include/fsp-sysparam.h000066400000000000000000000047571265204436200211650ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __FSP_SYSPARAM_H #define __FSP_SYSPARAM_H /* System parameter numbers used in the protocol * * these are the only ones we care about right now */ #define SYS_PARAM_SURV 0xf0000001 #define SYS_PARAM_HMC_MANAGED 0xf0000003 #define SYS_PARAM_FLASH_POLICY 0xf0000012 #define SYS_PARAM_NEED_HMC 0xf0000016 #define SYS_PARAM_REAL_SAI 0xf0000019 #define SYS_PARAM_PARTITION_SAI 0xf000001A #define SYS_PARAM_PLAT_SAI 0xf000001B #define SYS_PARAM_FW_LICENSE 0xf000001d #define SYS_PARAM_WWPN 0xf0000023 #define SYS_PARAM_DEF_BOOT_DEV 0xf0000024 #define SYS_PARAM_NEXT_BOOT_DEV 0xf0000025 #define SYS_PARAM_CONSOLE_SELECT 0xf0000026 #define SYS_PARAM_BOOT_DEV_PATH 0xf0000027 /* Completion for a sysparam call. err_len is either a negative error * code or the positive length of the returned data */ typedef void (*sysparam_compl_t)(uint32_t param_id, int err_len, void *data); /* Send a sysparam query request. Operation can be synchronous or * asynchronous: * * - synchronous (async_complete is NULL), the result code is either * a negative error code or a positive returned length. * * - asynchronous (async_complete non NULL). The result code is 0 for * successfully queued request or an error for an immediate error. * A successfully queued request will complete via the completion * callback defined above */ int fsp_get_sys_param(uint32_t param_id, void *buffer, uint32_t length, sysparam_compl_t async_complete, void *comp_data); void fsp_sysparam_init(void); /* * System parameter update notification. * param_id : parameter id * len : length of data * data : pointer to data */ typedef bool (*sysparam_update_notify)(struct fsp_msg *msg); /* Register/unregister for system parameter update notifier chain */ void sysparam_add_update_notifier(sysparam_update_notify notify); void sysparam_del_update_notifier(sysparam_update_notify notify); #endif /* __FSP_SYSPARAM_H */ skiboot-skiboot-5.1.13/include/fsp.h000066400000000000000000000715101265204436200173170ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * IBM System P FSP (Flexible Service Processor) */ #ifndef __FSP_H #define __FSP_H #include #include /* Current max number of FSPs * one primary and one secondary is all we support */ #define FSP_MAX 2 /* Command protocol. * * Commands have a byte class and a byte subcommand. With the exception * of some HMC related commands (class 0xe0) which we don't support, * only one outstanding command is allowed for a given class. * * Note: 0xCE and 0xCF fall into the same class, ie, only one of them can * be outstanding. * * A command is outstanding until it has been acknowledged. This doesn't * imply a response, the response can come later. */ /* Protocol status error codes used by the protocol */ #define FSP_STATUS_SUCCESS 0x00 /* Command successful */ #define FSP_STATUS_MORE_DATA 0x02 /* Success, EOF not reached */ #define FSP_STATUS_DATA_INLINE 0x11 /* Data inline in mbox */ #define FSP_STATUS_INVALID_SUBCMD 0x20 #define FSP_STATUS_INVALID_MOD 0x21 #define FSP_STATUS_INVALID_DATA 0x22 #define FSP_STATUS_INVALID_DPOSTATE 0x23 #define FSP_STATUS_DMA_ERROR 0x24 #define FSP_STATUS_INVALID_CMD 0x2c #define FSP_STATUS_SEQ_ERROR 0x2d #define FSP_STATUS_BAD_STATE 0x2e #define FSP_STATUS_NOT_SUPPORTED 0x2f #define FSP_STATUS_FILE_TOO_LARGE 0x43 #define FSP_STATUS_FLASH_INPROGRESS 0x61 #define FSP_STATUS_FLASH_NOPROGRESS 0x62 #define FSP_STATUS_FLASH_INVALID_SIDE 0x63 #define FSP_STATUS_GENERIC_ERROR 0xfe #define FSP_STATUS_EOF_ERROR 0x02 #define FSP_STATUS_DMA_ERROR 0x24 #define FSP_STATUS_BUSY 0x3e #define FSP_STATUS_FLASH_BUSY 0x3f #define FSP_STATUS_INVALID_SUBID 0x41 #define FSP_STATUS_LENGTH_ERROR 0x42 #define FSP_STAUS_INVALID_HMC_ID 0x51 #define FSP_STATUS_SPCN_ERROR 0xA8 /* SPCN error */ #define FSP_STATUS_INVALID_LC 0xC0 /* Invalid location code */ #define FSP_STATUS_ENCL_IND_RESET 0xC2 /* Enclosure Indicator cannot be reset */ #define FSP_STATUS_TOD_RESET 0xA9 /* TOD reset due to invalid state at POR */ #define FSP_STATUS_TOD_PERMANENT_ERROR 0xAF /* Permanent error in TOD */ #define FSP_STATUS_I2C_TRANS_ERROR 0xE4 /* I2C device / transmission error */ #define FSP_STATUS_IND_PARTIAL_SUCCESS 0xE5 /* Indicator partial success */ #define FSP_STATUS_GENERIC_FAILURE 0xEF /* Generic Failure in execution */ /* * FSP registers * * All of the below register definitions come from the FSP0 "Black Widow" spec * They are the same for FSP1 except they are presented big-endian vs * little-endian for FSP0 -- which used PCI * all regs are 4 bytes wide, and we read the larger data areas in 4 byte * granularity as well * * there are actually two defined sets of MBX registers * MBX2 can't generate interrupts to the host and only MBX1 is currently * used by firmware running on the FSP, so we're mostly ignoring MBX2 */ /* Device Reset Control Register */ #define FSP_DRCR_REG 0x00 #define FSP_DRCR_CLR_REG 0x04 /* Bit masks for DRCR */ #define FSP_DRCR_CMD_VALID PPC_BIT32(16) #define FSP_DRCR_TERMINATE PPC_BIT32(17) #define FSP_DRCR_PREP_FOR_RESET PPC_BIT32(23) #define FSP_DRCR_CLEAR_DISR PPC_BIT32(30) /* DRCR commands need the CMD_VALID bit set */ #define FSP_PREP_FOR_RESET_CMD (FSP_DRCR_CMD_VALID | \ FSP_DRCR_PREP_FOR_RESET) #define FSP_DRCR_ACK_MASK (0xff << 8) /* Device Immediate Status Register */ #define FSP_DISR_REG 0x08 #define FSP_DISR_CLR_REG 0x0C /* Bit masks for DISR */ #define FSP_DISR_FSP_UNIT_CHECK PPC_BIT32(16) #define FSP_DISR_FSP_RUNTIME_TERM PPC_BIT32(21) #define FSP_DISR_FSP_RR_COMPLETE PPC_BIT32(22) #define FSP_DISR_FSP_FLASH_TERM PPC_BIT32(23) #define FSP_DISR_RUNTIME_STATE_SYNCD PPC_BIT32(24) #define FSP_DISR_DBG_IN_PROGRESS PPC_BIT32(25) #define FSP_DISR_FSP_IN_RR PPC_BIT32(26) #define FSP_DISR_FSP_REBOOT_IN_PROGRESS PPC_BIT32(27) #define FSP_DISR_CRIT_OP_IN_PROGRESS PPC_BIT32(28) #define FSP_DISR_STATUS_ACK_RXD PPC_BIT32(31) #define FSP_DISR_HIR_TRIGGER_MASK (FSP_DISR_FSP_UNIT_CHECK | \ FSP_DISR_FSP_RUNTIME_TERM | \ FSP_DISR_FSP_FLASH_TERM) /* The host version of the control register shares bits with the FSP's * control reg. Those bits are defined such that one side can set * a bit and the other side can clear it */ #define FSP_MBX1_HCTL_REG 0x080 /* AKA DSCR1 */ #define FSP_MBX1_FCTL_REG 0x090 #define FSP_MBX2_HCTL_REG 0x0a0 /* AKA DSCR2 */ #define FSP_MBX2_FCTL_REG 0x0b0 /* Bits in the control reg */ #define FSP_MBX_CTL_PTS (1 << 31) #define FSP_MBX_CTL_ABORT (1 << 30) #define FSP_MBX_CTL_SPPEND (1 << 29) #define FSP_MBX_CTL_HPEND (1 << 28) #define FSP_MBX_CTL_XDN (1 << 26) #define FSP_MBX_CTL_XUP (1 << 25) #define FSP_MBX_CTL_HCHOST_MASK (0xf << 20) #define FSP_MBX_CTL_HCHOST_SHIFT 20 #define FSP_MBX_CTL_DCHOST_MASK (0xff << 12) #define FSP_MBX_CTL_DCHOST_SHIFT 12 #define FSP_MBX_CTL_HCSP_MASK (0xf << 8) #define FSP_MBX_CTL_HCSP_SHIFT 8 #define FSP_MBX_CTL_DCSP_MASK (0xff) #define FSP_MBX_CTL_DCSP_SHIFT 0 /* Three header registers owned by the host */ #define FSP_MBX1_HHDR0_REG 0x84 #define FSP_MBX1_HHDR1_REG 0x88 #define FSP_MBX1_HHDR2_REG 0x8C #define FSP_MBX2_HHDR0_REG 0xa4 #define FSP_MBX2_HHDR1_REG 0xa8 #define FSP_MBX2_HHDR2_REG 0xaC /* SP Doorbell Error Status register */ #define FSP_SDES_REG 0xc0 /* Host Doorbell Error Status register */ #define FSP_HDES_REG 0xc4 /* Bit definitions for both SDES and HDES * * Notes: * * - CLR: is written to clear the status and always reads * as 0. It can be used to detect an error state (a HB * freeze will return all 1's) * - ILLEGAL: illegal operation such as host trying to write * to an FSP only register etc... * - WFULL: set if host tried to write to the SP doorbell while * the pending bit is still set * - REMPTY: tried to read while host pending bit not set * - PAR: SP RAM parity error */ #define FSP_DBERRSTAT_ILLEGAL1 (1 << 27) #define FSP_DBERRSTAT_WFULL1 (1 << 26) #define FSP_DBERRSTAT_REMPTY1 (1 << 25) #define FSP_DBERRSTAT_PAR1 (1 << 24) #define FSP_DBERRSTAT_CLR1 (1 << 16) #define FSP_DBERRSTAT_ILLEGAL2 (1 << 11) #define FSP_DBERRSTAT_WFULL2 (1 << 10) #define FSP_DBERRSTAT_REMPTY2 (1 << 9) #define FSP_DBERRSTAT_PAR2 (1 << 8) #define FSP_DBERRSTAT_CLR2 (1 << 0) /* Host Doorbell Interrupt Register and mask * * Note that while HDIR has bits for MBX2, only * MBX1 can actually generate interrupts. Thus only the * MBX1 bits are implemented in the mask register. */ #define FSP_HDIR_REG 0xc8 #define FSP_HDIM_SET_REG 0xcc #define FSP_HDIM_CLR_REG 0xd0 #define FSP_DBIRQ_ERROR2 (1 << 10) #define FSP_DBIRQ_XUP2 (1 << 9) #define FSP_DBIRQ_HPEND2 (1 << 8) #define FSP_DBIRQ_ERROR1 (1 << 2) #define FSP_DBIRQ_XUP1 (1 << 1) #define FSP_DBIRQ_HPEND1 (1 << 0) #define FSP_DBIRQ_MBOX1 (FSP_DBIRQ_ERROR1 | FSP_DBIRQ_XUP1 | \ FSP_DBIRQ_HPEND1) #define FSP_DBIRQ_MBOX2 (FSP_DBIRQ_ERROR2 | FSP_DBIRQ_XUP2 | \ FSP_DBIRQ_HPEND2) #define FSP_DBIRQ_ALL (FSP_DBIRQ_MBOX1 | FSP_DBIRQ_MBOX2) /* Doorbell Interrupt Register (FSP internal interrupt latch * read-only on host side */ #define FSP_PDIR_REG 0xd4 /* And associated mask */ #define FSP_PDIM_SET_REG 0xd8 #define FSP_PDIM_CLR_REG 0xdc /* Bits for the above */ #define FSP_PDIRQ_ABORT2 (1 << 7) #define FSP_PDIRQ_ABORT1 (1 << 6) #define FSP_PDIRQ_ERROR2 (1 << 5) #define FSP_PDIRQ_ERROR1 (1 << 4) #define FSP_PDIRQ_XDN2 (1 << 3) #define FSP_PDIRQ_XDN1 (1 << 2) #define FSP_PDIRQ_SPPEND2 (1 << 1) #define FSP_PDIRQ_SPPEND1 (1 << 0) /* FSP owned headers */ #define FSP_MBX1_FHDR0_REG 0x094 #define FSP_MBX1_FHDR1_REG 0x098 #define FSP_MBX1_FHDR2_REG 0x09C #define FSP_MBX2_FHDR0_REG 0x0b4 #define FSP_MBX2_FHDR1_REG 0x0b8 #define FSP_MBX2_FHDR2_REG 0x0bC /* Data areas, we can only write to host data, and read from FSP data * * Each area is 0x140 bytes long */ #define FSP_MBX1_HDATA_AREA 0x100 #define FSP_MBX1_FDATA_AREA 0x200 #define FSP_MBX2_HDATA_AREA 0x300 #define FSP_MBX2_FDATA_AREA 0x400 /* These are scratch registers */ #define FSP_SCRATCH0_REG 0xe0 #define FSP_SCRATCH1_REG 0xe4 #define FSP_SCRATCH2_REG 0xe8 #define FSP_SCRATCH3_REG 0xec /* This is what the cmd_sub_mod will have for FSP_MCLASS_RR_EVENT */ #define FSP_RESET_START 0x1 #define FSP_RELOAD_COMPLETE 0x2 /* * Message classes */ /* The FSP_MCLASS_RR_EVENT is a special message class that doesn't * participate in mbox event related activities. Its relevant only * for hypervisor internal use. So, handle it specially for command * class extraction too. */ #define FSP_MCLASS_RR_EVENT 0xaa /* see FSP_R/R defines above */ #define FSP_MCLASS_FIRST 0xce #define FSP_MCLASS_SERVICE 0xce #define FSP_MCLASS_IPL 0xcf #define FSP_MCLASS_PCTRL_MSG 0xd0 #define FSP_MCLASS_PCTRL_ABORTS 0xd1 #define FSP_MCLASS_ERR_LOG 0xd2 #define FSP_MCLASS_CODE_UPDATE 0xd3 #define FSP_MCLASS_FETCH_SPDATA 0xd4 #define FSP_MCLASS_FETCH_HVDATA 0xd5 #define FSP_MCLASS_NVRAM 0xd6 #define FSP_MCLASS_MBOX_SURV 0xd7 #define FSP_MCLASS_RTC 0xd8 #define FSP_MCLASS_SMART_CHIP 0xd9 #define FSP_MCLASS_INDICATOR 0xda #define FSP_MCLASS_HMC_INTFMSG 0xe0 #define FSP_MCLASS_HMC_VT 0xe1 #define FSP_MCLASS_HMC_BUFFERS 0xe2 #define FSP_MCLASS_SHARK 0xe3 #define FSP_MCLASS_MEMORY_ERR 0xe4 #define FSP_MCLASS_CUOD_EVENT 0xe5 #define FSP_MCLASS_HW_MAINT 0xe6 #define FSP_MCLASS_VIO 0xe7 #define FSP_MCLASS_SRC_MSG 0xe8 #define FSP_MCLASS_DATA_COPY 0xe9 #define FSP_MCLASS_TONE 0xea #define FSP_MCLASS_VIRTUAL_NVRAM 0xeb #define FSP_MCLASS_TORRENT 0xec #define FSP_MCLASS_NODE_PDOWN 0xed #define FSP_MCLASS_DIAG 0xee #define FSP_MCLASS_PCIE_LINK_TOPO 0xef #define FSP_MCLASS_OCC 0xf0 #define FSP_MCLASS_LAST 0xf0 /* * Commands are provided in rxxyyzz form where: * * - r is 0: no response or 1: response expected * - xx is class * - yy is subcommand * - zz is mod * * WARNING: We only set the r bit for HV->FSP commands * long run, we want to remove use of that bit * and instead have a table of all commands in * the FSP driver indicating which ones take a * response... */ /* * Class 0xCF */ #define FSP_CMD_OPL 0x0cf7100 /* HV->FSP: Operational Load Compl. */ #define FSP_CMD_HV_STATE_CHG 0x0cf0200 /* FSP->HV: Request HV state change */ #define FSP_RSP_HV_STATE_CHG 0x0cf8200 #define FSP_CMD_SP_NEW_ROLE 0x0cf0700 /* FSP->HV: FSP assuming a new role */ #define FSP_RSP_SP_NEW_ROLE 0x0cf8700 #define FSP_CMD_SP_RELOAD_COMP 0x0cf0102 /* FSP->HV: FSP reload complete */ /* * Class 0xCE */ #define FSP_CMD_ACK_DUMP 0x1ce0200 /* HV->FSP: Dump ack */ #define FSP_CMD_HV_QUERY_CAPS 0x1ce0400 /* HV->FSP: Query capabilities */ #define FSP_RSP_HV_QUERY_CAPS 0x1ce8400 #define FSP_CMD_SP_QUERY_CAPS 0x0ce0501 /* FSP->HV */ #define FSP_RSP_SP_QUERY_CAPS 0x0ce8500 #define FSP_CMD_GET_IPL_SIDE 0x1ce0600 /* HV->FSP: Get IPL side and speed */ #define FSP_CMD_SET_IPL_SIDE 0x1ce0780 /* HV->FSP: Set next IPL side */ #define FSP_CMD_ERRLOG_PHYP_ACK 0x1ce0800 /* HV->FSP */ #define FSP_RSP_ERRLOG_PHYP_ACK 0x0ce8800 /* FSP->HV */ #define FSP_CMD_ERRLOG_GET_PLID 0x0ce0900 /* FSP->HV: Get PLID */ #define FSP_RSP_ERRLOG_GET_PLID 0x0ce8900 /* HV->FSP */ #define FSP_CMD_SA_INDICATOR 0x1ce1000 /* HV->FSP: read/update SAI */ #define FSP_RSP_SA_INDICATOR 0x0ce9000 /* FSP->HV */ #define FSP_CMD_QUERY_SPARM 0x1ce1200 /* HV->FSP: System parameter query */ #define FSP_RSP_QUERY_SPARM 0x0ce9200 /* FSP->HV: System parameter resp */ #define FSP_CMD_SET_SPARM_1 0x1ce1301 /* HV->FSP: Set system parameter */ #define FSP_CMD_SET_SPARM_2 0x1ce1302 /* HV->FSP: Set system parameter TCE */ #define FSP_RSP_SET_SPARM 0x0ce9300 /* FSP->HV: Set system parameter resp */ #define FSP_CMD_SP_SPARM_UPD_0 0x0ce1600 /* FSP->HV: Sysparm updated no data */ #define FSP_CMD_SP_SPARM_UPD_1 0x0ce1601 /* FSP->HV: Sysparm updated data */ #define FSP_CMD_HYP_MDST_TABLE 0x1ce2600 /* HV->FSP: Sapphire MDST table */ #define FSP_CMD_TPO_READ 0x1ce4201 /* FSP->HV */ #define FSP_CMD_TPO_WRITE 0x1ce4301 /* HV->FSP */ #define FSP_CMD_STATUS_REQ 0x1ce4800 /* HV->FSP: Request normal panel status */ #define FSP_CMD_STATUS_EX1_REQ 0x1ce4802 /* HV->FSP: Request extended 1 panel status */ #define FSP_CMD_STATUS_EX2_REQ 0x1ce4803 /* HV->FSP: Request extended 2 panel status */ #define FSP_CMD_CONTINUE_ACK 0x0ce5700 /* HV->FSP: HV acks CONTINUE IPL */ #define FSP_CMD_HV_FUNCTNAL 0x1ce5707 /* HV->FSP: Set HV functional state */ #define FSP_CMD_FSP_FUNCTNAL 0x0ce5708 /* FSP->HV: FSP functional state */ #define FSP_CMD_CONTINUE_IPL 0x0ce7000 /* FSP->HV: HV has control */ #define FSP_RSP_SYS_DUMP_OLD 0x0ce7800 /* FSP->HV: Sys Dump Available */ #define FSP_RSP_SYS_DUMP 0x0ce7802 /* FSP->HV: Sys Dump Available */ #define FSP_RSP_RES_DUMP 0x0ce7807 /* FSP->HV: Resource Dump Available */ #define FSP_CMD_PCI_POWER_CONF 0x1ce1b00 /* HV->FSP: Send PCIe list to FSP */ #define FSP_CMD_POWERDOWN_NORM 0x1ce4d00 /* HV->FSP: Normal power down */ #define FSP_CMD_POWERDOWN_QUICK 0x1ce4d01 /* HV->FSP: Quick power down */ #define FSP_CMD_POWERDOWN_PCIRS 0x1ce4d02 /* HV->FSP: PCI cfg reset power dwn */ #define FSP_CMD_REBOOT 0x1ce4e00 /* HV->FSP: Standard IPL */ #define FSP_CMD_DEEP_REBOOT 0x1ce4e04 /* HV->FSP: Deep IPL */ #define FSP_CMD_INIT_DPO 0x0ce5b00 /* FSP->HV: Initialize Delayed Power Off */ #define FSP_RSP_INIT_DPO 0x0cedb00 /* HV->FSP: Response for DPO init command */ #define FSP_CMD_PANELSTATUS 0x0ce5c00 /* FSP->HV */ #define FSP_CMD_PANELSTATUS_EX1 0x0ce5c02 /* FSP->HV */ #define FSP_CMD_PANELSTATUS_EX2 0x0ce5c03 /* FSP->HV */ /* SAI read/update sub commands */ #define FSP_LED_RESET_REAL_SAI 0x00 #define FSP_LED_READ_REAL_SAI 0x02 #define FSP_LED_RESET_PARTITION_SAI 0x80 #define FSP_LED_SET_PARTITION_SAI 0x81 #define FSP_LED_READ_PARTITION_SAI 0x82 #define FSP_LED_READ_PLAT_SAI 0x83 #define FSP_LED_RESET_PLAT_SAI 0x84 #define FSP_LED_SET_PLAT_SAI 0x85 /* * Class 0xD2 */ #define FSP_CMD_CREATE_ERRLOG 0x1d21000 /* HV->FSP */ #define FSP_RSP_CREATE_ERRLOG 0x0d29000 /* FSP->HV */ #define FSP_CMD_ERRLOG_NOTIFICATION 0x0d25a00 /* FSP->HV */ #define FSP_RSP_ERRLOG_NOTIFICATION 0x0d2da00 /* HV->FSP */ #define FSP_RSP_ELOG_NOTIFICATION_ERROR 0x1d2dafe /* HV->FSP */ #define FSP_CMD_FSP_DUMP_INIT 0x1d21200 /* HV->FSP: FSP dump init */ /* * Class 0xD0 */ #define FSP_CMD_SPCN_PASSTHRU 0x1d05400 /* HV->FSP */ #define FSP_RSP_SPCN_PASSTHRU 0x0d0d400 /* FSP->HV */ /* * Class 0xD3 */ #define FSP_CMD_FLASH_START 0x01d30101 /* HV->FSP: Code update start */ #define FSP_CMD_FLASH_COMPLETE 0x01d30201 /* HV->FSP: Code update complete */ #define FSP_CMD_FLASH_ABORT 0x01d302ff /* HV->FSP: Code update complete */ #define FSP_CMD_FLASH_WRITE 0x01d30300 /* HV->FSP: Write LID */ #define FSP_CMD_FLASH_DEL 0x01d30500 /* HV->FSP: Delete LID */ #define FSP_CMD_FLASH_NORMAL 0x01d30401 /* HV->FSP: Commit (T -> P) */ #define FSP_CMD_FLASH_REMOVE 0x01d30402 /* HV->FSP: Reject (P -> T) */ #define FSP_CMD_FLASH_SWAP 0x01d30403 /* HV->FSP: Swap */ #define FSP_CMD_FLASH_OUTC 0x00d30601 /* FSP->HV: Out of band commit */ #define FSP_CMD_FLASH_OUTR 0x00d30602 /* FSP->HV: Out of band reject */ #define FSP_CMD_FLASH_OUTS 0x00d30603 /* FSP->HV: Out of band swap */ #define FSP_CMD_FLASH_OUT_RSP 0x00d38600 /* HV->FSP: Out of band Resp */ #define FSP_CMD_FLASH_CACHE 0x00d30700 /* FSP->HV: Update LID cache */ #define FSP_CMD_FLASH_CACHE_RSP 0x00d38700 /* HV->FSP: Update LID cache Resp */ /* * Class 0xD4 */ #define FSP_CMD_FETCH_SP_DATA 0x1d40101 /* HV->FSP: Fetch & DMA data */ #define FSP_CMD_WRITE_SP_DATA 0x1d40201 /* HV->FSP: Fetch & DMA data */ #define FSP_CMD_FETCH_PLAT_DATA 0x1d40500 /* HV->FSP: Platform function data */ #define FSP_CMD_SEND_PLAT_DATA 0x0d40501 /* FSP->HV */ #define FSP_RSP_PLAT_DATA 0x0d48500 /* HV->FSP */ /* Data set IDs for SP data commands */ #define FSP_DATASET_SP_DUMP 0x01 #define FSP_DATASET_HW_DUMP 0x02 #define FSP_DATASET_ERRLOG 0x03 /* error log entry */ #define FSP_DATASET_MASTER_LID 0x04 #define FSP_DATASET_NONSP_LID 0x05 #define FSP_DATASET_ELID_RDATA 0x06 #define FSP_DATASET_BLADE_PARM 0x07 #define FSP_DATASET_LOC_PORTMAP 0x08 #define FSP_DATASET_SYSIND_CAP 0x09 #define FSP_DATASET_FSP_RSRCDMP 0x0a #define FSP_DATASET_HBRT_BLOB 0x0b /* Adjustment to get T side LIDs */ #define ADJUST_T_SIDE_LID_NO 0x8000 /* * Class 0xD5 */ #define FSP_CMD_ALLOC_INBOUND 0x0d50400 /* FSP->HV: Allocate inbound buf. */ #define FSP_RSP_ALLOC_INBOUND 0x0d58400 /* * Class 0xD7 */ #define FSP_CMD_SURV_HBEAT 0x1d70000 /* ? */ #define FSP_CMD_SURV_ACK 0x0d78000 /* ? */ /* * Class 0xD8 */ #define FSP_CMD_READ_TOD 0x1d82000 /* HV->FSP */ #define FSP_CMD_READ_TOD_EXT 0x1d82001 /* HV->FSP */ #define FSP_CMD_WRITE_TOD 0x1d82100 /* HV->FSP */ #define FSP_CMD_WRITE_TOD_EXT 0x1d82101 /* HV->FSP */ /* * Class 0xDA */ #define FSP_CMD_GET_LED_LIST 0x00da1101 /* Location code information structure */ #define FSP_RSP_GET_LED_LIST 0x00da9100 #define FSP_CMD_RET_LED_BUFFER 0x00da1102 /* Location code buffer information */ #define FSP_RSP_RET_LED_BUFFER 0x00da9100 #define FSP_CMD_GET_LED_STATE 0x00da1103 /* Retrieve Indicator State */ #define FSP_RSP_GET_LED_STATE 0x00da9100 #define FSP_CMD_SET_LED_STATE 0x00da1104 /* Set Service Indicator State */ #define FSP_RSP_SET_LED_STATE 0x00da9100 #define FSP_CMD_GET_MTMS_LIST 0x00da1105 /* Get MTMS and config ID list */ #define FSP_RSP_GET_MTMS_LIST 0x00da9100 #define FSP_CMD_SET_ENCL_MTMS 0x00da1106 /* Set MTMS */ #define FSP_RSP_SET_ENCL_MTMS 0x00da9100 #define FSP_CMD_SET_ENCL_CNFG 0x00da1107 /* Set config ID */ #define FSP_RSP_SET_ENCL_CNFG 0x00da9100 #define FSP_CMD_CLR_INCT_ENCL 0x00da1108 /* Clear inactive address */ #define FSP_RSP_CLR_INCT_ENCL 0x00da9100 #define FSP_CMD_RET_MTMS_BUFFER 0x00da1109 /* Return MTMS buffer */ #define FSP_RSP_RET_MTMS_BUFFER 0x00da9100 #define FSP_CMD_ENCL_MCODE_INIT 0x00da110A /* Mcode update (Initiate download) */ #define FSP_RSP_ENCL_MCODE_INIT 0x00da9100 #define FSP_CMD_ENCL_MCODE_INTR 0x00da110B /* Mcode update (Interrupt download) */ #define FSP_RSP_ENCL_MCODE_INTR 0x00da9100 #define FSP_CMD_ENCL_POWR_TRACE 0x00da110D /* Enclosure power network trace */ #define FSP_RSP_ENCL_POWR_TRACE 0x00da9100 #define FSP_CMD_RET_ENCL_TRACE_BUFFER 0x00da110E /* Return power trace buffer */ #define FSP_RSP_RET_ENCL_TRACE_BUFFER 0x00da9100 #define FSP_CMD_GET_SPCN_LOOP_STATUS 0x00da110F /* Get SPCN loop status */ #define FSP_RSP_GET_SPCN_LOOP_STATUS 0x00da9100 #define FSP_CMD_INITIATE_LAMP_TEST 0x00da1300 /* Initiate LAMP test */ /* * Class 0xE0 * * HACK ALERT: We mark E00A01 (associate serial port) as not needing * a response. We need to do that because the FSP will send as a result * an Open Virtual Serial of the same class *and* expect a reply before * it will respond to associate serial port. That breaks our logic of * supporting only one cmd/resp outstanding per class. */ #define FSP_CMD_HMC_INTF_QUERY 0x0e00100 /* FSP->HV */ #define FSP_RSP_HMC_INTF_QUERY 0x0e08100 /* HV->FSP */ #define FSP_CMD_ASSOC_SERIAL 0x0e00a01 /* HV->FSP: Associate with a port */ #define FSP_RSP_ASSOC_SERIAL 0x0e08a00 /* FSP->HV */ #define FSP_CMD_UNASSOC_SERIAL 0x0e00b01 /* HV->FSP: Deassociate */ #define FSP_RSP_UNASSOC_SERIAL 0x0e08b00 /* FSP->HV */ #define FSP_CMD_OPEN_VSERIAL 0x0e00601 /* FSP->HV: Open serial session */ #define FSP_RSP_OPEN_VSERIAL 0x0e08600 /* HV->FSP */ #define FSP_CMD_CLOSE_VSERIAL 0x0e00701 /* FSP->HV: Close serial session */ #define FSP_RSP_CLOSE_VSERIAL 0x0e08700 /* HV->FSP */ #define FSP_CMD_CLOSE_HMC_INTF 0x0e00300 /* FSP->HV: Close HMC interface */ #define FSP_RSP_CLOSE_HMC_INTF 0x0e08300 /* HV->FSP */ /* * Class E1 */ #define FSP_CMD_VSERIAL_IN 0x0e10100 /* FSP->HV */ #define FSP_CMD_VSERIAL_OUT 0x0e10200 /* HV->FSP */ /* * Class E6 */ #define FSP_CMD_TOPO_ENABLE_DISABLE 0x0e60600 /* FSP->HV */ #define FSP_RSP_TOPO_ENABLE_DISABLE 0x0e68600 /* HV->FSP */ /* * Class E8 */ #define FSP_CMD_READ_SRC 0x1e84a40 /* HV->FSP */ #define FSP_CMD_DISP_SRC_INDIR 0x1e84a41 /* HV->FSP */ #define FSP_CMD_DISP_SRC_DIRECT 0x1e84a42 /* HV->FSP */ #define FSP_CMD_CLEAR_SRC 0x1e84b00 /* HV->FSP */ #define FSP_CMD_DIS_SRC_ECHO 0x1e87600 /* HV->FSP */ /* * Class EB */ #define FSP_CMD_GET_VNVRAM_SIZE 0x01eb0100 /* HV->FSP */ #define FSP_CMD_OPEN_VNVRAM 0x01eb0200 /* HV->FSP */ #define FSP_CMD_READ_VNVRAM 0x01eb0300 /* HV->FSP */ #define FSP_CMD_WRITE_VNVRAM 0x01eb0400 /* HV->FSP */ #define FSP_CMD_GET_VNV_STATS 0x00eb0500 /* FSP->HV */ #define FSP_RSP_GET_VNV_STATS 0x00eb8500 #define FSP_CMD_FREE_VNV_STATS 0x00eb0600 /* FSP->HV */ #define FSP_RSP_FREE_VNV_STATS 0x00eb8600 /* * Class 0xEE */ #define FSP_RSP_DIAG_LINK_ERROR 0x00ee1100 /* FSP->HV */ #define FSP_RSP_DIAG_ACK_TIMEOUT 0x00ee0000 /* FSP->HV */ /* * Class F0 */ #define FSP_CMD_LOAD_OCC 0x00f00100 /* FSP->HV */ #define FSP_RSP_LOAD_OCC 0x00f08100 /* HV->FSP */ #define FSP_CMD_LOAD_OCC_STAT 0x01f00300 /* HV->FSP */ #define FSP_CMD_RESET_OCC 0x00f00200 /* FSP->HV */ #define FSP_RSP_RESET_OCC 0x00f08200 /* HV->FSP */ #define FSP_CMD_RESET_OCC_STAT 0x01f00400 /* HV->FSP */ /* * Class E4 */ #define FSP_CMD_MEM_RES_CE 0x00e40300 /* FSP->HV: Memory resilience CE */ #define FSP_CMD_MEM_RES_UE 0x00e40301 /* FSP->HV: Memory resilience UE */ #define FSP_CMD_MEM_RES_UE_SCRB 0x00e40302 /* FSP->HV: UE detected by scrub */ #define FSP_RSP_MEM_RES 0x00e48300 /* HV->FSP */ #define FSP_CMD_MEM_DYN_DEALLOC 0x00e40500 /* FSP->HV: Dynamic mem dealloc */ #define FSP_RSP_MEM_DYN_DEALLOC 0x00e48500 /* HV->FSP */ /* * Functions exposed to the rest of skiboot */ /* An FSP message */ enum fsp_msg_state { fsp_msg_unused = 0, fsp_msg_queued, fsp_msg_sent, fsp_msg_wresp, fsp_msg_done, fsp_msg_timeout, fsp_msg_incoming, fsp_msg_response, fsp_msg_cancelled, }; struct fsp_msg { /* * User fields. Don't populate word0.seq (upper 16 bits), this * will be done by fsp_queue_msg() */ u8 dlen; /* not including word0/word1 */ u32 word0; /* seq << 16 | cmd */ u32 word1; /* mod << 8 | sub */ union { u32 words[14]; u8 bytes[56]; } data; /* Completion function. Called with no lock held */ void (*complete)(struct fsp_msg *msg); void *user_data; /* * Driver updated fields */ /* Current msg state */ enum fsp_msg_state state; /* Set if the message expects a response */ bool response; /* Response will be filed by driver when response received */ struct fsp_msg *resp; /* Internal queuing */ struct list_node link; }; /* This checks if a message is still "in progress" in the FSP driver */ static inline bool fsp_msg_busy(struct fsp_msg *msg) { switch(msg->state) { case fsp_msg_unused: case fsp_msg_done: case fsp_msg_timeout: case fsp_msg_response: /* A response is considered a completed msg */ return false; default: break; } return true; } static inline u32 fsp_msg_cmd(const struct fsp_msg *msg) { u32 cmd_sub_mod; cmd_sub_mod = (msg->word0 & 0xff) << 16; cmd_sub_mod |= (msg->word1 & 0xff) << 8; cmd_sub_mod |= (msg->word1 & 0xff00) >> 8; return cmd_sub_mod; } /* Initialize the FSP mailbox driver */ extern void fsp_init(void); /* Perform the OPL sequence */ extern void fsp_opl(void); /* Check if system has an FSP */ extern bool fsp_present(void); /* Allocate and populate an fsp_msg structure * * WARNING: Do _NOT_ use free() on an fsp_msg, use fsp_freemsg() * instead as we will eventually use pre-allocated message pools */ extern struct fsp_msg *fsp_allocmsg(bool alloc_response) __warn_unused_result; extern struct fsp_msg *fsp_mkmsg(u32 cmd_sub_mod, u8 add_words, ...) __warn_unused_result; /* Populate a pre-allocated msg */ extern void fsp_fillmsg(struct fsp_msg *msg, u32 cmd_sub_mod, u8 add_words, ...); /* Free a message * * WARNING: This will also free an attached response if any */ extern void fsp_freemsg(struct fsp_msg *msg); /* Free a message and not the attached reply */ extern void __fsp_freemsg(struct fsp_msg *msg); /* Cancel a message from the msg queue * * WARNING: * This is intended for use only in the FSP r/r scenario. * * This will also free an attached response if any */ extern void fsp_cancelmsg(struct fsp_msg *msg); /* Enqueue it in the appropriate FSP queue * * NOTE: This supports being called with the FSP lock already * held. This is the only function in this module that does so * and is meant to be used that way for sending serial "poke" * commands to the FSP. */ extern int fsp_queue_msg(struct fsp_msg *msg, void (*comp)(struct fsp_msg *msg)) __warn_unused_result; /* Synchronously send a command. If there's a response, the status is * returned as a positive number. A negative result means an error * sending the message. * * If autofree is set, the message and the reply (if any) are freed * after extracting the status. If not set, you are responsible for * freeing both the message and an eventual response * * NOTE: This will call fsp_queue_msg(msg, NULL), hence clearing the * completion field of the message. No synchronous message is expected * to utilize asynchronous completions. */ extern int fsp_sync_msg(struct fsp_msg *msg, bool autofree); /* Handle FSP interrupts */ extern void fsp_interrupt(void); /* An FSP client is interested in messages for a given class */ struct fsp_client { /* Return true to "own" the message (you can free it) */ bool (*message)(u32 cmd_sub_mod, struct fsp_msg *msg); struct list_node link; }; /* WARNING: Command class FSP_MCLASS_IPL is aliased to FSP_MCLASS_SERVICE, * thus a client of one will get both types of messages. * * WARNING: Client register/unregister takes *NO* lock. These are expected * to be called early at boot before CPUs are brought up and before * fsp_poll() can race. The client callback is called with no lock held. */ extern void fsp_register_client(struct fsp_client *client, u8 msgclass); extern void fsp_unregister_client(struct fsp_client *client, u8 msgclass); /* FSP TCE map/unmap functions */ extern void fsp_tce_map(u32 offset, void *addr, u32 size); extern void fsp_tce_unmap(u32 offset, u32 size); extern void *fsp_inbound_buf_from_tce(u32 tce_token); /* Data fetch helper */ extern uint32_t fsp_adjust_lid_side(uint32_t lid_no); extern int fsp_fetch_data(uint8_t flags, uint16_t id, uint32_t sub_id, uint32_t offset, void *buffer, size_t *length); extern int fsp_fetch_data_queue(uint8_t flags, uint16_t id, uint32_t sub_id, uint32_t offset, void *buffer, size_t *length, void (*comp)(struct fsp_msg *msg)) __warn_unused_result; extern int fsp_start_preload_resource(enum resource_id id, uint32_t idx, void *buf, size_t *size); extern int fsp_resource_loaded(enum resource_id id, uint32_t idx); extern int fsp_preload_lid(uint32_t lid_no, char *buf, size_t *size); extern int fsp_wait_lid_loaded(uint32_t lid_no); /* FSP console stuff */ extern void fsp_console_preinit(void); extern void fsp_console_init(void); extern void fsp_console_add_nodes(void); extern void fsp_console_select_stdout(void); extern void fsp_console_reset(void); extern void fsp_console_poll(void *); /* Mark FSP lock */ extern void fsp_used_by_console(void); /* NVRAM */ extern int fsp_nvram_info(uint32_t *total_size); extern int fsp_nvram_start_read(void *dst, uint32_t src, uint32_t len); extern int fsp_nvram_write(uint32_t offset, void *src, uint32_t size); extern void fsp_nvram_wait_open(void); /* RTC */ extern void fsp_rtc_init(void); /* ELOG */ extern void fsp_elog_read_init(void); extern void fsp_elog_write_init(void); /* Code update */ extern void fsp_code_update_init(void); extern void fsp_code_update_wait_vpd(bool is_boot); /* Dump */ extern void fsp_dump_init(void); extern void fsp_fips_dump_notify(uint32_t dump_id, uint32_t dump_len); /* Attention Handler */ extern void fsp_attn_init(void); /* MDST table */ extern void fsp_mdst_table_init(void); /* This can be set by the fsp_opal_update_flash so that it can * get called just reboot we reboot shutdown the machine. */ extern int (*fsp_flash_term_hook)(void); /* Surveillance */ extern void fsp_init_surveillance(void); extern void fsp_surv_query(void); /* IPMI */ extern void fsp_ipmi_init(void); /* Reset/Reload */ extern void fsp_reinit_fsp(void); extern void fsp_trigger_reset(void); extern void fsp_reset_links(void); /* FSP memory errors */ extern void fsp_memory_err_init(void); /* Sensor */ extern void fsp_init_sensor(void); extern int64_t fsp_opal_read_sensor(uint32_t sensor_hndl, int token, uint32_t *sensor_data); /* Diagnostic */ extern void fsp_init_diag(void); /* LED */ extern void fsp_led_init(void); extern void create_led_device_nodes(void); /* EPOW */ extern void fsp_epow_init(void); /* DPO */ extern void fsp_dpo_init(void); extern bool fsp_dpo_pending; /* Chiptod */ extern void fsp_chiptod_init(void); /* Terminate immediate */ extern void __attribute__((noreturn)) ibm_fsp_terminate(const char *msg); #endif /* __FSP_H */ skiboot-skiboot-5.1.13/include/gx.h000066400000000000000000000033601265204436200171430ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Definitions relative to the P7 and P7+ GX controller */ #ifndef __GX_H #define __GX_H #include /* P7 GX Mode 1 register (contains PSI BUID) */ #define GX_P7_MODE1_REG 0x0201180A #define GX_P7_MODE1_PSI_BUID PPC_BITMASK(18,26) #define GX_P7_MODE1_PSI_BUID_DISABLE PPC_BIT(27) /* P7+ GX Mode 4 register (PSI and NX BUIDs ) */ #define GX_P7P_MODE4_REG 0x02011811 #define GX_P7P_MODE4_ENABLE_NX_BUID PPC_BIT(0) #define GX_P7P_MODE4_NX_BUID_BASE PPC_BITMASK(1,9) #define GX_P7P_MODE4_NX_BUID_MASK PPC_BITMASK(10,18) #define GX_P7P_MODE4_PSI_BUID PPC_BITMASK(19,27) #define GX_P7P_MODE4_PSI_BUID_DISABLE PPC_BIT(28) /* P7 GX TCE BAR and mask */ #define GX_P7_GX0_TCE_BAR 0x02011845 #define GX_P7_TCE_BAR_ADDR PPC_BITMASK(0,25) #define GX_P7_TCE_BAR_ADDR_SHIFT PPC_BITLSHIFT(43) #define GX_P7_TCE_BAR_ENABLE PPC_BIT(26) #define GX_P7_GX0_TCE_MASK 0x0201184B #define GX_P7_TCE_MASK PPC_BITMASK(0,25) #define GX_P7_GX1_TCE_BAR 0x02011885 #define GX_P7_GX1_TCE_MASK 0x0201188B extern int gx_configure_psi_buid(uint32_t chip, uint32_t buid); extern int gx_configure_tce_bar(uint32_t chip, uint32_t gx, uint64_t addr, uint64_t size); #endif /* __GX_H */ skiboot-skiboot-5.1.13/include/hostservices.h000066400000000000000000000023021265204436200212410ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __HOSTSERVICES_H #define __HOSTSERVICES_H bool hservices_init(void); void hservices_lid_preload(void); bool hservices_lid_preload_complete(void); int host_services_occ_load(void); int host_services_occ_start(void); int host_services_occ_stop(void); void host_services_occ_base_setup(void); /* No LID can be larger than 16M, but OCC lid is less than 1 MB */ #define HBRT_LOAD_LID_SIZE 0x100000 /* 1MB */ /* TODO: Detect OCC lid size at runtime */ /* Homer and OCC area size */ #define HOMER_IMAGE_SIZE 0x400000 /* 4MB per-chip */ #define OCC_COMMON_SIZE 0x800000 /* 8MB */ #endif /* __HOSTSERVICES_H */ skiboot-skiboot-5.1.13/include/i2c.h000066400000000000000000000052461265204436200172070ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __I2C_H #define __I2C_H struct i2c_request; struct i2c_bus { struct list_node link; struct dt_node *dt_node; uint32_t opal_id; int (*queue_req)(struct i2c_request *req); struct i2c_request *(*alloc_req)(struct i2c_bus *bus); void (*free_req)(struct i2c_request *req); }; /* * I2C specific OPAL error codes: * * OPAL_I2C_TIMEOUT I2C request timed out * OPAL_I2C_INVALID_CMD New command given when old not completed yet * OPAL_I2C_LBUS_PARITY Local bus parity error * OPAL_I2C_BKEND_OVERRUN Writing/reading into full/empty fifo respectively * OPAL_I2C_BKEND_ACCESS Writing/reading more data than requested * OPAL_I2C_ARBT_LOST I2C bus is held by some other master * OPAL_I2C_NACK_RCVD Slave is not responding back with the ACK * OPAL_I2C_STOP_ERR Did not able to send the STOP condtion on bus */ struct i2c_request { struct list_node link; struct i2c_bus *bus; enum i2c_operation { I2C_READ, /* RAW read from the device without offset */ I2C_WRITE, /* RAW write to the device without offset */ SMBUS_READ, /* SMBUS protocol read from the device */ SMBUS_WRITE, /* SMBUS protocol write to the device */ } op; int result; /* OPAL i2c error code */ uint32_t dev_addr; /* Slave device address */ uint32_t offset_bytes; /* Internal device offset */ uint32_t offset; /* Internal device offset */ uint32_t rw_len; /* Length of the data request */ void *rw_buf; /* Data request buffer */ void (*completion)( /* Completion callback */ int rc, struct i2c_request *req); void *user_data; /* Client data */ }; /* Generic i2c */ extern void i2c_add_bus(struct i2c_bus *bus); extern struct i2c_bus *i2c_find_bus_by_id(uint32_t opal_id); static inline struct i2c_request *i2c_alloc_req(struct i2c_bus *bus) { return bus->alloc_req(bus); } static inline void i2c_free_req(struct i2c_request *req) { req->bus->free_req(req); } static inline int i2c_queue_req(struct i2c_request *req) { return req->bus->queue_req(req); } /* P8 implementation details */ extern void p8_i2c_init(void); extern void p8_i2c_interrupt(uint32_t chip_id); #endif /* __I2C_H */ skiboot-skiboot-5.1.13/include/interrupts.h000066400000000000000000000213651265204436200207510ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __INTERRUPTS_H #define __INTERRUPTS_H #include #include /* * Note about interrupt numbers on P7/P7+ * ====================================== * * The form of an interrupt number in the system on P7/P7+ is as follow: * * | Node | T| Chip|GX| BUID | Level | * |--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--| * * Where: * * - Node : The 3-bit node number * - T : 1 for a Torrent chip, 0 otherwise * - Chip : 2-bit chip number in a node * - GX : GX bus identifier * - BUID : Bus identifier (*) * - Level : Interrupt number * * (*) The BUID/Level distinction is mostly historical, interrupt * controllers such as the ICS in the PHBs "use" some of the * low BUID bits as an extension to the interrupt number * * The NodeID and ChipID together form a 5-bit Processor Chip ID as * found in the PIR or in the SPIRA data structures (without the T bit) * * PSI interrupt numbering scheme: * ------------------------------- * * This is tentatively deduced from stuff I found in some SCOM regs * and in the BookIV. The PSIHB can be used to specify the 9-bit BUID, * the Level is always 0. The doc also says that it prepends the 6-bit * PowerBus chipID (Node + T + Chip). I *assume* that it also prepends * a 0 in place of the GX bit. * * OPAL seems to be arbitrarily using a BUID value of 0x3, I shall do * the same "just in case" :-) * * NOTE: From grep'ing around the giant SCOM file for "Build", I found * what looks like a register in the GX controller (Mode1 * register) where the PSI BUID can be stored as well. From * looking around with the FSP getscom command, it appears * that both pHyp and OPAL set this consistently to the same * value that appears in the PHB configuration. * * => This is confirmed. The NX needs a similar configuration, this * tells the GX controller not to forward transactions for these * BUIDs down the GX bus. * * PCI interrupt numbering scheme: * ------------------------------- * * See IOCs * * NX interrupt numbering scheme (p7+): * ------------------------------------ * * TBD * * * Additional note about routing of interrupts in P7 and P7+ * ========================================================= * * There are two on-chip sources of interrupts on these that need a * special treatment: The PSI interrupt and the NX interrupts. * * The problem is that they use the same BUID space as the IO chips * connected to the GX bus, so the GX controller needs to be told * about these BUIDs in order to avoid forwarding them down the GX * link (and possibly choking due to the lack of reply). * * The bad news is that it's all undocumented. The good news is that * I found the info after chatting with Bill Daly (HW design) and * looking at the SCOM register maps. * * The way to set that up differs between P7 and P7+: * * - On P7, it's in the GX_MODE1 register at SCOM 0x0201180A, which * among other things, contains those bits: * * 18:26 PSI_BUID: BUID to be used to indicate the interrupt is * for the PSI * 27 DISABLE_PSI_BUID: set to 1 to disable the buid reservation * for PSI * * So one must write the 9-bit BUID (without the top chipID) of the * PSI interrupt in there and clear the disable bit. * * - On P7+ it's in the GX_MODE4 register at SCOM 0x02011811 * * 0 ENABLE_NX_BUID: set to 1 to enable the buid reservation for nx * 1:9 NX_BUID_BASE: BUID BASE to be used to indicate the interrupt * is for the nx * 10:18 NX_BUID_MASK: BUID mask for the nx buid base * 19:27 PSI_BUID: BUID to be used to indicate the interrupt is for * the PSI * 28 DISABLE_PSI_BUID: set to 1 to disable the buid reservation * for PSI * * Note: The NX_BUID_MASK should have bits set to 1 that are relevant for * the comparison to NX_BUID_BASE, ie 4 interrupts means a mask * value of b'111111100 */ #define P7_PSI_IRQ_BUID 0x3 /* 9-bit BUID for the PSI interrupts */ /* Extract individual components of an IRQ number */ #define P7_IRQ_BUID(irq) (((irq) >> 4) & 0x1ff) #define P7_IRQ_GXID(irq) (((irq) >> 13) & 0x1) #define P7_IRQ_CHIP(irq) (((irq) >> 14) & 0x3) #define P7_IRQ_TBIT(irq) (((irq) >> 16) & 0x1) #define P7_IRQ_NODE(irq) (((irq) >> 17) & 0x7) /* Extract the "full BUID" (extension + BUID) */ #define P7_IRQ_FBUID(irq) (((irq) >> 4) & 0xffff) /* BUID Extension (GX + CHIP + T + NODE) */ #define P7_IRQ_BEXT(irq) (((irq) >> 13) & 0x7f) /* Strip extension from BUID */ #define P7_BUID_BASE(buid) ((buid) & 0x1ff) /* Note about interrupt numbers on P8 * ================================== * * On P8 the interrupts numbers are just a flat space of 19-bit, * there is no BUID or similar. * * However, various unit tend to require blocks of interrupt that * are naturally power-of-two aligned * * Our P8 Interrupt map consits thus of dividing the chip space * into 4 "blocks" of 2048 interrupts. Block 0 is for random chip * interrupt sources (NX, PSI, OCC, ...) and keeps sources 0..15 * clear to avoid conflits with IPIs etc.... Block 1..3 are assigned * to PHB 0..2 respectively. * * That gives us an interrupt number made of: * 18 13 12 11 10 0 * | | | | | | * +--------------------+------+-----------------------------+ * | Chip# | PHB# | IVE# | * +--------------------+------+-----------------------------+ * * We can thus support a max of 2^6 = 64 chips * * Each PHB supports 2K interrupt sources, which is shared by * LSI and MSI. With default configuration, MSI would use range * [0, 0x7f7] and LSI would use [0x7f8, 0x7ff]. The interrupt * source should be combined with IRSN to form final hardware * IRQ. * */ #define P8_CHIP_IRQ_BASE(chip) ((chip) << 13) #define P8_CHIP_IRQ_BLOCK_BASE(chip, block) (P8_CHIP_IRQ_BASE(chip) \ | ((block) << 11)) #define P8_IRQ_BLOCK_MISC 0 #define P8_IRQ_BLOCK_PHB0 1 #define P8_IRQ_BLOCK_PHB1 2 #define P8_IRQ_BLOCK_PHB2 3 #define P8_CHIP_IRQ_PHB_BASE(chip, phb) (P8_CHIP_IRQ_BLOCK_BASE(chip,\ (phb) + P8_IRQ_BLOCK_PHB0)) #define P8_IRQ_TO_CHIP(irq) (((irq) >> 13) & 0x3f) #define P8_IRQ_TO_BLOCK(irq) (((irq) >> 11) & 0x03) #define P8_IRQ_TO_PHB(irq) (P8_IRQ_TO_BLOCK(irq) - \ P8_IRQ_BLOCK_PHB0) /* Assignment of the "MISC" block: * ------------------------------- * * PSI interface has 6 interrupt sources: * * FSP, OCC, FSI, LPC, Local error, Host error * * and thus needs a block of 8 */ #define P8_IRQ_MISC_PSI_BASE 0x10 /* 0x10..0x17 */ /* These are handled by skiboot */ #define P8_IRQ_PSI_SKIBOOT_BASE 0 #define P8_IRQ_PSI_FSP 0 #define P8_IRQ_PSI_OCC 1 #define P8_IRQ_PSI_FSI 2 #define P8_IRQ_PSI_LPC 3 #define P8_IRQ_PSI_LOCAL_ERR 4 #define P8_IRQ_PSI_LOCAL_COUNT 5 #define P8_IRQ_PSI_ALL_COUNT 6 /* These are passed onto Linux */ #define P8_IRQ_PSI_LINUX_BASE 5 #define P8_IRQ_PSI_HOST_ERR 5 /* Used for UART */ #define P8_IRQ_PSI_LINUX_COUNT 1 /* TBD: NX, AS, ... */ /* * IRQ sources register themselves here. If an "interrupts" callback * is provided, then all interrupts in that source will appear in * 'opal-interrupts' and will be handled by us. */ struct irq_source_ops { int64_t (*set_xive)(void *data, uint32_t isn, uint16_t server, uint8_t priority); int64_t (*get_xive)(void *data, uint32_t isn, uint16_t *server, uint8_t *priority); void (*interrupt)(void *data, uint32_t isn); }; extern void register_irq_source(const struct irq_source_ops *ops, void *data, uint32_t start, uint32_t count); extern void unregister_irq_source(uint32_t start, uint32_t count); extern uint32_t get_psi_interrupt(uint32_t chip_id); extern struct dt_node *add_ics_node(void); extern void add_opal_interrupts(void); extern uint32_t get_ics_phandle(void); struct cpu_thread; extern void reset_cpu_icp(void); extern void icp_send_eoi(uint32_t interrupt); extern void icp_prep_for_rvwinkle(void); extern void icp_kick_cpu(struct cpu_thread *cpu); extern void init_interrupts(void); #endif /* __INTERRUPTS_H */ skiboot-skiboot-5.1.13/include/inttypes.h000066400000000000000000000024311265204436200204020ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This file exists because a bunch of files are built as part of * unit tests as well as skiboot and inttypes.h is part of libc rather * than gcc, so to get the magic to work when we don't have libc sitting * around, we get to rewrite inttypes.h. */ #ifndef __SKIBOOT_INTTYPES_H #define __SKIBOOT_INTTYPES_H #include #ifndef __WORDSIZE /* If we don't have __WORDSIZE it means we're *certainly* building skiboot * which will *ALWAYS* have a word size of 32bits. * (unless someone goes and ports skiboot to something that isn't powerpc) */ #define __WORDSIZE 32 #endif #if __WORDSIZE == 64 #define PRIu64 "lu" #define PRIx64 "lx" #else #define PRIu64 "llu" #define PRIx64 "llx" #endif #endif skiboot-skiboot-5.1.13/include/io.h000066400000000000000000000075021265204436200171360ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __IO_H #define __IO_H #ifndef __ASSEMBLY__ #include #include #include #include /* * IO access functions * * __in_beXX() / __out_beXX() : non-byteswap, no barrier * in_beXX() / out_beXX() : non-byteswap, barrier * in_leXX() / out_leXX() : byteswap, barrier */ static inline uint8_t __in_8(const volatile uint8_t *addr) { uint8_t val; asm volatile("lbzcix %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr) : "memory"); return val; } static inline uint8_t in_8(const volatile uint8_t *addr) { sync(); return __in_8(addr); } static inline uint16_t __in_be16(const volatile uint16_t *addr) { uint16_t val; asm volatile("lhzcix %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr) : "memory"); return val; } static inline uint16_t in_be16(const volatile uint16_t *addr) { sync(); return __in_be16(addr); } static inline uint16_t in_le16(const volatile uint16_t *addr) { return bswap_16(in_be16(addr)); } static inline uint32_t __in_be32(const volatile uint32_t *addr) { uint32_t val; asm volatile("lwzcix %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr) : "memory"); return val; } static inline uint32_t in_be32(const volatile uint32_t *addr) { sync(); return __in_be32(addr); } static inline uint32_t in_le32(const volatile uint32_t *addr) { return bswap_32(in_be32(addr)); } static inline uint64_t __in_be64(const volatile uint64_t *addr) { uint64_t val; asm volatile("ldcix %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr) : "memory"); return val; } static inline uint64_t in_be64(const volatile uint64_t *addr) { sync(); return __in_be64(addr); } static inline uint64_t in_le64(const volatile uint64_t *addr) { return bswap_64(in_be64(addr)); } static inline void __out_8(volatile uint8_t *addr, uint8_t val) { asm volatile("stbcix %0,0,%1" : : "r"(val), "r"(addr), "m"(*addr) : "memory"); } static inline void out_8(volatile uint8_t *addr, uint8_t val) { sync(); return __out_8(addr, val); } static inline void __out_be16(volatile uint16_t *addr, uint16_t val) { asm volatile("sthcix %0,0,%1" : : "r"(val), "r"(addr), "m"(*addr) : "memory"); } static inline void out_be16(volatile uint16_t *addr, uint16_t val) { sync(); return __out_be16(addr, val); } static inline void out_le16(volatile uint16_t *addr, uint16_t val) { out_be16(addr, bswap_16(val)); } static inline void __out_be32(volatile uint32_t *addr, uint32_t val) { asm volatile("stwcix %0,0,%1" : : "r"(val), "r"(addr), "m"(*addr) : "memory"); } static inline void out_be32(volatile uint32_t *addr, uint32_t val) { sync(); return __out_be32(addr, val); } static inline void out_le32(volatile uint32_t *addr, uint32_t val) { out_be32(addr, bswap_32(val)); } static inline void __out_be64(volatile uint64_t *addr, uint64_t val) { asm volatile("stdcix %0,0,%1" : : "r"(val), "r"(addr), "m"(*addr) : "memory"); } static inline void out_be64(volatile uint64_t *addr, uint64_t val) { sync(); return __out_be64(addr, val); } static inline void out_le64(volatile uint64_t *addr, uint64_t val) { out_be64(addr, bswap_64(val)); } /* Assistant to macros used to access PCI config space */ #define in_le8 in_8 #define out_le8 out_8 #endif /* __ASSEMBLY__ */ #endif /* __IO_H */ skiboot-skiboot-5.1.13/include/ipmi.h000066400000000000000000000222221265204436200174610ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __IPMI_H #define __IPMI_H #include #include #include /* * IPMI codes as defined by the standard. */ #define IPMI_GET_DEVICE_ID_CMD 0x01 #define IPMI_COLD_RESET_CMD 0x02 #define IPMI_WARM_RESET_CMD 0x03 #define IPMI_CLEAR_MSG_FLAGS_CMD 0x30 #define IPMI_GET_DEVICE_GUID_CMD 0x08 #define IPMI_GET_MSG_FLAGS_CMD 0x31 #define IPMI_SEND_MSG_CMD 0x34 #define IPMI_GET_MSG_CMD 0x33 #define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f #define IPMI_READ_EVENT_MSG_BUFFER_CMD 0x35 #define IPMI_GET_CHANNEL_INFO_CMD 0x42 /* * 28. Chassis Commands */ #define IPMI_CHASSIS_GET_CAP_CMD 0x00 #define IPMI_CHASSIS_GET_STATUS_CMD 0x01 #define IPMI_CHASSIS_CONTROL_CMD 0x02 #define IPMI_CHASSIS_RESET_CMD 0x03 #define IPMI_CHASSIS_IDENTIFY_CMD 0x04 #define IPMI_CHASSIS_SET_PANEL_BUTTON_EN_CMD 0x05 #define IPMI_CHASSIS_SET_CAP_CMD 0x06 #define IPMI_CHASSIS_SET_PWR_RESTORE_CMD 0x07 #define IPMI_CHASSIS_SET_PWR_CYCLE_CMD 0x08 #define IPMI_CHASSIS_GET_SYS_RESTART_CAUSE_CMD 0x09 #define IPMI_CHASSIS_SET_SYS_BOOT_OPT_CMD 0x0a #define IPMI_CHASSIS_GET_SYS_BOOT_OPT_CMD 0x0b #define IPMI_CHASSIS_GET_POH_COUNTER_CMD 0x0f /* 28.3. Chassis Control Command */ #define IPMI_CHASSIS_PWR_DOWN 0x00 #define IPMI_CHASSIS_PWR_UP 0x01 #define IPMI_CHASSIS_PWR_CYCLE 0x02 #define IPMI_CHASSIS_HARD_RESET 0x03 #define IPMI_CHASSIS_PULSE_DIAG 0x04 #define IPMI_CHASSIS_SOFT_SHUTDOWN 0x05 /* 20.7. ACPI Power State Command */ #define IPMI_PWR_SYS_S0_WORKING 0x00 #define IPMI_PWR_SYS_S1 0x01 #define IPMI_PWR_SYS_S2 0x02 #define IPMI_PWR_SYS_S3_SUSPEND_TO_RAM 0x03 #define IPMI_PWR_SYS_S4_SUSPEND_TO_DISK 0x04 #define IPMI_PWR_SYS_S5_SOFT_OFF 0x05 #define IPMI_PWR_SYS_SUSPEND 0x06 #define IPMI_PWR_SYS_LEGACY_ON 0x20 #define IPMI_PWR_SYS_LEGACY_OFF 0x21 #define IPMI_PWR_SYS_UNKNOWN 0x2a #define IPMI_PWR_NOCHANGE 0x7f /* 22.{3,4} Clear / Get message flags */ #define IPMI_MESSAGE_FLAGS_RX_MESSAGE_QUEUE (1<<0) #define IPMI_MESSAGE_FLAGS_EVENT_BUFFER (1<<1) #define IPMI_MESSAGE_FLAGS_WATCHDOG_PRE_TIMEOUT (1<<3) #define IPMI_MESSAGE_FLAGS_OEM0 (1<<5) #define IPMI_MESSAGE_FLAGS_OEM1 (1<<6) #define IPMI_MESSAGE_FLAGS_OEM2 (1<<7) /* Firmware Progress Sensor states */ #define IPMI_FW_PCI_INIT 0x07 #define IPMI_FW_OS_BOOT 0x13 #define IPMI_FW_MOTHERBOARD_INIT 0x14 #define IPMI_CODE(netfn, cmd) ((netfn) << 8 | (cmd)) #define IPMI_CMD(code) ((code) & 0xff) #define IPMI_NETFN(code) ((code) >> 8 & 0xff) #define IPMI_NETFN_RETURN_CODE(netfn) ((netfn) | 0x4) #define IPMI_NETFN_CHASSIS 0x00 #define IPMI_NETFN_SE 0x04 #define IPMI_NETFN_STORAGE 0x0a #define IPMI_NETFN_APP 0x06 #define IPMI_WRITE_FRU IPMI_CODE(IPMI_NETFN_STORAGE, 0x12) #define IPMI_GET_SEL_INFO IPMI_CODE(IPMI_NETFN_STORAGE, 0x40) #define IPMI_RESERVE_SEL IPMI_CODE(IPMI_NETFN_STORAGE, 0x42) #define IPMI_ADD_SEL_EVENT IPMI_CODE(IPMI_NETFN_STORAGE, 0x44) #define IPMI_GET_SEL_TIME IPMI_CODE(IPMI_NETFN_STORAGE, 0x48) #define IPMI_SET_SEL_TIME IPMI_CODE(IPMI_NETFN_STORAGE, 0x49) #define IPMI_CHASSIS_CONTROL IPMI_CODE(IPMI_NETFN_CHASSIS, 0x02) #define IPMI_SET_POWER_STATE IPMI_CODE(IPMI_NETFN_APP, 0x06) #define IPMI_GET_POWER_STATE IPMI_CODE(IPMI_NETFN_APP, 0x07) #define IPMI_RESET_WDT IPMI_CODE(IPMI_NETFN_APP, 0x22) #define IPMI_SET_WDT IPMI_CODE(IPMI_NETFN_APP, 0x24) #define IPMI_SET_ENABLES IPMI_CODE(IPMI_NETFN_APP, 0x2E) #define IPMI_GET_ENABLES IPMI_CODE(IPMI_NETFN_APP, 0x2F) #define IPMI_CLEAR_MESSAGE_FLAGS IPMI_CODE(IPMI_NETFN_APP, 0x30) #define IPMI_GET_MESSAGE_FLAGS IPMI_CODE(IPMI_NETFN_APP, 0x31) #define IPMI_GET_MESSAGE IPMI_CODE(IPMI_NETFN_APP, 0x33) #define IPMI_READ_EVENT IPMI_CODE(IPMI_NETFN_APP, 0x35) #define IPMI_SET_SENSOR_READING IPMI_CODE(IPMI_NETFN_SE, 0x30) /* AMI OEM comamnds. AMI uses NETFN 0x3a and 0x32 */ #define IPMI_PARTIAL_ADD_ESEL IPMI_CODE(0x32, 0xf0) #define IPMI_PNOR_ACCESS_STATUS IPMI_CODE(0x3a, 0x07) /* * IPMI response codes. */ #define IPMI_CC_NO_ERROR 0x00 #define IPMI_NODE_BUSY_ERR 0xc0 #define IPMI_INVALID_COMMAND_ERR 0xc1 #define IPMI_TIMEOUT_ERR 0xc3 #define IPMI_ERR_MSG_TRUNCATED 0xc6 #define IPMI_REQ_LEN_INVALID_ERR 0xc7 #define IPMI_REQ_LEN_EXCEEDED_ERR 0xc8 #define IPMI_NOT_IN_MY_STATE_ERR 0xd5 /* IPMI 2.0 */ #define IPMI_LOST_ARBITRATION_ERR 0x81 #define IPMI_BUS_ERR 0x82 #define IPMI_NAK_ON_WRITE_ERR 0x83 #define IPMI_ERR_UNSPECIFIED 0xff #define IPMI_DEFAULT_INTERFACE 0 #define IPMI_MAX_REQ_SIZE 60 #define IPMI_MAX_RESP_SIZE 60 /* * As far as I can tell the size of PEL record is unbounded (due to * the possible presence of the user defined section). We chose this * size because it's what hostboot also uses and most of the OPAL logs * are few hundred bytes. */ #define IPMI_MAX_PEL_SIZE 0x800 struct ipmi_backend; struct ipmi_msg { /* Can be used by command implementations to track requests */ struct list_node link; struct ipmi_backend *backend; uint8_t netfn; uint8_t cmd; uint8_t cc; /* Called when a response is received to the ipmi message */ void (*complete)(struct ipmi_msg *); /* Called if non-NULL when the ipmi layer detects an error */ void (*error)(struct ipmi_msg *); void *user_data; uint8_t req_size; uint8_t resp_size; uint8_t *data; }; struct ipmi_backend { uint64_t opal_event_ipmi_recv; struct ipmi_msg *(*alloc_msg)(size_t, size_t); void (*free_msg)(struct ipmi_msg *); int (*queue_msg)(struct ipmi_msg *); int (*queue_msg_head)(struct ipmi_msg *); int (*dequeue_msg)(struct ipmi_msg *); }; extern struct ipmi_backend *ipmi_backend; /* Initialise the IPMI interface */ void ipmi_init(void); bool ipmi_present(void); void ipmi_free_msg(struct ipmi_msg *msg); struct ipmi_msg *ipmi_mkmsg_simple(uint32_t code, void *req_data, size_t req_size); struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code, void (*complete)(struct ipmi_msg *), void *user_data, void *req_data, size_t req_size, size_t resp_size); /* Initialise a previously allocated message with the required fields. The caller must ensure the message is large enough to hold the request and response data. */ void ipmi_init_msg(struct ipmi_msg *msg, int interface, uint32_t code, void (*complete)(struct ipmi_msg *), void *user_data, size_t req_size, size_t resp_size); /* called by backend code to indicate a SMS_ATN event */ void ipmi_sms_attention(void); /* Add an ipmi message to the queue */ int ipmi_queue_msg(struct ipmi_msg *msg); /* Add an ipmi message to the start of the queue */ int ipmi_queue_msg_head(struct ipmi_msg *msg); /* Synchronously send an ipmi message. This won't return until the * messages callback has been called. */ void ipmi_queue_msg_sync(struct ipmi_msg *msg); /* Removes the message from the list, queued previously */ int ipmi_dequeue_msg(struct ipmi_msg *msg); /* Process a completed message */ void ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc, struct ipmi_msg *msg); /* 28.3 Chassis Control Command. Changes the power state of the P8. */ int ipmi_chassis_control(uint8_t request); /* 20.7 ACPI Power State Command (without the ACPI part). Informative only, * use chassis control to perform power off and reboot. */ int ipmi_set_power_state(uint8_t system, uint8_t device); /* 35.17 Set Sensor Reading Command */ int ipmi_set_sensor(uint8_t sensor, uint8_t *reading, size_t len); int ipmi_set_fw_progress_sensor(uint8_t state); /* Register a backend with the ipmi core. Currently we only support one. */ void ipmi_register_backend(struct ipmi_backend *backend); /* Allocate IPMI SEL panic message */ void ipmi_sel_init(void); /* Register rtc ipmi commands with as opal callbacks. */ void ipmi_rtc_init(void); /* Register ipmi host interface access callbacks */ void ipmi_opal_init(void); /* Populate fru data */ void ipmi_fru_init(uint8_t fru_dev_id); /* Commit an error log to the bmc using the OEM add eSEL commands */ struct errorlog; int ipmi_elog_commit(struct errorlog *elog_buf); /* Callback to parse an OEM SEL message */ void ipmi_parse_sel(struct ipmi_msg *msg); /* Starts the watchdog timer */ void ipmi_wdt_init(void); /* Stop the wdt */ void ipmi_wdt_stop(void); /* Reset the watchdog timer. Does not return until the timer has been * reset and does not schedule future resets. */ void ipmi_wdt_final_reset(void); /* Discover id of settable ipmi sensors */ void ipmi_sensor_init(void); /* Get sensor number for given sensor type */ uint8_t ipmi_get_sensor_number(uint8_t sensor_type); /* Set the boot count once the OS is up and running */ int ipmi_set_boot_count(void); /* Terminate immediate */ void __attribute__((noreturn)) ipmi_terminate(const char *msg); #endif skiboot-skiboot-5.1.13/include/lock.h000066400000000000000000000045541265204436200174630ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LOCK_H #define __LOCK_H #include struct lock { /* Lock value has bit 63 as lock bit and the PIR of the owner * in the top 32-bit */ unsigned long lock_val; /* * Set to true if lock is involved in the console flush path * in which case taking it will suspend console flushing */ bool in_con_path; }; /* Initializer */ #define LOCK_UNLOCKED { .lock_val = 0, .in_con_path = 0 } /* Note vs. libc and locking: * * The printf() family of * functions use stack based t buffers and call into skiboot * underlying read() and write() which use a console lock. * * The underlying FSP console code will thus operate within that * console lock. * * The libc does *NOT* lock stream buffer operations, so don't * try to scanf() from the same FILE from two different processors. * * FSP operations are locked using an FSP lock, so all processors * can safely call the FSP API * * Note about ordering: * * lock() is a full memory barrier. unlock() is a lwsync * */ extern bool bust_locks; static inline void init_lock(struct lock *l) { *l = (struct lock)LOCK_UNLOCKED; } extern bool __try_lock(struct lock *l); extern bool try_lock(struct lock *l); extern void lock(struct lock *l); extern void unlock(struct lock *l); extern bool lock_held_by_me(struct lock *l); /* The debug output can happen while the FSP lock, so we need some kind * of recursive lock support here. I don't want all locks to be recursive * though, thus the caller need to explicitly call lock_recursive which * returns false if the lock was already held by this cpu. If it returns * true, then the caller shall release it when done. */ extern bool lock_recursive(struct lock *l); /* Called after per-cpu data structures are available */ extern void init_locks(void); #endif /* __LOCK_H */ skiboot-skiboot-5.1.13/include/lpc.h000066400000000000000000000112631265204436200173040ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LPC_H #define __LPC_H #include #include /* Note about LPC interrupts * * LPC interrupts come in two categories: * * - External device LPC interrupts * - Error interrupts generated by the LPC controller * * The former is implemented differently depending on whether * you are using Murano/Venice or Naples. * * The former two chips don't have a pin to deserialize the LPC * SerIRQ protocol, so the only source of LPC device interrupts * is an external interrupt pin, which is usually connected to a * CPLD which deserializes SerIRQ. * * So in that case, we get external interrupts from the PSI which * are in effect the "OR" of all the active LPC interrupts. * * The error interrupt generated by the LPC controllers however * are internally routed normally to the PSI bridge and muxed with * the I2C interrupts. * * On Naples, there is a pin to deserialize SerIRQ, so the individual * LPC device interrupts (up to 19) are represented in the same status * and mask register as the LPC error interrupts. They are still all * then turned into a single XIVE interrupts in the PSI however, muxed * with the I2C. * * In order to more/less transparently handle this, we let individual * "drivers" register for specific LPC interrupts. On Naples, the handlers * will be called individually based on what has been demuxed by the * controller. On Venice/Murano, all the handlers will be called on * every external interrupt. The platform is responsible of calling * lpc_all_interrupts() from the platform external interrupt handler. */ /* Routines for accessing the LPC bus on Power8 */ extern void lpc_init(void); /* Check for a default bus */ extern bool lpc_present(void); /* Return of LPC is currently usable. This can be false if the caller * currently holds a lock that would make it unsafe, or the LPC bus * is known to be in some error condition (TBI). */ extern bool lpc_ok(void); /* Handle the interrupt from the LPC controller */ extern void lpc_interrupt(uint32_t chip_id); /* Call all external handlers */ extern void lpc_all_interrupts(uint32_t chip_id); /* Register/deregister handler */ struct lpc_client { /* Callback on LPC reset */ void (*reset)(uint32_t chip_id); /* Callback on LPC interrupt */ void (*interrupt)(uint32_t chip_id, uint32_t irq_msk); /* Bitmask of interrupts this client is interested in * Note: beware of ordering, use LPC_IRQ() macro */ uint32_t interrupts; #define LPC_IRQ(n) (0x80000000 >> (n)) }; extern void lpc_register_client(uint32_t chip_id, const struct lpc_client *clt); /* Default bus accessors */ extern int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz); extern int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz); /* Mark LPC bus as used by console */ extern void lpc_used_by_console(void); /* * Simplified big endian FW accessors */ static inline int64_t lpc_fw_read32(uint32_t *val, uint32_t addr) { return lpc_read(OPAL_LPC_FW, addr, val, 4); } static inline int64_t lpc_fw_write32(uint32_t val, uint32_t addr) { return lpc_write(OPAL_LPC_FW, addr, val, 4); } /* * Simplified Little Endian IO space accessors * * Note: We do *NOT* handle unaligned accesses */ static inline void lpc_outb(uint8_t data, uint32_t addr) { lpc_write(OPAL_LPC_IO, addr, data, 1); } static inline uint8_t lpc_inb(uint32_t addr) { uint32_t d32; int64_t rc = lpc_read(OPAL_LPC_IO, addr, &d32, 1); return (rc == OPAL_SUCCESS) ? d32 : 0xff; } static inline void lpc_outw(uint16_t data, uint32_t addr) { lpc_write(OPAL_LPC_IO, addr, cpu_to_le16(data), 2); } static inline uint16_t lpc_inw(uint32_t addr) { uint32_t d32; int64_t rc = lpc_read(OPAL_LPC_IO, addr, &d32, 2); return (rc == OPAL_SUCCESS) ? le16_to_cpu(d32) : 0xffff; } static inline void lpc_outl(uint32_t data, uint32_t addr) { lpc_write(OPAL_LPC_IO, addr, cpu_to_le32(data), 4); } static inline uint32_t lpc_inl(uint32_t addr) { uint32_t d32; int64_t rc = lpc_read(OPAL_LPC_IO, addr, &d32, 4); return (rc == OPAL_SUCCESS) ? le32_to_cpu(d32) : 0xffffffff; } #endif /* __LPC_H */ skiboot-skiboot-5.1.13/include/mem-map.h000066400000000000000000000101041265204436200200500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __MEM_MAP_H #define __MEM_MAP_H /* This is our main offset for relocation. All our buffers * are offset from that and our code relocates itself to * that location */ #define SKIBOOT_BASE 0x30000000 /* Stack size set to 16K, some of it will be used for * machine check (see stack.h) */ #define STACK_SHIFT 14 #define STACK_SIZE (1 << STACK_SHIFT) /* The NACA and other stuff in head.S need to be at the start: we * give it 64k before placing the SPIRA and related data. */ #define SPIRA_OFF 0x00010000 /* SPIRA is 2k, then follow with for proc_init_data (aka PROCIN). * These need to be at fixed addresses in case we're ever little * endian: linker can't endian reverse a pointer for us. Text, data * et. al. follows this. */ #define PROCIN_OFF (SPIRA_OFF + 0x800) /* Initial MDST table like PROCIN, we need fixed addresses, * we leave a 2k gap for PROCIN */ #define MDST_TABLE_OFF (SPIRA_OFF + 0x1000) /* Like MDST, we need fixed address for CPU control header. * We leave a 2k gap for MDST. CPU_CTL table is of size ~4k */ #define CPU_CTL_OFF (SPIRA_OFF + 0x1800) /* We keep a gap of 2M for skiboot text & bss for now. We will * then we have our heap which goes up to base + 14M (so 12M for * now, though we can certainly reduce that a lot). * * Ideally, we should fix the heap end and use _end to basically * initialize our heap so that it covers anything from _end to * that heap end, avoiding wasted space. * * That's made a bit tricky however due to how we create those * regions statically in mem_region.c, but still on the list of * things to improve. * * As of this writing (2014/4/6), we use approc 512K for skiboot * core and 2M of heap on a 1 socket machine. * * As of 2015/5/7 we use approx 800k for skiboot, 500k HEAP for * mambo boot. */ #define HEAP_BASE (SKIBOOT_BASE + 0x00300000) #define HEAP_SIZE 0x00d00000 /* This is the location of our console buffer at base + 16M */ #define INMEM_CON_START (SKIBOOT_BASE + 0x01000000) #define INMEM_CON_LEN 0x100000 /* This is the location of HBRT console buffer at base + 17M */ #define HBRT_CON_START (SKIBOOT_BASE + 0x01100000) #define HBRT_CON_LEN 0x100000 /* Tell FSP to put the init data at base + 20M, allocate 8M */ #define SPIRA_HEAP_BASE (SKIBOOT_BASE + 0x01200000) #define SPIRA_HEAP_SIZE 0x00800000 /* This is our PSI TCE table. It's 16K entries on P7 and 256K * entries on P8 */ #define PSI_TCE_TABLE_BASE (SKIBOOT_BASE + 0x01a00000) #define PSI_TCE_TABLE_SIZE_P7 0x00020000UL #define PSI_TCE_TABLE_SIZE_P8 0x00200000UL /* Total size of the above area * * (Ensure this has at least a 64k alignment) */ #define SKIBOOT_SIZE 0x01c00000 /* We start laying out the CPU stacks from here, indexed by PIR * each stack is STACK_SIZE in size (naturally aligned power of * two) and the bottom of the stack contains the cpu thread * structure for the processor, so it can be obtained by a simple * bit mask from the stack pointer. * * The size of this array is dynamically determined at boot time */ #define CPU_STACKS_BASE (SKIBOOT_BASE + SKIBOOT_SIZE) /* * Address at which we load the kernel LID. This is also where * we expect a passed-in kernel if booting without FSP and * without a built-in kernel. */ #define KERNEL_LOAD_BASE ((void *)0x20000000) #define KERNEL_LOAD_SIZE 0x08000000 #define INITRAMFS_LOAD_BASE KERNEL_LOAD_BASE + KERNEL_LOAD_SIZE #define INITRAMFS_LOAD_SIZE 0x08000000 /* Size allocated to build the device-tree */ #define DEVICE_TREE_MAX_SIZE 0x80000 #endif /* __MEM_MAP_H */ skiboot-skiboot-5.1.13/include/mem_region-malloc.h000066400000000000000000000032361265204436200221150ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __MEM_REGION_MALLOC_H #define __MEM_REGION_MALLOC_H #include #define __loc2(line) #line #define __loc(line) __loc2(line) #define __location__ __FILE__ ":" __loc(__LINE__) void *__malloc(size_t size, const char *location) __warn_unused_result; void *__zalloc(size_t size, const char *location) __warn_unused_result; void *__realloc(void *ptr, size_t size, const char *location) __warn_unused_result; void __free(void *ptr, const char *location); void *__memalign(size_t boundary, size_t size, const char *location) __warn_unused_result; #define malloc(size) __malloc(size, __location__) #define zalloc(size) __zalloc(size, __location__) #define realloc(ptr, size) __realloc(ptr, size, __location__) #define free(ptr) __free(ptr, __location__) #define memalign(boundary, size) __memalign(boundary, size, __location__) void *__local_alloc(unsigned int chip, size_t size, size_t align, const char *location) __warn_unused_result; #define local_alloc(chip_id, size, align) \ __local_alloc((chip_id), (size), (align), __location__) #endif /* __MEM_REGION_MALLOC_H */ skiboot-skiboot-5.1.13/include/mem_region.h000066400000000000000000000045121265204436200206460ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __MEMORY_REGION #define __MEMORY_REGION #include #include #include enum mem_region_type { /* ranges allocatable by mem_alloc: this will be most of memory */ REGION_SKIBOOT_HEAP, /* ranges allocatable by mem_alloc but shrunk (e.g. whole memory) */ REGION_MEMORY, /* ranges used explicitly for skiboot, but not allocatable. eg .text */ REGION_SKIBOOT_FIRMWARE, /* ranges reserved before skiboot init, eg HBRT memory */ REGION_HW_RESERVED, /* ranges reserved, eg HW framebuffer */ REGION_RESERVED, /* ranges available for the OS, created by mem_region_release_unused */ REGION_OS, }; /* An area of physical memory. */ struct mem_region { struct list_node list; const char *name; uint64_t start, len; struct dt_node *node; enum mem_region_type type; struct list_head free_list; struct lock free_list_lock; }; extern struct lock mem_region_lock; extern unsigned long top_of_ram; void *mem_alloc(struct mem_region *region, size_t size, size_t align, const char *location); void mem_free(struct mem_region *region, void *mem, const char *location); bool mem_resize(struct mem_region *region, void *mem, size_t len, const char *location); size_t mem_allocated_size(const void *ptr); bool mem_check(const struct mem_region *region); void mem_region_release_unused(void); /* Specifically for working on the heap. */ extern struct mem_region skiboot_heap; void mem_region_init(void); void adjust_cpu_stacks_alloc(void); void mem_region_add_dt_reserved(void); /* Mark memory as reserved */ void mem_reserve_hw(const char *name, uint64_t start, uint64_t len); struct mem_region *find_mem_region(const char *name); bool mem_range_is_reserved(uint64_t start, uint64_t size); #endif /* __MEMORY_REGION */ skiboot-skiboot-5.1.13/include/nvram-format.h000066400000000000000000000014311265204436200211330ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NVRAM_FORMAT_H #define __NVRAM_FORMAT_H int nvram_format(void *nvram_image, uint32_t nvram_size); int nvram_check(void *nvram_image, uint32_t nvram_size); #endif /* __NVRAM_FORMAT_H */ skiboot-skiboot-5.1.13/include/nx.h000066400000000000000000000277271265204436200171670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NX_H #define __NX_H /*************************************/ /* Register addresses and bit fields */ /*************************************/ #define NX_P7_SAT(sat, offset) XSCOM_SAT(0x1, sat, offset) #define NX_P8_SAT(sat, offset) XSCOM_SAT(0xc, sat, offset) /* Random Number Generator */ #define NX_P7_RNG_BAR NX_P7_SAT(0x2, 0x0c) #define NX_P8_RNG_BAR NX_P8_SAT(0x2, 0x0d) #define NX_P7_RNG_BAR_ADDR PPC_BITMASK(18, 51) #define NX_P8_RNG_BAR_ADDR PPC_BITMASK(14, 51) #define NX_RNG_BAR_SIZE PPC_BITMASK(53, 55) #define NX_RNG_BAR_ENABLE PPC_BIT(52) #define NX_P7_RNG_CFG NX_P7_SAT(0x2, 0x12) #define NX_P8_RNG_CFG NX_P8_SAT(0x2, 0x12) #define NX_RNG_CFG_ENABLE PPC_BIT(63) /* Symmetric Crypto */ #define NX_P7_SYM_CFG NX_P7_SAT(0x2, 0x09) #define NX_P8_SYM_CFG NX_P8_SAT(0x2, 0x0a) #define NX_SYM_CFG_CI PPC_BITMASK(2, 14) #define NX_SYM_CFG_CT PPC_BITMASK(18, 23) #define NX_SYM_CFG_FC_ENABLE PPC_BITMASK(32, 39) #define NX_SYM_CFG_ENABLE PPC_BIT(63) /* Asymmetric Crypto */ #define NX_P7_ASYM_CFG NX_P7_SAT(0x2, 0x0a) #define NX_P8_ASYM_CFG NX_P8_SAT(0x2, 0x0b) #define NX_ASYM_CFG_CI PPC_BITMASK(2, 14) #define NX_ASYM_CFG_CT PPC_BITMASK(18, 23) #define NX_ASYM_CFG_FC_ENABLE PPC_BITMASK(32, 52) #define NX_ASYM_CFG_ENABLE PPC_BIT(63) /* 842 Compression */ #define NX_P7_842_CFG NX_P7_SAT(0x2, 0x0b) #define NX_P8_842_CFG NX_P8_SAT(0x2, 0x0c) #define NX_842_CFG_CI PPC_BITMASK(2, 14) #define NX_842_CFG_CT PPC_BITMASK(18, 23) #define NX_842_CFG_FC_ENABLE PPC_BITMASK(32, 36) #define NX_842_CFG_ENABLE PPC_BIT(63) /* DMA */ #define NX_P7_DMA_CFG NX_P7_SAT(0x1, 0x02) #define NX_P8_DMA_CFG NX_P8_SAT(0x1, 0x02) #define NX_P8_DMA_CFG_842_COMPRESS_PREFETCH PPC_BIT(23) #define NX_P8_DMA_CFG_842_DECOMPRESS_PREFETCH PPC_BIT(24) #define NX_DMA_CFG_AES_SHA_MAX_RR PPC_BITMASK(25, 28) #define NX_DMA_CFG_AMF_MAX_RR PPC_BITMASK(29, 32) #define NX_DMA_CFG_842_COMPRESS_MAX_RR PPC_BITMASK(33, 36) #define NX_DMA_CFG_842_DECOMPRESS_MAX_RR PPC_BITMASK(37, 40) #define NX_DMA_CFG_AES_SHA_CSB_WR PPC_BITMASK(41, 42) #define NX_DMA_CFG_AES_SHA_COMPLETION_MODE PPC_BITMASK(43, 44) #define NX_DMA_CFG_AES_SHA_CPB_WR PPC_BITMASK(45, 46) #define NX_DMA_CFG_AES_SHA_OUTPUT_DATA_WR PPC_BIT(47) #define NX_DMA_CFG_AMF_CSB_WR PPC_BITMASK(49, 50) #define NX_DMA_CFG_AMF_COMPLETION_MODE PPC_BITMASK(51, 52) #define NX_DMA_CFG_AMF_CPB_WR PPC_BITMASK(53, 54) #define NX_DMA_CFG_AMF_OUTPUT_DATA_WR PPC_BIT(55) #define NX_DMA_CFG_842_SPBC PPC_BIT(56) #define NX_DMA_CFG_842_CSB_WR PPC_BITMASK(57, 58) #define NX_DMA_CFG_842_COMPLETION_MODE PPC_BITMASK(59, 60) #define NX_DMA_CFG_842_CPB_WR PPC_BITMASK(61, 62) #define NX_DMA_CFG_842_OUTPUT_DATA_WR PPC_BIT(63) /* Engine Enable Register */ #define NX_P7_EE_CFG NX_P7_SAT(0x1, 0x01) #define NX_P8_EE_CFG NX_P8_SAT(0x1, 0x01) #define NX_EE_CFG_EFUSE PPC_BIT(0) #define NX_EE_CFG_CH7 PPC_BIT(53) /* AMF */ #define NX_EE_CFG_CH6 PPC_BIT(54) /* AMF */ #define NX_EE_CFG_CH5 PPC_BIT(55) /* AMF */ #define NX_EE_CFG_CH4 PPC_BIT(56) /* P7: SYM, P8: AMF */ #define NX_EE_CFG_CH3 PPC_BIT(57) /* SYM */ #define NX_EE_CFG_CH2 PPC_BIT(58) /* SYM */ #define NX_EE_CFG_CH1 PPC_BIT(62) /* 842 */ #define NX_EE_CFG_CH0 PPC_BIT(63) /* 842 */ /* PowerBus Registers */ #define NX_P7_CRB_IQ NX_P7_SAT(0x2, 0x0e) #define NX_P8_CRB_IQ NX_P8_SAT(0x2, 0x0f) #define NX_CRB_IQ_SYM PPC_BITMASK(0, 2) #define NX_CRB_IQ_ASYM PPC_BITMASK(3, 5) /* NX Status Register */ #define NX_P7_STATUS NX_P7_SAT(0x1, 0x00) #define NX_P8_STATUS NX_P8_SAT(0x1, 0x00) #define NX_STATUS_HMI_ACTIVE PPC_BIT(54) #define NX_STATUS_PBI_IDLE PPC_BIT(55) #define NX_STATUS_DMA_CH0_IDLE PPC_BIT(56) #define NX_STATUS_DMA_CH1_IDLE PPC_BIT(57) #define NX_STATUS_DMA_CH2_IDLE PPC_BIT(58) #define NX_STATUS_DMA_CH3_IDLE PPC_BIT(59) #define NX_STATUS_DMA_CH4_IDLE PPC_BIT(60) #define NX_STATUS_DMA_CH5_IDLE PPC_BIT(61) #define NX_STATUS_DMA_CH6_IDLE PPC_BIT(62) #define NX_STATUS_DMA_CH7_IDLE PPC_BIT(63) /* Channel Status Registers */ #define NX_P7_CH_CRB(ch) NX_P7_SAT(0x1, 0x03 + ((ch) * 2)) #define NX_P8_CH_CRB(ch) NX_P8_SAT(0x1, 0x03 + ((ch) * 2)) #define NX_P7_CH_STATUS(ch) NX_P7_SAT(0x1, 0x04 + ((ch) * 2)) #define NX_P8_CH_STATUS(ch) NX_P8_SAT(0x1, 0x04 + ((ch) * 2)) #define NX_CH_STATUS_ABORT PPC_BIT(0) #define NX_CH_STATUS_CCB_VALID PPC_BIT(4) #define NX_CH_STATUS_CCB_CM PPC_BITMASK(5, 7) #define NX_CH_STATUS_CCB_PRIO PPC_BITMASK(8, 15) #define NX_CH_STATUS_CCB_SN PPC_BITMASK(16, 31) #define NX_CH_STATUS_VALID PPC_BIT(32) #define NX_CH_STATUS_LPID PPC_BITMASK(38, 47) #define NX_CH_STATUS_CCB_ISN PPC_BITMASK(50, 63) #define NX_CH_STATUS_CRB_SJT PPC_BITMASK(50, 63) /* Kill Register */ #define NX_P7_CRB_KILL NX_P7_SAT(0x1, 0x13) #define NX_P8_CRB_KILL NX_P8_SAT(0x1, 0x13) #define NX_CRB_KILL_LPID_KILL PPC_BIT(0) #define NX_CRB_KILL_LPID PPC_BITMASK(6, 15) #define NX_CRB_KILL_ISN_KILL PPC_BIT(16) #define NX_CRB_KILL_SJT_KILL PPC_BIT(17) #define NX_CRB_KILL_ISN PPC_BITMASK(18, 31) #define NX_CRB_KILL_SJT PPC_BITMASK(18, 31) #define NX_CRB_KILL_DONE PPC_BIT(32) #define NX_CRB_KILL_PBI_LOC PPC_BITMASK(40, 47) #define NX_CRB_KILL_PREFETCH_CH PPC_BITMASK(48, 55) #define NX_CRB_KILL_ALG_CH PPC_BITMASK(56, 63) /* Fault Isolation Registers (FIR) */ #define NX_P7_DE_FIR_DATA NX_P7_SAT(0x4, 0x00) #define NX_P8_DE_FIR_DATA NX_P8_SAT(0x4, 0x00) #define NX_P7_DE_FIR_DATA_CLR NX_P7_SAT(0x4, 0x01) #define NX_P8_DE_FIR_DATA_CLR NX_P8_SAT(0x4, 0x01) #define NX_P7_DE_FIR_DATA_SET NX_P7_SAT(0x4, 0x02) #define NX_P8_DE_FIR_DATA_SET NX_P8_SAT(0x4, 0x02) #define NX_P7_DE_FIR_MASK NX_P7_SAT(0x4, 0x06) #define NX_P8_DE_FIR_MASK NX_P8_SAT(0x4, 0x03) #define NX_P7_DE_FIR_MASK_CLR NX_P7_SAT(0x4, 0x07) #define NX_P8_DE_FIR_MASK_CLR NX_P8_SAT(0x4, 0x04) #define NX_P7_DE_FIR_MASK_SET NX_P7_SAT(0x4, 0x08) #define NX_P8_DE_FIR_MASK_SET NX_P8_SAT(0x4, 0x05) #define NX_P7_DE_FIR_ACTION0 NX_P7_SAT(0x4, 0x03) #define NX_P8_DE_FIR_ACTION0 NX_P8_SAT(0x4, 0x06) #define NX_P7_DE_FIR_ACTION1 NX_P7_SAT(0x4, 0x04) #define NX_P8_DE_FIR_ACTION1 NX_P8_SAT(0x4, 0x07) #define NX_P7_DE_FIR_WOF NX_P7_SAT(0x4, 0x05) #define NX_P8_DE_FIR_WOF NX_P8_SAT(0x4, 0x08) #define NX_P7_PB_FIR_DATA NX_P7_SAT(0x2, 0x00) #define NX_P8_PB_FIR_DATA NX_P8_SAT(0x2, 0x00) #define NX_P7_PB_FIR_DATA_CLR NX_P7_SAT(0x2, 0x01) #define NX_P8_PB_FIR_DATA_CLR NX_P8_SAT(0x2, 0x01) #define NX_P7_PB_FIR_DATA_SET NX_P7_SAT(0x2, 0x02) #define NX_P8_PB_FIR_DATA_SET NX_P8_SAT(0x2, 0x02) #define NX_P7_PB_FIR_MASK NX_P7_SAT(0x2, 0x06) #define NX_P8_PB_FIR_MASK NX_P8_SAT(0x2, 0x03) #define NX_P7_PB_FIR_MASK_CLR NX_P7_SAT(0x2, 0x07) #define NX_P8_PB_FIR_MASK_CLR NX_P8_SAT(0x2, 0x04) #define NX_P7_PB_FIR_MASK_SET NX_P7_SAT(0x2, 0x08) #define NX_P8_PB_FIR_MASK_SET NX_P8_SAT(0x2, 0x05) #define NX_P7_PB_FIR_ACTION0 NX_P7_SAT(0x2, 0x03) #define NX_P8_PB_FIR_ACTION0 NX_P8_SAT(0x2, 0x06) #define NX_P7_PB_FIR_ACTION1 NX_P7_SAT(0x2, 0x04) #define NX_P8_PB_FIR_ACTION1 NX_P8_SAT(0x2, 0x07) #define NX_P7_PB_FIR_WOF NX_P7_SAT(0x2, 0x05) #define NX_P8_PB_FIR_WOF NX_P8_SAT(0x2, 0x08) #define NX_FIR_MCD_PB_CMD_HANG PPC_BIT(0) /* P7 only */ #define NX_FIR_SHM_INV PPC_BIT(1) #define NX_FIR_MCD_ARRAY_ECC_CE PPC_BIT(2) /* P7 only */ #define NX_FIR_MCD_ARRAY_ECC_UE PPC_BIT(3) /* P7 only */ #define NX_FIR_CH0_ECC_CE PPC_BIT(4) #define NX_FIR_CH0_ECC_UE PPC_BIT(5) #define NX_FIR_CH1_ECC_CE PPC_BIT(6) #define NX_FIR_CH1_ECC_UE PPC_BIT(7) #define NX_FIR_DMA_NZ_CSB_CC PPC_BIT(8) /* lab use only */ #define NX_FIR_DMA_ARRAY_ECC_CE PPC_BIT(9) #define NX_FIR_DMA_RW_ECC_CE PPC_BIT(10) #define NX_FIR_CH5_ECC_CE PPC_BIT(11) #define NX_FIR_CH6_ECC_CE PPC_BIT(12) #define NX_FIR_CH7_ECC_CE PPC_BIT(13) #define NX_FIR_OTHER_SCOM_ERR PPC_BIT(14) #define NX_FIR_DMA_INV_STATE PPC_BITMASK(15, 16) #define NX_FIR_DMA_ARRAY_ECC_UE PPC_BIT(17) #define NX_FIR_DMA_RW_ECC_UE PPC_BIT(18) #define NX_FIR_HYP PPC_BIT(19) /* for HYP to force HMI */ #define NX_FIR_CH0_INV_STATE PPC_BIT(20) #define NX_FIR_CH1_INV_STATE PPC_BIT(21) #define NX_FIR_CH2_INV_STATE PPC_BIT(22) #define NX_FIR_CH3_INV_STATE PPC_BIT(23) #define NX_FIR_CH4_INV_STATE PPC_BIT(24) #define NX_FIR_CH5_INV_STATE PPC_BIT(25) #define NX_FIR_CH6_INV_STATE PPC_BIT(26) #define NX_FIR_CH7_INV_STATE PPC_BIT(27) #define NX_FIR_CH5_ECC_UE PPC_BIT(28) #define NX_FIR_CH6_ECC_UE PPC_BIT(29) #define NX_FIR_CH7_ECC_UE PPC_BIT(30) #define NX_FIR_CRB_UE PPC_BIT(31) #define NX_FIR_CRB_SUE PPC_BIT(32) #define NX_FIR_DMA_RW_ECC_SUE PPC_BIT(33) #define NX_FIR_MCD_CFG_REG_PARITY PPC_BIT(34) /* P7 only */ #define NX_FIR_MCD_RECOVERY_INV_STATE PPC_BIT(35) /* P7 only */ #define NX_FIR_P7_PARITY PPC_BIT(36) /* P7 only */ #define NX_FIR_CH4_ECC_CE PPC_BIT(36) /* P8 only */ #define NX_FIR_CH5_ECC_UE_2 PPC_BIT(37) /* P8 only */ #define NX_FIR_P8_PARITY PPC_BITMASK(48, 49) /**************************************/ /* Register field values/restrictions */ /**************************************/ /* Arbitrary Coprocessor Type values */ #define NX_CT_SYM (1) #define NX_CT_ASYM (2) #define NX_CT_842 (3) /* Coprocessor Instance counter * NX workbook, section 5.5.1 * "Assigning Values" */ #define NX_SYM_CFG_CI_MAX (511) #define NX_SYM_CFG_CI_LSHIFT (2) #define NX_ASYM_CFG_CI_MAX (127) #define NX_ASYM_CFG_CI_LSHIFT (4) #define NX_842_CFG_CI_MAX (511) #define NX_842_CFG_CI_LSHIFT (2) /* DMA configuration values * NX workbook, section 5.2.3, table 5-4 * "DMA Configuration Register Bits" * * These values can be used for the AES/SHA, AMF, and 842 DMA * configuration fields in the DMA configuration register. * * Abbreviations used below: * pDMA - "partial DMA write" * fDMA - "full DMA write" * CI - Cache Inject */ /* NX_DMA_CSB_WR values: * 0 = Always perform 8 or 16 byte pDMA * 1 = Do 128 byte CI if CSB at end of cache line, else pDMA * 2 = Do 128 byte fDMA if CSB at end of cache line, else pDMA */ #define NX_DMA_CSB_WR_PDMA (0) #define NX_DMA_CSB_WR_CI (1) #define NX_DMA_CSB_WR_FDMA (2) /* NX_DMA_COMPLETION_MODE values: * 0 = Always perform 8 byte pDMA * 1 = Do 128 byte CI, replicating 8 bytes across entire 128 byte cache line * 2 = Do 128 byte fDMA, replicating 8 bytes across entire 128 byte cache line */ #define NX_DMA_COMPLETION_MODE_PDMA (0) #define NX_DMA_COMPLETION_MODE_CI (1) #define NX_DMA_COMPLETION_MODE_FDMA (2) /* NX_DMA_CPB_WR values: * 0 = Always do pDMA or fDMA, based on number of bytes and alignment * 1 = Always do pDMA on non-aligned cache lines, fDMA on aligned cache lines * (may store dummy data at the end of the aligned data) * 2 = Do 128 byte CI when writing 128 aligned bytes, else pDMA * 3 = Do 128 byte CI when writing aligned cache lines, else pDMA * (may store dummy data at the end of the aligned data) */ #define NX_DMA_CPB_WR_DMA_NOPAD (0) #define NX_DMA_CPB_WR_DMA_PAD (1) #define NX_DMA_CPB_WR_CI_NOPAD (2) #define NX_DMA_CPB_WR_CI_PAD (3) /* NX_DMA_OUTPUT_DATA_WR values: * 0 = Always do pDMA or fDMA, based on number of bytes and alignment * 1 = Do 128 byte CI when writing 128 aligned bytes, else pDMA */ #define NX_DMA_OUTPUT_DATA_WR_DMA (0) #define NX_DMA_OUTPUT_DATA_WR_CI (1) /******************************/ /* NX node creation functions */ /******************************/ extern void nx_create_rng_node(struct dt_node *); extern void nx_create_crypto_node(struct dt_node *); extern void nx_create_842_node(struct dt_node *); extern void nx_init(void); #endif /* __NX_H */ skiboot-skiboot-5.1.13/include/op-panel.h000066400000000000000000000041651265204436200202440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OP_PANEL_H #define __OP_PANEL_H #include /* Severity */ enum op_severity { OP_LOG = 0x4342, /* 'CB' - Progress info */ OP_WARN = 0x4542, /* 'EB' - Information condition */ OP_ERROR = 0x4442, /* 'DB' - Non fatal error */ OP_FATAL = 0x4242, /* 'BB' - Fatal error */ }; /* Module */ enum op_module { OP_MOD_CORE = 0x3030, /* '00' - Anything really */ OP_MOD_INIT = 0x3031, /* '01' - init */ OP_MOD_LOCK = 0x3032, /* '02' - spinlocks */ OP_MOD_FSP = 0x3033, /* '03' - FSP */ OP_MOD_FSPCON = 0x3034, /* '04' - FSPCON */ OP_MOD_CHIPTOD = 0x3035, /* '05' - ChipTOP */ OP_MOD_CPU = 0x3036, /* '06' - CPU bringup */ OP_MOD_MEM = 0x3037, /* '07' - Memory */ OP_MOD_XSCOM = 0x3038, /* '08' - XSCOM */ }; /* Common codes: * * 'BA010001' : Failed to load a kernel * 'BA010002' : Failed to create a device-tree * 'BA020000' : Locking already owned lock * 'BA020001' : Unlocking unlocked lock * 'BA020002' : Unlocking not-owned lock * 'BA006666' : Abort * 'BA050000' : Failed ChipTOD init/sync * 'BA050001' : Failed to find a CPU on the master chip * 'BA050002' : Master chip sync failed * 'EA05xxx2' : Slave sync failed (xxx = PIR) * 'BA070000' : Cannot find MS VPD or invalid * 'BA070001' : MS VPD wrong size * 'BA070002' : MS VPD doesn't have an MSAC * 'BA070003' : MS VPD doesn't have a total config */ extern void op_display(enum op_severity, enum op_module, uint16_t code); extern void op_panel_disable_src_echo(void); extern void op_panel_clear_src(void); extern void fsp_oppanel_init(void); #endif /* __OP_PANEL_H */ skiboot-skiboot-5.1.13/include/opal-api.h000066400000000000000000000616661265204436200202440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OPAL_API_H #define __OPAL_API_H /****** OPAL APIs ******/ /* Return codes */ #define OPAL_SUCCESS 0 #define OPAL_PARAMETER -1 #define OPAL_BUSY -2 #define OPAL_PARTIAL -3 #define OPAL_CONSTRAINED -4 #define OPAL_CLOSED -5 #define OPAL_HARDWARE -6 #define OPAL_UNSUPPORTED -7 #define OPAL_PERMISSION -8 #define OPAL_NO_MEM -9 #define OPAL_RESOURCE -10 #define OPAL_INTERNAL_ERROR -11 #define OPAL_BUSY_EVENT -12 #define OPAL_HARDWARE_FROZEN -13 #define OPAL_WRONG_STATE -14 #define OPAL_ASYNC_COMPLETION -15 #define OPAL_EMPTY -16 #define OPAL_I2C_TIMEOUT -17 #define OPAL_I2C_INVALID_CMD -18 #define OPAL_I2C_LBUS_PARITY -19 #define OPAL_I2C_BKEND_OVERRUN -20 #define OPAL_I2C_BKEND_ACCESS -21 #define OPAL_I2C_ARBT_LOST -22 #define OPAL_I2C_NACK_RCVD -23 #define OPAL_I2C_STOP_ERR -24 /* API Tokens (in r0) */ #define OPAL_INVALID_CALL -1 #define OPAL_TEST 0 #define OPAL_CONSOLE_WRITE 1 #define OPAL_CONSOLE_READ 2 #define OPAL_RTC_READ 3 #define OPAL_RTC_WRITE 4 #define OPAL_CEC_POWER_DOWN 5 #define OPAL_CEC_REBOOT 6 #define OPAL_READ_NVRAM 7 #define OPAL_WRITE_NVRAM 8 #define OPAL_HANDLE_INTERRUPT 9 #define OPAL_POLL_EVENTS 10 #define OPAL_PCI_SET_HUB_TCE_MEMORY 11 #define OPAL_PCI_SET_PHB_TCE_MEMORY 12 #define OPAL_PCI_CONFIG_READ_BYTE 13 #define OPAL_PCI_CONFIG_READ_HALF_WORD 14 #define OPAL_PCI_CONFIG_READ_WORD 15 #define OPAL_PCI_CONFIG_WRITE_BYTE 16 #define OPAL_PCI_CONFIG_WRITE_HALF_WORD 17 #define OPAL_PCI_CONFIG_WRITE_WORD 18 #define OPAL_SET_XIVE 19 #define OPAL_GET_XIVE 20 #define OPAL_GET_COMPLETION_TOKEN_STATUS 21 /* obsolete */ #define OPAL_REGISTER_OPAL_EXCEPTION_HANDLER 22 #define OPAL_PCI_EEH_FREEZE_STATUS 23 #define OPAL_PCI_SHPC 24 #define OPAL_CONSOLE_WRITE_BUFFER_SPACE 25 #define OPAL_PCI_EEH_FREEZE_CLEAR 26 #define OPAL_PCI_PHB_MMIO_ENABLE 27 #define OPAL_PCI_SET_PHB_MEM_WINDOW 28 #define OPAL_PCI_MAP_PE_MMIO_WINDOW 29 #define OPAL_PCI_SET_PHB_TABLE_MEMORY 30 #define OPAL_PCI_SET_PE 31 #define OPAL_PCI_SET_PELTV 32 #define OPAL_PCI_SET_MVE 33 #define OPAL_PCI_SET_MVE_ENABLE 34 #define OPAL_PCI_GET_XIVE_REISSUE 35 #define OPAL_PCI_SET_XIVE_REISSUE 36 #define OPAL_PCI_SET_XIVE_PE 37 #define OPAL_GET_XIVE_SOURCE 38 #define OPAL_GET_MSI_32 39 #define OPAL_GET_MSI_64 40 #define OPAL_START_CPU 41 #define OPAL_QUERY_CPU_STATUS 42 #define OPAL_WRITE_OPPANEL 43 /* unimplemented */ #define OPAL_PCI_MAP_PE_DMA_WINDOW 44 #define OPAL_PCI_MAP_PE_DMA_WINDOW_REAL 45 #define OPAL_PCI_RESET 49 #define OPAL_PCI_GET_HUB_DIAG_DATA 50 #define OPAL_PCI_GET_PHB_DIAG_DATA 51 #define OPAL_PCI_FENCE_PHB 52 #define OPAL_PCI_REINIT 53 #define OPAL_PCI_MASK_PE_ERROR 54 #define OPAL_SET_SLOT_LED_STATUS 55 #define OPAL_GET_EPOW_STATUS 56 #define OPAL_SET_SYSTEM_ATTENTION_LED 57 #define OPAL_RESERVED1 58 #define OPAL_RESERVED2 59 #define OPAL_PCI_NEXT_ERROR 60 #define OPAL_PCI_EEH_FREEZE_STATUS2 61 #define OPAL_PCI_POLL 62 #define OPAL_PCI_MSI_EOI 63 #define OPAL_PCI_GET_PHB_DIAG_DATA2 64 #define OPAL_XSCOM_READ 65 #define OPAL_XSCOM_WRITE 66 #define OPAL_LPC_READ 67 #define OPAL_LPC_WRITE 68 #define OPAL_RETURN_CPU 69 #define OPAL_REINIT_CPUS 70 #define OPAL_ELOG_READ 71 #define OPAL_ELOG_WRITE 72 #define OPAL_ELOG_ACK 73 #define OPAL_ELOG_RESEND 74 #define OPAL_ELOG_SIZE 75 #define OPAL_FLASH_VALIDATE 76 #define OPAL_FLASH_MANAGE 77 #define OPAL_FLASH_UPDATE 78 #define OPAL_RESYNC_TIMEBASE 79 #define OPAL_CHECK_TOKEN 80 #define OPAL_DUMP_INIT 81 #define OPAL_DUMP_INFO 82 #define OPAL_DUMP_READ 83 #define OPAL_DUMP_ACK 84 #define OPAL_GET_MSG 85 #define OPAL_CHECK_ASYNC_COMPLETION 86 #define OPAL_SYNC_HOST_REBOOT 87 #define OPAL_SENSOR_READ 88 #define OPAL_GET_PARAM 89 #define OPAL_SET_PARAM 90 #define OPAL_DUMP_RESEND 91 #define OPAL_ELOG_SEND 92 /* Deprecated */ #define OPAL_PCI_SET_PHB_CAPI_MODE 93 #define OPAL_DUMP_INFO2 94 #define OPAL_WRITE_OPPANEL_ASYNC 95 #define OPAL_PCI_ERR_INJECT 96 #define OPAL_PCI_EEH_FREEZE_SET 97 #define OPAL_HANDLE_HMI 98 #define OPAL_CONFIG_CPU_IDLE_STATE 99 #define OPAL_SLW_SET_REG 100 #define OPAL_REGISTER_DUMP_REGION 101 #define OPAL_UNREGISTER_DUMP_REGION 102 #define OPAL_WRITE_TPO 103 #define OPAL_READ_TPO 104 #define OPAL_GET_DPO_STATUS 105 #define OPAL_OLD_I2C_REQUEST 106 /* Deprecated */ #define OPAL_IPMI_SEND 107 #define OPAL_IPMI_RECV 108 #define OPAL_I2C_REQUEST 109 #define OPAL_FLASH_READ 110 #define OPAL_FLASH_WRITE 111 #define OPAL_FLASH_ERASE 112 #define OPAL_PRD_MSG 113 #define OPAL_LEDS_GET_INDICATOR 114 #define OPAL_LEDS_SET_INDICATOR 115 #define OPAL_CEC_REBOOT2 116 #define OPAL_CONSOLE_FLUSH 117 #define OPAL_LAST 117 /* Device tree flags */ /* Flags set in power-mgmt nodes in device tree if * respective idle states are supported in the platform. */ #define OPAL_PM_NAP_ENABLED 0x00010000 #define OPAL_PM_SLEEP_ENABLED 0x00020000 #define OPAL_PM_WINKLE_ENABLED 0x00040000 #define OPAL_PM_SLEEP_ENABLED_ER1 0x00080000 /* with workaround */ #ifndef __ASSEMBLY__ /* Other enums */ enum OpalVendorApiTokens { OPAL_START_VENDOR_API_RANGE = 1000, OPAL_END_VENDOR_API_RANGE = 1999 }; enum OpalFreezeState { OPAL_EEH_STOPPED_NOT_FROZEN = 0, OPAL_EEH_STOPPED_MMIO_FREEZE = 1, OPAL_EEH_STOPPED_DMA_FREEZE = 2, OPAL_EEH_STOPPED_MMIO_DMA_FREEZE = 3, OPAL_EEH_STOPPED_RESET = 4, OPAL_EEH_STOPPED_TEMP_UNAVAIL = 5, OPAL_EEH_STOPPED_PERM_UNAVAIL = 6 }; enum OpalEehFreezeActionToken { OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO = 1, OPAL_EEH_ACTION_CLEAR_FREEZE_DMA = 2, OPAL_EEH_ACTION_CLEAR_FREEZE_ALL = 3, OPAL_EEH_ACTION_SET_FREEZE_MMIO = 1, OPAL_EEH_ACTION_SET_FREEZE_DMA = 2, OPAL_EEH_ACTION_SET_FREEZE_ALL = 3 }; enum OpalPciStatusToken { OPAL_EEH_NO_ERROR = 0, OPAL_EEH_IOC_ERROR = 1, OPAL_EEH_PHB_ERROR = 2, OPAL_EEH_PE_ERROR = 3, OPAL_EEH_PE_MMIO_ERROR = 4, OPAL_EEH_PE_DMA_ERROR = 5 }; enum OpalPciErrorSeverity { OPAL_EEH_SEV_NO_ERROR = 0, OPAL_EEH_SEV_IOC_DEAD = 1, OPAL_EEH_SEV_PHB_DEAD = 2, OPAL_EEH_SEV_PHB_FENCED = 3, OPAL_EEH_SEV_PE_ER = 4, OPAL_EEH_SEV_INF = 5 }; enum OpalErrinjectType { OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR = 0, OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64 = 1, }; enum OpalErrinjectFunc { /* IOA bus specific errors */ OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR = 0, OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_DATA = 1, OPAL_ERR_INJECT_FUNC_IOA_LD_IO_ADDR = 2, OPAL_ERR_INJECT_FUNC_IOA_LD_IO_DATA = 3, OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_ADDR = 4, OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_DATA = 5, OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_ADDR = 6, OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_DATA = 7, OPAL_ERR_INJECT_FUNC_IOA_ST_IO_ADDR = 8, OPAL_ERR_INJECT_FUNC_IOA_ST_IO_DATA = 9, OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_ADDR = 10, OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_DATA = 11, OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_ADDR = 12, OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_DATA = 13, OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_MASTER = 14, OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_TARGET = 15, OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_ADDR = 16, OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_DATA = 17, OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_MASTER = 18, OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET = 19, }; enum OpalShpcAction { OPAL_SHPC_GET_LINK_STATE = 0, OPAL_SHPC_GET_SLOT_STATE = 1 }; enum OpalShpcLinkState { OPAL_SHPC_LINK_DOWN = 0, OPAL_SHPC_LINK_UP_x1 = 1, OPAL_SHPC_LINK_UP_x2 = 2, OPAL_SHPC_LINK_UP_x4 = 4, OPAL_SHPC_LINK_UP_x8 = 8, OPAL_SHPC_LINK_UP_x16 = 16, OPAL_SHPC_LINK_UP_x32 = 32 }; enum OpalMmioWindowType { OPAL_M32_WINDOW_TYPE = 1, OPAL_M64_WINDOW_TYPE = 2, OPAL_IO_WINDOW_TYPE = 3 }; enum OpalShpcSlotState { OPAL_SHPC_DEV_NOT_PRESENT = 0, OPAL_SHPC_DEV_PRESENT = 1 }; enum OpalShpcPowerState { OPAL_SHPC_POWER_OFF = 0, OPAL_SHPC_POWER_ON = 1 }; enum OpalExceptionHandler { OPAL_MACHINE_CHECK_HANDLER = 1, OPAL_HYPERVISOR_MAINTENANCE_HANDLER = 2, OPAL_SOFTPATCH_HANDLER = 3 }; enum OpalPendingState { OPAL_EVENT_OPAL_INTERNAL = 0x1, OPAL_EVENT_NVRAM = 0x2, OPAL_EVENT_RTC = 0x4, OPAL_EVENT_CONSOLE_OUTPUT = 0x8, OPAL_EVENT_CONSOLE_INPUT = 0x10, OPAL_EVENT_ERROR_LOG_AVAIL = 0x20, OPAL_EVENT_ERROR_LOG = 0x40, OPAL_EVENT_EPOW = 0x80, OPAL_EVENT_LED_STATUS = 0x100, OPAL_EVENT_PCI_ERROR = 0x200, OPAL_EVENT_DUMP_AVAIL = 0x400, OPAL_EVENT_MSG_PENDING = 0x800, }; enum OpalThreadStatus { OPAL_THREAD_INACTIVE = 0x0, OPAL_THREAD_STARTED = 0x1, OPAL_THREAD_UNAVAILABLE = 0x2 /* opal-v3 */ }; enum OpalPciBusCompare { OpalPciBusAny = 0, /* Any bus number match */ OpalPciBus3Bits = 2, /* Match top 3 bits of bus number */ OpalPciBus4Bits = 3, /* Match top 4 bits of bus number */ OpalPciBus5Bits = 4, /* Match top 5 bits of bus number */ OpalPciBus6Bits = 5, /* Match top 6 bits of bus number */ OpalPciBus7Bits = 6, /* Match top 7 bits of bus number */ OpalPciBusAll = 7, /* Match bus number exactly */ }; enum OpalDeviceCompare { OPAL_IGNORE_RID_DEVICE_NUMBER = 0, OPAL_COMPARE_RID_DEVICE_NUMBER = 1 }; enum OpalFuncCompare { OPAL_IGNORE_RID_FUNCTION_NUMBER = 0, OPAL_COMPARE_RID_FUNCTION_NUMBER = 1 }; enum OpalPeAction { OPAL_UNMAP_PE = 0, OPAL_MAP_PE = 1 }; enum OpalPeltvAction { OPAL_REMOVE_PE_FROM_DOMAIN = 0, OPAL_ADD_PE_TO_DOMAIN = 1 }; enum OpalMveEnableAction { OPAL_DISABLE_MVE = 0, OPAL_ENABLE_MVE = 1 }; enum OpalM64Action { OPAL_DISABLE_M64 = 0, OPAL_ENABLE_M64_SPLIT = 1, OPAL_ENABLE_M64_NON_SPLIT = 2 }; enum OpalPciResetScope { OPAL_RESET_PHB_COMPLETE = 1, OPAL_RESET_PCI_LINK = 2, OPAL_RESET_PHB_ERROR = 3, OPAL_RESET_PCI_HOT = 4, OPAL_RESET_PCI_FUNDAMENTAL = 5, OPAL_RESET_PCI_IODA_TABLE = 6 }; enum OpalPciReinitScope { /* * Note: we chose values that do not overlap * OpalPciResetScope as OPAL v2 used the same * enum for both */ OPAL_REINIT_PCI_DEV = 1000 }; enum OpalPciResetState { OPAL_DEASSERT_RESET = 0, OPAL_ASSERT_RESET = 1 }; enum OpalPciMaskAction { OPAL_UNMASK_ERROR_TYPE = 0, OPAL_MASK_ERROR_TYPE = 1 }; enum OpalSlotLedType { OPAL_SLOT_LED_TYPE_ID = 0, /* IDENTIFY LED */ OPAL_SLOT_LED_TYPE_FAULT = 1, /* FAULT LED */ OPAL_SLOT_LED_TYPE_ATTN = 2, /* System Attention LED */ OPAL_SLOT_LED_TYPE_MAX = 3 }; enum OpalSlotLedState { OPAL_SLOT_LED_STATE_OFF = 0, /* LED is OFF */ OPAL_SLOT_LED_STATE_ON = 1 /* LED is ON */ }; enum OpalEpowStatus { OPAL_EPOW_NONE = 0, OPAL_EPOW_UPS = 1, OPAL_EPOW_OVER_AMBIENT_TEMP = 2, OPAL_EPOW_OVER_INTERNAL_TEMP = 3 }; enum OpalCheckTokenStatus { OPAL_TOKEN_ABSENT = 0, OPAL_TOKEN_PRESENT = 1 }; /* * Address cycle types for LPC accesses. These also correspond * to the content of the first cell of the "reg" property for * device nodes on the LPC bus */ enum OpalLPCAddressType { OPAL_LPC_MEM = 0, OPAL_LPC_IO = 1, OPAL_LPC_FW = 2, }; enum opal_msg_type { OPAL_MSG_ASYNC_COMP = 0, /* params[0] = token, params[1] = rc, * additional params function-specific */ OPAL_MSG_MEM_ERR = 1, OPAL_MSG_EPOW = 2, OPAL_MSG_SHUTDOWN = 3, /* params[0] = 1 reboot, 0 shutdown */ OPAL_MSG_HMI_EVT = 4, OPAL_MSG_DPO = 5, OPAL_MSG_PRD = 6, OPAL_MSG_OCC = 7, OPAL_MSG_TYPE_MAX, }; struct opal_msg { __be32 msg_type; __be32 reserved; __be64 params[8]; }; /* System parameter permission */ enum OpalSysparamPerm { OPAL_SYSPARAM_READ = 0x1, OPAL_SYSPARAM_WRITE = 0x2, OPAL_SYSPARAM_RW = (OPAL_SYSPARAM_READ | OPAL_SYSPARAM_WRITE), }; enum { OPAL_IPMI_MSG_FORMAT_VERSION_1 = 1, }; struct opal_ipmi_msg { uint8_t version; uint8_t netfn; uint8_t cmd; uint8_t data[]; }; /* * EPOW status sharing (OPAL and the host) * * The host will pass on OPAL, a buffer of length OPAL_SYSEPOW_MAX * with individual elements being 16 bits wide to fetch the system * wide EPOW status. Each element in the buffer will contain the * EPOW status in it's bit representation for a particular EPOW sub * class as defiend here. So multiple detailed EPOW status bits * specific for any sub class can be represented in a single buffer * element as it's bit representation. */ /* System EPOW type */ enum OpalSysEpow { OPAL_SYSEPOW_POWER = 0, /* Power EPOW */ OPAL_SYSEPOW_TEMP = 1, /* Temperature EPOW */ OPAL_SYSEPOW_COOLING = 2, /* Cooling EPOW */ OPAL_SYSEPOW_MAX = 3, /* Max EPOW categories */ }; /* Power EPOW */ enum OpalSysPower { OPAL_SYSPOWER_UPS = 0x0001, /* System on UPS power */ OPAL_SYSPOWER_CHNG = 0x0002, /* System power configuration change */ OPAL_SYSPOWER_FAIL = 0x0004, /* System impending power failure */ OPAL_SYSPOWER_INCL = 0x0008, /* System incomplete power */ }; /* Temperature EPOW */ enum OpalSysTemp { OPAL_SYSTEMP_AMB = 0x0001, /* System over ambient temperature */ OPAL_SYSTEMP_INT = 0x0002, /* System over internal temperature */ OPAL_SYSTEMP_HMD = 0x0004, /* System over ambient humidity */ }; /* Cooling EPOW */ enum OpalSysCooling { OPAL_SYSCOOL_INSF = 0x0001, /* System insufficient cooling */ }; /* FSP memory errors handling */ enum OpalMemErr_Version { OpalMemErr_V1 = 1, }; enum OpalMemErrType { OPAL_MEM_ERR_TYPE_RESILIENCE = 0, OPAL_MEM_ERR_TYPE_DYN_DALLOC, }; /* Memory Reilience error type */ enum OpalMemErr_ResilErrType { OPAL_MEM_RESILIENCE_CE = 0, OPAL_MEM_RESILIENCE_UE, OPAL_MEM_RESILIENCE_UE_SCRUB, }; /* Dynamic Memory Deallocation type */ enum OpalMemErr_DynErrType { OPAL_MEM_DYNAMIC_DEALLOC = 0, }; /* OpalMemoryErrorData->flags */ #define OPAL_MEM_CORRECTED_ERROR 0x0001 #define OPAL_MEM_THRESHOLD_EXCEEDED 0x0002 #define OPAL_MEM_ACK_REQUIRED 0x8000 struct OpalMemoryErrorData { enum OpalMemErr_Version version:8; /* 0x00 */ enum OpalMemErrType type:8; /* 0x01 */ __be16 flags; /* 0x02 */ uint8_t reserved_1[4]; /* 0x04 */ union { /* Memory Resilience corrected/uncorrected error info */ struct { enum OpalMemErr_ResilErrType resil_err_type:8; uint8_t reserved_1[7]; __be64 physical_address_start; __be64 physical_address_end; } resilience; /* Dynamic memory deallocation error info */ struct { enum OpalMemErr_DynErrType dyn_err_type:8; uint8_t reserved_1[7]; __be64 physical_address_start; __be64 physical_address_end; } dyn_dealloc; } u; }; /* HMI interrupt event */ enum OpalHMI_Version { OpalHMIEvt_V1 = 1, OpalHMIEvt_V2 = 2, }; enum OpalHMI_Severity { OpalHMI_SEV_NO_ERROR = 0, OpalHMI_SEV_WARNING = 1, OpalHMI_SEV_ERROR_SYNC = 2, OpalHMI_SEV_FATAL = 3, }; enum OpalHMI_Disposition { OpalHMI_DISPOSITION_RECOVERED = 0, OpalHMI_DISPOSITION_NOT_RECOVERED = 1, }; enum OpalHMI_ErrType { OpalHMI_ERROR_MALFUNC_ALERT = 0, OpalHMI_ERROR_PROC_RECOV_DONE, OpalHMI_ERROR_PROC_RECOV_DONE_AGAIN, OpalHMI_ERROR_PROC_RECOV_MASKED, OpalHMI_ERROR_TFAC, OpalHMI_ERROR_TFMR_PARITY, OpalHMI_ERROR_HA_OVERFLOW_WARN, OpalHMI_ERROR_XSCOM_FAIL, OpalHMI_ERROR_XSCOM_DONE, OpalHMI_ERROR_SCOM_FIR, OpalHMI_ERROR_DEBUG_TRIG_FIR, OpalHMI_ERROR_HYP_RESOURCE, OpalHMI_ERROR_CAPP_RECOVERY, }; enum OpalHMI_XstopType { CHECKSTOP_TYPE_UNKNOWN = 0, CHECKSTOP_TYPE_CORE = 1, CHECKSTOP_TYPE_NX = 2, }; enum OpalHMI_CoreXstopReason { CORE_CHECKSTOP_IFU_REGFILE = 0x00000001, CORE_CHECKSTOP_IFU_LOGIC = 0x00000002, CORE_CHECKSTOP_PC_DURING_RECOV = 0x00000004, CORE_CHECKSTOP_ISU_REGFILE = 0x00000008, CORE_CHECKSTOP_ISU_LOGIC = 0x00000010, CORE_CHECKSTOP_FXU_LOGIC = 0x00000020, CORE_CHECKSTOP_VSU_LOGIC = 0x00000040, CORE_CHECKSTOP_PC_RECOV_IN_MAINT_MODE = 0x00000080, CORE_CHECKSTOP_LSU_REGFILE = 0x00000100, CORE_CHECKSTOP_PC_FWD_PROGRESS = 0x00000200, CORE_CHECKSTOP_LSU_LOGIC = 0x00000400, CORE_CHECKSTOP_PC_LOGIC = 0x00000800, CORE_CHECKSTOP_PC_HYP_RESOURCE = 0x00001000, CORE_CHECKSTOP_PC_HANG_RECOV_FAILED = 0x00002000, CORE_CHECKSTOP_PC_AMBI_HANG_DETECTED = 0x00004000, CORE_CHECKSTOP_PC_DEBUG_TRIG_ERR_INJ = 0x00008000, CORE_CHECKSTOP_PC_SPRD_HYP_ERR_INJ = 0x00010000, }; enum OpalHMI_NestAccelXstopReason { NX_CHECKSTOP_SHM_INVAL_STATE_ERR = 0x00000001, NX_CHECKSTOP_DMA_INVAL_STATE_ERR_1 = 0x00000002, NX_CHECKSTOP_DMA_INVAL_STATE_ERR_2 = 0x00000004, NX_CHECKSTOP_DMA_CH0_INVAL_STATE_ERR = 0x00000008, NX_CHECKSTOP_DMA_CH1_INVAL_STATE_ERR = 0x00000010, NX_CHECKSTOP_DMA_CH2_INVAL_STATE_ERR = 0x00000020, NX_CHECKSTOP_DMA_CH3_INVAL_STATE_ERR = 0x00000040, NX_CHECKSTOP_DMA_CH4_INVAL_STATE_ERR = 0x00000080, NX_CHECKSTOP_DMA_CH5_INVAL_STATE_ERR = 0x00000100, NX_CHECKSTOP_DMA_CH6_INVAL_STATE_ERR = 0x00000200, NX_CHECKSTOP_DMA_CH7_INVAL_STATE_ERR = 0x00000400, NX_CHECKSTOP_DMA_CRB_UE = 0x00000800, NX_CHECKSTOP_DMA_CRB_SUE = 0x00001000, NX_CHECKSTOP_PBI_ISN_UE = 0x00002000, }; struct OpalHMIEvent { uint8_t version; /* 0x00 */ uint8_t severity; /* 0x01 */ uint8_t type; /* 0x02 */ uint8_t disposition; /* 0x03 */ uint8_t reserved_1[4]; /* 0x04 */ __be64 hmer; /* TFMR register. Valid only for TFAC and TFMR_PARITY error type. */ __be64 tfmr; /* version 2 and later */ union { /* * checkstop info (Core/NX). * Valid for OpalHMI_ERROR_MALFUNC_ALERT. */ struct { uint8_t xstop_type; /* enum OpalHMI_XstopType */ uint8_t reserved_1[3]; __be32 xstop_reason; union { __be32 pir; /* for CHECKSTOP_TYPE_CORE */ __be32 chip_id; /* for CHECKSTOP_TYPE_NX */ } u; } xstop_error; } u; }; enum { OPAL_P7IOC_DIAG_TYPE_NONE = 0, OPAL_P7IOC_DIAG_TYPE_RGC = 1, OPAL_P7IOC_DIAG_TYPE_BI = 2, OPAL_P7IOC_DIAG_TYPE_CI = 3, OPAL_P7IOC_DIAG_TYPE_MISC = 4, OPAL_P7IOC_DIAG_TYPE_I2C = 5, OPAL_P7IOC_DIAG_TYPE_LAST = 6 }; struct OpalIoP7IOCErrorData { __be16 type; /* GEM */ __be64 gemXfir; __be64 gemRfir; __be64 gemRirqfir; __be64 gemMask; __be64 gemRwof; /* LEM */ __be64 lemFir; __be64 lemErrMask; __be64 lemAction0; __be64 lemAction1; __be64 lemWof; union { struct OpalIoP7IOCRgcErrorData { __be64 rgcStatus; /* 3E1C10 */ __be64 rgcLdcp; /* 3E1C18 */ }rgc; struct OpalIoP7IOCBiErrorData { __be64 biLdcp0; /* 3C0100, 3C0118 */ __be64 biLdcp1; /* 3C0108, 3C0120 */ __be64 biLdcp2; /* 3C0110, 3C0128 */ __be64 biFenceStatus; /* 3C0130, 3C0130 */ uint8_t biDownbound; /* BI Downbound or Upbound */ }bi; struct OpalIoP7IOCCiErrorData { __be64 ciPortStatus; /* 3Dn008 */ __be64 ciPortLdcp; /* 3Dn010 */ uint8_t ciPort; /* Index of CI port: 0/1 */ }ci; }; }; /** * This structure defines the overlay which will be used to store PHB error * data upon request. */ enum { OPAL_PHB_ERROR_DATA_VERSION_1 = 1, }; enum { OPAL_PHB_ERROR_DATA_TYPE_P7IOC = 1, OPAL_PHB_ERROR_DATA_TYPE_PHB3 = 2 }; enum { OPAL_P7IOC_NUM_PEST_REGS = 128, OPAL_PHB3_NUM_PEST_REGS = 256 }; struct OpalIoPhbErrorCommon { __be32 version; __be32 ioType; __be32 len; }; struct OpalIoP7IOCPhbErrorData { struct OpalIoPhbErrorCommon common; __be32 brdgCtl; // P7IOC utl regs __be32 portStatusReg; __be32 rootCmplxStatus; __be32 busAgentStatus; // P7IOC cfg regs __be32 deviceStatus; __be32 slotStatus; __be32 linkStatus; __be32 devCmdStatus; __be32 devSecStatus; // cfg AER regs __be32 rootErrorStatus; __be32 uncorrErrorStatus; __be32 corrErrorStatus; __be32 tlpHdr1; __be32 tlpHdr2; __be32 tlpHdr3; __be32 tlpHdr4; __be32 sourceId; __be32 rsv3; // Record data about the call to allocate a buffer. __be64 errorClass; __be64 correlator; //P7IOC MMIO Error Regs __be64 p7iocPlssr; // n120 __be64 p7iocCsr; // n110 __be64 lemFir; // nC00 __be64 lemErrorMask; // nC18 __be64 lemWOF; // nC40 __be64 phbErrorStatus; // nC80 __be64 phbFirstErrorStatus; // nC88 __be64 phbErrorLog0; // nCC0 __be64 phbErrorLog1; // nCC8 __be64 mmioErrorStatus; // nD00 __be64 mmioFirstErrorStatus; // nD08 __be64 mmioErrorLog0; // nD40 __be64 mmioErrorLog1; // nD48 __be64 dma0ErrorStatus; // nD80 __be64 dma0FirstErrorStatus; // nD88 __be64 dma0ErrorLog0; // nDC0 __be64 dma0ErrorLog1; // nDC8 __be64 dma1ErrorStatus; // nE00 __be64 dma1FirstErrorStatus; // nE08 __be64 dma1ErrorLog0; // nE40 __be64 dma1ErrorLog1; // nE48 __be64 pestA[OPAL_P7IOC_NUM_PEST_REGS]; __be64 pestB[OPAL_P7IOC_NUM_PEST_REGS]; }; struct OpalIoPhb3ErrorData { struct OpalIoPhbErrorCommon common; __be32 brdgCtl; /* PHB3 UTL regs */ __be32 portStatusReg; __be32 rootCmplxStatus; __be32 busAgentStatus; /* PHB3 cfg regs */ __be32 deviceStatus; __be32 slotStatus; __be32 linkStatus; __be32 devCmdStatus; __be32 devSecStatus; /* cfg AER regs */ __be32 rootErrorStatus; __be32 uncorrErrorStatus; __be32 corrErrorStatus; __be32 tlpHdr1; __be32 tlpHdr2; __be32 tlpHdr3; __be32 tlpHdr4; __be32 sourceId; __be32 rsv3; /* Record data about the call to allocate a buffer */ __be64 errorClass; __be64 correlator; /* PHB3 MMIO Error Regs */ __be64 nFir; /* 000 */ __be64 nFirMask; /* 003 */ __be64 nFirWOF; /* 008 */ __be64 phbPlssr; /* 120 */ __be64 phbCsr; /* 110 */ __be64 lemFir; /* C00 */ __be64 lemErrorMask; /* C18 */ __be64 lemWOF; /* C40 */ __be64 phbErrorStatus; /* C80 */ __be64 phbFirstErrorStatus; /* C88 */ __be64 phbErrorLog0; /* CC0 */ __be64 phbErrorLog1; /* CC8 */ __be64 mmioErrorStatus; /* D00 */ __be64 mmioFirstErrorStatus; /* D08 */ __be64 mmioErrorLog0; /* D40 */ __be64 mmioErrorLog1; /* D48 */ __be64 dma0ErrorStatus; /* D80 */ __be64 dma0FirstErrorStatus; /* D88 */ __be64 dma0ErrorLog0; /* DC0 */ __be64 dma0ErrorLog1; /* DC8 */ __be64 dma1ErrorStatus; /* E00 */ __be64 dma1FirstErrorStatus; /* E08 */ __be64 dma1ErrorLog0; /* E40 */ __be64 dma1ErrorLog1; /* E48 */ __be64 pestA[OPAL_PHB3_NUM_PEST_REGS]; __be64 pestB[OPAL_PHB3_NUM_PEST_REGS]; }; enum { OPAL_REINIT_CPUS_HILE_BE = (1 << 0), OPAL_REINIT_CPUS_HILE_LE = (1 << 1), }; typedef struct oppanel_line { __be64 line; __be64 line_len; } oppanel_line_t; enum opal_prd_msg_type { OPAL_PRD_MSG_TYPE_INIT = 0, /* HBRT --> OPAL */ OPAL_PRD_MSG_TYPE_FINI, /* HBRT/kernel --> OPAL */ OPAL_PRD_MSG_TYPE_ATTN, /* HBRT <-- OPAL */ OPAL_PRD_MSG_TYPE_ATTN_ACK, /* HBRT --> OPAL */ OPAL_PRD_MSG_TYPE_OCC_ERROR, /* HBRT <-- OPAL */ OPAL_PRD_MSG_TYPE_OCC_RESET, /* HBRT <-- OPAL */ }; struct opal_prd_msg_header { uint8_t type; uint8_t pad[1]; __be16 size; }; struct opal_prd_msg { struct opal_prd_msg_header hdr; __be32 token; union { struct { __be64 version; __be64 ipoll; } init; struct { __be64 proc; __be64 ipoll_status; __be64 ipoll_mask; } attn; struct { __be64 proc; __be64 ipoll_ack; } attn_ack; struct { __be64 chip; } occ_error; struct { __be64 chip; } occ_reset; }; }; #define OCC_RESET 0 #define OCC_LOAD 1 #define OCC_THROTTLE 2 #define OCC_MAX_THROTTLE_STATUS 5 /* * struct opal_occ_msg: * type: OCC_RESET, OCC_LOAD, OCC_THROTTLE * chip: chip id * throttle status: indicates the reason why OCC may have limited * the max Pstate of the chip. * 0x00 = No throttle * 0x01 = Power Cap * 0x02 = Processor Over Temperature * 0x03 = Power Supply Failure (currently not used) * 0x04 = Over current (currently not used) * 0x05 = OCC Reset (not reliable as some failures will not allow for * OCC to update throttle status) */ struct opal_occ_msg { __be64 type; __be64 chip; __be64 throttle_status; }; /* * SG entries * * WARNING: The current implementation requires each entry * to represent a block that is 4k aligned *and* each block * size except the last one in the list to be as well. */ struct opal_sg_entry { __be64 data; __be64 length; }; /* * Candidate image SG list. * * length = VER | length */ struct opal_sg_list { __be64 length; __be64 next; struct opal_sg_entry entry[]; }; /* * Dump region ID range usable by the OS */ #define OPAL_DUMP_REGION_HOST_START 0x80 #define OPAL_DUMP_REGION_LOG_BUF 0x80 #define OPAL_DUMP_REGION_HOST_END 0xFF /* CAPI modes for PHB */ enum { OPAL_PHB_CAPI_MODE_PCIE = 0, OPAL_PHB_CAPI_MODE_CAPI = 1, OPAL_PHB_CAPI_MODE_SNOOP_OFF = 2, OPAL_PHB_CAPI_MODE_SNOOP_ON = 3, }; /* CAPI feature flags (in device-tree) */ #define OPAL_PHB_CAPI_FLAG_SNOOP_CONTROL 0x00000001 #define OPAL_PHB_CAPI_FLAG_REVERT_TO_PCIE 0x00000002 /* OPAL I2C request */ struct opal_i2c_request { uint8_t type; #define OPAL_I2C_RAW_READ 0 #define OPAL_I2C_RAW_WRITE 1 #define OPAL_I2C_SM_READ 2 #define OPAL_I2C_SM_WRITE 3 uint8_t flags; #define OPAL_I2C_ADDR_10 0x01 /* Not supported yet */ uint8_t subaddr_sz; /* Max 4 */ uint8_t reserved; __be16 addr; /* 7 or 10 bit address */ __be16 reserved2; __be32 subaddr; /* Sub-address if any */ __be32 size; /* Data size */ __be64 buffer_ra; /* Buffer real address */ }; /* Argument to OPAL_CEC_REBOOT2() */ enum { OPAL_REBOOT_NORMAL = 0, OPAL_REBOOT_PLATFORM_ERROR, }; #endif /* __ASSEMBLY__ */ #endif /* __OPAL_API_H */ skiboot-skiboot-5.1.13/include/opal-internal.h000066400000000000000000000052611265204436200212740ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OPAL_INTERNAL_H #define __OPAL_INTERNAL_H /****** Internal header for OPAL API related things in skiboot **********/ #include /* An opal table entry */ struct opal_table_entry { void *func; __be32 token; __be32 nargs; }; #define opal_call(__tok, __func, __nargs) \ static struct opal_table_entry __e_##__func __used __section(".opal_table") = \ { .func = __func, .token = __tok, \ .nargs = __nargs + 0 * sizeof(__func( __test_args##__nargs )) } /* Make sure function takes args they claim. Look away now... */ #define __test_args0 #define __test_args1 0 #define __test_args2 0,0 #define __test_args3 0,0,0 #define __test_args4 0,0,0,0 #define __test_args5 0,0,0,0,0 #define __test_args6 0,0,0,0,0,0 #define __test_args7 0,0,0,0,0,0,0 extern struct opal_table_entry __opal_table_start[]; extern struct opal_table_entry __opal_table_end[]; extern __be64 opal_pending_events; extern struct dt_node *opal_node; extern void opal_table_init(void); extern void opal_update_pending_evt(__be64 evt_mask, __be64 evt_values); __be64 opal_dynamic_event_alloc(void); void opal_dynamic_event_free(__be64 event); extern void add_opal_node(void); #define opal_register(token, func, nargs) \ __opal_register((token) + 0*sizeof(func(__test_args##nargs)), \ (func), (nargs)) extern void __opal_register(__be64 token, void *func, unsigned num_args); /* Warning: no locking at the moment, do at init time only * * XXX TODO: Add the big RCU-ish "opal API lock" to protect us here * which will also be used for other things such as runtime updates */ extern void opal_add_poller(void (*poller)(void *data), void *data); extern void opal_del_poller(void (*poller)(void *data)); extern void opal_run_pollers(void); /* * Warning: no locking, only call that from the init processor */ extern void opal_add_host_sync_notifier(bool (*notify)(void *data), void *data); extern void opal_del_host_sync_notifier(bool (*notify)(void *data)); /* * Opal internal function prototype */ struct OpalHMIEvent; extern int handle_hmi_exception(__be64 hmer, struct OpalHMIEvent *hmi_evt); #endif /* __OPAL_INTERNAL_H */ skiboot-skiboot-5.1.13/include/opal-msg.h000066400000000000000000000023351265204436200202450ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OPALMSG_H #define __OPALMSG_H #include /* * It dictates the number of asynchronous tokens available at the kernel, * ideally the value matches to the number of modules using async * infrastructure, but not necessarily the same.. */ #define OPAL_MAX_ASYNC_COMP 8 int _opal_queue_msg(enum opal_msg_type msg_type, void *data, void (*consumed)(void *data), size_t num_params, const u64 *params); #define opal_queue_msg(msg_type, data, cb, ...) \ _opal_queue_msg(msg_type, data, cb, \ sizeof((u64[]) {__VA_ARGS__})/sizeof(u64), \ (u64[]) {__VA_ARGS__}); void opal_init_msg(void); #endif /* __OPALMSG_H */ skiboot-skiboot-5.1.13/include/opal.h000066400000000000000000000013221265204436200174540ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OPAL_H #define __OPAL_H #include #include #include #endif /* __OPAL_H */ skiboot-skiboot-5.1.13/include/p5ioc2-regs.h000066400000000000000000000167071265204436200205750ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __P5IOC2_REGS_H #define __P5IOC2_REGS_H /* * IO HUB registers * * Most (all) of those registers support an AND access * at address + 0x1000 and an OR access at address + 0x2000 */ #define P5IOC2_REG_AND 0x1000 #define P5IOC2_REG_OR 0x2000 /* Internal BARs */ #define P5IOC2_BAR0 0x0100 #define P5IOC2_BAR1 0x0108 #define P5IOC2_BAR2 0x0110 #define P5IOC2_BAR3 0x0118 #define P5IOC2_BAR4 0x0120 #define P5IOC2_BAR5 0x0128 #define P5IOC2_BAR6 0x0130 #define P5IOC2_BAR7 0x0138 #define P5IOC2_BARM0 0x0180 #define P5IOC2_BARM1 0x0188 #define P5IOC2_BARM2 0x0190 #define P5IOC2_BARM3 0x0198 #define P5IOC2_BARM4 0x01a0 #define P5IOC2_BARM5 0x01a8 #define P5IOC2_BARM6 0x01b0 #define P5IOC2_BARM7 0x01b8 #define P5IOC2_BAR(n) (0x100 + ((n) << 3)) #define P5IOC2_BARM(n) (0x180 + ((n) << 3)) /* Routing table */ #define P5IOC2_TxRTE(x,n) (0x200 + ((x) << 7) + ((n) << 3)) #define P5IOC2_TxRTE_VALID PPC_BIT(47) /* BUID routing table */ #define P5IOC2_BUIDRTE(n) (0x600 + ((n) << 3)) #define P5IOC2_BUIDRTE_VALID PPC_BIT(47) #define P5IOC2_BUIDRTE_RR_EOI PPC_BIT(48) #define P5IOC2_BUIDRTE_RR_RET PPC_BIT(49) /* Others */ #define P5IOC2_FIRMC 0x0008 /* FIR Mask Checkstop */ #define P5IOC2_CTL 0x0030 /* Control register part 1 */ #define P5IOC2_CTL2 0x00c8 /* Control register part 2 */ #define P5IOC2_DIRA 0x0090 /* Cache dir. address */ #define P5IOC2_DIRD 0x0098 /* Cache dir. data */ #define P5IOC2_IBASE 0x0048 /* Interrupt base address */ #define P5IOC2_IRBM 0x00d8 /* Interrupt re-issue broadcast mask */ #define P5IOC2_SID 0x0038 /* P5IOC2 ID register */ #define P5IOC2_SID_BUID_BASE PPC_BITMASK(14,22) #define P5IOC2_SID_BUID_MASK PPC_BITMASK(27,30) #define P5IOC2_SBUID 0x00f8 /* P5IOC2 HUB BUID */ /* XIPM area */ #define P5IOC2_BUCO 0x40008 #define P5IOC2_MIIP 0x40000 #define P5IOC2_XINM 0x40010 /* Xin/Xout area */ #define P5IOC2_XIXO 0xf0030 #define P5IOC2_XIXO_ENH_TCE PPC_BIT(0) /* * Calgary registers * * CA0 is PCI-X and CA1 is PCIE, though the type can be discovered * from registers so we'll simply let it do so */ #define CA_CCR 0x108 #define CA_DEVBUID 0x118 #define CA_DEVBUID_MASK PPC_BITMASK32(7,15) #define CA_TAR0 0x580 #define CA_TAR_HUBID PPC_BITMASK(0,5) #define CA_TAR_ALTHUBID PPC_BITMASK(6,11) #define CA_TAR_TCE_ADDR PPC_BITMASK(16,48) #define CA_TAR_VALID PPC_BIT(60) #define CA_TAR_NUM_TCE PPC_BITMASK(61,63) #define CA_TAR1 0x588 #define CA_TAR2 0x590 #define CA_TAR3 0x598 #define CA_TARn(n) (0x580 + ((n) << 3)) #define CA_PHBID0 0x650 #define CA_PHBID_PHB_ENABLE PPC_BIT32(0) #define CA_PHBID_ADDRSPACE_ENABLE PPC_BIT32(1) #define CA_PHBID_PHB_TYPE PPC_BITMASK32(4,7) #define CA_PHBTYPE_PCIX1_0 0 #define CA_PHBTYPE_PCIX2_0 1 #define CA_PHBTYPE_PCIE_G1 4 #define CA_PHBTYPE_PCIE_G2 5 /* PCI-X bits */ #define CA_PHBID_XMODE_EMBEDDED PPC_BIT32(8) #define CA_PHBID_XBUS_64BIT PPC_BIT32(9) #define CA_PHBID_XBUS_266MHZ PPC_BIT32(10) /* PCI-E bits */ #define CA_PHBID_EWIDTH PPC_BITMASK32(8,10) #define CA_PHB_EWIDTH_X4 0 #define CA_PHB_EWIDTH_X8 1 #define CA_PHB_EWIDTH_X16 2 #define CA_PHBID1 0x658 #define CA_PHBID2 0x660 #define CA_PHBID3 0x668 #define CA_PHBIDn(n) (0x650 + ((n) << 3)) /* PHB n reg base inside CA */ #define CA_PHBn_REGS(n) (0x8000 + ((n) << 12)) /* * P5IOC2 PHB registers */ #define CAP_BUID 0x100 #define CAP_BUID_MASK PPC_BITMASK32(7,15) #define CAP_MSIBASE 0x108 /* Undocumented ! */ #define CAP_DMACSR 0x110 #define CAP_PLSSR 0x120 #define CAP_PCADR 0x140 #define CAP_PCADR_ENABLE PPC_BIT32(0) #define CAP_PCADR_FUNC PPC_BITMASK32(21,23) #define CAP_PCADR_BDFN PPC_BITMASK32(8,23) /* bus,dev,func */ #define CAP_PCADR_EXTOFF PPC_BITMASK32(4,7) #define CAP_PCDAT 0x130 #define CAP_PCFGRW 0x160 #define CAP_PCFGRW_ERR_RECOV_EN PPC_BIT32(1) #define CAP_PCFGRW_TCE_EN PPC_BIT32(2) #define CAP_PCFGRW_FREEZE_EN PPC_BIT32(3) #define CAP_PCFGRW_MMIO_FROZEN PPC_BIT32(4) #define CAP_PCFGRW_DMA_FROZEN PPC_BIT32(5) #define CAP_PCFGRW_ENHANCED_CFG_EN PPC_BIT32(6) #define CAP_PCFGRW_DAC_DISABLE PPC_BIT32(7) #define CAP_PCFGRW_2ND_MEM_SPACE_EN PPC_BIT32(9) #define CAP_PCFGRW_MASK_PLSSR_IRQ PPC_BIT32(10) #define CAP_PCFGRW_MASK_CSR_IRQ PPC_BIT32(11) #define CAP_PCFGRW_IO_SPACE_DIABLE PPC_BIT32(12) #define CAP_PCFGRW_SZ_MASK_IS_LIMIT PPC_BIT32(13) #define CAP_PCFGRW_MSI_EN PPC_BIT32(14) #define CAP_IOAD_L 0x170 #define CAP_IOAD_H 0x180 #define CAP_MEM1_L 0x190 #define CAP_MEM1_H 0x1a0 #define CAP_IOSZ 0x1b0 #define CAP_MSZ1 0x1c0 #define CAP_MEM_ST 0x1d0 #define CAP_IO_ST 0x1e0 #define CAP_AER 0x200 #define CAP_BPR 0x210 #define CAP_CRR 0x270 #define CAP_CRR_RESET1 PPC_BIT32(0) #define CAP_CRR_RESET2 PPC_BIT32(1) #define CAP_XIVR0 0x400 #define CAP_XIVR_PRIO 0x000000ff #define CAP_XIVR_SERVER 0x0000ff00 #define CAP_XIVRn(n) (0x400 + ((n) << 4)) #define CAP_MVE0 0x500 #define CAP_MVE_VALID PPC_BIT32(0) #define CAP_MVE_TBL_OFF PPC_BITMASK32(13,15) #define CAP_MVE_NUM_INT PPC_BITMASK32(18,19) #define CAP_MVE1 0x510 #define CAP_MODE0 0x880 #define CAP_MODE1 0x890 #define CAP_MODE2 0x8a0 #define CAP_MODE3 0x8b0 /* * SHPC Registers */ #define SHPC_LOGICAL_SLOT 0xb40 #define SHPC_LOGICAL_SLOT_STATE 0x00000003 #define SHPC_SLOT_STATE_POWER_ONLY 1 #define SHPC_SLOT_STATE_ENABLED 2 #define SHPC_SLOT_STATE_DISABLED 3 #define SHPC_LOGICAL_SLOT_PRSNT 0x000000c00 #define SHPC_SLOT_PRSTN_7_5W 0 #define SHPC_SLOT_PRSTN_25W 1 #define SHPC_SLOT_STATE_15W 2 #define SHPC_SLOT_STATE_EMPTY 3 /* UTL registers */ #define UTL_SYS_BUS_CONTROL 0xc00 #define UTL_STATUS 0xc04 #define UTL_SYS_BUS_AGENT_STATUS 0xc08 #define UTL_SYS_BUS_AGENT_ERR_EN 0xc0c #define UTL_SYS_BUS_AGENT_IRQ_EN 0xc10 #define UTL_SYS_BUS_BURST_SZ_CONF 0xc20 #define UTL_REVISION_ID 0xc24 #define UTL_TX_NON_POST_DEBUG_STAT1 0xc30 #define UTL_TX_NON_POST_DEBUG_STAT2 0xc34 #define UTL_GBIF_READ_REQ_DEBUG 0xc38 #define UTL_GBIF_WRITE_REQ_DEBUG 0xc3c #define UTL_GBIF_TX_COMP_DEBUG 0xc40 #define UTL_GBIF_RX_COMP_DEBUG 0xc44 #define UTL_OUT_POST_HDR_BUF_ALLOC 0xc60 #define UTL_OUT_POST_DAT_BUF_ALLOC 0xc68 #define UTL_IN_POST_HDR_BUF_ALLOC 0xc70 #define UTL_IN_POST_DAT_BUF_ALLOC 0xc78 #define UTL_OUT_NP_BUF_ALLOC 0xc80 #define UTL_IN_NP_BUF_ALLOC 0xc88 #define UTL_PCIE_TAGS_ALLOC 0xc90 #define UTL_GBIF_READ_TAGS_ALLOC 0xc98 #define UTL_PCIE_PORT_CONTROL 0xca0 #define UTL_PCIE_PORT_STATUS 0xca4 #define UTL_PCIE_PORT_ERR_EN 0xca8 #define UTL_PCIE_PORT_IRQ_EN 0xcac #define UTL_RC_STATUS 0xcb0 #define UTL_RC_ERR_EN 0xcb4 #define UTL_RC_IRQ_EN 0xcb8 #define UTL_PCI_PM_CONTROL 0xcc8 #define UTL_PCIE_PORT_ID 0xccc #define UTL_TLP_DEBUG 0xcd0 #define UTL_VC_CTL_DEBUG 0xcd4 #define UTL_NP_BUFFER_DEBUG 0xcd8 #define UTL_POSTED_BUFFER_DEBUG 0xcdc #define UTL_TX_FIFO_DEBUG 0xce0 #define UTL_TLP_COMPL_DEBUG 0xce4 #endif /* __P5IOC2_REGS_H */ skiboot-skiboot-5.1.13/include/p5ioc2.h000066400000000000000000000111601265204436200176230ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __P5IOC2_H #define __P5IOC2_H #include #include #include #include #include #include #include #include /* * Various definitions which are the result of various * things we have hard wired (routing etc...) */ /* It looks like our registers are at an offset from GX BAR 0 ... */ #define P5IOC2_REGS_OFFSET 0x01F00000 #define P5IOC2_CA0_REG_OFFSET 0 /* From BAR6, R0 */ #define P5IOC2_CA1_REG_OFFSET 0x01000000 /* From BAR6, R1 */ #define P5IOC2_CA0_MM_OFFSET 0 /* From BAR0, R0 and 1 */ #define P5IOC2_CA1_MM_OFFSET 0x400000000ul /* From BAR0, R1 and 2 */ #define P5IOC2_CA_PHB_COUNT 4 #define P5IOC2_CA0_RIO_ID 2 #define P5IOC2_CA1_RIO_ID 3 #define P5IOC2_CA0_BUID 0x10 #define P5IOC2_CA1_BUID 0x20 /* * Our memory space is slightly different than pHyp * (or even BML). We do as follow: * * - IO space is in the Calgary MMIO, at (phb_index +1) * 1M * (pHyp seems to mangle the IO space location) and is always * 1M in size mapping to PCI 0 * * - Memory space is in the BAR0 mapped region. Each PHB gets * allocated a 4G window at base + (phb_index * 4G). It uses * a portion of that space based on the chosen size of the * MMIO space, typically 2G. */ #define MM_WINDOW_SIZE 0x100000000ul #define MM_PCI_START 0x80000000 #define MM_PCI_SIZE 0x80000000 #define IO_PCI_START 0x00000000 #define IO_PCI_SIZE 0x00100000 /* * CAn interrupts * * Within Calgary BUID space */ #define P5IOC2_CA_HOST_IRQ 0 #define P5IOC2_CA_SPCN_IRQ 1 #define P5IOC2_CA_PERF_IRQ 2 /* * The PHB states are similar to P7IOC, see the explanation * in p7ioc.h */ enum p5ioc2_phb_state { /* First init state */ P5IOC2_PHB_STATE_UNINITIALIZED, /* During PHB HW inits */ P5IOC2_PHB_STATE_INITIALIZING, /* Set if the PHB is for some reason unusable */ P5IOC2_PHB_STATE_BROKEN, /* Normal PHB functional state */ P5IOC2_PHB_STATE_FUNCTIONAL, }; /* * Structure for a PHB */ struct p5ioc2; struct p5ioc2_phb { bool active; /* Is this PHB functional ? */ bool is_pcie; uint8_t ca; /* CA0 or CA1 */ uint8_t index; /* 0..3 index inside CA */ void *ca_regs; /* Calgary regs */ void *regs; /* PHB regs */ struct lock lock; uint32_t buid; uint64_t mm_base; uint64_t io_base; int64_t ecap; /* cached PCI-E cap offset */ int64_t aercap; /* cached AER ecap offset */ enum p5ioc2_phb_state state; uint64_t delay_tgt_tb; uint64_t retries; uint64_t xive_cache[16]; struct p5ioc2 *ioc; struct phb phb; }; static inline struct p5ioc2_phb *phb_to_p5ioc2_phb(struct phb *phb) { return container_of(phb, struct p5ioc2_phb, phb); } extern void p5ioc2_phb_setup(struct p5ioc2 *ioc, struct p5ioc2_phb *p, uint8_t ca, uint8_t index, bool active, uint32_t buid); /* * State structure for P5IOC2 IO HUB */ struct p5ioc2 { /* Device node */ struct dt_node *dt_node; /* MMIO regs for the chip */ void *regs; /* BAR6 (matches GX BAR 1) is used for internal Calgary MMIO and * for PCI IO space. */ uint64_t bar6; /* BAR0 (matches GX BAR 2) is used for PCI memory space */ uint64_t bar0; /* Calgary 0 and 1 registers. We assume their BBAR values as such * that CA0 is at bar6 and CA1 at bar6 + 16M */ void* ca0_regs; void* ca1_regs; /* The large MM regions assigned off bar0 to CA0 and CA1 for use * by their PHBs (16G each) */ uint64_t ca0_mm_region; uint64_t ca1_mm_region; /* BUID base for the PHB. This does include the top bits * (chip, GX bus ID, etc...). This is initialized from the * SPIRA. */ uint32_t buid_base; /* TCE region set by the user */ uint64_t tce_base; uint64_t tce_size; /* Calgary 0 and 1 PHBs */ struct p5ioc2_phb ca0_phbs[P5IOC2_CA_PHB_COUNT]; struct p5ioc2_phb ca1_phbs[P5IOC2_CA_PHB_COUNT]; uint32_t host_chip; uint32_t gx_bus; struct io_hub hub; }; static inline struct p5ioc2 *iohub_to_p5ioc2(struct io_hub *hub) { return container_of(hub, struct p5ioc2, hub); } #endif /* __P5IOC2_H */ skiboot-skiboot-5.1.13/include/p7ioc-regs.h000066400000000000000000000361711265204436200205120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __P7IOC_REGS_H #define __P7IOC_REGS_H /* * Register definitions * * We only define some registers here. Ideally we should auto-generate * the full list from the spec. For now I add them as I need them */ /* RGC GEM registers */ #define P7IOC_GEM_XFIR 0x3E0008 #define P7IOC_GEM_RFIR 0x3E0010 #define P7IOC_GEM_RIRQFIR 0x3E0018 #define P7IOC_GEM_MASK 0x3E0020 #define P7IOC_GEM_RWOF 0x3E0028 /* LEM register base */ #define P7IOC_RGC_LEM_BASE 0x3E1E00 #define P7IOC_BI_UP_LEM_BASE 0x3C0000 #define P7IOC_BI_DOWN_LEM_BASE 0x3C0050 #define P7IOC_CI_PORTn_LEM_BASE(n) (0x3d0200 | ((n) * 0x1000)) #define P7IOC_PHBn_LEM_BASE(n) (0x000C00 | ((n) * 0x10000)) #define P7IOC_MISC_LEM_BASE 0x3EA000 #define P7IOC_I2C_LEM_BASE 0x3EB000 /* LEM register offset */ #define P7IOC_LEM_FIR_OFFSET 0x00 #define P7IOC_LEM_FIR_AND_OFFSET 0x08 #define P7IOC_LEM_FIR_OR_OFFSET 0x10 #define P7IOC_LEM_ERR_MASK_OFFSET 0x18 #define P7IOC_LEM_ERR_MASK_AND_OFFSET 0x20 #define P7IOC_LEM_ERR_MASK_OR_OFFSET 0x28 #define P7IOC_LEM_ACTION_0_OFFSET 0x30 #define P7IOC_LEM_ACTION_1_OFFSET 0x38 #define P7IOC_LEM_WOF_OFFSET 0x40 /* HSS registers */ #define P7IOC_HSS_BASE 0x3E8000 #define P7IOC_HSS_STRIDE 0x200 #define P7IOC_HSSn_CTL2_OFFSET 0x10 #define P7IOC_HSSn_CTL3_OFFSET 0x18 #define P7IOC_HSSn_CTL8_OFFSET 0x40 #define P7IOC_HSSn_CTL9_OFFSET 0x48 #define P7IOC_HSSn_CTL10_OFFSET 0x50 #define P7IOC_HSSn_CTL11_OFFSET 0x58 #define P7IOC_HSSn_CTL12_OFFSET 0x60 #define P7IOC_HSSn_CTL13_OFFSET 0x68 #define P7IOC_HSSn_CTL14_OFFSET 0x70 #define P7IOC_HSSn_CTL15_OFFSET 0x78 #define P7IOC_HSSn_CTL16_OFFSET 0x80 #define P7IOC_HSSn_CTL17_OFFSET 0x88 #define P7IOC_HSSn_CTL18_OFFSET 0x90 #define P7IOC_HSSn_CTL19_OFFSET 0x98 #define P7IOC_HSSn_CTL20_OFFSET 0xa0 #define P7IOC_HSSn_CTL21_OFFSET 0xa8 #define P7IOC_HSSn_CTL22_OFFSET 0xb0 #define P7IOC_HSSn_CTL23_OFFSET 0xb8 /* CI Routing registers & helper macros */ #define P7IOC_CI_RMATC_REG(i) (0x3D0400ul + ((i) << 4)) #define P7IOC_CI_RMASK_REG(i) (0x3D0408ul + ((i) << 4)) #define P7IOC_CI_RMATC_PORT(n) PPC_BIT(n) #define P7IOC_CI_RMATC_ADDR_VALID PPC_BIT(16) #define P7IOC_CI_RMATC_BUID_VALID PPC_BIT(17) #define P7IOC_CI_RMATC_TYPE_VALID PPC_BIT(18) /* AIB Addresses are 48-bit, the top 32 are used in * the routing tables, we thus shift by 16 */ #define P7IOC_CI_RMATC_ENCODE_ADDR(addr) ((uint32_t)((addr) >> 16)) #define P7IOC_CI_RMATC_ENCODE_BUID(buid) ((uint32_t)((buid) << 20)) #define P7IOC_CI_RMATC_ENCODE_TYPE(type) ((uint32_t)(type)) /* CI port numbers */ #define P7IOC_CI_PHB_PORT(pnum) ((pnum) + 2) #define P7IOC_CI_UPSTREAM_PORT 0 #define P7IOC_CI_RGC_PORT 1 /* Other random chip registers */ #define P7IOC_CHIP_FENCE_SHADOW 0x3ec010 #define P7IOC_CHIP_FENCE_WOF 0x3ec018 #define P7IOC_CCRR 0x3e1c00 /* CI registers */ #define P7IOC_CIn_BASE(n) (0x3d0000 | ((n) * 0x1000)) #define P7IOC_CIn_LEM_FIR(n) (P7IOC_CIn_BASE(n) + 0x200) #define P7IOC_CIn_LEM_FIR_AND(n) (P7IOC_CIn_BASE(n) + 0x208) #define P7IOC_CIn_LEM_FIR_OR(n) (P7IOC_CIn_BASE(n) + 0x210) #define P7IOC_CIn_LEM_ERR_MASK(n) (P7IOC_CIn_BASE(n) + 0x218) #define P7IOC_CIn_LEM_ERR_MASK_AND(n) (P7IOC_CIn_BASE(n) + 0x220) #define P7IOC_CIn_LEM_ERR_MASK_OR(n) (P7IOC_CIn_BASE(n) + 0x228) /* * PHB registers */ /* PHB Fundamental register set A */ #define PHB_BUID 0x100 #define PHB_BUID_LSI PPC_BITMASK(7,15) #define PHB_BUID_MSI PPC_BITMASK(23,31) #define PHB_DMA_CHAN_STATUS 0x110 #define PHB_CPU_LOADSTORE_STATUS 0x120 #define PHB_CONFIG_DATA 0x130 #define PHB_LOCK0 0x138 #define PHB_CONFIG_ADDRESS 0x140 #define PHB_CA_ENABLE PPC_BIT(0) #define PHB_CA_BUS PPC_BITMASK(4,11) #define PHB_CA_DEV PPC_BITMASK(12,16) #define PHB_CA_FUNC PPC_BITMASK(17,19) #define PHB_CA_BDFN PPC_BITMASK(4,19) /* bus,dev,func */ #define PHB_CA_REG PPC_BITMASK(20,31) #define PHB_LOCK1 0x148 #define PHB_PHB2_CONFIG 0x160 #define PHB_PHB2C_64B_TCE_EN PPC_BIT(2) #define PHB_PHB2C_32BIT_MSI_EN PPC_BIT(8) #define PHB_PHB2C_IO_EN PPC_BIT(12) #define PHB_PHB2C_64BIT_MSI_EN PPC_BIT(14) #define PHB_PHB2C_M32_EN PPC_BIT(16) #define PHB_IO_BASE_ADDR 0x170 #define PHB_IO_BASE_MASK 0x178 #define PHB_IO_START_ADDR 0x180 #define PHB_M32_BASE_ADDR 0x190 #define PHB_M32_BASE_MASK 0x198 #define PHB_M32_START_ADDR 0x1a0 #define PHB_M64_UPPER_BITS 0x1f0 #define PHB_TCE_KILL 0x210 #define PHB_TCEKILL_PAIR PPC_BIT(0) #define PHB_TCEKILL_ADDR PPC_BITMASK(16,59) #define PHB_TCE_PREFETCH 0x218 #define PHB_IODA_ADDR 0x220 #define PHB_IODA_AD_AUTOINC PPC_BIT(0) #define PHB_IODA_AD_TSEL PPC_BITMASK(11,15) #define PHB_IODA_AD_TADR PPC_BITMASK(48,63) #define PHB_IODA_DATA0 0x228 #define PHB_IODA_DATA1 0x230 #define PHB_LOCK2 0x240 #define PHB_XIVE_UPDATE 0x248 #define PHB_PHB2_GEN_CAP 0x250 #define PHB_PHB2_TCE_CAP 0x258 #define PHB_PHB2_IRQ_CAP 0x260 #define PHB_PHB2_EEH_CAP 0x268 #define PHB_PAPR_ERR_INJ_CTL 0x2b0 #define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0) #define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1) #define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2) #define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3) #define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4) #define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5) #define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6) #define PHB_PAPR_ERR_INJ_ADDR 0x2b8 #define PHB_PAPR_ERR_INJ_MASK 0x2c0 #define PHB_PAPR_ERR_INJ_MASK_CFG PPC_BITMASK(4,11) #define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16,39) /* 16M aligned */ #define PHB_PAPR_ERR_INJ_MASK_IO PPC_BITMASK(16,47) /* 64K aligned */ #define PHB_PAPR_ERR_INJ_MASK_DMA PPC_BITMASK(60,63) /* 16 window */ #define PHB_ETU_ERR_SUMMARY 0x2c8 /* UTL registers */ #define UTL_SYS_BUS_CONTROL 0x400 #define UTL_STATUS 0x408 #define UTL_SYS_BUS_AGENT_STATUS 0x410 #define UTL_SYS_BUS_AGENT_ERR_SEVERITY 0x418 #define UTL_SYS_BUS_AGENT_IRQ_EN 0x420 #define UTL_SYS_BUS_BURST_SZ_CONF 0x440 #define UTL_REVISION_ID 0x448 #define UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 #define UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 #define UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 #define UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 #define UTL_OUT_NP_BUF_ALLOC 0x500 #define UTL_IN_NP_BUF_ALLOC 0x510 #define UTL_PCIE_TAGS_ALLOC 0x520 #define UTL_GBIF_READ_TAGS_ALLOC 0x530 #define UTL_PCIE_PORT_CONTROL 0x540 #define UTL_PCIE_PORT_STATUS 0x548 #define UTL_PCIE_PORT_ERROR_SEV 0x550 #define UTL_PCIE_PORT_IRQ_EN 0x558 #define UTL_RC_STATUS 0x560 #define UTL_RC_ERR_SEVERITY 0x568 #define UTL_RC_IRQ_EN 0x570 #define UTL_EP_STATUS 0x578 #define UTL_EP_ERR_SEVERITY 0x580 #define UTL_EP_ERR_IRQ_EN 0x588 #define UTL_PCI_PM_CTRL1 0x590 #define UTL_PCI_PM_CTRL2 0x598 #define UTL_GP_CTL1 0x5a0 #define UTL_GP_CTL2 0x5a8 /* PCI-E Stack registers */ #define PHB_PCIE_SYSTEM_CONFIG 0x600 #define PHB_PCIE_BUS_NUMBER 0x608 #define PHB_PCIE_SYSTEM_TEST 0x618 #define PHB_PCIE_LINK_MANAGEMENT 0x630 #define PHB_PCIE_DLP_TRAIN_CTL 0x640 #define PHB_PCIE_DLP_TCTX_DISABLE PPC_BIT(1) #define PHB_PCIE_DLP_TCRX_DISABLED PPC_BIT(16) #define PHB_PCIE_DLP_TC_DL_LINKUP PPC_BIT(21) #define PHB_PCIE_DLP_TC_DL_PGRESET PPC_BIT(22) #define PHB_PCIE_DLP_TC_DL_LINKACT PPC_BIT(23) #define PHB_PCIE_SLOP_LOOPBACK_STATUS 0x648 #define PHB_PCIE_AER_CONTROL 0x650 #define PHB_PCIE_AUX_POWER_CONTROL 0x658 #define PHB_PCIE_SLOTCTL1 0x660 #define PHB_PCIE_SLOTCTL2 0x668 #define PHB_PCIE_SLOTCTL2_SLOTWAKE PPC_BIT(16) #define PHB_PCIE_SLOTCTL2_PWR_EN_STAT PPC_BIT(17) #define PHB_PCIE_SLOTCTL2_RCK_EN_STAT PPC_BIT(18) #define PHB_PCIE_SLOTCTL2_PERST_STAT PPC_BIT(19) #define PHB_PCIE_SLOTCTL2_PLED_S PPC_BITMASK(20,21) /* use PCIE_INDIC_* */ #define PHB_PCIE_SLOTCTL2_ALED_S PPC_BITMASK(22,23) #define PHB_PCIE_SLOTCTL2_PRSTN_STAT PPC_BIT(24) #define PHB_PCIE_SLOTCTL2_PWRFLT_STAT PPC_BIT(25) #define PHB_PCIE_UTL_CONFIG 0x670 #define PHB_PCIE_DLP_CONTROL 0x678 #define PHB_PCIE_UTL_ERRLOG1 0x680 #define PHB_PCIE_UTL_ERRLOG2 0x688 #define PHB_PCIE_UTL_ERRLOG3 0x690 #define PHB_PCIE_UTL_ERRLOG4 0x698 #define PHB_PCIE_DLP_ERRLOG1 0x6a0 #define PHB_PCIE_DLP_ERRLOG2 0x6a8 #define PHB_PCIE_UTL_ERR_INJECT 0x6c0 #define PHB_PCIE_TLDLP_ERR_INJECT 0x6c8 #define PHB_PCIE_STRAPPING 0x700 /* Fundamental register set B */ #define PHB_VERSION 0x800 #define PHB_RESET 0x808 #define PHB_CONTROL 0x810 #define PHB_AIB_RX_CRED_INIT_TIMER 0x818 #define PHB_AIB_RX_CMD_CRED 0x820 #define PHB_AIB_RX_DATA_CRED 0x828 #define PHB_AIB_TX_CMD_CRED 0x830 #define PHB_AIB_TX_DATA_CRED 0x838 #define PHB_AIB_TX_CHAN_MAPPING 0x840 #define PHB_AIB_TX_CRED_SYNC_CTRL 0x848 #define PHB_LEGACY_CTRL 0x850 #define PHB_AIB_TAG_ENABLE 0x858 #define PHB_AIB_FENCE_CTRL 0x860 #define PHB_TCE_TAG_ENABLE 0x868 #define PHB_TCE_WATERMARK 0x870 #define PHB_TIMEOUT_CTRL1 0x878 #define PHB_TIMEOUT_CTRL2 0x880 #define PHB_QUIESCE_DMA_G 0x888 #define PHB_AIB_TAG_STATUS 0x900 #define PHB_TCE_TAG_STATUS 0x908 /* FIR & Error registers */ #define PHB_LEM_FIR_ACCUM 0xc00 #define PHB_LEM_FIR_AND_MASK 0xc08 #define PHB_LEM_FIR_OR_MASK 0xc10 #define PHB_LEM_ERROR_MASK 0xc18 #define PHB_LEM_ERROR_AND_MASK 0xc20 #define PHB_LEM_ERROR_OR_MASK 0xc28 #define PHB_LEM_ACTION0 0xc30 #define PHB_LEM_ACTION1 0xc38 #define PHB_LEM_WOF 0xc40 #define PHB_ERR_STATUS 0xc80 #define PHB_ERR1_STATUS 0xc88 #define PHB_ERR_INJECT 0xc90 #define PHB_ERR_LEM_ENABLE 0xc98 #define PHB_ERR_IRQ_ENABLE 0xca0 #define PHB_ERR_FREEZE_ENABLE 0xca8 #define PHB_ERR_AIB_FENCE_ENABLE 0xcb0 #define PHB_ERR_LOG_0 0xcc0 #define PHB_ERR_LOG_1 0xcc8 #define PHB_ERR_STATUS_MASK 0xcd0 #define PHB_ERR1_STATUS_MASK 0xcd8 #define PHB_OUT_ERR_STATUS 0xd00 #define PHB_OUT_ERR1_STATUS 0xd08 #define PHB_OUT_ERR_INJECT 0xd10 #define PHB_OUT_ERR_LEM_ENABLE 0xd18 #define PHB_OUT_ERR_IRQ_ENABLE 0xd20 #define PHB_OUT_ERR_FREEZE_ENABLE 0xd28 #define PHB_OUT_ERR_AIB_FENCE_ENABLE 0xd30 #define PHB_OUT_ERR_LOG_0 0xd40 #define PHB_OUT_ERR_LOG_1 0xd48 #define PHB_OUT_ERR_STATUS_MASK 0xd50 #define PHB_OUT_ERR1_STATUS_MASK 0xd58 #define PHB_INA_ERR_STATUS 0xd80 #define PHB_INA_ERR1_STATUS 0xd88 #define PHB_INA_ERR_INJECT 0xd90 #define PHB_INA_ERR_LEM_ENABLE 0xd98 #define PHB_INA_ERR_IRQ_ENABLE 0xda0 #define PHB_INA_ERR_FREEZE_ENABLE 0xda8 #define PHB_INA_ERR_AIB_FENCE_ENABLE 0xdb0 #define PHB_INA_ERR_LOG_0 0xdc0 #define PHB_INA_ERR_LOG_1 0xdc8 #define PHB_INA_ERR_STATUS_MASK 0xdd0 #define PHB_INA_ERR1_STATUS_MASK 0xdd8 #define PHB_INB_ERR_STATUS 0xe00 #define PHB_INB_ERR1_STATUS 0xe08 #define PHB_INB_ERR_INJECT 0xe10 #define PHB_INB_ERR_LEM_ENABLE 0xe18 #define PHB_INB_ERR_IRQ_ENABLE 0xe20 #define PHB_INB_ERR_FREEZE_ENABLE 0xe28 #define PHB_INB_ERR_AIB_FENCE_ENABLE 0xe30 #define PHB_INB_ERR_LOG_0 0xe40 #define PHB_INB_ERR_LOG_1 0xe48 #define PHB_INB_ERR_STATUS_MASK 0xe50 #define PHB_INB_ERR1_STATUS_MASK 0xe58 /* Performance monitor & Debug registers */ #define PHB_TRACE_CONTROL 0xf80 #define PHB_PERFMON_CONFIG 0xf88 #define PHB_PERFMON_CTR0 0xf90 #define PHB_PERFMON_CTR1 0xf98 #define PHB_PERFMON_CTR2 0xfa0 #define PHB_PERFMON_CTR3 0xfa8 #define PHB_HOTPLUG_OVERRIDE 0xfb0 /* * IODA tables */ #define IODA_TBL_HRT 0 #define IODA_TBL_LIST 1 #define IODA_TBL_LXIVT 2 #define IODA_TBL_MIST 3 #define IODA_TBL_MXIVT 4 #define IODA_TBL_MVT 5 #define IODA_TBL_PELTM 6 #define IODA_TBL_PESTA 7 #define IODA_TBL_PESTB 8 #define IODA_TBL_TVT 9 #define IODA_TBL_TCAM 10 #define IODA_TBL_TDR 11 #define IODA_TBL_PELTV 12 #define IODA_TBL_M64BT 16 #define IODA_TBL_IODT 17 #define IODA_TBL_M32DT 18 #define IODA_TBL_M64DT 19 #define IODA_TBL_PEEV 20 /* L/M XIVT */ #define IODA_XIVT_SERVER PPC_BITMASK(8,23) #define IODA_XIVT_PRIORITY PPC_BITMASK(24,31) #define IODA_XIVT_PENUM PPC_BITMASK(41,47) #define IODA_XIVT_HUBNUM PPC_BITMASK(58,59) /* M64BT */ #define IODA_M64BT_ENABLE PPC_BIT(0) #define IODA_M64BT_BASE PPC_BITMASK(8,31) #define IODA_M64BT_MASK PPC_BITMASK(40,63) /* IODT/M32DT/M64DX */ #define IODA_XXDT_PE PPC_BITMASK(0,6) /* PELTM */ #define IODA_PELTM_BUS PPC_BITMASK(0,7) #define IODA_PELTM_DEV PPC_BITMASK(8,12) #define IODA_PELTM_FUNC PPC_BITMASK(13,15) #define IODA_PELTM_BUS_VALID PPC_BITMASK(16,18) #define IODA_BUS_VALID_ANY 0 #define IODA_BUS_VALID_3_BITS 2 #define IODA_BUS_VALID_4_BITS 3 #define IODA_BUS_VALID_5_BITS 4 #define IODA_BUS_VALID_6_BITS 5 #define IODA_BUS_VALID_7_BITS 6 #define IODA_BUS_VALID_ALL 7 #define IODA_PELTM_DEV_VALID PPC_BIT(19) #define IODA_PELTM_FUNC_VALID PPC_BIT(20) /* TVT */ #define IODA_TVT0_TABLE_ADDR PPC_BITMASK(0,47) #define IODA_TVT0_BUS_VALID PPC_BITMASK(48,50) #define IODA_TVT0_TCE_TABLE_SIZE PPC_BITMASK(51,55) #define IODA_TVT0_BUS_NUM PPC_BITMASK(56,63) #define IODA_TVT1_DEV_VALID PPC_BIT(2) #define IODA_TVT1_DEV_NUM PPC_BITMASK(3,7) #define IODA_TVT1_HUB_NUM PPC_BITMASK(10,11) #define IODA_TVT1_FUNC_VALID PPC_BIT(12) #define IODA_TVT1_FUNC_NUM PPC_BITMASK(13,15) #define IODA_TVT1_IO_PSIZE PPC_BITMASK(19,23) #define IODA_TVT1_PE_NUM PPC_BITMASK(57,63) /* MVT */ #define IODA_MVT_VALID PPC_BIT(0) #define IODA_MVT_BUS_VALID PPC_BITMASK(21,23) #define IODA_MVT_BUS_NUM PPC_BITMASK(24,31) #define IODA_MVT_PE_NUM PPC_BITMASK(41,47) #define IODA_MVT_DEV_VALID PPC_BIT(50) #define IODA_MVT_DEV_NUM PPC_BITMASK(51,55) #define IODA_MVT_FUNC_VALID PPC_BIT(60) #define IODA_MVT_FUNC_NUM PPC_BITMASK(61,63) /* PESTA */ #define IODA_PESTA_MMIO_FROZEN PPC_BIT(0) #define IODA_PESTA_MMIO_CAUSE PPC_BIT(2) #define IODA_PESTA_CFG_READ PPC_BIT(3) #define IODA_PESTA_CFG_WRITE PPC_BIT(4) #define IODA_PESTA_TTYPE PPC_BITMASK(5,7) #define PESTA_TTYPE_DMA_WRITE 0 #define PESTA_TTYPE_MSI 1 #define PESTA_TTYPE_DMA_READ 2 #define PESTA_TTYPE_DMA_READ_RESP 3 #define PESTA_TTYPE_MMIO_LOAD 4 #define PESTA_TTYPE_MMIO_STORE 5 #define PESTA_TTYPE_OTHER 7 #define IODA_PESTA_CA_RETURN PPC_BIT(8) #define IODA_PESTA_UTL_RTOS_TIMEOUT PPC_BIT(8) /* Same bit as CA return */ #define IODA_PESTA_UR_RETURN PPC_BIT(9) #define IODA_PESTA_UTL_NONFATAL PPC_BIT(10) #define IODA_PESTA_UTL_FATAL PPC_BIT(11) #define IODA_PESTA_TAG_REUSE_ERROR PPC_BIT(12) #define IODA_PESTA_PARITY_UE PPC_BIT(13) #define IODA_PESTA_UTL_CORRECTABLE PPC_BIT(14) #define IODA_PESTA_UTL_INTERRUPT PPC_BIT(15) #define IODA_PESTA_MMIO_XLATE PPC_BIT(16) #define IODA_PESTA_IODA_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */ #define IODA_PESTA_TVT_EXT_ERROR PPC_BIT(17) #define IODA_PESTA_TCE_PAGE_FAULT PPC_BIT(18) #define IODA_PESTA_TCE_ACCESS_FAULT PPC_BIT(19) #define IODA_PESTA_DMA_RESP_TIMEOUT PPC_BIT(20) #define IODA_PESTA_AIB_SIZE_INVALID PPC_BIT(21) #define IODA_PESTA_LEM_BIT PPC_BITMASK(26,31) #define IODA_PESTA_RID PPC_BITMASK(32,47) #define IODA_PESTA_MSI_DATA PPC_BITMASK(48,63) /* PESTB */ #define IODA_PESTB_DMA_STOPPED PPC_BIT(0) #define IODA_PESTB_FAIL_ADDR PPC_BITMASK(3,63) #endif /* __P7IOC_REGS_H */ skiboot-skiboot-5.1.13/include/p7ioc.h000066400000000000000000000337601265204436200175550ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __P7IOC_H #define __P7IOC_H #include #include #include #include /* * Memory windows and BUID assignment * * - GX BAR assignment * * I don't know of any spec here, so we're going to mimmic what * OPAL seems to be doing: * * - BAR 0 : 32M, disabled. We just leave it alone. * - BAR 1 : 8G, enabled. Appears to correspond to the MMIO * space of the IOC itself and the PCI IO space * - BAR 2: 128G, * - BAR 3: 128G, * - BAR 4: 128G, all 3 contiguous, forming a single 368G region * and is used for M32 and M64 PHB windows. * * - Memory map * * MWIN1 = BAR1 (8G) * MWIN2 = BAR2,3,4 (384G) * * MWIN2 is divided into 6 * 4G regions for use by M32's (*) and * 6 * 32G regions for use by M64's. * * (*) The M32 will typically be configured to only 2G or so, however * the OS is in control of that setting, and since we have to reserve * a power of two, we reserve the whole 4G. * * - RGC registers: MWIN1 + 0x00000000 * - PHBn IO space: MWIN1 + 0x01000000 + n * 0x00800000 (8M each) * - PHBn M32 : MWIN2 + n * 0x1_00000000 (4G each) * - PHBn M64 : MWIN2 + (n + 1) * 0x8_00000000 (32G each) * * - BUID map. The RGC has interrupts, each PHB has then its own * interrupts (errors etc...), 4 LSIs and 256 LSIs so * respectively 1 BUID for self, 1 for LSIs and 16 for LSIs * * We keep all BUIDs below 0x10 reserved. They will be used for things * like the PSI controller, the NX unit, etc.. in the P7 chip. * * RGC : 0x010 * PHBn LSI : 0x040 + n * 0x40 ( 1 BUID) * PHBn MSI : 0x060 + n * 0x40 (0x10 BUIDs) * * -> For routing, each PHB gets a block of 0x40 BUIDs: * * from 0x40 * (n + 1) to 0x7f * (n + 1) */ /* Some definitions resulting from the above description * * Note: A better approach might be to read the GX BAR content * and isolate the biggest contiguous windows. From there * we could divide things algorithmically and thus be * less sensitive to a change in the memory map by the FSP */ #define MWIN1_SIZE 0x200000000ul /* MWIN1 is 8G */ #define MWIN2_SIZE 0x6000000000ul /* MWIN2 is 384G */ #define PHB_IO_OFFSET 0x01000000ul /* Offset of PHB IO space in MWIN1 */ #define PHB_IO_SIZE 0x00800000ul #define PHB_M32_OFFSET 0x0ul /* Offset of PHB M32 space in MWIN2 */ #define PHB_M32_SIZE 0x100000000ul #define PHB_M64_OFFSET 0x800000000ul /* Offset of PHB M64 space in MWIN2 */ #define PHB_M64_SIZE 0x800000000ul #define RGC_BUID_OFFSET 0x10 /* Offset of RGC BUID */ #define PHB_BUID_OFFSET 0x40 /* Offset of PHB BUID blocks */ #define PHB_BUID_SIZE 0x40 /* Size of PHB BUID blocks */ #define PHB_BUID_LSI_OFFSET 0x00 /* Offset of LSI in PHB BUID block */ #define PHB_BUID_MSI_OFFSET 0x20 /* Offset of MSI in PHB BUID block */ #define PHB_BUID_MSI_SIZE 0x10 /* Size of PHB MSI BUID block */ #define PHBn_IO_BASE(n) (PHB_IO_OFFSET + (n) * PHB_IO_SIZE) #define PHBn_M32_BASE(n) (PHB_M32_OFFSET + (n) * PHB_M32_SIZE) #define PHBn_M64_BASE(n) (PHB_M64_OFFSET + (n) * PHB_M64_SIZE) #define PHBn_BUID_BASE(n) (PHB_BUID_OFFSET + (n) * PHB_BUID_SIZE) #define BUID_TO_PHB(buid) (((buid) - PHB_BUID_OFFSET) / PHB_BUID_SIZE) /* p7ioc has 6 PHBs */ #define P7IOC_NUM_PHBS 6 /* M32 window setting at boot: * * To allow for DMA, we need to split the 32-bit PCI address space between * MMIO and DMA. For now, we use a 2G/2G split with MMIO at the top. * * Note: The top 64K of the M32 space are used by MSIs. This is not * visible here but need to be conveyed to the OS one way or another * * Note2: The space reserved in the system address space for M32 is always * 4G. That we chose to use a smaller portion of it is not relevant to * the upper levels. To keep things consistent, the offset we apply to * the window start is also applied on the host side. */ #define M32_PCI_START 0x80000000 #define M32_PCI_SIZE 0x80000000 /* PHB registers exist in both a hard coded space and a programmable * AIB space. We program the latter to the values recommended in the * documentation: * * 0x80000 + n * 0x10000 */ #define PHBn_ASB_BASE(n) (((n) << 16)) #define PHBn_ASB_SIZE 0x10000ul #define PHBn_AIB_BASE(n) (0x80000ul + ((n) << 16)) #define PHBn_AIB_SIZE 0x10000ul /* * LSI interrupts * * The LSI interrupt block supports 8 interrupts. 4 of them are the * standard PCIe INTA..INTB. The rest is for additional functions * of the PHB */ #define PHB_LSI_PCIE_INTA 0 #define PHB_LSI_PCIE_INTB 1 #define PHB_LSI_PCIE_INTC 2 #define PHB_LSI_PCIE_INTD 3 #define PHB_LSI_PCIE_HOTPLUG 4 #define PHB_LSI_PCIE_PERFCTR 5 #define PHB_LSI_PCIE_UNUSED 6 #define PHB_LSI_PCIE_ERROR 7 /* * State structure for a PHB on P7IOC */ /* * The PHB State structure is essentially used during PHB reset * or recovery operations to indicate that the PHB cannot currently * be used for normal operations. * * Some states involve waiting for the timebase to reach a certain * value. In which case the field "delay_tgt_tb" is set and the * state machine will be run from the "state_poll" callback. * * At IPL time, we call this repeatedly during the various sequences * however under OS control, this will require a change in API. * * Fortunately, the OPAL API for slot power & reset are not currently * used by Linux, so changing them isn't going to be an issue. The idea * here is that some of these APIs will return a positive integer when * needing such a delay to proceed. The OS will then be required to * call a new function opal_poll_phb() after that delay. That function * will potentially return a new delay, or OPAL_SUCCESS when the original * operation has completed successfully. If the operation has completed * with an error, then opal_poll_phb() will return that error. * * Note: Should we consider also returning optionally some indication * of what operation is in progress for OS debug/diag purposes ? * * Any attempt at starting a new "asynchronous" operation while one is * already in progress will result in an error. * * Internally, this is represented by the state being P7IOC_PHB_STATE_FUNCTIONAL * when no operation is in progress, which it reaches at the end of the * boot time initializations. Any attempt at performing a slot operation * on a PHB in that state will change the state to the corresponding * operation state machine. Any attempt while not in that state will * return an error. * * Some operations allow for a certain amount of retries, this is * provided for by the "retries" structure member for use by the state * machine as it sees fit. */ enum p7ioc_phb_state { /* First init state */ P7IOC_PHB_STATE_UNINITIALIZED, /* During PHB HW inits */ P7IOC_PHB_STATE_INITIALIZING, /* Set if the PHB is for some reason unusable */ P7IOC_PHB_STATE_BROKEN, /* Set if the PHB is fenced due to an error */ P7IOC_PHB_STATE_FENCED, /* PHB turned off by FSP (no clocks) */ P7IOC_PHB_STATE_OFF, /* Slot Power up state machine */ P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY, /* Step 3 Delay 2s */ P7IOC_PHB_STATE_SPUP_SLOT_STATUS, /* Step 4 waiting for status */ /* Slot Power down state machine */ P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY, /* Step 2 Delay 2s */ P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS, /* Step 3 waiting for status */ /* Fundamental reset sequence */ P7IOC_PHB_STATE_FRESET_DISABLE_LINK, /* Disable link training */ P7IOC_PHB_STATE_FRESET_ASSERT_DELAY, /* Delay on fundamental reset assert */ P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY, /* Delay on fundamental reset deassert */ P7IOC_PHB_STATE_FRESET_WAIT_LINK, /* Wait for link up */ /* Hot Reset sequence */ P7IOC_PHB_STATE_HRESET_DISABLE_LINK, /* Disable Link training */ P7IOC_PHB_STATE_HRESET_ASSERT, /* Hot reset assert */ P7IOC_PHB_STATE_HRESET_DELAY, /* Hot reset delay */ P7IOC_PHB_STATE_HRESET_ENABLE_LINK, /* Enable Link training */ P7IOC_PHB_STATE_HRESET_WAIT_LINK, /* Wait link traing */ /* Normal PHB functional state */ P7IOC_PHB_STATE_FUNCTIONAL, }; /* * In order to support error detection and recovery on different * types of IOCs (e.g. P5IOC, P7IOC, P8IOC), the best bet would * be make the implementation to be 2 layers: OPAL layer and IOC * layer. The OPAL layer just handles the general information and * IOC layer should process much more detailed information, which * is sensitive to itself. */ #define P7IOC_ERR_SRC_NONE 0 #define P7IOC_ERR_SRC_EI 1 #define P7IOC_ERR_SRC_RGC 2 #define P7IOC_ERR_SRC_BI_UP 3 #define P7IOC_ERR_SRC_BI_DOWN 4 #define P7IOC_ERR_SRC_CI_P0 5 #define P7IOC_ERR_SRC_CI_P1 6 #define P7IOC_ERR_SRC_CI_P2 7 #define P7IOC_ERR_SRC_CI_P3 8 #define P7IOC_ERR_SRC_CI_P4 9 #define P7IOC_ERR_SRC_CI_P5 10 #define P7IOC_ERR_SRC_CI_P6 11 #define P7IOC_ERR_SRC_CI_P7 12 #define P7IOC_ERR_SRC_PHB0 13 #define P7IOC_ERR_SRC_PHB1 14 #define P7IOC_ERR_SRC_PHB2 15 #define P7IOC_ERR_SRC_PHB3 16 #define P7IOC_ERR_SRC_PHB4 17 #define P7IOC_ERR_SRC_PHB5 18 #define P7IOC_ERR_SRC_MISC 19 #define P7IOC_ERR_SRC_I2C 20 #define P7IOC_ERR_SRC_LAST 21 #define P7IOC_ERR_CLASS_NONE 0 #define P7IOC_ERR_CLASS_GXE 1 #define P7IOC_ERR_CLASS_PLL 2 #define P7IOC_ERR_CLASS_RGA 3 #define P7IOC_ERR_CLASS_PHB 4 #define P7IOC_ERR_CLASS_ER 5 #define P7IOC_ERR_CLASS_INF 6 #define P7IOC_ERR_CLASS_MAL 7 #define P7IOC_ERR_CLASS_LAST 8 /* * P7IOC error descriptor. For errors from PHB and PE, they * will be cached to the corresponding PHBs. However, the * left errors (e.g. EI, CI Port0/1) will be cached to the * IOC directly. */ struct p7ioc_err { uint32_t err_src; uint32_t err_class; uint32_t err_bit; }; struct p7ioc; #define P7IOC_PHB_CFG_USE_ASB 0x00000001 /* ASB to access PCI-CFG */ #define P7IOC_PHB_CFG_BLOCKED 0x00000002 /* PCI-CFG blocked except 0 */ #define P7IOC_RESTORE_BUS_NUM 0x00000004 /* Restore buses after reset */ struct p7ioc_phb { uint8_t index; /* 0..5 index inside p7ioc */ uint8_t gen; uint32_t flags; #define P7IOC_REV_DD10 0x00a20001 #define P7IOC_REV_DD11 0x00a20002 uint32_t rev; /* Both major and minor have 2 bytes */ void *regs_asb; void *regs; /* AIB regs */ struct lock lock; uint32_t buid_lsi; uint32_t buid_msi; uint64_t io_base; uint64_t m32_base; uint64_t m64_base; enum p7ioc_phb_state state; uint64_t delay_tgt_tb; uint64_t retries; int64_t ecap; /* cached PCI-E cap offset */ int64_t aercap; /* cached AER ecap offset */ uint64_t lxive_cache[8]; uint64_t mxive_cache[256]; uint64_t mve_cache[256]; uint64_t peltm_cache[128]; uint64_t peltv_lo_cache[128]; uint64_t peltv_hi_cache[128]; uint64_t tve_lo_cache[128]; uint64_t tve_hi_cache[128]; uint64_t iod_cache[128]; uint64_t m32d_cache[128]; uint64_t m64b_cache[16]; uint64_t m64d_cache[128]; bool err_pending; struct p7ioc_err err; struct p7ioc *ioc; struct phb phb; }; static inline struct p7ioc_phb *phb_to_p7ioc_phb(struct phb *phb) { return container_of(phb, struct p7ioc_phb, phb); } static inline bool p7ioc_phb_err_pending(struct p7ioc_phb *p) { return p->err_pending; } static inline void p7ioc_phb_set_err_pending(struct p7ioc_phb *p, bool pending) { if (!pending) { p->err.err_src = P7IOC_ERR_SRC_NONE; p->err.err_class = P7IOC_ERR_CLASS_NONE; p->err.err_bit = -1; } p->err_pending = pending; } /* * State structure for P7IOC IO HUB */ struct p7ioc { /* Device node */ struct dt_node *dt_node; /* MMIO regs */ void *regs; /* Main MMIO window from GX for registers & PCI IO space */ uint64_t mmio1_win_start; uint64_t mmio1_win_size; /* Secondary MMIO window for PCI MMIO space */ uint64_t mmio2_win_start; uint64_t mmio2_win_size; /* BUID base for the PHB. This does include the top bits * (chip, GX bus ID, etc...). This is initialized from the * SPIRA. It does not contain the offset 0x10 for RGC * interrupts. * * The OPAL-defined "interrupt-base" property will contain * the RGC BUID, not this base value, since this is the real * starting point of interrupts for the IOC and we don't want * to cover the BUID 0..f gap which is reserved for P7 on-chip * interrupt sources. */ uint32_t buid_base; uint32_t rgc_buid; /* XIVT cache for RGC interrupts */ uint64_t xive_cache[16]; bool err_pending; struct p7ioc_err err; /* PHB array & presence detect */ struct p7ioc_phb phbs[P7IOC_NUM_PHBS]; uint8_t phb_pdt; struct io_hub hub; }; static inline struct p7ioc *iohub_to_p7ioc(struct io_hub *hub) { return container_of(hub, struct p7ioc, hub); } static inline bool p7ioc_err_pending(struct p7ioc *ioc) { return ioc->err_pending; } static inline void p7ioc_set_err_pending(struct p7ioc *ioc, bool pending) { if (!pending) { ioc->err.err_src = P7IOC_ERR_SRC_NONE; ioc->err.err_class = P7IOC_ERR_CLASS_NONE; ioc->err.err_bit = -1; } ioc->err_pending = pending; } static inline bool p7ioc_phb_enabled(struct p7ioc *ioc, unsigned int phb) { return !!(ioc->phb_pdt & (0x80 >> phb)); } extern int64_t p7ioc_inits(struct p7ioc *ioc); extern void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index); extern int64_t p7ioc_phb_init(struct p7ioc_phb *p); extern bool p7ioc_check_LEM(struct p7ioc *ioc, uint16_t *pci_error_type, uint16_t *severity); extern int64_t p7ioc_phb_get_xive(struct p7ioc_phb *p, uint32_t isn, uint16_t *server, uint8_t *prio); extern int64_t p7ioc_phb_set_xive(struct p7ioc_phb *p, uint32_t isn, uint16_t server, uint8_t prio); extern void p7ioc_reset(struct io_hub *hub); extern void p7ioc_phb_reset(struct phb *phb); #endif /* __P7IOC_H */ skiboot-skiboot-5.1.13/include/pci-cfg.h000066400000000000000000000457101265204436200200420ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * PCI Configuration space definitions */ #ifndef __PCI_CFG_H #define __PCI_CFG_H /* Common cfg space header */ #define PCI_CFG_VENDOR_ID 0x0000 #define PCI_CFG_DEVICE_ID 0x0002 #define PCI_CFG_CMD 0x0004 #define PCI_CFG_CMD_IO_EN 0x0001 #define PCI_CFG_CMD_MEM_EN 0x0002 #define PCI_CFG_CMD_BUS_MASTER_EN 0x0004 #define PCI_CFG_CMD_PERR_RESP 0x0040 #define PCI_CFG_CMD_SERR_EN 0x0100 #define PCI_CFG_CMD_INTx_DIS 0x0400 #define PCI_CFG_STAT 0x0006 #define PCI_CFG_STAT_INTx 0x0008 #define PCI_CFG_STAT_CAP 0x0010 #define PCI_CFG_STAT_MDATAPERR 0x0100 #define PCI_CFG_STAT_SENT_TABORT 0x0800 #define PCI_CFG_STAT_RECV_TABORT 0x1000 #define PCI_CFG_STAT_RECV_MABORT 0x2000 #define PCI_CFG_STAT_SENT_SERR 0x4000 #define PCI_CFG_STAT_RECV_PERR 0x8000 #define PCI_CFG_REV_ID 0x0008 #define PCI_CFG_CLAS_CODE 0x0009 #define PCI_CFG_CACHE_LINE_SIZE 0x000c #define PCI_CFG_LAT_TIMER 0x000d #define PCI_CFG_HDR_TYPE 0x000e #define PCI_CFG_BIST 0x000f #define PCI_CFG_BAR0 0x0010 #define PCI_CFG_BAR_TYPE 0x00000001 #define PCI_CFG_BAR_TYPE_MEM 0x00000000 #define PCI_CFG_BAR_TYPE_IO 0x00000001 #define PCI_CFG_BAR_MEM64 0x00000004 #define PCI_CFG_BAR_MEM_PREFETCH 0x00000008 #define PCI_CFG_BAR1 0x0014 /* Type 0 fields */ #define PCI_CFG_BAR2 0x0018 #define PCI_CFG_BAR3 0x001c #define PCI_CFG_BAR4 0x0020 #define PCI_CFG_BAR5 0x0024 #define PCI_CFG_CARDBUS_CIS 0x0028 #define PCI_CFG_SUBSYS_VENDOR_ID 0x002c #define PCI_CFG_SUBSYS_ID 0x002e #define PCI_CFG_ROMBAR 0x0030 #define PCI_CFG_CAP 0x0034 #define PCI_CFG_INT_LINE 0x003c #define PCI_CFG_INT_PIN 0x003d #define PCI_CFG_INT_MIN_GNT 0x003e #define PCI_CFG_INT_MAX_LAT 0x003f /* Type 1 fields */ #define PCI_CFG_PRIMARY_BUS 0x0018 #define PCI_CFG_SECONDARY_BUS 0x0019 #define PCI_CFG_SUBORDINATE_BUS 0x001a #define PCI_CFG_SEC_LAT_TIMER 0x001b #define PCI_CFG_IO_BASE 0x001c #define PCI_CFG_IO_LIMIT 0x001d #define PCI_CFG_SECONDARY_STATUS 0x001e #define PCI_CFG_MEM_BASE 0x0020 #define PCI_CFG_MEM_LIMIT 0x0022 #define PCI_CFG_PREF_MEM_BASE 0x0024 #define PCI_CFG_PREF_MEM_LIMIT 0x0026 #define PCI_CFG_PREF_MEM_BASE_U32 0x0028 #define PCI_CFG_PREF_MEM_LIMIT_U32 0x002c #define PCI_CFG_IO_BASE_U16 0x0030 #define PCI_CFG_IO_LIMIT_U16 0x0032 #define PCI_CFG_BR_CAP 0x0034 /* Same as type 0 */ #define PCI_CFG_BR_ROMBAR 0x0038 /* Different from type 0 */ #define PCI_CFG_BR_INT_LINE 0x003c /* Same as type 0 */ #define PCI_CFG_BR_INT_PIN 0x003d /* Same as type 0 */ #define PCI_CFG_BRCTL 0x003e #define PCI_CFG_BRCTL_PERR_RESP_EN 0x0001 #define PCI_CFG_BRCTL_SERR_EN 0x0002 #define PCI_CFG_BRCTL_ISA_EN 0x0004 #define PCI_CFG_BRCTL_VGA_EN 0x0008 #define PCI_CFG_BRCTL_VGA_16BIT 0x0010 #define PCI_CFG_BRCTL_MABORT_REPORT 0x0020 #define PCI_CFG_BRCTL_SECONDARY_RESET 0x0040 #define PCI_CFG_BRCTL_FAST_BACK2BACK 0x0080 #define PCI_CFG_BRCTL_PRI_DISC_TIMER 0x0100 #define PCI_CFG_BRCTL_SEC_DISC_TIMER 0x0200 #define PCI_CFG_BRCTL_DISC_TIMER_STAT 0x0400 #define PCI_CFG_BRCTL_DISC_TIMER_SERR 0x0800 /* * Standard capabilties */ #define PCI_CFG_CAP_ID 0 #define PCI_CFG_CAP_NEXT 1 /* PCI bridge subsystem ID capability */ #define PCI_CFG_CAP_ID_SUBSYS_VID 0x0d #define PCICAP_SUBSYS_VID_VENDOR 4 #define PCICAP_SUBSYS_VID_DEVICE 6 /* PCI Express capability */ #define PCI_CFG_CAP_ID_EXP 0x10 /* PCI Express capability fields */ #define PCICAP_EXP_CAPABILITY_REG 0x02 #define PCICAP_EXP_CAP_VERSION 0x000f #define PCICAP_EXP_CAP_TYPE 0x00f0 #define PCIE_TYPE_ENDPOINT 0x0 #define PCIE_TYPE_LEGACY 0x1 #define PCIE_TYPE_ROOT_PORT 0x4 #define PCIE_TYPE_SWITCH_UPPORT 0x5 #define PCIE_TYPE_SWITCH_DNPORT 0x6 #define PCIE_TYPE_PCIE_TO_PCIX 0x7 #define PCIE_TYPE_PCIX_TO_PCIE 0x8 #define PCIE_TYPE_RC_INTEGRATED 0x9 #define PCIE_TYPE_RC_EVT_COLL 0xa #define PCICAP_EXP_CAP_SLOT 0x0100 #define PCICAP_EXP_CAP_MSI_NUM 0x3e00 #define PCICAP_EXP_CAP_TCS_ROUTING 0x4000 #define PCICAP_EXP_DEVCAP 0x04 #define PCICAP_EXP_DEVCAP_MPSS 0x00000007 #define PCIE_MPSS_128 0 #define PCIE_MPSS_256 1 #define PCIE_MPSS_512 2 #define PCIE_MPSS_1024 3 #define PCIE_MPSS_2048 4 #define PCIE_MPSS_4096 5 #define PCICAP_EXP_DEVCAP_PHANT 0x00000018 #define PCIE_PHANTOM_NONE 0 #define PCIE_PHANTOM_1MSB 1 #define PCIE_PHANTOM_2MSB 2 #define PCIE_PHANTOM_3MSB 3 #define PCICAP_EXP_DEVCAP_EXTTAG 0x00000020 #define PCICAP_EXP_DEVCAP_L0SL 0x000001c0 #define PCIE_L0SL_MAX_64NS 0 #define PCIE_L0SL_MAX_128NS 1 #define PCIE_L0SL_MAX_256NS 2 #define PCIE_L0SL_MAX_512NS 3 #define PCIE_L0SL_MAX_1US 4 #define PCIE_L0SL_MAX_2US 5 #define PCIE_L0SL_MAX_4US 6 #define PCIE_L0SL_MAX_NO_LIMIT 7 #define PCICAP_EXP_DEVCAP_L1L 0x00000e00 #define PCIE_L1L_MAX_1US 0 #define PCIE_L1L_MAX_2US 1 #define PCIE_L1L_MAX_4US 2 #define PCIE_L1L_MAX_8US 3 #define PCIE_L1L_MAX_16US 4 #define PCIE_L1L_MAX_32US 5 #define PCIE_L1L_MAX_64US 6 #define PCIE_L1L_MAX_NO_LIMIT 7 #define PCICAP_EXP_ROLE_BASED_ERR 0x00008000 #define PCICAP_EXP_DEVCAP_PWRVAL 0x03fc0000 #define PCICAP_EXP_DEVCAP_PWRSCA 0x0c000000 #define PCIE_SLOT_PWR_SCALE_1x 0 #define PCIE_SLOT_PWR_SCALE_0d1x 1 #define PCIE_SLOT_PWR_SCALE_0d01x 2 #define PCIE_SLOT_PWR_SCALE_0d001x 3 #define PCICAP_EXP_DEVCAP_FUNC_RESET 0x10000000 #define PCICAP_EXP_DEVCTL 0x08 #define PCICAP_EXP_DEVCTL_CE_REPORT 0x0001 #define PCICAP_EXP_DEVCTL_NFE_REPORT 0x0002 #define PCICAP_EXP_DEVCTL_FE_REPORT 0x0004 #define PCICAP_EXP_DEVCTL_UR_REPORT 0x0008 #define PCICAP_EXP_DEVCTL_RELAX_ORD 0x0010 #define PCICAP_EXP_DEVCTL_MPS 0x00e0 #define PCIE_MPS_128B 0 #define PCIE_MPS_256B 1 #define PCIE_MPS_512B 2 #define PCIE_MPS_1024B 3 #define PCIE_MPS_2048B 4 #define PCIE_MPS_4096B 5 #define PCICAP_EXP_DEVCTL_EXT_TAG 0x0100 #define PCICAP_EXP_DEVCTL_PHANTOM 0x0200 #define PCICAP_EXP_DEVCTL_AUX_POW_PM 0x0400 #define PCICAP_EXP_DEVCTL_NO_SNOOP 0x0800 #define PCICAP_EXP_DEVCTL_MRRS 0x7000 #define PCIE_MRSS_128B 0 #define PCIE_MRSS_256B 1 #define PCIE_MRSS_512B 2 #define PCIE_MRSS_1024B 3 #define PCIE_MRSS_2048B 4 #define PCIE_MRSS_4096B 5 #define PCICAP_EXP_DEVCTL_PCIX_RETRY 0x8000 /* PCIe - PCIX bridges only */ #define PCICAP_EXP_DEVCTL_FUNC_RESET 0x8000 /* all others */ #define PCICAP_EXP_DEVSTAT 0x0a #define PCICAP_EXP_DEVSTAT_CE 0x0001 #define PCICAP_EXP_DEVSTAT_NFE 0x0002 #define PCICAP_EXP_DEVSTAT_FE 0x0004 #define PCICAP_EXP_DEVSTAT_UE 0x0008 #define PCICAP_EXP_DEVSTAT_AUX_POW 0x0010 #define PCICAP_EXP_DEVSTAT_TPEND 0x0020 #define PCICAP_EXP_LCAP 0x0c #define PCICAP_EXP_LCAP_MAXSPD 0x0000000f #define PCIE_LSPEED_VECBIT_0 0x1 #define PCIE_LSPEED_VECBIT_1 0x2 #define PCIE_LSPEED_VECBIT_2 0x3 #define PCIE_LSPEED_VECBIT_3 0x4 #define PCIE_LSPEED_VECBIT_4 0x5 #define PCIE_LSPEED_VECBIT_5 0x6 #define PCIE_LSPEED_VECBIT_6 0x7 #define PCICAP_EXP_LCAP_MAXWDTH 0x000003f0 #define PCIE_LWIDTH_1X 1 #define PCIE_LWIDTH_2X 2 #define PCIE_LWIDTH_4X 4 #define PCIE_LWIDTH_8X 8 #define PCIE_LWIDTH_12X 12 #define PCIE_LWIDTH_16X 16 #define PCIE_LWIDTH_32X 32 #define PCICAP_EXP_LCAP_ASPM_L0S 0x00000400 #define PCICAP_EXP_LCAP_ASPM_L1 0x00000800 #define PCICAP_EXP_LCAP_L0S_EXLT 0x00007000 #define PCIE_L0S_EXLT_LESS_64NS 0 #define PCIE_L0S_EXLT_64NS_128NS 1 #define PCIE_L0S_EXLT_128NS_256NS 2 #define PCIE_L0S_EXLT_256NS_512NS 3 #define PCIE_L0S_EXLT_512NS_1US 4 #define PCIE_L0S_EXLT_1US_2US 5 #define PCIE_L0S_EXLT_2US_4US 6 #define PCIE_L0S_EXLT_MORE_4US 7 #define PCICAP_EXP_LCAP_L1_EXLT 0x00038000 #define PCIE_L1_EXLT_LESS_1US 0 #define PCIE_L1_EXLT_1US_2US 1 #define PCIE_L1_EXLT_2US_4US 2 #define PCIE_L1_EXLT_4US_8US 3 #define PCIE_L1_EXLT_8US_16US 4 #define PCIE_L1_EXLT_16US_32US 5 #define PCIE_L1_EXLT_32US_64US 6 #define PCIE_L1_EXLT_MORE_64US 7 #define PCICAP_EXP_LCAP_CLK_PM 0x00040000 #define PCICAP_EXP_LCAP_SURP_DWN_ERR 0x00080000 #define PCICAP_EXP_LCAP_DL_ACT_REP 0x00100000 #define PCICAP_EXP_LCAP_LNKBWDTH_NOTF 0x00200000 #define PCICAP_EXP_LCAP_ASPM_OPT_CMPL 0x00400000 #define PCICAP_EXP_LCAP_PORTNUM 0xff000000 #define PCICAP_EXP_LCTL 0x10 #define PCICAP_EXP_LCTL_ASPM_L0S 0x0001 #define PCICAP_EXP_LCTL_ASPM_L1 0x0002 #define PCICAP_EXP_LCTL_RCB 0x0008 /* RO on root ports */ #define PCICAP_EXP_LCTL_LINK_DIS 0x0010 #define PCICAP_EXP_LCTL_LINK_RETRAIN 0x0020 #define PCICAP_EXP_LCTL_COMMON_CLK 0x0040 #define PCICAP_EXP_LCTL_EXT_SYNCH 0x0080 #define PCICAP_EXP_LCTL_CLOCK_PM 0x0100 #define PCICAP_EXP_LCTL_HW_AWIDTH_DIS 0x0200 #define PCICAP_EXP_LCTL_LBWM_INT_EN 0x0400 #define PCICAP_EXP_LCTL_LAWD_INT_EN 0x0800 #define PCICAP_EXP_LSTAT 0x12 #define PCICAP_EXP_LSTAT_SPEED 0x000f /* use PCIE_LSPEED_* consts */ #define PCICAP_EXP_LSTAT_WIDTH 0x03f0 /* use PCIE_LWIDTH_* consts */ #define PCICAP_EXP_LSTAT_TRAINING 0x0800 #define PCICAP_EXP_LSTAT_SLOTCLKCFG 0x1000 #define PCICAP_EXP_LSTAT_DLLL_ACT 0x2000 #define PCICAP_EXP_LSTAT_LBWM_STAT 0x4000 #define PCICAP_EXP_LSTAT_LAWS_STAT 0x8000 #define PCICAP_EXP_SLOTCAP 0x14 #define PCICAP_EXP_SLOTCAP_ATTNB 0x00000001 #define PCICAP_EXP_SLOTCAP_PWCTRL 0x00000002 #define PCICAP_EXP_SLOTCAP_MRLSENS 0x00000004 #define PCICAP_EXP_SLOTCAP_ATTNI 0x00000008 #define PCICAP_EXP_SLOTCAP_PWRI 0x00000010 #define PCICAP_EXP_SLOTCAP_HPLUG_SURP 0x00000020 #define PCICAP_EXP_SLOTCAP_HPLUG_CAP 0x00000040 #define PCICAP_EXP_SLOTCAP_SPLVA 0x00007f80 #define PCICAP_EXP_SLOTCAP_SPLSC 0x00018000 #define PCICAP_EXP_SLOTCAP_EIP 0x00020000 #define PCICAP_EXP_SLOTCAP_NO_CMDCOMP 0x00040000 #define PCICAP_EXP_SLOTCAP_PSLOT 0xfff80000 #define PCICAP_EXP_SLOTCTL 0x18 #define PCICAP_EXP_SLOTCTL_ATTNB 0x0001 #define PCICAP_EXP_SLOTCTL_PFLT 0x0002 #define PCICAP_EXP_SLOTCTL_MRLSENSE 0x0004 #define PCICAP_EXP_SLOTCTL_PDETECT 0x0008 #define PCICAP_EXP_SLOTCTL_CMDCOMPINT 0x0010 #define PCICAP_EXP_SLOTCTL_HPINT 0x0020 #define PCICAP_EXP_SLOTCTL_ATTNI 0x00c0 #define PCIE_INDIC_ON 1 #define PCIE_INDIC_BLINK 2 #define PCIE_INDIC_OFF 3 #define PCICAP_EXP_SLOTCTL_PWRI 0x0300 /* Use PCIE_INDIC_* consts */ #define PCICAP_EXP_SLOTCTL_PWRCTLR 0x0400 #define PCICAP_EXP_SLOTCTL_EIC 0x0800 #define PCICAP_EXP_SLOTCTL_DLLSTCHG 0x1000 #define PCICAP_EXP_SLOTSTAT 0x1a #define PCICAP_EXP_SLOTSTAT_ATTNBCH 0x0001 #define PCICAP_EXP_SLOTSTAT_PWRFLTCH 0x0002 #define PCICAP_EXP_SLOTSTAT_MRLSENSCH 0x0004 #define PCICAP_EXP_SLOTSTAT_PDETECTCH 0x0008 #define PCICAP_EXP_SLOTSTAT_CMDCOMPCH 0x0010 #define PCICAP_EXP_SLOTSTAT_MRLSENSST 0x0020 #define PCICAP_EXP_SLOTSTAT_PDETECTST 0x0040 #define PCICAP_EXP_SLOTSTAT_EIS 0x0080 #define PCICAP_EXP_SLOTSTAT_DLLSTCH 0x0100 #define PCICAP_EXP_RC 0x1c #define PCICAP_EXP_RC_SYSERR_ON_CE 0x0001 #define PCICAP_EXP_RC_SYSERR_ON_NFE 0x0002 #define PCICAP_EXP_RC_SYSERR_ON_FE 0x0004 #define PCICAP_EXP_RC_PME_INT_EN 0x0008 #define PCICAP_EXP_RC_CRS_VISIBLE 0x0010 #define PCICAP_EXP_RCAP 0x1e #define PCICAP_EXP_RCAP_CRS_VISIBLE 0x0001 #define PCICAP_EXP_RSTAT 0x20 #define PCICAP_EXP_RSTAT_PME_RID 0x0000ffff #define PCICAP_EXP_RSTAT_PME_STATUS 0x00010000 #define PCICAP_EXP_RSTAT_PME_PENDING 0x00020000 #define PCIECAP_EXP_DCAP2 0x24 #define PCICAP_EXP_DCAP2_CMPTOUT 0x0000000f #define PCICAP_EXP_DCAP2_CMPTOUT_DIS 0x00000010 #define PCICAP_EXP_DCAP2_ARI_FWD 0x00000020 #define PCICAP_EXP_DCAP2_ATOMIC_RTE 0x00000040 #define PCICAP_EXP_DCAP2_ATOMIC32 0x00000080 #define PCICAP_EXP_DCAP2_ATOMIC64 0x00000100 #define PCICAP_EXP_DCAP2_CAS128 0x00000200 #define PCICAP_EXP_DCAP2_NORO_PRPR 0x00000400 #define PCICAP_EXP_DCAP2_LTR 0x00000800 #define PCICAP_EXP_DCAP2_TPHCOMP 0x00001000 #define PCICAP_EXP_DCAP2_TPHCOMP_EXT 0x00002000 #define PCICAP_EXP_DCAP2_OBFF_MSG 0x00040000 #define PCICAP_EXP_DCAP2_OBFF_WAKE 0x00080000 #define PCICAP_EXP_DCAP2_EXTFMT 0x00100000 #define PCICAP_EXP_DCAP2_EETLP_PFX 0x00200000 #define PCICAP_EXP_DCAP2_MAXEETP 0x00c00000 #define PCIE_EETLPP_1 1 #define PCIE_EETLPP_2 2 #define PCIE_EETLPP_3 3 #define PCIE_EETLPP_4 0 #define PCICAP_EXP_DCTL2 0x28 #define PCICAP_EXP_DCTL2_CMPTOUT 0x000f #define PCICAP_EXP_DCTL2_CMPTOUT_DIS 0x0010 #define PCICAP_EXP_DCTL2_ARI_FWD 0x0020 #define PCICAP_EXP_DCTL2_ATOMIC_REQ 0x0040 #define PCICAP_EXP_DCTL2_ATOMIC_EGBLK 0x0080 #define PCICAP_EXP_DCTL2_IDO_REQ 0x0100 #define PCICAP_EXP_DCTL2_IDO_COMPL 0x0200 #define PCICAP_EXP_DCTL2_LTR 0x0400 #define PCICAP_EXP_DCTL2_OBFF 0x6000 #define PCIE_OBFF_MODE_DISABLED 0 #define PCIE_OBFF_MODE_MSG_A 1 #define PCIE_OBFF_MODE_MSG_B 2 #define PCIE_OBFF_MODE_WAKE 3 #define PCICAP_EXP_DCTL2_EETLPP_BLK 0x8000 #define PCICAP_EXP_DSTA2 0x2a #define PCICAP_EXP_LCAP2 0x2c #define PCICAP_EXP_LCAP2_SP_2d5GTs 0x00000002 #define PCICAP_EXP_LCAP2_SP_5d0GTs 0x00000004 #define PCICAP_EXP_LCAP2_SP_8d0GTs 0x00000008 #define PCICAP_EXP_LCAP2_XLINK 0x00000100 #define PCICAP_EXP_LCTL2 0x30 #define PCICAP_EXP_LCTL2_TLSPD 0x000f /* use PCIE_LSPEED_ consts */ #define PCICAP_EXP_LCTL2_ENTER_COMPL 0x0010 #define PCICAP_EXP_LCTL2_HWAUTSPDIS 0x0020 #define PCICAP_EXP_LCTL2_SEL_DEEMPH 0x0040 #define PCICAP_EXP_LCTL2_XMTMARG 0x0380 #define PCICAP_EXP_LCTL2_ENTER_MCOMPL 0x0400 #define PCICAP_EXP_LCTL2_COMPL_SOS 0x0800 #define PCICAP_EXP_LCTL2_CMPPDEM 0xf000 #define PCICAP_EXP_LSTA2 0x32 #define PCICAP_EXP_LSTA2_DEMPH_LVL 0x0001 #define PCICAP_EXP_LSTA2_EQ_COMPLETE 0x0002 #define PCICAP_EXP_LSTA2_EQ_PH1_OK 0x0004 #define PCICAP_EXP_LSTA2_EQ_PH2_OK 0x0008 #define PCICAP_EXP_LSTA2_EQ_PH3_OK 0x0010 #define PCICAP_EXP_LSTA2_LINK_EQ_REQ 0x0020 #define PCICAP_EXP_SCAP2 0x34 #define PCICAP_EXP_SCTL2 0x38 #define PCICAP_EXP_SSTA2 0x3a /* * PCI-E Extended capabilties */ #define PCI_CFG_ECAP_START 0x100 #define PCI_CFG_ECAP_ID 0x0000ffff #define PCI_CFG_ECAP_VERS 0x000f0000 #define PCI_CFG_ECAP_NEXT 0xfff00000 /* AER Ext. Capability */ #define PCIECAP_ID_AER 0x0001 #define PCIECAP_AER_UE_STATUS 0x04 #define PCIECAP_AER_UE_DLP 0x00000010 #define PCIECAP_AER_UE_SURPRISE_DOWN 0x00000020 #define PCIECAP_AER_UE_POISON_TLP 0x00001000 #define PCIECAP_AER_UE_FLOW_CTL_PROT 0x00002000 #define PCIECAP_AER_UE_COMPL_TIMEOUT 0x00004000 #define PCIECAP_AER_UE_COMPL_ABORT 0x00008000 #define PCIECAP_AER_UE_UNEXP_COMPL 0x00010000 #define PCIECAP_AER_UE_RECV_OVFLOW 0x00020000 #define PCIECAP_AER_UE_MALFORMED_TLP 0x00040000 #define PCIECAP_AER_UE_ECRC 0x00080000 #define PCIECAP_AER_UE_UNSUPP_REQ 0x00100000 #define PCIECAP_AER_UE_ACS_VIOLATION 0x00200000 #define PCIECAP_AER_UE_INTERNAL 0x00400000 #define PCIECAP_AER_UE_MC_BLKD_TLP 0x00800000 #define PCIECAP_AER_UE_ATOMIC_EGBLK 0x01000000 #define PCIECAP_AER_UE_TLP_PRFX_BLK 0x02000000 #define PCIECAP_AER_UE_MASK 0x08 #define PCIECAP_AER_UE_MASK_DLLP 0x00000010 #define PCIECAP_AER_UE_MASK_SURPRISE_DOWN 0x00000020 #define PCIECAP_AER_UE_MASK_POISON_TLP 0x00001000 #define PCIECAP_AER_UE_MASK_FLOW_CTL_PROT 0x00002000 #define PCIECAP_AER_UE_MASK_COMPL_TIMEOUT 0x00004000 #define PCIECAP_AER_UE_MASK_COMPL_ABORT 0x00008000 #define PCIECAP_AER_UE_MASK_UNEXP_COMPL 0x00010000 #define PCIECAP_AER_UE_MASK_RECV_OVFLOW 0x00020000 #define PCIECAP_AER_UE_MASK_MALFORMED_TLP 0x00040000 #define PCIECAP_AER_UE_MASK_ECRC 0x00080000 #define PCIECAP_AER_UE_MASK_UNSUPP_REQ 0x00100000 #define PCIECAP_AER_UE_SEVERITY 0x0c #define PCIECAP_AER_UE_SEVERITY_DLLP 0x00000010 #define PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN 0x00000020 #define PCIECAP_AER_UE_SEVERITY_POISON_TLP 0x00001000 #define PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT 0x00002000 #define PCIECAP_AER_UE_SEVERITY_COMPL_TIMEOUT 0x00004000 #define PCIECAP_AER_UE_SEVERITY_COMPL_ABORT 0x00008000 #define PCIECAP_AER_UE_SEVERITY_UNEXP_COMPL 0x00010000 #define PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW 0x00020000 #define PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP 0x00040000 #define PCIECAP_AER_UE_SEVERITY_ECRC 0x00080000 #define PCIECAP_AER_UE_SEVERITY_UNSUPP_REQ 0x00100000 #define PCIECAP_AER_UE_SEVERITY_INTERNAL 0x00400000 #define PCIECAP_AER_CE_STATUS 0x10 #define PCIECAP_AER_CE_RECVR_ERR 0x00000001 #define PCIECAP_AER_CE_BAD_TLP 0x00000040 #define PCIECAP_AER_CE_BAD_DLLP 0x00000080 #define PCIECAP_AER_CE_REPLAY_ROLLVR 0x00000100 #define PCIECAP_AER_CE_REPLAY_TMR_TO 0x00001000 #define PCIECAP_AER_CE_ADV_NONFATAL 0x00002000 #define PCIECAP_AER_CE_CORTD_INTERNAL 0x00004000 #define PCIECAP_AER_CE_HDR_LOG_OVFL 0x00008000 #define PCIECAP_AER_CE_MASK 0x14 #define PCIECAP_AER_CE_MASK_RECVR_ERR 0x00000001 #define PCIECAP_AER_CE_MASK_BAD_TLP 0x00000040 #define PCIECAP_AER_CE_MASK_BAD_DLLP 0x00000080 #define PCIECAP_AER_CE_MASK_REPLAY_ROLLVR 0x00000100 #define PCIECAP_AER_CE_MASK_REPLAY_TMR_TO 0x00001000 #define PCIECAP_AER_CE_MASK_ADV_NONFATAL 0x00002000 #define PCIECAP_AER_CE_MASK_CORTD_INTERNAL 0x00004000 #define PCIECAP_AER_CE_MASK_HDR_LOG_OVFL 0x00008000 #define PCIECAP_AER_CAPCTL 0x18 #define PCIECAP_AER_CAPCTL_FPTR 0x0000001f #define PCIECAP_AER_CAPCTL_ECRCG_CAP 0x00000020 #define PCIECAP_AER_CAPCTL_ECRCG_EN 0x00000040 #define PCIECAP_AER_CAPCTL_ECRCC_CAP 0x00000080 #define PCIECAP_AER_CAPCTL_ECRCC_EN 0x00000100 #define PCIECAP_AER_CAPCTL_MHREC_CAP 0x00000200 #define PCIECAP_AER_CAPCTL_MHREC_EN 0x00000400 #define PCIECAP_AER_CAPCTL_TLPPL_PR 0x00000800 #define PCIECAP_AER_HDR_LOG0 0x1c #define PCIECAP_AER_HDR_LOG1 0x20 #define PCIECAP_AER_HDR_LOG2 0x24 #define PCIECAP_AER_HDR_LOG3 0x28 #define PCIECAP_AER_RERR_CMD 0x2c #define PCIECAP_AER_RERR_CMD_FE 0x00000001 #define PCIECAP_AER_RERR_CMD_NFE 0x00000002 #define PCIECAP_AER_RERR_CMD_CE 0x00000004 #define PCIECAP_AER_RERR_STA 0x30 #define PCIECAP_AER_RERR_STA_CORR 0x00000001 #define PCIECAP_AER_RERR_STA_MCORR 0x00000002 #define PCIECAP_AER_RERR_STA_FNF 0x00000004 #define PCIECAP_AER_RERR_STA_MFNF 0x00000008 #define PCIECAP_AER_RERR_F_UFATAL 0x00000010 #define PCIECAP_AER_RERR_NFE 0x00000020 #define PCIECAP_AER_RERR_FE 0x00000040 #define PCIECAP_AER_RERR_MSINO 0xf8000000 #define PCIECAP_AER_SRCID 0x34 #define PCIECAP_AER_SRCID_CORR 0x0000ffff #define PCIECAP_AER_SRCID_FNF 0xffff0000 #define PCIECAP_AER_TLP_PFX_LOG0 0x38 #define PCIECAP_AER_TLP_PFX_LOG1 0x3c #define PCIECAP_AER_TLP_PFX_LOG2 0x40 #define PCIECAP_AER_TLP_PFX_LOG3 0x44 #endif /* __PCI_CFG_H */ skiboot-skiboot-5.1.13/include/pci.h000066400000000000000000000370241265204436200173040ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PCI_H #define __PCI_H #include #include #include /* PCI Slot Info: Wired Lane Values * * Values 0 to 6 match slot map 1005. In case of *any* change here * make sure to keep the lxvpd.c parsing code in sync *and* the * corresponding label strings in pci.c */ #define PCI_SLOT_WIRED_LANES_UNKNOWN 0x00 #define PCI_SLOT_WIRED_LANES_PCIE_X1 0x01 #define PCI_SLOT_WIRED_LANES_PCIE_X2 0x02 #define PCI_SLOT_WIRED_LANES_PCIE_X4 0x03 #define PCI_SLOT_WIRED_LANES_PCIE_X8 0x04 #define PCI_SLOT_WIRED_LANES_PCIE_X16 0x05 #define PCI_SLOT_WIRED_LANES_PCIE_X32 0x06 #define PCI_SLOT_WIRED_LANES_PCIX_32 0x07 #define PCI_SLOT_WIRED_LANES_PCIX_64 0x08 /* PCI Slot Info: Bus Clock Values */ #define PCI_SLOT_BUS_CLK_RESERVED 0x00 #define PCI_SLOT_BUS_CLK_GEN_1 0x01 #define PCI_SLOT_BUS_CLK_GEN_2 0x02 #define PCI_SLOT_BUS_CLK_GEN_3 0x03 /* PCI Slot Info: Connector Type Values */ #define PCI_SLOT_CONNECTOR_PCIE_EMBED 0x00 #define PCI_SLOT_CONNECTOR_PCIE_X1 0x01 #define PCI_SLOT_CONNECTOR_PCIE_X2 0x02 #define PCI_SLOT_CONNECTOR_PCIE_X4 0x03 #define PCI_SLOT_CONNECTOR_PCIE_X8 0x04 #define PCI_SLOT_CONNECTOR_PCIE_X16 0x05 #define PCI_SLOT_CONNECTOR_PCIE_NS 0x0E /* Non-Standard */ /* PCI Slot Info: Card Description Values */ #define PCI_SLOT_DESC_NON_STANDARD 0x00 /* Embed/Non-Standard Connector */ #define PCI_SLOT_DESC_PCIE_FH_FL 0x00 /* Full Height, Full Length */ #define PCI_SLOT_DESC_PCIE_FH_HL 0x01 /* Full Height, Half Length */ #define PCI_SLOT_DESC_PCIE_HH_FL 0x02 /* Half Height, Full Length */ #define PCI_SLOT_DESC_PCIE_HH_HL 0x03 /* Half Height, Half Length */ /* PCI Slot Info: Mechanicals Values */ #define PCI_SLOT_MECH_NONE 0x00 #define PCI_SLOT_MECH_RIGHT 0x01 #define PCI_SLOT_MECH_LEFT 0x02 #define PCI_SLOT_MECH_RIGHT_LEFT 0x03 /* PCI Slot Info: Power LED Control Values */ #define PCI_SLOT_PWR_LED_CTL_NONE 0x00 /* No Control */ #define PCI_SLOT_PWR_LED_CTL_FSP 0x01 /* FSP Controlled */ #define PCI_SLOT_PWR_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ /* PCI Slot Info: ATTN LED Control Values */ #define PCI_SLOT_ATTN_LED_CTL_NONE 0x00 /* No Control */ #define PCI_SLOT_ATTN_LED_CTL_FSP 0x01 /* FSP Controlled */ #define PCI_SLOT_ATTN_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ /* PCI Slot Entry Information */ struct pci_slot_info { char label[16]; bool pluggable; bool power_ctl; int wired_lanes; int bus_clock; int connector_type; int card_desc; int card_mech; int pwr_led_ctl; int attn_led_ctl; int slot_index; }; /* * While this might not be necessary in the long run, the existing * Linux kernels expect us to provide a device-tree that contains * a representation of all PCI devices below the host bridge. Thus * we need to perform a bus scan. We don't need to assign MMIO/IO * resources, but we do need to assign bus numbers in a way that * is going to be compatible with the HW constraints for PE filtering * that is naturally aligned power of twos for ranges below a bridge. * * Thus the structure pci_device is used for the tracking of the * detected devices and the later generation of the device-tree. * * We do not keep a separate structure for a bus, however a device * can have children in which case a device is a bridge. * * Because this is likely to change, we avoid putting too much * information in that structure nor relying on it for anything * else but the construction of the flat device-tree. */ struct pci_device { uint16_t bdfn; bool is_bridge; bool is_multifunction; uint8_t dev_type; /* PCIE */ uint8_t primary_bus; uint8_t secondary_bus; uint8_t subordinate_bus; uint32_t scan_map; uint64_t cap_list; uint32_t cap[64]; uint32_t mps; /* Max payload size capability */ struct pci_slot_info *slot_info; struct pci_device *parent; struct list_head children; struct list_node link; }; static inline void pci_set_cap(struct pci_device *pd, int id, int pos, bool ext) { if (!ext) { pd->cap_list |= (0x1ul << id); pd->cap[id] = pos; } else { pd->cap_list |= (0x1ul << (id + 32)); pd->cap[id + 32] = pos; } } static inline bool pci_has_cap(struct pci_device *pd, int id, bool ext) { if (!ext) return !!(pd->cap_list & (0x1ul << id)); else return !!(pd->cap_list & (0x1ul << (id + 32))); } static inline int pci_cap(struct pci_device *pd, int id, bool ext) { if (!ext) return pd->cap[id]; else return pd->cap[id + 32]; } /* * When generating the device-tree, we need to keep track of * the LSI mapping & swizzle it. This state structure is * passed by the PHB to pci_add_nodes() and will be used * internally. * * We assume that the interrupt parent (PIC) #address-cells * is 0 and #interrupt-cells has a max value of 2. */ struct pci_lsi_state { #define MAX_INT_SIZE 2 uint32_t int_size; /* #cells */ uint32_t int_val[4][MAX_INT_SIZE]; /* INTA...INTD */ uint32_t int_parent[4]; }; /* * NOTE: All PCI functions return negative OPAL error codes * * In addition, some functions may return a positive timeout * value or some other state information, see the description * of individual functions. If nothing is specified, it's * just an error code or 0 (success). * * Functions that operate asynchronously will return a positive * delay value and will require the ->poll() op to be called after * that delay. ->poll() will then return success, a negative error * code, or another delay. * * Note: If an asynchronous function returns 0, it has completed * successfully and does not require a call to ->poll(). Similarly * if ->poll() is called while no operation is in progress, it will * simply return 0 (success) * * Note that all functions except ->lock() itself assume that the * caller is holding the PHB lock. * * TODO: Add more interfaces to control things like link width * reduction for power savings etc... */ struct phb; struct phb_ops { /* * Locking. This is called around OPAL accesses */ void (*lock)(struct phb *phb); void (*unlock)(struct phb *phb); /* * Config space ops */ int64_t (*cfg_read8)(struct phb *phb, uint32_t bdfn, uint32_t offset, uint8_t *data); int64_t (*cfg_read16)(struct phb *phb, uint32_t bdfn, uint32_t offset, uint16_t *data); int64_t (*cfg_read32)(struct phb *phb, uint32_t bdfn, uint32_t offset, uint32_t *data); int64_t (*cfg_write8)(struct phb *phb, uint32_t bdfn, uint32_t offset, uint8_t data); int64_t (*cfg_write16)(struct phb *phb, uint32_t bdfn, uint32_t offset, uint16_t data); int64_t (*cfg_write32)(struct phb *phb, uint32_t bdfn, uint32_t offset, uint32_t data); /* * Bus number selection. See pci_scan() for a description */ uint8_t (*choose_bus)(struct phb *phb, struct pci_device *bridge, uint8_t candidate, uint8_t *max_bus, bool *use_max); /* * Device init method is called after a device has been detected * and before probing further. It can alter things like scan_map * for bridge ports etc... */ void (*device_init)(struct phb *phb, struct pci_device *device); /* * EEH methods * * The various arguments are identical to the corresponding * OPAL functions */ int64_t (*eeh_freeze_status)(struct phb *phb, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint16_t *severity, uint64_t *phb_status); int64_t (*eeh_freeze_clear)(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token); int64_t (*eeh_freeze_set)(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token); int64_t (*err_inject)(struct phb *phb, uint32_t pe_no, uint32_t type, uint32_t func, uint64_t addr, uint64_t mask); int64_t (*get_diag_data)(struct phb *phb, void *diag_buffer, uint64_t diag_buffer_len); int64_t (*get_diag_data2)(struct phb *phb, void *diag_buffer, uint64_t diag_buffer_len); int64_t (*next_error)(struct phb *phb, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity); /* * Other IODA methods * * The various arguments are identical to the corresponding * OPAL functions */ int64_t (*pci_reinit)(struct phb *phb, uint64_t scope, uint64_t data); int64_t (*phb_mmio_enable)(struct phb *phb, uint16_t window_type, uint16_t window_num, uint16_t enable); int64_t (*set_phb_mem_window)(struct phb *phb, uint16_t window_type, uint16_t window_num, uint64_t addr, uint64_t pci_addr, uint64_t size); int64_t (*map_pe_mmio_window)(struct phb *phb, uint16_t pe_number, uint16_t window_type, uint16_t window_num, uint16_t segment_num); int64_t (*set_pe)(struct phb *phb, uint64_t pe_number, uint64_t bus_dev_func, uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, uint8_t pe_action); int64_t (*set_peltv)(struct phb *phb, uint32_t parent_pe, uint32_t child_pe, uint8_t state); int64_t (*map_pe_dma_window)(struct phb *phb, uint16_t pe_number, uint16_t window_id, uint16_t tce_levels, uint64_t tce_table_addr, uint64_t tce_table_size, uint64_t tce_page_size); int64_t (*map_pe_dma_window_real)(struct phb *phb, uint16_t pe_number, uint16_t dma_window_number, uint64_t pci_start_addr, uint64_t pci_mem_size); int64_t (*set_mve)(struct phb *phb, uint32_t mve_number, uint32_t pe_number); int64_t (*set_mve_enable)(struct phb *phb, uint32_t mve_number, uint32_t state); int64_t (*set_xive_pe)(struct phb *phb, uint32_t pe_number, uint32_t xive_num); int64_t (*get_xive_source)(struct phb *phb, uint32_t xive_num, int32_t *interrupt_source_number); int64_t (*get_msi_32)(struct phb *phb, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint32_t *msi_address, uint32_t *message_data); int64_t (*get_msi_64)(struct phb *phb, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, uint64_t *msi_address, uint32_t *message_data); int64_t (*ioda_reset)(struct phb *phb, bool purge); int64_t (*papr_errinjct_reset)(struct phb *phb); /* * P5IOC2 only */ int64_t (*set_phb_tce_memory)(struct phb *phb, uint64_t tce_mem_addr, uint64_t tce_mem_size); /* * IODA2 PCI interfaces */ int64_t (*pci_msi_eoi)(struct phb *phb, uint32_t hwirq); /* * Slot control */ /* presence_detect - Check for a present device * * Immediate return of: * * OPAL_SHPC_DEV_NOT_PRESENT = 0, * OPAL_SHPC_DEV_PRESENT = 1 * * or a negative OPAL error code */ int64_t (*presence_detect)(struct phb *phb); /* link_state - Check link state * * Immediate return of: * * OPAL_SHPC_LINK_DOWN = 0, * OPAL_SHPC_LINK_UP_x1 = 1, * OPAL_SHPC_LINK_UP_x2 = 2, * OPAL_SHPC_LINK_UP_x4 = 4, * OPAL_SHPC_LINK_UP_x8 = 8, * OPAL_SHPC_LINK_UP_x16 = 16, * OPAL_SHPC_LINK_UP_x32 = 32 * * or a negative OPAL error code */ int64_t (*link_state)(struct phb *phb); /* power_state - Check slot power state * * Immediate return of: * * OPAL_SLOT_POWER_OFF = 0, * OPAL_SLOT_POWER_ON = 1, * * or a negative OPAL error code */ int64_t (*power_state)(struct phb *phb); /* slot_power_off - Start slot power off sequence * * Asynchronous function, returns a positive delay * or a negative error code */ int64_t (*slot_power_off)(struct phb *phb); /* slot_power_on - Start slot power on sequence * * Asynchronous function, returns a positive delay * or a negative error code. */ int64_t (*slot_power_on)(struct phb *phb); /* PHB power off and on after complete init */ int64_t (*complete_reset)(struct phb *phb, uint8_t assert); /* hot_reset - Hot Reset sequence */ int64_t (*hot_reset)(struct phb *phb); /* Fundamental reset */ int64_t (*fundamental_reset)(struct phb *phb); /* poll - Poll and advance asynchronous operations * * Returns a positive delay, 0 for success or a * negative OPAL error code */ int64_t (*poll)(struct phb *phb); /* Put phb in capi mode or pcie mode */ int64_t (*set_capi_mode)(struct phb *phb, uint64_t mode, uint64_t pe_number); int64_t (*set_capp_recovery)(struct phb *phb); }; enum phb_type { phb_type_pci, phb_type_pcix_v1, phb_type_pcix_v2, phb_type_pcie_v1, phb_type_pcie_v2, phb_type_pcie_v3, }; struct phb { struct dt_node *dt_node; int opal_id; uint32_t scan_map; enum phb_type phb_type; struct list_head devices; const struct phb_ops *ops; struct pci_lsi_state lstate; uint32_t mps; /* PCI-X only slot info, for PCI-E this is in the RC bridge */ struct pci_slot_info *slot_info; /* Base location code used to generate the children one */ const char *base_loc_code; /* Additional data the platform might need to attach */ void *platform_data; }; /* Config space ops wrappers */ static inline int64_t pci_cfg_read8(struct phb *phb, uint32_t bdfn, uint32_t offset, uint8_t *data) { return phb->ops->cfg_read8(phb, bdfn, offset, data); } static inline int64_t pci_cfg_read16(struct phb *phb, uint32_t bdfn, uint32_t offset, uint16_t *data) { return phb->ops->cfg_read16(phb, bdfn, offset, data); } static inline int64_t pci_cfg_read32(struct phb *phb, uint32_t bdfn, uint32_t offset, uint32_t *data) { return phb->ops->cfg_read32(phb, bdfn, offset, data); } static inline int64_t pci_cfg_write8(struct phb *phb, uint32_t bdfn, uint32_t offset, uint8_t data) { return phb->ops->cfg_write8(phb, bdfn, offset, data); } static inline int64_t pci_cfg_write16(struct phb *phb, uint32_t bdfn, uint32_t offset, uint16_t data) { return phb->ops->cfg_write16(phb, bdfn, offset, data); } static inline int64_t pci_cfg_write32(struct phb *phb, uint32_t bdfn, uint32_t offset, uint32_t data) { return phb->ops->cfg_write32(phb, bdfn, offset, data); } /* Utilities */ extern int64_t pci_find_cap(struct phb *phb, uint16_t bdfn, uint8_t cap); extern int64_t pci_find_ecap(struct phb *phb, uint16_t bdfn, uint16_t cap, uint8_t *version); extern void pci_device_init(struct phb *phb, struct pci_device *pd); extern struct pci_device *pci_walk_dev(struct phb *phb, int (*cb)(struct phb *, struct pci_device *, void *), void *userdata); extern struct pci_device *pci_find_dev(struct phb *phb, uint16_t bdfn); extern void pci_restore_bridge_buses(struct phb *phb); /* Manage PHBs */ extern int64_t pci_register_phb(struct phb *phb); extern int64_t pci_unregister_phb(struct phb *phb); extern struct phb *pci_get_phb(uint64_t phb_id); static inline void pci_put_phb(struct phb *phb __unused) { } /* Device tree */ extern void pci_std_swizzle_irq_map(struct dt_node *dt_node, struct pci_device *pd, struct pci_lsi_state *lstate, uint8_t swizzle); /* Initialize all PCI slots */ extern void pci_init_slots(void); extern void pci_reset(void); extern void opal_pci_eeh_set_evt(uint64_t phb_id); extern void opal_pci_eeh_clear_evt(uint64_t phb_id); #endif /* __PCI_H */ skiboot-skiboot-5.1.13/include/pel.h000066400000000000000000000123331265204436200173050ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PEL_H #define __PEL_H #include #include /* Data Structures for PEL data. */ #define PRIVATE_HEADER_SECTION_SIZE 48 #define USER_HEADER_SECTION_SIZE 24 #define SRC_SECTION_SIZE 80 #define SRC_SUBSECTION_SIZE 4 #define SRC_LENGTH 72 #define OPAL_MAX_SRC_BYTES 32 #define EXTENDED_HEADER_SECTION_SIZE 76 #define MTMS_SECTION_SIZE 28 #define IO_EVENT_SECTION_SIZE 16 #define OPAL_ELOG_VERSION 1 #define OPAL_ELOG_SST 0 #define OPAL_SRC_MAX_WORD_COUNT 8 #define OPAL_SRC_FORMAT 0x80 #define OPAL_FAILING_SUBSYSTEM 0x82 #define OPAL_SYS_MODEL_LEN 8 #define OPAL_SYS_SERIAL_LEN 12 #define OPAL_VER_LEN 16 #define OPAL_SYMPID_LEN 80 #define OPAL_RC_NONE 0 #define OPAL_IO_MAX_RPC_DATA 216 #define OPAL_SRC_SEC_VER 0x02 #define OPAL_EXT_HRD_VER 0x01 /* Error log reporting action */ #define ERRL_ACTION_REPORT 0x2000 #define ERRL_ACTION_NONE 0x0000 enum elogSectionId { ELOG_SID_PRIVATE_HEADER = 0x5048, /* PH */ ELOG_SID_USER_HEADER = 0x5548, /* UH */ ELOG_SID_EXTENDED_HEADER = 0x4548, /* EH */ ELOG_SID_PRIMARY_SRC = 0x5053, /* PS */ ELOG_SID_MACHINE_TYPE = 0x4D54, /* MT */ ELOG_SID_SECONDARY_SRC = 0x5353, /* SS */ ELOG_SID_CALL_HOME = 0x4348, /* CH */ ELOG_SID_DUMP_LOCATOR = 0x4448, /* DH */ ELOG_SID_SOFTWARE_ERROR = 0x5357, /* SW */ ELOG_SID_PARTITION = 0x4C50, /* LP */ ELOG_SID_LOGICAL_RESOURCE = 0x4C52, /* LR */ ELOG_SID_HMC_ID = 0x484D, /* HM */ ELOG_SID_EPOW = 0x4550, /* EP */ ELOG_SID_IO_EVENT = 0x4945, /* IE */ ELOG_SID_MFG_INFORMATION = 0x4D49, /* MI */ ELOG_SID_USER_DEFINED = 0x5544 /* UD */ }; struct opal_v6_header { enum elogSectionId id:16; /* section id */ uint16_t length; /* section length */ uint8_t version; /* section version */ uint8_t subtype; /* section sub-type id */ uint16_t component_id; /* component id of section creator */ }; /* opal_srctype */ #define OPAL_SRC_TYPE_ERROR 0xBB #define OPAL_CID_SAPPHIRE 'K' /* creator ID for sapphire log */ #define OPAL_CID_POWERNV 'P' /* creator ID for powernv log */ /* Origin of error, elog_origin */ #define ORG_SAPPHIRE 1 #define ORG_POWERNV 2 /* MAX time for error log commit */ #define ERRORLOG_TIMEOUT_INTERVAL 180 /*struct opal_private head section_ */ struct opal_private_header_section { struct opal_v6_header v6header; uint32_t create_date; uint32_t create_time; uint32_t commit_date; uint32_t commit_time; uint32_t creator_id:8; /* subsystem component id */ uint32_t reserved_0:16; uint32_t section_count:8; /* number of sections in log */ uint32_t reserved_1; uint32_t creator_subid_hi; uint32_t creator_subid_lo; uint32_t plid; /* platform log id */ uint32_t log_entry_id; /* Unique log entry id */ }; /* opal user header section */ struct opal_user_header_section { struct opal_v6_header v6header; uint8_t subsystem_id; /* subsystem id */ uint8_t event_scope; uint8_t event_severity; uint8_t event_type; /* error/event severity */ uint32_t reserved_0; uint16_t reserved_1; uint16_t action_flags; /* error action code */ uint32_t reserved_2; }; struct opal_src_section { struct opal_v6_header v6header; uint8_t version; uint8_t flags; uint8_t reserved_0; uint8_t wordcount; uint16_t reserved_1; uint16_t srclength; uint32_t hexwords[OPAL_SRC_MAX_WORD_COUNT]; char srcstring[OPAL_MAX_SRC_BYTES]; }; struct opal_extended_header_section { struct opal_v6_header v6header; char model[OPAL_SYS_MODEL_LEN]; char serial_no[OPAL_SYS_SERIAL_LEN]; char opal_release_version[OPAL_VER_LEN]; char opal_subsys_version[OPAL_VER_LEN]; uint16_t reserved_0; uint32_t extended_header_date; uint32_t extended_header_time; uint16_t reserved_1; uint8_t reserved_2; uint8_t opal_symid_len; char opalsymid[OPAL_SYMPID_LEN]; }; /* opal MTMS section */ struct opal_mtms_section { struct opal_v6_header v6header; char model[OPAL_SYS_MODEL_LEN]; char serial_no[OPAL_SYS_SERIAL_LEN]; }; /* User defined section */ struct opal_user_section { struct opal_v6_header v6header; char dump[1]; }; /* The minimum size of a PEL record */ #define PEL_MIN_SIZE (PRIVATE_HEADER_SECTION_SIZE + USER_HEADER_SECTION_SIZE \ + SRC_SECTION_SIZE + EXTENDED_HEADER_SECTION_SIZE \ + MTMS_SECTION_SIZE) size_t pel_size(struct errorlog *elog_data); int create_pel_log(struct errorlog *elog_data, char *pel_buffer, size_t pel_buffer_size) __warn_unused_result; #endif skiboot-skiboot-5.1.13/include/phb3-regs.h000066400000000000000000000332151265204436200203210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PHB3_REGS_H #define __PHB3_REGS_H /* * PHB registers */ /* PHB Fundamental register set A */ #define PHB_LSI_SOURCE_ID 0x100 #define PHB_LSI_SRC_ID PPC_BITMASK(5,12) #define PHB_DMA_CHAN_STATUS 0x110 #define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27) #define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28) #define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29) #define PHB_CPU_LOADSTORE_STATUS 0x120 #define PHB_CPU_LS_ANY_ERR PPC_BIT(27) #define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28) #define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29) #define PHB_DMA_MSI_NODE_ID 0x128 #define PHB_DMAMSI_NID_FIXED PPC_BIT(0) #define PHB_DMAMSI_NID PPC_BITMASK(24,31) #define PHB_CONFIG_DATA 0x130 #define PHB_LOCK0 0x138 #define PHB_CONFIG_ADDRESS 0x140 #define PHB_CA_ENABLE PPC_BIT(0) #define PHB_CA_BUS PPC_BITMASK(4,11) #define PHB_CA_DEV PPC_BITMASK(12,16) #define PHB_CA_FUNC PPC_BITMASK(17,19) #define PHB_CA_BDFN PPC_BITMASK(4,19) /* bus,dev,func */ #define PHB_CA_REG PPC_BITMASK(20,31) #define PHB_CA_PE PPC_BITMASK(40,47) #define PHB_LOCK1 0x148 #define PHB_IVT_BAR 0x150 #define PHB_IVT_BAR_ENABLE PPC_BIT(0) #define PHB_IVT_BASE_ADDRESS PPC_BITMASK(14,48) #define PHB_IVT_LENGTH PPC_BITMASK(52,63) #define PHB_RBA_BAR 0x158 #define PHB_RBA_BAR_ENABLE PPC_BIT(0) #define PHB_RBA_BASE_ADDRESS PPC_BITMASK(14,55) #define PHB_PHB3_CONFIG 0x160 #define PHB_PHB3C_64B_TCE_EN PPC_BIT(2) #define PHB_PHB3C_32BIT_MSI_EN PPC_BIT(8) #define PHB_PHB3C_64BIT_MSI_EN PPC_BIT(14) #define PHB_PHB3C_M32_EN PPC_BIT(16) #define PHB_RTT_BAR 0x168 #define PHB_RTT_BAR_ENABLE PPC_BIT(0) #define PHB_RTT_BASE_ADDRESS PPC_BITMASK(14,46) #define PHB_PELTV_BAR 0x188 #define PHB_PELTV_BAR_ENABLE PPC_BIT(0) #define PHB_PELTV_BASE_ADDRESS PPC_BITMASK(14,50) #define PHB_M32_BASE_ADDR 0x190 #define PHB_M32_BASE_MASK 0x198 #define PHB_M32_START_ADDR 0x1a0 #define PHB_PEST_BAR 0x1a8 #define PHB_PEST_BAR_ENABLE PPC_BIT(0) #define PHB_PEST_BASE_ADDRESS PPC_BITMASK(14,51) #define PHB_M64_UPPER_BITS 0x1f0 #define PHB_INTREP_TIMER 0x1f8 #define PHB_DMARD_SYNC 0x200 #define PHB_RTC_INVALIDATE 0x208 #define PHB_RTC_INVALIDATE_ALL PPC_BIT(0) #define PHB_RTC_INVALIDATE_RID PPC_BITMASK(16,31) #define PHB_TCE_KILL 0x210 #define PHB_TCE_KILL_ALL PPC_BIT(0) #define PHB_TCE_SPEC_CTL 0x218 #define PHB_IODA_ADDR 0x220 #define PHB_IODA_AD_AUTOINC PPC_BIT(0) #define PHB_IODA_AD_TSEL PPC_BITMASK(11,15) #define PHB_IODA_AD_TADR PPC_BITMASK(55,63) #define PHB_IODA_DATA0 0x228 #define PHB_FFI_REQUEST 0x238 #define PHB_FFI_LOCK_CLEAR PPC_BIT(3) #define PHB_FFI_REQUEST_ISN PPC_BITMASK(49,59) #define PHB_FFI_LOCK 0x240 #define PHB_XIVE_UPDATE 0x248 /* Broken in DD1 */ #define PHB_PHB3_GEN_CAP 0x250 #define PHB_PHB3_TCE_CAP 0x258 #define PHB_PHB3_IRQ_CAP 0x260 #define PHB_PHB3_EEH_CAP 0x268 #define PHB_IVC_INVALIDATE 0x2a0 #define PHB_IVC_INVALIDATE_ALL PPC_BIT(0) #define PHB_IVC_INVALIDATE_SID PPC_BITMASK(16,31) #define PHB_IVC_UPDATE 0x2a8 #define PHB_IVC_UPDATE_ENABLE_P PPC_BIT(0) #define PHB_IVC_UPDATE_ENABLE_Q PPC_BIT(1) #define PHB_IVC_UPDATE_ENABLE_SERVER PPC_BIT(2) #define PHB_IVC_UPDATE_ENABLE_PRI PPC_BIT(3) #define PHB_IVC_UPDATE_ENABLE_GEN PPC_BIT(4) #define PHB_IVC_UPDATE_ENABLE_CON PPC_BIT(5) #define PHB_IVC_UPDATE_GEN_MATCH PPC_BITMASK(6, 7) #define PHB_IVC_UPDATE_SERVER PPC_BITMASK(8, 23) #define PHB_IVC_UPDATE_PRI PPC_BITMASK(24, 31) #define PHB_IVC_UPDATE_GEN PPC_BITMASK(32,33) #define PHB_IVC_UPDATE_P PPC_BITMASK(34,34) #define PHB_IVC_UPDATE_Q PPC_BITMASK(35,35) #define PHB_IVC_UPDATE_SID PPC_BITMASK(48,63) #define PHB_PAPR_ERR_INJ_CTL 0x2b0 #define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0) #define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1) #define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2) #define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3) #define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4) #define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5) #define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6) #define PHB_PAPR_ERR_INJ_ADDR 0x2b8 #define PHB_PAPR_ERR_INJ_ADDR_MMIO PPC_BITMASK(16,63) #define PHB_PAPR_ERR_INJ_MASK 0x2c0 #define PHB_PAPR_ERR_INJ_MASK_CFG PPC_BITMASK(4,11) #define PHB_PAPR_ERR_INJ_MASK_CFG_ALL PPC_BITMASK(4,19) #define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16,63) #define PHB_ETU_ERR_SUMMARY 0x2c8 /* UTL registers */ #define UTL_SYS_BUS_CONTROL 0x400 #define UTL_STATUS 0x408 #define UTL_SYS_BUS_AGENT_STATUS 0x410 #define UTL_SYS_BUS_AGENT_ERR_SEVERITY 0x418 #define UTL_SYS_BUS_AGENT_IRQ_EN 0x420 #define UTL_SYS_BUS_BURST_SZ_CONF 0x440 #define UTL_REVISION_ID 0x448 #define UTL_BCLK_DOMAIN_DBG1 0x460 #define UTL_BCLK_DOMAIN_DBG2 0x468 #define UTL_BCLK_DOMAIN_DBG3 0x470 #define UTL_BCLK_DOMAIN_DBG4 0x478 #define UTL_BCLK_DOMAIN_DBG5 0x480 #define UTL_BCLK_DOMAIN_DBG6 0x488 #define UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 #define UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 #define UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 #define UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 #define UTL_OUT_NP_BUF_ALLOC 0x500 #define UTL_IN_NP_BUF_ALLOC 0x510 #define UTL_PCIE_TAGS_ALLOC 0x520 #define UTL_GBIF_READ_TAGS_ALLOC 0x530 #define UTL_PCIE_PORT_CONTROL 0x540 #define UTL_PCIE_PORT_STATUS 0x548 #define UTL_PCIE_PORT_ERROR_SEV 0x550 #define UTL_PCIE_PORT_IRQ_EN 0x558 #define UTL_RC_STATUS 0x560 #define UTL_RC_ERR_SEVERITY 0x568 #define UTL_RC_IRQ_EN 0x570 #define UTL_EP_STATUS 0x578 #define UTL_EP_ERR_SEVERITY 0x580 #define UTL_EP_ERR_IRQ_EN 0x588 #define UTL_PCI_PM_CTRL1 0x590 #define UTL_PCI_PM_CTRL2 0x598 #define UTL_GP_CTL1 0x5a0 #define UTL_GP_CTL2 0x5a8 #define UTL_PCLK_DOMAIN_DBG1 0x5b0 #define UTL_PCLK_DOMAIN_DBG2 0x5b8 #define UTL_PCLK_DOMAIN_DBG3 0x5c0 #define UTL_PCLK_DOMAIN_DBG4 0x5c8 /* PCI-E Stack registers */ #define PHB_PCIE_SYSTEM_CONFIG 0x600 #define PHB_PCIE_SCONF_SLOT PPC_BIT(15) #define PHB_PCIE_SCONF_MAXLINKSPEED PPC_BITMASK(32,35) #define PHB_PCIE_BUS_NUMBER 0x608 #define PHB_PCIE_SYSTEM_TEST 0x618 #define PHB_PCIE_LINK_MANAGEMENT 0x630 #define PHB_PCIE_LM_LINK_ACTIVE PPC_BIT(8) #define PHB_PCIE_DLP_TRAIN_CTL 0x640 #define PHB_PCIE_DLP_TCTX_DISABLE PPC_BIT(1) #define PHB_PCIE_DLP_TCRX_DISABLED PPC_BIT(16) #define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19) #define PHB_PCIE_DLP_TC_DL_LINKUP PPC_BIT(21) #define PHB_PCIE_DLP_TC_DL_PGRESET PPC_BIT(22) #define PHB_PCIE_DLP_TC_DL_LINKACT PPC_BIT(23) #define PHB_PCIE_SLOP_LOOPBACK_STATUS 0x648 #define PHB_PCIE_SYS_LINK_INIT 0x668 #define PHB_PCIE_UTL_CONFIG 0x670 #define PHB_PCIE_DLP_CONTROL 0x678 #define PHB_PCIE_UTL_ERRLOG1 0x680 #define PHB_PCIE_UTL_ERRLOG2 0x688 #define PHB_PCIE_UTL_ERRLOG3 0x690 #define PHB_PCIE_UTL_ERRLOG4 0x698 #define PHB_PCIE_DLP_ERRLOG1 0x6a0 #define PHB_PCIE_DLP_ERRLOG2 0x6a8 #define PHB_PCIE_DLP_ERR_STATUS 0x6b0 #define PHB_PCIE_DLP_ERR_COUNTERS 0x6b8 #define PHB_PCIE_UTL_ERR_INJECT 0x6c0 #define PHB_PCIE_TLDLP_ERR_INJECT 0x6c8 #define PHB_PCIE_LANE_EQ_CNTL0 0x6d0 #define PHB_PCIE_LANE_EQ_CNTL1 0x6d8 #define PHB_PCIE_LANE_EQ_CNTL2 0x6e0 #define PHB_PCIE_LANE_EQ_CNTL3 0x6e8 #define PHB_PCIE_STRAPPING 0x700 /* Fundamental register set B */ #define PHB_VERSION 0x800 #define PHB_RESET 0x808 #define PHB_CONTROL 0x810 #define PHB_AIB_RX_CRED_INIT_TIMER 0x818 #define PHB_AIB_RX_CMD_CRED 0x820 #define PHB_AIB_RX_DATA_CRED 0x828 #define PHB_AIB_TX_CMD_CRED 0x830 #define PHB_AIB_TX_DATA_CRED 0x838 #define PHB_AIB_TX_CHAN_MAPPING 0x840 #define PHB_AIB_TAG_ENABLE 0x858 #define PHB_AIB_FENCE_CTRL 0x860 #define PHB_TCE_TAG_ENABLE 0x868 #define PHB_TCE_WATERMARK 0x870 #define PHB_TIMEOUT_CTRL1 0x878 #define PHB_TIMEOUT_CTRL2 0x880 #define PHB_QUIESCE_DMA_G 0x888 #define PHB_AIB_TAG_STATUS 0x900 #define PHB_TCE_TAG_STATUS 0x908 /* FIR & Error registers */ #define PHB_LEM_FIR_ACCUM 0xc00 #define PHB_LEM_FIR_AND_MASK 0xc08 #define PHB_LEM_FIR_OR_MASK 0xc10 #define PHB_LEM_ERROR_MASK 0xc18 #define PHB_LEM_ERROR_AND_MASK 0xc20 #define PHB_LEM_ERROR_OR_MASK 0xc28 #define PHB_LEM_ACTION0 0xc30 #define PHB_LEM_ACTION1 0xc38 #define PHB_LEM_WOF 0xc40 #define PHB_ERR_STATUS 0xc80 #define PHB_ERR1_STATUS 0xc88 #define PHB_ERR_INJECT 0xc90 #define PHB_ERR_LEM_ENABLE 0xc98 #define PHB_ERR_IRQ_ENABLE 0xca0 #define PHB_ERR_FREEZE_ENABLE 0xca8 #define PHB_ERR_AIB_FENCE_ENABLE 0xcb0 #define PHB_ERR_LOG_0 0xcc0 #define PHB_ERR_LOG_1 0xcc8 #define PHB_ERR_STATUS_MASK 0xcd0 #define PHB_ERR1_STATUS_MASK 0xcd8 #define PHB_OUT_ERR_STATUS 0xd00 #define PHB_OUT_ERR1_STATUS 0xd08 #define PHB_OUT_ERR_INJECT 0xd10 #define PHB_OUT_ERR_LEM_ENABLE 0xd18 #define PHB_OUT_ERR_IRQ_ENABLE 0xd20 #define PHB_OUT_ERR_FREEZE_ENABLE 0xd28 #define PHB_OUT_ERR_AIB_FENCE_ENABLE 0xd30 #define PHB_OUT_ERR_LOG_0 0xd40 #define PHB_OUT_ERR_LOG_1 0xd48 #define PHB_OUT_ERR_STATUS_MASK 0xd50 #define PHB_OUT_ERR1_STATUS_MASK 0xd58 #define PHB_INA_ERR_STATUS 0xd80 #define PHB_INA_ERR1_STATUS 0xd88 #define PHB_INA_ERR_INJECT 0xd90 #define PHB_INA_ERR_LEM_ENABLE 0xd98 #define PHB_INA_ERR_IRQ_ENABLE 0xda0 #define PHB_INA_ERR_FREEZE_ENABLE 0xda8 #define PHB_INA_ERR_AIB_FENCE_ENABLE 0xdb0 #define PHB_INA_ERR_LOG_0 0xdc0 #define PHB_INA_ERR_LOG_1 0xdc8 #define PHB_INA_ERR_STATUS_MASK 0xdd0 #define PHB_INA_ERR1_STATUS_MASK 0xdd8 #define PHB_INB_ERR_STATUS 0xe00 #define PHB_INB_ERR1_STATUS 0xe08 #define PHB_INB_ERR_INJECT 0xe10 #define PHB_INB_ERR_LEM_ENABLE 0xe18 #define PHB_INB_ERR_IRQ_ENABLE 0xe20 #define PHB_INB_ERR_FREEZE_ENABLE 0xe28 #define PHB_INB_ERR_AIB_FENCE_ENABLE 0xe30 #define PHB_INB_ERR_LOG_0 0xe40 #define PHB_INB_ERR_LOG_1 0xe48 #define PHB_INB_ERR_STATUS_MASK 0xe50 #define PHB_INB_ERR1_STATUS_MASK 0xe58 /* Performance monitor & Debug registers */ #define PHB_TRACE_CONTROL 0xf80 #define PHB_PERFMON_CONFIG 0xf88 #define PHB_PERFMON_CTR0 0xf90 #define PHB_PERFMON_CTR1 0xf98 #define PHB_PERFMON_CTR2 0xfa0 #define PHB_PERFMON_CTR3 0xfa8 #define PHB_HOTPLUG_OVERRIDE 0xfb0 #define PHB_HPOVR_FORCE_RESAMPLE PPC_BIT(9) #define PHB_HPOVR_PRESENCE_A PPC_BIT(10) #define PHB_HPOVR_PRESENCE_B PPC_BIT(11) #define PHB_HPOVR_LINK_ACTIVE PPC_BIT(12) #define PHB_HPOVR_LINK_BIFURCATED PPC_BIT(13) #define PHB_HPOVR_LINK_LANE_SWAPPED PPC_BIT(14) /* * IODA2 on-chip tables */ #define IODA2_TBL_LIST 1 #define IODA2_TBL_LXIVT 2 #define IODA2_TBL_IVC_CAM 3 #define IODA2_TBL_RBA 4 #define IODA2_TBL_RCAM 5 #define IODA2_TBL_MRT 6 #define IODA2_TBL_PESTA 7 #define IODA2_TBL_PESTB 8 #define IODA2_TBL_TVT 9 #define IODA2_TBL_TCAM 10 #define IODA2_TBL_TDR 11 #define IODA2_TBL_M64BT 16 #define IODA2_TBL_M32DT 17 #define IODA2_TBL_PEEV 20 /* LXIVT */ #define IODA2_LXIVT_SERVER PPC_BITMASK(8,23) #define IODA2_LXIVT_PRIORITY PPC_BITMASK(24,31) #define IODA2_LXIVT_NODE_ID PPC_BITMASK(56,63) /* IVT */ #define IODA2_IVT_SERVER PPC_BITMASK(0,23) #define IODA2_IVT_PRIORITY PPC_BITMASK(24,31) #define IODA2_IVT_P PPC_BITMASK(39,39) #define IODA2_IVT_Q PPC_BITMASK(47,47) #define IODA2_IVT_PE PPC_BITMASK(48,63) /* TVT */ #define IODA2_TVT_TABLE_ADDR PPC_BITMASK(0,47) #define IODA2_TVT_NUM_LEVELS PPC_BITMASK(48,50) #define IODA2_TVE_1_LEVEL 0 #define IODA2_TVE_2_LEVELS 1 #define IODA2_TVE_3_LEVELS 2 #define IODA2_TVE_4_LEVELS 3 #define IODA2_TVE_5_LEVELS 4 #define IODA2_TVT_TCE_TABLE_SIZE PPC_BITMASK(51,55) #define IODA2_TVT_IO_PSIZE PPC_BITMASK(59,63) /* PESTA */ #define IODA2_PESTA_MMIO_FROZEN PPC_BIT(0) /* PESTB */ #define IODA2_PESTB_DMA_STOPPED PPC_BIT(0) /* M32DT */ #define IODA2_M32DT_PE PPC_BITMASK(8,15) /* M64BT */ #define IODA2_M64BT_ENABLE PPC_BIT(0) #define IODA2_M64BT_SINGLE_PE PPC_BIT(1) #define IODA2_M64BT_BASE PPC_BITMASK(2,31) #define IODA2_M64BT_MASK PPC_BITMASK(34,63) #define IODA2_M64BT_SINGLE_BASE PPC_BITMASK(2,26) #define IODA2_M64BT_PE_HI PPC_BITMASK(27,31) #define IODA2_M64BT_SINGLE_MASK PPC_BITMASK(34,58) #define IODA2_M64BT_PE_LOW PPC_BITMASK(59,63) /* * IODA2 in-memory tables */ /* PEST * * 2x8 bytes entries, PEST0 and PEST1 */ #define IODA2_PEST0_MMIO_CAUSE PPC_BIT(2) #define IODA2_PEST0_CFG_READ PPC_BIT(3) #define IODA2_PEST0_CFG_WRITE PPC_BIT(4) #define IODA2_PEST0_TTYPE PPC_BITMASK(5,7) #define PEST_TTYPE_DMA_WRITE 0 #define PEST_TTYPE_MSI 1 #define PEST_TTYPE_DMA_READ 2 #define PEST_TTYPE_DMA_READ_RESP 3 #define PEST_TTYPE_MMIO_LOAD 4 #define PEST_TTYPE_MMIO_STORE 5 #define PEST_TTYPE_OTHER 7 #define IODA2_PEST0_CA_RETURN PPC_BIT(8) #define IODA2_PEST0_UTL_RTOS_TIMEOUT PPC_BIT(8) /* Same bit as CA return */ #define IODA2_PEST0_UR_RETURN PPC_BIT(9) #define IODA2_PEST0_UTL_NONFATAL PPC_BIT(10) #define IODA2_PEST0_UTL_FATAL PPC_BIT(11) #define IODA2_PEST0_PARITY_UE PPC_BIT(13) #define IODA2_PEST0_UTL_CORRECTABLE PPC_BIT(14) #define IODA2_PEST0_UTL_INTERRUPT PPC_BIT(15) #define IODA2_PEST0_MMIO_XLATE PPC_BIT(16) #define IODA2_PEST0_IODA2_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */ #define IODA2_PEST0_TCE_PAGE_FAULT PPC_BIT(18) #define IODA2_PEST0_TCE_ACCESS_FAULT PPC_BIT(19) #define IODA2_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20) #define IODA2_PEST0_AIB_SIZE_INVALID PPC_BIT(21) #define IODA2_PEST0_LEM_BIT PPC_BITMASK(26,31) #define IODA2_PEST0_RID PPC_BITMASK(32,47) #define IODA2_PEST0_MSI_DATA PPC_BITMASK(48,63) #define IODA2_PEST1_FAIL_ADDR PPC_BITMASK(3,63) #endif /* __PHB3_REGS_H */ skiboot-skiboot-5.1.13/include/phb3.h000066400000000000000000000242631265204436200173660ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* */ #ifndef __PHB3_H #define __PHB3_H #include /* * Memory map * * In addition to the 4K MMIO registers window, the PBCQ will * forward down one or two large MMIO regions for use by the * PHB. * * We try to use the largest MMIO window for the M64 space and * the smallest for the M32 space, but we require at least 2G * of M32, otherwise we carve it out of M64. */ #define M32_PCI_START 0x080000000 /* Offset of the actual M32 window in PCI */ #define M32_PCI_SIZE 0x80000000ul /* Size for M32 */ /* * Interrupt map. * * Each PHB supports 2K interrupt sources, which is shared by * LSI and MSI. With default configuration, MSI would use range * [0, 0x7f7] and LSI would use [0x7f8, 0x7ff]. The interrupt * source should be combined with IRSN to form final hardware * IRQ. */ #define PHB3_MSI_IRQ_MIN 0x000 #define PHB3_MSI_IRQ_COUNT 0x7F8 #define PHB3_MSI_IRQ_MAX (PHB3_MSI_IRQ_MIN+PHB3_MSI_IRQ_COUNT-1) #define PHB3_LSI_IRQ_MIN (PHB3_MSI_IRQ_COUNT) #define PHB3_LSI_IRQ_COUNT 8 #define PHB3_LSI_IRQ_MAX (PHB3_LSI_IRQ_MIN+PHB3_LSI_IRQ_COUNT-1) #define PHB3_MSI_IRQ_BASE(chip, phb) (P8_CHIP_IRQ_PHB_BASE(chip, phb) | \ PHB3_MSI_IRQ_MIN) #define PHB3_LSI_IRQ_BASE(chip, phb) (P8_CHIP_IRQ_PHB_BASE(chip, phb) | \ PHB3_LSI_IRQ_MIN) #define PHB3_IRQ_NUM(irq) (irq & 0x7FF) /* * LSI interrupts * * The LSI interrupt block supports 8 interrupts. 4 of them are the * standard PCIe INTA..INTB. The rest is for additional functions * of the PHB */ #define PHB3_LSI_PCIE_INTA 0 #define PHB3_LSI_PCIE_INTB 1 #define PHB3_LSI_PCIE_INTC 2 #define PHB3_LSI_PCIE_INTD 3 #define PHB3_LSI_PCIE_INF 6 #define PHB3_LSI_PCIE_ER 7 /* * In-memory tables * * PHB3 requires a bunch of tables to be in memory instead of * arrays inside the chip (unlike previous versions of the * design). * * Some of them (IVT, etc...) will be provided by the OS via an * OPAL call, not only not all of them, we also need to make sure * some like PELT-V exist before we do our internal slot probing * or bad thing would happen on error (the whole PHB would go into * Fatal error state). * * So we maintain a set of tables internally for those mandatory * ones within our core memory. They are fairly small. They can * still be replaced by OS provided ones via OPAL APIs (and reset * to the internal ones) so the OS can provide node local allocation * for better performances. * * All those tables have to be naturally aligned */ /* RTT Table : 128KB - Maps RID to PE# * * Entries are 2 bytes indexed by PCIe RID */ #define RTT_TABLE_ENTRIES 0x10000 #define RTT_TABLE_SIZE 0x20000 /* IVT Table : MSI Interrupt vectors * state. * * We're sure that simics has 16-bytes IVE, totally 32KB. * However the real HW possiblly has 128-bytes IVE, totally 256KB. */ #define IVT_TABLE_ENTRIES 0x800 /* Default to 128-bytes IVEs, uncomment that to force it back to 16-bytes */ //#define IVT_TABLE_IVE_16B #ifdef IVT_TABLE_IVE_16B #define IVT_TABLE_SIZE 0x8000 #define IVT_TABLE_STRIDE 2 /* double-words */ #else #define IVT_TABLE_SIZE 0x40000 #define IVT_TABLE_STRIDE 16 /* double-words */ #endif /* PELT-V Table : 8KB - Maps PE# to PE# dependencies * * 256 entries of 256 bits (32 bytes) each */ #define PELTV_TABLE_SIZE 0x2000 /* PEST Table : 4KB - PE state table * * 256 entries of 16 bytes each containing state bits for each PE * * AFAIK: This acts as a backup for an on-chip cache and shall be * accessed via the indirect IODA table access registers only */ #define PEST_TABLE_SIZE 0x1000 /* RBA Table : 256 bytes - Reject Bit Array * * 2048 interrupts, 1 bit each, indiates the reject state of interrupts */ #define RBA_TABLE_SIZE 0x100 /* * Maximal supported PE# in PHB3. We probably probe it from EEH * capability register later. */ #define PHB3_MAX_PE_NUM 256 #define PHB3_RESERVED_PE_NUM 255 /* * State structure for a PHB */ /* * (Comment copied from p7ioc.h, please update both when relevant) * * The PHB State structure is essentially used during PHB reset * or recovery operations to indicate that the PHB cannot currently * be used for normal operations. * * Some states involve waiting for the timebase to reach a certain * value. In which case the field "delay_tgt_tb" is set and the * state machine will be run from the "state_poll" callback. * * At IPL time, we call this repeatedly during the various sequences * however under OS control, this will require a change in API. * * Fortunately, the OPAL API for slot power & reset are not currently * used by Linux, so changing them isn't going to be an issue. The idea * here is that some of these APIs will return a positive integer when * neededing such a delay to proceed. The OS will then be required to * call a new function opal_poll_phb() after that delay. That function * will potentially return a new delay, or OPAL_SUCCESS when the original * operation has completed successfully. If the operation has completed * with an error, then opal_poll_phb() will return that error. * * Note: Should we consider also returning optionally some indication * of what operation is in progress for OS debug/diag purposes ? * * Any attempt at starting a new "asynchronous" operation while one is * already in progress will result in an error. * * Internally, this is represented by the state being P7IOC_PHB_STATE_FUNCTIONAL * when no operation is in progress, which it reaches at the end of the * boot time initializations. Any attempt at performing a slot operation * on a PHB in that state will change the state to the corresponding * operation state machine. Any attempt while not in that state will * return an error. * * Some operations allow for a certain amount of retries, this is * provided for by the "retries" structure member for use by the state * machine as it sees fit. */ enum phb3_state { /* First init state */ PHB3_STATE_UNINITIALIZED, /* During PHB HW inits */ PHB3_STATE_INITIALIZING, /* Set if the PHB is for some reason unusable */ PHB3_STATE_BROKEN, /* PHB fenced */ PHB3_STATE_FENCED, /* Normal PHB functional state */ PHB3_STATE_FUNCTIONAL, /* Hot reset */ PHB3_STATE_HRESET_DELAY, PHB3_STATE_HRESET_DELAY2, /* Fundamental reset */ PHB3_STATE_FRESET_START, PHB3_STATE_FRESET_ASSERT_DELAY, PHB3_STATE_FRESET_DEASSERT_DELAY, /* Complete reset */ PHB3_STATE_CRESET_WAIT_CQ, PHB3_STATE_CRESET_REINIT, PHB3_STATE_CRESET_FRESET, /* Link state machine */ PHB3_STATE_WAIT_LINK_ELECTRICAL, PHB3_STATE_WAIT_LINK, }; /* * PHB3 error descriptor. Errors from all components (PBCQ, PHB) * will be cached to PHB3 instance. However, PBCQ errors would * have higher priority than those from PHB */ #define PHB3_ERR_SRC_NONE 0 #define PHB3_ERR_SRC_PBCQ 1 #define PHB3_ERR_SRC_PHB 2 #define PHB3_ERR_CLASS_NONE 0 #define PHB3_ERR_CLASS_DEAD 1 #define PHB3_ERR_CLASS_FENCED 2 #define PHB3_ERR_CLASS_ER 3 #define PHB3_ERR_CLASS_INF 4 #define PHB3_ERR_CLASS_LAST 5 struct phb3_err { uint32_t err_src; uint32_t err_class; uint32_t err_bit; }; /* Link timeouts, increments of 100ms */ #define PHB3_LINK_WAIT_RETRIES 20 #define PHB3_LINK_ELECTRICAL_RETRIES 20 /* PHB3 flags */ #define PHB3_AIB_FENCED 0x00000001 #define PHB3_CFG_USE_ASB 0x00000002 #define PHB3_CFG_BLOCKED 0x00000004 #define PHB3_RESTORE_BUS_NUM 0x00000008 #define PHB3_CAPP_RECOVERY 0x00000010 struct phb3 { unsigned int index; /* 0..2 index inside P8 */ unsigned int flags; unsigned int chip_id; /* Chip ID (== GCID on P8) */ unsigned int rev; /* 00MMmmmm */ #define PHB3_REV_MURANO_DD10 0xa30001 #define PHB3_REV_VENICE_DD10 0xa30002 #define PHB3_REV_MURANO_DD20 0xa30003 #define PHB3_REV_MURANO_DD21 0xa30004 #define PHB3_REV_VENICE_DD20 0xa30005 #define PHB3_REV_NAPLES_DD10 0xb30001 void *regs; uint64_t pe_xscom; /* XSCOM bases */ uint64_t pci_xscom; uint64_t spci_xscom; struct lock lock; uint64_t mm0_base; /* Full MM window to PHB */ uint64_t mm0_size; /* '' '' '' */ uint64_t mm1_base; /* Full MM window to PHB */ uint64_t mm1_size; /* '' '' '' */ uint32_t base_msi; uint32_t base_lsi; /* SkiBoot owned in-memory tables */ uint64_t tbl_rtt; uint64_t tbl_peltv; uint64_t tbl_pest; uint64_t tbl_ivt; uint64_t tbl_rba; bool skip_perst; /* Skip first perst */ bool has_link; enum phb3_state state; enum phb3_state retry_state; uint64_t delay_tgt_tb; uint64_t retries; int64_t ecap; /* cached PCI-E cap offset */ int64_t aercap; /* cached AER ecap offset */ const __be64 *lane_eq; unsigned int max_link_speed; uint16_t rte_cache[RTT_TABLE_ENTRIES]; uint8_t peltv_cache[PELTV_TABLE_SIZE]; uint64_t lxive_cache[8]; uint64_t ive_cache[IVT_TABLE_ENTRIES]; uint64_t tve_cache[512]; uint64_t m32d_cache[256]; uint64_t m64b_cache[16]; uint64_t nfir_cache; /* Used by complete reset */ bool err_pending; struct phb3_err err; struct phb phb; }; static inline struct phb3 *phb_to_phb3(struct phb *phb) { return container_of(phb, struct phb3, phb); } static inline uint64_t phb3_read_reg_asb(struct phb3 *p, uint64_t offset) { uint64_t val; xscom_write(p->chip_id, p->spci_xscom, offset); xscom_read(p->chip_id, p->spci_xscom + 0x2, &val); return val; } static inline void phb3_write_reg_asb(struct phb3 *p, uint64_t offset, uint64_t val) { xscom_write(p->chip_id, p->spci_xscom, offset); xscom_write(p->chip_id, p->spci_xscom + 0x2, val); } static inline bool phb3_err_pending(struct phb3 *p) { return p->err_pending; } static inline void phb3_set_err_pending(struct phb3 *p, bool pending) { if (!pending) { p->err.err_src = PHB3_ERR_SRC_NONE; p->err.err_class = PHB3_ERR_CLASS_NONE; p->err.err_bit = -1; } p->err_pending = pending; } #endif /* __PHB3_H */ skiboot-skiboot-5.1.13/include/platform.h000066400000000000000000000126201265204436200203500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PLATFORM_H #define __PLATFORM_H /* Some fwd declarations for types used further down */ struct phb; struct pci_device; struct errorlog; enum resource_id { RESOURCE_ID_KERNEL, RESOURCE_ID_INITRAMFS, RESOURCE_ID_CAPP, }; #define RESOURCE_SUBID_NONE 0 #define RESOURCE_SUBID_SUPPORTED 1 /* * Each platform can provide a set of hooks * that can affect the generic code */ struct platform { const char *name; /* * Probe platform, return true on a match, called before * any allocation has been performed outside of the heap * so the platform can perform additional memory reservations * here if needed. * * Only the boot CPU is running at this point and the cpu_thread * structure for secondaries have not been initialized yet. The * timebases are not synchronized. * * Services available: * * - Memory allocations / reservations * - XSCOM * - FSI * - Host Services */ bool (*probe)(void); /* * This is called right after the secondary processors are brought * up and the timebases in sync to perform any additional platform * specific initializations. On FSP based machines, this is where * the FSP driver is brought up. */ void (*init)(void); /* * These are used to power down and reboot the machine */ int64_t (*cec_power_down)(uint64_t request); int64_t (*cec_reboot)(void); /* * This is called once per PHB before probing. It allows the * platform to setup some PHB private data that can be used * later on by calls such as pci_get_slot_info() below. The * "index" argument is the PHB index within the IO HUB (or * P8 chip). * * This is called before the PHB HW has been initialized. */ void (*pci_setup_phb)(struct phb *phb, unsigned int index); /* * Called during PCI scan for each device. For bridges, this is * called before its children are probed. This is called for * every device and for the PHB itself with a NULL pd though * typically the implementation will only populate the slot * info structure for bridge ports */ void (*pci_get_slot_info)(struct phb *phb, struct pci_device *pd); /* * Called after PCI probe is complete and before inventory is * displayed in console. This can either run platform fixups or * can be used to send the inventory to a service processor. */ void (*pci_probe_complete)(void); /* * If the above is set to skiboot, the handler is here */ void (*external_irq)(unsigned int chip_id); /* * nvram ops. * * Note: To keep the FSP driver simple, we only ever read the * whole nvram once at boot and we do this passing a dst buffer * that is 4K aligned. The read is asynchronous, the backend * must call nvram_read_complete() when done (it's allowed to * do it recursively from nvram_read though). */ int (*nvram_info)(uint32_t *total_size); int (*nvram_start_read)(void *dst, uint32_t src, uint32_t len); int (*nvram_write)(uint32_t dst, void *src, uint32_t len); /* * OCC timeout. This return how long we should wait for the OCC * before timing out. This lets us use a high value on larger FSP * machines and cut it off completely on BML boots and OpenPower * machines without pre-existing OCC firmware. Returns a value in * seconds. */ uint32_t (*occ_timeout)(void); int (*elog_commit)(struct errorlog *buf); /* * Initiate loading an external resource (e.g. kernel payload, OCC) * into a preallocated buffer. * This is designed to asynchronously load external resources. * Returns OPAL_SUCCESS or error. */ int (*start_preload_resource)(enum resource_id id, uint32_t idx, void *buf, size_t *len); /* * Returns true when resource is loaded. * Only has to return true once, for the * preivous start_preload_resource call for this resource. * If not implemented, will return true and start_preload_resource * *must* have synchronously done the load. * Retruns OPAL_SUCCESS, OPAL_BUSY or an error code */ int (*resource_loaded)(enum resource_id id, uint32_t idx); /* * Executed just prior to handing control over to the payload. */ void (*exit)(void); /* * Read a sensor value */ int64_t (*sensor_read)(uint32_t sensor_hndl, int token, uint32_t *sensor_data); /* * OPAL terminate */ void __attribute__((noreturn)) (*terminate)(const char *msg); }; extern struct platform __platforms_start; extern struct platform __platforms_end; extern struct platform platform; #define DECLARE_PLATFORM(name)\ static const struct platform __used __section(".platforms") name ##_platform extern void probe_platform(void); extern int start_preload_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len); extern int resource_loaded(enum resource_id id, uint32_t idx); extern int wait_for_resource_loaded(enum resource_id id, uint32_t idx); extern void mambo_sim_exit(void); #endif /* __PLATFORM_H */ skiboot-skiboot-5.1.13/include/pool.h000066400000000000000000000010071265204436200174720ustar00rootroot00000000000000#ifndef __POOL_H #define __POOL_H #include #include #include struct pool { void *buf; size_t obj_size; struct list_head free_list; int free_count; int reserved; }; enum pool_priority {POOL_NORMAL, POOL_HIGH}; void* pool_get(struct pool *pool, enum pool_priority priority) __warn_unused_result; void pool_free_object(struct pool *pool, void *obj); int pool_init(struct pool *pool, size_t obj_size, int count, int reserved) __warn_unused_result; #endif /* __POOL_H */ skiboot-skiboot-5.1.13/include/processor.h000066400000000000000000000241051265204436200205440ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PROCESSOR_H #define __PROCESSOR_H #include /* P7 MSR bits */ #define MSR_SF PPC_BIT(0) /* 64-bit mode */ #define MSR_HV PPC_BIT(3) /* Hypervisor mode */ #define MSR_VEC PPC_BIT(38) /* VMX enable */ #define MSR_VSX PPC_BIT(40) /* VSX enable */ #define MSR_EE PPC_BIT(48) /* External Int. Enable */ #define MSR_PR PPC_BIT(49) /* Problem state */ #define MSR_FP PPC_BIT(50) /* Floating Point Enable */ #define MSR_ME PPC_BIT(51) /* Machine Check Enable */ #define MSR_FE0 PPC_BIT(52) /* FP Exception 0 */ #define MSR_SE PPC_BIT(53) /* Step enable */ #define MSR_BE PPC_BIT(54) /* Branch trace enable */ #define MSR_FE1 PPC_BIT(55) /* FP Exception 1 */ #define MSR_IR PPC_BIT(58) /* Instructions reloc */ #define MSR_DR PPC_BIT(59) /* Data reloc */ #define MSR_PMM PPC_BIT(61) /* Perf Monitor */ #define MSR_RI PPC_BIT(62) /* Recoverable Interrupt */ #define MSR_LE PPC_BIT(63) /* Little Endian */ /* PIR */ #define SPR_PIR_P8_THREAD_MASK 0x0007 /* Mask of thread bits */ #define SPR_PIR_P8_MASK 0x1fff /* Mask of implemented bits */ #define SPR_PIR_P7_THREAD_MASK 0x0003 /* Mask of thread bits */ #define SPR_PIR_P7_MASK 0x03ff /* Mask of implemented bits */ /* SPR register definitions */ #define SPR_DSISR 0x012 /* RW: Data storage interrupt status reg */ #define SPR_DAR 0x013 /* RW: Data address reg */ #define SPR_DEC 0x016 /* RW: Decrement Register */ #define SPR_SDR1 0x019 #define SPR_SRR0 0x01a /* RW: Exception save/restore reg 0 */ #define SPR_SRR1 0x01b /* RW: Exception save/restore reg 1 */ #define SPR_CFAR 0x01c /* RW: Come From Address Register */ #define SPR_RPR 0x0ba /* RW: Relative Priority Register */ #define SPR_TBRL 0x10c /* RO: Timebase low */ #define SPR_TBRU 0x10d /* RO: Timebase high */ #define SPR_SPRC 0x114 /* RW: Access to uArch SPRs (ex SCOMC) */ #define SPR_SPRD 0x115 /* RW: Access to uArch SPRs (ex SCOMD) */ #define SPR_SCOMC 0x114 /* RW: SCOM Control - old name of SPRC */ #define SPR_SCOMD 0x115 /* RW: SCOM Data - old name of SPRD */ #define SPR_TBWL 0x11c /* RW: Timebase low */ #define SPR_TBWU 0x11d /* RW: Timebase high */ #define SPR_TBU40 0x11e /* RW: Timebase Upper 40 bit */ #define SPR_PVR 0x11f /* RO: Processor version register */ #define SPR_HSPRG0 0x130 /* RW: Hypervisor scratch 0 */ #define SPR_HSPRG1 0x131 /* RW: Hypervisor scratch 1 */ #define SPR_SPURR 0x134 /* RW: Scaled Processor Utilization Resource */ #define SPR_PURR 0x135 /* RW: Processor Utilization Resource reg */ #define SPR_HDEC 0x136 /* RW: Hypervisor Decrementer */ #define SPR_HSRR0 0x13a /* RW: HV Exception save/restore reg 0 */ #define SPR_HSRR1 0x13b /* RW: HV Exception save/restore reg 1 */ #define SPR_TFMR 0x13d #define SPR_LPCR 0x13e #define SPR_HMER 0x150 /* Hypervisor Maintenance Exception */ #define SPR_HMEER 0x151 /* HMER interrupt enable mask */ #define SPR_AMOR 0x15d #define SPR_TSCR 0x399 #define SPR_HID0 0x3f0 #define SPR_HID1 0x3f1 #define SPR_HID2 0x3f8 #define SPR_HID4 0x3f4 #define SPR_HID5 0x3f6 #define SPR_PIR 0x3ff /* RO: Processor Identification */ /* Bits in LPCR */ /* Powersave Exit Cause Enable is different for P7 and P8 */ #define SPR_LPCR_P7_PECE PPC_BITMASK(49,51) #define SPR_LPCR_P7_PECE0 PPC_BIT(49) /* Wake on external interrupts */ #define SPR_LPCR_P7_PECE1 PPC_BIT(50) /* Wake on decrementer */ #define SPR_LPCR_P7_PECE2 PPC_BIT(51) /* Wake on MCs, HMIs, etc... */ #define SPR_LPCR_P8_PECE PPC_BITMASK(47,51) #define SPR_LPCR_P8_PECE0 PPC_BIT(47) /* Wake on priv doorbell */ #define SPR_LPCR_P8_PECE1 PPC_BIT(48) /* Wake on hv doorbell */ #define SPR_LPCR_P8_PECE2 PPC_BIT(49) /* Wake on external interrupts */ #define SPR_LPCR_P8_PECE3 PPC_BIT(50) /* Wake on decrementer */ #define SPR_LPCR_P8_PECE4 PPC_BIT(51) /* Wake on MCs, HMIs, etc... */ /* Bits in TFMR - control bits */ #define SPR_TFMR_MAX_CYC_BET_STEPS PPC_BITMASK(0,7) #define SPR_TFMR_N_CLKS_PER_STEP PPC_BITMASK(8,9) #define SPR_TFMR_MASK_HMI PPC_BIT(10) #define SPR_TFMR_SYNC_BIT_SEL PPC_BITMASK(11,13) #define SPR_TFMR_TB_ECLIPZ PPC_BIT(14) #define SPR_TFMR_LOAD_TOD_MOD PPC_BIT(16) #define SPR_TFMR_MOVE_CHIP_TOD_TO_TB PPC_BIT(18) #define SPR_TFMR_CLEAR_TB_ERRORS PPC_BIT(24) /* Bits in TFMR - thread indep. status bits */ #define SPR_TFMR_HDEC_PARITY_ERROR PPC_BIT(26) #define SPR_TFMR_TBST_CORRUPT PPC_BIT(27) #define SPR_TFMR_TBST_ENCODED PPC_BITMASK(28,31) #define SPR_TFMR_TBST_LAST PPC_BITMASK(32,35) #define SPR_TFMR_TB_ENABLED PPC_BIT(40) #define SPR_TFMR_TB_VALID PPC_BIT(41) #define SPR_TFMR_TB_SYNC_OCCURED PPC_BIT(42) #define SPR_TFMR_TB_MISSING_SYNC PPC_BIT(43) #define SPR_TFMR_TB_MISSING_STEP PPC_BIT(44) #define SPR_TFMR_TB_RESIDUE_ERR PPC_BIT(45) #define SPR_TFMR_FW_CONTROL_ERR PPC_BIT(46) #define SPR_TFMR_CHIP_TOD_STATUS PPC_BITMASK(47,50) #define SPR_TFMR_CHIP_TOD_INTERRUPT PPC_BIT(51) #define SPR_TFMR_CHIP_TOD_TIMEOUT PPC_BIT(54) #define SPR_TFMR_CHIP_TOD_PARITY_ERR PPC_BIT(56) /* Bits in TFMR - thread specific. status bits */ #define SPR_TFMR_PURR_PARITY_ERR PPC_BIT(57) #define SPR_TFMR_SPURR_PARITY_ERR PPC_BIT(58) #define SPR_TFMR_DEC_PARITY_ERR PPC_BIT(59) #define SPR_TFMR_TFMR_CORRUPT PPC_BIT(60) #define SPR_TFMR_PURR_OVERFLOW PPC_BIT(61) #define SPR_TFMR_SPURR_OVERFLOW PPC_BIT(62) /* Bits in HMER/HMEER */ #define SPR_HMER_MALFUNCTION_ALERT PPC_BIT(0) #define SPR_HMER_PROC_RECV_DONE PPC_BIT(2) #define SPR_HMER_PROC_RECV_ERROR_MASKED PPC_BIT(3) #define SPR_HMER_TFAC_ERROR PPC_BIT(4) #define SPR_HMER_TFMR_PARITY_ERROR PPC_BIT(5) #define SPR_HMER_XSCOM_FAIL PPC_BIT(8) #define SPR_HMER_XSCOM_DONE PPC_BIT(9) #define SPR_HMER_PROC_RECV_AGAIN PPC_BIT(11) #define SPR_HMER_WARN_RISE PPC_BIT(14) #define SPR_HMER_WARN_FALL PPC_BIT(15) #define SPR_HMER_SCOM_FIR_HMI PPC_BIT(16) #define SPR_HMER_TRIG_FIR_HMI PPC_BIT(17) #define SPR_HMER_HYP_RESOURCE_ERR PPC_BIT(20) #define SPR_HMER_XSCOM_STATUS PPC_BITMASK(21,23) /* * HMEER: initial bits for HMI interrupt enable mask. * Per Dave Larson, never enable 8,9,21-23 */ #define SPR_HMEER_HMI_ENABLE_MASK (SPR_HMER_MALFUNCTION_ALERT |\ SPR_HMER_HYP_RESOURCE_ERR |\ SPR_HMER_PROC_RECV_DONE |\ SPR_HMER_PROC_RECV_ERROR_MASKED |\ SPR_HMER_TFAC_ERROR |\ SPR_HMER_TFMR_PARITY_ERROR |\ SPR_HMER_PROC_RECV_AGAIN) /* Bits in HID0 */ #define SPR_HID0_POWER8_4LPARMODE PPC_BIT(2) #define SPR_HID0_POWER8_2LPARMODE PPC_BIT(6) #define SPR_HID0_HILE PPC_BIT(19) #define SPR_HID0_ENABLE_ATTN PPC_BIT(31) /* PVR bits */ #define SPR_PVR_TYPE 0xffff0000 #define SPR_PVR_VERS_MAJ 0x00000f00 #define SPR_PVR_VERS_MIN 0x000000ff #define PVR_TYPE(_pvr) GETFIELD(SPR_PVR_TYPE, _pvr) #define PVR_VERS_MAJ(_pvr) GETFIELD(SPR_PVR_VERS_MAJ, _pvr) #define PVR_VERS_MIN(_pvr) GETFIELD(SPR_PVR_VERS_MIN, _pvr) /* PVR definitions */ #define PVR_TYPE_P7 0x003f #define PVR_TYPE_P7P 0x004a #define PVR_TYPE_P8E 0x004b /* Murano */ #define PVR_TYPE_P8 0x004d /* Venice */ #define PVR_TYPE_P8NVL 0x004c /* Naples */ #ifdef __ASSEMBLY__ /* Thread priority control opcodes */ #define smt_low or 1,1,1 #define smt_medium or 2,2,2 #define smt_high or 3,3,3 #define smt_medium_high or 5,5,5 #define smt_medium_low or 6,6,6 #define smt_extra_high or 7,7,7 #define smt_very_low or 31,31,31 #else /* __ASSEMBLY__ */ #include #include /* * SMT priority */ static inline void smt_low(void) { asm volatile("or 1,1,1"); } static inline void smt_medium(void) { asm volatile("or 2,2,2"); } static inline void smt_high(void) { asm volatile("or 3,3,3"); } static inline void smt_medium_high(void){ asm volatile("or 5,5,5"); } static inline void smt_medium_low(void) { asm volatile("or 6,6,6"); } static inline void smt_extra_high(void) { asm volatile("or 7,7,7"); } static inline void smt_very_low(void) { asm volatile("or 31,31,31"); } /* * SPR access functions */ static inline unsigned long mfmsr(void) { unsigned long val; asm volatile("mfmsr %0" : "=r"(val) : : "memory"); return val; } static inline void mtmsr(unsigned long val) { asm volatile("mtmsr %0" : : "r"(val) : "memory"); } static inline void mtmsrd(unsigned long val, int l) { asm volatile("mtmsrd %0,%1" : : "r"(val), "i"(l) : "memory"); } static inline unsigned long mfspr(unsigned int spr) { unsigned long val; asm volatile("mfspr %0,%1" : "=r"(val) : "i"(spr) : "memory"); return val; } static inline void mtspr(unsigned int spr, unsigned long val) { asm volatile("mtspr %0,%1" : : "i"(spr), "r"(val) : "memory"); } /* Helpers for special sequences needed by some registers */ extern void set_hid0(unsigned long hid0); extern void trigger_attn(void); /* * Barriers */ static inline void eieio(void) { asm volatile("eieio" : : : "memory"); } static inline void sync(void) { asm volatile("sync" : : : "memory"); } static inline void lwsync(void) { asm volatile("lwsync" : : : "memory"); } static inline void isync(void) { asm volatile("isync" : : : "memory"); } /* * Cache sync */ static inline void sync_icache(void) { asm volatile("sync; icbi 0,%0; sync; isync" : : "r" (0) : "memory"); } /* * Byteswap load/stores */ static inline uint16_t ld_le16(const uint16_t *addr) { uint16_t val; asm volatile("lhbrx %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr)); return val; } static inline uint32_t ld_le32(const uint32_t *addr) { uint32_t val; asm volatile("lwbrx %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr)); return val; } static inline void st_le16(uint16_t *addr, uint16_t val) { asm volatile("sthbrx %0,0,%1" : : "r"(val), "r"(addr), "m"(*addr)); } static inline void st_le32(uint32_t *addr, uint32_t val) { asm volatile("stwbrx %0,0,%1" : : "r"(val), "r"(addr), "m"(*addr)); } #endif /* __ASSEMBLY__ */ #endif /* __PROCESSOR_H */ skiboot-skiboot-5.1.13/include/psi.h000066400000000000000000000206021265204436200173160ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * IBM System P PSI (Processor Service Interface) */ #ifndef __PSI_H #define __PSI_H #include /* * PSI Host Bridge Registers (MMIO) * * The PSI interface is the bridge to the FPS, it has its own * registers. The FSP registers appear at an offset within the * aperture defined by the PSI_FSPBAR */ /* Base address of the PSI MMIO space and LSB is the enable/valid bit */ #define PSIHB_BBAR 0x00 /* FSP MMIO region -- this is where the mbx regs are (offset defined below) */ #define PSIHB_FSPBAR 0x08 /* FSP MMIO region mask register -- determines size of region */ #define PSIHB_FSPMMR 0x10 /* TCE address register */ #define PSIHB_TAR 0x18 #define PSIHB_TAR_8K_ENTRIES 0 #define PSIHB_TAR_16K_ENTRIES 1 #define PSIHB_TAR_256K_ENTRIES 2 /* P8 only */ #define PSIHB_TAR_512K_ENTRIES 4 /* P8 only */ /* PSI Host Bridge Control Register * * note: TCE_ENABLE moved to the new PSIHB_PHBSCR on P8 but is * the same bit position */ #define PSIHB_CR 0x20 #define PSIHB_CR_FSP_CMD_ENABLE PPC_BIT(0) #define PSIHB_CR_FSP_MMIO_ENABLE PPC_BIT(1) #define PSIHB_CR_TCE_ENABLE PPC_BIT(2) /* P7 only */ #define PSIHB_CR_FSP_IRQ_ENABLE PPC_BIT(3) #define PSIHB_CR_FSP_ERR_RSP_ENABLE PPC_BIT(4) #define PSIHB_CR_PSI_LINK_ENABLE PPC_BIT(5) #define PSIHB_CR_FSP_RESET PPC_BIT(6) #define PSIHB_CR_PSIHB_RESET PPC_BIT(7) #define PSIHB_CR_PSI_IRQ PPC_BIT(16) /* PSIHB interrupt */ #define PSIHB_CR_FSP_IRQ PPC_BIT(17) /* FSP interrupt */ #define PSIHB_CR_FSP_LINK_ACTIVE PPC_BIT(18) /* FSP link active */ /* Error conditions in the GXHB */ #define PSIHB_CR_PSI_ERROR PPC_BIT(32) /* PSI error */ #define PSIHB_CR_PSI_LINK_INACTIVE PPC_BIT(33) /* Link inactive */ #define PSIHB_CR_FSP_ACK_TIMEOUT PPC_BIT(34) /* FSP ack timeout */ #define PSIHB_CR_MMIO_LOAD_TIMEOUT PPC_BIT(35) /* MMIO load timeout */ #define PSIHB_CR_MMIO_LENGTH_ERROR PPC_BIT(36) /* MMIO length error */ #define PSIHB_CR_MMIO_ADDRESS_ERROR PPC_BIT(37) /* MMIO address error */ #define PSIHB_CR_MMIO_TYPE_ERROR PPC_BIT(38) /* MMIO type error */ #define PSIHB_CR_UE PPC_BIT(39) /* UE detected */ #define PSIHB_CR_PARITY_ERROR PPC_BIT(40) /* Parity error */ #define PSIHB_CR_SYNC_ERR_ALERT1 PPC_BIT(41) /* Sync alert 1 */ #define PSIHB_CR_SYNC_ERR_ALERT2 PPC_BIT(42) /* Sync alert 2 */ #define PSIHB_CR_FSP_COMMAND_ERROR PPC_BIT(43) /* FSP cmd error */ /* PSI Status / Error Mask Register */ #define PSIHB_SEMR 0x28 /* XIVR and BUID used for PSI interrupts on P7 */ #define PSIHB_XIVR 0x30 /* XIVR and BUID used for PSI interrupts on P8 */ #define PSIHB_XIVR_FSP 0x30 #define PSIHB_XIVR_OCC 0x60 #define PSIHB_XIVR_FSI 0x68 #define PSIHB_XIVR_LPC 0x70 #define PSIHB_XIVR_LOCAL_ERR 0x78 #define PSIHB_XIVR_HOST_ERR 0x80 #define PSIHB_IRSN 0x88 #define PSIHB_IRSN_COMP PPC_BITMASK(0,18) #define PSIHB_IRSN_IRQ_MUX PPC_BIT(28) #define PSIHB_IRSN_IRQ_RESET PPC_BIT(29) #define PSIHB_IRSN_DOWNSTREAM_EN PPC_BIT(30) #define PSIHB_IRSN_UPSTREAM_EN PPC_BIT(31) #define PSIHB_IRSN_MASK PPC_BITMASK(32,50) #define PSIHB_IRQ_STATUS 0x58 #define PSIHB_IRQ_STAT_OCC PPC_BIT(27) #define PSIHB_IRQ_STAT_FSI PPC_BIT(28) #define PSIHB_IRQ_STAT_LPC PPC_BIT(29) #define PSIHB_IRQ_STAT_LOCAL_ERR PPC_BIT(30) #define PSIHB_IRQ_STAT_HOST_ERR PPC_BIT(31) /* Secure version of CR for P8 (TCE enable bit) */ #define PSIHB_PHBSCR 0x90 /* * PSI Host Bridge Registers (XSCOM) */ #define PSIHB_XSCOM_P7_HBBAR 0x9 #define PSIHB_XSCOM_P7_HBBAR_EN PPC_BIT(28) #define PSIHB_XSCOM_P7_HBCSR 0xd #define PSIHB_XSCOM_P7_HBCSR_SET 0x11 #define PSIHB_XSCOM_P7_HBCSR_CLR 0x12 #define PSIHB_XSCOM_P7_HBSCR_FSP_IRQ PPC_BIT(13) #define PSIHB_XSCOM_P8_BASE 0xa #define PSIHB_XSCOM_P8_HBBAR_EN PPC_BIT(63) #define PSIHB_XSCOM_P8_HBCSR 0xe #define PSIHB_XSCOM_P8_HBCSR_SET 0x12 #define PSIHB_XSCOM_P8_HBCSR_CLR 0x13 #define PSIHB_XSCOM_P8_HBSCR_FSP_IRQ PPC_BIT(17) /* * Layout of the PSI DMA address space * * On P7, we instanciate a TCE table of 16K TCEs mapping 64M * * On P8, we use a larger mapping of 256K TCEs which provides * us with a 1G window in order to fit the trace buffers * * Currently we have: * * - 4x256K serial areas (each divided in 2: in and out buffers) * - 1M region for inbound buffers * - 2M region for generic data fetches */ #define PSI_DMA_SER0_BASE 0x00000000 #define PSI_DMA_SER0_SIZE 0x00040000 #define PSI_DMA_SER1_BASE 0x00040000 #define PSI_DMA_SER1_SIZE 0x00040000 #define PSI_DMA_SER2_BASE 0x00080000 #define PSI_DMA_SER2_SIZE 0x00040000 #define PSI_DMA_SER3_BASE 0x000c0000 #define PSI_DMA_SER3_SIZE 0x00040000 #define PSI_DMA_INBOUND_BUF 0x00100000 #define PSI_DMA_INBOUND_SIZE 0x00100000 #define PSI_DMA_FETCH 0x00200000 #define PSI_DMA_FETCH_SIZE 0x00800000 #define PSI_DMA_NVRAM_BODY 0x00a00000 #define PSI_DMA_NVRAM_BODY_SZ 0x00100000 #define PSI_DMA_NVRAM_TRIPL 0x00b00000 #define PSI_DMA_NVRAM_TRIPL_SZ 0x00001000 #define PSI_DMA_OP_PANEL_MISC 0x00b01000 #define PSI_DMA_OP_PANEL_SIZE 0x00001000 #define PSI_DMA_GET_SYSPARAM 0x00b02000 #define PSI_DMA_GET_SYSPARAM_SZ 0x00001000 #define PSI_DMA_SET_SYSPARAM 0x00b03000 #define PSI_DMA_SET_SYSPARAM_SZ 0x00001000 #define PSI_DMA_ERRLOG_READ_BUF 0x00b04000 #define PSI_DMA_ERRLOG_READ_BUF_SZ 0x00040000 #define PSI_DMA_ELOG_PANIC_WRITE_BUF 0x00b44000 #define PSI_DMA_ELOG_PANIC_WRITE_BUF_SZ 0x00010000 #define PSI_DMA_ERRLOG_WRITE_BUF 0x00b54000 #define PSI_DMA_ERRLOG_WRITE_BUF_SZ 0x00040000 #define PSI_DMA_ELOG_WR_TO_HOST_BUF 0x00b94000 /* Unused */ #define PSI_DMA_ELOG_WR_TO_HOST_BUF_SZ 0x00010000 #define PSI_DMA_HBRT_LOG_WRITE_BUF 0x00ba4000 #define PSI_DMA_HBRT_LOG_WRITE_BUF_SZ 0x00001000 #define PSI_DMA_CODE_UPD 0x00c04000 #define PSI_DMA_CODE_UPD_SIZE 0x01001000 #define PSI_DMA_DUMP_DATA 0x01c05000 #define PSI_DMA_DUMP_DATA_SIZE 0x00500000 #define PSI_DMA_SENSOR_BUF 0x02105000 #define PSI_DMA_SENSOR_BUF_SZ 0x00080000 #define PSI_DMA_MDST_TABLE 0x02185000 #define PSI_DMA_MDST_TABLE_SIZE 0x00001000 #define PSI_DMA_HYP_DUMP 0x02186000 #define PSI_DMA_HYP_DUMP_SIZE 0x01000000 #define PSI_DMA_PCIE_INVENTORY 0x03186000 #define PSI_DMA_PCIE_INVENTORY_SIZE 0x00010000 #define PSI_DMA_LED_BUF 0x03196000 #define PSI_DMA_LED_BUF_SZ 0x00001000 #define PSI_DMA_LOC_COD_BUF 0x03197000 #define PSI_DMA_LOC_COD_BUF_SZ 0x00008000 #define PSI_DMA_MEMCONS 0x0319f000 #define PSI_DMA_MEMCONS_SZ 0x00001000 #define PSI_DMA_LOG_BUF 0x03200000 #define PSI_DMA_LOG_BUF_SZ 0x00100000 /* INMEM_CON_LEN */ #define PSI_DMA_PLAT_REQ_BUF 0x03300000 #define PSI_DMA_PLAT_REQ_BUF_SIZE 0x00001000 #define PSI_DMA_PLAT_RESP_BUF 0x03301000 #define PSI_DMA_PLAT_RESP_BUF_SIZE 0x00001000 /* P8 only mappings */ #define PSI_DMA_TRACE_BASE 0x04000000 struct psi { struct list_node list; uint64_t xscom_base; void *regs; unsigned int chip_id; unsigned int interrupt; bool working; bool active; }; extern void psi_set_link_polling(bool active); extern struct psi *first_psi; extern void psi_init(void); extern struct psi *psi_find_link(uint32_t chip_id); extern void psi_init_for_fsp(struct psi *psi); extern void psi_disable_link(struct psi *psi); extern void psi_reset_fsp(struct psi *psi); extern bool psi_check_link_active(struct psi *psi); extern bool psi_poll_fsp_interrupt(struct psi *psi); extern struct psi *psi_find_functional_chip(void); /* Interrupts */ extern void psi_irq_reset(void); extern void psi_enable_fsp_interrupt(struct psi *psi); extern void psi_fsp_link_in_use(struct psi *psi); /* * Must be called by the platform probe() function as the policy * is established before platform.init * * This defines whether the external interrupt should be passed to * the OS or handled locally in skiboot. Return true for skiboot * handling. Default if not called is Linux. */ #define EXTERNAL_IRQ_POLICY_LINUX false #define EXTERNAL_IRQ_POLICY_SKIBOOT true extern void psi_set_external_irq_policy(bool policy); #endif /* __PSI_H */ skiboot-skiboot-5.1.13/include/rtc.h000066400000000000000000000022021265204436200173070ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __RTC_H #define __RTC_H #include /* * Update the cache to the current time as specified by tm. */ void rtc_cache_update(struct tm *tm); /* * Get the current time based on the cache. If the cache is valid the result * is returned in tm and the function returns 0. Otherwise returns -1. */ int rtc_cache_get(struct tm *tm); /* * Same as the previous function except the result is returned as an OPAL * datetime. */ int rtc_cache_get_datetime(uint32_t *year_month_day, uint64_t *hour_minute_second_millisecond); #endif skiboot-skiboot-5.1.13/include/sensor.h000066400000000000000000000033261265204436200200400ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SENSOR_H #define __SENSOR_H /* * A sensor handler is a four bytes value which identifies a sensor by * its resource class (temperature, fans ...), a resource identifier * and an attribute number (data, status, ...) : * * Res. * | Attr. | Class | Resource Id | * |--------|--------|----------------| * * * Helper routines to build or use the sensor handler. */ #define sensor_make_handler(sensor_class, sensor_rid, sensor_attr) \ (((sensor_attr) << 24) | ((sensor_class) & 0xff) << 16 | \ ((sensor_rid) & 0xffff)) #define sensor_get_frc(handler) (((handler) >> 16) & 0xff) #define sensor_get_rid(handler) ((handler) & 0xffff) #define sensor_get_attr(handler) ((handler) >> 24) /* * Sensor families * * This identifier is used to dispatch calls to OPAL_SENSOR_READ to * the appropriate component. FSP is the initial family. */ #define SENSOR_FSP 0x0 #define SENSOR_DTS 0x80 #define sensor_is_dts(handler) (sensor_get_frc(handler) & SENSOR_DTS) /* * root node of all sensors : /ibm,opal/sensors */ extern struct dt_node *sensor_node; extern void sensor_init(void); #endif /* __SENSOR_H */ skiboot-skiboot-5.1.13/include/sfc-ctrl.h000066400000000000000000000014221265204436200202370ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SFC_CTRL_H #define SFC_CTRL_H struct spi_flash_ctrl; extern int sfc_open(struct spi_flash_ctrl **ctrl); extern void sfc_close(struct spi_flash_ctrl *ctrl); #endif /* SFC_CTRL_H */ skiboot-skiboot-5.1.13/include/skiboot.h000066400000000000000000000161721265204436200202040ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SKIBOOT_H #define __SKIBOOT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Special ELF sections */ #define __force_data __section(".force.data") /* Readonly section start and end. */ extern char __rodata_start[], __rodata_end[]; struct mem_region; extern struct mem_region *mem_region_next(struct mem_region *region); static inline bool is_rodata(const void *p) { return ((const char *)p >= __rodata_start && (const char *)p < __rodata_end); } #define OPAL_BOOT_COMPLETE 0x1 /* Debug descriptor. This structure is pointed to by the word at offset * 0x80 in the sapphire binary */ struct debug_descriptor { u8 eye_catcher[8]; /* "OPALdbug" */ #define DEBUG_DESC_VERSION 1 u32 version; u8 console_log_levels; /* high 4 bits in memory, * low 4 bits driver (e.g. uart). */ u8 state_flags; /* various state flags - OPAL_BOOT_COMPLETE etc */ u16 reserved2; u32 reserved[2]; /* Memory console */ u64 memcons_phys; u32 memcons_tce; u32 memcons_obuf_tce; u32 memcons_ibuf_tce; /* Traces */ u64 trace_mask; u32 num_traces; #define DEBUG_DESC_MAX_TRACES 256 u64 trace_phys[DEBUG_DESC_MAX_TRACES]; u32 trace_size[DEBUG_DESC_MAX_TRACES]; u32 trace_tce[DEBUG_DESC_MAX_TRACES]; }; extern struct debug_descriptor debug_descriptor; /* Console logging */ #define PR_EMERG 0 #define PR_ALERT 1 #define PR_CRIT 2 #define PR_ERR 3 #define PR_WARNING 4 #define PR_NOTICE 5 #define PR_PRINTF PR_NOTICE #define PR_INFO 6 #define PR_DEBUG 7 #define PR_TRACE 8 #define PR_INSANE 9 #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif void _prlog(int log_level, const char* fmt, ...) __attribute__((format (printf, 2, 3))); #define prlog(l, f, ...) do { _prlog(l, pr_fmt(f), ##__VA_ARGS__); } while(0) #define prerror(fmt...) do { prlog(PR_ERR, fmt); } while(0) #define prlog_once(arg, ...) \ ({ \ static bool __prlog_once = false; \ if (!__prlog_once) { \ __prlog_once = true; \ prlog(arg, ##__VA_ARGS__); \ } \ }) /* Location codes -- at most 80 chars with null termination */ #define LOC_CODE_SIZE 80 /* Processor generation */ enum proc_gen { proc_gen_unknown, proc_gen_p7, /* P7 and P7+ */ proc_gen_p8, }; extern enum proc_gen proc_gen; /* Convert a 4-bit number to a hex char */ extern char __attrconst tohex(uint8_t nibble); /* Bit position of the most significant 1-bit (LSB=0, MSB=63) */ static inline int ilog2(unsigned long val) { int left_zeros; asm volatile ("cntlzd %0,%1" : "=r" (left_zeros) : "r" (val)); return 63 - left_zeros; } static inline bool is_pow2(unsigned long val) { return val == (1ul << ilog2(val)); } #define lo32(x) ((x) & 0xffffffff) #define hi32(x) (((x) >> 32) & 0xffffffff) /* WARNING: _a *MUST* be a power of two */ #define ALIGN_UP(_v, _a) (((_v) + (_a) - 1) & ~((_a) - 1)) #define ALIGN_DOWN(_v, _a) ((_v) & ~((_a) - 1)) /* TCE alignment */ #define TCE_SHIFT 12 #define TCE_PSIZE (1ul << 12) #define TCE_MASK (TCE_PSIZE - 1) /* Not the greatest variants but will do for now ... */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) /* Clean the stray high bit which the FSP inserts: we only have 52 bits real */ static inline u64 cleanup_addr(u64 addr) { return addr & ((1ULL << 52) - 1); } /* Start the kernel */ extern void start_kernel(uint64_t entry, void* fdt, uint64_t mem_top) __noreturn; extern void start_kernel32(uint64_t entry, void* fdt, uint64_t mem_top) __noreturn; extern void start_kernel_secondary(uint64_t entry) __noreturn; /* Get description of machine from HDAT and create device-tree */ extern void parse_hdat(bool is_opal, uint32_t master_cpu); /* Root of device tree. */ extern struct dt_node *dt_root; /* Full skiboot version number (possibly includes gitid). */ extern const char version[]; /* Debug support */ extern char __sym_map_start[]; extern char __sym_map_end[]; extern unsigned long get_symbol(unsigned long addr, char **sym, char **sym_end); /* Fast reboot support */ extern void fast_reset(void); extern void __noreturn __secondary_cpu_entry(void); extern void __noreturn load_and_boot_kernel(bool is_reboot); extern void cleanup_tlb(void); extern void init_shared_sprs(void); extern void init_replicated_sprs(void); /* Various probe routines, to replace with an initcall system */ extern void probe_p5ioc2(void); extern void probe_p7ioc(void); extern void probe_phb3(void); extern int phb3_preload_capp_ucode(void); extern void phb3_preload_vpd(void); extern void uart_init(bool enable_interrupt); extern void homer_init(void); extern void occ_pstates_init(void); extern void slw_init(void); extern void occ_fsp_init(void); extern void lpc_rtc_init(void); /* flash support */ struct flash_chip; extern int flash_register(struct blocklevel_device *bl, bool is_system_flash); extern int flash_start_preload_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len); extern int flash_resource_loaded(enum resource_id id, uint32_t idx); extern bool flash_reserve(void); extern void flash_release(void); /* NVRAM support */ extern void nvram_init(void); extern void nvram_read_complete(bool success); /* UART stuff */ extern void uart_setup_linux_passthrough(void); extern void uart_setup_opal_console(void); /* OCC interrupt */ extern void occ_interrupt(uint32_t chip_id); extern void occ_send_dummy_interrupt(void); /* OCC load support */ extern void occ_poke_load_queue(void); /* OCC/Host PNOR ownership */ enum pnor_owner { PNOR_OWNER_HOST, PNOR_OWNER_EXTERNAL, }; extern void occ_pnor_set_owner(enum pnor_owner owner); /* PRD */ extern void prd_psi_interrupt(uint32_t proc); extern void prd_tmgt_interrupt(uint32_t proc); extern void prd_occ_reset(uint32_t proc); extern void prd_init(void); extern void prd_register_reserved_memory(void); /* Flatten device-tree */ extern void *create_dtb(const struct dt_node *root); /* SLW reinit function for switching core settings */ extern int64_t slw_reinit(uint64_t flags); /* SLW update timer function */ extern void slw_update_timer_expiry(uint64_t new_target); /* Is SLW timer available ? */ extern bool slw_timer_ok(void); /* Fallback fake RTC */ extern void fake_rtc_init(void); #endif /* __SKIBOOT_H */ skiboot-skiboot-5.1.13/include/spcn.h000066400000000000000000000070371265204436200174750ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* */ #ifndef __SPCN_H #define __SPCN_H /* SPCN commands */ #define SPCN_CMD_PRS 0x42 /* Power Resource Status */ #define SPCN_CMD_SET 0x66 /* Set Environmental Thresholds */ /* SPCN command address modes */ #define SPCN_ADDR_MODE_CEC_NODE 0x0000d000 /* CEC node single destination */ #define SPCN_ADDR_MODE_ALL_SLAVES 0x0000f000 /* Address all slaves in all racks */ #define SPCN_ADDR_MODE_RACK_NODES 0x00000000 /* Address rack node in all racks */ /* SPCN PRS command modifiers */ #define SPCN_MOD_PRS_STATUS_FIRST 0x01 /* Power Resource Status (First 1KB) */ #define SPCN_MOD_PRS_STATUS_SUBS 0x02 /* Subsequent set of 1KB PRS entries */ #define SPCN_MOD_PRS_LED_DATA_FIRST 0x51 /* LED data entry (First 1KB) */ #define SPCN_MOD_PRS_LED_DATA_SUB 0x52 /* Subsequent LED data entries */ /* SPCN SET command modifiers */ #define SPCN_MOD_SET_LED_CTL_LOC_CODE 0x07 /* Control LED with location code */ #define SPCN_MOD_SET_IDENTIFY_OFF_ENC 0x08 /* Turn off identify LEDs in CEC */ #define SPCN_MOD_SET_IDENTIFY_OFF_NODE 0x0B /* Turn off identify LEDs in Node */ /* SPCN SENSOR command modifiers */ #define SPCN_MOD_SENSOR_PARAM_FIRST 0x10 /* First 1K sensor parameters */ #define SPCN_MOD_SENSOR_PARAM_SUBS 0x11 /* Subsequent sensor parameters */ #define SPCN_MOD_SENSOR_DATA_FIRST 0x12 /* First 1K sensor data */ #define SPCN_MOD_SENSOR_DATA_SUBS 0x13 /* Subsequent sensor data blocks */ #define SPCN_MOD_PROC_JUNC_TEMP 0x14 /* Process junction temperatures */ #define SPCN_MOD_SENSOR_POWER 0x1c /* System power consumption */ #define SPCN_MOD_LAST 0xff /* * Modifiers 0x53 and 0x54 are used by LEDS at standby. So HV does not come into * the picture here. Do we need those? */ /* Supported SPCN response codes */ #define LOGICAL_IND_STATE_MASK 0x10 /* If set, control fault state */ #define ACTIVE_LED_STATE_MASK 0x01 /* If set, switch on the LED */ #define SPCN_LED_IDENTIFY_MASK 0x80 /* Set identify indicator */ #define SPCN_LED_FAULT_MASK 0x40 /* Set fault indicator */ #define SPCN_LED_TRANS_MASK 0x20 /* LED is in transition */ #define SPCN_CLR_LED_STATE 0x00 /* Reset identify indicator */ /* SPCN command response status codes */ enum spcn_rsp_status { SPCN_RSP_STATUS_SUCCESS = 0x01, /* Command successful */ SPCN_RSP_STATUS_COND_SUCCESS = 0x02, /* Command successful, but additional entries exist */ SPCN_RSP_STATUS_INVALID_RACK = 0x15, /* Invalid rack command */ SPCN_RSP_STATUS_INVALID_SLAVE = 0x16, /* Invalid slave command */ SPCN_RSP_STATUS_INVALID_MOD = 0x18, /* Invalid modifier */ SPCN_RSP_STATUS_STATE_PROHIBIT = 0x21, /* Present state prohibits */ SPCN_RSP_STATUS_UNKNOWN = 0xff, /* Default state */ }; /* Sensor FRCs (Frame resource class) */ enum { SENSOR_FRC_POWER_CTRL = 0x02, SENSOR_FRC_POWER_SUPPLY, SENSOR_FRC_REGULATOR, SENSOR_FRC_COOLING_FAN, SENSOR_FRC_COOLING_CTRL, SENSOR_FRC_BATTERY_CHRG, SENSOR_FRC_BATTERY_PACK, SENSOR_FRC_AMB_TEMP, SENSOR_FRC_TEMP, SENSOR_FRC_VRM, SENSOR_FRC_RISER_CARD, SENSOR_FRC_IO_BP, }; #endif /* __SPCN_H */ skiboot-skiboot-5.1.13/include/stack.h000066400000000000000000000062211265204436200176310ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __STACKFRAME_H #define __STACKFRAME_H #include #define STACK_ENTRY_OPAL_API 0 /* OPAL call */ #define STACK_ENTRY_HMI 0x0e60 /* Hypervisor maintenance */ #define STACK_ENTRY_RESET 0x0100 /* System reset */ #define STACK_ENTRY_SOFTPATCH 0x1500 /* Soft patch (denorm emulation) */ /* Safety/ABI gap at top of stack */ #define STACK_TOP_GAP 0x100 /* Remaining stack space (gap included) */ #define NORMAL_STACK_SIZE STACK_SIZE /* Offset to get to normal CPU stacks */ #define CPU_STACKS_OFFSET (CPU_STACKS_BASE + \ NORMAL_STACK_SIZE - STACK_TOP_GAP) /* Gap below the stack. If our stack checker sees the stack below that * gap, it will flag a stack overflow */ #define STACK_SAFETY_GAP 512 /* Warning threshold, if stack goes below that on mcount, print a * warning. */ #define STACK_WARNING_GAP 2048 #ifndef __ASSEMBLY__ #include /* This is the struct used to save GPRs etc.. on OPAL entry * and from some exceptions. It is not always entirely populated * depending on the entry type */ struct stack_frame { /* Standard 112-byte stack frame header (the minimum size required, * using an 8-doubleword param save area). The callee (in C) may use * lrsave; we declare these here so we don't get our own save area * overwritten */ uint64_t backchain; uint64_t crsave; uint64_t lrsave; uint64_t compiler_dw; uint64_t linker_dw; uint64_t tocsave; uint64_t paramsave[8]; /* Space for stack-local vars used by asm. At present we only use * one doubleword. */ uint64_t locals[1]; /* Entry type */ uint64_t type; /* GPR save area * * We don't necessarily save everything in here */ uint64_t gpr[32]; /* Other SPR saved * * Only for some exceptions. */ uint32_t cr; uint32_t xer; uint64_t ctr; uint64_t lr; uint64_t pc; uint64_t cfar; uint64_t srr0; uint64_t srr1; uint64_t hsrr0; uint64_t hsrr1; } __attribute__((aligned(16))); /* Backtrace */ struct bt_entry { unsigned long sp; unsigned long pc; }; /* Boot stack top */ extern void *boot_stack_top; /* Create a backtrace */ extern void __backtrace(struct bt_entry *entries, unsigned int *count); /* Convert a backtrace to ASCII */ extern void __print_backtrace(unsigned int pir, struct bt_entry *entries, unsigned int count, char *out_buf, unsigned int *len, bool symbols); /* For use by debug code, create and print backtrace, uses a static buffer */ extern void backtrace(void); #ifdef STACK_CHECK_ENABLED extern void check_stacks(void); #else static inline void check_stacks(void) { } #endif #endif /* __ASSEMBLY__ */ #endif /* __STACKFRAME_H */ skiboot-skiboot-5.1.13/include/time-utils.h000066400000000000000000000023321265204436200206170ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __TIME_UTILS_H #define __TIME_UTILS_H #include #include /* BCD conversion utilities. MSB is byte 3, LSB is byte 0 */ static inline unsigned int bcd_byte(uint32_t bcd, int byteno) { bcd >>= byteno * 8; return (bcd >> 4 & 0xf) * 10 + (bcd & 0xf); } static inline uint32_t int_to_bcd2(unsigned int x) { return (((x / 10) << 4) & 0xf0) | (x % 10); } static inline uint32_t int_to_bcd4(unsigned int x) { return int_to_bcd2(x / 100) << 8 | int_to_bcd2(x % 100); } void tm_to_datetime(struct tm *tm, uint32_t *y_m_d, uint64_t *h_m_s_m); void datetime_to_tm(uint32_t y_m_d, uint64_t h_m_s_m, struct tm *tm); #endif skiboot-skiboot-5.1.13/include/timebase.h000066400000000000000000000050331265204436200203150ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Timebase helpers. * * Note: Only use after the TODs are in sync ! */ #ifndef __TIME_H #define __TIME_H #include #ifndef __TEST__ static inline unsigned long mftb(void) { unsigned long tb; /* We use a memory clobber to avoid this being * moved in the instruction stream */ asm volatile("mftb %0" : "=r"(tb) : : "memory"); return tb; } #endif enum tb_cmpval { TB_ABEFOREB = -1, TB_AEQUALB = 0, TB_AAFTERB = 1 }; static inline enum tb_cmpval tb_compare(unsigned long a, unsigned long b) { if (a == b) return TB_AEQUALB; return ((long)(b - a)) > 0 ? TB_ABEFOREB : TB_AAFTERB; } /* Architected timebase */ static const unsigned long tb_hz = 512000000; static inline unsigned long secs_to_tb(unsigned long secs) { return secs * tb_hz; } static inline unsigned long tb_to_secs(unsigned long tb) { return tb / tb_hz; } static inline unsigned long msecs_to_tb(unsigned long msecs) { return msecs * (tb_hz / 1000); } static inline unsigned long tb_to_msecs(unsigned long tb) { return (tb * 1000) / tb_hz; } static inline unsigned long usecs_to_tb(unsigned long usecs) { return usecs * (tb_hz / 1000000); } static inline unsigned long tb_to_usecs(unsigned long tb) { return (tb * 1000000) / tb_hz; } extern unsigned long timespec_to_tb(const struct timespec *ts); /* time_wait - Wait a certain number of TB ticks while polling FSP */ extern void time_wait(unsigned long duration); extern void time_wait_nopoll(unsigned long duration); /* time_wait_ms - Wait a certain number of milliseconds while polling FSP */ extern void time_wait_ms(unsigned long ms); extern void time_wait_ms_nopoll(unsigned long ms); /* time_wait_us - Wait a certain number of microseconds while polling FSP */ extern void time_wait_us(unsigned long us); extern void time_wait_us_nopoll(unsigned long us); /* nanosleep_nopoll - variant for use from hostservices */ extern int nanosleep_nopoll(const struct timespec *req, struct timespec *rem); #endif /* __TIME_H */ skiboot-skiboot-5.1.13/include/timer.h000066400000000000000000000060071265204436200176460ustar00rootroot00000000000000#ifndef __TIMER_H #define __TIMER_H #include #include struct timer; typedef void (*timer_func_t)(struct timer *t, void *data, uint64_t now); /* Structure exposed in order to be able to allocate it * statically but otherwise, use accessors, don't access * the fields directly * * WARNING: Do not free a timer object unless you have cancelled * it first or you know it won't reschedule itself and have done * a sync_timer() on it. The timer core *will* access the object * again after you return from the expiry callback so it must not * be freed from the callback itself. */ struct timer { struct list_node link; uint64_t target; timer_func_t expiry; void * user_data; void * running; uint64_t gen; }; extern void init_timer(struct timer *t, timer_func_t expiry, void *data); /* (re)schedule a timer. If already scheduled, it's expiry will be updated * * This doesn't synchronize so if the timer also reschedules itself there * is no telling which one "wins". The advantage is that this can be called * with any lock held or from the timer expiry itself. * * We support a magic expiry of TIMER_POLL which causes a given timer to * be called whenever OPAL main polling loop is run, which is often during * boot and occasionally while Linux is up. This can be used with both * schedule_timer() and schedule_timer_at() * * This is useful for a number of interrupt driven drivers to have a way * to crank their state machine at times when the interrupt isn't available * such as during early boot. * * Note: For convenience, schedule_timer() returns the current TB value */ #define TIMER_POLL ((uint64_t)-1) extern uint64_t schedule_timer(struct timer *t, uint64_t how_long); extern void schedule_timer_at(struct timer *t, uint64_t when); /* Synchronization point with the timer. If the callback has started before * that function is called, it will be complete when this function returns. * * It might start *again* but at least anything before this will be visible * to any subsequent occurrence. * * The usual issue of such sync functions exist: don't call it while holding * a lock that the timer callback might take or from the timer expiry itself. */ extern void sync_timer(struct timer *t); /* cancel_timer() will ensure the timer isn't concurrently running so * the cancellation is guaranteed even if the timer reschedules itself. * * This uses sync_timer() internally so don't call this while holding a * lock the timer might use. */ extern void cancel_timer(struct timer *t); /* cancel_timer_async() allows to remove the timer from the schedule * list without trying to synchronize. This is useful if the cancellation * must happen while holding locks that would make the synchronization * impossible. The user is responsible of ensuring it deals with potentially * spurrious occurrences */ extern void cancel_timer_async(struct timer *t); /* Run the timers */ extern void check_timers(bool from_interrupt); /* Core init */ void late_init_timers(void); #endif /* __TIMER_H */ skiboot-skiboot-5.1.13/include/trace.h000066400000000000000000000024131265204436200176210ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __TRACE_H #define __TRACE_H #include #include #include #include #define TBUF_SZ (1024 * 1024) struct cpu_thread; /* Here's one we prepared earlier. */ void init_boot_tracebuf(struct cpu_thread *boot_cpu); struct trace_info { /* Lock for writers. */ struct lock lock; /* Exposed to kernel. */ struct tracebuf tb; }; /* Allocate trace buffers once we know memory topology */ void init_trace_buffers(void); /* This will fill in timestamp and cpu; you must do type and len. */ void trace_add(union trace *trace, u8 type, u16 len); /* Put trace node into dt. */ void trace_add_node(void); #endif /* __TRACE_H */ skiboot-skiboot-5.1.13/include/trace_types.h000066400000000000000000000066111265204436200210510ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* API for kernel to read trace buffer. */ #ifndef __TRACE_TYPES_H #define __TRACE_TYPES_H #include #define TRACE_REPEAT 1 #define TRACE_OVERFLOW 2 #define TRACE_OPAL 3 /* OPAL call */ #define TRACE_FSP_MSG 4 /* FSP message sent/received */ #define TRACE_FSP_EVENT 5 /* FSP driver event */ #define TRACE_UART 6 /* UART driver traces */ /* One per cpu, plus one for NMIs */ struct tracebuf { /* Mask to apply to get buffer offset. */ __be64 mask; /* This where the buffer starts. */ __be64 start; /* This is where writer has written to. */ __be64 end; /* This is where the writer wrote to previously. */ __be64 last; /* This is where the reader is up to. */ __be64 rpos; /* If the last one we read was a repeat, this shows how many. */ __be32 last_repeat; /* Maximum possible size of a record. */ __be32 max_size; char buf[/* TBUF_SZ + max_size */]; }; /* Common header for all trace entries. */ struct trace_hdr { __be64 timestamp; u8 type; u8 len_div_8; __be16 cpu; u8 unused[4]; }; /* Note: all other entries must be at least as large as this! */ struct trace_repeat { __be64 timestamp; /* Last repeat happened at this timestamp */ u8 type; /* == TRACE_REPEAT */ u8 len_div_8; __be16 cpu; __be16 prev_len; __be16 num; /* Starts at 1, ie. 1 repeat, or two traces. */ /* Note that the count can be one short, if read races a repeat. */ }; /* Overflow is special */ struct trace_overflow { __be64 unused64; /* Timestamp is unused */ u8 type; /* == TRACE_OVERFLOW */ u8 len_div_8; u8 unused[6]; /* ie. hdr.cpu is indeterminate */ __be64 bytes_missed; }; /* All other trace types have a full header */ struct trace_opal { struct trace_hdr hdr; __be64 token, lr, sp, r3_to_11[9]; }; #define TRACE_FSP_MSG_IN 0 #define TRACE_FSP_MSG_OUT 1 struct trace_fsp_msg { struct trace_hdr hdr; __be32 word0; __be32 word1; u8 dlen; u8 dir; /* TRACE_FSP_MSG_IN or TRACE_FSP_MSG_OUT */ u8 data[56]; /* See dlen, but max is 56 bytes. */ }; #define TRACE_FSP_EVT_LINK_DOWN 0 #define TRACE_FSP_EVT_DISR_CHG 1 /* 0:disr */ #define TRACE_FSP_EVT_SOFT_RR 2 /* 0:disr */ #define TRACE_FSP_EVT_RR_COMPL 3 #define TRACE_FSP_EVT_HDES_CHG 4 /* 0:hdes */ #define TRACE_FSP_EVT_POLL_IRQ 5 /* 0:irq? 1:hdir 2:ctl 3:psi_irq */ struct trace_fsp_event { struct trace_hdr hdr; __be16 event; __be16 fsp_state; __be32 data[4]; /* event type specific */ }; #define TRACE_UART_CTX_IRQ 0 #define TRACE_UART_CTX_POLL 1 #define TRACE_UART_CTX_READ 2 struct trace_uart { struct trace_hdr hdr; u8 ctx; u8 cnt; u8 irq_state; u8 unused; __be16 in_count; }; union trace { struct trace_hdr hdr; /* Trace types go here... */ struct trace_repeat repeat; struct trace_overflow overflow; struct trace_opal opal; struct trace_fsp_msg fsp_msg; struct trace_fsp_event fsp_evt; struct trace_uart uart; }; #endif /* __TRACE_TYPES_H */ skiboot-skiboot-5.1.13/include/types.h000066400000000000000000000014771265204436200177000ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __TYPES_H #define __TYPES_H #include /* These are currently just for clarity, but we could apply sparse. */ typedef u16 __be16; typedef u32 __be32; typedef u64 __be64; #endif /* __TYPES_H */ skiboot-skiboot-5.1.13/include/vpd.h000066400000000000000000000024171265204436200173200ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __VPD_H #define __VPD_H struct machine_info { const char *mtm; const char *name; }; const struct machine_info *machine_info_lookup(char *mtm); const void *vpd_find_keyword(const void *rec, size_t rec_sz, const char *kw, uint8_t *kw_size); const void *vpd_find_record(const void *vpd, size_t vpd_size, const char *record, size_t *sz); const void *vpd_find(const void *vpd, size_t vpd_size, const char *record, const char *keyword, uint8_t *sz); /* Add model property to dt_root */ void add_dtb_model(void); void vpd_iohub_load(struct dt_node *hub_node); void vpd_preload(struct dt_node *hub_node); #define VPD_LOAD_LXRN_VINI 0xff #endif /* __VPD_H */ skiboot-skiboot-5.1.13/include/xscom.h000066400000000000000000000153661265204436200176670ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __XSCOM_H #define __XSCOM_H #include #include #include /* * SCOM "partID" definitions: * * All Ids are 32-bits long, top nibble is reserved for the * 'type' field: * 0x0 = Processor Chip * 0x8 = Memory Buffer (Centaur) Chip * 0x4 = EX/Core Chiplet * * Processor Chip = Logical Fabric Id = PIR>>7 * 0b0000.0000.0000.0000.0000.0000.00NN.NCCC * N=Node, C=Chip * Centaur Chip = Associated Processor Chip with memory channel * appended and flag set * 0b1000.0000.0000.0000.0000.00NN.NCCC.MMMM * N=Node, C=Chip, M=Memory Channel * Processor EX/Core chiplet = PIR >> 3 with flag set * 0b0100.0000.0000.0000.0000.00NN.NCCC.PPPP * N=Node, C=Chip, P=Processor core */ /* * SCOM Address definition extracted from HWPs for documentation * purposes * * "Normal" (legacy) format * * 111111 11112222 22222233 33333333 44444444 44555555 55556666 * 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123 * -------- -------- -------- -------- -------- -------- -------- -------- * 00000000 00000000 00000000 00000000 0MCCCCCC ????PPPP 00LLLLLL LLLLLLLL * || | | * || | `-> Local Address* * || | * || `-> Port * || * |`-> Chiplet ID** * | * `-> Multicast bit * * * Local address is composed of "00" + 4-bit ring + 10-bit ID * The 10-bit ID is usually 4-bit sat_id and 6-bit reg_id * * ** Chiplet ID turns into multicast operation type and group number * if the multicast bit is set * * "Indirect" format * * * 111111 11112222 22222233 33333333 44444444 44555555 55556666 * 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123 * -------- -------- -------- -------- -------- -------- -------- -------- * 10000000 0000IIII IIIIIGGG GGGLLLLL 0MCCCCCC ????PPPP 00LLLLLL LLLLLLLL * | | | || | | * | | | || | `-> Local Address* * | | | || | * | | | || `-> Port * | | | || * | | | |`-> Chiplet ID** * | | | | * | | | `-> Multicast bit * | | | * | | `-> Lane ID * | | * | `-> RX or TX Group ID * | * `-> Indirect Register Address * * * Local address is composed of "00" + 4-bit ring + 4-bit sat_id + "111111" * * ** Chiplet ID turns into multicast operation type and group number * if the multicast bit is set */ /* * Generate a local address from a given ring/satellite/offset * combination: * * Ring Satellite offset * +---------+---------+-------------+ * | 4 | 4 | 6 | * +---------+---------+-------------+ */ #define XSCOM_SAT(_r, _s, _o) \ (((_r) << 10) | ((_s) << 6) | (_o)) /* * Additional useful definitions */ #define P8_EX_PCB_SLAVE_BASE 0x100F0000 #define XSCOM_ADDR_P8_EX_SLAVE(core, offset) \ (P8_EX_PCB_SLAVE_BASE | (((core) & 0xF) << 24) | ((offset) & 0xFFFF)) #define XSCOM_ADDR_P8_EX(core, addr) \ ((((core) & 0xF) << 24) | (addr)) /* Per core power mgt registers */ #define PM_OHA_MODE_REG 0x1002000D #define L2_FIR_ACTION1 0x10012807 /* EX slave per-core power mgt slave regisers */ #define EX_PM_GP0 0x0100 #define EX_PM_GP1 0x0103 #define EX_PM_CLEAR_GP1 0x0104 /* AND SCOM */ #define EX_PM_SET_GP1 0x0105 /* OR SCOM */ #define EX_PM_SPECIAL_WAKEUP_FSP 0x010B #define EX_PM_SPECIAL_WAKEUP_OCC 0x010C #define EX_PM_SPECIAL_WAKEUP_PHYP 0x010D #define EX_PM_IDLE_STATE_HISTORY_PHYP 0x0110 #define EX_PM_IDLE_STATE_HISTORY_FSP 0x0111 #define EX_PM_IDLE_STATE_HISTORY_OCC 0x0112 #define EX_PM_IDLE_STATE_HISTORY_PERF 0x0113 #define EX_PM_CORE_PFET_VRET 0x0130 #define EX_PM_CORE_ECO_VRET 0x0150 #define EX_PM_PPMSR 0x0153 #define EX_PM_PPMCR 0x0159 /* Power mgt bits in GP0 */ #define EX_PM_GP0_SPECIAL_WAKEUP_DONE PPC_BIT(31) /* Power mgt settings in GP1 */ #define EX_PM_SETUP_GP1_FAST_SLEEP 0xD800000000000000ULL #define EX_PM_SETUP_GP1_DEEP_SLEEP 0x2400000000000000ULL #define EX_PM_SETUP_GP1_FAST_SLEEP_DEEP_WINKLE 0xC400000000000000ULL #define EX_PM_GP1_SLEEP_WINKLE_MASK 0xFC00000000000000ULL #define EX_PM_SETUP_GP1_PM_SPR_OVERRIDE_EN 0x0010000000000000ULL #define EX_PM_SETUP_GP1_DPLL_FREQ_OVERRIDE_EN 0x0020000000000000ULL /* Fields in history regs */ #define EX_PM_IDLE_ST_HIST_PM_STATE_MASK PPC_BITMASK(0, 2) #define EX_PM_IDLE_ST_HIST_PM_STATE_LSH PPC_BITLSHIFT(2) /* Definitions relating to indirect XSCOMs shared with centaur */ #define XSCOM_ADDR_IND_FLAG PPC_BIT(0) #define XSCOM_ADDR_IND_ADDR PPC_BITMASK(12,31) #define XSCOM_ADDR_IND_DATA PPC_BITMASK(48,63) #define XSCOM_DATA_IND_READ PPC_BIT(0) #define XSCOM_DATA_IND_COMPLETE PPC_BIT(32) #define XSCOM_DATA_IND_ERR PPC_BITMASK(33,35) #define XSCOM_DATA_IND_DATA PPC_BITMASK(48,63) /* HB folks say: try 10 time for now */ #define XSCOM_IND_MAX_RETRIES 10 /* * Error handling: * * Error codes TBD, 0 = success */ /* Targeted SCOM access */ extern int xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val); extern int xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val); /* This chip SCOM access */ extern int xscom_readme(uint64_t pcb_addr, uint64_t *val); extern int xscom_writeme(uint64_t pcb_addr, uint64_t val); extern void xscom_init(void); /* Mark XSCOM lock as being in console path */ extern void xscom_used_by_console(void); /* Returns true if XSCOM can be used. Typically this returns false if * the current CPU holds the XSCOM lock (to avoid re-entrancy from error path). */ extern bool xscom_ok(void); extern int64_t xscom_read_cfam_chipid(uint32_t partid, uint32_t *chip_id); extern int64_t xscom_trigger_xstop(void); #endif /* __XSCOM_H */ skiboot-skiboot-5.1.13/libc/000077500000000000000000000000001265204436200156405ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/Makefile.inc000066400000000000000000000004741265204436200200550ustar00rootroot00000000000000LIBCDIR = libc SUBDIRS += $(LIBCDIR) LIBC = $(LIBCDIR)/built-in.o $(LIBCDIR)/time.o include $(SRC)/$(LIBCDIR)/string/Makefile.inc include $(SRC)/$(LIBCDIR)/ctype/Makefile.inc include $(SRC)/$(LIBCDIR)/stdlib/Makefile.inc include $(SRC)/$(LIBCDIR)/stdio/Makefile.inc $(LIBC): $(STRING) $(CTYPE) $(STDLIB) $(STDIO) skiboot-skiboot-5.1.13/libc/README.txt000066400000000000000000000047111265204436200173410ustar00rootroot00000000000000 skiboot changes: - Added malloc lock - Added a few functions - Malloc will always 4 byte align everything - Added asserts, requires core to provide abort() WARNINGS: - don't use free() on the result of memalign() (should be fixable) Previous notes: Standard C library for the SLOF firmware project ================================================ To use this library, link your target against the "libc.a" archive. However, there are some prerequisites before you can use certain parts of the library: 1) If you want to use malloc() and the like, you have to supply an implemen- tation of sbrk() in your own code. malloc() uses sbrk() to get new, free memory regions. Prototype: void *sbrk(int incr); Description: sbrk() increments the available data space by incr bytes and returns a pointer to the start of the new area. See the man-page of sbrk for details about this function. 2) Before you can use the stdio output functions like printf(), puts() and the like, you have to provide a standard write() function in your code. printf() and the like use write() to print out the strings to the standard output. Prototype: ssize_t write(int fd, const void *buf, size_t cnt); Description: Write cnt byte from the buffer buf to the stream associated with the file descriptor fd. The stdio functions will print their output to the stdout channel which is assigned with the file descriptor 1 by default. Note that the stdio functions will not use open() before calling write(), so if the stdout cannel needs to be opened first, you should do that in your start-up code before using the libc functions for the first time. 3) Before you can use the stdio input functions like scanf() and the like, you have to provide a standard read() function in your code. scanf() and the like use read() to get the characters from the standard input. Prototype: ssize_t read(int fd, void *buf, size_t cnt); Description: Read cnt byte from the stream associated with the file descriptor fd and put them into the buffer buf. The stdio functions will get their input from the stdin channel which is assigned with the file descriptor 0 by default. Note that the stdio functions will not use open() before calling read(), so if the stdin cannel needs to be opened first, you should do that in your start-up code before using the libc functions for the first time. skiboot-skiboot-5.1.13/libc/ctype/000077500000000000000000000000001265204436200167645ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/ctype/Makefile.inc000066400000000000000000000013061265204436200211740ustar00rootroot00000000000000# ***************************************************************************** # * Copyright (c) 2004, 2008 IBM Corporation # * All rights reserved. # * This program and the accompanying materials # * are made available under the terms of the BSD License # * which accompanies this distribution, and is available at # * http://www.opensource.org/licenses/bsd-license.php # * # * Contributors: # * IBM Corporation - initial implementation # ****************************************************************************/ SUBDIRS += $(LIBCDIR)/ctype CTYPE_OBJS = isdigit.o isprint.o isspace.o isxdigit.o tolower.o toupper.o CTYPE = $(LIBCDIR)/ctype/built-in.o $(CTYPE): $(CTYPE_OBJS:%=$(LIBCDIR)/ctype/%) skiboot-skiboot-5.1.13/libc/ctype/isdigit.c000066400000000000000000000013601265204436200205640ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int __attrconst isdigit(int ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return 1; default: return 0; } } skiboot-skiboot-5.1.13/libc/ctype/isprint.c000066400000000000000000000011611265204436200206170ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int __attrconst isprint(int ch) { return (ch >= 32 && ch < 127); } skiboot-skiboot-5.1.13/libc/ctype/isspace.c000066400000000000000000000013251265204436200205600ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int __attrconst isspace(int ch) { switch (ch) { case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': return 1; default: return 0; } } skiboot-skiboot-5.1.13/libc/ctype/isxdigit.c000066400000000000000000000013021265204436200207500ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int __attrconst isxdigit(int ch) { return ( (ch >= '0' && ch <= '9') | (ch >= 'A' && ch <= 'F') | (ch >= 'a' && ch <= 'f') ); } skiboot-skiboot-5.1.13/libc/ctype/tolower.c000066400000000000000000000012161265204436200206230ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int __attrconst tolower(int c) { return (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 'a' ) : c); } skiboot-skiboot-5.1.13/libc/ctype/toupper.c000066400000000000000000000012361265204436200206300ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include "ctype.h" int __attrconst toupper (int cha) { if((cha >= 'a') && (cha <= 'z')) return(cha - 'a' + 'A'); return(cha); } skiboot-skiboot-5.1.13/libc/include/000077500000000000000000000000001265204436200172635ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/include/assert.h000066400000000000000000000016331265204436200207400ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008, 2012 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _ASSERT_H #define _ASSERT_H #define assert(cond) \ do { if (!(cond)) { \ assert_fail(__FILE__ \ ":" stringify(__LINE__) \ ":" stringify(cond)); } \ } while(0) void __attribute__((noreturn)) assert_fail(const char *msg); #define stringify(expr) stringify_1(expr) /* Double-indirection required to stringify expansions */ #define stringify_1(expr) #expr #endif skiboot-skiboot-5.1.13/libc/include/ctype.h000066400000000000000000000014071265204436200205620ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _CTYPE_H #define _CTYPE_H #include int __attrconst isdigit(int c); int __attrconst isxdigit(int c); int __attrconst isprint(int c); int __attrconst isspace(int c); int __attrconst tolower(int c); int __attrconst toupper(int c); #endif skiboot-skiboot-5.1.13/libc/include/errno.h000066400000000000000000000023351265204436200205640ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _ERRNO_H #define _ERRNO_H extern int errno; /* * Error number definitions */ #define EPERM 1 /* not permitted */ #define ENOENT 2 /* file or directory not found */ #define EIO 5 /* input/output error */ #define EBADF 9 /* Bad file number */ #define ENOMEM 12 /* not enough space */ #define EACCES 13 /* permission denied */ #define EFAULT 14 /* bad address */ #define EBUSY 16 /* resource busy */ #define EEXIST 17 /* file already exists */ #define ENODEV 19 /* device not found */ #define EINVAL 22 /* invalid argument */ #define EDOM 33 /* math argument out of domain of func */ #define ERANGE 34 /* math result not representable */ #define ENOSYS 38 /* Function not implemented */ #endif skiboot-skiboot-5.1.13/libc/include/getopt.h000066400000000000000000000016331265204436200207410ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef GETOPT_H #define GETOPT_H extern char *optarg; extern int optind; extern int opterr; extern int optopt; struct option { const char *name; int has_arg; int *flag; int val; }; enum { no_argument = 0, required_argument, optional_argument }; int getopt(int argc, char **, const char *); int getopt_long(int argc, char **, const char *, const struct option *, int *); #endif /* GETOPT_H */ skiboot-skiboot-5.1.13/libc/include/limits.h000066400000000000000000000016421265204436200207400ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _LIMITS_H #define _LIMITS_H #define UCHAR_MAX 255 #define SCHAR_MAX 127 #define SCHAR_MIN (-128) #define USHRT_MAX 65535 #define SHRT_MAX 32767 #define SHRT_MIN (-32768) #define UINT_MAX (4294967295U) #define INT_MAX 2147483647 #define INT_MIN (-2147483648) #define ULONG_MAX ((unsigned long)-1L) #define LONG_MAX (ULONG_MAX/2) #define LONG_MIN ((-LONG_MAX)-1) #define CHAR_BIT 8 #endif skiboot-skiboot-5.1.13/libc/include/stdint.h000066400000000000000000000015251265204436200207440ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _STDINT_H #define _STDINT_H typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned short uint16_t; typedef signed short int16_t; typedef unsigned int uint32_t; typedef signed int int32_t; typedef unsigned long long uint64_t; typedef signed long long int64_t; typedef unsigned long int uintptr_t; #endif skiboot-skiboot-5.1.13/libc/include/stdio.h000066400000000000000000000040231265204436200205550ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _STDIO_H #define _STDIO_H #include #include "stddef.h" #define EOF (-1) #define _IONBF 0 #define _IOLBF 1 #define _IOFBF 2 #define BUFSIZ 80 typedef struct { int fd; int mode; int pos; char *buf; int bufsiz; } FILE; extern FILE stdin_data; extern FILE stdout_data; extern FILE stderr_data; #define stdin (&stdin_data) #define stdout (&stdout_data) #define stderr (&stderr_data) int fileno(FILE *stream); int _printf(const char *format, ...) __attribute__((format (printf, 1, 2))); #ifndef pr_fmt #define prfmt(fmt) fmt #endif #define printf(f, ...) do { _printf(pr_fmt(f), ##__VA_ARGS__); } while(0) int fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); int snprintf(char *str, size_t size, const char *format, ...) __attribute__((format (printf, 3, 4))); int vfprintf(FILE *stream, const char *format, va_list); int vsnprintf(char *str, size_t size, const char *format, va_list); void setbuf(FILE *stream, char *buf); int setvbuf(FILE *stream, char *buf, int mode , size_t size); int fputc(int ch, FILE *stream); #define putc(ch, stream) fputc(ch, stream) int putchar(int ch); int puts(const char *str); int fputs(const char *str, FILE *stream); int scanf(const char *format, ...) __attribute__((format (scanf, 1, 2))); int fscanf(FILE *stream, const char *format, ...) __attribute__((format (scanf, 2, 3))); int vfscanf(FILE *stream, const char *format, va_list); int vsscanf(const char *str, const char *format, va_list); int getc(FILE *stream); int getchar(void); #endif skiboot-skiboot-5.1.13/libc/include/stdlib.h000066400000000000000000000017771265204436200207310ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _STDLIB_H #define _STDLIB_H #include "stddef.h" #define RAND_MAX 32767 #include "mem_region-malloc.h" int atoi(const char *str); long atol(const char *str); unsigned long int strtoul(const char *nptr, char **endptr, int base); long int strtol(const char *nptr, char **endptr, int base); int rand(void); void __attribute__((noreturn)) _abort(const char *msg); #define abort() do { \ _abort("abort():" __FILE__ \ ":" stringify(__LINE__)); \ } while(0) #endif skiboot-skiboot-5.1.13/libc/include/string.h000066400000000000000000000025761265204436200207540ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _STRING_H #define _STRING_H #include "stddef.h" char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n); char *strcat(char *dest, const char *src); int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); char *strchr(const char *s, int c); char *strrchr(const char *s, int c); size_t strlen(const char *s); char *strstr(const char *hay, const char *needle); char *strtok(char *src, const char *pattern); char *strdup(const char *src); void *memset(void *s, int c, size_t n); void *memchr(const void *s, int c, size_t n); void *memcpy(void *dest, const void *src, size_t n); void *memmove(void *dest, const void *src, size_t n); int memcmp(const void *s1, const void *s2, size_t n); #endif skiboot-skiboot-5.1.13/libc/include/time.h000066400000000000000000000020371265204436200203740ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _TIME_H #define _TIME_H struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; }; typedef long time_t; struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; struct tm *gmtime_r(const time_t *timep, struct tm *result); time_t mktime(struct tm *tm); /* Not implemented by libc but by hosting environment, however * this is where the prototype is expected */ int nanosleep(const struct timespec *req, struct timespec *rem); #endif /* _TIME_H */ skiboot-skiboot-5.1.13/libc/include/unistd.h000066400000000000000000000015161265204436200207450ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #ifndef _UNISTD_H #define _UNISTD_H #include typedef long ssize_t; extern int open(const char *name, int flags); extern int close(int fd); extern ssize_t read(int fd, void *buf, size_t count); extern ssize_t write(int fd, const void *buf, size_t count); extern ssize_t lseek(int fd, long offset, int whence); #endif skiboot-skiboot-5.1.13/libc/stdio/000077500000000000000000000000001265204436200167625ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/stdio/Makefile.inc000066400000000000000000000014511265204436200211730ustar00rootroot00000000000000# ***************************************************************************** # * Copyright (c) 2004, 2008 IBM Corporation # * All rights reserved. # * This program and the accompanying materials # * are made available under the terms of the BSD License # * which accompanies this distribution, and is available at # * http://www.opensource.org/licenses/bsd-license.php # * # * Contributors: # * IBM Corporation - initial implementation # ****************************************************************************/ SUBDIRS += $(LIBCDIR)/stdio STDIO_OBJS = fscanf.o vfprintf.o vsnprintf.o fprintf.o \ setvbuf.o fputc.o puts.o fputs.o putchar.o scanf.o \ stdchnls.o vfscanf.o vsscanf.o fileno.o snprintf.o STDIO = $(LIBCDIR)/stdio/built-in.o $(STDIO): $(STDIO_OBJS:%=$(LIBCDIR)/stdio/%) skiboot-skiboot-5.1.13/libc/stdio/fileno.c000066400000000000000000000011101265204436200203730ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int fileno(FILE *stream) { return stream->fd; } skiboot-skiboot-5.1.13/libc/stdio/fprintf.c000066400000000000000000000013031265204436200205730ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" int fprintf(FILE *stream, const char* fmt, ...) { int count; va_list ap; va_start(ap, fmt); count = vfprintf(stream, fmt, ap); va_end(ap); return count; } skiboot-skiboot-5.1.13/libc/stdio/fputc.c000066400000000000000000000012751265204436200202540ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" #include "unistd.h" int fputc(int ch, FILE *stream) { unsigned char outchar = ch; if (write(stream->fd, &outchar, 1) == 1) return outchar; else return EOF; } skiboot-skiboot-5.1.13/libc/stdio/fputs.c000066400000000000000000000012341265204436200202670ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" #include "string.h" #include "unistd.h" int fputs(const char *str, FILE *stream) { return write(stream->fd, str, strlen(str)); } skiboot-skiboot-5.1.13/libc/stdio/fscanf.c000066400000000000000000000012701265204436200203660ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int fscanf(FILE *stream, const char *fmt, ...) { int count; va_list ap; va_start(ap, fmt); count = vfscanf(stream, fmt, ap); va_end(ap); return count; } skiboot-skiboot-5.1.13/libc/stdio/putchar.c000066400000000000000000000011131265204436200205700ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" int putchar(int ch) { return putc(ch, stdout); } skiboot-skiboot-5.1.13/libc/stdio/puts.c000066400000000000000000000013051265204436200201200ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" #include "string.h" #include "unistd.h" int puts(const char *str) { int ret; ret = write(stdout->fd, str, strlen(str)); write(stdout->fd, "\n", 1); return ret + 1; } skiboot-skiboot-5.1.13/libc/stdio/scanf.c000066400000000000000000000012501265204436200202160ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int scanf(const char *fmt, ...) { int count; va_list ap; va_start(ap, fmt); count = vfscanf(stdin, fmt, ap); va_end(ap); return count; } skiboot-skiboot-5.1.13/libc/stdio/setvbuf.c000066400000000000000000000015131265204436200206040ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int setvbuf(FILE *stream, char *buf, int mode , size_t size) { if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) return -1; stream->buf = buf; stream->mode = mode; stream->bufsiz = size; return 0; } void setbuf(FILE *stream, char *buf) { setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); } skiboot-skiboot-5.1.13/libc/stdio/snprintf.c000066400000000000000000000014141265204436200207710ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int snprintf(char *buff, size_t size, const char *format, ...) { va_list ar; int count; if ((buff==NULL) || (format==NULL)) return(-1); va_start(ar, format); count = vsnprintf(buff, size, format, ar); va_end(ar); return(count); } skiboot-skiboot-5.1.13/libc/stdio/stdchnls.c000066400000000000000000000015741265204436200207570ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" static char stdin_buffer[BUFSIZ], stdout_buffer[BUFSIZ]; FILE stdin_data = { .fd = 0, .mode = _IOLBF, .pos = 0, .buf = stdin_buffer, .bufsiz = BUFSIZ }; FILE stdout_data = { .fd = 1, .mode = _IOLBF, .pos = 0, .buf = stdout_buffer, .bufsiz = BUFSIZ }; FILE stderr_data = { .fd = 2, .mode = _IONBF, .pos = 0, .buf = NULL, .bufsiz = 0 }; skiboot-skiboot-5.1.13/libc/stdio/vfprintf.c000066400000000000000000000013611265204436200207650ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" #include "unistd.h" int vfprintf(FILE *stream, const char *fmt, va_list ap) { int count; char buffer[320]; count = vsnprintf(buffer, sizeof(buffer), fmt, ap); write(stream->fd, buffer, count); return count; } skiboot-skiboot-5.1.13/libc/stdio/vfscanf.c000066400000000000000000000114341265204436200205570ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "string.h" #include "ctype.h" #include "stdlib.h" #include "stdio.h" #include "unistd.h" static int _getc(FILE * stream) { int count; char c; if (stream->mode == _IONBF || stream->buf == NULL) { if (read(stream->fd, &c, 1) == 1) return (int) c; else return EOF; } if (stream->pos == 0 || stream->pos >= BUFSIZ || stream->buf[stream->pos] == '\0') { count = read(stream->fd, stream->buf, BUFSIZ); if (count < 0) count = 0; if (count < BUFSIZ) stream->buf[count] = '\0'; stream->pos = 0; } return stream->buf[stream->pos++]; } static void _ungetc(int ch, FILE * stream) { if (stream->mode != _IONBF && stream->pos > 0) { if (stream->pos < BUFSIZ) stream->buf[stream->pos] = ch; stream->pos--; } } static int _is_voidage(int ch) { if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\0') return 1; else return 0; } static int _scanf(FILE * stream, const char *fmt, va_list * ap) { int i = 0; int length = 0; fmt++; while (*fmt != '\0') { char tbuf[256]; char ch; switch (*fmt) { case 'd': case 'i': ch = _getc(stream); if (length == 0) { while (!_is_voidage(ch) && isdigit(ch)) { tbuf[i] = ch; ch = _getc(stream); i++; } } else { while (!_is_voidage(ch) && i < length && isdigit(ch)) { tbuf[i] = ch; ch = _getc(stream); i++; } } /* We tried to understand what this is good for... * but we did not. We know for sure that it does not * work on SLOF if this is active. */ /* _ungetc(ch, stream); */ tbuf[i] = '\0'; /* ch = _getc(stream); */ if (!_is_voidage(ch)) _ungetc(ch, stream); if (strlen(tbuf) == 0) return 0; *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 10); break; case 'X': case 'x': ch = _getc(stream); if (length == 0) { while (!_is_voidage(ch) && isxdigit(ch)) { tbuf[i] = ch; ch = _getc(stream); i++; } } else { while (!_is_voidage(ch) && i < length && isxdigit(ch)) { tbuf[i] = ch; ch = _getc(stream); i++; } } /* _ungetc(ch, stream); */ tbuf[i] = '\0'; /* ch = _getc(stream); */ if (!_is_voidage(ch)) _ungetc(ch, stream); if (strlen(tbuf) == 0) return 0; *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 16); break; case 'O': case 'o': ch = _getc(stream); if (length == 0) { while (!_is_voidage(ch) && !(ch < '0' || ch > '7')) { tbuf[i] = ch; ch = _getc(stream); i++; } } else { while (!_is_voidage(ch) && i < length && !(ch < '0' || ch > '7')) { tbuf[i] = ch; ch = _getc(stream); i++; } } /* _ungetc(ch, stream); */ tbuf[i] = '\0'; /* ch = _getc(stream); */ if (!_is_voidage(ch)) _ungetc(ch, stream); if (strlen(tbuf) == 0) return 0; *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 8); break; case 'c': ch = _getc(stream); while (_is_voidage(ch)) ch = _getc(stream); *(va_arg(*ap, char *)) = ch; ch = _getc(stream); if (!_is_voidage(ch)) _ungetc(ch, stream); break; case 's': ch = _getc(stream); if (length == 0) { while (!_is_voidage(ch)) { tbuf[i] = ch; ch = _getc(stream); i++; } } else { while (!_is_voidage(ch) && i < length) { tbuf[i] = ch; ch = _getc(stream); i++; } } /* _ungetc(ch, stream); */ tbuf[i] = '\0'; /* ch = _getc(stream); */ if (!_is_voidage(ch)) _ungetc(ch, stream); strcpy(va_arg(*ap, char *), tbuf); break; default: if (*fmt >= '0' && *fmt <= '9') length += *fmt - '0'; break; } fmt++; } return 1; } int vfscanf(FILE * stream, const char *fmt, va_list ap) { int args = 0; while (*fmt != '\0') { if (*fmt == '%') { char formstr[20]; int i = 0; do { formstr[i] = *fmt; fmt++; i++; } while (! (*fmt == 'd' || *fmt == 'i' || *fmt == 'x' || *fmt == 'X' || *fmt == 'p' || *fmt == 'c' || *fmt == 's' || *fmt == '%' || *fmt == 'O' || *fmt == 'o')); formstr[i++] = *fmt; formstr[i] = '\0'; if (*fmt != '%') { if (_scanf(stream, formstr, &ap) <= 0) return args; else args++; } } fmt++; } return args; } int getc(FILE * stream) { return _getc(stream); } int getchar(void) { return _getc(stdin); } skiboot-skiboot-5.1.13/libc/stdio/vsnprintf.c000066400000000000000000000145051265204436200211640ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include #include "stdio.h" #include "stdlib.h" #include "string.h" #include "ctype.h" static const unsigned long long convert[] = { 0x0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL }; static int print_str_fill(char **buffer, size_t bufsize, char *sizec, const char *str, char c) { int i, sizei, len; char *bstart = *buffer; sizei = strtoul(sizec, NULL, 10); len = strlen(str); if (sizei > len) { for (i = 0; (i < (sizei - len)) && ((*buffer - bstart) < bufsize); i++) { **buffer = c; *buffer += 1; } } return 1; } static int print_str(char **buffer, size_t bufsize, const char *str) { char *bstart = *buffer; int i; for (i = 0; (i < strlen(str)) && ((*buffer - bstart) < bufsize); i++) { **buffer = str[i]; *buffer += 1; } return 1; } static unsigned int __attrconst print_intlen(unsigned long value, unsigned short int base) { int i = 0; while (value > 0) { value /= base; i++; } if (i == 0) i = 1; return i; } static int print_itoa(char **buffer, size_t bufsize, unsigned long value, unsigned short base, bool upper) { const char zeichen[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; char c; int i, len; if(base <= 2 || base > 16) return 0; len = i = print_intlen(value, base); /* Don't print to buffer if bufsize is not enough. */ if (len > bufsize) return 0; do { c = zeichen[value % base]; if (upper) c = toupper(c); (*buffer)[--i] = c; value /= base; } while(value); *buffer += len; return 1; } static int print_fill(char **buffer, size_t bufsize, char *sizec, unsigned long size, unsigned short int base, char c, int optlen) { int i, sizei, len; char *bstart = *buffer; sizei = strtoul(sizec, NULL, 10); len = print_intlen(size, base) + optlen; if (sizei > len) { for (i = 0; (i < (sizei - len)) && ((*buffer - bstart) < bufsize); i++) { **buffer = c; *buffer += 1; } } return 0; } static int print_format(char **buffer, size_t bufsize, const char *format, void *var) { char *start; unsigned int i = 0, length_mod = sizeof(int); unsigned long value = 0; unsigned long signBit; char *form, sizec[32]; char sign = ' '; bool upper = false; form = (char *) format; start = *buffer; form++; if(*form == '0' || *form == '.') { sign = '0'; form++; } while ((*form != '\0') && ((*buffer - start) < bufsize)) { switch(*form) { case 'u': case 'd': case 'i': sizec[i] = '\0'; value = (unsigned long) var; signBit = 0x1ULL << (length_mod * 8 - 1); if ((*form != 'u') && (signBit & value)) { **buffer = '-'; *buffer += 1; value = (-(unsigned long)value) & convert[length_mod]; } print_fill(buffer, bufsize - (*buffer - start), sizec, value, 10, sign, 0); print_itoa(buffer, bufsize - (*buffer - start), value, 10, upper); break; case 'X': upper = true; case 'x': sizec[i] = '\0'; value = (unsigned long) var & convert[length_mod]; print_fill(buffer, bufsize - (*buffer - start), sizec, value, 16, sign, 0); print_itoa(buffer, bufsize - (*buffer - start), value, 16, upper); break; case 'O': case 'o': sizec[i] = '\0'; value = (long int) var & convert[length_mod]; print_fill(buffer, bufsize - (*buffer - start), sizec, value, 8, sign, 0); print_itoa(buffer, bufsize - (*buffer - start), value, 8, upper); break; case 'p': sizec[i] = '\0'; print_fill(buffer, bufsize - (*buffer - start), sizec, (unsigned long) var, 16, ' ', 2); print_str(buffer, bufsize - (*buffer - start), "0x"); print_itoa(buffer, bufsize - (*buffer - start), (unsigned long) var, 16, upper); break; case 'c': sizec[i] = '\0'; print_fill(buffer, bufsize - (*buffer - start), sizec, 1, 10, ' ', 0); **buffer = (unsigned long) var; *buffer += 1; break; case 's': sizec[i] = '\0'; print_str_fill(buffer, bufsize - (*buffer - start), sizec, (char *) var, ' '); print_str(buffer, bufsize - (*buffer - start), (char *) var); break; case 'l': form++; if(*form == 'l') { length_mod = sizeof(long long int); } else { form--; length_mod = sizeof(long int); } break; case 'h': form++; if(*form == 'h') { length_mod = sizeof(signed char); } else { form--; length_mod = sizeof(short int); } break; case 'z': length_mod = sizeof(size_t); break; default: if(*form >= '0' && *form <= '9') sizec[i++] = *form; } form++; } return (long int) (*buffer - start); } /* * The vsnprintf function prints a formated strings into a buffer. * BUG: buffer size checking does not fully work yet */ int vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg) { char *ptr, *bstart; bstart = buffer; ptr = (char *) format; /* * Return from here if size passed is zero, otherwise we would * overrun buffer while setting NULL character at the end. */ if (!buffer || !bufsize) return 0; /* Leave one space for NULL character */ bufsize--; while(*ptr != '\0' && (buffer - bstart) < bufsize) { if(*ptr == '%') { char formstr[20]; int i=0; do { formstr[i] = *ptr; ptr++; i++; } while(!(*ptr == 'd' || *ptr == 'i' || *ptr == 'u' || *ptr == 'x' || *ptr == 'X' || *ptr == 'p' || *ptr == 'c' || *ptr == 's' || *ptr == '%' || *ptr == 'O' || *ptr == 'o' )); formstr[i++] = *ptr; formstr[i] = '\0'; if(*ptr == '%') { *buffer++ = '%'; } else { print_format(&buffer, bufsize - (buffer - bstart), formstr, va_arg(arg, void *)); } ptr++; } else { *buffer = *ptr; buffer++; ptr++; } } *buffer = '\0'; return (buffer - bstart); } skiboot-skiboot-5.1.13/libc/stdio/vsscanf.c000066400000000000000000000053011265204436200205700ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "stdio.h" #include "stdlib.h" #include "string.h" static void _scanf(const char **buffer, const char *fmt, va_list *ap) { int i; int length = 0; fmt++; while(*fmt != '\0') { char tbuf[256]; switch(*fmt) { case 'd': case 'i': if(length == 0) length = 256; for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { tbuf[i] = **buffer; *buffer += 1; } tbuf[i] = '\0'; *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 10); break; case 'X': case 'x': if(length == 0) length = 256; for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { tbuf[i] = **buffer; *buffer += 1; } tbuf[i] = '\0'; *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 16); break; case 'O': case 'o': if(length == 0) length = 256; for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { tbuf[i] = **buffer; *buffer += 1; } tbuf[i] = '\0'; *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 8); break; case 'c': *(va_arg(*ap, char *)) = **buffer; *buffer += 1; if(length > 1) for(i = 1; i < length; i++) *buffer += 1; break; case 's': if(length == 0) length = 256; for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { tbuf[i] = **buffer; *buffer += 1; } tbuf[i] = '\0'; strcpy(va_arg(*ap, char *), tbuf); break; default: if(*fmt >= '0' && *fmt <= '9') length += *fmt - '0'; break; } fmt++; } } int vsscanf(const char *buffer, const char *fmt, va_list ap) { while(*fmt != '\0') { if(*fmt == '%') { char formstr[20]; int i=0; do { formstr[i] = *fmt; fmt++; i++; } while(!(*fmt == 'd' || *fmt == 'i' || *fmt == 'x' || *fmt == 'X' || *fmt == 'p' || *fmt == 'c' || *fmt == 's' || *fmt == '%' || *fmt == 'O' || *fmt == 'o' )); formstr[i++] = *fmt; formstr[i] = '\0'; if(*fmt != '%') { while(*buffer == ' ' || *buffer == '\t' || *buffer == '\n') buffer++; _scanf(&buffer, formstr, &ap); } } fmt++; } return 0; } skiboot-skiboot-5.1.13/libc/stdlib/000077500000000000000000000000001265204436200171215ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/stdlib/Makefile.inc000066400000000000000000000013111265204436200213250ustar00rootroot00000000000000# ***************************************************************************** # * Copyright (c) 2004, 2008 IBM Corporation # * All rights reserved. # * This program and the accompanying materials # * are made available under the terms of the BSD License # * which accompanies this distribution, and is available at # * http://www.opensource.org/licenses/bsd-license.php # * # * Contributors: # * IBM Corporation - initial implementation # ****************************************************************************/ SUBDIRS += $(LIBCDIR)/stdlib STDLIB_OBJS = error.o atoi.o atol.o strtol.o strtoul.o \ rand.o STDLIB = $(LIBCDIR)/stdlib/built-in.o $(STDLIB): $(STDLIB_OBJS:%=$(LIBCDIR)/stdlib/%) skiboot-skiboot-5.1.13/libc/stdlib/atoi.c000066400000000000000000000011251265204436200202200ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int atoi(const char *str) { return strtol(str, NULL, 10); } skiboot-skiboot-5.1.13/libc/stdlib/atol.c000066400000000000000000000011261265204436200202240ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include long atol(const char *str) { return strtol(str, NULL, 10); } skiboot-skiboot-5.1.13/libc/stdlib/error.c000066400000000000000000000010431265204436200204140ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int errno; skiboot-skiboot-5.1.13/libc/stdlib/rand.c000066400000000000000000000012441265204436200202120ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include static unsigned long _rand = 1; int rand(void) { _rand = _rand * 25364735 + 34563; return ((unsigned int) (_rand >> 16) & RAND_MAX); } skiboot-skiboot-5.1.13/libc/stdlib/strtol.c000066400000000000000000000044121265204436200206150ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include long int strtol(const char *S, char **PTR,int BASE) { long rval = 0; short int negative = 0; short int digit; // *PTR is S, unless PTR is NULL, in which case i override it with my own ptr char* ptr; if (PTR == NULL) { //override PTR = &ptr; } // i use PTR to advance through the string *PTR = (char *) S; //check if BASE is ok if ((BASE < 0) || BASE > 36) { return 0; } // ignore white space at beginning of S while ((**PTR == ' ') || (**PTR == '\t') || (**PTR == '\n') || (**PTR == '\r') ) { (*PTR)++; } // check if S starts with "-" in which case the return value is negative if (**PTR == '-') { negative = 1; (*PTR)++; } // if BASE is 0... determine the base from the first chars... if (BASE == 0) { // if S starts with "0x", BASE = 16, else 10 if ((**PTR == '0') && (*((*PTR)+1) == 'x')) { BASE = 16; } else { BASE = 10; } } if (BASE == 16) { // S may start with "0x" if ((**PTR == '0') && (*((*PTR)+1) == 'x')) { (*PTR)++; (*PTR)++; } } //until end of string while (**PTR) { if (((**PTR) >= '0') && ((**PTR) <= '9')) { //digit (0..9) digit = **PTR - '0'; } else if (((**PTR) >= 'a') && ((**PTR) <='z')) { //alphanumeric digit lowercase(a (10) .. z (35) ) digit = (**PTR - 'a') + 10; } else if (((**PTR) >= 'A') && ((**PTR) <='Z')) { //alphanumeric digit uppercase(a (10) .. z (35) ) digit = (**PTR - 'A') + 10; } else { //end of parseable number reached... break; } if (digit < BASE) { rval = (rval * BASE) + digit; } else { //digit found, but its too big for current base //end of parseable number reached... break; } //next... (*PTR)++; } if (negative) { return rval * -1; } //else return rval; } skiboot-skiboot-5.1.13/libc/stdlib/strtoul.c000066400000000000000000000041341265204436200210030ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include unsigned long int strtoul(const char *S, char **PTR,int BASE) { unsigned long rval = 0; short int digit; // *PTR is S, unless PTR is NULL, in which case i override it with my own ptr char* ptr; if (PTR == NULL) { //override PTR = &ptr; } // i use PTR to advance through the string *PTR = (char *) S; //check if BASE is ok if ((BASE < 0) || BASE > 36) { return 0; } // ignore white space at beginning of S while ((**PTR == ' ') || (**PTR == '\t') || (**PTR == '\n') || (**PTR == '\r') ) { (*PTR)++; } // if BASE is 0... determine the base from the first chars... if (BASE == 0) { // if S starts with "0x", BASE = 16, else 10 if ((**PTR == '0') && (*((*PTR)+1) == 'x')) { BASE = 16; } else { BASE = 10; } } if (BASE == 16) { // S may start with "0x" if ((**PTR == '0') && (*((*PTR)+1) == 'x')) { (*PTR)++; (*PTR)++; } } //until end of string while (**PTR) { if (((**PTR) >= '0') && ((**PTR) <='9')) { //digit (0..9) digit = **PTR - '0'; } else if (((**PTR) >= 'a') && ((**PTR) <='z')) { //alphanumeric digit lowercase(a (10) .. z (35) ) digit = (**PTR - 'a') + 10; } else if (((**PTR) >= 'A') && ((**PTR) <='Z')) { //alphanumeric digit uppercase(a (10) .. z (35) ) digit = (**PTR - 'A') + 10; } else { //end of parseable number reached... break; } if (digit < BASE) { rval = (rval * BASE) + digit; } else { //digit found, but its too big for current base //end of parseable number reached... break; } //next... (*PTR)++; } //done return rval; } skiboot-skiboot-5.1.13/libc/string/000077500000000000000000000000001265204436200171465ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/string/Makefile.inc000066400000000000000000000015061265204436200213600ustar00rootroot00000000000000# ***************************************************************************** # * Copyright (c) 2004, 2008 IBM Corporation # * All rights reserved. # * This program and the accompanying materials # * are made available under the terms of the BSD License # * which accompanies this distribution, and is available at # * http://www.opensource.org/licenses/bsd-license.php # * # * Contributors: # * IBM Corporation - initial implementation # ****************************************************************************/ SUBDIRS += $(LIBCDIR)/string STRING_OBJS = strcat.o strchr.o strcmp.o strcpy.o strlen.o strncmp.o \ strncpy.o strstr.o memset.o memcpy.o memmove.o memchr.o \ memcmp.o strcasecmp.o strncasecmp.o strtok.o strdup.o STRING = $(LIBCDIR)/string/built-in.o $(STRING): $(STRING_OBJS:%=$(LIBCDIR)/string/%) skiboot-skiboot-5.1.13/libc/string/memchr.c000066400000000000000000000013471265204436200205720ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "string.h" void * memchr(const void *ptr, int c, size_t n) { unsigned char ch = (unsigned char)c; const unsigned char *p = ptr; while (n-- > 0) { if (*p == ch) return (void *)p; p += 1; } return NULL; } skiboot-skiboot-5.1.13/libc/string/memcmp.c000066400000000000000000000013721265204436200205730ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "string.h" int memcmp(const void *ptr1, const void *ptr2, size_t n) { const unsigned char *p1 = ptr1; const unsigned char *p2 = ptr2; while (n-- > 0) { if (*p1 != *p2) return (*p1 - *p2); p1 += 1; p2 += 1; } return 0; } skiboot-skiboot-5.1.13/libc/string/memcpy.c000066400000000000000000000013031265204436200206010ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "string.h" void * memcpy(void *dest, const void *src, size_t n) { char *cdest; const char *csrc = src; cdest = dest; while (n-- > 0) { *cdest++ = *csrc++; } return dest; } skiboot-skiboot-5.1.13/libc/string/memmove.c000066400000000000000000000017451265204436200207660ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "string.h" void * memmove(void *dest, const void *src, size_t n) { char *cdest; const char *csrc; int i; /* Do the buffers overlap in a bad way? */ if (src < dest && src + n >= dest) { /* Copy from end to start */ cdest = dest + n - 1; csrc = src + n - 1; for (i = 0; i < n; i++) { *cdest-- = *csrc--; } } else { /* Normal copy is possible */ cdest = dest; csrc = src; for (i = 0; i < n; i++) { *cdest++ = *csrc++; } } return dest; } skiboot-skiboot-5.1.13/libc/string/memset.c000066400000000000000000000022061265204436200206040ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include "string.h" #define CACHE_LINE_SIZE 128 void * memset(void *dest, int c, size_t size) { unsigned char *d = (unsigned char *)dest; #if defined(__powerpc__) || defined(__powerpc64__) if (size > CACHE_LINE_SIZE && c==0) { while ((unsigned long long)d % CACHE_LINE_SIZE) { *d++ = (unsigned char)c; size--; } while (size >= CACHE_LINE_SIZE) { asm volatile ("dcbz 0,%0\n" : : "r"(d) : "memory"); d+= CACHE_LINE_SIZE; size-= CACHE_LINE_SIZE; } } #endif while (size >= 8 && c == 0) { *((unsigned long long*)d) = 0ULL; d+=8; size-=8; } while (size-- > 0) { *d++ = (unsigned char)c; } return dest; } skiboot-skiboot-5.1.13/libc/string/strcasecmp.c000066400000000000000000000013241265204436200214560ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int strcasecmp(const char *s1, const char *s2) { while (*s1 != 0 && *s2 != 0) { if (toupper(*s1) != toupper(*s2)) break; ++s1; ++s2; } return *s1 - *s2; } skiboot-skiboot-5.1.13/libc/string/strcat.c000066400000000000000000000012061265204436200206110ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include char * strcat(char *dst, const char *src) { int p; p = strlen(dst); strcpy(&dst[p], src); return dst; } skiboot-skiboot-5.1.13/libc/string/strchr.c000066400000000000000000000012511265204436200206160ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include char * strchr(const char *s, int c) { char cb = c; while (*s != 0) { if (*s == cb) { return (char *)s; } s += 1; } return NULL; } skiboot-skiboot-5.1.13/libc/string/strcmp.c000066400000000000000000000012621265204436200206230ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int strcmp(const char *s1, const char *s2) { while (*s1 != 0 && *s2 != 0) { if (*s1 != *s2) break; s1 += 1; s2 += 1; } return *s1 - *s2; } skiboot-skiboot-5.1.13/libc/string/strcpy.c000066400000000000000000000012261265204436200206370ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include char * strcpy(char *dst, const char *src) { char *ptr = dst; do { *ptr++ = *src; } while (*src++ != 0); return dst; } skiboot-skiboot-5.1.13/libc/string/strdup.c000066400000000000000000000012701265204436200206330ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2012 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include char *strdup(const char *src) { size_t len = strlen(src) + 1; char *ret; ret = malloc(len); if (ret) memcpy(ret, src, len); return ret; } skiboot-skiboot-5.1.13/libc/string/strlen.c000066400000000000000000000012031265204436200206150ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include size_t strlen(const char *s) { int len = 0; while (*s != 0) { len += 1; s += 1; } return len; } skiboot-skiboot-5.1.13/libc/string/strncasecmp.c000066400000000000000000000014261265204436200216370ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include int strncasecmp(const char *s1, const char *s2, size_t n) { if (n < 1) return 0; while (*s1 != 0 && *s2 != 0 && --n > 0) { if (toupper(*s1) != toupper(*s2)) break; ++s1; ++s2; } return toupper(*s1) - toupper(*s2); } skiboot-skiboot-5.1.13/libc/string/strncmp.c000066400000000000000000000013411265204436200207770ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include int strncmp(const char *s1, const char *s2, size_t n) { if (n < 1) return 0; while (*s1 != 0 && *s2 != 0 && --n > 0) { if (*s1 != *s2) break; s1 += 1; s2 += 1; } return *s1 - *s2; } skiboot-skiboot-5.1.13/libc/string/strncpy.c000066400000000000000000000014571265204436200210230ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include char * strncpy(char *dst, const char *src, size_t n) { char *ret = dst; /* Copy string */ while (*src != 0 && n > 0) { *dst++ = *src++; n -= 1; } /* strncpy always clears the rest of destination string... */ while (n > 0) { *dst++ = 0; n -= 1; } return ret; } skiboot-skiboot-5.1.13/libc/string/strstr.c000066400000000000000000000015701265204436200206560ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include char * strstr(const char *hay, const char *needle) { char *pos; int hlen, nlen; if (hay == NULL || needle == NULL) return NULL; hlen = strlen(hay); nlen = strlen(needle); if (nlen < 1) return (char *)hay; for (pos = (char *)hay; pos < hay + hlen; pos++) { if (strncmp(pos, needle, nlen) == 0) { return pos; } } return NULL; } skiboot-skiboot-5.1.13/libc/string/strtok.c000066400000000000000000000016311265204436200206410ustar00rootroot00000000000000/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include char * strtok(char *src, const char *pattern) { static char *nxtTok; char *retVal = NULL; if (!src) src = nxtTok; while (*src) { const char *pp = pattern; while (*pp) { if (*pp == *src) { break; } pp++; } if (!*pp) { if (!retVal) retVal = src; else if (!src[-1]) break; } else *src = '\0'; src++; } nxtTok = src; return retVal; } skiboot-skiboot-5.1.13/libc/test/000077500000000000000000000000001265204436200166175ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libc/test/.gitignore000066400000000000000000000001701265204436200206050ustar00rootroot00000000000000gmon.out run-ctype run-ctype-gcov run-memops run-memops-gcov run-snprintf run-snprintf-gcov run-stdlib run-stdlib-gcov skiboot-skiboot-5.1.13/libc/test/Makefile.check000066400000000000000000000060421265204436200213350ustar00rootroot00000000000000# -*-Makefile-*- LIBC_TEST := libc/test/run-time # We have some tricky tests that have system libc and skiboot libc, # so that we can test parts of skiboot libc but in userspace. LIBC_DUALLIB_TEST := libc/test/run-snprintf \ libc/test/run-memops \ libc/test/run-stdlib \ libc/test/run-ctype LCOV_EXCLUDE += $(LIBC_TEST:%=%.c) $(LIBC_DUALLIB_TEST:%=%.c) $(LIBC_DUALLIB_TEST:%=%-test.c) check: $(LIBC_TEST:%=%-check) $(LIBC_DUALLIB_TEST:%=%-check) coverage: $(LIBC_TEST:%=%-gcov-run) $(LIBC_DUALLIB_TEST:%=%-gcov-run) $(LIBC_TEST:%=%-gcov-run) : %-run: % $(call Q, TEST-COVERAGE ,$< , $<) $(LIBC_DUALLIB_TEST:%=%-gcov-run) : %-run: % $(eval LCOV_DIRS += -d $(dir $<) ) $(call Q, TEST-COVERAGE , (cd $(dir $<); GCOV_PREFIX_STRIP=`(c=0; while [ "\`pwd\`" != '/' ]; do cd ..; c=\`expr 1 + $$c\`; done; echo $$c)` ./$(notdir $<) ), $< ) $(LIBC_TEST:%=%-check) : %-check: % $(call Q, RUN-TEST ,$(VALGRIND) $<, $<) $(LIBC_DUALLIB_TEST:%=%-check) : %-check: % $(call Q, RUN-TEST ,$(VALGRIND) $<, $<) $(LIBC_TEST) : % : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -I libfdt -I libc/include -o $@ $<, $<) $(LIBC_TEST:%=%-gcov): %-gcov : %.c % $(call Q, HOSTCC ,(cd $(dir $<); $(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -lgcov -O0 -g -I$(shell pwd)/include -I$(shell pwd) -I$(shell pwd)/libfdt -I$(shell pwd)/libc/include -o $(notdir $@) $(notdir $<)), $<) $(LIBC_DUALLIB_TEST) : % : %.o %-test.o $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -I libfdt -o $@ $@-test.o $<, $<) $(LIBC_DUALLIB_TEST:%=%-gcov) : %-gcov : %-gcov.o %-gcov-test.o $(call Q, HOSTCC ,(cd $(dir $<); $(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -lgcov -O0 -pg -g -I$(shell pwd)/include -I$(shell pwd)/. -I$(shell pwd)/libfdt -o $(notdir $@) $(notdir $@)-test.o $(notdir $<)), $<) $(LIBC_DUALLIB_TEST:%=%-test.o): %-test.o : %-test.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -I libfdt -I libc/include -ffreestanding -o $@ -c $<, $<) $(LIBC_DUALLIB_TEST:%=%.o): %.o : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -o $@ -c $<, $<) $(LIBC_DUALLIB_TEST:%=%-gcov.o): %-gcov.o : %.c $(call Q, HOSTCC ,(cd $(dir $<); $(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -lgcov -pg -O0 -g -o $(notdir $@) -c $(notdir $<)), $<) $(LIBC_DUALLIB_TEST:%=%-gcov-test.o): %-gcov-test.o : %-test.c % $(call Q, HOSTCC ,(cd $(dir $<); $(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -lgcov -pg -O0 -g -I$(shell pwd)/include -I$(shell pwd)/. -I$(shell pwd)/libfdt -I$(shell pwd)/libc/include -ffreestanding -o $(notdir $@) -c $(notdir $<) ), $<) -include $(wildcard libc/test/*.d) clean: libc-test-clean libc-test-clean: $(RM) -f libc/test/*.[od] $(RM) -f $(LIBC_TEST) \ $(LIBC_TEST:%=%-gcov) \ $(LIBC_TEST:%=%.gcda) \ $(LIBC_TEST:%=%.gcno) $(RM) -f $(LIBC_DUALLIB_TEST) \ $(LIBC_DUALLIB_TEST:%=%-gcov) \ $(LIBC_DUALLIB_TEST:%=%-gcov.gcda) \ $(LIBC_DUALLIB_TEST:%=%-gcov.gcno) \ $(LIBC_DUALLIB_TEST:%=%-gcov-test.gcda) \ $(LIBC_DUALLIB_TEST:%=%-gcov-test.gcno) \ $(LIBC_DUALLIB_TEST:%=%-test.o) skiboot-skiboot-5.1.13/libc/test/run-ctype-test.c000066400000000000000000000030041265204436200216630ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file is run with the skiboot libc files rather than system libc. * This means we have a bit of "fun" with actually executing the tests on * the host. * Patches to make this less ugly are very welcome. */ #include #include #include "../ctype/isdigit.c" #include "../ctype/isprint.c" #include "../ctype/isspace.c" #include "../ctype/isxdigit.c" #include "../ctype/tolower.c" #include "../ctype/toupper.c" int skiboot_isdigit(int ch); int skiboot_isprint(int ch); int skiboot_isspace(int ch); int skiboot_isxdigit(int ch); int skiboot_tolower(int ch); int skiboot_toupper(int ch); int skiboot_isdigit(int ch) { return isdigit(ch); } int skiboot_isprint(int ch) { return isprint(ch); } int skiboot_isspace(int ch) { return isspace(ch); } int skiboot_isxdigit(int ch) { return isxdigit(ch); } int skiboot_tolower(int ch) { return tolower(ch); } int skiboot_toupper(int ch) { return toupper(ch); } skiboot-skiboot-5.1.13/libc/test/run-ctype.c000066400000000000000000000043221265204436200207120ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define BUFSZ 50 #include #include #include #include #include #include int skiboot_isdigit(int ch); int skiboot_isprint(int ch); int skiboot_isspace(int ch); int skiboot_isxdigit(int ch); int skiboot_tolower(int ch); int skiboot_toupper(int ch); int main(void) { int i; int r1, r2; for(i = '0'; i <= '9'; i++) assert(skiboot_isdigit(i)); assert(skiboot_isdigit('a') == 0); assert(skiboot_isdigit('Z') == 0); for (i = 0; i < 257; i++) { r1 = skiboot_isdigit(i); r2 = isdigit(i); if (r1) assert(r2); if (!r1) assert(!r2); } for(i = '0'; i <= '9'; i++) assert(skiboot_isprint(i)); assert(skiboot_isprint('\0') == 0); assert(skiboot_isprint(4) == 0); for (i = 0; i < 257; i++) { r1 = skiboot_isprint(i); r2 = isprint(i); if (r1) assert(r2); if (!r1) assert(!r2); } for(i = '0'; i <= '9'; i++) assert(skiboot_isspace(i) == 0); assert(skiboot_isspace('\f')); assert(skiboot_isspace('\n')); assert(skiboot_isspace(' ')); for (i = 0; i < 257; i++) { r1 = skiboot_isspace(i); r2 = isspace(i); if (r1) assert(r2); if (!r1) assert(!r2); } for(i = '0'; i <= '9'; i++) assert(skiboot_isxdigit(i)); assert(skiboot_isxdigit('a')); assert(skiboot_isxdigit('A')); assert(skiboot_isxdigit('F')); assert(skiboot_isxdigit('Z') == 0); for (i = 0; i < 257; i++) { r1 = skiboot_isxdigit(i); r2 = isxdigit(i); if (r1) assert(r2); if (!r1) assert(!r2); } for (i = 0; i < 257; i++) { assert(skiboot_tolower(i) == tolower(i)); } for (i = 0; i < 257; i++) { assert(skiboot_toupper(i) == toupper(i)); } return 0; } skiboot-skiboot-5.1.13/libc/test/run-memops-test.c000066400000000000000000000057141265204436200220510ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file is run with the skiboot libc files rather than system libc. * This means we have a bit of "fun" with actually executing the tests on * the host. * Patches to make this less ugly are very welcome. */ #include #include #include "../string/memchr.c" #include "../string/memcmp.c" #include "../string/memcpy.c" #include "../string/memmove.c" #include "../string/memset.c" #include "../string/strcasecmp.c" #include "../string/strcat.c" #include "../string/strchr.c" #include "../string/strcmp.c" #include "../string/strcpy.c" /* #include "../string/strdup.c" */ #include "../string/strlen.c" #include "../string/strncasecmp.c" #include "../string/strncmp.c" #include "../string/strncpy.c" #include "../string/strstr.c" #include "../string/strtok.c" #include int test_memset(char* buf, int c, size_t s); int test_memchr(const void *ptr, int c, size_t n, void* expected); int test_memcmp(const void *ptr1, const void *ptr2, size_t n, int expected); int test_strcmp(const void *ptr1, const void *ptr2, int expected); int test_strchr(const char *s, int c, char *expected); int test_strcasecmp(const char *s1, const char *s2, int expected); int test_strncasecmp(const char *s1, const char *s2, size_t n, int expected); int test_memmove(void *dest, const void *src, size_t n, const void *r, const void *expected, size_t expected_n); int test_memset(char* buf, int c, size_t s) { int i; int r= 0; memset(buf, c, s); for(i=0; i #include #include #include int test_memset(char* buf, int c, size_t s); int test_memchr(const void *ptr, int c, size_t n, void* expected); int test_memcmp(const void *ptr1, const void *ptr2, size_t n, int expected); int test_strcmp(const void *ptr1, const void *ptr2, int expected); int test_strchr(const char *s, int c, char *expected); int test_strcasecmp(const char *s1, const char *s2, int expected); int test_strncasecmp(const char *s1, const char *s2, size_t n, int expected); int test_memmove(void *dest, const void *src, size_t n, const void *r, const void *expected, size_t expected_n); int main(void) { char *buf; char *buf2; buf = malloc(100); assert(test_memset(buf, 0x42, 100) == 0); free(buf); buf = malloc(128); assert(test_memset(buf, 0, 128) == 0); assert(test_memset(buf+1, 0, 127) == 0); free(buf); buf = malloc(1024); assert(test_memset(buf, 0, 1024) == 0); free(buf); buf = malloc(20); strncpy(buf, "Hello World!", 20); assert(test_memchr(buf, 'o', strlen(buf), buf+4)); assert(test_memchr(buf, 'a', strlen(buf), NULL)); assert(test_memcmp(buf, "Hello World!", strlen(buf), 0)); assert(test_memcmp(buf, "Hfllow World", strlen(buf), -1)); assert(test_strcmp(buf, "Hello World!", 0)); assert(test_strcmp(buf, "Hfllow World", -1)); assert(test_strchr(buf, 'H', buf)); assert(test_strchr(buf, 'e', buf+1)); assert(test_strchr(buf, 'a', NULL)); assert(test_strchr(buf, '!', buf+11)); assert(test_strcasecmp(buf, "Hello World!", 0)); assert(test_strcasecmp(buf, "HELLO WORLD!", 0)); assert(test_strcasecmp(buf, "IELLO world!", -1)); assert(test_strcasecmp(buf, "HeLLo WOrlc!", 1)); assert(test_strncasecmp(buf, "Hello World!", strlen(buf), 0)); assert(test_strncasecmp(buf, "HELLO WORLD!", strlen(buf), 0)); assert(test_strncasecmp(buf, "IELLO world!", strlen(buf), -1)); assert(test_strncasecmp(buf, "HeLLo WOrlc!", strlen(buf), 1)); assert(test_strncasecmp(buf, "HeLLo WOrlc!", 0, 0)); assert(test_strncasecmp(buf, "HeLLo WOrlc!", 1, 0)); assert(test_strncasecmp(buf, "HeLLo WOrlc!", 2, 0)); assert(test_strncasecmp(buf, "HeLLp WOrlc!", 5, -1)); free(buf); buf = malloc(20); buf2 = malloc(20); strncpy(buf, "Hello", 20); strncpy(buf2, " World!", 20); assert(test_memmove(buf + 5, buf2, strlen(buf2), buf, "Hello World!", strlen("Hello World!"))); strncpy(buf, "HHello World!", 20); assert(test_memmove(buf, buf+1, strlen("Hello World!"), buf, "Hello World!", strlen("Hello World!"))); strncpy(buf, "0123456789", 20); assert(test_memmove(buf+1, buf , strlen("0123456789"), buf, "00123456789", strlen("00123456789"))); free(buf); free(buf2); return 0; } skiboot-skiboot-5.1.13/libc/test/run-snprintf-test.c000066400000000000000000000025321265204436200224070ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file is run with the skiboot libc files rather than system libc. * This means we have a bit of "fun" with actually executing the tests on * the host. * Patches to make this less ugly are very welcome. */ #include #include #include "../stdio/snprintf.c" #include "../stdio/vsnprintf.c" int test1(void); int test1(void) { return snprintf(NULL, 1, "Hello"); } int skiboot_snprintf(char *buf, size_t bufsz, size_t l, const char* format, ...); int skiboot_snprintf(char *buf, size_t bufsz, size_t l, const char* format, ...) { va_list ar; int count; if (buf) memset(buf, 0, bufsz); if ((buf==NULL) || (format==NULL)) return(-1); va_start(ar, format); count = vsnprintf(buf, l, format, ar); va_end(ar); return(count); } skiboot-skiboot-5.1.13/libc/test/run-snprintf.c000066400000000000000000000107331265204436200214340ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define BUFSZ 50 #include #include #include #include int test1(void); int skiboot_snprintf(char *buf, size_t bufsz, size_t l, const char* format, ...); static void test_printf_d(int n) { char *buf, *buf2; int blen; int r; int i; for(i=1; i<10; i++) { blen = i+1; if (n<0) blen++; buf = (char*)malloc(blen); buf2 = (char*)malloc(blen); r = skiboot_snprintf(buf, blen, blen, "%d", n); snprintf(buf2, blen, "%d", n); n = n * 10; if (n<0) assert(i+1 == r); else assert(i == r); assert(0 == strncmp(buf, buf2, blen)); free(buf); free(buf2); } } static void test_printf_x(const char* f) { char *buf, *buf2; int blen; int i, r, n=0x1; for (i=0; i<8; i++) { blen = i+2; buf = (char*)malloc(blen); buf2 = (char*)malloc(blen); r = skiboot_snprintf(buf, blen, blen, f, n); snprintf(buf2, blen, f, n); assert(i+1 == r); assert(0 == strncmp(buf, buf2, blen)); free(buf); free(buf2); n = n << 4; } } static void test_printf_c(void) { char *buf= (char*)malloc(2); char buf2[2]; unsigned char i= 0xff; int r; while(i) { r = skiboot_snprintf(buf, 2, 2, "%c", i); snprintf(buf2, 2, "%c", i); assert(r==1); assert(0 == strncmp(buf, buf2, 2)); i--; } free(buf); } static void test_printf_p(void) { char *buf= (char*)malloc(32); char buf2[32]; skiboot_snprintf(buf, 32, 32, "%p", buf); snprintf(buf2, 32, "%p", buf); assert(0 == strncmp(buf, buf2, 32)); free(buf); } static void test_printf_o(void) { char *buf= (char*)malloc(32); char buf2[32]; skiboot_snprintf(buf, 32, 32, "%o", 0x12345678); snprintf(buf2, 32, "%o", 0x12345678); assert(0 == strncmp(buf, buf2, 32)); free(buf); } static void test_printf_h(short i) { char *buf= (char*)malloc(32); char buf2[32]; skiboot_snprintf(buf, 32, 32, "%hd", i); snprintf(buf2, 32, "%hd", i); assert(0 == strncmp(buf, buf2, 32)); free(buf); } static void test_printf_z(size_t i) { char *buf= (char*)malloc(32); char buf2[32]; skiboot_snprintf(buf, 32, 32, "%zu", i); snprintf(buf2, 32, "%zu", i); assert(0 == strncmp(buf, buf2, 32)); free(buf); } int main(void) { char *buf; int r; buf = (char*)malloc(BUFSZ); memset(buf, 0, BUFSZ); assert(-1 == test1()); r = skiboot_snprintf(buf, BUFSZ, 2, "%%"); assert(r==1); assert(buf[0] == '%' && buf[1] == 0); r = skiboot_snprintf(buf, BUFSZ, 2, "%d", 137); /* BUG/FIXME: * skiboot libc does NOT return the length of the buffer you'd need * Instead, it'll return something random, possibly zero (as here) * but as you'll see in test_in_buf_len2, sometimes not. * * Basically, we're not POSIX printf and this is some day going to * cause things to be awful. */ assert(0 == r); // BUG, should be 3 assert(0 == strncmp(buf, "", 3)); r = skiboot_snprintf(buf, BUFSZ, 4, "%d", 137); assert(3 == r); assert(0 == strncmp(buf, "137", 3)); assert(buf[3] == 0); /* Now we test the strange behaviour of our printf. * For strings, we get partial prints going, but if we whack an * integer on the end, we may or may not get that integer, depending * on if we have enough size. We should test that though */ r = skiboot_snprintf(buf, BUFSZ, 4, "Hello %d", 137); assert(3 == r); assert(0 == strncmp(buf, "Hel", 3)); assert(buf[3] == 0); r = skiboot_snprintf(buf, BUFSZ, 7, "Hello %d", 137); assert(6 == r); assert(0 == strncmp(buf, "Hello ", 6)); assert(buf[6] == 0); r = skiboot_snprintf(buf, BUFSZ, 10, "Hello %d", 137); assert(9 == r); assert(0 == strncmp(buf, "Hello 137", 10)); assert(buf[9] == 0); free(buf); test_printf_d(1); test_printf_d(-1); test_printf_x("%x"); test_printf_x("%X"); test_printf_c(); test_printf_p(); test_printf_o(); test_printf_h(0); test_printf_h(128); test_printf_h(256); test_printf_h(-1); test_printf_h(32767); test_printf_h(32768); test_printf_h(65535); test_printf_z(0); test_printf_z(-1); test_printf_z(12345); test_printf_z(128000000); return 0; } skiboot-skiboot-5.1.13/libc/test/run-stdlib-test.c000066400000000000000000000020071265204436200220220ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file is run with the skiboot libc files rather than system libc. * This means we have a bit of "fun" with actually executing the tests on * the host. * Patches to make this less ugly are very welcome. */ #include #include #include "../stdlib/atoi.c" #include "../stdlib/atol.c" #include "../stdlib/error.c" #include "../stdlib/rand.c" #include "../stdlib/strtol.c" #include "../stdlib/strtoul.c" skiboot-skiboot-5.1.13/libc/test/run-stdlib.c000066400000000000000000000051231265204436200210470ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define BUFSZ 50 #include #include #include #include int main(void) { char buf[] = "42, and stuff."; char *ptr; /* atoi/strtol - general correct behavior */ assert(atoi("0") == 0); assert(atoi("1") == 1); assert(atoi(" 123456") == 123456); assert(atoi("-72") == -72); assert(atoi(" -84") == -84); assert(atoi("2147483647") == 2147483647); /* atoi/strtol - numbers before and after strings */ assert(atoi("hello!123") == 0); assert(atoi(buf) == 42); assert(atoi("42isthemagicnumber") == 42); /* atoi is base 10 only */ assert(atoi("0x800") == 0); /* atol - ensure it recognises longs */ assert(atol("2147483648") == 2147483648); assert(atol("-2147483649") == -2147483649); /* strtol detects hex */ assert(strtol("0x800", NULL, 0) == 0x800); /* But not with a duplicate prefix */ assert(strtol("0x0x800", NULL, 0) == 0); /* strtol - invalid/weird bases */ assert(strtol("z", NULL, -1) == 0); assert(strtol("11111", NULL, 1) == 0); assert(strtol("z", NULL, 37) == 0); assert(strtol("z", NULL, 36) == 35); assert(strtol("-Y", NULL, 36) == -34); /* strtol - ptr advanced correctly */ ptr = buf; assert(strtol(buf, &ptr, 10) == 42); assert(ptr == buf + 2); /* strtoul - base 10 */ assert(strtoul("0", NULL, 10) == 0); assert(strtoul("1", NULL, 10) == 1); assert(strtoul(" 123456", NULL, 10) == 123456); assert(strtoul("-72", NULL, 10) == 0); assert(strtoul("9999999999", NULL, 10) == 9999999999); assert(strtoul("hello!123", NULL, 10) == 0); assert(strtoul(buf, NULL, 10) == 42); assert(strtoul("42isthemagicnumber", NULL, 10) == 42); /* strtoul - autodetection of base */ assert(strtoul(" 123456", NULL, 0) == 123456); assert(strtoul("0x800", NULL, 0) == 0x800); assert(strtoul("0x0x800", NULL, 0) == 0); /* strtoul - weird/invalid bases */ assert(strtoul("z", NULL, -1) == 0); assert(strtoul("11111", NULL, 1) == 0); assert(strtoul("z", NULL, 37) == 0); assert(strtoul("z", NULL, 36) == 35); assert(strtoul("Y", NULL, 36) == 34); return 0; } skiboot-skiboot-5.1.13/libc/test/run-time.c000066400000000000000000000042241265204436200205250ustar00rootroot00000000000000#include "/usr/include/assert.h" #include #include #include #include "../time.c" #define MKTIME_TEST(Y,M,D,h,m,s,t) \ tm.tm_year = Y; \ tm.tm_mon = M; \ tm.tm_mday = D; \ tm.tm_hour = h; \ tm.tm_min = m; \ tm.tm_sec = s; \ assert(mktime(&tm) == t); \ assert(tm.tm_year == Y); \ assert(tm.tm_mon == M); \ assert(tm.tm_mday == D); \ assert(tm.tm_hour == h); \ assert(tm.tm_min == m); \ assert(tm.tm_sec == s) #define GMTIME_TEST(Y,M,D,h,m,s,tv) \ t = tv; \ gmtime_r(&t, &tm); \ assert(tm.tm_year == Y); \ assert(tm.tm_mon == M); \ assert(tm.tm_mday == D); \ assert(tm.tm_hour == h); \ assert(tm.tm_min == m); \ assert(tm.tm_sec == s) #define TIME_TEST(Y,M,D,h,m,s,tv) \ MKTIME_TEST(Y,M,D,h,m,s,tv); \ GMTIME_TEST(Y,M,D,h,m,s,tv) int main(void) { struct tm tm; time_t t = 0; TIME_TEST(1970, 0, 1, 0, 0, 0, 0); TIME_TEST(1971, 0, 1, 0, 0, 0, 365*SECS_PER_DAY); TIME_TEST(1972, 0, 1, 0, 0, 0, 2*365*SECS_PER_DAY); TIME_TEST(1972, 11, 31, 0, 0, 0, 3*365*SECS_PER_DAY); TIME_TEST(1973, 0, 1, 0, 0, 0, (3*365+1)*SECS_PER_DAY); TIME_TEST(2000, 11, 31, 0, 0, 0, 978220800); TIME_TEST(2001, 0, 1, 0, 0, 0, 978307200); TIME_TEST(2003, 11, 31, 0, 0, 0, 1072828800); TIME_TEST(2004, 0, 1, 0, 0, 0, 1072828800+SECS_PER_DAY); TIME_TEST(2004, 11, 29, 0, 0, 0, 1072828800+364*SECS_PER_DAY); TIME_TEST(2004, 11, 30, 0, 0, 0, 1072828800+365*SECS_PER_DAY); TIME_TEST(2004, 11, 31, 0, 0, 0, 1072828800+366*SECS_PER_DAY); TIME_TEST(2004, 11, 31, 23, 59, 59, 1072828800+367*SECS_PER_DAY-1); TIME_TEST(2100, 11, 31, 0, 0, 0, 4133894400); TIME_TEST(2101, 0, 1, 0, 0, 0, 4133980800); /* Test the normalisation functionality of mktime */ tm.tm_year = 2000; tm.tm_mon = 1; tm.tm_mday = 10; tm.tm_hour = 5; tm.tm_min = 32; tm.tm_sec = 105; mktime(&tm); assert(tm.tm_year == 2000); assert(tm.tm_mon == 1); assert(tm.tm_mday == 10); assert(tm.tm_hour == 5); assert(tm.tm_min == 33); assert(tm.tm_sec == 45); tm.tm_sec += 366*24*60*60; mktime(&tm); assert(tm.tm_year == 2001); assert(tm.tm_mon == 1); assert(tm.tm_mday == 10); assert(tm.tm_hour == 5); assert(tm.tm_min == 33); assert(tm.tm_sec == 45); return 0; } skiboot-skiboot-5.1.13/libc/time.c000066400000000000000000000061721265204436200167500ustar00rootroot00000000000000#include #include /* * Returns the number of leap years prior to the given year. */ static int leap_years(int year) { return (year-1)/4 + (year-1)/400 - (year-1)/100; } static int is_leap_year(int year) { return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } static int days_in_month(int month, int year) { static int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, }; /* we may need to update this in the year 4000, pending a * decision on whether or not it's a leap year */ if (month == 1) return is_leap_year(year) ? 29 : 28; return month_days[month]; } static const int days_per_month[2][13] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}}; #define SECS_PER_MIN 60 #define SECS_PER_HOUR (SECS_PER_MIN*60) #define SECS_PER_DAY (24*SECS_PER_HOUR) #define DAYS_PER_YEAR 365 struct tm *gmtime_r(const time_t *timep, struct tm *result) { int i; int Y; int M; int D; int h; int m; int s; D = *timep / SECS_PER_DAY; s = *timep % SECS_PER_DAY; m = s / 60; h = m / 60; m %= 60; s %= 60; /* * Work out the year. We subtract one day for every four years * and every 400 years after 1969. However as leap years don't * occur every 100 years we add one day back to counteract the * the substraction for every 4 years. */ Y = (D - (1+D/365)/4 + (69+D/365)/100 - (369+D/365)/400)/365; /* * Remember we're doing integer arithmetic here so * leap_years(Y+1970) - leap_years(1970) != leap_years(Y) */ D = D - Y*365 - (leap_years(Y+1970) - leap_years(1970)) + 1; Y += 1970; M = 0; for (i = 0; i < 13; i++) if (D <= days_per_month[is_leap_year(Y) ? 1 : 0][i]) { M = i; break; } D -= days_per_month[is_leap_year(Y)][M-1]; result->tm_year = Y; result->tm_mon = M - 1; result->tm_mday = D; result->tm_hour = h; result->tm_min = m; result->tm_sec = s; return result; } time_t mktime(struct tm *tm) { unsigned long year, month, mday, hour, minute, second, d; static const unsigned long sec_in_400_years = ((3903ul * 365) + (97 * 366)) * 24 * 60 * 60; second = tm->tm_sec; minute = tm->tm_min; hour = tm->tm_hour; mday = tm->tm_mday; month = tm->tm_mon; year = tm->tm_year; /* There are the same number of seconds in any 400-year block; this * limits the iterations in the loop below */ year += 400 * (second / sec_in_400_years); second = second % sec_in_400_years; if (second >= 60) { minute += second / 60; second = second % 60; } if (minute >= 60) { hour += minute / 60; minute = minute % 60; } if (hour >= 24) { mday += hour / 24; hour = hour % 24; } for (d = days_in_month(month, year); mday > d; d = days_in_month(month, year)) { month++; if (month > 12) { month = 0; year++; } mday -= d; } tm->tm_year = year; tm->tm_mon = month; tm->tm_mday = mday; tm->tm_hour = hour; tm->tm_min = minute; tm->tm_sec = second; d = mday; d += days_per_month[is_leap_year(year)][month]; d += (year-1970)*DAYS_PER_YEAR + leap_years(year) - leap_years(1970) - 1; return d*SECS_PER_DAY + hour*SECS_PER_HOUR + minute*SECS_PER_MIN + second; } skiboot-skiboot-5.1.13/libfdt/000077500000000000000000000000001265204436200161735ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libfdt/Makefile.inc000066400000000000000000000007611265204436200204070ustar00rootroot00000000000000# Makefile.libfdt # # This is not a complete Makefile of itself. Instead, it is designed to # be easily embeddable into other systems of Makefiles. # LIBFDT_INCLUDES = fdt.h libfdt.h LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) # That warning can't really be fixed so compile the file without it CFLAGS_SKIP_libfdt/fdt_sw.o = -Wstack-usage=4096 SUBDIRS += libfdt LIBFDT = libfdt/built-in.o $(LIBFDT): $(LIBFDT_OBJS:%=libfdt/%) skiboot-skiboot-5.1.13/libfdt/Makefile.libfdt000066400000000000000000000005021265204436200210730ustar00rootroot00000000000000# Makefile.libfdt # # This is not a complete Makefile of itself. Instead, it is designed to # be easily embeddable into other systems of Makefiles. # LIBFDT_INCLUDES = fdt.h libfdt.h LIBFDT_VERSION = version.lds LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) skiboot-skiboot-5.1.13/libfdt/TODO000066400000000000000000000001251265204436200166610ustar00rootroot00000000000000- Tree traversal functions - Graft function - Complete libfdt.h documenting comments skiboot-skiboot-5.1.13/libfdt/fdt.c000066400000000000000000000132261265204436200171200ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libfdt_env.h" #include #include #include "libfdt_internal.h" int fdt_check_header(const void *fdt) { if (fdt_magic(fdt) == FDT_MAGIC) { /* Complete tree */ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) return -FDT_ERR_BADVERSION; if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) return -FDT_ERR_BADVERSION; } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { /* Unfinished sequential-write blob */ if (fdt_size_dt_struct(fdt) == 0) return -FDT_ERR_BADSTATE; } else { return -FDT_ERR_BADMAGIC; } return 0; } const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) { const char *p; if (fdt_version(fdt) >= 0x11) if (((offset + len) < offset) || ((offset + len) > fdt_size_dt_struct(fdt))) return NULL; p = _fdt_offset_ptr(fdt, offset); if (p + len < p) return NULL; return p; } uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) { const uint32_t *tagp, *lenp; uint32_t tag; int offset = startoffset; const char *p; *nextoffset = -FDT_ERR_TRUNCATED; tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); if (!tagp) return FDT_END; /* premature end */ tag = fdt32_to_cpu(*tagp); offset += FDT_TAGSIZE; *nextoffset = -FDT_ERR_BADSTRUCTURE; switch (tag) { case FDT_BEGIN_NODE: /* skip name */ do { p = fdt_offset_ptr(fdt, offset++, 1); } while (p && (*p != '\0')); if (!p) return FDT_END; /* premature end */ break; case FDT_PROP: lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); if (!lenp) return FDT_END; /* premature end */ /* skip-name offset, length and value */ offset += sizeof(struct fdt_property) - FDT_TAGSIZE + fdt32_to_cpu(*lenp); break; case FDT_END: case FDT_END_NODE: case FDT_NOP: break; default: return FDT_END; } if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) return FDT_END; /* premature end */ *nextoffset = FDT_TAGALIGN(offset); return tag; } int _fdt_check_node_offset(const void *fdt, int offset) { if ((offset < 0) || (offset % FDT_TAGSIZE) || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) return -FDT_ERR_BADOFFSET; return offset; } int fdt_next_node(const void *fdt, int offset, int *depth) { int nextoffset = 0; uint32_t tag; if (offset >= 0) if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) return nextoffset; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_PROP: case FDT_NOP: break; case FDT_BEGIN_NODE: if (depth) (*depth)++; break; case FDT_END_NODE: if (depth && ((--(*depth)) < 0)) return nextoffset; break; case FDT_END: if ((nextoffset >= 0) || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) return -FDT_ERR_NOTFOUND; else return nextoffset; } } while (tag != FDT_BEGIN_NODE); return offset; } const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) { int len = strlen(s) + 1; const char *last = strtab + tabsize - len; const char *p; for (p = strtab; p <= last; p++) if (memcmp(p, s, len) == 0) return p; return NULL; } int fdt_move(const void *fdt, void *buf, int bufsize) { FDT_CHECK_HEADER(fdt); if (fdt_totalsize(fdt) > bufsize) return -FDT_ERR_NOSPACE; memmove(buf, fdt, fdt_totalsize(fdt)); return 0; } skiboot-skiboot-5.1.13/libfdt/fdt.h000066400000000000000000000030251265204436200171210ustar00rootroot00000000000000#ifndef _FDT_H #define _FDT_H #ifndef __ASSEMBLY__ struct fdt_header { uint32_t magic; /* magic word FDT_MAGIC */ uint32_t totalsize; /* total size of DT block */ uint32_t off_dt_struct; /* offset to structure */ uint32_t off_dt_strings; /* offset to strings */ uint32_t off_mem_rsvmap; /* offset to memory reserve map */ uint32_t version; /* format version */ uint32_t last_comp_version; /* last compatible version */ /* version 2 fields below */ uint32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */ /* version 3 fields below */ uint32_t size_dt_strings; /* size of the strings block */ /* version 17 fields below */ uint32_t size_dt_struct; /* size of the structure block */ }; struct fdt_reserve_entry { uint64_t address; uint64_t size; }; struct fdt_node_header { uint32_t tag; char name[0]; }; struct fdt_property { uint32_t tag; uint32_t len; uint32_t nameoff; char data[0]; }; #endif /* !__ASSEMBLY */ #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(uint32_t) #define FDT_BEGIN_NODE 0x1 /* Start node: full name */ #define FDT_END_NODE 0x2 /* End node */ #define FDT_PROP 0x3 /* Property: name off, size, content */ #define FDT_NOP 0x4 /* nop */ #define FDT_END 0x9 #define FDT_V1_SIZE (7*sizeof(uint32_t)) #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t)) #define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t)) #define FDT_V16_SIZE FDT_V3_SIZE #define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t)) #endif /* _FDT_H */ skiboot-skiboot-5.1.13/libfdt/fdt_ro.c000066400000000000000000000313371265204436200176230ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libfdt_env.h" #include #include #include "libfdt_internal.h" static int _fdt_nodename_eq(const void *fdt, int offset, const char *s, int len) { const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); if (! p) /* short match */ return 0; if (memcmp(p, s, len) != 0) return 0; if (p[len] == '\0') return 1; else if (!memchr(s, '@', len) && (p[len] == '@')) return 1; else return 0; } const char *fdt_string(const void *fdt, int stroffset) { return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; } static int _fdt_string_eq(const void *fdt, int stroffset, const char *s, int len) { const char *p = fdt_string(fdt, stroffset); return (strlen(p) == len) && (memcmp(p, s, len) == 0); } int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) { FDT_CHECK_HEADER(fdt); *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); return 0; } int fdt_num_mem_rsv(const void *fdt) { int i = 0; while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) i++; return i; } int fdt_subnode_offset_namelen(const void *fdt, int offset, const char *name, int namelen) { int depth; FDT_CHECK_HEADER(fdt); for (depth = 0; (offset >= 0) && (depth >= 0); offset = fdt_next_node(fdt, offset, &depth)) if ((depth == 1) && _fdt_nodename_eq(fdt, offset, name, namelen)) return offset; if (depth < 0) return -FDT_ERR_NOTFOUND; return offset; /* error */ } int fdt_sibling_offset_namelen(const void *fdt, int offset, const char *name, int namelen) { int depth; FDT_CHECK_HEADER(fdt); for (depth = 1, offset = fdt_next_node(fdt, offset, &depth); (offset >= 0) && (depth >= 1); offset = fdt_next_node(fdt, offset, &depth)) { if ((depth == 1) && _fdt_nodename_eq(fdt, offset, name, namelen)) return offset; } if (depth < 0) return -FDT_ERR_NOTFOUND; return offset; /* error */ } int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name) { return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_sibling_offset(const void *fdt, int offset, const char *name) { return fdt_sibling_offset_namelen(fdt, offset, name, strlen(name)); } int fdt_path_offset(const void *fdt, const char *path) { const char *end = path + strlen(path); const char *p = path; int offset = 0; FDT_CHECK_HEADER(fdt); /* see if we have an alias */ if (*path != '/') { const char *q = strchr(path, '/'); if (!q) q = end; p = fdt_get_alias_namelen(fdt, p, q - p); if (!p) return -FDT_ERR_BADPATH; offset = fdt_path_offset(fdt, p); p = q; } while (*p) { const char *q; while (*p == '/') p++; if (! *p) return offset; q = strchr(p, '/'); if (! q) q = end; offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); if (offset < 0) return offset; p = q; } return offset; } const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) { const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); int err; if (((err = fdt_check_header(fdt)) != 0) || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) goto fail; if (len) *len = strlen(nh->name); return nh->name; fail: if (len) *len = err; return NULL; } const struct fdt_property *fdt_get_property_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { uint32_t tag; const struct fdt_property *prop; int offset, nextoffset; int err; if (((err = fdt_check_header(fdt)) != 0) || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) goto fail; nextoffset = err; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_END: if (nextoffset < 0) err = nextoffset; else /* FDT_END tag with unclosed nodes */ err = -FDT_ERR_BADSTRUCTURE; goto fail; case FDT_PROP: prop = _fdt_offset_ptr(fdt, offset); if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), name, namelen)) { /* Found it! */ if (lenp) *lenp = fdt32_to_cpu(prop->len); return prop; } break; } } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE)); err = -FDT_ERR_NOTFOUND; fail: if (lenp) *lenp = err; return NULL; } const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_get_property_namelen(fdt, nodeoffset, name, strlen(name), lenp); } const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { const struct fdt_property *prop; prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); if (! prop) return NULL; return prop->data; } const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); } uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) { const uint32_t *php; int len; php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); if (!php || (len != sizeof(*php))) return 0; return fdt32_to_cpu(*php); } const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen) { int aliasoffset; aliasoffset = fdt_path_offset(fdt, "/aliases"); if (aliasoffset < 0) return NULL; return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); } const char *fdt_get_alias(const void *fdt, const char *name) { return fdt_get_alias_namelen(fdt, name, strlen(name)); } int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) { int pdepth = 0, p = 0; int offset, depth, namelen; const char *name; FDT_CHECK_HEADER(fdt); if (buflen < 2) return -FDT_ERR_NOSPACE; for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { while (pdepth > depth) { do { p--; } while (buf[p-1] != '/'); pdepth--; } if (pdepth >= depth) { name = fdt_get_name(fdt, offset, &namelen); if (!name) return namelen; if ((p + namelen + 1) <= buflen) { memcpy(buf + p, name, namelen); p += namelen; buf[p++] = '/'; pdepth++; } } if (offset == nodeoffset) { if (pdepth < (depth + 1)) return -FDT_ERR_NOSPACE; if (p > 1) /* special case so that root path is "/", not "" */ p--; buf[p] = '\0'; return 0; } } if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; return offset; /* error from fdt_next_node() */ } int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) { int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL; FDT_CHECK_HEADER(fdt); if (supernodedepth < 0) return -FDT_ERR_NOTFOUND; for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { if (depth == supernodedepth) supernodeoffset = offset; if (offset == nodeoffset) { if (nodedepth) *nodedepth = depth; if (supernodedepth > depth) return -FDT_ERR_NOTFOUND; else return supernodeoffset; } } if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; return offset; /* error from fdt_next_node() */ } int fdt_node_depth(const void *fdt, int nodeoffset) { int nodedepth; int err; err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); if (err) return (err < 0) ? err : -FDT_ERR_INTERNAL; return nodedepth; } int fdt_parent_offset(const void *fdt, int nodeoffset) { int nodedepth = fdt_node_depth(fdt, nodeoffset); if (nodedepth < 0) return nodedepth; return fdt_supernode_atdepth_offset(fdt, nodeoffset, nodedepth - 1, NULL); } int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen) { int offset; const void *val; int len; FDT_CHECK_HEADER(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_getprop(), then if that didn't * find what we want, we scan over them again making our way * to the next node. Still it's the easiest to implement * approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { val = fdt_getprop(fdt, offset, propname, &len); if (val && (len == proplen) && (memcmp(val, propval, len) == 0)) return offset; } return offset; /* error from fdt_next_node() */ } int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) { if ((phandle == 0) || (phandle == -1)) return -FDT_ERR_BADPHANDLE; phandle = cpu_to_fdt32(phandle); return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle", &phandle, sizeof(phandle)); } static int _fdt_stringlist_contains(const char *strlist, int listlen, const char *str) { int len = strlen(str); const char *p; while (listlen >= len) { if (memcmp(str, strlist, len+1) == 0) return 1; p = memchr(strlist, '\0', listlen); if (!p) return 0; /* malformed strlist.. */ listlen -= (p-strlist) + 1; strlist = p + 1; } return 0; } int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) { const void *prop; int len; prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); if (!prop) return len; if (_fdt_stringlist_contains(prop, len, compatible)) return 0; else return 1; } int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible) { int offset, err; FDT_CHECK_HEADER(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_node_check_compatible(), then if * that didn't find what we want, we scan over them again * making our way to the next node. Still it's the easiest to * implement approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { err = fdt_node_check_compatible(fdt, offset, compatible); if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) return err; else if (err == 0) return offset; } return offset; /* error from fdt_next_node() */ } skiboot-skiboot-5.1.13/libfdt/fdt_rw.c000066400000000000000000000300641265204436200176270ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libfdt_env.h" #include #include #include "libfdt_internal.h" static int _fdt_blocks_misordered(const void *fdt, int mem_rsv_size, int struct_size) { return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) || (fdt_off_dt_struct(fdt) < (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) || (fdt_off_dt_strings(fdt) < (fdt_off_dt_struct(fdt) + struct_size)) || (fdt_totalsize(fdt) < (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } static int _fdt_rw_check_header(void *fdt) { FDT_CHECK_HEADER(fdt); if (fdt_version(fdt) < 17) return -FDT_ERR_BADVERSION; if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry), fdt_size_dt_struct(fdt))) return -FDT_ERR_BADLAYOUT; if (fdt_version(fdt) > 17) fdt_set_version(fdt, 17); return 0; } #define FDT_RW_CHECK_HEADER(fdt) \ { \ int err; \ if ((err = _fdt_rw_check_header(fdt)) != 0) \ return err; \ } static inline int _fdt_data_size(void *fdt) { return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); } static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen) { char *p = splicepoint; char *end = (char *)fdt + _fdt_data_size(fdt); if (((p + oldlen) < p) || ((p + oldlen) > end)) return -FDT_ERR_BADOFFSET; if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) return -FDT_ERR_NOSPACE; memmove(p + newlen, p + oldlen, end - p - oldlen); return 0; } static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p, int oldn, int newn) { int delta = (newn - oldn) * sizeof(*p); int err; err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); if (err) return err; fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); return 0; } static int _fdt_splice_struct(void *fdt, void *p, int oldlen, int newlen) { int delta = newlen - oldlen; int err; if ((err = _fdt_splice(fdt, p, oldlen, newlen))) return err; fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); return 0; } static int _fdt_splice_string(void *fdt, int newlen) { void *p = (char *)fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); int err; if ((err = _fdt_splice(fdt, p, 0, newlen))) return err; fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); return 0; } static int _fdt_find_add_string(void *fdt, const char *s) { char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); const char *p; char *new; int len = strlen(s) + 1; int err; p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); if (p) /* found it */ return (p - strtab); new = strtab + fdt_size_dt_strings(fdt); err = _fdt_splice_string(fdt, len); if (err) return err; memcpy(new, s, len); return (new - strtab); } int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) { struct fdt_reserve_entry *re; int err; FDT_RW_CHECK_HEADER(fdt); re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt)); err = _fdt_splice_mem_rsv(fdt, re, 0, 1); if (err) return err; re->address = cpu_to_fdt64(address); re->size = cpu_to_fdt64(size); return 0; } int fdt_del_mem_rsv(void *fdt, int n) { struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n); int err; FDT_RW_CHECK_HEADER(fdt); if (n >= fdt_num_mem_rsv(fdt)) return -FDT_ERR_NOTFOUND; err = _fdt_splice_mem_rsv(fdt, re, 1, 0); if (err) return err; return 0; } static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) { int oldlen; int err; *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); if (! (*prop)) return oldlen; if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), FDT_TAGALIGN(len)))) return err; (*prop)->len = cpu_to_fdt32(len); return 0; } static int _fdt_add_property(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) { int proplen; int nextoffset; int namestroff; int err; if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) return nextoffset; namestroff = _fdt_find_add_string(fdt, name); if (namestroff < 0) return namestroff; *prop = _fdt_offset_ptr_w(fdt, nextoffset); proplen = sizeof(**prop) + FDT_TAGALIGN(len); err = _fdt_splice_struct(fdt, *prop, 0, proplen); if (err) return err; (*prop)->tag = cpu_to_fdt32(FDT_PROP); (*prop)->nameoff = cpu_to_fdt32(namestroff); (*prop)->len = cpu_to_fdt32(len); return 0; } int fdt_set_name(void *fdt, int nodeoffset, const char *name) { char *namep; int oldlen, newlen; int err; FDT_RW_CHECK_HEADER(fdt); namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); if (!namep) return oldlen; newlen = strlen(name); err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1), FDT_TAGALIGN(newlen+1)); if (err) return err; memcpy(namep, name, newlen+1); return 0; } int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { struct fdt_property *prop; int err; FDT_RW_CHECK_HEADER(fdt); err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop); if (err == -FDT_ERR_NOTFOUND) err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); if (err) return err; memcpy(prop->data, val, len); return 0; } int fdt_delprop(void *fdt, int nodeoffset, const char *name) { struct fdt_property *prop; int len, proplen; FDT_RW_CHECK_HEADER(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &len); if (! prop) return len; proplen = sizeof(*prop) + FDT_TAGALIGN(len); return _fdt_splice_struct(fdt, prop, proplen, 0); } int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen) { struct fdt_node_header *nh; int offset, nextoffset; int nodelen; int err; uint32_t tag; uint32_t *endtag; FDT_RW_CHECK_HEADER(fdt); offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); if (offset >= 0) return -FDT_ERR_EXISTS; else if (offset != -FDT_ERR_NOTFOUND) return offset; /* Try to place the new node after the parent's properties */ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); } while ((tag == FDT_PROP) || (tag == FDT_NOP)); nh = _fdt_offset_ptr_w(fdt, offset); nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; err = _fdt_splice_struct(fdt, nh, 0, nodelen); if (err) return err; nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); memcpy(nh->name, name, namelen); endtag = (uint32_t *)((char *)nh + nodelen - FDT_TAGSIZE); *endtag = cpu_to_fdt32(FDT_END_NODE); return offset; } int fdt_add_subnode(void *fdt, int parentoffset, const char *name) { return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_del_node(void *fdt, int nodeoffset) { int endoffset; FDT_RW_CHECK_HEADER(fdt); endoffset = _fdt_node_end_offset(fdt, nodeoffset); if (endoffset < 0) return endoffset; return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset), endoffset - nodeoffset, 0); } static void _fdt_packblocks(const char *old, char *new, int mem_rsv_size, int struct_size) { int mem_rsv_off, struct_off, strings_off; mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); struct_off = mem_rsv_off + mem_rsv_size; strings_off = struct_off + struct_size; memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); fdt_set_off_mem_rsvmap(new, mem_rsv_off); memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); fdt_set_off_dt_struct(new, struct_off); fdt_set_size_dt_struct(new, struct_size); memmove(new + strings_off, old + fdt_off_dt_strings(old), fdt_size_dt_strings(old)); fdt_set_off_dt_strings(new, strings_off); fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); } int fdt_open_into(const void *fdt, void *buf, int bufsize) { int err; int mem_rsv_size, struct_size; int newsize; const char *fdtstart = fdt; const char *fdtend = fdtstart + fdt_totalsize(fdt); char *tmp; FDT_CHECK_HEADER(fdt); mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); if (fdt_version(fdt) >= 17) { struct_size = fdt_size_dt_struct(fdt); } else { struct_size = 0; while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) ; if (struct_size < 0) return struct_size; } if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) { /* no further work necessary */ err = fdt_move(fdt, buf, bufsize); if (err) return err; fdt_set_version(buf, 17); fdt_set_size_dt_struct(buf, struct_size); fdt_set_totalsize(buf, bufsize); return 0; } /* Need to reorder */ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + struct_size + fdt_size_dt_strings(fdt); if (bufsize < newsize) return -FDT_ERR_NOSPACE; /* First attempt to build converted tree at beginning of buffer */ tmp = buf; /* But if that overlaps with the old tree... */ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { /* Try right after the old tree instead */ tmp = (char *)(uintptr_t)fdtend; if ((tmp + newsize) > ((char *)buf + bufsize)) return -FDT_ERR_NOSPACE; } _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size); memmove(buf, tmp, newsize); fdt_set_magic(buf, FDT_MAGIC); fdt_set_totalsize(buf, bufsize); fdt_set_version(buf, 17); fdt_set_last_comp_version(buf, 16); fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); return 0; } int fdt_pack(void *fdt) { int mem_rsv_size; FDT_RW_CHECK_HEADER(fdt); mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); fdt_set_totalsize(fdt, _fdt_data_size(fdt)); return 0; } skiboot-skiboot-5.1.13/libfdt/fdt_strerror.c000066400000000000000000000065541265204436200210700ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "libfdt_env.h" #include #include #include "libfdt_internal.h" struct fdt_errtabent { const char *str; }; #define FDT_ERRTABENT(val) \ [(val)] = { .str = #val, } static struct fdt_errtabent fdt_errtable[] = { FDT_ERRTABENT(FDT_ERR_NOTFOUND), FDT_ERRTABENT(FDT_ERR_EXISTS), FDT_ERRTABENT(FDT_ERR_NOSPACE), FDT_ERRTABENT(FDT_ERR_BADOFFSET), FDT_ERRTABENT(FDT_ERR_BADPATH), FDT_ERRTABENT(FDT_ERR_BADSTATE), FDT_ERRTABENT(FDT_ERR_TRUNCATED), FDT_ERRTABENT(FDT_ERR_BADMAGIC), FDT_ERRTABENT(FDT_ERR_BADVERSION), FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), FDT_ERRTABENT(FDT_ERR_BADLAYOUT), }; #define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) const char * __attrconst fdt_strerror(int errval) { if (errval > 0) return ""; else if (errval == 0) return ""; else if (errval > -FDT_ERRTABSIZE) { const char *s = fdt_errtable[-errval].str; if (s) return s; } return ""; } skiboot-skiboot-5.1.13/libfdt/fdt_sw.c000066400000000000000000000171201265204436200176260ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libfdt_env.h" #include #include #include "libfdt_internal.h" #include #include static int _fdt_sw_check_header(void *fdt) { if (fdt_magic(fdt) != FDT_SW_MAGIC) return -FDT_ERR_BADMAGIC; /* FIXME: should check more details about the header state */ return 0; } #define FDT_SW_CHECK_HEADER(fdt) \ { \ int err; \ if ((err = _fdt_sw_check_header(fdt)) != 0) \ return err; \ } static void *_fdt_grab_space(void *fdt, size_t len) { int offset = fdt_size_dt_struct(fdt); int spaceleft; spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) - fdt_size_dt_strings(fdt); if ((offset + len < offset) || (offset + len > spaceleft)) return NULL; fdt_set_size_dt_struct(fdt, offset + len); return _fdt_offset_ptr_w(fdt, offset); } int fdt_create(void *buf, int bufsize) { void *fdt = buf; if (bufsize < sizeof(struct fdt_header)) return -FDT_ERR_NOSPACE; memset(buf, 0, bufsize); fdt_set_magic(fdt, FDT_SW_MAGIC); fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); fdt_set_totalsize(fdt, bufsize); fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), sizeof(struct fdt_reserve_entry))); fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); fdt_set_off_dt_strings(fdt, bufsize); return 0; } int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) { struct fdt_reserve_entry *re; int offset; FDT_SW_CHECK_HEADER(fdt); if (fdt_size_dt_struct(fdt)) return -FDT_ERR_BADSTATE; offset = fdt_off_dt_struct(fdt); if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) return -FDT_ERR_NOSPACE; re = (struct fdt_reserve_entry *)((char *)fdt + offset); re->address = cpu_to_fdt64(addr); re->size = cpu_to_fdt64(size); fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); return 0; } int fdt_finish_reservemap(void *fdt) { return fdt_add_reservemap_entry(fdt, 0, 0); } int fdt_begin_node(void *fdt, const char *name) { struct fdt_node_header *nh; int namelen = strlen(name) + 1; FDT_SW_CHECK_HEADER(fdt); nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); if (! nh) return -FDT_ERR_NOSPACE; nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); memcpy(nh->name, name, namelen); return 0; } int fdt_end_node(void *fdt) { uint32_t *en; FDT_SW_CHECK_HEADER(fdt); en = _fdt_grab_space(fdt, FDT_TAGSIZE); if (! en) return -FDT_ERR_NOSPACE; *en = cpu_to_fdt32(FDT_END_NODE); return 0; } static int _fdt_find_add_string(void *fdt, const char *s) { char *strtab = (char *)fdt + fdt_totalsize(fdt); const char *p; int strtabsize = fdt_size_dt_strings(fdt); int len = strlen(s) + 1; int struct_top, offset; p = _fdt_find_string(strtab - strtabsize, strtabsize, s); if (p) return p - strtab; /* Add it */ offset = -strtabsize - len; struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); if (fdt_totalsize(fdt) + offset < struct_top) return 0; /* no more room :( */ memcpy(strtab + offset, s, len); fdt_set_size_dt_strings(fdt, strtabsize + len); return offset; } int fdt_property(void *fdt, const char *name, const void *val, int len) { struct fdt_property *prop; int nameoff; FDT_SW_CHECK_HEADER(fdt); nameoff = _fdt_find_add_string(fdt, name); if (nameoff == 0) return -FDT_ERR_NOSPACE; prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); if (! prop) return -FDT_ERR_NOSPACE; prop->tag = cpu_to_fdt32(FDT_PROP); prop->nameoff = cpu_to_fdt32(nameoff); prop->len = cpu_to_fdt32(len); memcpy(prop->data, val, len); return 0; } int fdt_property_cells_v(void *fdt, unsigned const char *name, int count, va_list args) { uint32_t *buffer; int i; buffer = (uint32_t*)malloc(sizeof(uint32_t)*count); assert(buffer); for (i = 0; i < count; i++) buffer[i] = cpu_to_fdt32(va_arg(args, uint32_t)); return fdt_property(fdt, name, buffer, sizeof(uint32_t)*count); } int fdt_property_cells(void *fdt, unsigned const char *name, int count, ...) { va_list args; int ret; va_start(args, count); ret = fdt_property_cells_v(fdt, name, count, args); va_end(args); return ret; } int fdt_finish(void *fdt) { char *p = (char *)fdt; uint32_t *end; int oldstroffset, newstroffset; uint32_t tag; int offset, nextoffset; FDT_SW_CHECK_HEADER(fdt); /* Add terminator */ end = _fdt_grab_space(fdt, sizeof(*end)); if (! end) return -FDT_ERR_NOSPACE; *end = cpu_to_fdt32(FDT_END); /* Relocate the string table */ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); fdt_set_off_dt_strings(fdt, newstroffset); /* Walk the structure, correcting string offsets */ offset = 0; while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { if (tag == FDT_PROP) { struct fdt_property *prop = _fdt_offset_ptr_w(fdt, offset); int nameoff; nameoff = fdt32_to_cpu(prop->nameoff); nameoff += fdt_size_dt_strings(fdt); prop->nameoff = cpu_to_fdt32(nameoff); } offset = nextoffset; } if (nextoffset < 0) return nextoffset; /* Finally, adjust the header */ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); fdt_set_magic(fdt, FDT_MAGIC); return 0; } skiboot-skiboot-5.1.13/libfdt/fdt_wip.c000066400000000000000000000071251265204436200200000ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libfdt_env.h" #include #include #include "libfdt_internal.h" int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len) { void *propval; int proplen; propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen); if (! propval) return proplen; if (proplen != len) return -FDT_ERR_NOSPACE; memcpy(propval, val, len); return 0; } static void _fdt_nop_region(void *start, int len) { uint32_t *p; for (p = start; (char *)p < ((char *)start + len); p++) *p = cpu_to_fdt32(FDT_NOP); } int fdt_nop_property(void *fdt, int nodeoffset, const char *name) { struct fdt_property *prop; int len; prop = fdt_get_property_w(fdt, nodeoffset, name, &len); if (! prop) return len; _fdt_nop_region(prop, len + sizeof(*prop)); return 0; } int _fdt_node_end_offset(void *fdt, int offset) { int depth = 0; while ((offset >= 0) && (depth >= 0)) offset = fdt_next_node(fdt, offset, &depth); return offset; } int fdt_nop_node(void *fdt, int nodeoffset) { int endoffset; endoffset = _fdt_node_end_offset(fdt, nodeoffset); if (endoffset < 0) return endoffset; _fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset); return 0; } skiboot-skiboot-5.1.13/libfdt/libfdt.h000066400000000000000000001257261265204436200176250ustar00rootroot00000000000000#ifndef _LIBFDT_H #define _LIBFDT_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #define FDT_FIRST_SUPPORTED_VERSION 0x10 #define FDT_LAST_SUPPORTED_VERSION 0x11 /* Error codes: informative error codes */ #define FDT_ERR_NOTFOUND 1 /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ #define FDT_ERR_EXISTS 2 /* FDT_ERR_EXISTS: Attempted to create a node or property which * already exists */ #define FDT_ERR_NOSPACE 3 /* FDT_ERR_NOSPACE: Operation needed to expand the device * tree, but its buffer did not have sufficient space to * contain the expanded tree. Use fdt_open_into() to move the * device tree to a buffer with more space. */ /* Error codes: codes for bad parameters */ #define FDT_ERR_BADOFFSET 4 /* FDT_ERR_BADOFFSET: Function was passed a structure block * offset which is out-of-bounds, or which points to an * unsuitable part of the structure for the operation. */ #define FDT_ERR_BADPATH 5 /* FDT_ERR_BADPATH: Function was passed a badly formatted path * (e.g. missing a leading / for a function which requires an * absolute path) */ #define FDT_ERR_BADPHANDLE 6 /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle * value. phandle values of 0 and -1 are not permitted. */ #define FDT_ERR_BADSTATE 7 /* FDT_ERR_BADSTATE: Function was passed an incomplete device * tree created by the sequential-write functions, which is * not sufficiently complete for the requested operation. */ /* Error codes: codes for bad device tree blobs */ #define FDT_ERR_TRUNCATED 8 /* FDT_ERR_TRUNCATED: Structure block of the given device tree * ends without an FDT_END tag. */ #define FDT_ERR_BADMAGIC 9 /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a * device tree at all - it is missing the flattened device * tree magic number. */ #define FDT_ERR_BADVERSION 10 /* FDT_ERR_BADVERSION: Given device tree has a version which * can't be handled by the requested operation. For * read-write functions, this may mean that fdt_open_into() is * required to convert the tree to the expected version. */ #define FDT_ERR_BADSTRUCTURE 11 /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt * structure block or other serious error (e.g. misnested * nodes, or subnodes preceding properties). */ #define FDT_ERR_BADLAYOUT 12 /* FDT_ERR_BADLAYOUT: For read-write functions, the given * device tree has it's sub-blocks in an order that the * function can't handle (memory reserve map, then structure, * then strings). Use fdt_open_into() to reorganize the tree * into a form suitable for the read-write operations. */ /* "Can't happen" error indicating a bug in libfdt */ #define FDT_ERR_INTERNAL 13 /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. * Should never be returned, if it is, it indicates a bug in * libfdt itself. */ #define FDT_ERR_MAX 13 /**********************************************************************/ /* Low-level functions (you probably don't need these) */ /**********************************************************************/ const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) { return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); } uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); /**********************************************************************/ /* Traversal functions */ /**********************************************************************/ int fdt_next_node(const void *fdt, int offset, int *depth); /**********************************************************************/ /* General functions */ /**********************************************************************/ #define fdt_get_header(fdt, field) \ (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) #define fdt_magic(fdt) (fdt_get_header(fdt, magic)) #define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) #define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) #define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) #define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) #define fdt_version(fdt) (fdt_get_header(fdt, version)) #define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) #define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) #define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) #define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) #define __fdt_set_hdr(name) \ static inline void fdt_set_##name(void *fdt, uint32_t val) \ { \ struct fdt_header *fdth = (struct fdt_header*)fdt; \ fdth->name = cpu_to_fdt32(val); \ } __fdt_set_hdr(magic); __fdt_set_hdr(totalsize); __fdt_set_hdr(off_dt_struct); __fdt_set_hdr(off_dt_strings); __fdt_set_hdr(off_mem_rsvmap); __fdt_set_hdr(version); __fdt_set_hdr(last_comp_version); __fdt_set_hdr(boot_cpuid_phys); __fdt_set_hdr(size_dt_strings); __fdt_set_hdr(size_dt_struct); #undef __fdt_set_hdr /** * fdt_check_header - sanity check a device tree or possible device tree * @fdt: pointer to data which might be a flattened device tree * * fdt_check_header() checks that the given buffer contains what * appears to be a flattened device tree with sane information in its * header. * * returns: * 0, if the buffer appears to contain a valid device tree * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings, as above */ int fdt_check_header(const void *fdt); /** * fdt_move - move a device tree around in memory * @fdt: pointer to the device tree to move * @buf: pointer to memory where the device is to be moved * @bufsize: size of the memory space at buf * * fdt_move() relocates, if possible, the device tree blob located at * fdt to the buffer at buf of size bufsize. The buffer may overlap * with the existing device tree blob at fdt. Therefore, * fdt_move(fdt, fdt, fdt_totalsize(fdt)) * should always succeed. * * returns: * 0, on success * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_move(const void *fdt, void *buf, int bufsize); /**********************************************************************/ /* Read-only functions */ /**********************************************************************/ /** * fdt_string - retrieve a string from the strings block of a device tree * @fdt: pointer to the device tree blob * @stroffset: offset of the string within the strings block (native endian) * * fdt_string() retrieves a pointer to a single string from the * strings block of the device tree blob at fdt. * * returns: * a pointer to the string, on success * NULL, if stroffset is out of bounds */ const char *fdt_string(const void *fdt, int stroffset); /** * fdt_num_mem_rsv - retrieve the number of memory reserve map entries * @fdt: pointer to the device tree blob * * Returns the number of entries in the device tree blob's memory * reservation map. This does not include the terminating 0,0 entry * or any other (0,0) entries reserved for expansion. * * returns: * the number of entries */ int fdt_num_mem_rsv(const void *fdt); /** * fdt_get_mem_rsv - retrieve one memory reserve map entry * @fdt: pointer to the device tree blob * @address, @size: pointers to 64-bit variables * * On success, *address and *size will contain the address and size of * the n-th reserve map entry from the device tree blob, in * native-endian format. * * returns: * 0, on success * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); /** * fdt_subnode_offset_namelen - find a subnode based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Identical to fdt_subnode_offset(), but only examine the first * namelen characters of name for matching the subnode name. This is * useful for finding subnodes based on a portion of a larger string, * such as a full path. */ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, const char *name, int namelen); /** * fdt_sibling_offset_namelen - find sibling node based on substring * @fdt: pointer to the device tree blob * @fromoffset: node to start from * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Typically used to continue the search started with * fdt_subnode_offset_namelen() using the same matching rules. */ int fdt_sibling_offset_namelen(const void *fdt, int fromoffset, const char *name, int namelen); /** * fdt_subnode_offset - find a subnode of a given node * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * * fdt_subnode_offset() finds a subnode of the node at structure block * offset parentoffset with the given name. name may include a unit * address, in which case fdt_subnode_offset() will find the subnode * with that unit address, or the unit address may be omitted, in * which case fdt_subnode_offset() will find an arbitrary subnode * whose name excluding unit address matches the given name. * * returns: * structure block offset of the requested subnode (>=0), on success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); /** * fdt_sibling_offset - find a sibling of a given node by name * @fdt: pointer to the device tree blob * @fromoffset: structure block offset of a node * @name: name of the subnode to locate * * Typically used to continue the search started with fdt_subnode_offset() * using the same matching rules. * * returns: * structure block offset of the requested subnode (>=0), on success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_sibling_offset(const void *fdt, int fromoffset, const char *name); /** * fdt_path_offset - find a tree node by its full path * @fdt: pointer to the device tree blob * @path: full path of the node to locate * * fdt_path_offset() finds a node of a given path in the device tree. * Each path component may omit the unit address portion, but the * results of this are undefined if any such path component is * ambiguous (that is if there are multiple nodes at the relevant * level matching the given component, differentiated only by unit * address). * * returns: * structure block offset of the node with the requested path (>=0), on success * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid * -FDT_ERR_NOTFOUND, if the requested node does not exist * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_path_offset(const void *fdt, const char *path); /** * fdt_get_name - retrieve the name of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of the starting node * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_name() retrieves the name (including unit address) of the * device tree node at structure block offset nodeoffset. If lenp is * non-NULL, the length of this name is also returned, in the integer * pointed to by lenp. * * returns: * pointer to the node's name, on success * If lenp is non-NULL, *lenp contains the length of that name (>=0) * NULL, on error * if lenp is non-NULL *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); /** * fdt_get_property_namelen - find a property based on substring * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @namelen: number of characters of name to consider * @lenp: pointer to an integer variable (will be overwritten) or NULL * * Identical to fdt_get_property_namelen(), but only examine the first * namelen characters of name for matching the property name. */ const struct fdt_property *fdt_get_property_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); /** * fdt_get_property - find a given property in a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_property() retrieves a pointer to the fdt_property * structure within the device tree blob corresponding to the property * named 'name' of the node at offset nodeoffset. If lenp is * non-NULL, the length of the property value is also returned, in the * integer pointed to by lenp. * * returns: * pointer to the structure representing the property * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp); static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (struct fdt_property *)(uintptr_t) fdt_get_property(fdt, nodeoffset, name, lenp); } /** * fdt_getprop_namelen - get property value based on substring * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @namelen: number of characters of name to consider * @lenp: pointer to an integer variable (will be overwritten) or NULL * * Identical to fdt_getprop(), but only examine the first namelen * characters of name for matching the property name. */ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); /** * fdt_getprop - retrieve the value of a given property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop() retrieves a pointer to the value of the property * named 'name' of the node at offset nodeoffset (this will be a * pointer to within the device blob itself, not a copy of the value). * If lenp is non-NULL, the length of the property value is also * returned, in the integer pointed to by lenp. * * returns: * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp); static inline void *fdt_getprop_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); } /** * fdt_get_phandle - retrieve the phandle of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of the node * * fdt_get_phandle() retrieves the phandle of the device tree node at * structure block offset nodeoffset. * * returns: * the phandle of the node at nodeoffset, on success (!= 0, != -1) * 0, if the node has no phandle, or another error occurs */ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); /** * fdt_get_alias_namelen - get alias based on substring * @fdt: pointer to the device tree blob * @name: name of the alias th look up * @namelen: number of characters of name to consider * * Identical to fdt_get_alias(), but only examine the first namelen * characters of name for matching the alias name. */ const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen); /** * fdt_get_alias - retrieve the path referenced by a given alias * @fdt: pointer to the device tree blob * @name: name of the alias th look up * * fdt_get_alias() retrieves the value of a given alias. That is, the * value of the property named 'name' in the node /aliases. * * returns: * a pointer to the expansion of the alias named 'name', of it exists * NULL, if the given alias or the /aliases node does not exist */ const char *fdt_get_alias(const void *fdt, const char *name); /** * fdt_get_path - determine the full path of a node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose path to find * @buf: character buffer to contain the returned path (will be overwritten) * @buflen: size of the character buffer at buf * * fdt_get_path() computes the full path of the node at offset * nodeoffset, and records that path in the buffer at buf. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * 0, on success * buf contains the absolute path of the node at * nodeoffset, as a NUL-terminated string. * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) * characters and will not fit in the given buffer. * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); /** * fdt_supernode_atdepth_offset - find a specific ancestor of a node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * @supernodedepth: depth of the ancestor to find * @nodedepth: pointer to an integer variable (will be overwritten) or NULL * * fdt_supernode_atdepth_offset() finds an ancestor of the given node * at a specific depth from the root (where the root itself has depth * 0, its immediate subnodes depth 1 and so forth). So * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); * will always return 0, the offset of the root node. If the node at * nodeoffset has depth D, then: * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); * will return nodeoffset itself. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * structure block offset of the node at node offset's ancestor * of depth supernodedepth (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth); /** * fdt_node_depth - find the depth of a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * * fdt_node_depth() finds the depth of a given node. The root node * has depth 0, its immediate subnodes depth 1 and so forth. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * depth of the node at nodeoffset (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_depth(const void *fdt, int nodeoffset); /** * fdt_parent_offset - find the parent of a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * * fdt_parent_offset() locates the parent node of a given node (that * is, it finds the offset of the node which contains the node at * nodeoffset as a subnode). * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset, *twice*. * * returns: * structure block offset of the parent of the node at nodeoffset * (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_parent_offset(const void *fdt, int nodeoffset); /** * fdt_node_offset_by_prop_value - find nodes with a given property value * @fdt: pointer to the device tree blob * @startoffset: only find nodes after this offset * @propname: property name to check * @propval: property value to search for * @proplen: length of the value in propval * * fdt_node_offset_by_prop_value() returns the offset of the first * node after startoffset, which has a property named propname whose * value is of length proplen and has value equal to propval; or if * startoffset is -1, the very first such node in the tree. * * To iterate through all nodes matching the criterion, the following * idiom can be used: * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, * propval, proplen); * while (offset != -FDT_ERR_NOTFOUND) { * // other code here * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, * propval, proplen); * } * * Note the -1 in the first call to the function, if 0 is used here * instead, the function will never locate the root node, even if it * matches the criterion. * * returns: * structure block offset of the located node (>= 0, >startoffset), * on success * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * tree after startoffset * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen); /** * fdt_node_offset_by_phandle - find the node with a given phandle * @fdt: pointer to the device tree blob * @phandle: phandle value * * fdt_node_offset_by_phandle() returns the offset of the node * which has the given phandle value. If there is more than one node * in the tree with the given phandle (an invalid tree), results are * undefined. * * returns: * structure block offset of the located node (>= 0), on success * -FDT_ERR_NOTFOUND, no node with that phandle exists * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); /** * fdt_node_check_compatible: check a node's compatible property * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @compatible: string to match against * * * fdt_node_check_compatible() returns 0 if the given node contains a * 'compatible' property with the given string as one of its elements, * it returns non-zero otherwise, or on error. * * returns: * 0, if the node has a 'compatible' property listing the given string * 1, if the node has a 'compatible' property, but it does not list * the given string * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible); /** * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value * @fdt: pointer to the device tree blob * @startoffset: only find nodes after this offset * @compatible: 'compatible' string to match against * * fdt_node_offset_by_compatible() returns the offset of the first * node after startoffset, which has a 'compatible' property which * lists the given compatible string; or if startoffset is -1, the * very first such node in the tree. * * To iterate through all nodes matching the criterion, the following * idiom can be used: * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); * while (offset != -FDT_ERR_NOTFOUND) { * // other code here * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); * } * * Note the -1 in the first call to the function, if 0 is used here * instead, the function will never locate the root node, even if it * matches the criterion. * * returns: * structure block offset of the located node (>= 0, >startoffset), * on success * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * tree after startoffset * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible); /**********************************************************************/ /* Write-in-place functions */ /**********************************************************************/ /** * fdt_setprop_inplace - change a property's value, but not its size * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: pointer to data to replace the property value with * @len: length of the property value * * fdt_setprop_inplace() replaces the value of a given property with * the data in val, of length len. This function cannot change the * size of a property, and so will only work if len is equal to the * current length of the property. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if len is not equal to the property's current length * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_setprop_inplace_cell - change the value of a single-cell property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: cell (32-bit integer) value to replace the property with * * fdt_setprop_inplace_cell() replaces the value of a given property * with the 32-bit integer cell value in val, converting val to * big-endian if necessary. This function cannot change the size of a * property, and so will only work if the property already exists and * has length 4. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { val = cpu_to_fdt32(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val)); } /** * fdt_nop_property - replace a property with nop tags * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * * fdt_nop_property() will replace a given property's representation * in the blob with FDT_NOP tags, effectively removing it from the * tree. * * This function will alter only the bytes in the blob which contain * the property, and will not alter or move any other part of the * tree. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_nop_property(void *fdt, int nodeoffset, const char *name); /** * fdt_nop_node - replace a node (subtree) with nop tags * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to nop * * fdt_nop_node() will replace a given node's representation in the * blob, including all its subnodes, if any, with FDT_NOP tags, * effectively removing it from the tree. * * This function will alter only the bytes in the blob which contain * the node and its properties and subnodes, and will not alter or * move any other part of the tree. * * returns: * 0, on success * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_nop_node(void *fdt, int nodeoffset); /**********************************************************************/ /* Sequential write functions */ /**********************************************************************/ int fdt_create(void *buf, int bufsize); int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); int fdt_finish_reservemap(void *fdt); int fdt_begin_node(void *fdt, const char *name); int fdt_property(void *fdt, const char *name, const void *val, int len); static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) { val = cpu_to_fdt32(val); return fdt_property(fdt, name, &val, sizeof(val)); } #define fdt_property_string(fdt, name, str) \ fdt_property(fdt, name, str, strlen(str)+1) int fdt_property_cells_v(void *fdt, unsigned const char *name, int count, va_list args); int fdt_property_cells(void *fdt, unsigned const char *name, int count, ...); int fdt_end_node(void *fdt); int fdt_finish(void *fdt); /**********************************************************************/ /* Read-write functions */ /**********************************************************************/ int fdt_open_into(const void *fdt, void *buf, int bufsize); int fdt_pack(void *fdt); /** * fdt_add_mem_rsv - add one memory reserve map entry * @fdt: pointer to the device tree blob * @address, @size: 64-bit values (native endian) * * Adds a reserve map entry to the given blob reserving a region at * address address of length size. * * This function will insert data into the reserve map and will * therefore change the indexes of some entries in the table. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new reservation entry * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); /** * fdt_del_mem_rsv - remove a memory reserve map entry * @fdt: pointer to the device tree blob * @n: entry to remove * * fdt_del_mem_rsv() removes the n-th memory reserve map entry from * the blob. * * This function will delete data from the reservation table and will * therefore change the indexes of some entries in the table. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there * are less than n+1 reserve map entries) * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_del_mem_rsv(void *fdt, int n); /** * fdt_set_name - change the name of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of a node * @name: name to give the node * * fdt_set_name() replaces the name (including unit address, if any) * of the given node with the given string. NOTE: this function can't * efficiently check if the new name is unique amongst the given * node's siblings; results are undefined if this function is invoked * with a name equal to one of the given node's siblings. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob * to contain the new name * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_set_name(void *fdt, int nodeoffset, const char *name); /** * fdt_setprop - create or change a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: pointer to data to set the property value to * @len: length of the property value * * fdt_setprop() sets the value of the named property in the given * node to the given value and length, creating the property if it * does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_setprop_cell - set a property to a single cell value * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value for the property (native endian) * * fdt_setprop_cell() sets the value of the named property in the * given node to the given cell value (converting to big-endian if * necessary), or creates a new property with that value if it does * not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { val = cpu_to_fdt32(val); return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val)); } /** * fdt_setprop_string - set a property to a string value * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @str: string value for the property * * fdt_setprop_string() sets the value of the named property in the * given node to the given string value (using the length of the * string to determine the new length of the property), or creates a * new property with that value if it does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_setprop_string(fdt, nodeoffset, name, str) \ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) /** * fdt_delprop - delete a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * * fdt_del_property() will delete the given property. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_delprop(void *fdt, int nodeoffset, const char *name); /** * fdt_add_subnode_namelen - creates a new node based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Identical to fdt_add_subnode(), but use only the first namelen * characters of name as the name of the new node. This is useful for * creating subnodes based on a portion of a larger string, such as a * full path. */ int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen); /** * fdt_add_subnode - creates a new node * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * * fdt_add_subnode() creates a new node as a subnode of the node at * structure block offset parentoffset, with the given name (which * should include the unit address, if any). * * This function will insert data into the blob, and will therefore * change the offsets of some existing nodes. * returns: * structure block offset of the created nodeequested subnode (>=0), on success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of * the given name * -FDT_ERR_NOSPACE, if there is insufficient free space in the * blob to contain the new node * -FDT_ERR_NOSPACE * -FDT_ERR_BADLAYOUT * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_add_subnode(void *fdt, int parentoffset, const char *name); /** * fdt_del_node - delete a node (subtree) * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to nop * * fdt_del_node() will remove the given node, including all its * subnodes if any, from the blob. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_del_node(void *fdt, int nodeoffset); /**********************************************************************/ /* Debugging / informational functions */ /**********************************************************************/ const char * __attrconst fdt_strerror(int errval); #endif /* _LIBFDT_H */ skiboot-skiboot-5.1.13/libfdt/libfdt_env.h000066400000000000000000000011221265204436200204540ustar00rootroot00000000000000#ifndef _LIBFDT_ENV_H #define _LIBFDT_ENV_H #include #include #include #include #define _B(n) ((unsigned long long)((uint8_t *)&x)[n]) static inline uint32_t fdt32_to_cpu(uint32_t x) { return (_B(0) << 24) | (_B(1) << 16) | (_B(2) << 8) | _B(3); } #define cpu_to_fdt32(x) fdt32_to_cpu(x) static inline uint64_t fdt64_to_cpu(uint64_t x) { return (_B(0) << 56) | (_B(1) << 48) | (_B(2) << 40) | (_B(3) << 32) | (_B(4) << 24) | (_B(5) << 16) | (_B(6) << 8) | _B(7); } #define cpu_to_fdt64(x) fdt64_to_cpu(x) #undef _B #endif /* _LIBFDT_ENV_H */ skiboot-skiboot-5.1.13/libfdt/libfdt_internal.h000066400000000000000000000070331265204436200215070ustar00rootroot00000000000000#ifndef _LIBFDT_INTERNAL_H #define _LIBFDT_INTERNAL_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) #define FDT_CHECK_HEADER(fdt) \ { \ int err; \ if ((err = fdt_check_header(fdt)) != 0) \ return err; \ } int _fdt_check_node_offset(const void *fdt, int offset); const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); int _fdt_node_end_offset(void *fdt, int nodeoffset); static inline const void *_fdt_offset_ptr(const void *fdt, int offset) { return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; } static inline void *_fdt_offset_ptr_w(void *fdt, int offset) { return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); } static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) { const struct fdt_reserve_entry *rsv_table = (const struct fdt_reserve_entry *) ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); return rsv_table + n; } static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) { return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); } #define FDT_SW_MAGIC (~FDT_MAGIC) #endif /* _LIBFDT_INTERNAL_H */ skiboot-skiboot-5.1.13/libfdt/version.lds000066400000000000000000000017511265204436200203700ustar00rootroot00000000000000LIBFDT_1.2 { global: fdt_next_node; fdt_check_header; fdt_move; fdt_string; fdt_num_mem_rsv; fdt_get_mem_rsv; fdt_subnode_offset_namelen; fdt_subnode_offset; fdt_path_offset; fdt_get_name; fdt_get_property_namelen; fdt_get_property; fdt_getprop_namelen; fdt_getprop; fdt_get_phandle; fdt_get_alias_namelen; fdt_get_alias; fdt_get_path; fdt_supernode_atdepth_offset; fdt_node_depth; fdt_parent_offset; fdt_node_offset_by_prop_value; fdt_node_offset_by_phandle; fdt_node_check_compatible; fdt_node_offset_by_compatible; fdt_setprop_inplace; fdt_nop_property; fdt_nop_node; fdt_create; fdt_add_reservemap_entry; fdt_finish_reservemap; fdt_begin_node; fdt_property; fdt_end_node; fdt_finish; fdt_open_into; fdt_pack; fdt_add_mem_rsv; fdt_del_mem_rsv; fdt_set_name; fdt_setprop; fdt_delprop; fdt_add_subnode_namelen; fdt_add_subnode; fdt_del_node; fdt_strerror; fdt_offset_ptr; fdt_next_tag; local: *; }; skiboot-skiboot-5.1.13/libflash/000077500000000000000000000000001265204436200165135ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libflash/Makefile.inc000066400000000000000000000003001265204436200207140ustar00rootroot00000000000000LIBFLASH_SRCS = libflash.c libffs.c ecc.c blocklevel.c LIBFLASH_OBJS = $(LIBFLASH_SRCS:%.c=%.o) SUBDIRS += libflash LIBFLASH = libflash/built-in.o $(LIBFLASH): $(LIBFLASH_OBJS:%=libflash/%) skiboot-skiboot-5.1.13/libflash/blocklevel.c000066400000000000000000000177761265204436200210230ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "blocklevel.h" #include "ecc.h" #define PROT_REALLOC_NUM 25 /* This function returns tristate values. * 1 - The region is ECC protected * 0 - The region is not ECC protected * -1 - Partially protected */ static int ecc_protected(struct blocklevel_device *bl, uint32_t pos, uint32_t len) { int i; /* Length of 0 is nonsensical so add 1 */ if (len == 0) len = 1; for (i = 0; i < bl->ecc_prot.n_prot; i++) { /* Fits entirely within the range */ if (bl->ecc_prot.prot[i].start <= pos && bl->ecc_prot.prot[i].start + bl->ecc_prot.prot[i].len >= pos + len) return 1; /* * Since we merge regions on inserting we can be sure that a * partial fit means that the non fitting region won't fit in another ecc * region */ if ((bl->ecc_prot.prot[i].start >= pos && bl->ecc_prot.prot[i].start < pos + len) || (bl->ecc_prot.prot[i].start <= pos && bl->ecc_prot.prot[i].start + bl->ecc_prot.prot[i].len > pos)) return -1; } return 0; } int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len) { int rc; struct ecc64 *buffer; uint32_t ecc_len = ecc_buffer_size(len); if (!bl || !bl->read || !buf) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } if (!ecc_protected(bl, pos, len)) { return bl->read(bl, pos, buf, len); } buffer = malloc(ecc_len); if (!buffer) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } rc = bl->read(bl, pos, buffer, ecc_len); if (rc) goto out; if (memcpy_from_ecc(buf, buffer, len)) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; } out: free(buffer); return rc; } int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len) { int rc; struct ecc64 *buffer; uint32_t ecc_len = ecc_buffer_size(len); if (!bl || !bl->write || !buf) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } if (!ecc_protected(bl, pos, len)) { return bl->write(bl, pos, buf, len); } buffer = malloc(ecc_len); if (!buffer) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } if (memcpy_to_ecc(buffer, buf, len)) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; goto out; } rc = bl->write(bl, pos, buffer, ecc_len); out: free(buffer); return rc; } int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len) { if (!bl || !bl->erase) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } /* Programmer may be making a horrible mistake without knowing it */ if (len & bl->erase_mask) { fprintf(stderr, "blocklevel_erase: len (0x%08x) is not erase block (0x%08x) aligned\n", len, bl->erase_mask + 1); return FLASH_ERR_ERASE_BOUNDARY; } return bl->erase(bl, pos, len); } int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size, uint32_t *erase_granule) { int rc; if (!bl || !bl->get_info) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } rc = bl->get_info(bl, name, total_size, erase_granule); /* Check the validity of what we are being told */ if (erase_granule && *erase_granule != bl->erase_mask + 1) fprintf(stderr, "blocklevel_get_info: WARNING: erase_granule (0x%08x) and erase_mask" " (0x%08x) don't match\n", *erase_granule, bl->erase_mask + 1); return rc; } /* * Compare flash and memory to determine if: * a) Erase must happen before write * b) Flash and memory are identical * c) Flash can simply be written to * * returns -1 for a * returns 0 for b * returns 1 for c */ static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint32_t len) { int i, same = true; const uint8_t *f_buf, *m_buf; f_buf = flash_buf; m_buf = mem_buf; for (i = 0; i < len; i++) { if (m_buf[i] & ~f_buf[i]) return -1; if (same && (m_buf[i] != f_buf[i])) same = false; } return same ? 0 : 1; } int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len) { uint32_t erase_size; const void *write_buf = buf; void *write_buf_start = NULL; void *erase_buf; int rc = 0; if (!write_buf || !bl) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } if (!(bl->flags & WRITE_NEED_ERASE)) return blocklevel_write(bl, pos, buf, len); rc = blocklevel_get_info(bl, NULL, NULL, &erase_size); if (rc) return rc; if (ecc_protected(bl, pos, len)) { len = ecc_buffer_size(len); write_buf_start = malloc(len); if (!write_buf_start) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } if (memcpy_to_ecc(write_buf_start, buf, ecc_buffer_size_minus_ecc(len))) { free(write_buf_start); errno = EBADF; return FLASH_ERR_ECC_INVALID; } write_buf = write_buf_start; } erase_buf = malloc(erase_size); if (!erase_buf) { errno = ENOMEM; rc = FLASH_ERR_MALLOC_FAILED; goto out; } while (len > 0) { uint32_t erase_block = pos & ~(erase_size - 1); uint32_t block_offset = pos & (erase_size - 1); uint32_t size = erase_size > len ? len : erase_size; int cmp; /* Write crosses an erase boundary, shrink the write to the boundary */ if (erase_size < block_offset + size) { size = erase_size - block_offset; } rc = bl->read(bl, erase_block, erase_buf, erase_size); if (rc) goto out; cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size); if (cmp != 0) { if (cmp == -1) bl->erase(bl, erase_block, erase_size); memcpy(erase_buf + block_offset, write_buf, size); rc = bl->write(bl, erase_block, erase_buf, erase_size); if (rc) goto out; } len -= size; pos += size; write_buf += size; } out: free(write_buf_start); free(erase_buf); return rc; } static int insert_bl_prot_range(struct blocklevel_range *ranges, struct bl_prot_range range) { struct bl_prot_range *new_ranges; struct bl_prot_range *old_ranges = ranges->prot; int i, count = ranges->n_prot; /* Try to merge into an existing range */ for (i = 0; i < count; i++) { if (!(range.start + range.len == old_ranges[i].start || old_ranges[i].start + old_ranges[i].len == range.start)) continue; if (range.start + range.len == old_ranges[i].start) old_ranges[i].start = range.start; old_ranges[i].len += range.len; /* * Check the inserted range isn't wedged between two ranges, if it * is, merge as well */ i++; if (i < count && range.start + range.len == old_ranges[i].start) { old_ranges[i - 1].len += old_ranges[i].len; for (; i + 1 < count; i++) old_ranges[i] = old_ranges[i + 1]; ranges->n_prot--; } return 0; } if (ranges->n_prot == ranges->total_prot) { new_ranges = realloc(ranges->prot, sizeof(range) * ((ranges->n_prot) + PROT_REALLOC_NUM)); if (new_ranges) ranges->total_prot += PROT_REALLOC_NUM; } else { new_ranges = old_ranges; } if (new_ranges) { memcpy(new_ranges + ranges->n_prot, &range, sizeof(range)); ranges->prot = new_ranges; ranges->n_prot++; } return !new_ranges; } int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len) { /* * Could implement this at hardware level by having an accessor to the * backend in struct blocklevel_device and as a result do nothing at * this level (although probably not for ecc!) */ struct bl_prot_range range = { .start = start, .len = len }; /* * Refuse to add regions that are already protected or are partially * protected */ if (len < BYTES_PER_ECC || ecc_protected(bl, start, len)) return -1; return insert_bl_prot_range(&bl->ecc_prot, range); } skiboot-skiboot-5.1.13/libflash/blocklevel.h000066400000000000000000000043501265204436200210100ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBFLASH_BLOCKLEVEL_H #define __LIBFLASH_BLOCKLEVEL_H #include struct bl_prot_range { uint32_t start; uint32_t len; }; struct blocklevel_range { struct bl_prot_range *prot; int n_prot; int total_prot; }; enum blocklevel_flags { WRITE_NEED_ERASE = 1, }; /* * libffs may be used with different backends, all should provide these for * libflash to get the information it needs */ struct blocklevel_device { void *priv; int (*read)(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len); int (*write)(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len); int (*erase)(struct blocklevel_device *bl, uint32_t pos, uint32_t len); int (*get_info)(struct blocklevel_device *bl, const char **name, uint32_t *total_size, uint32_t *erase_granule); /* * Keep the erase mask so that blocklevel_erase() can do sanity checking */ uint32_t erase_mask; enum blocklevel_flags flags; struct blocklevel_range ecc_prot; }; int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len); int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len); int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len); int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size, uint32_t *erase_granule); /* Convienience functions */ int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len); /* Implemented in software at this level */ int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len); #endif /* __LIBFLASH_BLOCKLEVEL_H */ skiboot-skiboot-5.1.13/libflash/ecc.c000066400000000000000000000170041265204436200174130ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This is based on the hostboot ecc code */ #include #include #include "libflash.h" #include "ecc.h" /* Bit field identifiers for syndrome calculations. */ enum eccbitfields { GD = 0xff, //< Good, ECC matches. UE = 0xfe, //< Uncorrectable. E0 = 71, //< Error in ECC bit 0 E1 = 70, //< Error in ECC bit 1 E2 = 69, //< Error in ECC bit 2 E3 = 68, //< Error in ECC bit 3 E4 = 67, //< Error in ECC bit 4 E5 = 66, //< Error in ECC bit 5 E6 = 65, //< Error in ECC bit 6 E7 = 64 //< Error in ECC bit 7 /* 0-63 Correctable bit in byte */ }; /* * Matrix used for ECC calculation. * * Each row of this is the set of data word bits that are used for * the calculation of the corresponding ECC bit. The parity of the * bitset is the value of the ECC bit. * * ie. ECC[n] = eccMatrix[n] & data * * Note: To make the math easier (and less shifts in resulting code), * row0 = ECC7. HW numbering is MSB, order here is LSB. * * These values come from the HW design of the ECC algorithm. */ static uint64_t eccmatrix[] = { 0x0000e8423c0f99ffull, 0x00e8423c0f99ff00ull, 0xe8423c0f99ff0000ull, 0x423c0f99ff0000e8ull, 0x3c0f99ff0000e842ull, 0x0f99ff0000e8423cull, 0x99ff0000e8423c0full, 0xff0000e8423c0f99ull }; /** * Syndrome calculation matrix. * * Maps syndrome to flipped bit. * * To perform ECC correction, this matrix is a look-up of the bit * that is bad based on the binary difference of the good and bad * ECC. This difference is called the "syndrome". * * When a particular bit is on in the data, it cause a column from * eccMatrix being XOR'd into the ECC field. This column is the * "effect" of each bit. If a bit is flipped in the data then its * "effect" is missing from the ECC. You can calculate ECC on unknown * quality data and compare the ECC field between the calculated * value and the stored value. If the difference is zero, then the * data is clean. If the difference is non-zero, you look up the * difference in the syndrome table to identify the "effect" that * is missing, which is the bit that is flipped. * * Notice that ECC bit flips are recorded by a single "effect" * bit (ie. 0x1, 0x2, 0x4, 0x8 ...) and double bit flips are identified * by the UE status in the table. * * Bits are in MSB order. */ static enum eccbitfields syndromematrix[] = { GD, E7, E6, UE, E5, UE, UE, 47, E4, UE, UE, 37, UE, 35, 39, UE, E3, UE, UE, 48, UE, 30, 29, UE, UE, 57, 27, UE, 31, UE, UE, UE, E2, UE, UE, 17, UE, 18, 40, UE, UE, 58, 22, UE, 21, UE, UE, UE, UE, 16, 49, UE, 19, UE, UE, UE, 23, UE, UE, UE, UE, 20, UE, UE, E1, UE, UE, 51, UE, 46, 9, UE, UE, 34, 10, UE, 32, UE, UE, 36, UE, 62, 50, UE, 14, UE, UE, UE, 13, UE, UE, UE, UE, UE, UE, UE, UE, 61, 8, UE, 41, UE, UE, UE, 11, UE, UE, UE, UE, UE, UE, UE, 15, UE, UE, UE, UE, UE, UE, UE, UE, UE, 12, UE, UE, UE, UE, UE, E0, UE, UE, 55, UE, 45, 43, UE, UE, 56, 38, UE, 1, UE, UE, UE, UE, 25, 26, UE, 2, UE, UE, UE, 24, UE, UE, UE, UE, UE, 28, UE, UE, 59, 54, UE, 42, UE, UE, 44, 6, UE, UE, UE, UE, UE, UE, UE, 5, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, 63, 53, UE, 0, UE, UE, UE, 33, UE, UE, UE, UE, UE, UE, UE, 3, UE, UE, 52, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, 7, UE, UE, UE, UE, UE, UE, UE, UE, 60, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, 4, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, }; static uint8_t parity(uint64_t data) { #ifdef __SKIBOOT__ uint8_t p; asm volatile( "popcntb %1,%0\n" "prtyd %1,%1\n" : "=r"(p) : "r"(data)); return p; #else return __builtin_parityl(data); #endif } /** * Create the ECC field corresponding to a 8-byte data field * * @data: The 8 byte data to generate ECC for. * @return: The 1 byte ECC corresponding to the data. */ static uint8_t eccgenerate(uint64_t data) { int i; uint8_t result = 0; for (i = 0; i < 8; i++) result |= parity(eccmatrix[i] & data) << i; return result; } /** * Verify the data and ECC match or indicate how they are wrong. * * @data: The data to check ECC on. * @ecc: The [supposed] ECC for the data. * * @return: eccBitfield or 0-64. * * @retval GD - Indicates the data is good (matches ECC). * @retval UE - Indicates the data is uncorrectable. * @retval all others - Indication of which bit is incorrect. */ static enum eccbitfields eccverify(uint64_t data, uint8_t ecc) { return syndromematrix[eccgenerate(data) ^ ecc]; } /* IBM bit ordering */ static inline uint64_t eccflipbit(uint64_t data, uint8_t bit) { if (bit > 63) return data; return data ^ (1ul << (63 - bit)); } /** * Copy data from an input buffer with ECC to an output buffer without ECC. * Correct it along the way and check for errors. * * @dst: destination buffer without ECC * @src: source buffer with ECC * @len: number of bytes of data to copy (without ecc). * Must be 8 byte aligned. * * @return: Success or error * * @retval: 0 - success * @retfal: other - fail */ int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint32_t len) { beint64_t data; uint8_t ecc; uint32_t i; uint8_t badbit; if (len & 0x7) { /* TODO: we could probably handle this */ FL_ERR("ECC data length must be 8 byte aligned length:%i\n", len); return -1; } /* Handle in chunks of 8 bytes, so adjust the length */ len >>= 3; for (i = 0; i < len; i++) { data = (src + i)->data; ecc = (src + i)->ecc; badbit = eccverify(be64_to_cpu(data), ecc); if (badbit == UE) { FL_ERR("ECC: uncorrectable error: %016lx %02x\n", (long unsigned int)be64_to_cpu(data), ecc); return badbit; } *dst = data; if (badbit <= UE) FL_INF("ECC: correctable error: %i\n", badbit); if (badbit < 64) *dst = (uint64_t)be64_to_cpu(eccflipbit(be64_to_cpu(data), badbit)); dst++; } return 0; } /** * Copy data from an input buffer without ECC to an output buffer with ECC. * * @dst: destination buffer with ECC * @src: source buffer without ECC * @len: number of bytes of data to copy (without ecc, length of src). * Note: dst must be big enough to hold ecc bytes as well. * Must be 8 byte aligned. * * @return: success or failure * * @retval: 0 - success * @retfal: other - fail */ int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint32_t len) { struct ecc64 ecc_word; uint32_t i; if (len & 0x7) { /* TODO: we could probably handle this */ FL_ERR("Data to add ECC bytes to must be 8 byte aligned length: %i\n", len); return -1; } /* Handle in chunks of 8 bytes, so adjust the length */ len >>= 3; for (i = 0; i < len; i++) { ecc_word.ecc = eccgenerate(be64_to_cpu(*(src + i))); ecc_word.data = *(src + i); *(dst + i) = ecc_word; } return 0; } skiboot-skiboot-5.1.13/libflash/ecc.h000066400000000000000000000032101265204436200174120ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This is based on the hostboot ecc code */ #ifndef __ECC_H #define __ECC_H #include #include struct ecc64 { beint64_t data; uint8_t ecc; } __attribute__((__packed__)); extern int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint32_t len); extern int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint32_t len); /* * Calculate the size of a buffer if ECC is added * * We add 1 byte of ecc for every 8 bytes of data. So we need to round up to 8 * bytes length and then add 1/8 */ #ifndef ALIGN_UP #define ALIGN_UP(_v, _a) (((_v) + (_a) - 1) & ~((_a) - 1)) #endif #define BYTES_PER_ECC 8 static inline uint32_t ecc_size(uint32_t len) { return ALIGN_UP(len, BYTES_PER_ECC) >> 3; } static inline uint32_t ecc_buffer_size(uint32_t len) { return ALIGN_UP(len, BYTES_PER_ECC) + ecc_size(len); } static inline int ecc_buffer_size_check(uint32_t len) { return len % (BYTES_PER_ECC + 1); } static inline uint32_t ecc_buffer_size_minus_ecc(uint32_t len) { return len * BYTES_PER_ECC / (BYTES_PER_ECC + 1); } #endif skiboot-skiboot-5.1.13/libflash/errors.h000066400000000000000000000023071265204436200202020ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBFLASH_ERRORS_H #define __LIBFLASH_ERRORS_H #define FLASH_ERR_MALLOC_FAILED 1 #define FLASH_ERR_CHIP_UNKNOWN 2 #define FLASH_ERR_PARM_ERROR 3 #define FLASH_ERR_ERASE_BOUNDARY 4 #define FLASH_ERR_WREN_TIMEOUT 5 #define FLASH_ERR_WIP_TIMEOUT 6 #define FLASH_ERR_BAD_PAGE_SIZE 7 #define FLASH_ERR_VERIFY_FAILURE 8 #define FLASH_ERR_4B_NOT_SUPPORTED 9 #define FLASH_ERR_CTRL_CONFIG_MISMATCH 10 #define FLASH_ERR_CHIP_ER_NOT_SUPPORTED 11 #define FLASH_ERR_CTRL_CMD_UNSUPPORTED 12 #define FLASH_ERR_CTRL_TIMEOUT 13 #define FLASH_ERR_ECC_INVALID 14 #define FLASH_ERR_BAD_READ 15 #endif /* __LIBFLASH_ERRORS_H */ skiboot-skiboot-5.1.13/libflash/ffs.h000066400000000000000000000100751265204436200174450ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright (c) International Business Machines Corp., 2012 * * FSP Flash Structure * * This header defines the layout for the FSP Flash Structure. */ #ifndef __FFS_H__ #define __FFS_H__ /* Pull in the correct header depending on what is being built */ #if defined(__KERNEL__) #include #else #include #endif /* The version of this partition implementation */ #define FFS_VERSION_1 1 /* Magic number for the partition header (ASCII 'PART') */ #define FFS_MAGIC 0x50415254 /* The maximum length of the partition name */ #define PART_NAME_MAX 15 /* * Sizes of the data structures */ #define FFS_HDR_SIZE sizeof(struct ffs_hdr) #define FFS_ENTRY_SIZE sizeof(struct ffs_entry) /* * Sizes of the data structures w/o checksum */ #define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t)) #define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t)) /* pid of logical partitions/containers */ #define FFS_PID_TOPLEVEL 0xFFFFFFFF /* * Type of image contained w/in partition */ enum type { FFS_TYPE_DATA = 1, FFS_TYPE_LOGICAL = 2, FFS_TYPE_PARTITION = 3, }; /* * Flag bit definitions */ #define FFS_FLAGS_PROTECTED 0x0001 #define FFS_FLAGS_U_BOOT_ENV 0x0002 /* Data integrity flags */ #define FFS_ENRY_INTEG_ECC 0x8000 /** * struct ffs_entry_user - User data enties * * @chip: Chip Select (0,1) * @compressType: Compression Indication/alg (0=not compressed) * @dataInteg: Indicates Data Integrity mechanism * @verCheck: Indicates Version check type * @miscFlags: Misc Partition related Flags * @freeMisc[2]: Unused Miscellaneious Info * @freeUser[14]: Unused User Data */ struct ffs_entry_user { uint8_t chip; uint8_t compresstype; uint16_t datainteg; uint8_t vercheck; uint8_t miscflags; uint8_t freemisc[2]; uint32_t reserved[14]; }; /** * struct ffs_entry - Partition entry * * @name: Opaque null terminated string * @base: Starting offset of partition in flash (in hdr.block_size) * @size: Partition size (in hdr.block_size) * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel) * @id: Partition entry ID [1..65536] * @type: Describe type of partition * @flags: Partition attributes (optional) * @actual: Actual partition size (in bytes) * @resvd: Reserved words for future use * @user: User data (optional) * @checksum: Partition entry checksum (includes all above) */ struct ffs_entry { char name[PART_NAME_MAX + 1]; uint32_t base; uint32_t size; uint32_t pid; uint32_t id; uint32_t type; uint32_t flags; uint32_t actual; uint32_t resvd[4]; struct ffs_entry_user user; uint32_t checksum; } __attribute__ ((packed)); /** * struct ffs_hdr - FSP Flash Structure header * * @magic: Eye catcher/corruption detector * @version: Version of the structure * @size: Size of partition table (in block_size) * @entry_size: Size of struct ffs_entry element (in bytes) * @entry_count: Number of struct ffs_entry elements in @entries array * @block_size: Size of block on device (in bytes) * @block_count: Number of blocks on device * @resvd: Reserved words for future use * @checksum: Header checksum * @entries: Pointer to array of partition entries */ struct ffs_hdr { uint32_t magic; uint32_t version; uint32_t size; uint32_t entry_size; uint32_t entry_count; uint32_t block_size; uint32_t block_count; uint32_t resvd[4]; uint32_t checksum; struct ffs_entry entries[]; } __attribute__ ((packed)); #endif /* __FFS_H__ */ skiboot-skiboot-5.1.13/libflash/file.c000066400000000000000000000147621265204436200176100ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libflash.h" #include "blocklevel.h" struct file_data { int fd; char *name; struct blocklevel_device bl; }; static int file_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len) { struct file_data *file_data = container_of(bl, struct file_data, bl); int count = 0; int rc; rc = lseek(file_data->fd, pos, SEEK_SET); /* errno should remain set */ if (rc != pos) return FLASH_ERR_PARM_ERROR; while (count < len) { rc = read(file_data->fd, buf, len); /* errno should remain set */ if (rc == -1) return FLASH_ERR_BAD_READ; count += rc; } return 0; } static int file_write(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t len) { struct file_data *file_data = container_of(bl, struct file_data, bl); int count = 0; int rc; rc = lseek(file_data->fd, dst, SEEK_SET); /* errno should remain set */ if (rc == -1) return FLASH_ERR_PARM_ERROR; while (count < len) { rc = write(file_data->fd, src, len); /* errno should remain set */ if (rc == -1) return FLASH_ERR_VERIFY_FAILURE; count += rc; } return 0; } /* * Due to to the fact these interfaces are ultimately supposed to deal with * flash, an erase function must be implemented even when the flash images * are backed by regular files. * Also, erasing flash leaves all the bits set to 1. This may be expected * by higher level functions so this function should also emulate that */ static int file_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len) { unsigned long long int d = ULLONG_MAX; int i = 0; int rc = 0; while (len - i > 0) { rc = file_write(bl, dst + i, &d, len - i > sizeof(d) ? sizeof(d) : len - i); if (rc) return rc; i += sizeof(d); } return 0; } static int mtd_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len) { struct file_data *file_data = container_of(bl, struct file_data, bl); struct erase_info_user erase_info = { .start = dst, .length = len }; return ioctl(file_data->fd, MEMERASE, &erase_info) == -1 ? -1 : 0; } static int get_info_name(struct file_data *file_data, char **name) { char *path, *lpath; int len; struct stat st; if (asprintf(&path, "/proc/self/fd/%d", file_data->fd) == -1) return FLASH_ERR_MALLOC_FAILED; if (lstat(path, &st)) { free(path); return FLASH_ERR_PARM_ERROR; } lpath = malloc(st.st_size + 1); if (!lpath) { free(path); return FLASH_ERR_MALLOC_FAILED; } len = readlink(path, lpath, st.st_size +1); if (len == -1) { free(path); free(lpath); return FLASH_ERR_PARM_ERROR; } lpath[len] = '\0'; *name = lpath; free(path); return 0; } static int mtd_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size, uint32_t *erase_granule) { struct file_data *file_data = container_of(bl, struct file_data, bl); struct mtd_info_user mtd_info; int rc; if (ioctl(file_data->fd, MEMGETINFO, &mtd_info) == -1) return FLASH_ERR_BAD_READ; if (total_size) *total_size = mtd_info.size; if (erase_granule) *erase_granule = mtd_info.erasesize; if (name) { rc = get_info_name(file_data, &(file_data->name)); if (rc) return rc; *name = file_data->name; } return 0; } static int file_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size, uint32_t *erase_granule) { struct file_data *file_data = container_of(bl, struct file_data, bl); struct stat st; int rc; if (fstat(file_data->fd, &st)) return FLASH_ERR_PARM_ERROR; if (total_size) *total_size = st.st_size; if (erase_granule) *erase_granule = 1; if (name) { rc = get_info_name(file_data, &(file_data->name)); if (rc) return rc; *name = file_data->name; } return 0; } int file_init(int fd, struct blocklevel_device **bl) { struct file_data *file_data; struct stat sbuf; if (!bl) return FLASH_ERR_PARM_ERROR; *bl = NULL; file_data = malloc(sizeof(struct file_data)); if (!file_data) return FLASH_ERR_MALLOC_FAILED; memset(file_data, 0, sizeof(*file_data)); file_data->fd = fd; file_data->bl.read = &file_read; file_data->bl.write = &file_write; file_data->bl.erase = &file_erase; file_data->bl.get_info = &file_get_info; file_data->bl.erase_mask = 0; /* * Unfortunately not all file descriptors are created equal... * Here we check to see if the file descriptor is to an MTD device, in * which case we have to erase and get the size of it differently. */ if (fstat(file_data->fd, &sbuf) == -1) goto out; /* Won't be able to handle other than MTD devices for now */ if (S_ISCHR(sbuf.st_mode)) { file_data->bl.erase = &mtd_erase; file_data->bl.get_info = &mtd_get_info; file_data->bl.flags = WRITE_NEED_ERASE; mtd_get_info(&file_data->bl, NULL, NULL, &(file_data->bl.erase_mask)); file_data->bl.erase_mask--; } else if (!S_ISREG(sbuf.st_mode)) { /* If not a char device or a regular file something went wrong */ goto out; } *bl = &(file_data->bl); return 0; out: free(file_data); return FLASH_ERR_PARM_ERROR; } int file_init_path(const char *path, int *r_fd, struct blocklevel_device **bl) { int fd, rc; if (!path || !bl) return FLASH_ERR_PARM_ERROR; fd = open(path, O_RDWR); if (fd == -1) return FLASH_ERR_PARM_ERROR; rc = file_init(fd, bl); if (rc) close(fd); if (r_fd) *r_fd = fd; return rc; } void file_exit(struct blocklevel_device *bl) { struct file_data *file_data; if (bl) { free(bl->ecc_prot.prot); file_data = container_of(bl, struct file_data, bl); free(file_data->name); free(file_data); } } void file_exit_close(struct blocklevel_device *bl) { struct file_data *file_data; if (bl) { file_data = container_of(bl, struct file_data, bl); close(file_data->fd); file_exit(bl); } } skiboot-skiboot-5.1.13/libflash/file.h000066400000000000000000000027241265204436200176100ustar00rootroot00000000000000/* Copyright 2013-2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBFLASH_FILE_H #define __LIBFLASH_FILE_H #include "blocklevel.h" /* * Blockevel functions created leave errno set on errors, as these calls * often boil down to standard read() and write() calls, inspecting errno * may prove useful */ int file_init(int fd, struct blocklevel_device **bl); void file_exit(struct blocklevel_device *bl); /* * file_init_path() is a convenience wrapper which will open the path and call * file_init(). The call to open happens with O_RDWR and no additional flags * Because file_exit() doesn't close the file descriptor, file_init_path() * makes it available. */ int file_init_path(const char *path, int *fd, struct blocklevel_device **bl); /* * file_exit_close is a convenience wrapper which will close the open * file descriptor and call file_exit(). */ void file_exit_close(struct blocklevel_device *bl); #endif /* __LIBFLASH_FILE_H */ skiboot-skiboot-5.1.13/libflash/libffs.c000066400000000000000000000217271265204436200201350ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* */ #include #include #include #ifndef __SKIBOOT__ #include #include #endif #include #include "libffs.h" enum ffs_type { ffs_type_flash, ffs_type_image, }; struct ffs_handle { struct ffs_hdr hdr; /* Converted header */ enum ffs_type type; struct flash_chip *chip; uint32_t toc_offset; uint32_t max_size; void *cache; uint32_t cached_size; struct blocklevel_device *bl; }; static uint32_t ffs_checksum(void* data, size_t size) { uint32_t i, csum = 0; for (i = csum = 0; i < (size/4); i++) csum ^= ((uint32_t *)data)[i]; return csum; } static int ffs_check_convert_header(struct ffs_hdr *dst, struct ffs_hdr *src) { dst->magic = be32_to_cpu(src->magic); if (dst->magic != FFS_MAGIC) return FFS_ERR_BAD_MAGIC; dst->version = be32_to_cpu(src->version); if (dst->version != FFS_VERSION_1) return FFS_ERR_BAD_VERSION; if (ffs_checksum(src, FFS_HDR_SIZE) != 0) return FFS_ERR_BAD_CKSUM; dst->size = be32_to_cpu(src->size); dst->entry_size = be32_to_cpu(src->entry_size); dst->entry_count = be32_to_cpu(src->entry_count); dst->block_size = be32_to_cpu(src->block_size); dst->block_count = be32_to_cpu(src->block_count); return 0; } int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl, struct ffs_handle **ffs, int mark_ecc) { struct ffs_hdr hdr; struct ffs_handle *f; uint32_t total_size; int rc, i; if (!ffs || !bl) return FLASH_ERR_PARM_ERROR; *ffs = NULL; rc = blocklevel_get_info(bl, NULL, &total_size, NULL); if (rc) { FL_ERR("FFS: Error %d retrieving flash info\n", rc); return rc; } if ((offset + max_size) < offset) return FLASH_ERR_PARM_ERROR; if ((max_size > total_size)) return FLASH_ERR_PARM_ERROR; /* Read flash header */ rc = blocklevel_read(bl, offset, &hdr, sizeof(hdr)); if (rc) { FL_ERR("FFS: Error %d reading flash header\n", rc); return rc; } /* Allocate ffs_handle structure and start populating */ f = malloc(sizeof(*f)); if (!f) return FLASH_ERR_MALLOC_FAILED; memset(f, 0, sizeof(*f)); f->toc_offset = offset; f->max_size = max_size; f->bl = bl; /* Convert and check flash header */ rc = ffs_check_convert_header(&f->hdr, &hdr); if (rc) { FL_ERR("FFS: Error %d checking flash header\n", rc); goto out; } /* * Decide how much of the image to grab to get the whole * partition map. */ f->cached_size = f->hdr.block_size * f->hdr.size; FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size); /* Allocate cache */ f->cache = malloc(f->cached_size); if (!f->cache) { rc = FLASH_ERR_MALLOC_FAILED; goto out; } /* Read the cached map */ rc = blocklevel_read(bl, offset, f->cache, f->cached_size); if (rc) { FL_ERR("FFS: Error %d reading flash partition map\n", rc); goto out; } if (mark_ecc) { uint32_t start, total_size; bool ecc; for (i = 0; i < f->hdr.entry_count; i++) { rc = ffs_part_info(f, i, NULL, &start, &total_size, NULL, &ecc); if (rc) { FL_ERR("FFS: Failed to read ffs partition %d\n", i); goto out; } if (ecc) { rc = blocklevel_ecc_protect(bl, start, total_size); if (rc) { FL_ERR("Failed to blocklevel_ecc_protect(0x%08x, 0x%08x)\n", start, total_size); goto out; } } /* ecc */ } /* for */ } out: if (rc == 0) *ffs = f; else free(f); return rc; } /* ffs_open_image is Linux only as it uses lseek, which skiboot does not * implement */ #ifndef __SKIBOOT__ int ffs_open_image(int fd, uint32_t size, uint32_t toc_offset, struct ffs_handle **ffsh) { struct ffs_hdr hdr; struct ffs_handle *f; int rc; if (!ffsh) return FLASH_ERR_PARM_ERROR; *ffsh = NULL; if (fd < 0) return FLASH_ERR_PARM_ERROR; if ((toc_offset + size) < toc_offset) return FLASH_ERR_PARM_ERROR; /* Read flash header */ rc = lseek(fd, toc_offset, SEEK_SET); if (rc < 0) return FLASH_ERR_PARM_ERROR; rc = read(fd, &hdr, sizeof(hdr)); if (rc != sizeof(hdr)) return FLASH_ERR_BAD_READ; /* Allocate ffs_handle structure and start populating */ f = malloc(sizeof(*f)); if (!f) return FLASH_ERR_MALLOC_FAILED; memset(f, 0, sizeof(*f)); f->type = ffs_type_image; f->toc_offset = toc_offset; f->max_size = size; f->chip = NULL; /* Convert and check flash header */ rc = ffs_check_convert_header(&f->hdr, &hdr); if (rc) { FL_ERR("FFS: Error %d checking flash header\n", rc); free(f); return rc; } /* * Decide how much of the image to grab to get the whole * partition map. */ f->cached_size = f->hdr.block_size * f->hdr.size; FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size); /* Allocate cache */ f->cache = malloc(f->cached_size); if (!f->cache) { free(f); return FLASH_ERR_MALLOC_FAILED; } /* Read the cached map */ rc = lseek(fd, toc_offset, SEEK_SET); if (rc < 0) return FLASH_ERR_PARM_ERROR; rc = read(fd, f->cache, f->cached_size); if (rc != f->cached_size) { FL_ERR("FFS: Error %d reading flash partition map\n", rc); free(f); return FLASH_ERR_BAD_READ; } *ffsh = f; return 0; } #endif /*!__SKIBOOT__*/ void ffs_close(struct ffs_handle *ffs) { if (ffs->cache) free(ffs->cache); free(ffs); } static struct ffs_entry *ffs_get_part(struct ffs_handle *ffs, uint32_t index, uint32_t *out_offset) { uint32_t esize = ffs->hdr.entry_size; uint32_t offset = FFS_HDR_SIZE + index * esize; if (index > ffs->hdr.entry_count) return NULL; if (out_offset) *out_offset = ffs->toc_offset + offset; return (struct ffs_entry *)(ffs->cache + offset); } static int ffs_check_convert_entry(struct ffs_entry *dst, struct ffs_entry *src) { if (ffs_checksum(src, FFS_ENTRY_SIZE) != 0) return FFS_ERR_BAD_CKSUM; memcpy(dst->name, src->name, sizeof(dst->name)); dst->base = be32_to_cpu(src->base); dst->size = be32_to_cpu(src->size); dst->pid = be32_to_cpu(src->pid); dst->id = be32_to_cpu(src->id); dst->type = be32_to_cpu(src->type); dst->flags = be32_to_cpu(src->flags); dst->actual = be32_to_cpu(src->actual); dst->user.datainteg = be16_to_cpu(src->user.datainteg); return 0; } int ffs_lookup_part(struct ffs_handle *ffs, const char *name, uint32_t *part_idx) { struct ffs_entry ent; uint32_t i; int rc; /* Lookup the requested partition */ for (i = 0; i < ffs->hdr.entry_count; i++) { struct ffs_entry *src_ent = ffs_get_part(ffs, i, NULL); rc = ffs_check_convert_entry(&ent, src_ent); if (rc) { FL_ERR("FFS: Bad entry %d in partition map\n", i); continue; } if (!strncmp(name, ent.name, sizeof(ent.name))) break; } if (i >= ffs->hdr.entry_count) return FFS_ERR_PART_NOT_FOUND; if (part_idx) *part_idx = i; return 0; } int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx, char **name, uint32_t *start, uint32_t *total_size, uint32_t *act_size, bool *ecc) { struct ffs_entry *raw_ent; struct ffs_entry ent; char *n; int rc; if (part_idx >= ffs->hdr.entry_count) return FFS_ERR_PART_NOT_FOUND; raw_ent = ffs_get_part(ffs, part_idx, NULL); if (!raw_ent) return FFS_ERR_PART_NOT_FOUND; rc = ffs_check_convert_entry(&ent, raw_ent); if (rc) { FL_ERR("FFS: Bad entry %d in partition map\n", part_idx); return rc; } if (start) *start = ent.base * ffs->hdr.block_size; if (total_size) *total_size = ent.size * ffs->hdr.block_size; if (act_size) *act_size = ent.actual; if (ecc) *ecc = ((ent.user.datainteg & FFS_ENRY_INTEG_ECC) != 0); if (name) { n = malloc(PART_NAME_MAX + 1); memset(n, 0, PART_NAME_MAX + 1); strncpy(n, ent.name, PART_NAME_MAX); *name = n; } return 0; } int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, uint32_t act_size) { struct ffs_entry *ent; uint32_t offset; if (part_idx >= ffs->hdr.entry_count) { FL_DBG("FFS: Entry out of bound\n"); return FFS_ERR_PART_NOT_FOUND; } ent = ffs_get_part(ffs, part_idx, &offset); if (!ent) { FL_DBG("FFS: Entry not found\n"); return FFS_ERR_PART_NOT_FOUND; } FL_DBG("FFS: part index %d at offset 0x%08x\n", part_idx, offset); /* * NOTE: We are accessing the unconverted ffs_entry from the PNOR here * (since we are going to write it back) so we need to be endian safe. */ if (ent->actual == cpu_to_be32(act_size)) { FL_DBG("FFS: ent->actual alrady matches: 0x%08x==0x%08x\n", cpu_to_be32(act_size), ent->actual); return 0; } ent->actual = cpu_to_be32(act_size); ent->checksum = ffs_checksum(ent, FFS_ENTRY_SIZE_CSUM); if (!ffs->chip) return 0; return blocklevel_write(ffs->bl, offset, ent, FFS_ENTRY_SIZE); } skiboot-skiboot-5.1.13/libflash/libffs.h000066400000000000000000000033631265204436200201360ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBFFS_H #define __LIBFFS_H #include #include #include /* FFS handle, opaque */ struct ffs_handle; /* Error codes: * * < 0 = flash controller errors * 0 = success * > 0 = libffs / libflash errors */ #define FFS_ERR_BAD_MAGIC 100 #define FFS_ERR_BAD_VERSION 101 #define FFS_ERR_BAD_CKSUM 102 #define FFS_ERR_PART_NOT_FOUND 103 #define FFS_ERR_BAD_ECC 104 /* Init */ int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl, struct ffs_handle **ffs, int mark_ecc); /* ffs_open_image is Linux only as it uses lseek, which skiboot does not * implement */ #ifndef __SKIBOOT__ int ffs_open_image(int fd, uint32_t size, uint32_t toc_offset, struct ffs_handle **ffs); #endif void ffs_close(struct ffs_handle *ffs); int ffs_lookup_part(struct ffs_handle *ffs, const char *name, uint32_t *part_idx); int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx, char **name, uint32_t *start, uint32_t *total_size, uint32_t *act_size, bool *ecc); int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, uint32_t act_size); #endif /* __LIBFFS_H */ skiboot-skiboot-5.1.13/libflash/libflash-priv.h000066400000000000000000000204151265204436200214300ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBFLASH_PRIV_H #define __LIBFLASH_PRIV_H #include #include #include /* Flash commands */ #define CMD_WRSR 0x01 /* Write Status Register (also config. on Macronix) */ #define CMD_PP 0x02 /* Page Program */ #define CMD_READ 0x03 /* READ */ #define CMD_WRDI 0x04 /* Write Disable */ #define CMD_RDSR 0x05 /* Read Status Register */ #define CMD_WREN 0x06 /* Write Enable */ #define CMD_RDCR 0x15 /* Read configuration register (Macronix) */ #define CMD_SE 0x20 /* Sector (4K) Erase */ #define CMD_RDSCUR 0x2b /* Read Security Register (Macronix) */ #define CMD_BE32K 0x52 /* Block (32K) Erase */ #define CMD_RDSFDP 0x5a /* Read SFDP JEDEC info */ #define CMD_CE 0x60 /* Chip Erase (Macronix/Winbond) */ #define CMD_MIC_WREVCONF 0x61 /* Micron Write Enhanced Volatile Config */ #define CMD_MIC_RDEVCONF 0x65 /* Micron Read Enhanced Volatile Config */ #define CMD_MIC_RDFLST 0x70 /* Micron Read Flag Status */ #define CMD_MIC_WRVCONF 0x81 /* Micron Write Volatile Config */ #define CMD_MIC_RDVCONF 0x85 /* Micron Read Volatile Config */ #define CMD_RDID 0x9f /* Read JEDEC ID */ #define CMD_EN4B 0xb7 /* Enable 4B addresses */ #define CMD_MIC_BULK_ERASE 0xc7 /* Micron Bulk Erase */ #define CMD_BE 0xd8 /* Block (64K) Erase */ #define CMD_RDDPB 0xe0 /* Read dynamic protection (Macronix) */ #define CMD_RDSPB 0xe2 /* Read static protection (Macronix) */ #define CMD_EX4B 0xe9 /* Exit 4B addresses */ /* Flash status bits */ #define STAT_WIP 0x01 #define STAT_WEN 0x02 /* This isn't exposed to clients but is to controllers */ struct flash_info { uint32_t id; uint32_t size; uint32_t flags; #define FL_ERASE_4K 0x00000001 /* Supports 4k erase */ #define FL_ERASE_32K 0x00000002 /* Supports 32k erase */ #define FL_ERASE_64K 0x00000004 /* Supports 64k erase */ #define FL_ERASE_CHIP 0x00000008 /* Supports 0x60 cmd chip erase */ #define FL_ERASE_BULK 0x00000010 /* Supports 0xc7 cmd bulk erase */ #define FL_MICRON_BUGS 0x00000020 /* Various micron bug workarounds */ #define FL_ERASE_ALL (FL_ERASE_4K | FL_ERASE_32K | FL_ERASE_64K | \ FL_ERASE_CHIP) #define FL_CAN_4B 0x00000010 /* Supports 4b mode */ const char *name; }; /* Flash controller, return negative values for errors */ struct spi_flash_ctrl { /* * The controller can provide basically two interfaces, * either a fairly high level one and a lower level one. * * If all functions of the high level interface are * implemented then the low level one is optional. A * controller can implement some of the high level one * in which case the missing ones will be handled by * libflash using the low level interface. * * There are also some common functions. */ /* ************************************************** * Misc / common functions * **************************************************/ /* * - setup(ctrl, tsize) * * Provides the controller with an option to configure itself * based on the specific flash type. It can also override some * settings in the info block such as available erase sizes etc... * which can be needed for high level controllers. It can also * override the total flash size. */ int (*setup)(struct spi_flash_ctrl *ctrl, uint32_t *tsize); /* * - set_4b(ctrl, enable) * * enable : Switch to 4bytes (true) or 3bytes (false) address mode * * Set the controller's address size. If the controller doesn't * implement the low level command interface, then this must also * configure the flash chip itself. Otherwise, libflash will do it. * * Note that if this isn't implemented, then libflash might still * try to switch large flash chips to 4b mode if the low level cmd * interface is implemented. It will then also stop using the high * level command interface since it's assumed that it cannot handle * 4b addresses. */ int (*set_4b)(struct spi_flash_ctrl *ctrl, bool enable); /* ************************************************** * High level interface * **************************************************/ /* * Read chip ID. This can return up to 16 bytes though the * current libflash will only use 3 (room for things like * extended micron stuff). * * id_size is set on entry to the buffer size and need to * be adjusted to the actual ID size read. * * If NULL, libflash will use cmd_rd to send normal RDID (0x9f) * command. */ int (*chip_id)(struct spi_flash_ctrl *ctrl, uint8_t *id_buf, uint32_t *id_size); /* * Read from flash. There is no specific constraint on * alignment or size other than not reading outside of * the chip. * * If NULL, libflash will use cmd_rd to send normal * READ (0x03) commands. */ int (*read)(struct spi_flash_ctrl *ctrl, uint32_t addr, void *buf, uint32_t size); /* * Write to flash. There is no specific constraint on * alignment or size other than not reading outside of * the chip. The driver is responsible for handling * 256-bytes page alignment and to send the write enable * commands when needed. * * If absent, libflash will use cmd_wr to send WREN (0x06) * and PP (0x02) commands. * * Note: This does not need to handle erasing. libflash * will ensure that this is never used for changing a bit * value from 0 to 1. */ int (*write)(struct spi_flash_ctrl *ctrl, uint32_t addr, const void *buf, uint32_t size); /* * Erase. This will be called for erasing a portion of * the flash using a granularity (alignment of start and * size) that is no less than the smallest supported * erase size in the info block (*). The driver is * responsible to send write enable commands when needed. * * If absent, libflash will use cmd_wr to send WREN (0x06) * and either of SE (0x20), BE32K (0x52) or BE (0xd8) * based on what the flash chip supports. * * (*) Note: This is called with addr=0 and size=0xffffffff * in which case this is used as a "chip erase". Return * FLASH_ERR_CHIP_ER_NOT_SUPPORTED if not supported. Some * future version of libflash might then emulate it using * normal erase commands. */ int (*erase)(struct spi_flash_ctrl *ctrl, uint32_t addr, uint32_t size); /* ************************************************** * Low level interface * **************************************************/ /* Note: For commands with no data, libflash will might use * either cmd_rd or cmd_wr. */ /* * - cmd_rd(ctrl, cmd, has_addr, address, buffer, size); * * cmd : command opcode * has_addr : send an address after the command * address : address to send * buffer : buffer for additional data to read (or NULL) * size : size of additional data read (or NULL) * * Sends a command and optionally read additional data */ int (*cmd_rd)(struct spi_flash_ctrl *ctrl, uint8_t cmd, bool has_addr, uint32_t addr, void *buffer, uint32_t size); /* * - cmd_wr(ctrl, cmd, has_addr, address, buffer, size); * * cmd : command opcode * has_addr : send an address after the command * address : address to send * buffer : buffer for additional data to write (or NULL) * size : size of additional data write (or NULL) * * Sends a command and optionally write additional data */ int (*cmd_wr)(struct spi_flash_ctrl *ctrl, uint8_t cmd, bool has_addr, uint32_t addr, const void *buffer, uint32_t size); /* The core will establish this at init, after chip ID has * been probed */ struct flash_info *finfo; void *priv; }; extern int fl_wren(struct spi_flash_ctrl *ct); extern int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat); extern int fl_sync_wait_idle(struct spi_flash_ctrl *ct); #endif /* LIBFLASH_PRIV_H */ skiboot-skiboot-5.1.13/libflash/libflash.c000066400000000000000000000475121265204436200204540ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "libflash.h" #include "libflash-priv.h" #include "ecc.h" #include "blocklevel.h" #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif static const struct flash_info flash_info[] = { { 0xc22018, 0x01000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL12835F"}, { 0xc22019, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL25635F"}, { 0xc2201a, 0x04000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL51235F"}, { 0xef4018, 0x01000000, FL_ERASE_ALL, "Winbond W25Q128BV" }, { 0x20ba20, 0x04000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B | FL_ERASE_BULK | FL_MICRON_BUGS, "Micron N25Qx512Ax" }, { 0x4d5444, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "File Abstraction"}, { 0x55aa55, 0x00100000, FL_ERASE_ALL | FL_CAN_4B, "TEST_FLASH" }, { 0xaa55aa, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "EMULATED_FLASH"}, }; struct flash_chip { struct spi_flash_ctrl *ctrl; /* Controller */ struct flash_info info; /* Flash info */ uint32_t tsize; /* Corrected flash size */ uint32_t min_erase_mask; /* Minimum erase size */ bool mode_4b; /* Flash currently in 4b mode */ struct flash_req *cur_req; /* Current request */ void *smart_buf; /* Buffer for smart writes */ struct blocklevel_device bl; }; #ifndef __SKIBOOT__ bool libflash_debug; #endif int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat) { return ct->cmd_rd(ct, CMD_RDSR, false, 0, stat, 1); } static void fl_micron_status(struct spi_flash_ctrl *ct) { uint8_t flst; /* * After a success status on a write or erase, we * need to do that command or some chip variants will * lock */ ct->cmd_rd(ct, CMD_MIC_RDFLST, false, 0, &flst, 1); } /* Synchronous write completion, probably need a yield hook */ int fl_sync_wait_idle(struct spi_flash_ctrl *ct) { uint8_t stat; int rc; /* XXX Add timeout */ for (;;) { rc = fl_read_stat(ct, &stat); if (rc) return rc; if (!(stat & STAT_WIP)) { if (ct->finfo->flags & FL_MICRON_BUGS) fl_micron_status(ct); return 0; } } /* return FLASH_ERR_WIP_TIMEOUT; */ } /* Exported for internal use */ int fl_wren(struct spi_flash_ctrl *ct) { int i, rc; uint8_t stat; /* Some flashes need it to be hammered */ for (i = 0; i < 1000; i++) { rc = ct->cmd_wr(ct, CMD_WREN, false, 0, NULL, 0); if (rc) return rc; rc = fl_read_stat(ct, &stat); if (rc) return rc; if (stat & STAT_WIP) { FL_ERR("LIBFLASH: WREN has WIP status set !\n"); rc = fl_sync_wait_idle(ct); if (rc) return rc; continue; } if (stat & STAT_WEN) return 0; } return FLASH_ERR_WREN_TIMEOUT; } static int flash_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); struct spi_flash_ctrl *ct = c->ctrl; /* XXX Add sanity/bound checking */ /* * If the controller supports read and either we are in 3b mode * or we are in 4b *and* the controller supports it, then do a * high level read. */ if ((!c->mode_4b || ct->set_4b) && ct->read) return ct->read(ct, pos, buf, len); /* Otherwise, go manual if supported */ if (!ct->cmd_rd) return FLASH_ERR_CTRL_CMD_UNSUPPORTED; return ct->cmd_rd(ct, CMD_READ, true, pos, buf, len); } #define COPY_BUFFER_LENGTH 4096 /* * This provides a wrapper around flash_read on ECCed data * len is length of data without ECC attached */ int flash_read_corrected(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len, bool ecc) { struct ecc64 *bufecc; uint32_t copylen; int rc; uint8_t ret; if (!ecc) return flash_read(bl, pos, buf, len); /* Copy the buffer in chunks */ bufecc = malloc(ecc_buffer_size(COPY_BUFFER_LENGTH)); if (!bufecc) return FLASH_ERR_MALLOC_FAILED; while (len > 0) { /* What's left to copy? */ copylen = MIN(len, COPY_BUFFER_LENGTH); /* Read ECCed data from flash */ rc = flash_read(bl, pos, bufecc, ecc_buffer_size(copylen)); if (rc) goto err; /* Extract data from ECCed data */ ret = memcpy_from_ecc(buf, bufecc, copylen); if (ret) { rc = FLASH_ERR_ECC_INVALID; goto err; } /* Update for next copy */ len -= copylen; buf = (uint8_t *)buf + copylen; pos += ecc_buffer_size(copylen); } rc = 0; err: free(bufecc); return rc; } static void fl_get_best_erase(struct flash_chip *c, uint32_t dst, uint32_t size, uint32_t *chunk, uint8_t *cmd) { /* Smaller than 32k, use 4k */ if ((dst & 0x7fff) || (size < 0x8000)) { *chunk = 0x1000; *cmd = CMD_SE; return; } /* Smaller than 64k and 32k is supported, use it */ if ((c->info.flags & FL_ERASE_32K) && ((dst & 0xffff) || (size < 0x10000))) { *chunk = 0x8000; *cmd = CMD_BE32K; return; } /* If 64K is not supported, use whatever smaller size is */ if (!(c->info.flags & FL_ERASE_64K)) { if (c->info.flags & FL_ERASE_32K) { *chunk = 0x8000; *cmd = CMD_BE32K; } else { *chunk = 0x1000; *cmd = CMD_SE; } return; } /* Allright, let's go for 64K */ *chunk = 0x10000; *cmd = CMD_BE; } static int flash_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t size) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); struct spi_flash_ctrl *ct = c->ctrl; uint32_t chunk; uint8_t cmd; int rc; /* Some sanity checking */ if (((dst + size) <= dst) || !size || (dst + size) > c->tsize) return FLASH_ERR_PARM_ERROR; /* Check boundaries fit erase blocks */ if ((dst | size) & c->min_erase_mask) return FLASH_ERR_ERASE_BOUNDARY; FL_DBG("LIBFLASH: Erasing 0x%08x..0%08x...\n", dst, dst + size); /* Use controller erase if supported */ if (ct->erase) return ct->erase(ct, dst, size); /* Allright, loop as long as there's something to erase */ while(size) { /* How big can we make it based on alignent & size */ fl_get_best_erase(c, dst, size, &chunk, &cmd); /* Poke write enable */ rc = fl_wren(ct); if (rc) return rc; /* Send erase command */ rc = ct->cmd_wr(ct, cmd, true, dst, NULL, 0); if (rc) return rc; /* Wait for write complete */ rc = fl_sync_wait_idle(ct); if (rc) return rc; size -= chunk; dst += chunk; } return 0; } int flash_erase_chip(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; int rc; /* XXX TODO: Fallback to using normal erases */ if (!(c->info.flags & (FL_ERASE_CHIP|FL_ERASE_BULK))) return FLASH_ERR_CHIP_ER_NOT_SUPPORTED; FL_DBG("LIBFLASH: Erasing chip...\n"); /* Use controller erase if supported */ if (ct->erase) return ct->erase(ct, 0, 0xffffffff); rc = fl_wren(ct); if (rc) return rc; if (c->info.flags & FL_ERASE_CHIP) rc = ct->cmd_wr(ct, CMD_CE, false, 0, NULL, 0); else rc = ct->cmd_wr(ct, CMD_MIC_BULK_ERASE, false, 0, NULL, 0); if (rc) return rc; /* Wait for write complete */ return fl_sync_wait_idle(ct); } static int fl_wpage(struct flash_chip *c, uint32_t dst, const void *src, uint32_t size) { struct spi_flash_ctrl *ct = c->ctrl; int rc; if (size < 1 || size > 0x100) return FLASH_ERR_BAD_PAGE_SIZE; rc = fl_wren(ct); if (rc) return rc; rc = ct->cmd_wr(ct, CMD_PP, true, dst, src, size); if (rc) return rc; /* Wait for write complete */ return fl_sync_wait_idle(ct); } static int flash_write(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool verify) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); struct spi_flash_ctrl *ct = c->ctrl; uint32_t todo = size; uint32_t d = dst; const void *s = src; uint8_t vbuf[0x100]; int rc; /* Some sanity checking */ if (((dst + size) <= dst) || !size || (dst + size) > c->tsize) return FLASH_ERR_PARM_ERROR; FL_DBG("LIBFLASH: Writing to 0x%08x..0%08x...\n", dst, dst + size); /* * If the controller supports write and either we are in 3b mode * or we are in 4b *and* the controller supports it, then do a * high level write. */ if ((!c->mode_4b || ct->set_4b) && ct->write) { rc = ct->write(ct, dst, src, size); if (rc) return rc; goto writing_done; } /* Otherwise, go manual if supported */ if (!ct->cmd_wr) return FLASH_ERR_CTRL_CMD_UNSUPPORTED; /* Iterate for each page to write */ while(todo) { uint32_t chunk; /* Handle misaligned start */ chunk = 0x100 - (d & 0xff); if (chunk > 0x100) chunk = 0x100; if (chunk > todo) chunk = todo; rc = fl_wpage(c, d, s, chunk); if (rc) return rc; d += chunk; s += chunk; todo -= chunk; } writing_done: if (!verify) return 0; /* Verify */ FL_DBG("LIBFLASH: Verifying...\n"); while(size) { uint32_t chunk; chunk = sizeof(vbuf); if (chunk > size) chunk = size; rc = flash_read(bl, dst, vbuf, chunk); if (rc) return rc; if (memcmp(vbuf, src, chunk)) { FL_ERR("LIBFLASH: Miscompare at 0x%08x\n", dst); return FLASH_ERR_VERIFY_FAILURE; } dst += chunk; src += chunk; size -= chunk; } return 0; } int flash_write_corrected(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len, bool verify, bool ecc) { struct ecc64 *bufecc; uint32_t copylen, copylen_minus_ecc; int rc; uint8_t ret; if (!ecc) return flash_write(bl, pos, buf, len, verify); /* Copy the buffer in chunks */ bufecc = malloc(ecc_buffer_size(COPY_BUFFER_LENGTH)); if (!bufecc) return FLASH_ERR_MALLOC_FAILED; while (len > 0) { /* What's left to copy? */ copylen = MIN(len, COPY_BUFFER_LENGTH); copylen_minus_ecc = ecc_buffer_size_minus_ecc(copylen); /* Add the ecc byte to the data */ ret = memcpy_to_ecc(bufecc, buf, copylen_minus_ecc); if (ret) { rc = FLASH_ERR_ECC_INVALID; goto err; } /* Write ECCed data to the flash */ rc = flash_write(bl, pos, bufecc, copylen, verify); if (rc) goto err; /* Update for next copy */ len -= copylen_minus_ecc; buf = (uint8_t *)buf + copylen_minus_ecc; pos += copylen; } rc = 0; err: free(bufecc); return rc; } enum sm_comp_res { sm_no_change, sm_need_write, sm_need_erase, }; static enum sm_comp_res flash_smart_comp(struct flash_chip *c, const void *src, uint32_t offset, uint32_t size) { uint8_t *b = c->smart_buf + offset; const uint8_t *s = src; bool is_same = true; uint32_t i; /* SRC DEST NEED_ERASE * 0 1 0 * 1 1 0 * 0 0 0 * 1 0 1 */ for (i = 0; i < size; i++) { /* Any bit need to be set, need erase */ if (s[i] & ~b[i]) return sm_need_erase; if (is_same && (b[i] != s[i])) is_same = false; } return is_same ? sm_no_change : sm_need_write; } static int flash_smart_write(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); uint32_t er_size = c->min_erase_mask + 1; uint32_t end = dst + size; int rc; /* Some sanity checking */ if (end <= dst || !size || end > c->tsize) { FL_DBG("LIBFLASH: Smart write param error\n"); return FLASH_ERR_PARM_ERROR; } FL_DBG("LIBFLASH: Smart writing to 0x%08x..0%08x...\n", dst, dst + size); /* As long as we have something to write ... */ while(dst < end) { uint32_t page, off, chunk; enum sm_comp_res sr; /* Figure out which erase page we are in and read it */ page = dst & ~c->min_erase_mask; off = dst & c->min_erase_mask; FL_DBG("LIBFLASH: reading page 0x%08x..0x%08x...", page, page + er_size); rc = flash_read(bl, page, c->smart_buf, er_size); if (rc) { FL_DBG(" error %d!\n", rc); return rc; } /* Locate the chunk of data we are working on */ chunk = er_size - off; if (size < chunk) chunk = size; /* Compare against what we are writing and ff */ sr = flash_smart_comp(c, src, off, chunk); switch(sr) { case sm_no_change: /* Identical, skip it */ FL_DBG(" same !\n"); break; case sm_need_write: /* Just needs writing over */ FL_DBG(" need write !\n"); rc = flash_write(bl, dst, src, chunk, true); if (rc) { FL_DBG("LIBFLASH: Write error %d !\n", rc); return rc; } break; case sm_need_erase: FL_DBG(" need erase !\n"); rc = flash_erase(bl, page, er_size); if (rc) { FL_DBG("LIBFLASH: erase error %d !\n", rc); return rc; } /* Then update the portion of the buffer and write the block */ memcpy(c->smart_buf + off, src, chunk); rc = flash_write(bl, page, c->smart_buf, er_size, true); if (rc) { FL_DBG("LIBFLASH: write error %d !\n", rc); return rc; } break; } dst += chunk; src += chunk; size -= chunk; } return 0; } int flash_smart_write_corrected(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool ecc) { struct ecc64 *buf; int rc; if (!ecc) return flash_smart_write(bl, dst, src, size); buf = malloc(ecc_buffer_size(size)); if (!buf) return FLASH_ERR_MALLOC_FAILED; rc = memcpy_to_ecc(buf, src, size); if (rc) { rc = FLASH_ERR_ECC_INVALID; goto out; } rc = flash_smart_write(bl, dst, buf, ecc_buffer_size(size)); out: free(buf); return rc; } static int fl_chip_id(struct spi_flash_ctrl *ct, uint8_t *id_buf, uint32_t *id_size) { int rc; uint8_t stat; /* Check initial status */ rc = fl_read_stat(ct, &stat); if (rc) return rc; /* If stuck writing, wait for idle */ if (stat & STAT_WIP) { FL_ERR("LIBFLASH: Flash in writing state ! Waiting...\n"); rc = fl_sync_wait_idle(ct); if (rc) return rc; } else FL_DBG("LIBFLASH: Init status: %02x\n", stat); /* Fallback to get ID manually */ rc = ct->cmd_rd(ct, CMD_RDID, false, 0, id_buf, 3); if (rc) return rc; *id_size = 3; return 0; } static int flash_identify(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; const struct flash_info *info = NULL; uint32_t iid, id_size; #define MAX_ID_SIZE 16 uint8_t id[MAX_ID_SIZE]; int rc, i; if (ct->chip_id) { /* High level controller interface */ id_size = MAX_ID_SIZE; rc = ct->chip_id(ct, id, &id_size); } else rc = fl_chip_id(ct, id, &id_size); if (rc) return rc; if (id_size < 3) return FLASH_ERR_CHIP_UNKNOWN; /* Convert to a dword for lookup */ iid = id[0]; iid = (iid << 8) | id[1]; iid = (iid << 8) | id[2]; FL_DBG("LIBFLASH: Flash ID: %02x.%02x.%02x (%06x)\n", id[0], id[1], id[2], iid); /* Lookup in flash_info */ for (i = 0; i < ARRAY_SIZE(flash_info); i++) { info = &flash_info[i]; if (info->id == iid) break; } if (!info || info->id != iid) return FLASH_ERR_CHIP_UNKNOWN; c->info = *info; c->tsize = info->size; ct->finfo = &c->info; /* * Let controller know about our settings and possibly * override them */ if (ct->setup) { rc = ct->setup(ct, &c->tsize); if (rc) return rc; } /* Calculate min erase granularity */ if (c->info.flags & FL_ERASE_4K) c->min_erase_mask = 0xfff; else if (c->info.flags & FL_ERASE_32K) c->min_erase_mask = 0x7fff; else if (c->info.flags & FL_ERASE_64K) c->min_erase_mask = 0xffff; else { /* No erase size ? oops ... */ FL_ERR("LIBFLASH: No erase sizes !\n"); return FLASH_ERR_CTRL_CONFIG_MISMATCH; } FL_DBG("LIBFLASH: Found chip %s size %dM erase granule: %dK\n", c->info.name, c->tsize >> 20, (c->min_erase_mask + 1) >> 10); return 0; } static int flash_set_4b(struct flash_chip *c, bool enable) { struct spi_flash_ctrl *ct = c->ctrl; int rc; /* Don't have low level interface, assume all is well */ if (!ct->cmd_wr) return 0; /* Some flash chips want this */ rc = fl_wren(ct); if (rc) { FL_ERR("LIBFLASH: Error %d enabling write for set_4b\n", rc); /* Ignore the error & move on (could be wrprotect chip) */ } /* Ignore error in case chip is write protected */ return ct->cmd_wr(ct, enable ? CMD_EN4B : CMD_EX4B, false, 0, NULL, 0); } int flash_force_4b_mode(struct flash_chip *c, bool enable_4b) { struct spi_flash_ctrl *ct = c->ctrl; int rc = FLASH_ERR_4B_NOT_SUPPORTED; /* * We only allow force 4b if both controller and flash do 4b * as this is mainly used if a 3rd party tries to directly * access a direct mapped read region */ if (enable_4b && !((c->info.flags & FL_CAN_4B) && ct->set_4b)) return rc; /* Only send to flash directly on controllers that implement * the low level callbacks */ if (ct->cmd_wr) { rc = flash_set_4b(c, enable_4b); if (rc) return rc; } /* Then inform the controller */ if (ct->set_4b) rc = ct->set_4b(ct, enable_4b); return rc; } static int flash_configure(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; int rc; /* Crop flash size if necessary */ if (c->tsize > 0x01000000 && !(c->info.flags & FL_CAN_4B)) { FL_ERR("LIBFLASH: Flash chip cropped to 16M, no 4b mode\n"); c->tsize = 0x01000000; } /* If flash chip > 16M, enable 4b mode */ if (c->tsize > 0x01000000) { FL_DBG("LIBFLASH: Flash >16MB, enabling 4B mode...\n"); /* Set flash to 4b mode if we can */ if (ct->cmd_wr) { rc = flash_set_4b(c, true); if (rc) { FL_ERR("LIBFLASH: Failed to set flash 4b mode\n"); return rc; } } /* Set controller to 4b mode if supported */ if (ct->set_4b) { FL_DBG("LIBFLASH: Enabling controller 4B mode...\n"); rc = ct->set_4b(ct, true); if (rc) { FL_ERR("LIBFLASH: Failed" " to set controller 4b mode\n"); return rc; } } } else { FL_DBG("LIBFLASH: Flash <=16MB, disabling 4B mode...\n"); /* * If flash chip supports 4b mode, make sure we disable * it in case it was left over by the previous user */ if (c->info.flags & FL_CAN_4B) { rc = flash_set_4b(c, false); if (rc) { FL_ERR("LIBFLASH: Failed to" " clear flash 4b mode\n"); return rc; } } /* Set controller to 3b mode if mode switch is supported */ if (ct->set_4b) { FL_DBG("LIBFLASH: Disabling controller 4B mode...\n"); rc = ct->set_4b(ct, false); if (rc) { FL_ERR("LIBFLASH: Failed to" " clear controller 4b mode\n"); return rc; } } } return 0; } static int flash_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size, uint32_t *erase_granule) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); if (name) *name = c->info.name; if (total_size) *total_size = c->tsize; if (erase_granule) *erase_granule = c->min_erase_mask + 1; return 0; } int flash_init(struct spi_flash_ctrl *ctrl, struct blocklevel_device **bl, struct flash_chip **flash_chip) { struct flash_chip *c; int rc; if (!bl) return FLASH_ERR_PARM_ERROR; *bl = NULL; c = malloc(sizeof(struct flash_chip)); if (!c) return FLASH_ERR_MALLOC_FAILED; memset(c, 0, sizeof(*c)); c->ctrl = ctrl; rc = flash_identify(c); if (rc) { FL_ERR("LIBFLASH: Flash identification failed\n"); goto bail; } c->smart_buf = malloc(c->min_erase_mask + 1); if (!c->smart_buf) { FL_ERR("LIBFLASH: Failed to allocate smart buffer !\n"); rc = FLASH_ERR_MALLOC_FAILED; goto bail; } rc = flash_configure(c); if (rc) FL_ERR("LIBFLASH: Flash configuration failed\n"); bail: if (rc) { free(c); return rc; } c->bl.read = &flash_read; c->bl.write = &flash_smart_write; c->bl.erase = &flash_erase; c->bl.get_info = &flash_get_info; c->bl.erase_mask = c->min_erase_mask; c->bl.flags = WRITE_NEED_ERASE; *bl = &(c->bl); if (flash_chip) *flash_chip = c; return 0; } void flash_exit(struct blocklevel_device *bl) { /* XXX Make sure we are idle etc... */ if (bl) free(container_of(bl, struct flash_chip, bl)); } void flash_exit_close(struct blocklevel_device *bl, void (*close)(struct spi_flash_ctrl *ctrl)) { if (bl) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); close(c->ctrl); free(c); } } skiboot-skiboot-5.1.13/libflash/libflash.h000066400000000000000000000064101265204436200204510ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBFLASH_H #define __LIBFLASH_H #include #include #include /* API status/return: * * <0 = flash controller errors passed through, * 0 = success * >0 = libflash error */ #include #ifdef __SKIBOOT__ #include #define FL_INF(fmt...) do { prlog(PR_INFO, fmt); } while(0) #define FL_DBG(fmt...) do { prlog(PR_DEBUG, fmt); } while(0) #define FL_ERR(fmt...) do { prlog(PR_ERR, fmt); } while(0) #else #include extern bool libflash_debug; #define FL_DBG(fmt...) do { if (libflash_debug) printf(fmt); } while(0) #define FL_INF(fmt...) do { printf(fmt); } while(0) #define FL_ERR(fmt...) do { printf(fmt); } while(0) #endif /* Flash chip, opaque */ struct flash_chip; struct spi_flash_ctrl; int flash_init(struct spi_flash_ctrl *ctrl, struct blocklevel_device **bl, struct flash_chip **flash_chip); void flash_exit(struct blocklevel_device *bl); /* * Function which till call close on the underlying struct spi_flash_ctrl */ void flash_exit_close(struct blocklevel_device *bl, void (*close)(struct spi_flash_ctrl *ctrl)); /* libflash sets the 4b mode automatically based on the flash * size and controller capabilities but it can be overriden */ int flash_force_4b_mode(struct flash_chip *c, bool enable_4b); /* * This provides a wapper around flash_read() on ECCed data. All params are * the same as to flash_read(). Not passing true in ecc is akin to calling * flash_read() directly. * * len is length of data without ecc attached therefore this will read beyond * pos + len. */ int flash_read_corrected(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len, bool ecc); /* * This provides a wrapper around flash_write() on ECCed data. All params are * the same as to flash_write(). Not passing true in ecc is akin to calling * flash_write() directly. * * size is length of data without ECC attached therefore this will write beyond * dst + size. */ int flash_write_corrected(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool verify, bool ecc); /* * This provides a wrapper around flash_smart_write() on ECCed data. All * params are the same as to flash_smart_write(). Not passing true in ecc is * akin to calling flash_smart_write() directly. * * size is length of data without ECC attached therefore this will write beyond * dst + size. */ int flash_smart_write_corrected(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool ecc); /* chip erase may not be supported by all chips/controllers, get ready * for FLASH_ERR_CHIP_ER_NOT_SUPPORTED */ int flash_erase_chip(struct flash_chip *c); #endif /* __LIBFLASH_H */ skiboot-skiboot-5.1.13/libflash/test/000077500000000000000000000000001265204436200174725ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libflash/test/Makefile.check000066400000000000000000000021371265204436200222110ustar00rootroot00000000000000# -*-Makefile-*- LIBFLASH_TEST := libflash/test/test-flash libflash/test/test-ecc libflash/test/test-blocklevel LCOV_EXCLUDE += $(LIBFLASH_TEST:%=%.c) check: $(LIBFLASH_TEST:%=%-check) $(CORE_TEST:%=%-gcov-run) coverage: $(LIBFLASH_TEST:%=%-gcov-run) $(LIBFLASH_TEST:%=%-gcov-run) : %-run: % $(call Q, TEST-COVERAGE ,$< , $<) $(LIBFLASH_TEST:%=%-check) : %-check: % $(call Q, RUN-TEST ,$(VALGRIND) $<, $<) libflash/test/stubs.o: libflash/test/stubs.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -g -c -o $@ $<, $<) $(LIBFLASH_TEST) : libflash/test/stubs.o libflash/libflash.c libflash/ecc.c libflash/blocklevel.c $(LIBFLASH_TEST) : % : %.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include -I . -o $@ $< libflash/test/stubs.o, $<) $(LIBFLASH_TEST:%=%-gcov): %-gcov : %.c % $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -fprofile-arcs -ftest-coverage -lgcov -O0 -g -I include -I . -o $@ $< libflash/test/stubs.o, $<) -include $(wildcard libflash/test/*.d) clean: libflash-test-clean libflash-test-clean: $(RM) libflash/test/*.o $(LIBFLASH_TEST) $(RM) libflash/test/*.d $(RM) libflash/test/*-gcov skiboot-skiboot-5.1.13/libflash/test/stubs.c000066400000000000000000000011611265204436200207750ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Stubs for libflash test */ skiboot-skiboot-5.1.13/libflash/test/test-blocklevel.c000066400000000000000000000116731265204436200227450ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "../ecc.c" #include "../blocklevel.c" #define __unused __attribute__((unused)) #define ERR(fmt...) fprintf(stderr, fmt) int main(void) { int i; struct blocklevel_device bl_mem = { 0 }; struct blocklevel_device *bl = &bl_mem; if (blocklevel_ecc_protect(bl, 0, 0x1000)) { ERR("Failed to blocklevel_ecc_protect!\n"); return 1; } /* 0x1000 -> 0x3000 should remain unprotected */ if (blocklevel_ecc_protect(bl, 0x3000, 0x1000)) { ERR("Failed to blocklevel_ecc_protect(0x3000, 0x1000)\n"); return 1; } /* Zero length protection */ if (!blocklevel_ecc_protect(bl, 0x4000, 0)) { ERR("Shouldn't have succeeded blocklevel_ecc_protect(0x4000, 0)\n"); return 1; } /* Minimum creatable size */ if (blocklevel_ecc_protect(bl, 0x4000, BYTES_PER_ECC)) { ERR("Failed to blocklevel_ecc_protect(0x4000, BYTES_PER_ECC)\n"); return 1; } /* Deal with overlapping protections */ if (!blocklevel_ecc_protect(bl, 0x100, 0x1000)) { ERR("Shouldn't have succeeded blocklevel_ecc_protect(0x100, 0x1000)\n"); return 1; } /* Deal with protections greater than max size */ if (!blocklevel_ecc_protect(bl, 0, 0xFFFFFFFF)) { ERR("Failed to blocklevel_ecc_protect(0, 0xFFFFFFFF)\n"); return 1; } if (ecc_protected(bl, 0, 1) != 1) { ERR("Invaid result for ecc_protected(0, 1)\n"); return 1; } if (ecc_protected(bl, 0, 0x1000) != 1) { ERR("Invalid result for ecc_protected(0, 0x1000)\n"); return 1; } if (ecc_protected(bl, 0x100, 0x100) != 1) { ERR("Invalid result for ecc_protected(0x0100, 0x100)\n"); return 1; } if (ecc_protected(bl, 0x1000, 0) != 0) { ERR("Invalid result for ecc_protected(0x1000, 0)\n"); return 1; } if (ecc_protected(bl, 0x1000, 0x1000) != 0) { ERR("Invalid result for ecc_protected(0x1000, 0x1000)\n"); return 1; } if (ecc_protected(bl, 0x1000, 0x100) != 0) { ERR("Invalid result for ecc_protected(0x1000, 0x100)\n"); return 1; } if (ecc_protected(bl, 0x2000, 0) != 0) { ERR("Invalid result for ecc_protected(0x2000, 0)\n"); return 1; } if (ecc_protected(bl, 0x4000, 1) != 1) { ERR("Invalid result for ecc_protected(0x4000, 1)\n"); return 1; } /* Check for asking for a region with mixed protection */ if (ecc_protected(bl, 0x100, 0x2000) != -1) { ERR("Invalid result for ecc_protected(0x100, 0x2000)\n"); return 1; } /* Test the auto extending of regions */ if (blocklevel_ecc_protect(bl, 0x5000, 0x100)) { ERR("Failed to blocklevel_ecc_protect(0x5000, 0x100)\n"); return 1; } if (blocklevel_ecc_protect(bl, 0x5100, 0x100)) { ERR("Failed to blocklevel_ecc_protect(0x5100, 0x100)\n"); return 1; } if (blocklevel_ecc_protect(bl, 0x5200, 0x100)) { ERR("Failed to blocklevel_ecc_protect(0x5200, 0x100)\n"); return 1; } if (ecc_protected(bl, 0x5120, 0x10) != 1) { ERR("Invalid result for ecc_protected(0x5120, 0x10)\n"); return 1; } if (blocklevel_ecc_protect(bl, 0x4900, 0x100)) { ERR("Failed to blocklevel_ecc_protected(0x4900, 0x100)\n"); return 1; } if (ecc_protected(bl, 0x4920, 0x10) != 1) { ERR("Invalid result for ecc_protected(0x4920, 0x10)\n"); return 1; } if (!blocklevel_ecc_protect(bl, 0x5290, 0x10)) { ERR("Shouldn't have been able to blocklevel_ecc_protect(0x5290, 0x10)\n"); return 1; } /* Test the auto extending of regions */ if (blocklevel_ecc_protect(bl, 0x6000, 0x100)) { ERR("Failed to blocklevel_ecc_protect(0x6000, 0x100)\n"); return 1; } if (blocklevel_ecc_protect(bl, 0x6200, 0x100)) { ERR("Failed to blocklevel_ecc_protect(0x6200, 0x100)\n"); return 1; } /*This addition should cause this one to merge the other two together*/ if (blocklevel_ecc_protect(bl, 0x6100, 0x100)) { ERR("Failed to blocklevel_ecc_protect(0x6100, 0x100)\n"); return 1; } /* Check that the region merging works */ for (i = 0; i < bl->ecc_prot.n_prot - 1; i++) { if (bl->ecc_prot.prot[i].start + bl->ecc_prot.prot[i].len == bl->ecc_prot.prot[i + 1].start || bl->ecc_prot.prot[i + 1].start + bl->ecc_prot.prot[i + 1].len == bl->ecc_prot.prot[i].start) { ERR("Problem with protection range merge code, region starting at 0x%08x for 0x%08x appears " "to touch region 0x%08x for 0x%08x\n", bl->ecc_prot.prot[i].start, bl->ecc_prot.prot[i].len, bl->ecc_prot.prot[i + 1].start, bl->ecc_prot.prot[i + 1].len); return 1; } } return 0; } skiboot-skiboot-5.1.13/libflash/test/test-ecc.c000066400000000000000000000671631265204436200213620ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "../ecc.c" #define __unused __attribute__((unused)) #define ERR(fmt...) fprintf(stderr, fmt) #define NUM_ECC_ROWS 320 /* * Note this data is big endian as this is what the ecc code expects. * The ECC code returns IBM bit numbers assuming the word was in CPU * endian! */ /* 8 data bytes 1 ecc byte per row */ struct ecc64 ecc_data[] = { { 0xfeffffffffffffff, 0x00 }, /* This row will have ecc correct bit 63 */ { 0xfdffffffffffffff, 0x00 }, /* This row will have ecc correct bit 62 */ { 0xfbffffffffffffff, 0x00 }, /* This row will have ecc correct bit 61 */ { 0xf7ffffffffffffff, 0x00 }, /* This row will have ecc correct bit 60 */ { 0xefffffffffffffff, 0x00 }, /* This row will have ecc correct bit 59 */ { 0xdfffffffffffffff, 0x00 }, /* This row will have ecc correct bit 58 */ { 0xbfffffffffffffff, 0x00 }, /* This row will have ecc correct bit 57 */ { 0x7fffffffffffffff, 0x00 }, /* This row will have ecc correct bit 56 */ { 0xfffeffffffffffff, 0x00 }, /* This row will have ecc correct bit 55 */ { 0xfffdffffffffffff, 0x00 }, /* This row will have ecc correct bit 54 */ { 0xfffbffffffffffff, 0x00 }, /* This row will have ecc correct bit 53 */ { 0xfff7ffffffffffff, 0x00 }, /* This row will have ecc correct bit 52 */ { 0xffefffffffffffff, 0x00 }, /* This row will have ecc correct bit 51 */ { 0xffdfffffffffffff, 0x00 }, /* This row will have ecc correct bit 50 */ { 0xffbfffffffffffff, 0x00 }, /* This row will have ecc correct bit 49 */ { 0xff7fffffffffffff, 0x00 }, /* This row will have ecc correct bit 48 */ { 0xfffffeffffffffff, 0x00 }, /* This row will have ecc correct bit 47 */ { 0xfffffdffffffffff, 0x00 }, /* This row will have ecc correct bit 46 */ { 0xfffffbffffffffff, 0x00 }, /* This row will have ecc correct bit 45 */ { 0xfffff7ffffffffff, 0x00 }, /* This row will have ecc correct bit 44 */ { 0xffffefffffffffff, 0x00 }, /* This row will have ecc correct bit 43 */ { 0xffffdfffffffffff, 0x00 }, /* This row will have ecc correct bit 42 */ { 0xffffbfffffffffff, 0x00 }, /* This row will have ecc correct bit 41 */ { 0xffff7fffffffffff, 0x00 }, /* This row will have ecc correct bit 40 */ { 0xfffffffeffffffff, 0x00 }, /* This row will have ecc correct bit 39 */ { 0xfffffffdffffffff, 0x00 }, /* This row will have ecc correct bit 38 */ { 0xfffffffbffffffff, 0x00 }, /* This row will have ecc correct bit 37 */ { 0xfffffff7ffffffff, 0x00 }, /* This row will have ecc correct bit 36 */ { 0xffffffefffffffff, 0x00 }, /* This row will have ecc correct bit 35 */ { 0xffffffdfffffffff, 0x00 }, /* This row will have ecc correct bit 34 */ { 0xffffffbfffffffff, 0x00 }, /* This row will have ecc correct bit 33 */ { 0xffffff7fffffffff, 0x00 }, /* This row will have ecc correct bit 32 */ { 0xfffffffffeffffff, 0x00 }, /* This row will have ecc correct bit 31 */ { 0xfffffffffdffffff, 0x00 }, /* This row will have ecc correct bit 30 */ { 0xfffffffffbffffff, 0x00 }, /* This row will have ecc correct bit 29 */ { 0xfffffffff7ffffff, 0x00 }, /* This row will have ecc correct bit 28 */ { 0xffffffffefffffff, 0x00 }, /* This row will have ecc correct bit 27 */ { 0xffffffffdfffffff, 0x00 }, /* This row will have ecc correct bit 26 */ { 0xffffffffbfffffff, 0x00 }, /* This row will have ecc correct bit 25 */ { 0xffffffff7fffffff, 0x00 }, /* This row will have ecc correct bit 24 */ { 0xfffffffffffeffff, 0x00 }, /* This row will have ecc correct bit 23 */ { 0xfffffffffffdffff, 0x00 }, /* This row will have ecc correct bit 22 */ { 0xfffffffffffbffff, 0x00 }, /* This row will have ecc correct bit 21 */ { 0xfffffffffff7ffff, 0x00 }, /* This row will have ecc correct bit 20 */ { 0xffffffffffefffff, 0x00 }, /* This row will have ecc correct bit 19 */ { 0xffffffffffdfffff, 0x00 }, /* This row will have ecc correct bit 18 */ { 0xffffffffffbfffff, 0x00 }, /* This row will have ecc correct bit 17 */ { 0xffffffffff7fffff, 0x00 }, /* This row will have ecc correct bit 16 */ { 0xfffffffffffffeff, 0x00 }, /* This row will have ecc correct bit 15 */ { 0xfffffffffffffdff, 0x00 }, /* This row will have ecc correct bit 14 */ { 0xfffffffffffffbff, 0x00 }, /* This row will have ecc correct bit 13 */ { 0xfffffffffffff7ff, 0x00 }, /* This row will have ecc correct bit 12 */ { 0xffffffffffffefff, 0x00 }, /* This row will have ecc correct bit 11 */ { 0xffffffffffffdfff, 0x00 }, /* This row will have ecc correct bit 10 */ { 0xffffffffffffbfff, 0x00 }, /* This row will have ecc correct bit 9 */ { 0xffffffffffff7fff, 0x00 }, /* This row will have ecc correct bit 8 */ { 0xfffffffffffffffe, 0x00 }, /* This row will have ecc correct bit 7 */ { 0xfffffffffffffffd, 0x00 }, /* This row will have ecc correct bit 6 */ { 0xfffffffffffffffb, 0x00 }, /* This row will have ecc correct bit 5 */ { 0xfffffffffffffff7, 0x00 }, /* This row will have ecc correct bit 4 */ { 0xffffffffffffffef, 0x00 }, /* This row will have ecc correct bit 3 */ { 0xffffffffffffffdf, 0x00 }, /* This row will have ecc correct bit 2 */ { 0xffffffffffffffbf, 0x00 }, /* This row will have ecc correct bit 1 */ { 0xffffffffffffff7f, 0x00 }, /* This row will have ecc correct bit 0 */ /* * 'Randomised' input into eccgenerate 0x54f7c5d1 was seeded to rand() * Note: eccgenerate from skiboot commit 6cfaa3ba1015c6ac9cc4a06f878b4289022cff54 * was used to generate these ecc numbers */ { 0x29d87c7c8ab7d46d, 0xb9 }, /* Use this row to check eccgenerate() */ { 0x9064174098381641, 0x3b }, /* Use this row to check eccgenerate() */ { 0x77fd7d2fc7d22154, 0xe4 }, /* Use this row to check eccgenerate() */ { 0x6b02ba39b64a6168, 0xbf }, /* Use this row to check eccgenerate() */ { 0x68fa9c633eef0544, 0x2a }, /* Use this row to check eccgenerate() */ { 0xe814b258b3f92e55, 0x35 }, /* Use this row to check eccgenerate() */ { 0xc3e2bd658db4db6d, 0xda }, /* Use this row to check eccgenerate() */ { 0xe1dd487b6209876a, 0x45 }, /* Use this row to check eccgenerate() */ { 0x309f9e6b91831433, 0xe4 }, /* Use this row to check eccgenerate() */ { 0xd8b77d39f4d66410, 0x6c }, /* Use this row to check eccgenerate() */ { 0x83ba293cf30a9e6a, 0xc9 }, /* Use this row to check eccgenerate() */ { 0x3aeaef79af97ec1a, 0x09 }, /* Use this row to check eccgenerate() */ { 0xa90ef431e4778c43, 0x91 }, /* Use this row to check eccgenerate() */ { 0xa74bbf1e6b6fda00, 0xc5 }, /* Use this row to check eccgenerate() */ { 0x67b5a872efa57c30, 0xb9 }, /* Use this row to check eccgenerate() */ { 0x795d511e3605ff67, 0x03 }, /* Use this row to check eccgenerate() */ { 0xce3d1529918d256f, 0x36 }, /* Use this row to check eccgenerate() */ { 0x586047430ac2685e, 0xab }, /* Use this row to check eccgenerate() */ { 0xc00cca46463b9358, 0x42 }, /* Use this row to check eccgenerate() */ { 0x842a991cc362017d, 0xb2 }, /* Use this row to check eccgenerate() */ { 0x765c30522807672a, 0x26 }, /* Use this row to check eccgenerate() */ { 0xb5bb42186c3f4b75, 0x2b }, /* Use this row to check eccgenerate() */ { 0xce48d25f393fee37, 0x90 }, /* Use this row to check eccgenerate() */ { 0xcbc2026b96998b13, 0x40 }, /* Use this row to check eccgenerate() */ { 0x8b70f023ffe7704b, 0x23 }, /* Use this row to check eccgenerate() */ { 0xf2f20e36a37a8024, 0x19 }, /* Use this row to check eccgenerate() */ { 0x52126d3f0e2b1a60, 0xa0 }, /* Use this row to check eccgenerate() */ { 0xf2a2a6232dddfe2f, 0xc4 }, /* Use this row to check eccgenerate() */ { 0x984cd930fb206171, 0xa5 }, /* Use this row to check eccgenerate() */ { 0xeac6dd2199ee6542, 0xea }, /* Use this row to check eccgenerate() */ { 0xd0f3642aff018223, 0x3b }, /* Use this row to check eccgenerate() */ { 0x908fa71263242f40, 0x0a }, /* Use this row to check eccgenerate() */ { 0x6de6971e9e317a53, 0xa6 }, /* Use this row to check eccgenerate() */ { 0xe46c0d2ce8efee55, 0xa4 }, /* Use this row to check eccgenerate() */ { 0xab52f0522df36165, 0x06 }, /* Use this row to check eccgenerate() */ { 0x55fac80f6997a648, 0x9a }, /* Use this row to check eccgenerate() */ { 0xd5d6f13d21af2025, 0xed }, /* Use this row to check eccgenerate() */ { 0x5bee0e5d0bb60b28, 0x66 }, /* Use this row to check eccgenerate() */ { 0xa14f973ba41fc41d, 0xa8 }, /* Use this row to check eccgenerate() */ { 0xa307356926b11148, 0x5a }, /* Use this row to check eccgenerate() */ { 0xc92b926c2cc0875f, 0x7e }, /* Use this row to check eccgenerate() */ { 0x3aeba13f95fa431f, 0x92 }, /* Use this row to check eccgenerate() */ { 0xc2d7424f1b3eff2b, 0xe6 }, /* Use this row to check eccgenerate() */ { 0x165f601d2c8e4863, 0x2b }, /* Use this row to check eccgenerate() */ { 0xc67cae255a241c00, 0x78 }, /* Use this row to check eccgenerate() */ { 0x5a269e2300263e3f, 0x07 }, /* Use this row to check eccgenerate() */ { 0x634a6d7f96701350, 0xe9 }, /* Use this row to check eccgenerate() */ { 0x34a28d23eab54536, 0xd2 }, /* Use this row to check eccgenerate() */ { 0xd3a5340cd130051e, 0x48 }, /* Use this row to check eccgenerate() */ { 0xfe236703190f9b4f, 0x7e }, /* Use this row to check eccgenerate() */ { 0x82a641187ef8245f, 0x20 }, /* Use this row to check eccgenerate() */ { 0xa0a74504541e3013, 0xc7 }, /* Use this row to check eccgenerate() */ { 0x5fd43b3b577d3356, 0x85 }, /* Use this row to check eccgenerate() */ { 0xfb9cf773fb955461, 0x06 }, /* Use this row to check eccgenerate() */ { 0x214766290024d376, 0x80 }, /* Use this row to check eccgenerate() */ { 0x2de45a569ea42c5d, 0x22 }, /* Use this row to check eccgenerate() */ { 0x349f707cea72f815, 0xf3 }, /* Use this row to check eccgenerate() */ { 0x05b1f74167cffc15, 0xe9 }, /* Use this row to check eccgenerate() */ { 0x945d4579f676b34b, 0x63 }, /* Use this row to check eccgenerate() */ { 0x519bcf4b1b10585f, 0x47 }, /* Use this row to check eccgenerate() */ { 0x1b36961e5adaf31e, 0x25 }, /* Use this row to check eccgenerate() */ { 0xf04a076fabc16d6f, 0x20 }, /* Use this row to check eccgenerate() */ { 0x9577b3257e80031e, 0xef }, /* Use this row to check eccgenerate() */ { 0x4fb1083c24ed9412, 0x97 }, /* Use this row to check eccgenerate() */ { 0x3dfc2f62681de831, 0x1f }, /* Use this row to check eccgenerate() */ { 0xe7150d114ed56f3f, 0x10 }, /* Use this row to check eccgenerate() */ { 0xa2f39f52bfa2717a, 0x40 }, /* Use this row to check eccgenerate() */ { 0x1720a55087bd5215, 0xb3 }, /* Use this row to check eccgenerate() */ { 0x8253a77601c8db0d, 0x45 }, /* Use this row to check eccgenerate() */ { 0x01ecae0412bd9c44, 0x5f }, /* Use this row to check eccgenerate() */ { 0xb161c921a39a0d20, 0x51 }, /* Use this row to check eccgenerate() */ { 0x8d0d06362ed0095b, 0x94 }, /* Use this row to check eccgenerate() */ { 0x969f0671e5003a1e, 0x9b }, /* Use this row to check eccgenerate() */ { 0xdb77ed6992befd77, 0x63 }, /* Use this row to check eccgenerate() */ { 0xadce55572afd4b6a, 0x3e }, /* Use this row to check eccgenerate() */ { 0x84d73f092c13bd35, 0x50 }, /* Use this row to check eccgenerate() */ { 0xd7d42a25c804ec75, 0x05 }, /* Use this row to check eccgenerate() */ { 0x4685ef1374224778, 0x72 }, /* Use this row to check eccgenerate() */ { 0x980fdc0a6d4cde4a, 0x9d }, /* Use this row to check eccgenerate() */ { 0xd569c67c9636f84f, 0x81 }, /* Use this row to check eccgenerate() */ { 0xe40b680fd60b0c6d, 0x2c }, /* Use this row to check eccgenerate() */ { 0x95ae7d67bc7fd30d, 0x72 }, /* Use this row to check eccgenerate() */ { 0x433d262386ff0762, 0xf4 }, /* Use this row to check eccgenerate() */ { 0x87c7e36facce2238, 0x5a }, /* Use this row to check eccgenerate() */ { 0xbf8bbf7cc590cd19, 0xe0 }, /* Use this row to check eccgenerate() */ { 0x682bdb3988b39274, 0x4f }, /* Use this row to check eccgenerate() */ { 0xb7839c4f70ed881e, 0x6b }, /* Use this row to check eccgenerate() */ { 0x55eec23cf538e16f, 0x72 }, /* Use this row to check eccgenerate() */ { 0x87f7de674d23a340, 0xb4 }, /* Use this row to check eccgenerate() */ { 0x7720ef2a3066b026, 0x7c }, /* Use this row to check eccgenerate() */ { 0x5d796d5c34c6343f, 0x5e }, /* Use this row to check eccgenerate() */ { 0xfcca2035fbf72e34, 0xc6 }, /* Use this row to check eccgenerate() */ { 0x6f1a762c344e9801, 0x87 }, /* Use this row to check eccgenerate() */ { 0xa19a764c43501049, 0x35 }, /* Use this row to check eccgenerate() */ { 0xd9860819072a5237, 0x6a }, /* Use this row to check eccgenerate() */ { 0xdd355e2477043d49, 0x2d }, /* Use this row to check eccgenerate() */ { 0x33841057bd927028, 0xaa }, /* Use this row to check eccgenerate() */ { 0x4392780a73e4db0b, 0xfa }, /* Use this row to check eccgenerate() */ { 0x1fb3fe4377c1367a, 0x47 }, /* Use this row to check eccgenerate() */ { 0x3c520414ca595c7a, 0x58 }, /* Use this row to check eccgenerate() */ { 0x520def6ede3ebe40, 0xac }, /* Use this row to check eccgenerate() */ { 0x4e2c475fa57ddf4d, 0x5c }, /* Use this row to check eccgenerate() */ { 0x9ab6c03d09918b3e, 0x95 }, /* Use this row to check eccgenerate() */ { 0x56b42e7fa31a0a1c, 0x5d }, /* Use this row to check eccgenerate() */ { 0xd480ba4222ae9f25, 0x87 }, /* Use this row to check eccgenerate() */ { 0x5674d464cdd41d2a, 0xc7 }, /* Use this row to check eccgenerate() */ { 0xc8cc4c5e31fa271f, 0x6e }, /* Use this row to check eccgenerate() */ { 0x6548c020533ff519, 0x00 }, /* Use this row to check eccgenerate() */ { 0x968f056337e7c20a, 0x0e }, /* Use this row to check eccgenerate() */ { 0x3f11154207e3366d, 0xbe }, /* Use this row to check eccgenerate() */ { 0x7ee773366f160e7c, 0x53 }, /* Use this row to check eccgenerate() */ { 0x2ca97e241c477366, 0x1c }, /* Use this row to check eccgenerate() */ { 0x8f2b4f72b16b840d, 0x88 }, /* Use this row to check eccgenerate() */ { 0x282dbb076f3bf72e, 0xd0 }, /* Use this row to check eccgenerate() */ { 0x39955329afde4d36, 0xc7 }, /* Use this row to check eccgenerate() */ { 0x8d1d0c77657fbf1b, 0x22 }, /* Use this row to check eccgenerate() */ { 0x0afd9e698ba24218, 0x1a }, /* Use this row to check eccgenerate() */ { 0x9533ce56dc495356, 0x2a }, /* Use this row to check eccgenerate() */ { 0x7f645d72a4b35f27, 0x80 }, /* Use this row to check eccgenerate() */ { 0xc661ff4cebe7fc55, 0xe2 }, /* Use this row to check eccgenerate() */ { 0xb9bc1a0053e51735, 0xff }, /* Use this row to check eccgenerate() */ { 0x84df3f541dd6d331, 0x54 }, /* Use this row to check eccgenerate() */ { 0x7015c94b8189675e, 0x02 }, /* Use this row to check eccgenerate() */ { 0xb9702a69ea270075, 0x1f }, /* Use this row to check eccgenerate() */ { 0xf10a376206a5ce2e, 0x6f }, /* Use this row to check eccgenerate() */ { 0x75bbdc2af8813f2b, 0xb1 }, /* Use this row to check eccgenerate() */ { 0x14c9b2116ff2aa18, 0x7a }, /* Use this row to check eccgenerate() */ { 0x205e2f26a1645b4f, 0x2b }, /* Use this row to check eccgenerate() */ { 0x10a0527ea4f40104, 0xf6 }, /* Use this row to check eccgenerate() */ { 0x53d34f3a498bea2d, 0x93 }, /* Use this row to check eccgenerate() */ { 0xae0aaa494935a627, 0xbf }, /* Use this row to check eccgenerate() */ { 0xd4d7e83fe0f05b31, 0x58 }, /* Use this row to check eccgenerate() */ { 0xbc3aaf07b8074933, 0x74 }, /* Use this row to check eccgenerate() */ { 0x5cbba85a690bb716, 0xbf }, /* Use this row to check eccgenerate() */ { 0x55f3b36c3c9f0c7a, 0x3a }, /* Use this row to check eccgenerate() */ { 0x8f84242f231da827, 0x50 }, /* Use this row to check eccgenerate() */ { 0x40f37b590eb0ce6c, 0x9c }, /* Use this row to check eccgenerate() */ { 0x8f39364b14646403, 0x0b }, /* Use this row to check eccgenerate() */ { 0xfe8b6478b0084525, 0x21 }, /* Use this row to check eccgenerate() */ { 0xb6ad135448aa6034, 0x1c }, /* Use this row to check eccgenerate() */ { 0x402ca05fef969b5a, 0x90 }, /* Use this row to check eccgenerate() */ { 0x5e8946732b69f07e, 0xaa }, /* Use this row to check eccgenerate() */ { 0xcccd4b4e55f55271, 0xe8 }, /* Use this row to check eccgenerate() */ { 0xf9e954757ee77519, 0xf8 }, /* Use this row to check eccgenerate() */ { 0xc7726047dc6d9e4c, 0x67 }, /* Use this row to check eccgenerate() */ { 0x25a344744cbda42f, 0x77 }, /* Use this row to check eccgenerate() */ { 0x2cae0061757d0a11, 0xca }, /* Use this row to check eccgenerate() */ { 0x2d855344f97a2d34, 0x9b }, /* Use this row to check eccgenerate() */ { 0x6386e44ae9e8af68, 0x6c }, /* Use this row to check eccgenerate() */ { 0x2588bc628a40fc1e, 0x4c }, /* Use this row to check eccgenerate() */ { 0xad5da446b8799837, 0x31 }, /* Use this row to check eccgenerate() */ { 0xc6296724b40ce111, 0xde }, /* Use this row to check eccgenerate() */ { 0xc8704515ed502020, 0x72 }, /* Use this row to check eccgenerate() */ { 0x9d59654555639d6f, 0x16 }, /* Use this row to check eccgenerate() */ { 0x9e0dfe23c6fca90d, 0x37 }, /* Use this row to check eccgenerate() */ { 0xb593456853077919, 0xee }, /* Use this row to check eccgenerate() */ { 0x7e706918de399e03, 0xe7 }, /* Use this row to check eccgenerate() */ { 0x332ff174131d8c5b, 0x34 }, /* Use this row to check eccgenerate() */ { 0x920402754a3eb566, 0x2f }, /* Use this row to check eccgenerate() */ { 0x26ac53332c19466a, 0x0c }, /* Use this row to check eccgenerate() */ { 0x78d6ea195977623c, 0x6f }, /* Use this row to check eccgenerate() */ { 0xcff46c4d4b4f9827, 0x20 }, /* Use this row to check eccgenerate() */ { 0x44cac55ba584eb7a, 0x5f }, /* Use this row to check eccgenerate() */ { 0x8e6d9b63fc79c011, 0xc8 }, /* Use this row to check eccgenerate() */ { 0x86babc30a750aa26, 0x20 }, /* Use this row to check eccgenerate() */ { 0x5fca425eb3f55746, 0x12 }, /* Use this row to check eccgenerate() */ { 0x6702395833186177, 0xaf }, /* Use this row to check eccgenerate() */ { 0x2069811725f4a902, 0x87 }, /* Use this row to check eccgenerate() */ { 0x7b57477230737e6d, 0xd9 }, /* Use this row to check eccgenerate() */ { 0xf66f287bbdc2e65c, 0xfa }, /* Use this row to check eccgenerate() */ { 0x10ca5f7619654516, 0x52 }, /* Use this row to check eccgenerate() */ { 0xf79ee319ac036e63, 0x58 }, /* Use this row to check eccgenerate() */ { 0xbf20fa3e8e3ac90e, 0x82 }, /* Use this row to check eccgenerate() */ { 0xd8787e752bced40e, 0x54 }, /* Use this row to check eccgenerate() */ { 0x57e71a795125fc33, 0xfe }, /* Use this row to check eccgenerate() */ { 0xab9c5e70fe24d228, 0xfc }, /* Use this row to check eccgenerate() */ { 0x49746a50d0bd0513, 0x9d }, /* Use this row to check eccgenerate() */ { 0x7542f10d7a91cb3d, 0xb9 }, /* Use this row to check eccgenerate() */ { 0x760b8c4f8e3e302c, 0x82 }, /* Use this row to check eccgenerate() */ { 0x358fda5203b08c71, 0x23 }, /* Use this row to check eccgenerate() */ { 0xb6a5e437fdc54800, 0xb6 }, /* Use this row to check eccgenerate() */ { 0x30dea97795591d31, 0x7c }, /* Use this row to check eccgenerate() */ { 0xba4dc7331da81d10, 0x11 }, /* Use this row to check eccgenerate() */ { 0x4d1b9c7d51472b0f, 0x37 }, /* Use this row to check eccgenerate() */ { 0x0e0a126c35a50e26, 0xd6 }, /* Use this row to check eccgenerate() */ { 0x4e0a543c448bc478, 0x0f }, /* Use this row to check eccgenerate() */ { 0xf08e325c1fd47162, 0x6b }, /* Use this row to check eccgenerate() */ { 0xad0e3b7146a93756, 0x86 }, /* Use this row to check eccgenerate() */ { 0x71770c65afaf2c1b, 0xae }, /* Use this row to check eccgenerate() */ { 0x01d5284f8687b966, 0x37 }, /* Use this row to check eccgenerate() */ { 0x84ac8b0fc85e275e, 0x86 }, /* Use this row to check eccgenerate() */ { 0x981c2d71ac71873f, 0x4e }, /* Use this row to check eccgenerate() */ { 0x2603537dce20f65f, 0xb5 }, /* Use this row to check eccgenerate() */ { 0x5c5f260c0d5f1e7f, 0x0b }, /* Use this row to check eccgenerate() */ { 0x100fab709c0edf4c, 0xc9 }, /* Use this row to check eccgenerate() */ { 0x99d4274d91ee005f, 0x83 }, /* Use this row to check eccgenerate() */ { 0x26481e10c6b48f28, 0x16 }, /* Use this row to check eccgenerate() */ { 0xe45cad38cab2d144, 0x9c }, /* Use this row to check eccgenerate() */ { 0x1bfafc53e195e543, 0x8e }, /* Use this row to check eccgenerate() */ { 0x163bf46931784936, 0xdc }, /* Use this row to check eccgenerate() */ { 0x75030e2f29040f40, 0x48 }, /* Use this row to check eccgenerate() */ { 0x48d8802265454826, 0x2a }, /* Use this row to check eccgenerate() */ { 0xabee7f7c6592400b, 0x2b }, /* Use this row to check eccgenerate() */ { 0x15426d26f6e6bb13, 0x89 }, /* Use this row to check eccgenerate() */ { 0x7c6e757a1c668c61, 0x6d }, /* Use this row to check eccgenerate() */ { 0xe4c4b33f16179675, 0x74 }, /* Use this row to check eccgenerate() */ { 0xc2881d35001b010a, 0xd4 }, /* Use this row to check eccgenerate() */ { 0xce3bf7697de1e030, 0x65 }, /* Use this row to check eccgenerate() */ { 0x8a40ff2fe88b7032, 0x19 }, /* Use this row to check eccgenerate() */ { 0x849a4f7f2a9b1d76, 0x58 }, /* Use this row to check eccgenerate() */ { 0xbc891e559b4faa20, 0x4c }, /* Use this row to check eccgenerate() */ { 0x61043a491e6f774c, 0x28 }, /* Use this row to check eccgenerate() */ { 0xe8214911e2d13c65, 0x9e }, /* Use this row to check eccgenerate() */ { 0xc36722294561e701, 0x3d }, /* Use this row to check eccgenerate() */ { 0x77d93038031c4665, 0x55 }, /* Use this row to check eccgenerate() */ { 0x2c205525daa21613, 0x85 }, /* Use this row to check eccgenerate() */ { 0x3fe85e39ecdc3e67, 0x20 }, /* Use this row to check eccgenerate() */ { 0x526f7f7275f8d547, 0xa4 }, /* Use this row to check eccgenerate() */ { 0x6bdf915bead6de35, 0xac }, /* Use this row to check eccgenerate() */ { 0x063d6b1767b1ec18, 0x78 }, /* Use this row to check eccgenerate() */ { 0x7dc8820ee74d0756, 0x31 }, /* Use this row to check eccgenerate() */ { 0xe7680860ea011f57, 0x3f }, /* Use this row to check eccgenerate() */ { 0x67e3ff073f51a043, 0xd6 }, /* Use this row to check eccgenerate() */ { 0x27dd1076b6a4ff49, 0x10 }, /* Use this row to check eccgenerate() */ { 0xe03f1d40f223ff37, 0xec }, /* Use this row to check eccgenerate() */ { 0x8d73a958ab776075, 0x6f }, /* Use this row to check eccgenerate() */ { 0xc9e6d7419cc93b15, 0x8f }, /* Use this row to check eccgenerate() */ { 0x7f9b787aee77e321, 0xb7 }, /* Use this row to check eccgenerate() */ { 0x34d9ca23b1082153, 0xa9 }, /* Use this row to check eccgenerate() */ { 0xb424673842039b23, 0xe2 }, /* Use this row to check eccgenerate() */ { 0x1ca6b136abb2fb5b, 0xe1 }, /* Use this row to check eccgenerate() */ { 0x978f3a43e144bc5d, 0x64 }, /* Use this row to check eccgenerate() */ { 0x563d92255b8e1070, 0x14 }, /* Use this row to check eccgenerate() */ { 0x4565ef25e9feb935, 0x2d }, /* Use this row to check eccgenerate() */ { 0x50b0a64ec11c2401, 0x3c }, /* Use this row to check eccgenerate() */ { 0xa86a2b574ba25a3d, 0x8b }, /* Use this row to check eccgenerate() */ { 0x36a47914cd78295d, 0xf1 }, /* Use this row to check eccgenerate() */ { 0x0ccac9208fd33337, 0xe4 }, /* Use this row to check eccgenerate() */ { 0x457833019d87791c, 0xc4 }, /* Use this row to check eccgenerate() */ { 0x8fab785433a7da16, 0x0c }, /* Use this row to check eccgenerate() */ { 0xdf1e3b0c26b85041, 0x94 }, /* Use this row to check eccgenerate() */ { 0xc2818c561c1f222d, 0x9a }, /* Use this row to check eccgenerate() */ { 0x0b97054fa805134e, 0xec }, /* Use this row to check eccgenerate() */ { 0x5a0e3421411d0551, 0x57 }, /* Use this row to check eccgenerate() */ { 0x8420a0743f70d072, 0xa8 }, /* Use this row to check eccgenerate() */ { 0xea22cc4e0e339b59, 0x15 }, /* Use this row to check eccgenerate() */ { 0xef775737a0c6512b, 0xe7 }, /* Use this row to check eccgenerate() */ { 0xfc54621b81b20612, 0x9a }, /* Use this row to check eccgenerate() */ { 0x6bb1c04745b5e95c, 0x1e }, /* Use this row to check eccgenerate() */ { 0x06d20d5e41ba5141, 0x56 }, /* Use this row to check eccgenerate() */ { 0x8d5cac7ebb616716, 0x43 }, /* Use this row to check eccgenerate() */ { 0x89da9073ae3c3935, 0xb1 }, /* Use this row to check eccgenerate() */ { 0x3e106d6cc3002613, 0xec }, /* Use this row to check eccgenerate() */ { 0x60889f2f95a45a14, 0x69 }, /* Use this row to check eccgenerate() */ { 0xc94b352b8388a06d, 0x53 }, /* Use this row to check eccgenerate() */ { 0xa940f12ef0331804, 0x7a }, /* Use this row to check eccgenerate() */ }; int main(void) { int i; uint8_t ret_memcpy; uint8_t ret_verify; uint64_t dst; uint64_t *buf; struct ecc64 *ret_buf; /* * Test that eccgenerate() still works, but skip the first 64 because they * have intentional bitflips */ printf("Checking eccgenerate()\n"); for (i = 64; i < NUM_ECC_ROWS; i++) { if (eccgenerate(be64toh(ecc_data[i].data)) != ecc_data[i].ecc) { ERR("ECC did not generate the correct value, expecting 0x%02x, got 0x%02x\n", ecc_data[i].ecc, eccgenerate(be64toh(ecc_data[i].data))); } } /* Test that the ecc code can detect and recover bitflips */ printf("Testing bitflip recovery\n"); for (i = 0; i < 64; i++) { ret_memcpy = memcpy_from_ecc(&dst, &ecc_data[i], sizeof(dst)); if (dst != 0xffffffffffffffff || ret_memcpy) { ERR("ECC code didn't correct bad bit %d in 0x%016lx\n", 63 - i, be64toh(ecc_data[i].data)); exit(1); } ret_verify = eccverify(be64toh(ecc_data[i].data), ecc_data[i].ecc); if (ret_verify != 63 - i) { ERR("ECC did not catch incorrect bit %d in row 0x%016lx 0x%02x, got 0x%02x\n", i, ecc_data[i].data, ecc_data[i].ecc, ret_verify); exit(1); } } buf = malloc(NUM_ECC_ROWS * sizeof(*buf)); if (!buf) { ERR("malloc #1 failed during ecc test\n"); exit(1); } printf("pass\n"); /* Test a large memcpy */ printf("Testing a large(ish) memcpy_from_ecc()\n"); ret_memcpy = memcpy_from_ecc(buf, ecc_data, NUM_ECC_ROWS * sizeof(*buf)); if (ret_memcpy) { ERR("ECC Couldn't memcpy entire buffer\n"); exit(1); } for (i = 0; i < NUM_ECC_ROWS; i++) { /* Large memcpy should have fixed the bitflips */ if (i < 64 && buf[i] != 0xffffffffffffffff) { ERR("memcpy_from_ecc got it wrong for uint64_t number %d, got 0x%016lx, expecting 0xffffffffffffffff\n", i, buf[i]); exit(1); } /* But not changed any of the correct data */ if (i > 63 && buf[i] != ecc_data[i].data) { ERR("memcpy_from_ecc got it wrong for uint64_t number %d, git 0x%016lx, expecting 0x%016lx\n", i, buf[i], ecc_data[i].data); exit(1); } } printf("pass\n"); /* Test a memcpy to add ecc data */ printf("Testing a large(ish) memcpy_to_ecc()\n"); ret_buf = malloc(ecc_buffer_size(NUM_ECC_ROWS * sizeof(*buf))); if (!buf) { ERR("malloc #2 failed during ecc test\n"); exit(1); } ret_memcpy = memcpy_to_ecc(ret_buf, buf, NUM_ECC_ROWS * sizeof(*buf)); if (ret_memcpy) { ERR("ECC Couldn't memcpy entire buffer\n"); exit(1); } for (i = 0; i < NUM_ECC_ROWS; i++) { /* The data should be the same */ if (ret_buf[i].data != buf[i]) { ERR("memcpy_to_ecc got it wrong on uint64_t %d, expecting 0x%016lx, got 0x%016lx\n", i, buf[i], ret_buf[i].data); exit(1); } /* Check the correctness of ecc bytes */ if (ret_buf[i].ecc != ecc_data[i].ecc) { ERR("memcpy_to_ecc got it on the ecc for uint64_t %d, expecting 0x%02x, got 0x%02x\n", i, ecc_data[i].ecc, ret_buf[i].ecc); exit(1); } } printf("ECC tests pass\n"); printf("ECC test error conditions\n"); if (memcpy_to_ecc(ret_buf, buf, 7) == 0) { ERR("memcpy_to_ecc didn't detect bad size 7\n"); exit(1); } if (memcpy_to_ecc(ret_buf, buf, 15) == 0) { ERR("memcpy_to_ecc didn't detect bad size 15\n"); exit(1); } if (memcpy_from_ecc(buf, ret_buf, 7) == 0) { ERR("memcpy_from_ecc didn't detect bad size 7\n"); exit(1); } if (memcpy_from_ecc(buf, ret_buf, 15) == 0) { ERR("memcpy_from_ecc didn't detect bad size 15\n"); exit(1); } printf("ECC error conditions pass\n"); free(buf); free(ret_buf); return 0; } skiboot-skiboot-5.1.13/libflash/test/test-flash.c000066400000000000000000000233541265204436200217170ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "../libflash.c" #include "../ecc.c" #define __unused __attribute__((unused)) #define ERR(fmt...) fprintf(stderr, fmt) /* Flash commands */ #define CMD_PP 0x02 #define CMD_READ 0x03 #define CMD_WRDI 0x04 #define CMD_RDSR 0x05 #define CMD_WREN 0x06 #define CMD_SE 0x20 #define CMD_RDSCUR 0x2b #define CMD_BE32K 0x52 #define CMD_CE 0x60 #define CMD_RDID 0x9f #define CMD_EN4B 0xb7 #define CMD_BE 0xd8 #define CMD_RDDPB 0xe0 #define CMD_RDSPB 0xe2 #define CMD_EX4B 0xe9 /* Flash status bits */ #define STAT_WIP 0x01 #define STAT_WEN 0x02 static uint8_t *sim_image; static uint32_t sim_image_sz = 0x100000; static uint32_t sim_index; static uint32_t sim_addr; static uint32_t sim_er_size; static uint8_t sim_sr; static bool sim_fl_4b; static bool sim_ct_4b; static enum sim_state { sim_state_idle, sim_state_rdid, sim_state_rdsr, sim_state_read_addr, sim_state_read_data, sim_state_write_addr, sim_state_write_data, sim_state_erase_addr, sim_state_erase_done, } sim_state; /* * Simulated flash & controller */ static int sim_start_cmd(uint8_t cmd) { if (sim_state != sim_state_idle) { ERR("SIM: Command %02x in wrong state %d\n", cmd, sim_state); return -1; } sim_index = 0; sim_addr = 0; switch(cmd) { case CMD_RDID: sim_state = sim_state_rdid; break; case CMD_RDSR: sim_state = sim_state_rdsr; break; case CMD_EX4B: sim_fl_4b = false; break; case CMD_EN4B: sim_fl_4b = true; break; case CMD_WREN: sim_sr |= STAT_WEN; break; case CMD_READ: sim_state = sim_state_read_addr; if (sim_ct_4b != sim_fl_4b) ERR("SIM: 4b mode mismatch in READ !\n"); break; case CMD_PP: sim_state = sim_state_write_addr; if (sim_ct_4b != sim_fl_4b) ERR("SIM: 4b mode mismatch in PP !\n"); if (!(sim_sr & STAT_WEN)) ERR("SIM: PP without WEN, ignoring... \n"); break; case CMD_SE: case CMD_BE32K: case CMD_BE: if (sim_ct_4b != sim_fl_4b) ERR("SIM: 4b mode mismatch in SE/BE !\n"); if (!(sim_sr & STAT_WEN)) ERR("SIM: SE/BE without WEN, ignoring... \n"); sim_state = sim_state_erase_addr; switch(cmd) { case CMD_SE: sim_er_size = 0x1000; break; case CMD_BE32K: sim_er_size = 0x8000; break; case CMD_BE: sim_er_size = 0x10000; break; } break; case CMD_CE: if (!(sim_sr & STAT_WEN)) { ERR("SIM: CE without WEN, ignoring... \n"); break; } memset(sim_image, 0xff, sim_image_sz); sim_sr |= STAT_WIP; sim_sr &= ~STAT_WEN; break; default: ERR("SIM: Unsupported command %02x\n", cmd); return -1; } return 0; } static void sim_end_cmd(void) { /* For write and sector/block erase, set WIP & clear WEN here */ if (sim_state == sim_state_write_data) { sim_sr |= STAT_WIP; sim_sr &= ~STAT_WEN; } sim_state = sim_state_idle; } static bool sim_do_address(const uint8_t **buf, uint32_t *len) { uint8_t asize = sim_fl_4b ? 4 : 3; const uint8_t *p = *buf; while(*len) { sim_addr = (sim_addr << 8) | *(p++); *buf = p; *len = *len - 1; sim_index++; if (sim_index >= asize) return true; } return false; } static int sim_wbytes(const void *buf, uint32_t len) { const uint8_t *b = buf; bool addr_complete; again: switch(sim_state) { case sim_state_read_addr: addr_complete = sim_do_address(&b, &len); if (addr_complete) { sim_state = sim_state_read_data; sim_index = 0; if (len) goto again; } break; case sim_state_write_addr: addr_complete = sim_do_address(&b, &len); if (addr_complete) { sim_state = sim_state_write_data; sim_index = 0; if (len) goto again; } break; case sim_state_write_data: if (!(sim_sr & STAT_WEN)) break; while(len--) { uint8_t c = *(b++); if (sim_addr >= sim_image_sz) { ERR("SIM: Write past end of flash\n"); return -1; } /* Flash write only clears bits */ sim_image[sim_addr] &= c; sim_addr = (sim_addr & 0xffffff00) | ((sim_addr + 1) & 0xff); } break; case sim_state_erase_addr: if (!(sim_sr & STAT_WEN)) break; addr_complete = sim_do_address(&b, &len); if (addr_complete) { memset(sim_image + sim_addr, 0xff, sim_er_size); sim_sr |= STAT_WIP; sim_sr &= ~STAT_WEN; sim_state = sim_state_erase_done; } break; default: ERR("SIM: Write in wrong state %d\n", sim_state); return -1; } return 0; } static int sim_rbytes(void *buf, uint32_t len) { uint8_t *b = buf; switch(sim_state) { case sim_state_rdid: while(len--) { switch(sim_index) { case 0: *(b++) = 0x55; break; case 1: *(b++) = 0xaa; break; case 2: *(b++) = 0x55; break; default: ERR("SIM: RDID index %d\n", sim_index); *(b++) = 0; break; } sim_index++; } break; case sim_state_rdsr: while(len--) { *(b++) = sim_sr; if (sim_index > 0) ERR("SIM: RDSR index %d\n", sim_index); sim_index++; /* If WIP was 1, clear it, ie, simulate write/erase * completion */ sim_sr &= ~STAT_WIP; } break; case sim_state_read_data: while(len--) { if (sim_addr >= sim_image_sz) { ERR("SIM: Read past end of flash\n"); return -1; } *(b++) = sim_image[sim_addr++]; } break; default: ERR("SIM: Read in wrong state %d\n", sim_state); return -1; } return 0; } static int sim_send_addr(uint32_t addr) { const void *ap; /* Layout address MSB first in memory */ addr = cpu_to_be32(addr); /* Send the right amount of bytes */ ap = (char *)&addr; if (sim_ct_4b) return sim_wbytes(ap, 4); else return sim_wbytes(ap + 1, 3); } static int sim_cmd_rd(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd, bool has_addr, uint32_t addr, void *buffer, uint32_t size) { int rc; rc = sim_start_cmd(cmd); if (rc) goto bail; if (has_addr) { rc = sim_send_addr(addr); if (rc) goto bail; } if (buffer && size) rc = sim_rbytes(buffer, size); bail: sim_end_cmd(); return rc; } static int sim_cmd_wr(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd, bool has_addr, uint32_t addr, const void *buffer, uint32_t size) { int rc; rc = sim_start_cmd(cmd); if (rc) goto bail; if (has_addr) { rc = sim_send_addr(addr); if (rc) goto bail; } if (buffer && size) rc = sim_wbytes(buffer, size); bail: sim_end_cmd(); return rc; } static int sim_set_4b(struct spi_flash_ctrl *ctrl __unused, bool enable) { sim_ct_4b = enable; return 0; } static int sim_read(struct spi_flash_ctrl *ctrl __unused, uint32_t pos, void *buf, uint32_t len) { if (sim_ct_4b != sim_fl_4b) ERR("SIM: 4b mode mismatch in autoread !\n"); if ((pos + len) < pos) return -1; if ((pos + len) > sim_image_sz) return -1; memcpy(buf, sim_image + pos, len); return 0; }; struct spi_flash_ctrl sim_ctrl = { .cmd_wr = sim_cmd_wr, .cmd_rd = sim_cmd_rd, .set_4b = sim_set_4b, .read = sim_read, }; int main(void) { struct blocklevel_device *bl; uint32_t total_size, erase_granule; const char *name; uint16_t *test; struct ecc64 *ecc_test; uint64_t *test64; int i, rc; sim_image = malloc(sim_image_sz); memset(sim_image, 0xff, sim_image_sz); test = malloc(0x10000 * 2); rc = flash_init(&sim_ctrl, &bl, NULL); if (rc) { ERR("flash_init failed with err %d\n", rc); exit(1); } rc = flash_get_info(bl, &name, &total_size, &erase_granule); if (rc) { ERR("flash_get_info failed with err %d\n", rc); exit(1); } /* Make up a test pattern */ for (i=0; i<0x10000;i++) test[i] = cpu_to_be16(i); /* Write 64k of stuff at 0 and at 128k */ printf("Writing test patterns...\n"); flash_smart_write(bl, 0, test, 0x10000); flash_smart_write(bl, 0x20000, test, 0x10000); /* Write "Hello world" straddling the 64k boundary */ #define HW "Hello World" printf("Writing test string...\n"); flash_smart_write(bl, 0xfffc, HW, sizeof(HW)); /* Check result */ if (memcmp(sim_image + 0xfffc, HW, sizeof(HW))) { ERR("Test string mismatch !\n"); exit(1); } printf("Test string pass\n"); if (memcmp(sim_image, test, 0xfffc)) { ERR("Test pattern mismatch !\n"); exit(1); } printf("Test pattern pass\n"); printf("Test ECC interfaces\n"); flash_smart_write_corrected(bl, 0, test, 0x10000, 1); ecc_test = (struct ecc64 *)sim_image; test64 = (uint64_t *)test; for (i = 0; i < 0x10000 / sizeof(*ecc_test); i++) { if (test64[i] != ecc_test[i].data) { ERR("flash_smart_write_corrected() pattern missmatch at %d: 0x%016lx vs 0x%016lx\n", i, test64[i], ecc_test[i].data); exit(1); } if (ecc_test[i].ecc != eccgenerate(be64toh(test64[i]))) { ERR("ECCs don't match 0x%02x vs 0x%02x\n", ecc_test[i].ecc, eccgenerate(test64[i])); exit(1); } } printf("Test ECC interface pass\n"); printf("Test ECC erase\n"); if (flash_erase(bl, 0, 0x10000) != 0) { ERR("flash_erase didn't return 0\n"); exit(1); } for (i = 0; i < 0x10000 / sizeof(*ecc_test); i++) { uint8_t zero = 0; if (ecc_test[i].data != 0xFFFFFFFFFFFFFFFF) { ERR("Data not properly cleared at %d\n", i); exit(1); } rc = flash_write(bl, i * sizeof(*ecc_test) + 8, &zero, 1, 0); if (rc || ecc_test[i].ecc != 0) { ERR("Cleared data not correctly ECCed: 0x%02x (0x%016lx) expecting 0 at %d\n", ecc_test[i].ecc, ecc_test[i].data, i); exit(1); } } printf("Test ECC erase pass\n"); flash_exit(bl); return 0; } skiboot-skiboot-5.1.13/libpore/000077500000000000000000000000001265204436200163635ustar00rootroot00000000000000skiboot-skiboot-5.1.13/libpore/Makefile.inc000066400000000000000000000006131265204436200205730ustar00rootroot00000000000000ifeq ($(PORE),1) LIBPORE_SRCS = p8_pore_table_gen_api_fixed.C LIBPORE_SRCS += p8_pore_table_static_data.c sbe_xip_image.c pore_inline_assembler.c LIBPORE_OBJS_1 = $(LIBPORE_SRCS:%.c=%.o) LIBPORE_OBJS = $(LIBPORE_OBJS_1:%.C=%.o) endif SUBDIRS += libpore LIBPORE = libpore/built-in.o CFLAGS_SKIP_libpore/pore_inline_assembler.o=-Wsuggest-attribute=const $(LIBPORE): $(LIBPORE_OBJS:%=libpore/%) skiboot-skiboot-5.1.13/libpore/fapi_sbe_common.H000066400000000000000000000062361265204436200216230ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/include/fapi_sbe_common.H $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ #ifndef __FAPI_SBE_COMMON_H #define __FAPI_SBE_COMMON_H // $Id: fapi_sbe_common.H,v 1.1 2012/04/16 23:55:37 bcbrock Exp $ // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/fapi_sbe_common.H,v $ //------------------------------------------------------------------------------ // *! (C) Copyright International Business Machines Corp. 2011 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //------------------------------------------------------------------------------ // *! OWNER NAME : Email: /// \file fapi_sbe_common.H /// \brief Definitions common to FAPI and SBE procedures /// /// Several preprocessor macros are required to have different definitions in /// C, C++ and SBE assembly procedures. These common forms are collected here. #if defined __ASSEMBLER__ #define CONST_UINT8_T(name, expr) .set name, (expr) #define CONST_UINT32_T(name, expr) .set name, (expr) #define CONST_UINT64_T(name, expr) .set name, (expr) #define ULL(x) x #elif defined __cplusplus #include #define CONST_UINT8_T(name, expr) const uint8_t name = (expr); #define CONST_UINT32_T(name, expr) const uint32_t name = (expr); #define CONST_UINT64_T(name, expr) const uint64_t name = (expr); #define ULL(x) x##ull #else // C code // CONST_UINT[8,3,64]_T() can't be used in C code/headers; Use // // #define [ or ULL() for 64-bit constants #define ULL(x) x##ull #endif // __ASSEMBLER__ #endif // __FAPI_SBE_COMMON_H skiboot-skiboot-5.1.13/libpore/p8_delta_scan_rw.h000066400000000000000000000464441265204436200217640ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/p8_delta_scan_rw.h $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: p8_delta_scan_rw.h,v 1.49 2014/05/13 13:31:51 jmcgill Exp $ #define OVERRIDE_OFFSET 8 // Byte offset of forward pointer's addr relative // to base forward pointer's addr. #define SIZE_IMAGE_BUF_MAX 5000000 // Max ~5MB image buffer size. #define SIZE_IMAGE_CENTAUR_MAX 5000000 // Max ~5MB image buffer size. #define SIZE_REPR_RING_MAX 50000 // Max ~50kB repr ring buffer size. #define SCOM_REG_MASK 0x00ffffff // Scom register mask (within a chiplet) #define CID_MASK 0xff000000 // Chiplet ID mask #define CID_EX_LOW 0x10 // Lowest EX chiplet addr #define CID_EX_HIGH 0x1f // Highest EX chiplet addr /***** Xip customize support ****/ #define COMBINED_GOOD_VECTORS_TOC_NAME "combined_good_vectors" #define L2_SINGLE_MEMBER_ENABLE_TOC_NAME "l2_single_member_enable_mask" #define PROC_PIB_REPR_VECTOR_TOC_NAME "proc_sbe_pibmem_repair_vector" #define NEST_SKEWADJUST_VECTOR_TOC_NAME "proc_sbe_nest_skewadjust_vector" #define SECURITY_SETUP_VECTOR_TOC_NAME "proc_sbe_security_setup_vector" #define VALID_BOOT_CORES_MASK_TOC_NAME "valid_boot_cores_mask" #define MAX_PLL_RING_SIZE 128 // Bytes #define PERV_BNDY_PLL_RING_TOC_NAME "perv_bndy_pll_ring" #define PERV_BNDY_PLL_RING_ALT_TOC_NAME "perv_bndy_pll_ring_alt" #define MAX_FUNC_L3_RING_LIST_ENTRIES 64 #define MAX_FUNC_L3_RING_SIZE 7000 // Bytes #define FUNC_L3_RING_TOC_NAME "ex_func_l3_ring" #define MAX_CEN_PLL_RING_SIZE 80 // Bytes #define TP_PLL_BNDY_RING_ALT_TOC_NAME "tp_pll_bndy_ring_alt" #define STANDALONE_MBOX0_VALUE_TOC_NAME "standalone_mbox0_value" #define STANDALONE_MBOX1_VALUE_TOC_NAME "standalone_mbox1_value" #define STANDALONE_MBOX2_VALUE_TOC_NAME "standalone_mbox2_value" #define STANDALONE_MBOX3_VALUE_TOC_NAME "standalone_mbox3_value" #define UNTRUSTED_BAR_TOC_NAME "fabric_config" #define UNTRUSTED_PBA_BAR_TOC_NAME "fabric_config_pba" #define REFCLOCK_TERM_TOC_NAME "refclock_term" /***** Scan setting *****/ #define OPCG_SCAN_RATIO 4 #define P8_OPCG_SCAN_RATIO_BITS (uint64_t(OPCG_SCAN_RATIO-1)<<(63-8)) #define P8_OPCG_GO_BITS (uint64_t(0x40000000)<<32) #define P8_SCAN_POLL_MASK_BIT15 (uint64_t(0x00010000)<<32) /***** Scan Control Regs *****/ #define P8_PORE_OPCG_CTRL_REG0_0x00030002 0x00030002 // OPCG control reg 0 #define P8_PORE_OPCG_CTRL_REG1_0x00030003 0x00030003 // OPCG control reg 1 #define P8_PORE_OPCG_CTRL_REG2_0x00030004 0x00030004 // OPCG control reg 2 #define P8_PORE_OPCG_START_REG3_0x00030005 0x00030005 // OPCG start reg 3 #define P8_PORE_CLOCK_REGION_0x00030006 0x00030006 // Clock region control #define P8_PORE_CLOCK_CONTROLLER_REG 0x00030007 // Addr of clock ctrl scom reg #define P8_PORE_CLOCK_STATUS_0x00030008 0x00030008 // Status of clocks running #define P8_PORE_SHIFT_REG 0x00038000 // Addr of scom reg that does scan ring shifting #define P8_SCAN_CHECK_WORD 0xA5A55A5A // Header check word /***** Ring state *****/ #define MAX_RING_SIZE 500000 // 500kbits is the max ring size in bits /***** Return codes *****/ #define DSLWB_RING_SEARCH_MATCH 0 #define DSLWB_RING_SEARCH_EXHAUST_MATCH 30 #define DSLWB_RING_SEARCH_NO_MATCH 31 #define DSLWB_RING_SEARCH_MESS 32 #define DSLWB_SLWB_SUCCESS 0 #define DSLWB_SLWB_NO_RING_MATCH 40 #define DSLWB_SLWB_DX_ERROR 41 #define DSLWB_SLWB_WF_ERROR 42 #define DSLWB_SLWB_WF_IMAGE_ERROR 43 #define DSLWB_SLWB_IMAGE_ERROR 44 #define DSLWB_SLWB_UNKNOWN_ERROR 45 #define IMGBUILD_SUCCESS 0 // Successful img build. #define IMGBUILD_ERR_GENERIC 1 // Non-specific error code. #define IMGBUILD_ERR_FILE_ACCESS 2 // Unable to access/open file. #define IMGBUILD_ERR_CHIPLET_ID_MESS 4 // Chiplet ID mess(mostly for VPD rings). #define IMGBUILD_NO_RINGS_FOUND 5 // Successful img build but no rings found. #define IMGBUILD_BAD_ARGS 6 // Bad function arguments. #define IMGBUILD_ERR_MEMORY 7 // Memory allocation error. #define IMGBUILD_ERR_RING_TOO_LARGE 8 // Ring size exceeds HB/PHYP's buffer. #define IMGBUILD_ERR_CHECK_CODE 9 // Coding or image data problem. #define IMGBUILD_INVALID_IMAGE 10 // Invalid image. #define IMGBUILD_IMAGE_SIZE_MISMATCH 11 // Mismatch between image sizes. #define IMGBUILD_IMAGE_SIZE_MESS 12 // Messed up image or section sizes. #define IMGBUILD_RINGTYPE_NOT_ALLOWED 13 // Ringtype not allowed. #define IMGBUILD_BUFFER_TOO_SMALL 14 // Buffer too small. #define IMGBUILD_ERR_PORE_INLINE 20 // Pore inline error. #define IMGBUILD_ERR_PORE_INLINE_ASM 21 // Err assoc w/inline assembler. #define IMGBUILD_RING_SEARCH_MATCH 0 #define IMGBUILD_RING_SEARCH_EXHAUST_MATCH 30 #define IMGBUILD_RING_SEARCH_NO_MATCH 31 #define IMGBUILD_RING_SEARCH_MESS 32 #define IMGBUILD_ERR_RING_SEARCH 33 // Err assoc w/ring retrieval. #define IMGBUILD_ERR_DATACARE_RING_MESS 34 // Err assoc w/datacare & vpd ring sizes. #define IMGBUILD_ERR_WF_CREATE 45 // Err assoc w/create_wiggle_flip_prg. #define IMGBUILD_ERR_RING_WRITE_TO_IMAGE 46 // Err assoc w/wr_ring_block_to_img. #define IMGBUILD_ERR_SECTION_SIZING 48 // Err assoc w/section sizing. #define IMGBUILD_ERR_GET_SECTION 49 // Err assoc w/getting section ID. #define IMGBUILD_ERR_SECTION_DELETE 50 // Err assoc w/deleting ELF section. #define IMGBUILD_ERR_APPEND 51 // Err assoc w/appending to ELF section. #define IMGBUILD_ERR_INCOMPLETE_IMG_BUILD 52 // The image was built, but with errors. #define IMGBUILD_ERR_FWD_BACK_PTR_MESS 53 // Forward or backward pointer mess. #define IMGBUILD_ERR_KEYWORD_NOT_FOUND 54 // Image keyword not found. #define IMGBUILD_ERR_MISALIGNED_RING_LAYOUT 55 // Ring layout is misaligned. #define IMGBUILD_ERR_IMAGE_TOO_LARGE 56 // Image too large. Exceeded max size. #define IMGBUILD_ERR_XIP_MISC 57 // Miscellaneous XIP image error. #define IMGBUILD_ERR_XIP_UNKNOWN 58 // Unknown XIP image error. #define IMGBUILD_ERR_RS4_DECOMPRESS 59 // Error during RS4 decompression. #define IMGBUILD_ERR_RS4_COMPRESS 60 // Error during RS4 compression. #define IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED 61 // Ram headers not synchronized. #define IMGBUILD_ERR_RAM_TABLE_FULL 63 // Ram table is full. #define IMGBUILD_ERR_RAM_CODE 64 // Code error in Ram API code. #define IMGBUILD_ERR_RAM_INVALID_PARM 65 // Invalid Ramming parameter. #define IMGBUILD_WARN_RAM_TABLE_CONTAMINATION 66 // Ram table contamination #define IMGBUILD_ERR_RAM_TABLE_FAIL 67 // Unsuccessful RAM table build. #define IMGBUILD_ERR_RAM_TABLE_END_NOT_FOUND 68 // Table entry end bit not found. #define IMGBUILD_ERR_SCOM_INVALID_PARM 70 // Invalid Scomming parameter. #define IMGBUILD_ERR_SCOM_HDRS_NOT_SYNCD 72 // Scom headers out of sync. #define IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND 74 // Scom entry not found (OR/AND oper.) #define IMGBUILD_ERR_SCOM_REPEAT_ENTRIES 76 // Repeat entries not allow. #define IMGBUILD_ERR_SCOM_INVALID_SUBSECTION 77 // Invalid subsection value. #define IMGBUILD_ERR_SCOM_TABLE_FAIL 79 // Unsuccessful SCOM table build. #if defined SLW_COMMAND_LINE_RAM || defined XIPC_COMMAND_LINE #define SLW_COMMAND_LINE #endif #if defined __FAPI && !(defined __P8_PORE_TABLE_GEN_API_C) #define MY_INF(_fmt_, _args_...) FAPI_INF(_fmt_, ##_args_) #ifndef SLW_COMMAND_LINE #define MY_ERR(_fmt_, _args_...) FAPI_ERR(_fmt_, ##_args_) #else #define MY_ERR(_fmt_, _args_...) FAPI_INF(_fmt_, ##_args_) #endif // End of SLW_COMMAND_LINE #define MY_DBG(_fmt_, _args_...) FAPI_DBG(_fmt_, ##_args_) #else // End of __FAPI #ifdef SLW_COMMAND_LINE #define MY_INF(_fmt_, _args_...) printf(_fmt_, ##_args_) #define MY_ERR(_fmt_, _args_...) printf(_fmt_, ##_args_) #define MY_DBG(_fmt_, _args_...) printf(_fmt_, ##_args_) #else // End of SLW_COMMAND_LINE #ifdef __SKIBOOT__ #define pr_fmt(fmt) "LIBPORE: " fmt #include //#define MY_INF(_fmt_, _args_...) prlog(PR_TRACE, _fmt_, ##_args_) #define MY_INF(_fmt_, _args_...) do { } while(0) #define MY_ERR(_fmt_, _args_...) prerror(_fmt_, ##_args_) //#define MY_DBG(_fmt_, _args_...) prlog(PR_INSANE, _fmt_, ##_args_) #define MY_DBG(_fmt_, _args_...) do { } while(0) #else #define MY_INF(_fmt_, _args_...) do { } while(0) #define MY_ERR(_fmt_, _args_...) do { } while(0) #define MY_DBG(_fmt_, _args_...) do { } while(0) #endif #endif // End of not(__FAPI) & not(SLW_COMMAND_LINE) #endif #ifdef SLW_COMMAND_LINE // Debug and development stuff //#define IGNORE_FOR_NOW // Causes code sections to be ignored. #define DEBUG_SUPPORT // Activates sbe-xip debug support. #endif /* XXX TEMPORARY */ #ifdef __SKIBOOT__ #define DEBUG_SUPPORT // Activates sbe-xip debug support. #endif //#include //#include //#include #include #include #if defined SLW_COMMAND_LINE #include // May be in conflict with p8_pore_api_custom.h #include // May be in conflict with p8_pore_api_custom.h #include #include #include #include #include #endif //End of SLW_COMMAND_LINE // Not needed by: // - Done: p8_pore_table_gen_api, p8_slw_build, p8_xip_customize, sbe_xip_tool, // p8_delta_scan, p8_ipl_build, p8_centaur_build. // - So, what was this used for? //#include #include #if !(defined __P8_PORE_TABLE_GEN_API_C) && !(defined __CEN_XIP_CUSTOMIZE_C) && !(defined SLW_COMMAND_LINE_RAM) // We don't need this include for gen_cpureg/scom or slw ramming. #include #endif #undef __PORE_INLINE_ASSEMBLER_C__ #include #if( defined(__cplusplus) && !defined(PLIC_MODULE) ) extern "C" { #endif #if !(defined __P8_PORE_TABLE_GEN_API_C) && !(defined SLW_COMMAND_LINE_RAM) // Info: // DeltaRingLayout describes the sequential order of the content in the compressed delta // ring blocks in the .initf section in the SBE-XIP images. // When creating the .initf delta ring blocks, the following rules must be followed: // - Everything must be stored in BE format. // - {entryOffset; sizeOfThis; sizeOfMeta; metaData} must be word-aligned to ensure // that the {rs4Launch} starts on a word boundary. // - {rs4Launch} must start on a word boundary (see earlier rule how to do that). // - {entryOffset; sizeOfThis; sizeOfMeta; metaData; rs4Launch} must be double-word- // aligned to ensure that {rs4Delta} starts on a double-word boundary. // - {rs4Delta} must start on a double-word bournday (see earlier rule how to do that). // typedef struct { uint64_t entryOffset; uint64_t backItemPtr; uint32_t sizeOfThis; uint32_t sizeOfMeta; // Exact size of meta data. Arbitrary size. Not null terminated. uint32_t ddLevel; uint8_t sysPhase; uint8_t override; uint8_t reserved1; uint8_t reserved2; char *metaData; // Arbitrary size. Extra bytes to next alignment are random or 0s. uint32_t *rs4Launch; // Code. Must be 4-byte aligned. Actually should be 8-B align! uint32_t *rs4Delta; // Data. Must be 8-byte aligned. uint32_t *wfInline; // Code. Must be 4-byte aligned. Actually should be 8-B align! } DeltaRingLayout; typedef struct { uint32_t sizeOfData; char data[]; } MetaData; int calc_ring_delta_state( const uint32_t *i_init, const uint32_t *i_alter, uint32_t *o_delta, const uint32_t i_ringLen); int create_wiggle_flip_prg( uint32_t *i_deltaRing, uint32_t i_ringBitLen, uint32_t i_scanSelectData, uint32_t i_chipletID, uint32_t **o_wfInline, uint32_t *o_wfInlineLenInWords, uint8_t i_flushOptimization, uint32_t i_scanMaxRotate, uint32_t i_waitsScanDelay, uint32_t i_ddLevel); uint64_t calc_ring_layout_entry_offset( uint8_t i_typeRingLayout, uint32_t i_sizeMetaData); int write_ring_block_to_image( void *io_image, const char *i_ringName, // NULL if no name. DeltaRingLayout *i_ringBlock, const uint8_t i_idxVector, // [0-15] - Ignored if ringName==NULL const uint8_t i_override, // [0,1] - Ignored if ringName==NULL const uint8_t i_overridable, // [0,1] - Ignored if ringName==NULL const uint32_t i_sizeImageMax, const uint8_t i_xipSectionId, void *i_bufTmp, const uint32_t i_sizeBufTmp); #if !(defined __CEN_XIP_CUSTOMIZE_C) int p8_centaur_build( void *i_imageIn, uint32_t i_ddLevel, void *i_imageOut, uint32_t i_sizeImageOutMax); int p8_ipl_build( void *i_imageIn, uint32_t i_ddLevel, void *i_imageOut, uint32_t i_sizeImageOutMax); int get_ring_layout_from_image2( const void *i_imageIn, uint32_t i_ddLevel, uint8_t i_sysPhase, DeltaRingLayout **o_rs4RingLayout, void **nextRing, uint8_t i_xipSectionId); int gen_ring_delta_state( uint32_t bitLen, uint32_t *i_init, uint32_t *i_alter, uint32_t *o_delta, uint32_t verbose); int write_rs4_ring_to_ref_image( char *i_fnImage, CompressedScanData *i_RS4, uint32_t i_ddLevel, uint8_t i_sysPhase, uint8_t i_override, uint8_t i_ringType, char *i_varName, char *i_fnMetaData, void *i_bufTmp, uint32_t i_sizeBufTmp, uint32_t verbose); int write_vpd_ring_to_ipl_image( void *io_image, uint32_t &io_sizeImageOut, CompressedScanData *i_bufRs4Ring, uint32_t i_ddLevel, uint8_t i_sysPhase, char *i_ringName, void *i_bufTmp, uint32_t i_sizeBufTmp, uint8_t i_xipSection); int write_vpd_ring_to_slw_image( void *io_image, uint32_t &io_sizeImageOut, CompressedScanData *i_bufRs4Ring, uint32_t i_ddLevel, uint8_t i_sysPhase, char *i_ringName, void *i_bufTmp, uint32_t i_sizeBufTmp, uint8_t i_bWcSpace); int check_and_perform_ring_datacare( void *i_imageRef, void *io_buf1, uint8_t i_ddLevel, uint8_t i_sysPhase, char *i_ringName, void *i_buf2, uint32_t i_sizeBuf2); int get_delta_ring_from_image( char *i_fnImage, char *i_varName, uint32_t i_ddLevel, uint8_t i_sysPhase, uint8_t i_override, MetaData **o_metaData, CompressedScanData **o_deltaRingRS4, uint32_t verbose); int write_wiggle_flip_to_image( void *io_imageOut, uint32_t *i_sizeImageMaxNew, DeltaRingLayout *i_ringLayout, uint32_t *i_wfInline, uint32_t i_wfInlineLenInWords); int get_ring_layout_from_image( const void *i_imageIn, uint32_t i_ddLevel, uint8_t i_sysPhase, DeltaRingLayout *o_rs4RingLayout, void **nextRing); int append_empty_section( void *io_image, int *i_sizeImageMaxNew, uint32_t i_sectionId, int *i_sizeSection, uint8_t i_bFixed); int initialize_slw_section( void *io_image, uint32_t *i_sizeImageMaxNew); int create_and_initialize_fixed_image( void *io_image); int update_runtime_scom_pointer( void *io_image); void cleanup( void *buf1=NULL, void *buf2=NULL, void *buf3=NULL, void *buf4=NULL, void *buf5=NULL); #endif // End of !(defined __CEN_XIP_CUSTOMIZE_C) #endif // End of !(defined __P8_PORE_TABLE_GEN_API_C) && !(defined SLW_COMMAND_LINE_RAM) // Byte-reverse a 32-bit integer if on an LE machine static inline uint32_t myRev32(const uint32_t i_x) { uint32_t rx; #ifdef _BIG_ENDIAN rx = i_x; #else uint8_t *pix = (uint8_t*)(&i_x); uint8_t *prx = (uint8_t*)(&rx); prx[0] = pix[3]; prx[1] = pix[2]; prx[2] = pix[1]; prx[3] = pix[0]; #endif return rx; } // Byte-reverse a 64-bit integer if on a little-endian machine static inline uint64_t myRev64(const uint64_t i_x) { uint64_t rx; #ifdef _BIG_ENDIAN rx = i_x; #else uint8_t *pix = (uint8_t*)(&i_x); uint8_t *prx = (uint8_t*)(&rx); prx[0] = pix[7]; prx[1] = pix[6]; prx[2] = pix[5]; prx[3] = pix[4]; prx[4] = pix[3]; prx[5] = pix[2]; prx[6] = pix[1]; prx[7] = pix[0]; #endif return rx; } // N-byte align an address, offset or size (aos) static inline uint64_t myByteAlign( const uint8_t nBytes, const uint64_t aos) { return ((aos+nBytes-1)/nBytes)*nBytes; } #if( defined(__cplusplus) && !defined(PLIC_MODULE) ) } #endif skiboot-skiboot-5.1.13/libpore/p8_image_help_base.H000066400000000000000000000140051265204436200221670ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/p8_image_help_base.H $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: p8_image_help_base.H,v 1.18 2013/06/10 22:08:20 jeshua Exp $ //------------------------------------------------------------------------------ // Title: p8_image_help_base.H // Description: Contains the most basic structures and defines needed for // image building and interpretation. //------------------------------------------------------------------------------ #ifndef _P8_IMAGE_HELP_BASE_H_ #define _P8_IMAGE_HELP_BASE_H_ #include // // Various image/ring buffer sizes. Must be used by all users (VBU, FSP, HB, HBI, Cronus) // const uint32_t MAX_REF_IMAGE_SIZE = 5000000; // Max reference image size. const uint32_t FIXED_SEEPROM_WORK_SPACE= 128*1024; // Max work space for Seeprom img. const uint32_t MAX_SEEPROM_IMAGE_SIZE = 56*1024; // Max Seeprom image size. // Fixed SLW image size (Ensure 128-byte alignment.) const uint32_t FIXED_SLW_IMAGE_SIZE = 1024*1024; // Fixed SLW image size for _fixed. const uint32_t FIXED_RING_BUF_SIZE = 60000; // Fixed ring buf size for _fixed. const uint8_t MAX_VPD_TYPES = 2; // #G and #R, so far. #define CHIPLET_ID_MIN 0x00 #define CHIPLET_ID_MAX 0x1F #define CHIPLET_ID_EX_MIN 0x10 #define CHIPLET_ID_EX_MAX 0x1F const uint8_t MAX_CHIPLETS = CHIPLET_ID_MAX-CHIPLET_ID_MIN+1; const uint32_t ASM_RS4_LAUNCH_BUF_SIZE = 24; // Byte size of RS4 launch buffer. const uint32_t WF_ENCAP_SIZE = 400; // Byte size of WF encapsulation. // (Actually, only 304B but may change.) const uint32_t WF_WORST_CASE_SIZE_FAC = 4; // WC WF size = 3x ring length. // (Assumes 12B per write.) // (4x w/waits instructions.) const uint32_t LISTING_STRING_SIZE = 256; const uint64_t MAX_UINT64_T = (uint64_t)0xFFFFFFFF<<32 | (uint64_t)0xFFFFFFFF; const uint8_t RING_SECTION_ID[] = { SBE_XIP_SECTION_RINGS, SBE_XIP_SECTION_DCRINGS, }; const uint8_t RING_SECTION_ID_SIZE = sizeof(RING_SECTION_ID) / sizeof(RING_SECTION_ID[0]); #ifdef __cplusplus extern "C" { #endif // Base (shared) ring layout for both RS4 and Wiggle-flip layouts. typedef struct { uint64_t entryOffset; uint64_t backItemPtr; uint32_t sizeOfThis; uint32_t sizeOfMeta; // Exact size of meta data. Arbitrary size. Not null terminated. } BaseRingLayout; // RS4 specific layout. typedef struct { uint64_t entryOffset; uint64_t backItemPtr; uint32_t sizeOfThis; uint32_t sizeOfMeta; // Exact size of meta data. Arbitrary size. Not null terminated. uint32_t ddLevel; uint8_t sysPhase; uint8_t override; uint8_t reserved1; uint8_t reserved2; } Rs4RingLayout; // PairingInfo is used for pairing, or matching, a back pointer address of a // ring block with its corresponding TOC name. typedef struct { uint64_t address; // (in) Holds PORE backPtr addr of the ring uint8_t vectorpos; // (in) Vector position of fwdPtr [0;31] // max=0 for most VPD rings // max=1 for all non-VPD rings // max=1 for perv_ VPD rings // max=15 for most VPD ex_ rings // max=31 for 16 ex_ chiplets with override char *name; // (out) TOC name uint8_t isvpd; // (out) 0: Non-VPD ring 1: VPD ring uint8_t overridable; // (out) 0: No (most VPD rings) 1: Yes (all non-VPD rings) uint8_t override; // (out) 0: base 1: override } PairingInfo; /// /// **************************************************************************** /// Function declares. /// **************************************************************************** /// int over_write_ring_data_in_image( void *io_image, const char *i_ringName, const void *i_ringData, // WF or RS4 const uint32_t i_sizeRingData, // Byte size const uint8_t i_idxVector, const uint8_t i_override, const uint8_t i_overridable ); #ifdef __cplusplus } #endif #endif //_P8_IMAGE_HELP_BASE_H_ skiboot-skiboot-5.1.13/libpore/p8_pore_api_custom.h000066400000000000000000000145271265204436200223440ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/p8_pore_api_custom.h $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ /* $Id: p8_pore_api_custom.h,v 1.5 2012/05/22 21:25:21 cmolsen Exp $ */ /* $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/utils/p8_pore_api_custom.h,v $ */ #include /* for uint32_t */ #include /* for printf */ #if !defined(__HOSTBOOT_MODULE) && !defined(__SKIBOOT__) #include /* for htonl */ #endif /** * This file should be modified by users to appropriately handle some * environment-specific operations. */ /*********************************/ /***** Logging and Tracing *****/ /*********************************/ /** * All tracing functions assume printf-style formatting */ #ifndef __FAPI /* Trace an informational message */ #define P8_PORE_ITRACE0(msg) printf("PORE> INFO: " msg "\n"); #define P8_PORE_ITRACE1(msg, arg0) printf("PORE> INFO: " msg "\n", arg0); /* Trace an error message */ #define P8_PORE_ETRACE0(msg) printf("PORE> ERROR: " msg "\n"); #define P8_PORE_ETRACE1(msg, arg0) printf("PORE> ERROR: " msg "\n", arg0); #define P8_PORE_ETRACE2(msg, arg0, arg1) printf("PORE> ERROR: " msg "\n", arg0, arg1); #define P8_PORE_ETRACE3(msg, arg0, arg1, arg2) printf("PORE> ERROR: " msg "\n", arg0, arg1, arg2); #define P8_PORE_ETRACE4(msg, arg0, arg1, arg2, arg3) printf("PORE> ERROR: " msg "\n", arg0, arg1, arg2, arg3); #define P8_PORE_ETRACE5(msg, arg0, arg1, arg2, arg3, arg4) printf("PORE> ERROR: " msg "\n", arg0, arg1, arg2, arg3, arg4); #endif /* Used for debug, Cronus/FW should leave these empty */ #define P8_PORE_DTRACE0(msg) #define P8_PORE_DTRACE1(msg, arg0) #define P8_PORE_DTRACE2(msg, arg0, arg1) #define P8_PORE_DTRACE3(msg, arg0, arg1, arg2) #define P8_PORE_DTRACE4(msg, arg0, arg1, arg2, arg3) /****** Following is only used for debug purposes ******/ /* FW/Cronus should NOT include this section */ /* DTRACE - Print debug statements to command line */ /* FTRACE - Print text PORE instructions of cpureg setup to DEBUG_FILE */ /* #define P8_PORE_DTRACE0(msg) printf("PORE> DEBUG: " msg "\n"); #define P8_PORE_DTRACE1(msg, arg0) printf("PORE> DEBUG: " msg "\n", arg0); #define P8_PORE_DTRACE2(msg, arg0, arg1) printf("PORE> DEBUG: " msg "\n", arg0, arg1); #define P8_PORE_DTRACE3(msg, arg0, arg1, arg2) printf("PORE> DEBUG: " msg "\n", arg0, arg1, arg2); #define P8_PORE_DTRACE4(msg, arg0, arg1, arg2, arg3) printf("PORE> DEBUG: " msg "\n", arg0, arg1, arg2, arg3); */ /**********************************/ /***** Endian-ness Handling *****/ /**********************************/ /** * Handle byte-swapping if necessary */ /* Default to big-endian format on both sides */ #define P8_PORE_HOST_TO_BIG32( bit32_int ) htonl(bit32_int) #define P8_PORE_BIG32_TO_HOST( bit32_int ) ntohl(bit32_int) #define P8_PORE_HOST_TO_BIG16( bit16_int ) htonl(bit16_int) #define P8_PORE_BIG16_TO_HOST( bit16_int ) ntohl(bit16_int) /* *************** Do not edit this area *************** This section is automatically updated by CVS when you check in this file. Be sure to create CVS comments when you commit so that they can be included here. $Log: p8_pore_api_custom.h,v $ Revision 1.5 2012/05/22 21:25:21 cmolsen Updated to remove FAPI tracing, which is not allowed in plain C files. Revision 1.4 2012/05/21 14:45:41 cmolsen Updated to address Gerrit review II comments about printf() usage. Revision 1.3 2012/05/15 19:53:38 cmolsen Updated to address Gerrit review comments: - Hostboot doesn't support printf(). Revision 1.2 2012/04/13 16:45:32 cmolsen Includes __HOSTBOOT_MODULE exclude of Revision 1.1 2011/08/25 12:28:38 yjkim initial check in Revision 1.10 2010/08/30 23:27:17 schwartz Added TRACE statements to include specified number of arguments Defined branch type constants Added constant for last scom op used to check if operation input to gen_scan is valid Added mult spr error constant Added p7p_pore_gen_wait API Changed additional C++ style comments to C style Initialized all variables to 0 Removed FTRACE statements Added additional information to trace statements Updated gen_scom to use the defined operation constants Updated branch gen_relbranch to use defined branch type constants Added rc check for calls to p7p_pore_gen_cpureg_status and p7p_pore_span_128byte_boundary subroutines Revision 1.9 2010/08/30 14:57:54 schwartz Removed FTRACE and associated #define statements Changed TRACE macros to multiple macros with specified number of args Revision 1.6 2010/08/26 15:13:34 schwartz Fixed more C++ style comments to C style comments Revision 1.5 2010/06/23 23:06:37 schwartz Defined additional trace functions to be used for debugging, not in FW or Cronus Revision 1.4 2010/05/24 02:34:07 schwartz Fixed errors that appear when using -Werrors flag Added in cvs logging (hopefully) */ skiboot-skiboot-5.1.13/libpore/p8_pore_table_gen_api.H000066400000000000000000000503061265204436200227050ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/p8_pore_table_gen_api.H $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: p8_pore_table_gen_api.H,v 1.27 2014/06/02 18:21:55 cmolsen Exp $ /*------------------------------------------------------------------------------*/ /* *! (C) Copyright International Business Machines Corp. 2012 */ /* *! All Rights Reserved -- Property of IBM */ /* *! *** IBM Confidential *** */ /*------------------------------------------------------------------------------*/ /* *! TITLE : p8_pore_table_gen_api.H */ /* *! DESCRIPTION : Contains all external APIs used by firmware (PHYP) to */ // generate/modify the P8 PORE SLW image with Ramming and // Scomming specific instructions to be executed on exit from // Sleep/Winkle. Also contains definitions for the ramming // PORE code. /* *! OWNER NAME : Michael Olsen Email: cmolsen@us.ibm.com */ // /* *! COMMENTS : *** VERY IMPORTANT *** */ // The "Shared RAM section", the "Pore RAM section" and the // "C-code RAM section" must closely match eachother. // /*------------------------------------------------------------------------------*/ #ifndef _P8_PORE_TABLE_GEN_API_H #define _P8_PORE_TABLE_GEN_API_H /********************************************************************/ /* Shared RAM section - begin */ /* This section MUST perfectly match the "Pore/C-code RAM section". */ /********************************************************************/ // Header defs (P8&PORE 64-bit notation where bits are numbered from left-to-right). // (Some of these defs are used in the c-specific section further down.) // ----------------------------------------------------------------------------- // Note: SPR register numbers have a swizzle about them per PPC architecture // spr(instruction) <- spr5:9 || spr0:4 // // For the PGAS routine, it is assumed that the API does the swizzling upon // building the instruction held in this structure // // Header configuration: CPU Register Operation Header // 0 - End: 1=End; 0=More // 1 - Reserved // 2:3 - Type // 00: MTSPR // 01: MTMSRD // 10: Reserved // 11: Reserved // 4:13 - SPR number in non-swizzled form (0:9) // 14:15 - Reserved for SPR nunmber expansion // 16:18 - Thread ID // 19:31 - Reserved #define RAM_HEADER_END_START 0 #define RAM_HEADER_END_MASK BITS(RAM_HEADER_END_START,1) #define RAM_HEADER_TYPE_START 2 #define RAM_HEADER_TYPE_MASK BITS(RAM_HEADER_TYPE_START,2) #define RAM_HEADER_SPRN_START 4 #define RAM_HEADER_SPRN_MASK BITS(RAM_HEADER_SPRN_START,10) #define RAM_HEADER_THREAD_START 16 #define RAM_HEADER_THREAD_MASK BITS(RAM_HEADER_THREAD_START,3) #define RAM_INSTR_START 32 #define RAM_INSTR_MASK BITS(RAM_INSTR_START,32) // MTSPR instr defs #define RAM_MTSPR_INSTR_TEMPL ( ( (uint64_t)31<<(63-5) | (uint64_t)467<<(63-30) ) ) #define RAM_MTSPR_SPR_START 11 #define RAM_MTSPR_SPR_MASK BITS(RAM_MTSPR_SPR_START,10) // Thread align defs #define RAM_HEADER_THREAD_RALIGN ( 61-16 ) // 3 Bit shift right amount #define RAM_HEADER_THREAD_LALIGN ( 61-16 ) // 3 Bit shift left amount /********************************************************************/ /* Shared RAM section - end */ /********************************************************************/ #ifdef FOR_PORE_RAMMING // Thread status CONST_UINT64_T( PROC_RAS_STAT_10013002 , ULL(0x10013002) ); // TCTL RAS Status (for each thread) // Note: the address is not included in the name to ease PGAS indexing // of these registers CONST_UINT64_T( EX_PERV_TCTL0_R_STAT , ULL(0x10013002) ); CONST_UINT64_T( EX_PERV_TCTL1_R_STAT , ULL(0x10013012) ); CONST_UINT64_T( EX_PERV_TCTL2_R_STAT , ULL(0x10013022) ); CONST_UINT64_T( EX_PERV_TCTL3_R_STAT , ULL(0x10013032) ); CONST_UINT64_T( EX_PERV_TCTL4_R_STAT , ULL(0x10013042) ); CONST_UINT64_T( EX_PERV_TCTL5_R_STAT , ULL(0x10013052) ); CONST_UINT64_T( EX_PERV_TCTL6_R_STAT , ULL(0x10013062) ); CONST_UINT64_T( EX_PERV_TCTL7_R_STAT , ULL(0x10013072) ); // Thread scratch registers // Note: the address is not included in the name to ease PGAS indexing // of these registers CONST_UINT64_T( EX_PERV_SCRATCH0 , ULL(0x10013283) ); CONST_UINT64_T( EX_PERV_SCRATCH1 , ULL(0x10013284) ); CONST_UINT64_T( EX_PERV_SCRATCH2 , ULL(0x10013285) ); CONST_UINT64_T( EX_PERV_SCRATCH3 , ULL(0x10013286) ); CONST_UINT64_T( EX_PERV_SCRATCH4 , ULL(0x10013287) ); CONST_UINT64_T( EX_PERV_SCRATCH5 , ULL(0x10013288) ); CONST_UINT64_T( EX_PERV_SCRATCH6 , ULL(0x10013289) ); CONST_UINT64_T( EX_PERV_SCRATCH7 , ULL(0x1001328A) ); // Ramming settings. CONST_UINT64_T( RAM_STATUS_REG_AFTER_RAM, 0x5000000000000000); CONST_UINT64_T( RAM_COMPLETE_POLLS, 0x0000000000000040); // mfspr gpr0, scratch0 opcode left-shifted 29 bits, ready for ramming. CONST_UINT64_T( MTSPR_SCRATCH0_GPR0_RAM_READY, (0x000000007C1543A6<<29)); CONST_UINT64_T( MFSPR_GPR0_SCRATCH0_RAM_READY, (0x000000007C1542A6<<29)); CONST_UINT64_T( MTMSRD_GPR0_RAM_READY, (0x000000007C000164<<29)); CONST_UINT64_T( MFMSR_GPR0_RAM_READY, (0x000000007C0000A6<<29)); // Predefined MSR content during Ramming CONST_UINT64_T( P8_PORE_MSR_DURING_RAM, (0x9000000002802000) ); // "reset" value of SCRATCH0 to ensure it gets updated from GPR0 CONST_UINT64_T( SCRATCH0_RESET_VALUE, (0xABBA99EBBA33DADA) ); #ifdef __ASSEMBLER__ /***********************************************************************/ /* Pore RAM section - begin */ /* This section MUST perfectly match the "Shared/C-code RAM section". */ /***********************************************************************/ .set RAM_HEADER, 0 .set RAM_INSTR, 4 .set RAM_DATA, 8 .set RAM_ENTRY_LENGTH, 16 /***********************************************************************/ /* Pore RAM section - end */ /***********************************************************************/ #endif // __ASSEMBLER__ #else // Not FOR_PORE_RAMMING //#include #ifndef PPC_HYP #include #endif // PPC_HYP #ifndef __P8_PORE_TABLE_GEN_API_C #include #endif //#include //#include // Defining local versions of BITS and BIT // Create a multi-bit mask of \a n bits starting at bit \a b #define BITS(b, n) ((ULL(0xffffffffffffffff) << (64 - (n))) >> (b)) #define BITS32(b,n) (uint32_t)((ULL(0xffffffff) << (32 - (n))) >> (b)) // Create a single bit mask at bit \a b #define BIT(b) BITS((b), 1) // Header defs (C notation where bits are numbered from right-to-left, and reducing to 32-bit) #define RAM_HEADER_END_START_C ( 31-RAM_HEADER_END_START+1-1 ) #define RAM_HEADER_END_MASK_C (uint32_t)(RAM_HEADER_END_MASK>>32) #define RAM_HEADER_TYPE_START_C ( 31-RAM_HEADER_TYPE_START+1-2 ) #define RAM_HEADER_TYPE_MASK_C (uint32_t)(RAM_HEADER_TYPE_MASK>>32) #define RAM_HEADER_SPRN_START_C ( 31-RAM_HEADER_SPRN_START+1-10 ) #define RAM_HEADER_SPRN_MASK_C (uint32_t)(RAM_HEADER_SPRN_MASK>>32) #define RAM_HEADER_THREAD_START_C ( 31-RAM_HEADER_THREAD_START+1-3 ) #define RAM_HEADER_THREAD_MASK_C (uint32_t)(RAM_HEADER_THREAD_MASK>>32) // MTSPR instr defs #define RAM_MTSPR_INSTR_TEMPL_C ( ( (uint32_t)31<<(31-5) | (uint32_t)467<<(31-30) ) ) #define RAM_MTSPR_SPR_START_C ( 31-RAM_MTSPR_SPR_START+1-10 ) //#define RAM_MTSPR_SPR_MASK_C (uint32_t)(BITS(RAM_MTSPR_SPR_START,10)>>32) #define RAM_MTSPR_SPR_MASK_C (uint32_t)(RAM_MTSPR_SPR_MASK>>32) // MTMSR innstr def #define RAM_MTMSRD_INSTR_TEMPL_C ( ( (uint32_t)31<<(31-5) | (uint32_t)178<<(31-30) ) ) /* Other defs needed for ramming and scomming */ // TOC names #define SLW_HOST_REG_VECTOR_TOC_NAME "slw_host_reg_vector" #define SLW_HOST_SCOM_NC_VECTOR_TOC_NAME "slw_host_scom_nc_vector" #define SLW_HOST_SCOM_L2_VECTOR_TOC_NAME "slw_host_scom_l2_vector" #define SLW_HOST_SCOM_L3_VECTOR_TOC_NAME "slw_host_scom_l3_vector" // Defines for slw_build() to update "runtime_scom" pointers w/pointer to // "sub_slw_runtime_scom" subroutines at SLW image build time. #define HOST_RUNTIME_SCOM_TOC_NAME "host_runtime_scom" // Null 1st, then fill w/addr of SLW_RUNTIME_SCOM_TOC_NAME #define SLW_RUNTIME_SCOM_TOC_NAME "sub_slw_runtime_scom" // The following two provide TRANSITIONAL SUPPORT only. TO BE REMOVED ASAP. #define EX_ENABLE_RUNTIME_SCOM_TOC_NAME "ex_enable_runtime_scom" #define SLW_EX_ENABLE_RUNTIME_SCOM_TOC_NAME "sub_slw_ex_enable_runtime_scom" #define SCAN_MAX_ROTATE_38XXX_NAME "scan_max_rotate_38xxx" #define SCAN_ROTATE_DEFAULT 110 // Limit suggested by Tilman. #define SCAN_MAX_ROTATE 0x00000FE0 #define SCAN_MAX_ROTATE_LONG 0x000FFFFF // All 1s in BITS 12->31. //#define SCAN_MAX_ROTATE_LONG 0x000000D0 // Experimental max val #define OVER_SAMPLING_POLL 10 #define WAITS_POLL_MIN 32 // RAM table defines #define XIPSIZE_RAM_ENTRY ( (sizeof(RamTableEntry)+7)/8*8 ) #define SLW_MAX_CORES 16 #define SLW_MAX_CPUREGS_CORE 10 #define SLW_MAX_CPUREGS_THREADS 5 #define SLW_CORE_THREADS 8 #define SLW_MAX_CPUREGS_OPS ( SLW_MAX_CPUREGS_CORE + \ SLW_CORE_THREADS*SLW_MAX_CPUREGS_THREADS ) #define SLW_RAM_TABLE_SPACE_PER_CORE ( SLW_MAX_CPUREGS_OPS * XIPSIZE_RAM_ENTRY ) #define SLW_RAM_TABLE_SIZE ( SLW_MAX_CORES * SLW_RAM_TABLE_SPACE_PER_CORE ) // SPR and MSR values for i_regName enum { P8_SPR_HRMOR = 313, P8_SPR_HMEER = 337, P8_SPR_PMICR = 852, P8_SPR_PMCR = 884, P8_SPR_HID0 = 1008, P8_SPR_HID1 = 1009, P8_SPR_HID4 = 1012, P8_SPR_HID5 = 1014, P8_CORE_XTRA8 =10008, P8_CORE_XTRA9 =10009, P8_SPR_HSPRG0 = 304, P8_SPR_LPCR = 318, P8_MSR_MSR = 2000, P8_THRD_XTRA3 =20003, P8_THRD_XTRA4 =20004 }; // SCOM table defines - Common #define XIPSIZE_SCOM_ENTRY 16 // SCOM table defines - Non-cache section #define SLW_MAX_SCOMS_NC 32 #define SLW_SCOM_TABLE_SPACE_PER_CORE_NC ( (SLW_MAX_SCOMS_NC+1)*XIPSIZE_SCOM_ENTRY ) // Add 1 for RNNN IIS #define SLW_SCOM_TABLE_SIZE_NC ( SLW_MAX_CORES * SLW_SCOM_TABLE_SPACE_PER_CORE_NC ) // SCOM table defines - L2 section #define SLW_MAX_SCOMS_L2 16 #define SLW_SCOM_TABLE_SPACE_PER_CORE_L2 ( (SLW_MAX_SCOMS_L2+1)*XIPSIZE_SCOM_ENTRY ) // Add 1 for RNNN IIS #define SLW_SCOM_TABLE_SIZE_L2 ( SLW_MAX_CORES * SLW_SCOM_TABLE_SPACE_PER_CORE_L2 ) // SCOM table defines - L3 section #define SLW_MAX_SCOMS_L3 16 #define SLW_SCOM_TABLE_SPACE_PER_CORE_L3 ( (SLW_MAX_SCOMS_L3+1)*XIPSIZE_SCOM_ENTRY ) // Add 1 for RNNN IIS #define SLW_SCOM_TABLE_SIZE_L3 ( SLW_MAX_CORES * SLW_SCOM_TABLE_SPACE_PER_CORE_L3 ) #define SLW_SCOM_TABLE_SIZE_ALL ( SLW_SCOM_TABLE_SIZE_NC + SLW_SCOM_TABLE_SIZE_L2 + SLW_SCOM_TABLE_SIZE_L3) // RAM and SCOM sub-section offsets from beginning of .slw section. #define SLW_RAM_TABLE_OFFSET 0 #define SLW_SCOM_TABLE_OFFSET_NC (SLW_RAM_TABLE_OFFSET + SLW_RAM_TABLE_SIZE) #define SLW_SCOM_TABLE_OFFSET_L2 (SLW_SCOM_TABLE_OFFSET_NC + SLW_SCOM_TABLE_SIZE_NC) #define SLW_SCOM_TABLE_OFFSET_L3 (SLW_SCOM_TABLE_OFFSET_L2 + SLW_SCOM_TABLE_SIZE_L2) #define SLW_TABLE_SIZE_ALL (SLW_RAM_TABLE_SIZE + SLW_SCOM_TABLE_SIZE_ALL) // Enumeration of Scom sections in .slw section. enum { P8_SCOM_SECTION_NC = 0, P8_SCOM_SECTION_L2 = 1, P8_SCOM_SECTION_L3 = 2, P8_SCOM_SECTION_MAX_VALUE = 2 }; // SLW section size (Ensure 128-byte alignment.) #define FIXED_SLW_SECTION_SIZE (SLW_TABLE_SIZE_ALL/128+(SLW_TABLE_SIZE_ALL%128+127)/128)*128 // FFDC section size (Ensure 128-byte alignment.) #define FIXED_FFDC_SECTION_SIZE 640*(SLW_MAX_CORES+1) // SCOM/CID masks and ranges #define P8_CID_EX_LOW 0x10 // Lowest EX chiplet addr #define P8_CID_EX_HIGH 0x1f // Highest EX chiplet addr // SCOM Operators #define P8_PORE_SCOM_FIRST_OP 0 // First supported Scom operation. #define P8_PORE_SCOM_APPEND 0 // Add Scom to end of table or at first PORE NOP // instruction, whichever comes first. #define P8_PORE_SCOM_REPLACE 1 // Repl 1st matching Scom addr or treat as APPEND // if Scom entry is not found. #define P8_PORE_SCOM_OR 2 // Overlay data onto existing Scom by bitwise OR. #define P8_PORE_SCOM_AND 3 // Overlay data onto existing Scom by bitwise AND. #define P8_PORE_SCOM_NOOP 4 // Replace existing Scom with a PORE NOP instruction, // NNNN. #define P8_PORE_SCOM_RESET 5 // Delete all entries for given coreID. Replace with // PORE RET instructions, RNNN. #define P8_PORE_SCOM_OR_APPEND 6 // Same as OR but treat as APPEND if Scom entry is // not found. #define P8_PORE_SCOM_AND_APPEND 7 // Same as AND but treat as APPEND if Scom entry is // not found. #define P8_PORE_SCOM_LAST_OP 7 // Last supported Scom operation. // Enumeration of SLW image build modes. enum { P8_SLW_MODEBUILD_IPL = 0, P8_SLW_MODEBUILD_REBUILD = 1, P8_SLW_MODEBUILD_SRAM = 2, P8_SLW_MODEBUILD_MAX_VALUE = 2 }; // Return codes #define SLW_RAM_SUCCESS 0 #define SLW_RAM_HEADERS_NOT_SYNCED 1 #define SLW_RAM_IMAGE_SIZE_MISMATCH 2 #define SLW_RAM_TABLE_ENTRY_OVERFLOW 3 #define SLW_RAM_CODE_ERROR 4 #define SLW_RAM_INVALID_PARAMETER 5 #define SLW_RAM_WARNING_TABLE_CONTAMINATION 6 #ifndef PPC_HYP #ifdef __cplusplus extern "C" { #endif #endif // PPC_HYP /********************************************************************/ /* C-code RAM section - begin */ /* This section MUST perfectly match the "Shared/Pore RAM section". */ /********************************************************************/ typedef struct ram_instr_t { uint32_t header; uint32_t instr; uint64_t data; } RamTableEntry; /********************************************************************/ /* C-code RAM section - end */ /********************************************************************/ // SLW supported SPR registers typedef struct { const char *name; uint32_t value; uint32_t swizzled; } SlwSprRegs; extern const SlwSprRegs SLW_SPR_REGS[]; extern const int SLW_SPR_REGS_SIZE; /* Name: p8_pore_gen_cpureg() * Description: Populates ramming entries in the .slw section * Parameter list: i_image - pointer to SLW mainstore image * i_sizeImage - size of SLW mainstore image * i_regName - unswizzled SPR register value * i_regData - data to write to SPR register * i_coreId - the core ID to operate on * i_threadId - the thread ID to operate on */ uint32_t p8_pore_gen_cpureg(void *io_image, uint32_t i_sizeImage, uint32_t i_regName, uint64_t i_regData, uint32_t i_coreId, uint32_t i_threadId); /* Name: p8_pore_gen_scom() * Description: Populates scom entries in the .slw section * Parameter list: i_image - pointer to SLW mainstore image * i_sizeImage - size of SLW mainstore image * i_scomAddr - scom register address * i_coreId - the core ID [0:15] * i_scomData - 64-bit data to put in scom register * i_operation - what to do with the scom data [0:5] * i_section - SCOM section [0,2,3] */ uint32_t p8_pore_gen_scom(void *io_image, uint32_t i_sizeImage, uint32_t i_scomAddr, uint32_t i_coreId, uint64_t i_scomData, uint32_t i_operation, uint32_t i_section); /* Name: p8_pore_gen_cpureg_fixed() * Description: Populates ramming entries in the .slw section * Parameter list: i_image - pointer to SLW mainstore image * i_modeBuild - 0: HB/IPL mode, 1: PHYP/Rebuild mode, 2: SRAM mode. * i_sizeImage - size of SLW mainstore image * i_regName - unswizzled SPR register value * i_regData - data to write to SPR register * i_coreId - the core ID to operate on * i_threadId - the thread ID to operate on */ uint32_t p8_pore_gen_cpureg_fixed(void *io_image, uint8_t i_modeBuild, uint32_t i_regName, uint64_t i_regData, uint32_t i_coreId, uint32_t i_threadId); /* Name: p8_pore_gen_scom_fixed() * Description: Populates scom entries in the .slw section * Parameter list: i_image - pointer to SLW mainstore image * i_modeBuild - 0: HB/IPL mode, 1: PHYP/Rebuild mode, 2: SRAM mode. * i_scomAddr - scom register address * i_coreId - the core ID [0:15] * i_scomData - 64-bit data to put in scom register * i_operation - what to do with the scom data [0:5] * i_section - 0: General Scoms, 1: L2 cache, 2: L3 cache */ uint32_t p8_pore_gen_scom_fixed(void *io_image, uint8_t i_modeBuild, uint32_t i_scomAddr, uint32_t i_coreId, uint64_t i_scomData, uint32_t i_operation, uint32_t i_section); #ifndef PPC_HYP #ifdef __cplusplus } #endif #endif // PPC_HYP #endif // FOR_PORE_RAMMING #endif // _P8_PORE_TABLE_GEN_API_H skiboot-skiboot-5.1.13/libpore/p8_pore_table_gen_api_fixed.C000066400000000000000000001062621265204436200240620ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/p8_pore_table_gen_api_fixed.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2013,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: p8_pore_table_gen_api_fixed.C,v 1.15 2014/05/30 20:31:24 cmolsen Exp $ // /*------------------------------------------------------------------------------*/ /* *! (C) Copyright International Business Machines Corp. 2012 */ /* *! All Rights Reserved -- Property of IBM */ /* *! *** IBM Confidential *** */ /*------------------------------------------------------------------------------*/ /* *! TITLE : p8_pore_table_gen_api_fixed.C */ /* *! DESCRIPTION : PORE SLW table generaion APIs */ /* *! OWNER NAME : Michael Olsen Email: cmolsen@us.ibm.com */ // /* *! USAGE : To build for PHYP command-line - */ /* buildecmdprcd -C "p8_pore_table_gen_api_fixed.C" -c "p8_pore_table_static_data.c,sbe_xip_image.c,pore_inline_assembler.c" -u "SLW_COMMAND_LINE_RAM" p8_pore_table_gen_api_fixed_main.C */ // /* *! COMMENTS : - The DYNAMIC_RAM_TABLE_PPD was dropped in v1.12 of this */ /* code. See v1.12 for explanation and code implementation. */ // /*------------------------------------------------------------------------------*/ #define __P8_PORE_TABLE_GEN_API_C #include #include #include /* // io_image - pointer to SLW image // i_modeBuild - 0: HB/IPL mode, 1: PHYP/Rebuild mode, 2: SRAM mode. // i_regName - unswizzled enum SPR value (NOT a name) // i_regData - data to write // i_coreIndex - core ID = [0:15] // i_threadIndex - thread to operate on = [0:7]. */ uint32_t p8_pore_gen_cpureg_fixed( void *io_image, uint8_t i_modeBuild, uint32_t i_regName, uint64_t i_regData, uint32_t i_coreId, uint32_t i_threadId) { uint32_t rc=0, rcLoc=0, iCount=0; int i=0, iReg=-1; uint64_t xipSlwRamSection; void *hostSlwRamSection; void *hostSlwSectionFixed; uint64_t xipRamTableThis; void *hostRamVector; void *hostRamTableThis=NULL; void *hostRamEntryThis=NULL, *hostRamEntryNext=NULL; uint8_t bNewTable=0, bFound=0; uint8_t bEntryEnd=1, headerType=0; SbeXipSection xipSection; SbeXipItem xipTocItem; RamTableEntry ramEntryThis, *ramEntryNext; uint32_t sprSwiz=0; uint8_t bReplaceEntry=0; uint32_t headerNext=0; uint32_t instrNext=0; // ------------------------------------------------------------------------- // Validate Ramming parameters. // // ...check mode build if (i_modeBuild>P8_SLW_MODEBUILD_MAX_VALUE) { MY_ERR("modeBuild=%i invalid. Valid range is [0;%i].", i_modeBuild,P8_SLW_MODEBUILD_MAX_VALUE); rcLoc = 1; } // ...check register value bFound = 0; for (i=0;i=SLW_MAX_CORES) { MY_ERR("Core ID = %i is not within valid range of [0;%i]\n",i_coreId,SLW_MAX_CORES-1); rcLoc = 1; } // ...check thread ID // - ensure it's zero if SPR is not thread scoped, i.e. if SPR is core scoped. // - error out if threadId exceed max num of threads. if (i_regName!=P8_SPR_HSPRG0 && i_regName!=P8_SPR_LPCR && i_regName!=P8_MSR_MSR) { i_threadId = 0; } if (i_threadId>=SLW_CORE_THREADS) { MY_ERR("Thread ID = %i is not within valid range of [0;%i]\n",i_threadId,SLW_CORE_THREADS-1); rcLoc = 1; } if (rcLoc) return IMGBUILD_ERR_RAM_INVALID_PARM; rcLoc = 0; // ------------------------------------------------------------------------- // Check slw section location and size. (Mainly needed for fixed image.) // if (i_modeBuild==P8_SLW_MODEBUILD_IPL || i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. hostSlwSectionFixed = (void*)( (uintptr_t)io_image + FIXED_SLW_IMAGE_SIZE - FIXED_FFDC_SECTION_SIZE - FIXED_SLW_SECTION_SIZE ); // Even though we shouldn't call this api during a rebuild, it should be // safe to do so in this particular case since none of the info requested // is supposed to be moved during a rebuild. rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); if (rc) { MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); return IMGBUILD_ERR_GET_SECTION; } hostSlwRamSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); if (hostSlwSectionFixed!=hostSlwRamSection) { MY_ERR("hostSlwSectionFixed != hostSlwRamSection(from image api).\n"); return IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED; } else { MY_INF("hostSlwSectionFixed == hostSlwRamSection(from image api).\n"); } } else { // SRAM non-fixed image. rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); if (rc) { MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); return IMGBUILD_ERR_GET_SECTION; } hostSlwRamSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); sbe_xip_host2pore( io_image, hostSlwRamSection, &xipSlwRamSection); } // ------------------------------------------------------------------------- // Cross check SPR register and table defines // if (SLW_SPR_REGS_SIZE!=(SLW_MAX_CPUREGS_CORE+SLW_MAX_CPUREGS_THREADS)) { MY_ERR("Defines in *.H header file not in sync.\n"); return IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED; } if (xipSection.iv_size!=FIXED_SLW_SECTION_SIZE) { MY_ERR("Fixed SLW table size in *.H header file differs from SLW section size in image.\n"); MY_ERR("Check code or image version.\n"); return IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED; } // ------------------------------------------------------------------------- // Summarize parameters and checking results. // MY_INF("Input parameter checks - OK\n"); MY_INF("\tMode build= %i\n",i_modeBuild); MY_INF("\tRegister = (%s,%i)\n",SLW_SPR_REGS[iReg].name,SLW_SPR_REGS[iReg].value); MY_INF("\tCore ID = %i\n",i_coreId); MY_INF("\tThread ID = %i\n",i_threadId); MY_INF("Image validation and size checks - OK\n"); MY_INF("\tSLW section size= %i\n",xipSection.iv_size); // ------------------------------------------------------------------------- // Locate RAM vector and locate RAM table associated with "This" core ID. // if (i_modeBuild==P8_SLW_MODEBUILD_IPL || i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. hostRamTableThis = (void*)( (uintptr_t)io_image + FIXED_SLW_IMAGE_SIZE - FIXED_FFDC_SECTION_SIZE - FIXED_SLW_SECTION_SIZE + SLW_RAM_TABLE_SPACE_PER_CORE*i_coreId ); if (*(uintptr_t*)hostRamTableThis) { // Table content NOT empty. bNewTable = 0; // So, NOT new table. } else { // Table content empty. bNewTable = 1; // So, new table. } } else { // SRAM non-fixed image. rc = sbe_xip_find( io_image, SLW_HOST_REG_VECTOR_TOC_NAME, &xipTocItem); if (rc) { MY_ERR("Probably invalid key word for SLW_HOST_REG_VECTOR_TOC_NAME.\n"); return IMGBUILD_ERR_KEYWORD_NOT_FOUND; } sbe_xip_pore2host( io_image, xipTocItem.iv_address, &hostRamVector); xipRamTableThis = myRev64(*((uint64_t*)hostRamVector + i_coreId)); if (xipRamTableThis) { sbe_xip_pore2host( io_image, xipRamTableThis, &hostRamTableThis); bNewTable = 0; } else { hostRamTableThis = (void*)( (uintptr_t)hostSlwRamSection + SLW_RAM_TABLE_SPACE_PER_CORE*i_coreId ); bNewTable = 1; } } // ------------------------------------------------------------------------- // Create most of the RAM entry, so it can be used to find a potential existing entry to // replace. Postpone decision about bEntryEnd and assume its zero for now (not end). // if (i_regName==P8_MSR_MSR) { // ...make the MSR header headerType = 0x1; // MTMSRD header. ramEntryThis.header = ( ((uint32_t)headerType) << RAM_HEADER_TYPE_START_C & RAM_HEADER_TYPE_MASK_C ) | ( i_threadId << RAM_HEADER_THREAD_START_C & RAM_HEADER_THREAD_MASK_C ); // ...make the MSR instr ramEntryThis.instr = RAM_MTMSRD_INSTR_TEMPL_C; } else { // ...make the SPR header headerType = 0x0; // MTSPR header. ramEntryThis.header = ( ((uint32_t)headerType) << RAM_HEADER_TYPE_START_C & RAM_HEADER_TYPE_MASK_C ) | ( i_regName << RAM_HEADER_SPRN_START_C & RAM_HEADER_SPRN_MASK_C ) | ( i_threadId << RAM_HEADER_THREAD_START_C & RAM_HEADER_THREAD_MASK_C ); // ...make the SPR instr sprSwiz = i_regName>>5 | (i_regName & 0x0000001f)<<5; if (sprSwiz!=SLW_SPR_REGS[iReg].swizzled) { MY_ERR("Inconsistent swizzle rules implemented. Check code. Dumping data.\n"); MY_ERR("\tsprSwiz (on-the-fly-calc)=%i\n",sprSwiz); MY_ERR("\tSLW_SPR_REGS[%i].swizzled=%i\n",iReg,SLW_SPR_REGS[iReg].swizzled); return IMGBUILD_ERR_RAM_CODE; } ramEntryThis.instr = RAM_MTSPR_INSTR_TEMPL_C | ( ( sprSwiz< First entry // - Existing Ram entry => Replace said entry // - Existing table, new Ram entry => Last entry // bReplaceEntry = 0; if (bNewTable) { // Append to beginning of agreed upon static Ram table position for this coreId. bEntryEnd = 1; ramEntryThis.header = ( ((uint32_t)bEntryEnd) << RAM_HEADER_END_START_C & RAM_HEADER_END_MASK_C ) | ramEntryThis.header; hostRamEntryThis = hostRamTableThis; if (i_modeBuild==P8_SLW_MODEBUILD_SRAM) { // Update RAM vector (since it is currently NULL) *((uint64_t*)hostRamVector + i_coreId) = myRev64( xipSlwRamSection + SLW_RAM_TABLE_SPACE_PER_CORE*i_coreId ); } } else { // Append at end of existing Ram table for this coreId // or // Replace an existing Ram entry hostRamEntryNext = hostRamTableThis; ramEntryNext = (RamTableEntry*)hostRamEntryNext; headerNext = myRev32(ramEntryNext->header); instrNext = myRev32(ramEntryNext->instr); iCount = 1; // Examine all entries, except last entry. while ((headerNext & RAM_HEADER_END_MASK_C)==0 && bReplaceEntry==0) { if (iCount>=SLW_MAX_CPUREGS_OPS) { MY_ERR("Bad table! Header end bit not found and RAM table full (=%i entries).\n",SLW_MAX_CPUREGS_OPS); return IMGBUILD_ERR_RAM_TABLE_END_NOT_FOUND; } if (ramEntryThis.header==headerNext && ramEntryThis.instr==instrNext) { // Its a replacement. Stop searching. Go do the replacement. bReplaceEntry = 1; hostRamEntryThis = hostRamEntryNext; } else { hostRamEntryNext = (void*)((uint8_t*)hostRamEntryNext + XIPSIZE_RAM_ENTRY); ramEntryNext = (RamTableEntry*)hostRamEntryNext; headerNext = myRev32(ramEntryNext->header); instrNext = myRev32(ramEntryNext->instr); iCount++; } } if (bReplaceEntry==0) { // Examine the last entry. if (headerNext & RAM_HEADER_END_MASK_C) { // Now we know for sure that our new Ram entry will also be the last, either as a // replace or append. So put the end bit into the new entry. bEntryEnd = 1; ramEntryThis.header = ( ((uint32_t)bEntryEnd) << RAM_HEADER_END_START_C & RAM_HEADER_END_MASK_C ) | ramEntryThis.header; // Determine if to replace or append. if (ramEntryThis.header==headerNext && ramEntryThis.instr==instrNext) { // Its a replacement. And it would be legal to replace the very last Ram in a completely full table. if (iCount<=SLW_MAX_CPUREGS_OPS) { bReplaceEntry = 1; hostRamEntryThis = hostRamEntryNext; } else { MY_ERR("RAM table is full. Max %i entries allowed.\n",SLW_MAX_CPUREGS_OPS); return IMGBUILD_ERR_RAM_TABLE_FULL; } } else { // Its an append. Make sure there's room for one more Ram entry. if (iCountheader = ramEntryNext->header & myRev32(~RAM_HEADER_END_MASK_C); hostRamEntryThis = (void*)((uint8_t*)hostRamEntryNext + XIPSIZE_RAM_ENTRY); } else { MY_ERR("RAM table is full. Max %i entries allowed.\n",SLW_MAX_CPUREGS_OPS); return IMGBUILD_ERR_RAM_TABLE_FULL; } } } else { MY_ERR("We should never get here. Check code. Dumping data:\n"); MY_ERR("myRev32(ramEntryNext->header) = 0x%08x\n",myRev32(ramEntryNext->header)); MY_ERR("RAM_HEADER_END_MASK_C = 0x%08x\n",RAM_HEADER_END_MASK_C); return IMGBUILD_ERR_RAM_CODE; } } } // Summarize new table entry data MY_INF("New table entry data (host format):\n"); MY_INF("\theader = 0x%08x\n",ramEntryThis.header); MY_INF("\tinstr = 0x%08x\n",ramEntryThis.instr); MY_INF("\tdata = 0x%016llx\n",ramEntryThis.data); // ------------------------------------------------------------------------- // Insert the new RAM entry into the table in BE format. // ramEntryNext = (RamTableEntry*)hostRamEntryThis; // ...some redundant checking if (bNewTable) { // For any new table, the insertion location should be clean. We check for this here. if (myRev32(ramEntryNext->header)!=0) { MY_ERR("WARNING : Table entry location should be empty for a new table. Check code and image. Dumping data:\n"); MY_ERR("\theader = 0x%08x\n",myRev32(ramEntryNext->header)); MY_ERR("\tinstr = 0x%08x\n",myRev32(ramEntryNext->instr)); MY_ERR("\tdata = 0x%016llx\n",myRev64(ramEntryNext->data)); rc = IMGBUILD_WARN_RAM_TABLE_CONTAMINATION; } } // ..insert the new Ram entry. ramEntryNext->header = myRev32(ramEntryThis.header); ramEntryNext->instr = myRev32(ramEntryThis.instr); ramEntryNext->data = myRev64(ramEntryThis.data); return rc; } /* // io_image - Pointer to SLW image. // i_modeBuild - 0: HB/IPL mode, 1: PHYP/Rebuild mode, 2: SRAM mode. // i_scomAddr - Scom address. // i_coreId - The core ID [0:15]. // i_scomData - Data to write to scom register. // i_operation - What to do with the scom addr and data. // i_section - 0: General Scoms, 1: L2 cache, 2: L3 cache. */ uint32_t p8_pore_gen_scom_fixed(void *io_image, uint8_t i_modeBuild, uint32_t i_scomAddr, uint32_t i_coreId, // [0:15] uint64_t i_scomData, uint32_t i_operation, // [0:7] uint32_t i_section) // [0,1,2] { uint32_t rc=0, rcLoc=0, iEntry=0; uint32_t chipletId=0; uint32_t operation=0; uint32_t entriesCount=0, entriesMatch=0, entriesNOP=0; void *hostSlwSection; void *hostSlwSectionFixed; uint64_t xipScomTableThis; void *hostScomVector, *hostScomTableThis; void *hostScomEntryNext; // running entry pointer void *hostScomEntryMatch=NULL; // pointer to entry that matches scomAddr void *hostScomEntryRET=NULL; // pointer to first return instr after table void *hostScomEntryNOP=NULL; // pointer to first nop IIS uint8_t bufIIS[XIPSIZE_SCOM_ENTRY], bufNOP[4], bufRET[4]; SbeXipSection xipSection; SbeXipItem xipTocItem; PoreInlineContext ctx; // ------------------------------------------------------------------------- // Validate Scom parameters. // // ...check if valid Scom register (is there anything we can do here to check?) // Skipping check. We blindly trust caller. // // ...check mode build if (i_modeBuild>P8_SLW_MODEBUILD_MAX_VALUE) { MY_ERR("modeBuild=%i invalid. Valid range is [0;%i].", i_modeBuild,P8_SLW_MODEBUILD_MAX_VALUE); rcLoc = 1; } // ...check Scom operation if (i_operation>P8_PORE_SCOM_LAST_OP) { MY_ERR("Scom operation = %i is not within valid range of [%d;%d]\n", i_operation, P8_PORE_SCOM_FIRST_OP, P8_PORE_SCOM_LAST_OP); rcLoc = 1; } // ...check that core ID corresponds to valid chiplet ID chipletId = i_coreId + P8_CID_EX_LOW; if (chipletIdP8_CID_EX_HIGH) { MY_ERR("Chiplet ID = 0x%02x is not within valid range of [0x%02x;0x%02x]\n", chipletId, P8_CID_EX_LOW, P8_CID_EX_HIGH); rcLoc = 1; } if (rcLoc) return IMGBUILD_ERR_SCOM_INVALID_PARM; rcLoc = 0; // ------------------------------------------------------------------------- // Check slw section location and size. (Mainly needed for fixed image.) // if (i_modeBuild==P8_SLW_MODEBUILD_IPL || i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. hostSlwSectionFixed = (void*)( (uintptr_t)io_image + FIXED_SLW_IMAGE_SIZE - FIXED_FFDC_SECTION_SIZE - FIXED_SLW_SECTION_SIZE ); // Even though we shouldn't call this api during a rebuild, it should be // safe to do so in this particular case since none of the info requested // is supposed to be moved during a rebuild. rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); if (rc) { MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); return IMGBUILD_ERR_GET_SECTION; } hostSlwSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); if (hostSlwSectionFixed!=hostSlwSection) { MY_ERR("hostSlwSectionFixed != hostSlwSection(from image api).\n"); return IMGBUILD_ERR_SCOM_HDRS_NOT_SYNCD; } else { MY_INF("hostSlwSectionFixed == hostSlwSection(from image api).\n"); } } else { // SRAM non-fixed image. rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); if (rc) { MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); return IMGBUILD_ERR_GET_SECTION; } hostSlwSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); } // ------------------------------------------------------------------------- // Check .slw section size and cross-check w/header define. // if (xipSection.iv_size!=FIXED_SLW_SECTION_SIZE) { MY_ERR("SLW table size in *.H header file (=%ld) differs from SLW section size in image (=%i).\n",FIXED_SLW_SECTION_SIZE,xipSection.iv_size); MY_ERR("Check code or image version.\n"); return IMGBUILD_ERR_SCOM_HDRS_NOT_SYNCD; } // ------------------------------------------------------------------------- // Summarize parameters and checking results. // MY_INF("Input parameter checks - OK\n"); MY_INF("\tRegister = 0x%08x\n",i_scomAddr); MY_INF("\tOperation = %i\n",i_operation); MY_INF("\tSection = %i\n",i_section); MY_INF("\tCore ID = %i\n",i_coreId); MY_INF("Image validation and size checks - OK\n"); MY_INF("\tSLW section size= %i\n",xipSection.iv_size); // ------------------------------------------------------------------------- // Locate Scom vector according to i_section and then locate Scom table // associated with "This" core ID. // if (i_modeBuild==P8_SLW_MODEBUILD_IPL || i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. switch (i_section) { case P8_SCOM_SECTION_NC: hostScomTableThis = (void*)( (uintptr_t)hostSlwSection + SLW_RAM_TABLE_SIZE + SLW_SCOM_TABLE_SPACE_PER_CORE_NC*i_coreId ); break; case P8_SCOM_SECTION_L2: hostScomTableThis = (void*)( (uintptr_t)hostSlwSection + SLW_RAM_TABLE_SIZE + SLW_SCOM_TABLE_SIZE_NC + SLW_SCOM_TABLE_SPACE_PER_CORE_L2*i_coreId ); break; case P8_SCOM_SECTION_L3: hostScomTableThis = (void*)( (uintptr_t)hostSlwSection + SLW_RAM_TABLE_SIZE + SLW_SCOM_TABLE_SIZE_NC + SLW_SCOM_TABLE_SIZE_L2 + SLW_SCOM_TABLE_SPACE_PER_CORE_L3*i_coreId ); break; default: MY_ERR("Invalid value for i_section (=%i).\n",i_section); MY_ERR("Valid values for i_section = [%i,%i,%i].\n", P8_SCOM_SECTION_NC,P8_SCOM_SECTION_L2,P8_SCOM_SECTION_L3); return IMGBUILD_ERR_SCOM_INVALID_SUBSECTION; break; } } else { // SRAM non-fixed image. switch (i_section) { case P8_SCOM_SECTION_NC: rc = sbe_xip_find( io_image, SLW_HOST_SCOM_NC_VECTOR_TOC_NAME, &xipTocItem); if (rc) { MY_ERR("Probably invalid key word for SLW_HOST_SCOM_NC_VECTOR_TOC_NAME.\n"); return IMGBUILD_ERR_KEYWORD_NOT_FOUND; } break; case P8_SCOM_SECTION_L2: rc = sbe_xip_find( io_image, SLW_HOST_SCOM_L2_VECTOR_TOC_NAME, &xipTocItem); if (rc) { MY_ERR("Probably invalid key word for SLW_HOST_SCOM_L2_VECTOR_TOC_NAME.\n"); return IMGBUILD_ERR_KEYWORD_NOT_FOUND; } break; case P8_SCOM_SECTION_L3: rc = sbe_xip_find( io_image, SLW_HOST_SCOM_L3_VECTOR_TOC_NAME, &xipTocItem); if (rc) { MY_ERR("Probably invalid key word for SLW_HOST_SCOM_L3_VECTOR_TOC_NAME.\n"); return IMGBUILD_ERR_KEYWORD_NOT_FOUND; } break; default: MY_ERR("Invalid value for i_section (=%i).\n",i_section); MY_ERR("Valid values for i_section = [%i,%i,%i].\n", P8_SCOM_SECTION_NC,P8_SCOM_SECTION_L2,P8_SCOM_SECTION_L3); return IMGBUILD_ERR_SCOM_INVALID_SUBSECTION; } MY_INF("xipTocItem.iv_address = 0x%016llx\n",xipTocItem.iv_address); sbe_xip_pore2host( io_image, xipTocItem.iv_address, &hostScomVector); MY_INF("hostScomVector = 0x%016llx\n",(uint64_t)hostScomVector); xipScomTableThis = myRev64(*((uint64_t*)hostScomVector + i_coreId)); MY_INF("xipScomTableThis = 0x%016llx\n",xipScomTableThis); if (xipScomTableThis) { sbe_xip_pore2host( io_image, xipScomTableThis, &hostScomTableThis); } else { // Should never be here. MY_ERR("Code or image bug. Scom vector table entries should never be null.\n"); return IMGBUILD_ERR_CHECK_CODE; } } // // Determine where to place/do Scom action and if entry already exists. // Insertion rules: // - If entry doesn't exist, insert at first NOP. (Note that if you don't do // this, then the table might potentially overflow since the max table size // doesn't include NOP entries.) // - If no NOP found, insert at first RET. // //---------------------------------------------------------------------------- // 1. Create search strings for addr, nop and ret. //---------------------------------------------------------------------------- // Note, the following IIS will also be used in case of // - i_operation==append // - i_operation==replace pore_inline_context_create( &ctx, (void*)bufIIS, XIPSIZE_SCOM_ENTRY, 0, 0); pore_LS( &ctx, P1, chipletId); pore_STI( &ctx, i_scomAddr, P1, i_scomData); if (ctx.error > 0) { MY_ERR("pore_LS or _STI generated rc = %d", ctx.error); return IMGBUILD_ERR_PORE_INLINE_ASM; } pore_inline_context_create( &ctx, (void*)bufRET, 4, 0, 0); pore_RET( &ctx); if (ctx.error > 0) { MY_ERR("pore_RET generated rc = %d", ctx.error); return IMGBUILD_ERR_PORE_INLINE_ASM; } pore_inline_context_create( &ctx, (void*)bufNOP, 4, 0, 0); pore_NOP( &ctx); if (ctx.error > 0) { MY_ERR("pore_NOP generated rc = %d", ctx.error); return IMGBUILD_ERR_PORE_INLINE_ASM; } //---------------------------------------------------------------------------- // 2. Search for addr and nop in relevant coreId table until first RET. //---------------------------------------------------------------------------- // Note: // - We go through ALL entries until first RET instr. We MUST find a RET instr, // though we don't check for overrun until later. (Could be improved.) // - Count number of entries, incl the NOOPs, until we find an RET. // - The STI(+SCOM_addr) opcode is in the 2nd word of the Scom entry. // - For an append operation, if a NOP is found (before a RET obviously), the // SCOM is replacing that NNNN sequence. hostScomEntryNext = hostScomTableThis; MY_INF("hostScomEntryNext (addr): 0x%016llx\n ",(uint64_t)hostScomEntryNext); while (memcmp(hostScomEntryNext, bufRET, sizeof(uint32_t))) { entriesCount++; MY_INF("Number of SCOM entries: %i\n ",entriesCount); if (*((uint32_t*)bufIIS+1)==*((uint32_t*)hostScomEntryNext+1) && entriesMatch==0) {// +1 skips 1st word in Scom entry (which loads the PC in an LS operation.) hostScomEntryMatch = hostScomEntryNext; entriesMatch++; } if (memcmp(hostScomEntryNext, bufNOP, sizeof(uint32_t))==0 && entriesNOP==0) { hostScomEntryNOP = hostScomEntryNext; entriesNOP++; } hostScomEntryNext = (void*)((uintptr_t)hostScomEntryNext + XIPSIZE_SCOM_ENTRY); } hostScomEntryRET = hostScomEntryNext; // The last EntryNext is always the first RET. //---------------------------------------------------------------------------- // 3. Qualify (translate) operation and IIS. //---------------------------------------------------------------------------- if (i_operation==P8_PORE_SCOM_APPEND) { operation = i_operation; } else if (i_operation==P8_PORE_SCOM_REPLACE) { if (hostScomEntryMatch) // ... do a replace operation = i_operation; else // ... do an append operation = P8_PORE_SCOM_APPEND; } else if (i_operation==P8_PORE_SCOM_NOOP) { // ...overwrite earlier bufIIS from the search step pore_inline_context_create( &ctx, (void*)bufIIS, XIPSIZE_SCOM_ENTRY, 0, 0); pore_NOP( &ctx); pore_NOP( &ctx); pore_NOP( &ctx); pore_NOP( &ctx); if (ctx.error > 0) { MY_ERR("*** _NOP generated rc = %d", ctx.error); return IMGBUILD_ERR_PORE_INLINE_ASM; } operation = i_operation; } else if ( i_operation==P8_PORE_SCOM_AND || i_operation==P8_PORE_SCOM_OR ) { operation = i_operation; } else if ( i_operation==P8_PORE_SCOM_AND_APPEND ) { if (hostScomEntryMatch) // ... do the AND on existing Scom operation = P8_PORE_SCOM_AND; else // ... do an append (this better be to an _AND register type) operation = P8_PORE_SCOM_APPEND; } else if ( i_operation==P8_PORE_SCOM_OR_APPEND ) { if (hostScomEntryMatch) // ... do the OR on existing Scom operation = P8_PORE_SCOM_OR; else // ... do an append (this better be to an _OR register type) operation = P8_PORE_SCOM_APPEND; } else if (i_operation==P8_PORE_SCOM_RESET) { // ... create RNNN instruction sequence. pore_inline_context_create( &ctx, (void*)bufIIS, XIPSIZE_SCOM_ENTRY, 0, 0); pore_RET( &ctx); pore_NOP( &ctx); pore_NOP( &ctx); pore_NOP( &ctx); if (ctx.error > 0) { MY_ERR("***_RET or _NOP generated rc = %d", ctx.error); return IMGBUILD_ERR_PORE_INLINE_ASM; } operation = i_operation; } else { MY_ERR("Scom operation = %i is not within valid range of [%d;%d]\n", i_operation, P8_PORE_SCOM_FIRST_OP, P8_PORE_SCOM_LAST_OP); return IMGBUILD_ERR_SCOM_INVALID_PARM; } //---------------------------------------------------------------------------- // 4. Check for overrun. //---------------------------------------------------------------------------- // Note: // - An entry count exceeding the max allocated entry count will result in a code error // because the allocation is based on an agreed upon max number of entries and // therefore either the code header file needs to change or the caller is not abiding // by the rules. // - An entry count equalling the max allocated entry count is allowed for all commands // except the APPEND command, incl the translated REPLACE->APPEND, which will result // in the previously mentioned code error being returned. // - The table can be full but still include NOOPs. If so, we can still APPEND since // we append at first occurrance of a NOOP or at the end of the table (at the RET). switch (i_section) { case P8_SCOM_SECTION_NC: if ( ( (operation==P8_PORE_SCOM_APPEND && entriesCount==SLW_MAX_SCOMS_NC) && hostScomEntryNOP==NULL ) || entriesCount>SLW_MAX_SCOMS_NC ) { MY_ERR("SCOM table NC is full. Max %i entries allowed.\n",SLW_MAX_SCOMS_NC); return IMGBUILD_ERR_CHECK_CODE; } break; case P8_SCOM_SECTION_L2: if ( ( (operation==P8_PORE_SCOM_APPEND && entriesCount==SLW_MAX_SCOMS_L2) && hostScomEntryNOP==NULL ) || entriesCount>SLW_MAX_SCOMS_L2 ) { MY_ERR("SCOM table L2 is full. Max %i entries allowed.\n",SLW_MAX_SCOMS_L2); return IMGBUILD_ERR_CHECK_CODE; } break; case P8_SCOM_SECTION_L3: if ( ( (operation==P8_PORE_SCOM_APPEND && entriesCount==SLW_MAX_SCOMS_L3) && hostScomEntryNOP==NULL ) || entriesCount>SLW_MAX_SCOMS_L3 ) { MY_ERR("SCOM table L3 is full. Max %i entries allowed.\n",SLW_MAX_SCOMS_L3); return IMGBUILD_ERR_CHECK_CODE; } break; default: MY_ERR("Invalid value for i_section (=%i).\n",i_section); MY_ERR("Valid values for i_section = [%i,%i,%i].\n", P8_SCOM_SECTION_NC,P8_SCOM_SECTION_L2,P8_SCOM_SECTION_L3); return IMGBUILD_ERR_SCOM_INVALID_SUBSECTION; } // --------------------------------------------------------------------------- // 5. Insert the SCOM. // --------------------------------------------------------------------------- // Assuming pre-allocated Scom table (after pre-allocated Ram table): // - Table is pre-filled with RNNN ISS. // - Each core Id has dedicated space, uniformly distributed by SLW_MAX_SCOMS_NC* // XIPSIZE_SCOM_ENTRY. // - Remember to check for more than SLW_MAX_SCOMS_NC entries! switch (operation) { case P8_PORE_SCOM_APPEND: // Append a Scom at first occurring NNNN or RNNN, if (hostScomEntryNOP) { // ... replace the NNNN MY_INF("Append at NOP\n"); memcpy(hostScomEntryNOP,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); } else if (hostScomEntryRET) { // ... replace the RNNN MY_INF("Append at RET\n"); memcpy(hostScomEntryRET,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); } else { // We should never be here. MY_ERR("In case=_SCOM_APPEND: EntryRET=NULL is impossible. Check code.\n"); return IMGBUILD_ERR_CHECK_CODE; } break; case P8_PORE_SCOM_REPLACE: // Replace existing Scom with new data if (hostScomEntryMatch) { // ... do a vanilla replace MY_INF("Replace existing Scom\n"); memcpy(hostScomEntryMatch,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); } else { // We should never be here. MY_ERR("In case=_SCOM_REPLACE: EntryMatch=NULL is impossible. Check code.\n"); return IMGBUILD_ERR_CHECK_CODE; } break; case P8_PORE_SCOM_NOOP: if (hostScomEntryMatch) { // ... do a vanilla replace MY_INF("Replace existing Scom w/NOPs\n"); memcpy(hostScomEntryMatch,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); } else { MY_ERR("No Scom entry found to replace NOOPs with.\n"); return IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND; } break; case P8_PORE_SCOM_OR: // Overlay Scom data onto existing data by bitwise OR if (hostScomEntryMatch) { // ... do an OR on the data (which is the 2nd DWord in the entry) MY_INF("Overlay existing Scom - OR case\n"); *((uint64_t*)hostScomEntryMatch+1) = *((uint64_t*)hostScomEntryMatch+1) | myRev64(i_scomData); } else { MY_ERR("No Scom entry found to do OR operation with.\n"); return IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND; } break; case P8_PORE_SCOM_AND: // Overlay Scom data onto existing data by bitwise AND if (hostScomEntryMatch) { // ... do an AND on the data (which is the 2nd DWord in the entry) MY_INF("Overlay existing Scom - AND case\n"); *((uint64_t*)hostScomEntryMatch+1) = *((uint64_t*)hostScomEntryMatch+1) & myRev64(i_scomData); } else { MY_ERR("No Scom entry found to do AND operation with.\n"); return IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND; } break; case P8_PORE_SCOM_RESET: // Reset (delete) table. Refill w/RNNN ISS. MY_INF("Reset table\n"); hostScomEntryNext = hostScomTableThis; for ( iEntry=0; iEntry const SlwSprRegs SLW_SPR_REGS[] = { /* name value swizzled */ // ...core regs { "P8_SPR_HRMOR", P8_SPR_HRMOR, ( P8_SPR_HRMOR >>5 | ( P8_SPR_HRMOR &0x1f)<<5 ) }, { "P8_SPR_HMEER", P8_SPR_HMEER, ( P8_SPR_HMEER >>5 | ( P8_SPR_HMEER &0x1f)<<5 ) }, { "P8_SPR_PMICR", P8_SPR_PMICR, ( P8_SPR_PMICR >>5 | ( P8_SPR_PMICR &0x1f)<<5 ) }, { "P8_SPR_PMCR", P8_SPR_PMCR, ( P8_SPR_PMCR >>5 | ( P8_SPR_PMCR &0x1f)<<5 ) }, { "P8_SPR_HID0", P8_SPR_HID0, ( P8_SPR_HID0 >>5 | ( P8_SPR_HID0 &0x1f)<<5 ) }, { "P8_SPR_HID1", P8_SPR_HID1, ( P8_SPR_HID1 >>5 | ( P8_SPR_HID1 &0x1f)<<5 ) }, { "P8_SPR_HID4", P8_SPR_HID4, ( P8_SPR_HID4 >>5 | ( P8_SPR_HID4 &0x1f)<<5 ) }, { "P8_SPR_HID5", P8_SPR_HID5, ( P8_SPR_HID5 >>5 | ( P8_SPR_HID5 &0x1f)<<5 ) }, { "P8_CORE_XTRA8", P8_CORE_XTRA8,( P8_CORE_XTRA8 ) }, { "P8_CORE_XTRA9", P8_CORE_XTRA9,( P8_CORE_XTRA9 ) }, // ...thread regs { "P8_SPR_HSPRG0", P8_SPR_HSPRG0,( P8_SPR_HSPRG0>>5 | ( P8_SPR_HSPRG0&0x1f)<<5 ) }, { "P8_SPR_LPCR", P8_SPR_LPCR, ( P8_SPR_LPCR >>5 | ( P8_SPR_LPCR &0x1f)<<5 ) }, { "P8_MSR_MSR", P8_MSR_MSR, ( P8_MSR_MSR ) }, { "P8_THRD_XTRA3", P8_THRD_XTRA3,( P8_THRD_XTRA3 ) }, { "P8_THRD_XTRA4", P8_THRD_XTRA4,( P8_THRD_XTRA4 ) }, }; const int SLW_SPR_REGS_SIZE = sizeof(SLW_SPR_REGS)/sizeof(SLW_SPR_REGS[0]); skiboot-skiboot-5.1.13/libpore/pgas.h000066400000000000000000001071271265204436200174760ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/pgas.h $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ #ifndef __PGAS_H__ #define __PGAS_H__ #define __PGAS__ // $Id: pgas.h,v 1.21 2013/11/20 14:06:39 bcbrock Exp $ // ** WARNING : This file is maintained as part of the OCC firmware. Do ** // ** not edit this file in the PMX area, the hardware procedure area, ** // ** or the PoreVe area as any changes will be lost. ** /// \file pgas.h /// \brief Pore GAS /// /// PGAS is documented in a seperate standalone document entitled PGAS : /// PORE GAS (GNU Assembler) User's and Reference Manual . /// /// This file defines support macros for the GNU PORE assembler, and the PORE /// inline assembler and disassebler which follow the PGAS assembly syntax. /// If the compile swith PGAS_PPC is defined in the environment then pgas.h /// includes pgas_ppc.h which transforms a PowerPC assembler into an assembler /// for PORE. // These are the opcodes and mnemonics as defined by the PORE hardware // manual. Many of them will change names slightly in PGAS. #define PORE_OPCODE_NOP 0x0f #define PORE_OPCODE_WAIT 0x01 #define PORE_OPCODE_TRAP 0x02 #define PORE_OPCODE_HOOK 0x4f #define PORE_OPCODE_BRA 0x10 #define PORE_OPCODE_BRAZ 0x12 #define PORE_OPCODE_BRANZ 0x13 #define PORE_OPCODE_BRAI 0x51 #define PORE_OPCODE_BSR 0x14 #define PORE_OPCODE_BRAD 0x1c #define PORE_OPCODE_BSRD 0x1d #define PORE_OPCODE_RET 0x15 #define PORE_OPCODE_CMPBRA 0x56 #define PORE_OPCODE_CMPNBRA 0x57 #define PORE_OPCODE_CMPBSR 0x58 #define PORE_OPCODE_LOOP 0x1f #define PORE_OPCODE_ANDI 0x60 #define PORE_OPCODE_ORI 0x61 #define PORE_OPCODE_XORI 0x62 #define PORE_OPCODE_AND 0x25 #define PORE_OPCODE_OR 0x26 #define PORE_OPCODE_XOR 0x27 #define PORE_OPCODE_ADD 0x23 #define PORE_OPCODE_ADDI 0x24 #define PORE_OPCODE_SUB 0x29 #define PORE_OPCODE_SUBI 0x28 #define PORE_OPCODE_NEG 0x2a #define PORE_OPCODE_COPY 0x2c #define PORE_OPCODE_ROL 0x2e #define PORE_OPCODE_LOAD20 0x30 #define PORE_OPCODE_LOAD64 0x71 #define PORE_OPCODE_SCR1RD 0x32 #define PORE_OPCODE_SCR1RDA 0x73 #define PORE_OPCODE_SCR2RD 0x36 #define PORE_OPCODE_SCR2RDA 0x77 #define PORE_OPCODE_WRI 0x78 #define PORE_OPCODE_BS 0x74 #define PORE_OPCODE_BC 0x75 #define PORE_OPCODE_SCR1WR 0x39 #define PORE_OPCODE_SCR2WR 0x3a #define PORE_OPCODE_SCAND 0x7c // These are the PGAS versions of the PORE opcodes used in the legacy PGAS_PPC // assembler and the current PORE inline assembler/disassembler. #define PGAS_OPCODE_NOP PORE_OPCODE_NOP #define PGAS_OPCODE_WAITS PORE_OPCODE_WAIT #define PGAS_OPCODE_TRAP PORE_OPCODE_TRAP #define PGAS_OPCODE_HOOKI PORE_OPCODE_HOOK #define PGAS_OPCODE_BRA PORE_OPCODE_BRA #define PGAS_OPCODE_BRAZ PORE_OPCODE_BRAZ #define PGAS_OPCODE_BRANZ PORE_OPCODE_BRANZ #define PGAS_OPCODE_BRAI PORE_OPCODE_BRAI #define PGAS_OPCODE_BSR PORE_OPCODE_BSR #define PGAS_OPCODE_BRAD PORE_OPCODE_BRAD #define PGAS_OPCODE_BSRD PORE_OPCODE_BSRD #define PGAS_OPCODE_RET PORE_OPCODE_RET #define PGAS_OPCODE_CMPIBRAEQ PORE_OPCODE_CMPBRA #define PGAS_OPCODE_CMPIBRANE PORE_OPCODE_CMPNBRA #define PGAS_OPCODE_CMPIBSREQ PORE_OPCODE_CMPBSR #define PGAS_OPCODE_LOOP PORE_OPCODE_LOOP #define PGAS_OPCODE_ANDI PORE_OPCODE_ANDI #define PGAS_OPCODE_ORI PORE_OPCODE_ORI #define PGAS_OPCODE_XORI PORE_OPCODE_XORI #define PGAS_OPCODE_AND PORE_OPCODE_AND #define PGAS_OPCODE_OR PORE_OPCODE_OR #define PGAS_OPCODE_XOR PORE_OPCODE_XOR #define PGAS_OPCODE_ADD PORE_OPCODE_ADD #define PGAS_OPCODE_ADDS PORE_OPCODE_ADDI #define PGAS_OPCODE_SUB PORE_OPCODE_SUB #define PGAS_OPCODE_SUBS PORE_OPCODE_SUBI #define PGAS_OPCODE_NEG PORE_OPCODE_NEG #define PGAS_OPCODE_MR PORE_OPCODE_COPY #define PGAS_OPCODE_ROLS PORE_OPCODE_ROL #define PGAS_OPCODE_LS PORE_OPCODE_LOAD20 #define PGAS_OPCODE_LI PORE_OPCODE_LOAD64 #define PGAS_OPCODE_LD0 PORE_OPCODE_SCR1RD /* Used by LD */ #define PGAS_OPCODE_LD0ANDI PORE_OPCODE_SCR1RDA /* Used by LDANDI */ #define PGAS_OPCODE_LD1 PORE_OPCODE_SCR2RD /* Used by LD */ #define PGAS_OPCODE_LD1ANDI PORE_OPCODE_SCR2RDA /* Used by LDANDI */ #define PGAS_OPCODE_STI PORE_OPCODE_WRI #define PGAS_OPCODE_STD0 PORE_OPCODE_SCR1WR /* Used by STD */ #define PGAS_OPCODE_STD1 PORE_OPCODE_SCR2WR /* Used by STD */ #define PGAS_OPCODE_SCAND PORE_OPCODE_SCAND #ifdef IGNORE_HW274735 // BSI and BCI are normally redacted due to HW274735. See also pgas.h #define PGAS_OPCODE_BSI PORE_OPCODE_BS #define PGAS_OPCODE_BCI PORE_OPCODE_BC #endif // IGNORE_HW274735 // These are the programmer-visible register names as defined by the PORE // hardware manual. All of these names (except the PC) appear differently in // the PGAS syntax, in some cases to reduce confusion, in other cases just to // have more traditional short mnemonics. #define PORE_REGISTER_PRV_BASE_ADDR0 0x0 #define PORE_REGISTER_PRV_BASE_ADDR1 0x1 #define PORE_REGISTER_OCI_BASE_ADDR0 0x2 #define PORE_REGISTER_OCI_BASE_ADDR1 0x3 #define PORE_REGISTER_SCRATCH0 0x4 #define PORE_REGISTER_SCRATCH1 0x5 #define PORE_REGISTER_SCRATCH2 0x6 #define PORE_REGISTER_ERROR_MASK 0x7 #define PORE_REGISTER_EXE_TRIGGER 0x9 #define PORE_REGISTER_DATA0 0xa #define PORE_REGISTER_PC 0xe #define PORE_REGISTER_IBUF_ID 0xf // PgP IBUF_ID values #define PORE_ID_GPE0 0x00 #define PORE_ID_GPE1 0x01 #define PORE_ID_SLW 0x08 #define PORE_ID_SBE 0x04 // Condition Codes #define PORE_CC_UGT 0x8000 #define PORE_CC_ULT 0x4000 #define PORE_CC_SGT 0x2000 #define PORE_CC_SLT 0x1000 #define PORE_CC_C 0x0800 #define PORE_CC_V 0x0400 #define PORE_CC_N 0x0200 #define PORE_CC_Z 0x0100 // Memory Spaces #define PORE_SPACE_UNDEFINED 0xffff #define PORE_SPACE_OCI 0x8000 #define PORE_SPACE_PNOR 0x800b #define PORE_SPACE_OTPROM 0x0001 #define PORE_SPACE_SEEPROM 0x800c #define PORE_SPACE_PIBMEM 0x0008 #ifdef __ASSEMBLER__ //////////////////////////////////////////////////////////////////////////// // PGAS Base Assembler Support //////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Condition Codes ////////////////////////////////////////////////////////////////////// .set CC_UGT, PORE_CC_UGT .set CC_ULT, PORE_CC_ULT .set CC_SGT, PORE_CC_SGT .set CC_SLT, PORE_CC_SLT .set CC_C, PORE_CC_C .set CC_V, PORE_CC_V .set CC_N, PORE_CC_N .set CC_Z, PORE_CC_Z ////////////////////////////////////////////////////////////////////// // Utility Macros ////////////////////////////////////////////////////////////////////// // 'Undefine' PowerPC mnemonics to trap programming errors .macro ..undefppc1, i .ifnc \i, ignore .macro \i, args:vararg .error "This is a PowerPC opcode - NOT a PGAS opcode or extended mnemonic" .endm .endif .endm .macro .undefppc, i0, i1=ignore, i2=ignore, i3=ignore ..undefppc1 \i0 ..undefppc1 \i1 ..undefppc1 \i2 ..undefppc1 \i3 .endm ////////////////////////////////////////////////////////////////////// // Argument Checking Macros ////////////////////////////////////////////////////////////////////// // // These macros remain in the final pgas.h file because 1) they are // required for some PGAS pseudo-ops, and 2) to support robust // assembler macro definitions. // Check an unsigned immediate for size .macro ..checku, x:req, bits:req, err="Unsigned value too large" .if (((\bits) <= 0) || ((\bits) > 63)) .error "The number of bits must be in the range 0 < bits < 64" .endif .iflt (\x) .error "An unsigned value is required here" .endif .ifgt ((\x) - (0xffffffffffffffff >> (64 - (\bits)))) .error "\err" .endif .endm // Check unsigned 16/22-bit immediates for size // // In general, PGAS can check immediate values for size restrictions, // but unfortunately is not able to check address offset immediates for // range. .macro ..check_u16, u16 ..checku (\u16), 16, "Unsigned immediate is larger than 16 bits" .endm .macro ..check_u24, u24 ..checku (\u24), 24, "Unsigned immediate is larger than 24 bits" .endm // Check a 16/20/22-bit signed immediate for size .macro ..check_s16, s16 .iflt \s16 .iflt \s16 + 0x8000 .error "Immediate value too small for a signed 16-bit field" .endif .else .ifgt \s16 - 0x7fff .error "Immediate value too large for a signed 16-bit field" .endif .endif .endm .macro ..check_s20, s20 .iflt \s20 .iflt \s20 + 0x80000 .error "Immediate value too small for a signed 20-bit field" .endif .else .ifgt \s20 - 0x7ffff .error "Immediate value too large for a signed 20-bit field" .endif .endif .endm .macro ..check_s22, s22 .iflt \s22 .iflt \s22 + 0x200000 .error "Immediate value too small for a signed 22-bit field" .endif .else .ifgt \s22 - 0x1fffff .error "Immediate value too large for a signed 22-bit field" .endif .endif .endm // Check a putative SCOM address for bits 0 and 8:11 == 0. .macro ..check_scom, address .if ((\address) & 0x80f00000) .error "Valid SCOM addresses must have bits 0 and 8:11 equal to 0." .endif .endm // A register required to be D0 .macro ..d0, reg .if (\reg != D0) .error "Data register D0 is required here" .endif .endm // A register pair required to be D0, D1 in order .macro ..d0d1, reg1, reg2 .if (((\reg1) != D0) && ((\reg2) != D1)) .error "Register-Register ALU operations are only defined on the source pair D0, D1" .endif .endm // A register pair required to be D0, D1 in any order .macro ..dxdy, reg1, reg2, err="Expecting D0, D1 in either order" .if !((((\reg1) == D0) && ((\reg2) == D1)) || \ (((\reg1) == D1) && ((\reg2) == D0))) .error "\err" .endif .endm // A register pair required to be A0, A1 in any order .macro ..axay, reg1, reg2, err="Expecting A0, A1 in either order" .if !((((\reg1) == A0) && ((\reg2) == A1)) || \ (((\reg1) == A1) && ((\reg2) == A0))) .error "\err" .endif .endm // A register pair required to be the same register .macro ..same, dest, src .if ((\dest) != (\src)) .error "PGAS requires the src and dest register of ADDS/SUBS to be explicit and identical" .endif .endm // A "Data" register .macro ..data, reg:req, err="Expecting a 'Data' register" .if (\reg != D0) .if (\reg != D1) .error "\err" .endif .endif .endm // An "Address" register .macro ..address, reg:req, err=:"Expecting an 'Address' register" .if (\reg != A0) .if (\reg != A1) .error "\err" .endif .endif .endm // A "Pervasive Chiplet ID" register .macro ..pervasive_chiplet_id, reg:req, err="Expecting a 'Pervasive Chiplet ID' register" .if (\reg != P0) .if (\reg != P1) .error "\err" .endif .endif .endm // A "Branch Compare Data" register .macro ..branch_compare_data, reg .if (\reg != D0) .if (\reg != D1) .if (\reg != CTR) .error "Expecting a 'Branch Compare Data' register" .endif .endif .endif .endm // An "LS Destination" register; Also the set for ADDS/SUBS .macro ..ls_destination, reg .if (\reg != D0) .if (\reg != D1) .if (\reg != A0) .if (\reg != A1) .if (\reg != P0) .if (\reg != P1) .if (\reg != CTR) .error "Expecting an 'LS Destination' register" .endif .endif .endif .endif .endif .endif .endif .endm // An "LI Destination" register .macro ..li_destination, reg .if (\reg != D0) .if (\reg != D1) .if (\reg != A0) .if (\reg != A1) .if (\reg != CTR) .error "Expecting an 'LI Destination' register" .endif .endif .endif .endif .endif .endm // An "LIA Destination" register .macro ..lia_destination, reg .if (\reg != D0) .if (\reg != D1) .if (\reg != A0) .if (\reg != A1) .if (\reg != TBAR) .error "Expecting an 'LIA Destination' register" .endif .endif .endif .endif .endif .endm // An "MR Source" register .macro ..mr_source, reg .if (\reg != D0) .if (\reg != D1) .if (\reg != A0) .if (\reg != A1) .if (\reg != P0) .if (\reg != P1) .if (\reg != CTR) .if (\reg != PC) .if (\reg != ETR) .if (\reg != SPRG0) .if (\reg != IFR) .if (\reg != EMR) .error "Expecting an 'MR Source' register" .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endm // An "MR Destination" register .macro ..mr_destination, reg .if (\reg != D0) .if (\reg != D1) .if (\reg != A0) .if (\reg != A1) .if (\reg != P0) .if (\reg != P1) .if (\reg != CTR) .if (\reg != PC) .if (\reg != ETR) .if (\reg != SPRG0) .if (\reg != EMR) .error "Expecting an 'MR Destination' register" .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endm ////////////////////////////////////////////////////////////////////// // PORE address spaces ////////////////////////////////////////////////////////////////////// // The ..set_address_space pseudo-op defines the default address // space. It must be defined in order to use BRAA, BRAIA, BSR and // CMPIBSR. Pseudo-ops are provided to set the default space of the // program. Note that code assembled for PNOR will also work in the // OCI space in the Sleep/Winkle engine. .macro ..set_default_space, s ..check_u16 (\s) .set _PGAS_DEFAULT_SPACE, (\s) .endm .macro ..check_default_space .if (_PGAS_DEFAULT_SPACE == PORE_SPACE_UNDEFINED) .error "The PGAS default address space has not been defined" .endif .endm ..set_default_space PORE_SPACE_UNDEFINED .macro .oci ..set_default_space PORE_SPACE_OCI .endm .macro .pnor ..set_default_space PORE_SPACE_PNOR .endm .macro .seeprom ..set_default_space PORE_SPACE_SEEPROM .endm .macro .otprom ..set_default_space PORE_SPACE_OTPROM .endm .macro .pibmem ..set_default_space PORE_SPACE_PIBMEM #ifndef PGAS_PPC .pibmem_port (PORE_SPACE_PIBMEM & 0xf) #else // NB: PGAS_PPC does not support relocatable PIBMEM addressing #endif .endm ////////////////////////////////////////////////////////////////////// // Address-Generation Pseudo Ops ////////////////////////////////////////////////////////////////////// // .QUADA, .QUADIA .macro .quada, offset:req ..check_default_space .long _PGAS_DEFAULT_SPACE .long (\offset) .endm .macro .quadia, space:req, offset:req ..check_u16 (\space) .long (\space) .long (\offset) .endm ////////////////////////////////////////////////////////////////////// // Bug workarounds ////////////////////////////////////////////////////////////////////// #ifndef IGNORE_HW274735 // HW274735 documents that BC and BS are broken for the PORE-GPE0/1 // pair. This bug is unfixed in POWER8, and by default we require BSI // and BCI to be implemented as macros on all engines. For // compatability we continue to require that dx == D0. .macro bsi, dx:req, offset:req, base:req, imm:req ..d0 (\dx) ld D0, (\offset), (\base) ori D0, D0, (\imm) std D0, (\offset), (\base) .endm .macro bci, dx:req, offset:req, base:req, imm:req ..d0 (\dx) ldandi D0, (\offset), (\base), ~(\imm) std D0, (\offset), (\base) .endm #endif // IGNORE_HW274735 ////////////////////////////////////////////////////////////////////// // "A"- and "IA"-form Instructions ////////////////////////////////////////////////////////////////////// // BRAA (Branch Address) is a 'long branch' to an address in the // default memory space. .macro braa, offset:req braia _PGAS_DEFAULT_SPACE, (\offset) .endm // LA (Load Address) loads the full address of an address in the // default memory space. .macro la, dest:req, offset:req lia (\dest), _PGAS_DEFAULT_SPACE, (\offset) .endm // STA (Store Address) stores the full address of an address in the // default memory space. .macro sta, mem_offset:req, base:req, addr_offset:req stia (\mem_offset), (\base), _PGAS_DEFAULT_SPACE, (\addr_offset) .endm // BSRIA is a subroutine branch into another memory space. This has to // be emulated by a local subroutine branch and a BRAIA. .macro bsria, space:req, offset:req bsr 27742f bra 27743f 27742: braia (\space), (\offset) 27743: .endm //////////////////////////////////////////////////////////////////////////// // Extended Mnemonics, Macros and Special Cases //////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // TFB - Test flags and branch conditionally //////////////////////////////////////////////////////////////////////' .macro ..tfb, dest, target, flags ..data (\dest) mr (\dest), IFR andi (\dest), (\dest), (\flags) branz (\dest), (\target) .endm .macro ..tfbn dest, target, flags ..data (\dest) mr (\dest), IFR andi (\dest), (\dest), (\flags) braz (\dest), (\target) .endm .macro tfbcs, dest:req, target:req ..tfb (\dest), (\target), CC_C .endm .macro tfbcc, dest:req, target:req ..tfbn (\dest), (\target), CC_C .endm .macro tfbvs, dest:req, target:req ..tfb (\dest), (\target), CC_V .endm .macro tfbvc, dest:req, target:req ..tfbn (\dest), (\target), CC_V .endm .macro tfbns, dest:req, target:req ..tfb (\dest), (\target), CC_N .endm .macro tfbnc, dest:req, target:req ..tfbn (\dest), (\target), CC_N .endm .macro tfbeq, dest:req, target:req ..tfb (\dest), (\target), CC_Z .endm .macro tfbne, dest:req, target:req ..tfbn (\dest), (\target), CC_Z .endm .macro tfbult, dest:req, target:req ..tfb (\dest), (\target), CC_ULT .endm .macro tfbule, dest:req, target:req ..tfbn (\dest), (\target), CC_UGT .endm .macro tfbuge, dest:req, target:req ..tfbn (\dest), (\target), CC_ULT .endm .macro tfbugt, dest:req, target:req ..tfb (\dest), (\target), CC_UGT .endm .macro tfbslt, dest:req, target:req ..tfb (\dest), (\target), CC_SLT .endm .macro tfbsle, dest:req, target:req ..tfbn (\dest), (\target), CC_SGT .endm .macro tfbsge, dest:req, target:req ..tfbn (\dest), (\target), CC_SLT .endm .macro tfbsgt, dest:req, target:req ..tfb (\dest), (\target), CC_SGT .endm ////////////////////////////////////////////////////////////////////// // TEB[N] - Test Engine and branch if [not] engine. ////////////////////////////////////////////////////////////////////// // // All but GPE0 use a 1-hot code. .macro tebgpe0, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), 0xf braz (\dest), (\target) .endm .macro tebgpe1, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), PORE_ID_GPE1 branz (\dest), (\target) .endm .macro tebslw, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), PORE_ID_SLW branz (\dest), (\target) .endm .macro tebsbe, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), PORE_ID_SBE branz (\dest), (\target) .endm .macro tebngpe0, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), 0xf branz (\dest), (\target) .endm .macro tebngpe1, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), PORE_ID_GPE1 braz (\dest), (\target) .endm .macro tebnslw, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), PORE_ID_SLW braz (\dest), (\target) .endm .macro tebnsbe, dest:req, target:req mr (\dest), IFR andi (\dest), (\dest), PORE_ID_SBE braz (\dest), (\target) .endm ////////////////////////////////////////////////////////////////////// // EXTRPRC - Extract and right-justify the PIB/PCB return code // TPRCB[N]Z - Test PIB return code and branch if [not] zero // TPRCBGT - Test PIB return code and branch if greater-than // TPRCBLE - Test PIB return code and branch if less-then or equal ////////////////////////////////////////////////////////////////////// // // To support cases where PORE code expects or must explicitly handle // non-0 PIB return codes, the PIB return code and parity indication // are stored in bits 32 (parity) and 33-35 (return code) of the IFR. // These macros extract the four PIB/PCB status bits from the IFR and // right-justifies them into the data register provided. For EXTRPRC // that is the total function of the macro. The TPRCB[N]Z macros // provide a simple non-destructive test and branch for zero (success) // and non-zero (potential problem) codes after the extraction. // // In complex error handling scenarios one would typically compare the // PIB return code against an upper-bound, e.g., the offline response // (0x2), and then take further action. If the parity error bit is set // then this would produce an aggregate "return code" higher than any // that one would typically want to ignore. The TPRCBGT/TPRCBLE macros // provide this function; however the test destroys the extracted // return code so that if further analysis is required the code will // need to be a extracted again. ////////////////////////////////////////////////////////////////////// .macro extrprc, dest:req ..data (\dest) mr (\dest), IFR extrdi (\dest), (\dest), 4, 32 .endm .macro tprcbz, dest:req, target:req extrprc (\dest) braz (\dest), (\target) .endm .macro tprcbnz, dest:req, target:req extrprc (\dest) branz (\dest), (\target) .endm .macro tprcbgt, dest:req, target:req, bound:req extrprc (\dest) subs (\dest), (\dest), (\bound) tfbugt (\dest), (\target) .endm .macro tprcble, dest:req, target:req, bound:req extrprc (\dest) subs (\dest), (\dest), (\bound) tfbule (\dest), (\target) .endm ////////////////////////////////////////////////////////////////////// // LPCS - Load Pervasive Chiplet from Scom address ////////////////////////////////////////////////////////////////////// .macro lpcs, dest:req, scom:req ..pervasive_chiplet_id (\dest) ..check_scom (\scom) ls (\dest), (((\scom) >> 24) & 0x7f) .endm ////////////////////////////////////////////////////////////////////// // Shift/Mask extended mnemonics ////////////////////////////////////////////////////////////////////// // All of the 'dot-dot' macros assume that error and identity // checking has been done on the arguments already. // The initial register-register rotate. If the incoming shift amount // is 0 then the instruction generated is a simple MR. .macro ..rotlrr, ra, rs, sh .if (\sh) >= 32 rols (\ra), (\rs), 32 ..rotlr (\ra), ((\sh) - 32) .elseif (\sh) >= 16 rols (\ra), (\rs), 16 ..rotlr (\ra), ((\sh) - 16) .elseif (\sh) >= 8 rols (\ra), (\rs), 8 ..rotlr (\ra), ((\sh) - 8) .elseif (\sh) >= 4 rols (\ra), (\rs), 4 ..rotlr (\ra), ((\sh) - 4) .elseif (\sh) >= 1 rols (\ra), (\rs), 1 ..rotlr (\ra), ((\sh) - 1) .else mr (\ra), (\rs) .endif .endm // Subsequent rotation of the same register. The SH should never be 0 // here. .macro ..rotlr, ra, sh .if (\sh) >= 32 rols (\ra), (\ra), 32 ..rotlr (\ra), ((\sh) - 32) .elseif (\sh) >= 16 rols (\ra), (\ra), 16 ..rotlr (\ra), ((\sh) - 16) .elseif (\sh) >= 8 rols (\ra), (\ra), 8 ..rotlr (\ra), ((\sh) - 8) .elseif (\sh) >= 4 rols (\ra), (\ra), 4 ..rotlr (\ra), ((\sh) - 4) .elseif (\sh) >= 1 rols (\ra), (\ra), 1 ..rotlr (\ra), ((\sh) - 1) .endif .endm // RLDINM RA, RS, SH, MB, ME // // Defined as if there were an equivalent PowerPC instruction. The // 'word' forms of the PowerPC instructions and extended mnemonics are // undefined in order to catch programming typos. .undefppc rlwinm, extrwi, rotlwi, rotrwi .undefppc slwi, srwi .macro rldinm, ra:req, rs:req, sh:req, mb:req, me:req .if ((\sh) < 0) || ((\sh) > 63) .error "SH must be in the range 0..63" .endif .if ((\mb) < 0) || ((\mb) > 63) .error "MB must be in the range 0..63" .endif .if ((\me) < 0) || ((\me) > 63) .error "ME must be in the range 0..63" .endif .if (((\mb) == 0) && ((\me) == 63) || ((\me) == ((\mb) - 1))) // The mask is effectively 0..63, i.e., no mask. This is a // simple rotate. ..rotlrr (\ra), (\rs), (\sh) .else // We need a mask step. However if SH == 0 and RA == RS we can // bypass the rotate step. .if ((\sh) != 0) || ((\ra) != (\rs)) ..rotlrr (\ra), (\rs), (\sh) .endif .if ((\mb) <= (\me)) // This is a straightforward masking operation with a // single mask. andi (\ra), (\ra), ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - (\me)))) .else // This is a wrapped mask. // It is created as 2 masks OR-ed together - 0-ME and MB-63 andi (\ra), (\ra), (((0xffffffffffffffff >> 0) & (0xffffffffffffffff << (63 - (\me)))) | ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - 63)))) .endif .endif .endm // RLDINM Extended Mnemonics // // Defined as if they were equivalent to PowerPC 32-bit extended // mnemonics .macro extldi, ra:req, rs:req, n:req, b:req .if ((\n) < 0) .error "EXTLDI requires N > 0" .endif rldinm (\ra), (\rs), (\b), 0, ((\n) - 1) .endm .macro extrdi, ra:req, rs:req, n:req, b:req .if ((\n) < 0) .error "EXTRDI requires N > 0" .endif rldinm (\ra), (\rs), (((\b) + (\n)) % 64), (64 - (\n)), 63 .endm .macro rotldi, ra:req, rs:req, n:req rldinm (\ra), (\rs), (\n), 0, 63 .endm .macro rotrdi, ra:req, rs:req, n:req rldinm (\ra), (\rs), (64 - (\n)), 0, 63 .endm .macro sldi, ra:req, rs:req, n:req rldinm (\ra), (\rs), (\n), 0, (63 - (\n)) .endm .macro srdi, ra:req, rs:req, n:req rldinm (\ra), (\rs), (64 - (\n)), (\n), 63 .endm // RLDIMI RA, RS, SH, MB, ME // // Defined as if there were an equivalent PowerPC instruction. The // 'word' forms of the PowerPC instructions and extended mnemonics are // undefined in order to catch programming typos. // // Note that unlike the PowerPC instructions, here RLDIMI must destroy // RS by masking and shifting it, and RA and RS may not be the same // register. .undefppc rlwimi, inslwi, insrwi .macro rldimi, ra:req, rs:req, sh:req, mb:req, me:req ..dxdy (\ra), (\rs) // SH error checks are done by rldinm .if (((\mb) == 0) && ((\me) == 63) || ((\me) == ((\mb) - 1))) // The mask is effectively 0..63, i.e., no mask. This is a // simple rotate of RS into RA rotldi (\ra), (\rs), (\sh) .else // Rotate RS and AND with mask rldinm (\rs), (\rs), (\sh), (\mb), (\me) // Mask out the significant bits of RS, clear that section of // RA, and logical OR RS into RA .if ((\mb) <= (\me)) // This is a straightforward masking operation with a // single mask. andi (\ra), (\ra), \ (~((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - (\me))))) .else // This is a wrapped mask. // It is created as 2 masks OR-ed together - 0-ME and MB-63 andi (\ra), (\ra), \ (~(((0xffffffffffffffff >> 0) & (0xffffffffffffffff << (63 - (\me)))) | \ ((0xffffffffffffffff >> (\mb)) & (0xffffffffffffffff << (63 - 63))))) .endif or (\ra), D0, D1 .endif .endm // RLDIMI Extended Mnemonics // // Defined as if they were equivalent to PowerPC 32-bit extended // mnemonics .macro insldi, ra:req, rs:req, n:req, b:req .if ((\n) < 0) .error "INSLDI requires N > 0" .endif rldimi (\ra), (\rs), (64 - (\b)), (\b), ((\b) + (\n) - 1) .endm .macro insrdi, ra:req, rs:req, n:req, b:req .if ((\n) < 0) .error "INSRDI requires N > 0" .endif rldimi (\ra), (\rs), (64 - (\b) - (\n)), (\b), ((\b) + (\n) - 1) .endm ////////////////////////////////////////////////////////////////////// // .HOOK ////////////////////////////////////////////////////////////////////// // The PoreVe (PORE Virtual Environment) is a PORE simulation // environment that allows the programmer to embed C/C++ code into the // PORE assembler source code, and arranges for the C/C++ code to be // executed in-line with the PORE assembly code. Instances of the // .hook macro are inserted into the assembler input by the // hook_extractor script, to mark the locations where hooks are // present. The hook reference is a string that combines the source // file name with an index number to uniquely identify the hook. // // .hook _ // // The .hook macro marks the location of each hook in the relocatable // binaries with special symbols. The symbol name includes the hook // reference, which is used to locate the hook in the HookManager // symbol table. Because hooks can be defined in macros, a hook that // appears once in a source file may appear multiple times in the // final binary. For this reason each hook must also be tagged with a // unique index number to avoid symbol name collisions. The // complexity of the .hook macro is due to the necessity to decode a // dynamic symbol value (_PGAS_HOOK_INDEX) into its binary string form // to create the unique symbol name. The final hook symbol has the // form: // // __hook___ // // where is a binary string. It is then straightforward to // locate these symbols in the 'nm' output of the final link and // create a map of final addresses to the hook routine to call (the // ) before executing the instruction at that address. // // Note: The maximum nesting depth of the recursive ..hook_helper // macro is log2(index), and the assembler supports nesting of at // least 32 which is much more than sufficient. .set _PGAS_HOOK_INDEX, 0 .macro .hook, reference:req .set _PGAS_HOOK_INDEX, (_PGAS_HOOK_INDEX + 1) ..hook_helper _PGAS_HOOK_INDEX, "", \reference .endm .macro ..hook_helper, index, unique, reference .ifeq \index __hook__\unique\()_\reference\(): .elseif (\index % 2) ..hook_helper (\index / 2), 1\unique, \reference .else ..hook_helper (\index / 2), 0\unique, \reference .endif .endm //////////////////////////////////////////////////////////////////////////// // Help for Conversion from Old to New PGAS syntax //////////////////////////////////////////////////////////////////////////// .macro loadp, arg:vararg .error "PGAS now implements 'lpcs' rather then 'loadp'" .endm .macro loadx, arg:vararg .error "PGAS now implements 'la' rather than 'loadx'" .endm #endif // __ASSEMBLER__ #ifdef PGAS_PPC #include "pgas_ppc.h" #endif #endif // __PGAS_H__ skiboot-skiboot-5.1.13/libpore/pore_inline.h000066400000000000000000000660501265204436200210460ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/pore_inline.h $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ #ifndef __PORE_INLINE_H__ #define __PORE_INLINE_H__ // $Id: pore_inline.h,v 1.20 2013/12/11 00:11:13 bcbrock Exp $ // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/pore_inline.h,v $ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2013 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //----------------------------------------------------------------------------- // ** WARNING : This file is maintained as part of the OCC firmware. Do ** // ** not edit this file in the PMX area or the hardware procedure area ** // ** as any changes will be lost. ** /// \file pore_inline.h /// \brief Inline assembler for PORE code /// /// Note that this file defines several short macro symbols for register names /// and other mnemonics used by inline assembly. For this reason it would /// probably be best to only include this header when it was absolutely /// necessary, i.e., only in C files that explicitly use inline assembly and /// disassembly. #ifndef PPC_HYP #include #include #include #endif // PPC_HYP #include "pgas.h" #if( defined(__cplusplus) && !defined(PLIC_MODULE) ) extern "C" { #endif #if 0 } /* So __cplusplus doesn't mess w/auto-indent */ #endif #ifndef __ASSEMBLER__ // PHYP tools do not support 'static' functions and variables as it interferes // with their concurrent patch methodology. So when compiling for PHYP the // PORE instruction "macros" are simply declared "inline". This also extends // into the implementation C files - so under PHYP all previosuly local static // functions will now be global functions. We retain 'static' to reduce code // size and improve abstraction for OCC applications. #ifdef PPC_HYP #define PORE_STATIC #include #else #define PORE_STATIC static #endif /// Error code strings from the PORE inline assembler/disassembler /// /// The PoreInlineContext object stores error codes that occur during /// assembly as small integers. The '0' code indicates success. This is a /// table of strings that describe the codes. It will be instantiated in /// pore_inline.c extern const char *pore_inline_error_strings[]; #ifdef __PORE_INLINE_ASSEMBLER_C__ const char *pore_inline_error_strings[] = { "No error", "The inline assembler memory is full, or disassembly has reached the end of the memory area", "The instruction requires an ImD24 operand", "The LC is not aligned or the instruction requires an aligned operand", "The branch target is unreachable (too distant)", "A register operand is illegal for the given instruction", "The instruction form requires a signed 16-bit immediate", "Valid rotate lengths are 1, 4, 8, 16 and 32", "The instruction requires a 20-bit signed immediate", "The instruction requires a 24-bit unsigned immediate", "A parameter to pore_inline_context_create() is invalid", "The instruction form requires an unsigned 22-bit immediate", "This error is due to a bug in the PORE inline assembler (Please report)", "The 'source' label for pore_inline_branch_fixup() is illegal", "The 'source' instruction for pore_inline_branch_fixup() is not a branch", "The disassembler does not recognize the instruction as a PORE opcode", "Instruction parity error during disassembly", "The string form of the disassembly is too long to represent (Please report)`", "Use HALT instead of WAIT 0 if the intention is to halt.", "A putative SCOM address is illegal (has non-0 bits where 0s are expected)." }; #endif /* __PORE_INLINE_ASSEMBLER_C__ */ #endif /* __ASSEMBLER__ */ #define PORE_INLINE_SUCCESS 0 #define PORE_INLINE_NO_MEMORY 1 #define PORE_INLINE_IMD24_ERROR 2 #define PORE_INLINE_ALIGNMENT_ERROR 3 #define PORE_INLINE_UNREACHABLE_TARGET 4 #define PORE_INLINE_ILLEGAL_REGISTER 5 #define PORE_INLINE_INT16_REQUIRED 6 #define PORE_INLINE_ILLEGAL_ROTATE 7 #define PORE_INLINE_INT20_REQUIRED 8 #define PORE_INLINE_UINT24_REQUIRED 9 #define PORE_INLINE_INVALID_PARAMETER 10 #define PORE_INLINE_UINT22_REQUIRED 11 #define PORE_INLINE_BUG 12 #define PORE_INLINE_ILLEGAL_SOURCE_LC 13 #define PORE_INLINE_NOT_A_BRANCH 14 #define PORE_INLINE_UNKNOWN_OPCODE 15 #define PORE_INLINE_PARITY_ERROR 16 #define PORE_INLINE_DISASSEMBLY_OVERFLOW 17 #define PORE_INLINE_USE_HALT 18 #define PORE_INLINE_ILLEGAL_SCOM_ADDRESS 19 /// Register name strings for the PORE inline assembler/disassembler extern const char *pore_inline_register_strings[16]; // C++ requires that these arrays of strings be declared 'const' to avoid // warnings. But then you get warnings when the strings get stored into // non-const variables. The solution is to rename these arrays inside the // disassembler. If anyone has a better solution please let me know - Bishop #ifdef __PORE_INLINE_ASSEMBLER_C__ const char* pore_inline_register_strings[16] = { "P0", "P1", "A0", "A1", "CTR", "D0", "D1", "EMR", "?", "ETR", "SPRG0", "?", "?", "?", "PC", "IFR" }; #endif /* __PORE_INLINE_ASSEMBLER_C__ */ // Shorthand forms of constants defined in pgas.h, defined for consistency // using the assembler-supported names. These constants are defined as an // enum to avoid name conflicts with some firmware symbols when the PORE // inline facility is used to create Host Boot procedures. enum { // Shorthand register mnemonics, defined as an enum to avoid name clashes. P0 = PORE_REGISTER_PRV_BASE_ADDR0, P1 = PORE_REGISTER_PRV_BASE_ADDR1, A0 = PORE_REGISTER_OCI_BASE_ADDR0, A1 = PORE_REGISTER_OCI_BASE_ADDR1, CTR = PORE_REGISTER_SCRATCH0, D0 = PORE_REGISTER_SCRATCH1, D1 = PORE_REGISTER_SCRATCH2, EMR = PORE_REGISTER_ERROR_MASK, ETR = PORE_REGISTER_EXE_TRIGGER, SPRG0 = PORE_REGISTER_DATA0, PC = PORE_REGISTER_PC, IFR = PORE_REGISTER_IBUF_ID, // PgP IBUF_ID values PORE_GPE0 = PORE_ID_GPE0, PORE_GPE1 = PORE_ID_GPE1, PORE_SLW = PORE_ID_SLW, PORE_SBE = PORE_ID_SBE, // Condition Codes CC_UGT = PORE_CC_UGT, CC_ULT = PORE_CC_ULT, CC_SGT = PORE_CC_SGT, CC_SLT = PORE_CC_SLT, CC_C = PORE_CC_C, CC_V = PORE_CC_V, CC_N = PORE_CC_N, CC_Z = PORE_CC_Z, }; // Pseudo-opcodes for LD/LDANDI/STD #define PORE_INLINE_PSEUDO_LD 0 #define PORE_INLINE_PSEUDO_LDANDI 1 #define PORE_INLINE_PSEUDO_STD 2 // Private version of _BIG_ENDIAN #ifndef _BIG_ENDIAN #define PORE_BIG_ENDIAN 0 #else #define PORE_BIG_ENDIAN _BIG_ENDIAN #endif /// Maximum size of disassembly strings /// /// This is currently sufficient for PORE_INLINE_LISTING_MODE. We don't want /// to make this too long since the PoreInlineDisassembly object may be on the /// stack in embedded applications. #define PORE_INLINE_DISASSEMBLER_STRING_SIZE 128 /// Generate PORE instruction parity /// /// This flag is an option to pore_inline_context_create(). If set, PORE /// inline assembly sets the instruction parity bit for each assembled /// instruction; otherwise the instruction parity bit is always 0. #define PORE_INLINE_GENERATE_PARITY 0x01 /// Check PORE instruction parity /// /// This flag is an option to pore_inline_context_create(). If set, PORE /// inline disassembly checks the instruction parity bit for each disassembled /// instruction, failing with PORE_INLINE_PARITY_ERROR if the parify is not /// correct. Otherwise the instruction parity bit is ignored during /// disassembly. #define PORE_INLINE_CHECK_PARITY 0x02 /// Disassemble in listing mode /// /// This flag is an option to pore_inline_context_create(). If set, then /// generate disassembly strings in the form of a listing that contains /// location counters and encoded instructions as well as their diassembly. /// By default the disassembly strings do not contain this information and can /// be fed back in as source code to a PORE assembler. #define PORE_INLINE_LISTING_MODE 0x04 /// Disassemble in data mode /// /// This flag is an option to pore_inline_context_create(). If set, then /// generate disassembly assuming that the context contains data rather than /// text. Normally data is disassembled as .long directives, however if the /// context is unaligned or of an odd length then .byte directives may be used /// as well. This option can be used in conjunction with /// PORE_INLINE_LISTING_MODE and PORE_INLINE_8_BYTE_DATA. /// /// Note: An intelligent application can switch between the default text /// disassembly and data disassembly by manipulating the \a options field of /// the PoreInlineContext between calls of pore_inline_disassemble(). #define PORE_INLINE_DISASSEMBLE_DATA 0x08 /// Disassemble data in 8-byte format /// /// This flag is an option to pore_inline_context_create(). If set, then if /// PORE_INLINE_DISASSEMBLE_DATA is also set then generate data disassembly as /// 8-byte values rather then the default 4-byte values. Normally data is /// disassembled as .quad directives under this option, however if the context /// is unaligned or of an odd length then .long and .byte directives may be /// used as well. This option can be used in conjunction with /// PORE_INLINE_LISTING_MODE. /// /// Note: An intelligent application can switch between the default text /// disassembly and data disassembly by manipulating the \a options field of /// the PoreInlineContext between calls of pore_inline_disassemble(). #define PORE_INLINE_8_BYTE_DATA 0x10 /// Disassemble unrecognized opcodes as 4-byte data /// /// This flag is an option to pore_inline_context_create(). If set, then /// any putative instruction with an unrecognized opcode will be silently /// diassembled as 4-byte data. /// /// This option was added to allow error-free disassembly of /// non-parity-protected PORE text sections that contain 0x00000000 alignment /// padding, and is not guaranteed to produce correct or consistent results in /// any other case. #define PORE_INLINE_DISASSEMBLE_UNKNOWN 0x20 #ifndef __ASSEMBLER__ /// The type of location counters for the PORE inline assembler typedef uint32_t PoreInlineLocation; /// PORE inline assembler context /// /// See the documentation page \ref pore_inline_assembler and the function /// pore_inline_context_create() for futher details. typedef struct { /// The memory area to receive the inline assembly /// /// This field is never modified, allowing the *reset* APIs to function. /// /// Note: C++ does not allow arithmetic on void* objects, so we use the /// Linux convention of storing memory addresses as type 'unsigned long'. unsigned long memory; /// The original size of the memory area to receive the inline assembly /// /// This field is never modified, allowing the *reset* APIs to function. size_t size; /// The original Location Counter (associated with \a memory) /// /// This field is never modified, allowing the *reset* APIs to function. PoreInlineLocation original_lc; /// The memory address associated with the current LC /// /// Note: C++ does not allow arithmetic on void* objects, so we use the /// Linux convention of storing memory addresses as type 'unsigned long'. unsigned long lc_address; /// The remaining size of the memory area to receive the inline assembly size_t remaining; /// The bytewise Location Counter of the assembled code PoreInlineLocation lc; /// Inline assembly options /// /// This field is never modified, allowing the *reset* APIs to function. int options; /// The last error code generated by the inline assembler int error; } PoreInlineContext; /// PORE inline disassembler result /// /// This object holds the disassembly produced by pore_inline_disassemble(). /// See documentation for that function for complete details. typedef struct { /// The context as it existed when the instruction was assembled /// /// Disassembling an instruction modifies the context provided to /// pore_inline_disassemble() to point to the next instruction. This /// structure stores a copy of the context at the initial call of /// pore_inline_disassemble(), that is, the context in effect when the /// dissassembled instruction was assembled. PoreInlineContext ctx; /// The first 32 bits of every instruction uint32_t instruction; /// The opcode; bits 0..6 of the instruction int opcode; /// A flag - If set the opcode is for a 12-byte instruction int long_instruction; /// The parity bit; bit 7 of the instruction int parity; /// The register specifier at bits 8..11 of the instruction /// /// This register is sometimes called the source, sometimes the target, /// depending on the opcode. int r0; /// The register specifier at bits 12..15 of the instruction /// /// This register is always called the 'source' but is named generically /// here since sometimes the specifier at bits 8..11 is also called a /// 'source'. int r1; /// 'ImD16' is the signed 16-bit immediate for short immediate adds and /// subtracts. For the rotate instruction this field also contains the /// rotate count which is either 1, 4, 8, 16 or 32. int16_t imd16; /// 'ImD20' is the 20-bit signed immediate for the LOAD20 instruction int32_t imd20; /// 'ImD24' is the 24-bit unsigned immediate for the WAIT instruction uint32_t imd24; /// 'ImD64' is the 64-bit immediate for data immediates and BRAI. This /// field is only set for 3-word instructions. uint64_t imd64; /// 'ImPCO20' is a signed, 20-bit word offset for branch instructions int32_t impco20; /// 'ImPCO24' is a signed, 24-bit word offset for branch instructions int32_t impco24; /// For imA24 opcodes, this indicates memory/pib (1/0) addressing.. int memory_space; /// This is the base register specifier - either a memory (OCI) base /// register or a pervasive base register - for Read/Write operations. /// Note that this is a PORE register index, not simply 0/1. int base_register; /// This is the 22-bit signed offset for memory (OCI) addressing. This /// unsigned offset is added to a memory base register (A0/A1) to form the /// final 32-bit address. uint32_t memory_offset; /// This field contains the port number and local address portions of the /// PIB/PCB address for load/store operations that target the PIB/PCB. /// Note that bits 0..11 will always be 0 in this address. Bits 1..7 (the /// multicast bit and chiplet id) are sourced from the associated /// pervasive base register when the instruction executes. uint32_t pib_offset; /// The update bit of the SCAND instruction int update; /// The capture bit of the SCAND instruction int capture; /// The scan length from a SCAND instruction int scan_length; /// The scan select from a SCAND instruction uint32_t scan_select; /// The address offset from a SCAND instruction uint32_t scan_offset; /// The string form of the disassembly. /// /// The disassembly string is \e not terminated by a newline. In listing /// mode the disassembly string \e will contain embedded newlines for long /// instructions. char s[PORE_INLINE_DISASSEMBLER_STRING_SIZE]; /// The data (for data disassembly) /// /// This is either 1, 4 or 8 bytes in host byte order. uint64_t data; /// The size of the disassembled \a data field (for data disassembly) size_t data_size; /// Was this location disassembled as an instruction (0) or as data (1) int is_data; } PoreInlineDisassembly; // These are internal APIs - they are not needed by application code. void pore_inline_be32(unsigned long p, uint32_t x); void pore_inline_be64(unsigned long p, uint64_t x); uint32_t pore_inline_host32(unsigned long p); uint64_t pore_inline_host64(unsigned long p); int pore_inline_parity(uint32_t instruction, uint64_t imd64); void pore_inline_context_bump(PoreInlineContext *ctx, size_t bytes); int pore_inline_instruction1(PoreInlineContext *ctx, int opcode, uint32_t operand); int pore_inline_instruction3(PoreInlineContext *ctx, int opcode, uint32_t operand, uint64_t imm); int pore_inline_bra(PoreInlineContext *ctx, int opcode, PoreInlineLocation target); int pore_inline_brac(PoreInlineContext *ctx, int opcode, int reg, PoreInlineLocation target); int pore_inline_cmpibra(PoreInlineContext *ctx, int opcode, int reg, PoreInlineLocation target, uint64_t imm); int pore_inline_brad(PoreInlineContext *ctx, int opcode, int reg); int pore_inline_ilogic(PoreInlineContext *ctx, int opcode, int dest, int src, uint64_t imm); int pore_inline_alurr(PoreInlineContext *ctx, int opcode, int dest, int src1, int src2); int pore_inline_adds(PoreInlineContext *ctx, int opcode, int dest, int src, int imm); int pore_inline_load_store(PoreInlineContext *ctx, int opcode, int src_dest, int32_t offset, int base, uint64_t imm); // These are utility APIs that may be required by special-purpose code that // uses the pore_inline library. void pore_inline_decode_instruction(PoreInlineDisassembly* dis, uint32_t instruction); void pore_inline_decode_imd64(PoreInlineDisassembly* dis, uint64_t imd64); // These are the inline PORE instructions, extended mnemonics and pseudo-ops // to be used by application code. /// Set a location counter variable from a context /// /// This is a macro that sets the \a var (of type PoreInlineLocation) to the /// current location counter of the \a ctx. The macro produces an expression /// that evaluates to 0 so that it can be used in the logical-OR expressions /// used to define inline assembly sequences. #define PORE_LOCATION(ctx, var) (((var) = (ctx)->lc), 0) int pore_inline_context_create(PoreInlineContext *context, void *memory, size_t size, PoreInlineLocation lc, int options); void pore_inline_context_reset(PoreInlineContext *context); void pore_inline_context_reset_excursion(PoreInlineContext *context); void pore_inline_context_copy(PoreInlineContext *dest, PoreInlineContext *src); int pore_inline_branch_fixup(PoreInlineContext *ctx, PoreInlineLocation source, PoreInlineLocation target); int pore_inline_disassemble(PoreInlineContext *ctx, PoreInlineDisassembly *dis); // Native PORE instruction assembly, using PGAS opcode names and operand // ordering rules. // NOP, TRAP, RET PORE_STATIC inline int pore_NOP(PoreInlineContext *ctx) { return pore_inline_instruction1(ctx, PGAS_OPCODE_NOP, 0); } PORE_STATIC inline int pore_TRAP(PoreInlineContext *ctx) { return pore_inline_instruction1(ctx, PGAS_OPCODE_TRAP, 0); } PORE_STATIC inline int pore_RET(PoreInlineContext *ctx) { return pore_inline_instruction1(ctx, PGAS_OPCODE_RET, 0); } // WAITS, HALT, HOOKI int pore_WAITS(PoreInlineContext *ctx, uint32_t cycles); PORE_STATIC inline int pore_HALT(PoreInlineContext *ctx) { return pore_inline_instruction1(ctx, PGAS_OPCODE_WAITS, 0); } int pore_HOOKI(PoreInlineContext *ctx, uint32_t index, uint64_t imm); // BRA, BSR, LOOP PORE_STATIC inline int pore_BRA(PoreInlineContext *ctx, PoreInlineLocation target) { return pore_inline_bra(ctx, PGAS_OPCODE_BRA, target); } PORE_STATIC inline int pore_BSR(PoreInlineContext *ctx, PoreInlineLocation target) { return pore_inline_bra(ctx, PGAS_OPCODE_BSR, target); } PORE_STATIC inline int pore_LOOP(PoreInlineContext *ctx, PoreInlineLocation target) { return pore_inline_bra(ctx, PGAS_OPCODE_LOOP, target); } // BRAZ, BRANZ PORE_STATIC inline int pore_BRAZ(PoreInlineContext *ctx, int reg, PoreInlineLocation target) { return pore_inline_brac(ctx, PGAS_OPCODE_BRAZ, reg, target); } PORE_STATIC inline int pore_BRANZ(PoreInlineContext *ctx, int reg, PoreInlineLocation target) { return pore_inline_brac(ctx, PGAS_OPCODE_BRANZ, reg, target); } // CMPIBRAEQ, CMPIBRANE, CMPIBSREQ PORE_STATIC inline int pore_CMPIBRAEQ(PoreInlineContext *ctx, int reg, PoreInlineLocation target, uint64_t imm) { return pore_inline_cmpibra(ctx, PGAS_OPCODE_CMPIBRAEQ, reg, target, imm); } PORE_STATIC inline int pore_CMPIBRANE(PoreInlineContext *ctx, int reg, PoreInlineLocation target, uint64_t imm) { return pore_inline_cmpibra(ctx, PGAS_OPCODE_CMPIBRANE, reg, target, imm); } PORE_STATIC inline int pore_CMPIBSREQ(PoreInlineContext *ctx, int reg, PoreInlineLocation target, uint64_t imm) { return pore_inline_cmpibra(ctx, PGAS_OPCODE_CMPIBSREQ, reg, target, imm); } // BRAD, BSRD PORE_STATIC inline int pore_BRAD(PoreInlineContext *ctx, int reg) { return pore_inline_brad(ctx, PGAS_OPCODE_BRAD, reg); } PORE_STATIC inline int pore_BSRD(PoreInlineContext *ctx, int reg) { return pore_inline_brad(ctx, PGAS_OPCODE_BSRD, reg); } // ANDI, ORI, XORI PORE_STATIC inline int pore_ANDI(PoreInlineContext *ctx, int dest, int src, uint64_t imm) { return pore_inline_ilogic(ctx, PGAS_OPCODE_ANDI, dest, src, imm); } PORE_STATIC inline int pore_ORI(PoreInlineContext *ctx, int dest, int src, uint64_t imm) { return pore_inline_ilogic(ctx, PGAS_OPCODE_ORI, dest, src, imm); } PORE_STATIC inline int pore_XORI(PoreInlineContext *ctx, int dest, int src, uint64_t imm) { return pore_inline_ilogic(ctx, PGAS_OPCODE_XORI, dest, src, imm); } // AND, OR, XOR, ADD, SUB PORE_STATIC inline int pore_AND(PoreInlineContext *ctx, int dest, int src1, int src2) { return pore_inline_alurr(ctx, PGAS_OPCODE_AND, dest, src1, src2); } PORE_STATIC inline int pore_OR(PoreInlineContext *ctx, int dest, int src1, int src2) { return pore_inline_alurr(ctx, PGAS_OPCODE_OR, dest, src1, src2); } PORE_STATIC inline int pore_XOR(PoreInlineContext *ctx, int dest, int src1, int src2) { return pore_inline_alurr(ctx, PGAS_OPCODE_XOR, dest, src1, src2); } PORE_STATIC inline int pore_ADD(PoreInlineContext *ctx, int dest, int src1, int src2) { return pore_inline_alurr(ctx, PGAS_OPCODE_ADD, dest, src1, src2); } PORE_STATIC inline int pore_SUB(PoreInlineContext *ctx, int dest, int src1, int src2) { return pore_inline_alurr(ctx, PGAS_OPCODE_SUB, dest, src1, src2); } // ADDS, SUBS PORE_STATIC inline int pore_ADDS(PoreInlineContext *ctx, int dest, int src, int imm) { return pore_inline_adds(ctx, PGAS_OPCODE_ADDS, dest, src, imm); } PORE_STATIC inline int pore_SUBS(PoreInlineContext *ctx, int dest, int src, int imm) { return pore_inline_adds(ctx, PGAS_OPCODE_SUBS, dest, src, imm); } // NEG, MR, ROLS, LS, LI int pore_NEG(PoreInlineContext *ctx, int dest, int src); int pore_MR(PoreInlineContext *ctx, int dest, int src); int pore_ROLS(PoreInlineContext *ctx, int dest, int src, int imm); int pore_LS(PoreInlineContext *ctx, int dest, int imm); int pore_LI(PoreInlineContext *ctx, int dest, uint64_t imm); // LD, LDANDI, STD, STI, BSI, BCI PORE_STATIC inline int pore_LD(PoreInlineContext *ctx, int dest, int32_t offset, int base) { return pore_inline_load_store(ctx, PORE_INLINE_PSEUDO_LD, dest, offset, base, 0); } PORE_STATIC inline int pore_LDANDI(PoreInlineContext *ctx, int dest, int32_t offset, int base, uint64_t imm) { return pore_inline_load_store(ctx, PORE_INLINE_PSEUDO_LDANDI, dest, offset, base, imm); } PORE_STATIC inline int pore_STD(PoreInlineContext *ctx, int src, int32_t offset, int base) { return pore_inline_load_store(ctx, PORE_INLINE_PSEUDO_STD, src, offset, base, 0); } PORE_STATIC inline int pore_STI(PoreInlineContext *ctx, int32_t offset, int base, uint64_t imm) { return pore_inline_load_store(ctx, PGAS_OPCODE_STI, 0, offset, base, imm); } #ifdef IGNORE_HW274735 // BSI and BCI are redacted as instructions and reimplemented as "macros" due // to HW274735, unless specifically overridden. Note that the inline assembler // will allow D1 to be used as scratch here, unlike the underlying hardware // instruction. PORE_STATIC inline int pore_BSI(PoreInlineContext *ctx, int src, int32_t offset, int base, uint64_t imm) { return pore_inline_load_store(ctx, PGAS_OPCODE_BSI, src, offset, base, imm); } PORE_STATIC inline int pore_BCI(PoreInlineContext *ctx, int src, int32_t offset, int base, uint64_t imm) { return pore_inline_load_store(ctx, PGAS_OPCODE_BCI, src, offset, base, imm); } #else PORE_STATIC inline int pore_BSI(PoreInlineContext *ctx, int src, int32_t offset, int base, uint64_t imm) { return ((pore_LD(ctx, src, offset, base) || pore_ORI(ctx, src, src, imm) || pore_STD(ctx, src, offset, base)) ? ctx->error : 0); } PORE_STATIC inline int pore_BCI(PoreInlineContext *ctx, int src, int32_t offset, int base, uint64_t imm) { return ((pore_LDANDI(ctx, src, offset, base, ~imm) || pore_STD(ctx, src, offset, base)) ? ctx->error : 0); } #endif // IGNORE_HW274735 // BRAIA int pore_BRAIA(PoreInlineContext *ctx, uint16_t address_space, uint32_t offset); // SCAND int pore_SCAND(PoreInlineContext *ctx, int update, int capture, uint16_t length, uint32_t select, uint32_t offset); #endif /* __ASSEMBLER__ */ #if 0 { /* So __cplusplus doesn't mess w/auto-indent */ #endif #if( defined(__cplusplus) && !defined(PLIC_MODULE) ) } #endif #endif /* __PORE_INLINE_H__ */ skiboot-skiboot-5.1.13/libpore/pore_inline_assembler.c000066400000000000000000001326461265204436200231030ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/pore_inline_assembler.c $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: pore_inline_assembler.c,v 1.22 2013/12/11 00:11:14 bcbrock Exp $ // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/pore_inline_assembler.c,v $ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2013 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //----------------------------------------------------------------------------- // ** WARNING : This file is maintained as part of the OCC firmware. Do ** // ** not edit this file in the PMX area or the hardware procedure area ** // ** as any changes will be lost. ** /// \file pore_inline_assembler.c /// \brief Inline PGAS assembler for PgP/Stage1 PORE /// /// \page pore_inline_assembler PORE Inline Assembler and Disassembler /// /// Several procedures targeting the PORE engine require inline assembly and /// disassembly of PORE code, that is, they require that PORE instructions be /// assembled/disassembled directly into/from a host memory buffer. This page /// describes these facilities. The APIs described here are implemented in /// the files pore_inline.h, pore_inline_assembler.c and /// pore_inline_disassembler.c. Both the inline assembelr and disassembler /// conform to the PGAS assembly format for PORE. /// /// Both inline assembly and disassembly make use of a PoreInlineContext /// structure. This structure represents the state of a memory area being /// targeted for inline assembly and disassembly. The context is initialized /// with the pore_inline_context_create() API, and a pointer to an instance of /// this structure appears as the first argument of all assembler/disassembler /// APIs. As assembly/disassembly progresses the PoreInlineContext keeps /// track of how much host memory area has been filled by assembled code or /// scanned by the disassebler. /// /// Assembler/disassembler APIs are predicates that return 0 for success and a /// non-zero error code for failure. In the event of failure, the error code /// (a small integer) is also stored in the \a error field of the context /// structure. String forms of the error codes are also available in the /// global array pore_inline_error_strings[]. /// /// The assembler always produces PORE code in the PORE-native big-endian /// format. Likewise, the diassembler assumes the host memory to be /// disassembled contains PORE code in big-endian format. /// /// \section Initialization /// /// Before invoking inline assembly/disassembly APIs, an instance of a /// PoreInlineContext structure must be initialized using the /// pore_inline_context_create() API. For assembly, the context describes the /// host memory buffer that will contain the assembled code. For disassembly, /// the context describes the host memory area that contains the code to be /// disassembled. Full documentation is available for /// pore_inline_context_create(), including documentation for options that /// control assembly and disassembly. The implementation also provides a /// 'copy operator' for the context, pore_inline_context_copy(). /// /// An example of initializing a context for inline assembly with parity /// checking appears below. /// /// \code /// /// PoreInlineContext ctx; /// uint32_t buf[BUFSIZE]; /// /// rc = pore_inline_context_create(&ctx, buf, BUFSIZE * 4, 0, /// PORE_INLINE_CHECK_PARITY); /// if (rc) . . . Handle Error /// /// \endcode /// /// Applications that reuse the same memory buffer for assembling and /// processing multiple PORE programs can 'reset' the context between uses by /// using the pore_inline_context_reset() API. pore_inline_context_reset() /// resets the location counter and memory extent to their initial (creation) /// values, and the context error code is cleared. Any options specified at /// creation remain as they were. /// /// \section Assembler /// /// The inline assembler implements each PORE/PGAS instruction as individual /// function calls. The APIs are consistently named \c pore_\, where /// \c \ is a PGAS mnemonic in upper case. The arguments to each /// opcode appear in the same order that they appear in the source-level /// assembler, with appropriate C-language types. The supported opcode APIs /// are defined in pore_inline.h /// /// Since the PORE instruction APIs are effectivly predicates, linear code /// sequences are easily assembled using the C-language logical OR construct. /// Any non-0 return code will immediatly break the sequence and set the /// expression value to 1. The failure code can then be recovered from the \a /// error field of the context. This coding technique is illustrated in the /// following example of assembling a memory-memory copy sequence. /// /// \code /// /// PoreInlineContext ctx; /// int error; /// /// . . . // Initialize context /// /// error = /// pore_LD(&ctx, D0, 0, A0) || /// pore_STD(&ctx, D0, 0, A1); /// /// if (error) <. . . Handle error based on ctx.error> /// /// \endcode /// /// The above example generates code equivalent to /// /// \code /// /// ld D0, 0, A0 /// std D0, 0, A1 /// /// \endcode /// /// Again, if an error were to occur during assembly, inline assembly would /// stop (and the logical OR would terminate) at the point of failure. In /// particular, the inline assembler will never allow assembled code to exceed /// the bounds of the memory area defined by the initial call of /// pore_inline_context_create() that defines the assembler memory space. /// /// /// \subsection Register Names and Other Mnemonics /// /// The header file pore_inline.h defines macros for the register mnemonics. /// /// - D0, D1 : 64-bit data registers /// - A0, A1 : 32-bit address registers /// - P0, P1 : 7-bit Pervasive chiplet id registers /// - CTR : 24-bit ounter register /// - PC : 48-bit Program Counter /// - ETR : 64-bit EXE-Trigger Register (Low-order 32 bits are writable) /// - EMR : The Error Mask Register /// - IFR : ID/Flags Register /// - SPRG0 : 32-bit Special-Purpose General Register 0 /// /// Mnemonics for the condition code bits are also defined by pore_inline.h /// using the PGAS mnemonics. /// /// /// \subsection Assembling Branches /// /// Opcodes that implement relative branches require that the branch target be /// specified as a location counter . Once initialized, the current /// location counter is available as the \a lc field of the PoreInlineContext /// object controlling the assembly. The \a lc field is the only field /// (besides the error code held in the \a error field) that application code /// should ever reference. The inline assembler also provides a typedef /// PoreInlineLocation to use for location counters, as well as the macro /// PORE_LOCATION() to define a location variable inline with the code flow. /// /// \subsubsection Backward Branches /// /// Backward branches are straightforward. For example, the memory-memory /// copy example from earlier can be converted into a loop as shown below. The /// \a loop_target variable is initialized with the location counter of the /// first instruction of the loop. The final instruction of the loop then /// branches back to the \a loop_target. /// /// \code /// /// PoreInlineContext ctx; /// PoreInlineLocation loop_target = 0; // See ** below the example /// int error; /// /// . . . // Initialize context /// /// error = /// PORE_LOCATION(&ctx, loop_target) || /// pore_LD(&ctx, D0, 0, A0) || /// pore_STD(&ctx, D0, 0, A1) || /// pore_ADDS(&ctx, A0, A0, 8) || /// pore_ADDS(&ctx, A1, A1, 8) || /// pore_LOOP(&ctx, loop_target); /// /// if (error) <. . . Handle error based on ctx.error> /// /// \endcode /// /// The above inline assembler sequence is equivalent to the PGAS code /// sequence: /// /// \code /// /// loop_target: /// ld D0, 0, A0 /// std D0, 0, A1 /// adds A0, A0, 8 /// adds A1, A1, 8 /// loop loop_target /// /// \endcode /// /// ** Location counters used as loop targets may need to be initialized, /// otherwise the compiler may issue a warning that the variable "may be used /// uninitialized", although in well-written code this would never happen. /// /// /// \subsubsection Forward Branches /// /// Forward branches are more complex. Since the target location counter is /// not known until the target has been assembled, the inline assembler /// provides the API pore_inline_branch_fixup() to fix up forward branches /// once the actual target is known. This is illustrated in the simple code /// sequence below, where an instruction is conditionally skipped. /// /// \code /// /// PoreInlineContext ctx; /// PoreInlineLocation source = 0, target = 0; /// int error, rc; /// /// . . . // Initialize context /// /// error = /// PORE_LOCATION(&ctx, source) || /// pore_BRANZ(&ctx, D0, source) || /// pore_ADDS(&ctx, D1, D1, 1) || /// PORE_LOCATION(&ctx, target) || /// pore_LD(&ctx, D0, 0, A0); /// /// if (error) <. . . Handle assembly error based on ctx->error> /// rc = pore_inline_branch_fixup(&ctx, source, target); /// if (rc) <. . . Handle branch fixup error> /// /// \endcode /// /// In the above code, the branch instruction is initially assembled as a /// branch-to-self - the recommended idiom for forward branch source /// instructions. Once the entire sequence has been assembled, /// pore_inline_branch_fixup() reassembles the \c source instruction as a /// branch to the \c target instruction. The above instruction sequence is /// equivalent to the PGAS code below: /// /// \code /// /// source: /// branz D0, target /// adds D1, D1, 1 /// target: /// ld D0, 0, A0 /// /// \endcode /// /// /// \subsubsection Absolute Branches /// /// It is unlikely that a typical application of the PORE inline assembler /// would ever need to include an absolute branch, since the branch target in /// this case is a fixed absolute address that must be known at assembly /// time. However the inline assembler does provide the pore_BRAIA() API for /// this purpose. This opcode requires a 16-bit address space constant and a /// 32-bit absoulte address (offset) within the memory space to specify the /// branch. /// /// /// \section Disassembly /// /// Inline disassembly is implemented by a single API, /// pore_inline_disassemble(). The idea is similar to assembly: A host memory /// context containing PORE code (or data) is described by a PoreInlineContext /// structure. Each call of pore_inline_disassemble() disassembles the next /// instruction (or datum) in the context into a PoreInlineDisassembly /// structure provided by the caller. The disassembly object contains both /// binary and string forms of the disassembled instruction (or data). The /// next call of pore_inline_disassemble() proceses the next instruction (or /// datum) and so on. /// /// \subsection Text (Code) Disassembly /// /// In the example below the inline disassembler is used to completely /// disassemble a memory area containing text (code) to \a stdout until an /// error occurs, assumed to be either due to disassembling the entire memory /// area or finding an illegal instruction. /// /// \code /// /// PoreInlineContext ctx; /// PoreInlineDisassembly dis; /// /// . . . // Initialize context /// /// while (pore_inline_disassemble(&ctx, &dis) == 0) { /// printf("%s\n", dis.s); /// } /// /// \endcode /// /// To illustrate binary disassembly, the following example uses the /// disassembler to search for a RET statement in a block of PORE code, in /// order to extend an inline subroutine with more code. Note that the field /// \a dis->ctx contains the context that existed at the time the instruction /// was assembled. By copying this context back into the global context, /// inline assembly will continue by overwriting the RET with new /// instructions. If the copy had \e not been done, then newly assembled code /// would have \e followed the RET. /// /// \code /// /// PoreInlineContext ctx; /// PoreInlineDisassembly dis; /// /// . . . // Initialize context /// /// while ((pore_inline_disassemble(&ctx, &dis) == 0) && /// (dis.opcode != PORE_OPCODE_RET)); /// if (ctx.error != 0) { /// . . . // Handle error /// } else { /// pore_inline_context_copy(&ctx, &dis.ctx); /// . . . // Continue assembly by overwriting the RET /// } /// /// \endcode /// /// A special type of context reset is available to simplify applications that /// need to disassemble a just-assembled code sequence, e.g. for debugging. /// pore_inline_context_reset_excursion() resets the context such that the /// effective size of the context only covers the just-assembled code, /// allowing a dissassembly loop to cleanly stop once all code has been /// disassembled. The use is illustrated below - note that the disassembly /// stops on the expected error code PORE_INLINE_NO_MEMORY once the /// (effective) end of the buffer is reached. /// /// \code /// /// PoreInlineContext ctx; /// PoreInlineDisassembly dis; /// /// . . . // Initialize context /// . . . // Assemble code into context /// /// pore_inline_context_reset_excursion(&ctx); /// /// while (pore_inline_disassemble(&ctx, &dis) == 0) { /// printf("%s\n", dis.s); /// } /// if (ctx.error != PORE_INLINE_NO_MEMORY) { /// . . . // Handle error /// } /// /// \endcode /// /// \subsection Data Disassembly /// /// If the PoreInlineContext is created with the flag /// PORE_INLINE_DISASSEMBLE_DATA, then the context is disassembled as data. If /// the PoreInlineContext is created with the flag /// PORE_INLINE_DISASSEMBLE_UNKNOWN then putative data embedded in a text /// section will be disassembled as data. For complete information see the /// documentation for pore_inline_disassemble(). #define __PORE_INLINE_ASSEMBLER_C__ #include "pore_inline.h" #undef __PORE_INLINE_ASSEMBLER_C__ // Definitions of PORE register classes. These are predicates that return // 1 if the register is a member of the class, else 0. PORE_STATIC int pore_data(int reg) { return (reg == D0) || (reg == D1); } PORE_STATIC int pore_address(int reg) { return (reg == A0) || (reg == A1); } PORE_STATIC int pore_pervasive_chiplet_id(int reg) { return (reg == P0) || (reg == P1); } PORE_STATIC int pore_branch_compare_data(int reg) { return (reg == D0) || (reg == D1) || (reg == CTR); } PORE_STATIC int pore_ls_destination(int reg) { return (reg == D0) || (reg == D1) || (reg == A0) || (reg == A1) || (reg == P0) || (reg == P1) || (reg == CTR); } PORE_STATIC int pore_li_destination(int reg) { return (reg == D0) || (reg == D1) || (reg == A0) || (reg == A1) || (reg == P0) || (reg == P1) || (reg == CTR); } PORE_STATIC int pore_mr_source(int reg) { return (reg == D0) || (reg == D1) || (reg == A0) || (reg == A1) || (reg == P0) || (reg == P1) || (reg == CTR) || (reg == PC) || (reg == ETR) || (reg == SPRG0) || (reg == IFR) || (reg == EMR); } PORE_STATIC int pore_mr_destination(int reg) { return (reg == D0) || (reg == D1) || (reg == A0) || (reg == A1) || (reg == P0) || (reg == P1) || (reg == CTR) || (reg == PC) || (reg == SPRG0)|| (reg == EMR); } /// Portable store of a 32-bit integer in big-endian format /// /// The address \a p to receive the data is in the form of an unsigned long. void pore_inline_be32(unsigned long p, uint32_t x) { uint8_t *p8 = (uint8_t *)p; uint8_t *px = (uint8_t *)(&x); int i, j; if (!PORE_BIG_ENDIAN) { for (i = 0, j = 3; i < 4; i++, j--) { p8[i] = px[j]; } } else { *((uint32_t *)p) = x; } } /// Portable store of a 64-bit integer in big-endian format /// /// The address \a p to receive the data is in the form of an unsigned long. void pore_inline_be64(unsigned long p, uint64_t x) { uint8_t *p8 = (uint8_t *)p; uint8_t *px = (uint8_t *)(&x); int i, j; if (!PORE_BIG_ENDIAN) { for (i = 0, j = 7; i < 8; i++, j--) { p8[i] = px[j]; } } else { *((uint64_t *)p) = x; } } // Portable load of a 32-bit integer in big-endian format uint32_t pore_inline_host32(unsigned long p) { uint32_t x; uint8_t *p8 = (uint8_t *)p; uint8_t *px = (uint8_t *)(&x); int i, j; if (!PORE_BIG_ENDIAN) { for (i = 0, j = 3; i < 4; i++, j--) { px[j] = p8[i]; } } else { x = *((uint32_t *)p); } return x; } // Portable load of a 64-bit integer in big-endian format uint64_t pore_inline_host64(unsigned long p) { uint64_t x; uint8_t *p8 = (uint8_t *)p; uint8_t *px = (uint8_t *)(&x); int i, j; if (!PORE_BIG_ENDIAN) { for (i = 0, j = 7; i < 8; i++, j--) { px[j] = p8[i]; } } else { x = *((uint64_t *)p); } return x; } // 32-bit population count // // This is a well-known divide-and-conquer algorithm. The idea is to compute // sums of adjacent bit segments in parallel, in place. PORE_STATIC int pore_popcount32(uint32_t x) { uint32_t m1 = 0x55555555; uint32_t m2 = 0x33333333; uint32_t m4 = 0x0f0f0f0f; x -= (x >> 1) & m1; /* Sum pairs of bits */ x = (x & m2) + ((x >> 2) & m2);/* Sum 4-bit segments */ x = (x + (x >> 4)) & m4; /* Sum 8-bit segments */ x += x >> 8; /* Sum 16-bit segments */ return (x + (x >> 16)) & 0x3f; /* Final sum */ } // 64-bit population count PORE_STATIC int pore_popcount64(uint64_t x) { return pore_popcount32(x & 0xffffffff) + pore_popcount32(x >> 32); } // Compute the parity of a PORE instruction as 0 or 1 int pore_inline_parity(uint32_t instruction, uint64_t imd64) { return (pore_popcount32(instruction) + pore_popcount64(imd64)) % 2; } /// Reset a PORE inline assembler context to its creation state /// /// \param ctx A pointer to an initialized (and likely 'used') /// PoreInlineContext object. /// /// This API resets a PoreInlineContext object to it's \e creation state, that /// is, the state it was in after the call of pore_inline_context_create(). /// This API is designed for applications that reuse a memory buffer to /// assemble multiple PORE code sequences. After each sequence has been fully /// assembled and processed, calling pore_inline_context_reset() sets the /// context back as it was when the context was initially created so that the /// memory area can be reused. In particular, this API resets the location /// counter and memory extent to their initial values, and the error code is /// cleared. Any options specified at creation remain as they were. /// /// For a slightly different type of reset, see /// pore_inline_context_reset_excursion(). void pore_inline_context_reset(PoreInlineContext *ctx) { ctx->lc_address = ctx->memory; ctx->remaining = ctx->size; ctx->lc = ctx->original_lc; ctx->error = 0; } /// Reset a PORE inline assembler context to a special state for disassembly /// /// \param ctx A pointer to an initialized (and almost certainly 'used') /// PoreInlineContext object. /// /// This API resets a PoreInlineContext object to it's \e creation state, that /// is, the state it was in after the call of pore_inline_context_create(), \e /// except that the effective size of the memory area has been reduced to the /// size that was actually used during assembly. This API is designed for /// applications that assemble into a memory buffer and then want to easily /// disassemble the code (e.g., for debugging). After a code sequence has /// been assembled, calling pore_inline_context_reset_excursion() sets the /// context back as it was when the context was initially created, but with a /// (typically) shorter effective length, so that the disassembly will cleanly /// stop once the entire sequence has been disassembled. Once disassembled, /// the buffer can be fully resued after a subsequent call of /// pore_inline_context_reset(). In particular, this API resets the location /// counter to its initial value, clears the error code, and sets the /// effective size of the context to the amount of memory currently used. Any /// options specified at creation remain as they were. /// /// For a full context reset see pore_inline_context_reset(). For an example /// see the \b Disassembly section of \ref pore_inline_assembler. void pore_inline_context_reset_excursion(PoreInlineContext *ctx) { ctx->lc_address = ctx->memory; ctx->remaining = ctx->size - ctx->remaining; ctx->lc = ctx->original_lc; ctx->error = 0; } /// Create a PORE inline assembler context /// /// \param ctx A pointer to a PoreInlineContext object to be initialized /// and used for inline assembly. or disassembly. /// /// \param memory A pointer to the host memory area to receive the assembled /// code, or contain the code to disassemble. In general the inline assembler /// will expect this memory area to be 4-byte aligned. This pointer may be /// NULL (0) only if the associated \a size is also 0. /// /// \param size The size (in bytes) of the host memory area. The inline /// assembler will generate the PORE_INLINE_NO_MEMORY error if an attempt is /// made to assemble an instruction that would overflow the buffer, or /// disassemble past the end of the buffer. A 0 size is valid. /// /// \param lc The initial, bytewise, target location counter for the assembled /// or disassembled code. This paramater will normally be initialized to 0 for /// assembling relocatable programs. The parameter would only need to be /// specified as non-0 for special cases, such as creating a context for /// disassembly. /// /// \param options Option flags. Option flags are OR-ed together to create /// the final set of options. Valid options are /// /// - PORE_INLINE_GENERATE_PARITY : Generate the proper parity bit for each /// instruction during assembly. /// /// - PORE_INLINE_CHECK_PARITY : Check for correct instruction parity during /// disassembly. /// /// - PORE_INLINE_LISTING_MODE : Generate disassembly strings in the form of a /// listing that contains location counters and encoded instructions as well /// as their diassembly. By default the disassembly strings do not contain /// this information and can be fed back in as source code to a PORE /// assembler. /// /// - PORE_INLINE_DISASSEMBLE_DATA : generate disassembly assuming that the /// context contains data rather than text. Normally data is disassembled as /// .long directives, however if the context is unaligned or of an odd length /// then .byte directives may be used as well. This option can be used in /// conjunction with PORE_INLINE_LISTING_MODE. /// /// - PORE_INLINE_8_BYTE_DATA : generate data disassembly using 8-byte values /// rather than the default 4-byte values. Normally data is disassembled as /// .quad directives under this option, however if the context is unaligned or /// of an odd length then .long and .byte directives may be used as well. /// This option can be used in conjunction with PORE_INLINE_LISTING_MODE. /// /// A PoreInlineContext describes a memory area and assembler context for /// inline assembly and disassembly. Assembly/disassembly begins at the host /// memory location and virtual location counter described in the parameters. /// As instructions are assembled/disassembled the PoreInlineContext keeps /// track of where in the host memory and virtual PORE memory areas to place /// new instructions during assembly, or from where to fetch the next /// instruction to disassemble. /// /// \retval 0 Success /// /// \retval PORE_INLINE_INVALID_PARAMETER Either the \a context pointer is /// NULL (0), the \a memory pointer is NULL (0) with a non-0 size, or the \a /// options include invalid options. The error code is also stored as the /// value of ctx->error, and in the event of an error the ctx->size field is /// set to 0, effectively preventing the context from being used. int pore_inline_context_create(PoreInlineContext *ctx, void *memory, size_t size, PoreInlineLocation lc, int options) { int rc; int valid_options = PORE_INLINE_GENERATE_PARITY | PORE_INLINE_CHECK_PARITY | PORE_INLINE_LISTING_MODE | PORE_INLINE_DISASSEMBLE_DATA | PORE_INLINE_8_BYTE_DATA | PORE_INLINE_DISASSEMBLE_UNKNOWN; if ((ctx == NULL) || ((memory == NULL) && (size != 0)) || ((options & ~valid_options) != 0)) { rc = PORE_INLINE_INVALID_PARAMETER; } else { rc = 0; ctx->memory = (unsigned long)memory; ctx->size = size; ctx->original_lc = lc; ctx->options = options; pore_inline_context_reset(ctx); } if (ctx != NULL) { ctx->error = rc; if (rc) { ctx->size = 0; /* Effectively prevents using the ctx */ } } return rc; } /// Copy a PORE inline assembler context /// /// \param dest A pointer to a PoreInlineContext object to be initialized /// as a copy of the \a src context. /// /// \param src A pointer to a PoreInlineContext object to be used as the /// source of the copy. /// /// This API copies one PoreInlineContext structure to another. An example /// use appears in \ref pore_inline_assembler in the section discussing /// disassembly. void pore_inline_context_copy(PoreInlineContext *dest, PoreInlineContext *src) { *dest = *src; } // 'Bump' a context forward by a given number of bytes. This an internal API // and the bump is always known to be legal. void pore_inline_context_bump(PoreInlineContext *ctx, size_t bytes) { ctx->remaining -= bytes; ctx->lc += bytes; ctx->lc_address += bytes; } // Allocate space in the inline assembler context // // Allocation is specified and implemented in bytes. Both the physical // memory and the virtual LC are required to be 4-byte aligned. The allocator // returns a pointer to the memory area, or 0 if allocation fails. // Allocation failure sets the context error code to either // PORE_INLINE_NO_MEMORY or PORE_INLINE_ALIGNMENT_ERROR. PORE_STATIC unsigned long pore_inline_allocate(PoreInlineContext *ctx, size_t bytes) { unsigned long p = 0; if (((ctx->lc % 4) != 0) || ((ctx->lc_address % 4) != 0)) { ctx->error = PORE_INLINE_ALIGNMENT_ERROR; } else if (bytes > ctx->remaining) { ctx->error = PORE_INLINE_NO_MEMORY; } else { p = ctx->lc_address; pore_inline_context_bump(ctx, bytes); } return p; } // Assemble a 1-word instruction // // The opcode and operand are assumed to be legal, having come from // abstractions that check their arguments. This call may fail with // PORE_INLINE_NO_MEMORY if there is no more room in the memory buffer. A // non-zero return indicates failure. int pore_inline_instruction1(PoreInlineContext *ctx, int opcode, uint32_t operand) { uint32_t instruction; unsigned long p; p = pore_inline_allocate(ctx, 4); if (p != 0) { instruction = (opcode << 25) | operand; if (ctx->options & PORE_INLINE_GENERATE_PARITY) { instruction |= (1 - pore_inline_parity(instruction, 0)) << 24; } pore_inline_be32(p, instruction); ctx->error = 0; } return p == 0; } // Assemble a 3-word instruction // // The opcode and operand are assumed to be legal, having come from // abstractions that check their arguments. This call may fail with // PORE_INLINE_NO_MEMORY if there is no more room in the memory buffer. A // non-zero return indicates failure. int pore_inline_instruction3(PoreInlineContext *ctx, int opcode, uint32_t operand, uint64_t immediate) { uint32_t instruction; unsigned long p; p = pore_inline_allocate(ctx, 12); if (p != 0) { instruction = (opcode << 25) | operand; if (ctx->options & PORE_INLINE_GENERATE_PARITY) { instruction |= (1 - pore_inline_parity(instruction, immediate)) << 24; } pore_inline_be32(p, instruction); pore_inline_be64(p + 4, immediate); ctx->error = 0; } return p == 0; } // Assemble WAIT // // The cycle count must be an unsigned 24-bit immediate otherwise the error // PORE_INLINE_UINT24_REQUIRED is signalled. PGAS requires that HALT be used // if the intention is to halt int pore_WAITS(PoreInlineContext *ctx, uint32_t cycles) { uint32_t operand; int opcode = PGAS_OPCODE_WAITS; if (cycles == 0) { ctx->error = PORE_INLINE_USE_HALT; } else if ((cycles & 0xffffff) != cycles) { ctx->error = PORE_INLINE_UINT24_REQUIRED; } else { operand = cycles; pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble HOOKI // // The hook index must be an unsigned 24-bit immediate otherwise the error // PORE_INLINE_UINT24_REQUIRED is signalled. int pore_HOOKI(PoreInlineContext *ctx, uint32_t index, uint64_t imm) { uint32_t operand; int opcode = PGAS_OPCODE_HOOKI; if ((index & 0xffffff) != index) { ctx->error = PORE_INLINE_UINT24_REQUIRED; } else { operand = index; pore_inline_instruction3(ctx, opcode, operand, imm); } return ctx->error; } // Assemble BRA, BSR and LOOP // // The branch target here is a bytewise location counter. The target must be // 4-byte aligned and must be within the legal signed 24-bit word offset of // the current LC. Unaligned targets cause PORE_INLINE_ALIGNMENT_ERROR. // Unreachable targets cause PORE_INLINE_UNREACHABLE_TARGET. int pore_inline_bra(PoreInlineContext *ctx, int opcode, PoreInlineLocation target) { int32_t offset; uint32_t operand; if (target % 4) { ctx->error = PORE_INLINE_ALIGNMENT_ERROR; } else { offset = (int32_t)(target - ctx->lc) / 4; if ((offset >= (1 << 23)) || (offset < -(1 << 23))) { ctx->error = PORE_INLINE_UNREACHABLE_TARGET; } else { operand = offset & 0xffffff; pore_inline_instruction1(ctx, opcode, operand); } } return ctx->error; } // Assemble BRAZ and BRANZ // // The branch target here is a bytewise location counter. The target must be // 4-byte aligned and must be within the legal signed 20-bit word offset of // the current LC. Unaligned targets cause PORE_INLINE_ALIGNMENT_ERROR. // Unreachable targets cause PORE_INLINE_UNREACHABLE_TARGET. Illegal // operands cause PORE_INLINE_ILLEGAL_REGISTER. int pore_inline_brac(PoreInlineContext *ctx, int opcode, int reg, PoreInlineLocation target) { int32_t offset; uint32_t operand; if (target % 4) { ctx->error = PORE_INLINE_ALIGNMENT_ERROR; } else if (!pore_branch_compare_data(reg)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { offset = (int32_t)(target - ctx->lc) / 4; if ((offset >= (1 << 20)) || (offset < -(1 << 20))) { ctx->error = PORE_INLINE_UNREACHABLE_TARGET; } else { operand = (offset & 0xfffff) | (reg << 20); pore_inline_instruction1(ctx, opcode, operand); } } return ctx->error; } // Assemble CMPIBRAEQ, CMPIBRANE, CMPIBSREQ // // The branch target here is a bytewise location counter. The target must be // 4-byte aligned and must be within the legal signed 24-bit word offset of // the current LC. Unaligned targets cause PORE_INLINE_ALIGNMENT_ERROR. // Unreachable targets cause PORE_INLINE_UNREACHABLE_TARGET. Illegal // operands cause PORE_INLINE_ILLEGAL_REGISTER. int pore_inline_cmpibra(PoreInlineContext *ctx, int opcode, int reg, PoreInlineLocation target, uint64_t imm) { int32_t offset; uint32_t operand; if (target % 4) { ctx->error = PORE_INLINE_ALIGNMENT_ERROR; } else if (reg != D0) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { offset = (int32_t)(target - ctx->lc) / 4; if ((offset >= (1 << 23)) || (offset < -(1 << 23))) { ctx->error = PORE_INLINE_UNREACHABLE_TARGET; } else { operand = offset & 0xffffff; pore_inline_instruction3(ctx, opcode, operand, imm); } } return ctx->error; } // Assemble BRAD and BSRD // // Illegal operands cause PORE_INLINE_ILLEGAL_REGISTER. int pore_inline_brad(PoreInlineContext *ctx, int opcode, int reg) { uint32_t operand; if (!pore_data(reg)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { operand = reg << 20; pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble ANDI, ORI, XORI // // Source and destination must be of class 'data' otherwise the // PORE_INLINE_ILLEGAL_REGISTER error is generated. int pore_inline_ilogic(PoreInlineContext *ctx, int opcode, int dest, int src, uint64_t imm) { uint32_t operand; if (!pore_data(dest) || !pore_data(src)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { operand = (dest << 20) | (src << 16); pore_inline_instruction3(ctx, opcode, operand, imm); } return ctx->error; } // Assemble AND, OR, XOR, ADD, SUB // // Destination must be of class 'data' otherwise the // PORE_INLINE_ILLEGAL_REGISTER error is generated. src1 and src2 must be D0, // D1 respectively otherwise the PORE_INLINE_ILLEGAL_REGISTER error is // generated. int pore_inline_alurr(PoreInlineContext *ctx, int opcode, int dest, int src1, int src2) { uint32_t operand; if (!pore_data(dest) || (src1 != D0) || (src2 != D1)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { operand = (dest << 20); pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble ADDS and SUBS // // Destination must be of class 'ls_destination' and must be equal to source, // otherwise the PORE_INLINE_ILLEGAL_REGISTER error is generated. If the // immediate is not a signed 16-bit immediate then the // PORE_INLINE_INT16_REQUIRED error is generated. int pore_inline_adds(PoreInlineContext *ctx, int opcode, int dest, int src, int imm) { uint32_t operand; if (!pore_ls_destination(dest) || (dest != src)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { if ((imm >= (1 << 15)) || (imm < -(1 << 15))) { ctx->error = PORE_INLINE_INT16_REQUIRED; } else { operand = (dest << 20) | (imm & 0xffff); pore_inline_instruction1(ctx, opcode, operand); } } return ctx->error; } // Assemble NEG // // Source and destination must be of class 'data' otherwise the // PORE_INLINE_ILLEGAL_REGISTER error is generated. int pore_NEG(PoreInlineContext *ctx, int dest, int src) { uint32_t operand; int opcode = PGAS_OPCODE_NEG; if (!pore_data(dest) || !pore_data(src)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { operand = (dest << 20) | (src << 16); pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble MR // // The source must be an 'mr_source' and the destination must be an // 'mr_destination' otherwise the PORE_INLINE_ILLEGAL_REGISTER error is // generated. int pore_MR(PoreInlineContext *ctx, int dest, int src) { uint32_t operand; int opcode = PGAS_OPCODE_MR; if (!pore_mr_destination(dest) || !pore_mr_source(src)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { operand = (dest << 20) | (src << 16); pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble ROLS // // Source and destination must be of class 'data' otherwise the // PORE_INLINE_ILLEGAL_REGISTER error is generated. Illegal shifts yield the // PORE_INLINE_ILLEGAL_ROTATE error. int pore_ROLS(PoreInlineContext *ctx, int dest, int src, int imm) { uint32_t operand; int opcode = PGAS_OPCODE_ROLS; if (!pore_data(dest) || !pore_data(src)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else if ((imm != 1) && (imm != 4) && (imm != 8) && (imm != 16) && (imm != 32)) { ctx->error = PORE_INLINE_ILLEGAL_ROTATE; } else { operand = (dest << 20) | (src << 16) | imm; pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble LS // // The destination must be an 'ls_destination' otherwise the // PORE_INLINE_ILLEGAL_REGISTER error is generated. If the immediate is not // a signed 20-bit immediate then the PORE_INLINE_INT20_REQUIRED error is // generated. int pore_LS(PoreInlineContext *ctx, int dest, int imm) { uint32_t operand; int opcode = PGAS_OPCODE_LS; if (!pore_ls_destination(dest)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else if ((imm >= (1 << 19)) || (imm < -(1 << 19))) { ctx->error = PORE_INLINE_INT20_REQUIRED; } else { operand = (dest << 20) | (imm & 0xfffff); pore_inline_instruction1(ctx, opcode, operand); } return ctx->error; } // Assemble LI // // The destination must be an 'li destination' otherwise the // PORE_INLINE_ILLEGAL_REGISTER error is generated. int pore_LI(PoreInlineContext *ctx, int dest, uint64_t imm) { uint32_t operand; int opcode = PGAS_OPCODE_LI; if (!pore_li_destination(dest)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { operand = dest << 20; pore_inline_instruction3(ctx, opcode, operand, imm); } return ctx->error; } // BSI and BCI are normally redacted as instructions due to HW274735 // LD, LDANDI, STD, STI, BSI, BCI PORE_STATIC void pervasive_ima24(PoreInlineContext *ctx, int opcode, uint32_t offset, int base, uint64_t imm) { uint32_t operand; if ((offset & 0x80f00000) != 0) { ctx->error = PORE_INLINE_ILLEGAL_SCOM_ADDRESS; } else { operand = ((base % 2) << 22) | (offset & 0xfffff); switch (opcode) { case PGAS_OPCODE_LD0: case PGAS_OPCODE_LD1: case PGAS_OPCODE_STD0: case PGAS_OPCODE_STD1: pore_inline_instruction1(ctx, opcode, operand); break; default: pore_inline_instruction3(ctx, opcode, operand, imm); break; } } } PORE_STATIC void memory_ima24(PoreInlineContext *ctx, int opcode, uint32_t offset, int base, uint64_t imm) { uint32_t operand; if ((offset & 0x3fffff) != offset) { ctx->error = PORE_INLINE_UINT22_REQUIRED; } else if ((offset % 8) != 0) { ctx->error = PORE_INLINE_ALIGNMENT_ERROR; } else { operand = 0x800000 | ((base % 2) << 22) | (offset & 0x3fffff); switch (opcode) { case PGAS_OPCODE_LD0: case PGAS_OPCODE_LD1: case PGAS_OPCODE_STD0: case PGAS_OPCODE_STD1: pore_inline_instruction1(ctx, opcode, operand); break; default: pore_inline_instruction3(ctx, opcode, operand, imm); break; } } } PORE_STATIC void ima24(PoreInlineContext *ctx, int opcode, uint32_t offset, int base, uint64_t imm) { if (pore_pervasive_chiplet_id(base)) { pervasive_ima24(ctx, opcode, offset, base, imm); } else if (pore_address(base)) { memory_ima24(ctx, opcode, offset, base, imm); } else { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } } int pore_inline_load_store(PoreInlineContext *ctx, int opcode, int src_dest, int32_t offset, int base, uint64_t imm) { switch (opcode) { case PORE_INLINE_PSEUDO_LD: case PORE_INLINE_PSEUDO_LDANDI: case PORE_INLINE_PSEUDO_STD: // These three pick the real opcode based on the dest. register if (!pore_data(src_dest)) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } else { switch (opcode) { case PORE_INLINE_PSEUDO_LD: opcode = (src_dest == D0) ? PGAS_OPCODE_LD0 : PGAS_OPCODE_LD1; break; case PORE_INLINE_PSEUDO_LDANDI: opcode = (src_dest == D0) ? PGAS_OPCODE_LD0ANDI : PGAS_OPCODE_LD1ANDI; break; case PORE_INLINE_PSEUDO_STD: opcode = (src_dest == D0) ? PGAS_OPCODE_STD0 : PGAS_OPCODE_STD1; break; } } break; #ifdef IGNORE_HW274735 // BSI and BCI are normally redacted as instructions due to HW274735 case PGAS_OPCODE_BSI: case PGAS_OPCODE_BCI: if (src_dest != D0) { ctx->error = PORE_INLINE_ILLEGAL_REGISTER; } break; #endif // IGNORE_HW274735 case PGAS_OPCODE_STI: break; default: ctx->error = PORE_INLINE_BUG; } if (ctx->error == 0) { ima24(ctx, opcode, offset, base, imm); } return ctx->error; } // Assemble BRAIA int pore_BRAIA(PoreInlineContext *ctx, uint16_t address_space, uint32_t offset) { int opcode = PGAS_OPCODE_BRAI; uint32_t operand = 0; uint64_t imm = ((uint64_t)address_space << 32) | offset; pore_inline_instruction3(ctx, opcode, operand, imm); return ctx->error; } // Assemble SCAND int pore_SCAND(PoreInlineContext *ctx, int update, int capture, uint16_t length, uint32_t select, uint32_t offset) { int opcode = PGAS_OPCODE_SCAND; uint32_t operand; uint64_t imm = ((uint64_t)select << 32) | offset; if ((update < 0) || (update > 1) || (capture < 0) || (capture > 1)) { ctx->error = PORE_INLINE_INVALID_PARAMETER; } else { opcode = PGAS_OPCODE_SCAND; operand = (update << 23) | (capture << 22) | length; pore_inline_instruction3(ctx, opcode, operand, imm); } return ctx->error; } /// Fix up a PORE inline assembler forward branch instruction /// /// \param ctx A pointer to the initialized PoreInlineContext object /// controlling inline assembly. /// /// \param source The PORE inline location counter associated with the source /// instruction of the forward branch. /// /// \param target The PORE inline location counter associated with the target /// instruction of the forward branch. /// /// For usage examples, see the documentation \ref pore_inline_assembler. /// Although intended for forward branches, this API could be used to create /// backward branches as well. Note however the limitation that the \a source /// must be in the current context, since the source instruction needs to be /// reassembled with the branch target. In theory the \a target could be /// anywhere, as long as the location counter of the target is known. /// /// \retval 0 Success /// /// \retval code Failure. Any non-zero return is the PORE inline assmebler /// error code. The failure code is also stored in the PoreInlineContext /// object \a error field. The most likely causes of failure include a source /// location that is not in the current context or not associated with a /// branch instruction. int pore_inline_branch_fixup(PoreInlineContext *ctx, PoreInlineLocation source, PoreInlineLocation target) { uint32_t instruction; int32_t distance; uint64_t imm; int opcode, reg; PoreInlineContext source_ctx; if ((source < ctx->original_lc) || (source > ctx->lc)) { ctx->error = PORE_INLINE_ILLEGAL_SOURCE_LC; } else { // Create a context as it existed when the source instruction was // initially assembled, and then reassemble the instruction in that // context with the actual target. distance = ctx->lc - source; source_ctx = *ctx; source_ctx.lc = source; source_ctx.remaining += distance; source_ctx.lc_address -= distance; source_ctx.error = 0; instruction = pore_inline_host32(source_ctx.lc_address); opcode = (instruction >> 25); reg = (instruction >> 20) & 0xf; switch (opcode) { case PGAS_OPCODE_BRA: pore_BRA(&source_ctx, target); break; case PGAS_OPCODE_BSR: pore_BSR(&source_ctx, target); break; case PGAS_OPCODE_LOOP: pore_LOOP(&source_ctx, target); break; case PGAS_OPCODE_BRAZ: pore_BRAZ(&source_ctx, reg, target); break; case PGAS_OPCODE_BRANZ: pore_BRANZ(&source_ctx, reg, target); break; case PGAS_OPCODE_CMPIBRAEQ: imm = pore_inline_host64(source_ctx.lc_address + 4); pore_CMPIBRAEQ(&source_ctx, D0, target, imm); break; case PGAS_OPCODE_CMPIBRANE: imm = pore_inline_host64(source_ctx.lc_address + 4); pore_CMPIBRANE(&source_ctx, D0, target, imm); break; case PGAS_OPCODE_CMPIBSREQ: imm = pore_inline_host64(source_ctx.lc_address + 4); pore_CMPIBSREQ(&source_ctx, D0, target, imm); break; default: source_ctx.error = PORE_INLINE_NOT_A_BRANCH; break; } ctx->error = source_ctx.error; } return ctx->error; } skiboot-skiboot-5.1.13/libpore/sbe_xip_image.c000066400000000000000000002131221265204436200213230ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/sbe_xip_image.c $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: sbe_xip_image.c,v 1.28 2013/12/11 00:12:41 bcbrock Exp $ // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ipl/sbe/sbe_xip_image.c,v $ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2011 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //----------------------------------------------------------------------------- // *! OWNER NAME: Bishop Brock Email: bcbrock@us.ibm.com //------------------------------------------------------------------------------ /// \file sbe_xip_image.c /// \brief APIs for validating, normalizing, searching and manipulating /// SBE-XIP images. /// /// The background, APIs and implementation details are documented in the /// document "SBE-XIP Binary format" currently available at this link: /// /// - https://mcdoc.boeblingen.de.ibm.com/out/out.ViewDocument.php?documentid=2678 /// /// \bug The sbe_xip_validate() API should be carefully reviewed to ensure /// that validating even a corrupt image can not lead to a segfault, i.e., to /// ensure that no memory outside of the putative bounds of the image is ever /// referenced during validation. #ifndef PLIC_MODULE #include #include #include #include #endif // PLIC_MODULE #include #include #include #include #include "sbe_xip_image.h" //////////////////////////////////////////////////////////////////////////// // Local Functions //////////////////////////////////////////////////////////////////////////// // PHYP has their own way of implementing the functions. PHYP also // does not allow static functions or data, so all of the XIP_STATIC functions // defined here are global to PHYP. #ifdef PPC_HYP #ifdef PLIC_MODULE #define strcpy(dest, src) hvstrcpy(dest, src) #define strlen(s) hvstrlen(s) #define strcmp(s1, s2) hvstrcmp(s1, s2) #endif //PLIC_MODULE #define XIP_STATIC #else // PPC_HYP #define XIP_STATIC static #endif // PPC_HYP #ifdef DEBUG_SBE_XIP_IMAGE // Debugging support, normally disabled. All of the formatted I/O you see in // the code is effectively under this switch. #ifdef __FAPI #include "fapi.H" #define fprintf(stream, ...) FAPI_ERR(__VA_ARGS__) #define printf(...) FAPI_INF(__VA_ARGS__) #define TRACE_NEWLINE "" #else // __FAPI #include #define TRACE_NEWLINE "\n" #endif // __FAPI // Portable formatting of uint64_t. The ISO C99 standard requires // __STDC_FORMAT_MACROS to be defined in order for PRIx64 etc. to be defined. #define __STDC_FORMAT_MACROS #include #define F0x016llx "0x%016" PRIx64 #define F0x012llx "0x%012" PRIx64 XIP_STATIC SBE_XIP_ERROR_STRINGS(sbe_xip_error_strings); #define TRACE_ERROR(x) \ ({ \ fprintf(stderr, "%s:%d : Returning error code %d : %s" TRACE_NEWLINE, \ __FILE__, __LINE__, (x), \ SBE_XIP_ERROR_STRING(sbe_xip_error_strings, (x))); \ (x); \ }) #define TRACE_ERRORX(x, ...) \ ({ \ TRACE_ERROR(x); \ fprintf(stderr, ##__VA_ARGS__); \ (x); \ }) // Uncomment these if required for debugging, otherwise we get warnings from // GCC as they are not otherwise used. #if 0 XIP_STATIC uint32_t xipRevLe32(const uint32_t i_x); XIP_STATIC SBE_XIP_TYPE_STRINGS(type_strings); XIP_STATIC void dumpToc(int index, SbeXipToc* toc) { printf("TOC entry %d @ %p\n" " iv_id = 0x%08x\n" " iv_data = 0x%08x\n" " iv_type = %s\n" " iv_section = 0x%02x\n" " iv_elements = %d\n", index, toc, xipRevLe32(toc->iv_id), xipRevLe32(toc->iv_data), SBE_XIP_TYPE_STRING(type_strings, toc->iv_type), toc->iv_section, toc->iv_elements); } #endif #if 0 XIP_STATIC void dumpItem(SbeXipItem* item) { printf("SbeXipItem @ %p\n" " iv_toc = %p\n" " iv_address = " F0x016llx "\n" " iv_imageData = %p\n" " iv_id = %s\n" " iv_type = %s\n" " iv_elements = %d\n", item, item->iv_toc, item->iv_address, item->iv_imageData, item->iv_id, SBE_XIP_TYPE_STRING(type_strings, item->iv_type), item->iv_elements); dumpToc(-1, item->iv_toc); } #endif /* 0 */ XIP_STATIC void dumpSectionTable(const void* i_image) { int i, rc; SbeXipSection section; printf("Section table dump of image @ %p\n" " Entry Offset Size\n" "-------------------------------\n", i_image); for (i = 0; i < SBE_XIP_SECTIONS; i++) { rc = sbe_xip_get_section(i_image, i, §ion); if (rc) { printf(">>> dumpSectionTable got error at entry %d : %s\n", i, SBE_XIP_ERROR_STRING(sbe_xip_error_strings, rc)); break; } printf("%7d 0x%08x 0x%08x\n", i, section.iv_offset, section.iv_size); } } #else #define TRACE_ERROR(x) (x) #define TRACE_ERRORX(x, ...) (x) #define dumpToc(...) #define dumpItem(...) #define dumpSectionTable(...) #endif // Note: For maximum flexibility we provide private versions of // endian-conversion routines rather than counting on a system-specific header // to provide these. /// Byte-reverse a 16-bit integer if on a little-endian machine XIP_STATIC uint16_t xipRevLe16(const uint16_t i_x) { uint16_t rx; #ifndef _BIG_ENDIAN uint8_t *pix = (uint8_t*)(&i_x); uint8_t *prx = (uint8_t*)(&rx); prx[0] = pix[1]; prx[1] = pix[0]; #else rx = i_x; #endif return rx; } /// Byte-reverse a 32-bit integer if on a little-endian machine XIP_STATIC uint32_t xipRevLe32(const uint32_t i_x) { uint32_t rx; #ifndef _BIG_ENDIAN uint8_t *pix = (uint8_t*)(&i_x); uint8_t *prx = (uint8_t*)(&rx); prx[0] = pix[3]; prx[1] = pix[2]; prx[2] = pix[1]; prx[3] = pix[0]; #else rx = i_x; #endif return rx; } /// Byte-reverse a 64-bit integer if on a little-endian machine XIP_STATIC uint64_t xipRevLe64(const uint64_t i_x) { uint64_t rx; #ifndef _BIG_ENDIAN uint8_t *pix = (uint8_t*)(&i_x); uint8_t *prx = (uint8_t*)(&rx); prx[0] = pix[7]; prx[1] = pix[6]; prx[2] = pix[5]; prx[3] = pix[4]; prx[4] = pix[3]; prx[5] = pix[2]; prx[6] = pix[1]; prx[7] = pix[0]; #else rx = i_x; #endif return rx; } /// What is the image link address? XIP_STATIC uint64_t xipLinkAddress(const void* i_image) { return xipRevLe64(((SbeXipHeader*)i_image)->iv_linkAddress); } /// What is the image size? XIP_STATIC uint32_t xipImageSize(const void* i_image) { return xipRevLe32(((SbeXipHeader*)i_image)->iv_imageSize); } /// Set the image size XIP_STATIC void xipSetImageSize(void* io_image, const size_t i_size) { ((SbeXipHeader*)io_image)->iv_imageSize = xipRevLe32(i_size); } /// Re-establish the required final alignment XIP_STATIC void xipFinalAlignment(void* io_image) { uint32_t size; size = xipImageSize(io_image); if ((size % SBE_XIP_FINAL_ALIGNMENT) != 0) { xipSetImageSize(io_image, size + (SBE_XIP_FINAL_ALIGNMENT - (size % SBE_XIP_FINAL_ALIGNMENT))); } } /// Compute a host address from an image address and offset XIP_STATIC void* xipHostAddressFromOffset(const void* i_image, const uint32_t offset) { return (void*)((unsigned long)i_image + offset); } /// Convert a PORE address to a host address XIP_STATIC void* xipPore2Host(const void* i_image, const uint64_t i_poreAddress) { return xipHostAddressFromOffset(i_image, i_poreAddress - xipLinkAddress(i_image)); } XIP_STATIC int xipValidatePoreAddress(const void* i_image, const uint64_t i_poreAddress, const uint32_t size) { int rc; if ((i_poreAddress < xipLinkAddress(i_image)) || (i_poreAddress > (xipLinkAddress(i_image) + xipImageSize(i_image) - size))) { rc = TRACE_ERRORX(SBE_XIP_INVALID_ARGUMENT, "The PORE address " F0x012llx " is outside the bounds " "of the image (" F0x012llx ":" F0x012llx ") for %u-byte access.\n", i_poreAddress, xipLinkAddress(i_image), xipLinkAddress(i_image) + xipImageSize(i_image) - 1, size); } else { rc = 0; } return rc; } /// Get the magic number from the image XIP_STATIC uint64_t xipMagic(const void* i_image) { return xipRevLe64(((SbeXipHeader*)i_image)->iv_magic); } /// Get the header version from the image XIP_STATIC uint8_t xipHeaderVersion(const void* i_image) { return ((SbeXipHeader*)i_image)->iv_headerVersion; } /// Has the image been normalized? XIP_STATIC uint8_t xipNormalized(const void* i_image) { return ((SbeXipHeader*)i_image)->iv_normalized; } /// Has the image TOC been sorted? XIP_STATIC uint8_t xipSorted(const void* i_image) { return ((SbeXipHeader*)i_image)->iv_tocSorted; } /// A quick check that the image exists, has the correct magic and header /// version, and optionally is normalized. XIP_STATIC int xipQuickCheck(const void* i_image, const int i_normalizationRequired) { int rc; do { rc = 0; if (i_image == 0) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "Image pointer is NULL (0)\n"); break; } if ((xipMagic(i_image) >> 32) != SBE_XIP_MAGIC) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "Magic number mismatch; Found " "" F0x016llx ", expected 0x%08x........\n", xipMagic(i_image), SBE_XIP_MAGIC); break; } if ((xipHeaderVersion(i_image)) != SBE_XIP_HEADER_VERSION) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "Header version mismatch; Expecting %d, " "found %d\n", SBE_XIP_HEADER_VERSION, xipHeaderVersion(i_image)); break; } if (i_normalizationRequired && !xipNormalized(i_image)) { rc = TRACE_ERRORX(SBE_XIP_NOT_NORMALIZED, "Image not normalized\n"); break; } } while(0); return rc; } /// Convert a 32-bit relocatable offset to a full PORE 48-bit address XIP_STATIC uint64_t xipFullAddress(const void* i_image, uint32_t offset) { return (xipLinkAddress(i_image) & 0x0000ffff00000000ull) + offset; } /// Translate a section table entry XIP_STATIC void xipTranslateSection(SbeXipSection* o_dest, const SbeXipSection* i_src) { #ifndef _BIG_ENDIAN #if SBE_XIP_HEADER_VERSION != 8 #error This code assumes the SBE-XIP header version 8 layout #endif o_dest->iv_offset = xipRevLe32(i_src->iv_offset); o_dest->iv_size = xipRevLe32(i_src->iv_size); o_dest->iv_alignment = i_src->iv_alignment; o_dest->iv_reserved8[0] = 0; o_dest->iv_reserved8[1] = 0; o_dest->iv_reserved8[2] = 0; #else if (o_dest != i_src) { *o_dest = *i_src; } #endif /* _BIG_ENDIAN */ } /// Translate a TOC entry XIP_STATIC void xipTranslateToc(SbeXipToc* o_dest, SbeXipToc* i_src) { #ifndef _BIG_ENDIAN #if SBE_XIP_HEADER_VERSION != 8 #error This code assumes the SBE-XIP header version 8 layout #endif o_dest->iv_id = xipRevLe32(i_src->iv_id); o_dest->iv_data = xipRevLe32(i_src->iv_data); o_dest->iv_type = i_src->iv_type; o_dest->iv_section = i_src->iv_section; o_dest->iv_elements = i_src->iv_elements; o_dest->iv_pad = 0; #else if (o_dest != i_src) { *o_dest = *i_src; } #endif /* _BIG_ENDIAN */ } /// Find the final (highest-address) section of the image XIP_STATIC int xipFinalSection(const void* i_image, int* o_sectionId) { int i, rc, found; uint32_t offset; SbeXipHeader hostHeader; sbe_xip_translate_header(&hostHeader, (SbeXipHeader*)i_image); found = 0; offset = 0; *o_sectionId = 0; /* Make GCC -O3 happy */ for (i = 0; i < SBE_XIP_SECTIONS; i++) { if ((hostHeader.iv_section[i].iv_size != 0) && (hostHeader.iv_section[i].iv_offset >= offset)) { *o_sectionId = i; offset = hostHeader.iv_section[i].iv_offset; found = 1; } } if (!found) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "The image is empty\n"); } else { rc = 0; } return rc; } /// Return a pointer to an image-format section table entry XIP_STATIC int xipGetSectionPointer(const void* i_image, const int i_sectionId, SbeXipSection** o_imageSection) { int rc; if ((i_sectionId < 0) || (i_sectionId >= SBE_XIP_SECTIONS)) { rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT); } else { *o_imageSection = &(((SbeXipHeader*)i_image)->iv_section[i_sectionId]); rc = 0; } return rc; } /// Restore a section table entry from host format to image format. XIP_STATIC int xipPutSection(const void* i_image, const int i_sectionId, SbeXipSection* i_hostSection) { int rc; SbeXipSection *imageSection; rc = xipGetSectionPointer(i_image, i_sectionId, &imageSection); if (!rc) { xipTranslateSection(imageSection, i_hostSection); } return rc; } /// Set the offset of a section XIP_STATIC int xipSetSectionOffset(void* io_image, const int i_section, const uint32_t i_offset) { SbeXipSection* section; int rc; rc = xipGetSectionPointer(io_image, i_section, §ion); if (!rc) { section->iv_offset = xipRevLe32(i_offset); } return rc; } /// Set the size of a section XIP_STATIC int xipSetSectionSize(void* io_image, const int i_section, const uint32_t i_size) { SbeXipSection* section; int rc; rc = xipGetSectionPointer(io_image, i_section, §ion); if (!rc) { section->iv_size = xipRevLe32(i_size); } return rc; } /// Translate a PORE address in the image to a section and offset // We first check to be sure that the PORE address is contained in the image, // using the full 48-bit form. Then we scan the section table to see which // section contains the address - if none then the image is corrupted. We can // (must) use the 32-bit offset form of the address here. XIP_STATIC int xipPore2Section(const void* i_image, const uint64_t i_poreAddress, int* o_section, uint32_t* o_offset) { int rc, sectionId; SbeXipSection section; uint32_t addressOffset; do { rc = 0; if ((i_poreAddress < xipLinkAddress(i_image)) || (i_poreAddress > (xipLinkAddress(i_image) + xipImageSize(i_image)))) { rc = TRACE_ERRORX(SBE_XIP_INVALID_ARGUMENT, "pore2section: The i_poreAddress argument " "(" F0x016llx ")\nis outside the bounds of the " "image (" F0x016llx ":" F0x016llx ")\n", i_poreAddress, xipLinkAddress(i_image), xipLinkAddress(i_image) + xipImageSize(i_image)); break; } addressOffset = (i_poreAddress - xipLinkAddress(i_image)) & 0xffffffff; for (sectionId = 0; sectionId < SBE_XIP_SECTIONS; sectionId++) { rc = sbe_xip_get_section(i_image, sectionId, §ion); if (rc) { rc = TRACE_ERROR(SBE_XIP_BUG); /* Can't happen */ break; } if ((section.iv_size != 0) && (addressOffset >= section.iv_offset) && (addressOffset < (section.iv_offset + section.iv_size))) { break; } } if (rc) break; if (sectionId == SBE_XIP_SECTIONS) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "Error processing PORE address " F0x016llx ". " "The address is not mapped in any section.\n" "A section table dump appears below\n", i_poreAddress); dumpSectionTable(i_image); break; } *o_section = sectionId; *o_offset = addressOffset - section.iv_offset; } while(0); return rc; } /// Get the information required to search the TOC. /// /// All return values are optional. XIP_STATIC int xipGetToc(void* i_image, SbeXipToc** o_toc, size_t* o_entries, int* o_sorted, char** o_strings) { int rc; SbeXipSection tocSection, stringsSection; do { rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_TOC, &tocSection); if (rc) break; rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_STRINGS, &stringsSection); if (rc) break; if (o_toc) { *o_toc = (SbeXipToc*)((uint8_t*)i_image + tocSection.iv_offset); } if (o_entries) { *o_entries = tocSection.iv_size / sizeof(SbeXipToc); } if (o_sorted) { *o_sorted = xipSorted(i_image); } if (o_strings) { *o_strings = (char*)i_image + stringsSection.iv_offset; } } while (0); return rc; } /// Compare two normalized TOC entries for sorting. XIP_STATIC int xipCompareToc(const SbeXipToc* i_a, const SbeXipToc* i_b, const char* i_strings) { return strcmp(i_strings + xipRevLe32(i_a->iv_id), i_strings + xipRevLe32(i_b->iv_id)); } /// Iterative quicksort of the TOC // Note: The stack requirement is limited to 256 bytes + minor local storage. XIP_STATIC void xipQuickSort(SbeXipToc* io_toc, int i_left, int i_right, const char* i_strings) { int i, j, left, right, sp; SbeXipToc pivot, temp; uint32_t stack[64]; sp = 0; stack[sp++] = i_left; stack[sp++] = i_right; while (sp) { right = stack[--sp]; left = stack[--sp]; i = left; j = right; pivot = io_toc[(i + j) / 2]; while (i <= j) { while (xipCompareToc(&(io_toc[i]), &pivot, i_strings) < 0) { i++; } while (xipCompareToc(&(io_toc[j]), &pivot, i_strings) > 0) { j--; } if (i <= j) { temp = io_toc[i]; io_toc[i] = io_toc[j]; io_toc[j] = temp; i++; j--; } } if (left < j) { stack[sp++] = left; stack[sp++] = j; } if (i < right) { stack[sp++] = i; stack[sp++] = right; } } } /// TOC linear search XIP_STATIC int xipLinearSearch(void* i_image, const char* i_id, SbeXipToc** o_entry) { int rc; SbeXipToc *imageToc, hostToc; size_t entries; char* strings; *o_entry = 0; rc = xipGetToc(i_image, &imageToc, &entries, 0, &strings); if (!rc) { for (; entries; entries--, imageToc++) { xipTranslateToc(&hostToc, imageToc); if (strcmp(i_id, strings + hostToc.iv_id) == 0) { break; } } if (entries) { *o_entry = imageToc; rc = 0; } else { *o_entry = 0; rc = TRACE_ERROR(SBE_XIP_ITEM_NOT_FOUND); } } return rc; } /// A classic binary search of a (presumed) sorted array XIP_STATIC int xipBinarySearch(void* i_image, const char* i_id, SbeXipToc** o_entry) { int rc; SbeXipToc *imageToc; size_t entries; char* strings; int sorted, left, right, next, sort; do { *o_entry = 0; rc = xipGetToc(i_image, &imageToc, &entries, &sorted, &strings); if (rc) break; if (!sorted) { rc = TRACE_ERROR(SBE_XIP_BUG); break; } left = 0; right = entries - 1; while (left <= right) { next = (left + right) / 2; sort = strcmp(i_id, strings + xipRevLe32(imageToc[next].iv_id)); if (sort == 0) { *o_entry = &(imageToc[next]); break; } else if (sort < 0) { right = next - 1; } else { left = next + 1; } } if (*o_entry == 0) { rc = TRACE_ERROR(SBE_XIP_ITEM_NOT_FOUND); break; } } while (0); return rc; } /// Validate a TOC entry as a mapping function /// /// The TOC is validated by searching for the entry, which will uncover /// duplicate entries or problems with sorting/searching. XIP_STATIC int xipValidateTocEntry(void* io_image, const SbeXipItem* i_item, void* io_arg) { int rc; SbeXipItem found; (void)io_arg; do { rc = sbe_xip_find(io_image, i_item->iv_id, &found); if (rc) { rc = TRACE_ERRORX(rc, "TOC entry for %s not found\n", i_item->iv_id); } else if (found.iv_toc != i_item->iv_toc) { rc = TRACE_ERRORX(SBE_XIP_TOC_ERROR, "Duplicate TOC entry for '%s'\n", i_item->iv_id); } break; } while (0); return rc; } // This is the FNV-1a hash, used for hashing symbol names in the .fixed // section into 32-bit hashes for the mini-TOC. // According to the authors: // "FNV hash algorithms and source code have been released into the public // domain. The authors of the FNV algorithmm look deliberate steps to disclose // the algorhtm (sic) in a public forum soon after it was invented. More than // a year passed after this public disclosure and the authors deliberatly took // no steps to patent the FNV algorithm. Therefore it is safe to say that the // FNV authors have no patent claims on the FNV algorithm as published." #define FNV_OFFSET_BASIS 2166136261u #define FNV_PRIME32 16777619u static uint32_t xipHash32(const char* s) { uint32_t hash; hash = FNV_OFFSET_BASIS; while (*s) { hash ^= *s++; hash *= FNV_PRIME32; } return hash; } // Normalize a TOC entry // Normalize the TOC entry by converting relocatable pointers into 32-bit // offsets from the beginning of the section containing the data. All // addresses in the TOC are actually 32-bit offsets in the address space named // in bits 16:31 of the link address of the image. XIP_STATIC int xipNormalizeToc(void* io_image, SbeXipToc *io_imageToc, SbeXipHashedToc** io_fixedTocEntry, size_t* io_fixedEntriesRemaining) { SbeXipToc hostToc; int idSection, dataSection; uint32_t idOffset, dataOffset; char* hostString; int rc; do { // Translate the TOC entry to host format. Then locate the // sections/offsets of the Id string (which must be in .strings) and // the data. xipTranslateToc(&hostToc, io_imageToc); hostString = (char*)xipPore2Host(io_image, xipFullAddress(io_image, hostToc.iv_id)); rc = xipPore2Section(io_image, xipFullAddress(io_image, hostToc.iv_id), &idSection, &idOffset); if (rc) break; if (idSection != SBE_XIP_SECTION_STRINGS) { rc = TRACE_ERROR(SBE_XIP_IMAGE_ERROR); break; } rc = xipPore2Section(io_image, xipFullAddress(io_image, hostToc.iv_data), &dataSection, &dataOffset); if (rc) break; // Now replace the Id and data pointers with their offsets, and update // the data section in the TOC entry. hostToc.iv_id = idOffset; hostToc.iv_data = dataOffset; hostToc.iv_section = dataSection; // If this TOC entry is from .fixed, create a new record in .fixed_toc if (hostToc.iv_section == SBE_XIP_SECTION_FIXED) { if (*io_fixedEntriesRemaining == 0) { rc = TRACE_ERRORX(SBE_XIP_TOC_ERROR, "Too many TOC entries for .fixed\n"); break; } if (hostToc.iv_data != (uint16_t)hostToc.iv_data) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "The .fixed section is too big to index\n"); break; } (*io_fixedTocEntry)->iv_hash = xipRevLe32(xipHash32(hostString)); (*io_fixedTocEntry)->iv_offset = xipRevLe16(hostToc.iv_data); (*io_fixedTocEntry)->iv_type = hostToc.iv_type; (*io_fixedTocEntry)->iv_elements = hostToc.iv_elements; (*io_fixedTocEntry)++; (*io_fixedEntriesRemaining)--; } // Finally update the TOC entry xipTranslateToc(io_imageToc, &hostToc); } while (0); return rc; } // Check for hash collisions in the .fixed mini-TOC. Note that endianness is // not an issue here, as we're comparing for equality. XIP_STATIC int xipHashCollision(SbeXipHashedToc* i_fixedToc, size_t i_entries) { int rc; size_t i, j; rc = 0; for (i = 0; i < i_entries; i++) { for (j = i + 1; j < i_entries; j++) { if (i_fixedToc[i].iv_hash == i_fixedToc[j].iv_hash) { rc = TRACE_ERRORX(SBE_XIP_HASH_COLLISION, "Hash collision at index %d\n", i); break; } } if (rc) break; } return rc; } /// Decode a normalized image-format TOC entry into a host-format SbeXipItem /// structure XIP_STATIC int xipDecodeToc(void* i_image, SbeXipToc* i_imageToc, SbeXipItem* o_item) { int rc; SbeXipToc hostToc; SbeXipSection dataSection, stringsSection; do { if (!xipNormalized(i_image)) { rc = TRACE_ERROR(SBE_XIP_NOT_NORMALIZED); break; } // Translate the TOC entry and set the TOC pointer, data type and // number of elements in the outgoing structure. The Id string is // always located in the TOC_STRINGS section. xipTranslateToc(&hostToc, i_imageToc); o_item->iv_toc = i_imageToc; o_item->iv_type = hostToc.iv_type; o_item->iv_elements = hostToc.iv_elements; sbe_xip_get_section(i_image, SBE_XIP_SECTION_STRINGS, &stringsSection); o_item->iv_id = (char*)i_image + stringsSection.iv_offset + hostToc.iv_id; // The data (or text address) are addressed by relative offsets from // the beginning of their section. The TOC entry may remain in the TOC // even though the section has been removed from the image, so this // case needs to be covered. rc = sbe_xip_get_section(i_image, hostToc.iv_section, &dataSection); if (rc) break; if (dataSection.iv_size == 0) { rc = TRACE_ERROR(SBE_XIP_DATA_NOT_PRESENT); break; } o_item->iv_imageData = (void*)((uint8_t*)i_image + dataSection.iv_offset + hostToc.iv_data); o_item->iv_address = xipLinkAddress(i_image) + dataSection.iv_offset + hostToc.iv_data; o_item->iv_partial = 0; } while (0); return rc; } /// Sort the TOC XIP_STATIC int xipSortToc(void* io_image) { int rc; SbeXipToc *hostToc; size_t entries; char* strings; do { rc = xipQuickCheck(io_image, 1); if (rc) break; if (xipSorted(io_image)) break; rc = xipGetToc(io_image, &hostToc, &entries, 0, &strings); if (rc) break; xipQuickSort(hostToc, 0, entries - 1, strings); ((SbeXipHeader*)io_image)->iv_tocSorted = 1; } while (0); return rc; } // Pad the image with 0 to a given power-of-2 alignment. The image size is // modified to reflect the pad, but the caller must modify the section size to // reflect the pad. XIP_STATIC int xipPadImage(void* io_image, uint32_t i_allocation, uint32_t i_align, uint32_t* pad) { int rc; do { rc = 0; if ((i_align == 0) || ((i_align & (i_align - 1)) != 0)) { rc = TRACE_ERRORX(SBE_XIP_INVALID_ARGUMENT, "Alignment specification (%u) " "not a power-of-2\n", i_align); break; } *pad = xipImageSize(io_image) % i_align; if (*pad != 0) { *pad = i_align - *pad; if ((xipImageSize(io_image) + *pad) > i_allocation) { rc = TRACE_ERROR(SBE_XIP_WOULD_OVERFLOW); break; } memset((void*)((unsigned long)io_image + xipImageSize(io_image)), 0, *pad); xipSetImageSize(io_image, xipImageSize(io_image) + *pad); } } while (0); return rc; } // Get the .fixed_toc section XIP_STATIC int xipGetFixedToc(void* io_image, SbeXipHashedToc** o_imageToc, size_t* o_entries) { int rc; SbeXipSection section; rc = sbe_xip_get_section(io_image, SBE_XIP_SECTION_FIXED_TOC, §ion); if (!rc) { *o_imageToc = (SbeXipHashedToc*)((unsigned long)io_image + section.iv_offset); *o_entries = section.iv_size / sizeof(SbeXipHashedToc); } return rc; } // Search for an item in the fixed TOC, and populate a partial TOC entry if // requested. This table is small and unsorted so a linear search is // adequate. The TOC structures are also small so all byte-reversal is done // 'by hand' rather than with a translate-type API. XIP_STATIC int xipFixedFind(void* i_image, const char* i_id, SbeXipItem* o_item) { int rc; SbeXipHashedToc* toc; size_t entries; uint32_t hash; SbeXipSection fixedSection; uint32_t offset; do { rc = xipGetFixedToc(i_image, &toc, &entries); if (rc) break; for (hash = xipRevLe32(xipHash32(i_id)); entries != 0; entries--, toc++) { if (toc->iv_hash == hash) break; } if (entries == 0) { rc = SBE_XIP_ITEM_NOT_FOUND; break; } else { rc = 0; } // The caller may have requested a lookup only (o_item == 0), in which // case we're done. Otherwise we create a partial SbeXipItem and // populate the non-0 fields analogously to the xipDecodeToc() // routine. The data resides in the .fixed section in this case. if (o_item == 0) break; o_item->iv_partial = 1; o_item->iv_toc = 0; o_item->iv_id = 0; o_item->iv_type = toc->iv_type; o_item->iv_elements = toc->iv_elements; rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_FIXED, &fixedSection); if (rc) break; if (fixedSection.iv_size == 0) { rc = TRACE_ERROR(SBE_XIP_DATA_NOT_PRESENT); break; } offset = fixedSection.iv_offset + xipRevLe16(toc->iv_offset); o_item->iv_imageData = (void*)((uint8_t*)i_image + offset); o_item->iv_address = xipLinkAddress(i_image) + offset; } while (0); return rc; } // Search for an item in the special built-in TOC of header fields, and // populate a partial TOC entry if requested. // // This facility was added to allow header data to be searched by name even // when the TOC has been stripped. This API will only be used in the case of a // stripped TOC since the header fields are also indexed in the main TOC. // // The table is allocated on the stack in order to make this code concurrently // patchable in PHYP (although PHYP applications will never use this code). // The table is small and unsorted so a linear search is adequate, and the // stack requirememts are small. XIP_STATIC int xipHeaderFind(void* i_image, const char* i_id, SbeXipItem* o_item) { int rc; unsigned i; uint32_t offset; SbeXipSection headerSection; #define HEADER_TOC(id, field, type) \ {#id, offsetof(SbeXipHeader, field), type} struct HeaderToc { const char* iv_id; uint16_t iv_offset; uint8_t iv_type; } toc[] = { HEADER_TOC(magic, iv_magic, SBE_XIP_UINT64), HEADER_TOC(entry_offset, iv_entryOffset, SBE_XIP_UINT64), HEADER_TOC(link_address, iv_linkAddress, SBE_XIP_UINT64), HEADER_TOC(image_size, iv_imageSize, SBE_XIP_UINT32), HEADER_TOC(build_date, iv_buildDate, SBE_XIP_UINT32), HEADER_TOC(build_time, iv_buildTime, SBE_XIP_UINT32), HEADER_TOC(header_version, iv_headerVersion, SBE_XIP_UINT8), HEADER_TOC(toc_normalized, iv_normalized, SBE_XIP_UINT8), HEADER_TOC(toc_sorted, iv_tocSorted, SBE_XIP_UINT8), HEADER_TOC(build_user, iv_buildUser, SBE_XIP_STRING), HEADER_TOC(build_host, iv_buildHost, SBE_XIP_STRING), }; do { rc = SBE_XIP_ITEM_NOT_FOUND; for (i = 0; i < (sizeof(toc) / sizeof(struct HeaderToc)); i++) { if (strcmp(i_id, toc[i].iv_id) == 0) { rc = 0; break; } } if (rc) break; // The caller may have requested a lookup only (o_item == 0), in which // case we're done. Otherwise we create a partial SbeXipItem and // populate the non-0 fields analogously to the xipDecodeToc() // routine. The data resides in the .fixed section in this case. if (o_item == 0) break; o_item->iv_partial = 1; o_item->iv_toc = 0; o_item->iv_id = 0; o_item->iv_type = toc[i].iv_type; o_item->iv_elements = 1; /* True for now... */ rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_HEADER, &headerSection); if (rc) break; if (headerSection.iv_size == 0) { rc = TRACE_ERROR(SBE_XIP_DATA_NOT_PRESENT); break; } offset = headerSection.iv_offset + toc[i].iv_offset; o_item->iv_imageData = (void*)((uint8_t*)i_image + offset); o_item->iv_address = xipLinkAddress(i_image) + offset; } while (0); return rc; } //////////////////////////////////////////////////////////////////////////// // Published API //////////////////////////////////////////////////////////////////////////// int sbe_xip_validate(void* i_image, const uint32_t i_size) { SbeXipHeader hostHeader; int rc = 0, i; uint32_t linkAddress, imageSize, extent, offset, size; uint8_t alignment; sbe_xip_translate_header(&hostHeader, (SbeXipHeader*)i_image); do { // Validate C/Assembler constraints. if (sizeof(SbeXipSection) != SIZE_OF_SBE_XIP_SECTION) { rc = TRACE_ERRORX(SBE_XIP_BUG, "C/Assembler size mismatch(%d/%d) " "for SbeXipSection\n", sizeof(SbeXipSection), SIZE_OF_SBE_XIP_SECTION); break; } if (sizeof(SbeXipToc) != SIZE_OF_SBE_XIP_TOC) { rc = TRACE_ERRORX(SBE_XIP_BUG, "C/Assembler size mismatch(%d/%d) " "for SbeXipToc\n", sizeof(SbeXipToc), SIZE_OF_SBE_XIP_TOC); break; } if (sizeof(SbeXipHashedToc) != SIZE_OF_SBE_XIP_HASHED_TOC) { rc = TRACE_ERRORX(SBE_XIP_BUG, "C/Assembler size mismatch(%d/%d) " "for SbeXipHashedToc\n", sizeof(SbeXipHashedToc), SIZE_OF_SBE_XIP_HASHED_TOC); break; } // Validate the image pointer and magic number rc = xipQuickCheck(i_image, 0); if (rc) break; // Validate the image size linkAddress = hostHeader.iv_linkAddress; imageSize = hostHeader.iv_imageSize; extent = linkAddress + imageSize; if (imageSize < sizeof(SbeXipHeader)) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "sbe_xip_validate(%p, %u) : " "The image size recorded in the image " "(%u) is smaller than the header size.\n", i_image, i_size, imageSize); break; } if (imageSize != i_size) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "sbe_xip_validate(%p, %u) : " "The image size recorded in the image " "(%u) does not match the i_size parameter.\n", i_image, i_size, imageSize); break; } if (extent <= linkAddress) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "sbe_xip_validate(%p, %u) : " "Given the link address (%u) and the " "image size, the image wraps the address space\n", i_image, i_size, linkAddress); break; } if ((imageSize % SBE_XIP_FINAL_ALIGNMENT) != 0) { rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR, "sbe_xip_validate(%p, %u) : " "The image size (%u) is not a multiple of %u\n", i_image, i_size, imageSize, SBE_XIP_FINAL_ALIGNMENT); break; } // Validate that all sections appear to be within the image // bounds, and are aligned correctly. for (i = 0; i < SBE_XIP_SECTIONS; i++) { offset = hostHeader.iv_section[i].iv_offset; size = hostHeader.iv_section[i].iv_size; alignment = hostHeader.iv_section[i].iv_alignment; if ((offset > imageSize) || ((offset + size) > imageSize) || ((offset + size) < offset)) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "Section %d does not appear to be within " "the bounds of the image\n" "offset = %u, size = %u, image size = %u\n", i, offset, size, imageSize); break; } if ((offset % alignment) != 0) { rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR, "Section %d requires %d-byte initial " "alignment but the section offset is %u\n", i, alignment, offset); break; } } if (rc) break; // If the TOC exists and the image is normalized, validate each TOC // entry. size = hostHeader.iv_section[SBE_XIP_SECTION_TOC].iv_size; if (size != 0) { if (xipNormalized(i_image)) { rc = sbe_xip_map_toc(i_image, xipValidateTocEntry, 0); if (rc) break; } } } while (0); return rc; } int sbe_xip_validate2(void* i_image, const uint32_t i_size, const uint32_t i_maskIgnores) { SbeXipHeader hostHeader; int rc = 0, i; uint32_t linkAddress, imageSize, extent, offset, size; uint8_t alignment; sbe_xip_translate_header(&hostHeader, (SbeXipHeader*)i_image); do { // Validate C/Assembler constraints. if (sizeof(SbeXipSection) != SIZE_OF_SBE_XIP_SECTION) { rc = TRACE_ERRORX(SBE_XIP_BUG, "C/Assembler size mismatch(%d/%d) " "for SbeXipSection\n", sizeof(SbeXipSection), SIZE_OF_SBE_XIP_SECTION); break; } if (sizeof(SbeXipToc) != SIZE_OF_SBE_XIP_TOC) { rc = TRACE_ERRORX(SBE_XIP_BUG, "C/Assembler size mismatch(%d/%d) " "for SbeXipToc\n", sizeof(SbeXipToc), SIZE_OF_SBE_XIP_TOC); break; } if (sizeof(SbeXipHashedToc) != SIZE_OF_SBE_XIP_HASHED_TOC) { rc = TRACE_ERRORX(SBE_XIP_BUG, "C/Assembler size mismatch(%d/%d) " "for SbeXipHashedToc\n", sizeof(SbeXipHashedToc), SIZE_OF_SBE_XIP_HASHED_TOC); break; } // Validate the image pointer and magic number rc = xipQuickCheck(i_image, 0); if (rc) break; // Validate the image size linkAddress = hostHeader.iv_linkAddress; imageSize = hostHeader.iv_imageSize; extent = linkAddress + imageSize; if (imageSize < sizeof(SbeXipHeader)) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "sbe_xip_validate2(%p, %u) : " "The image size recorded in the image " "(%u) is smaller than the header size.\n", i_image, i_size, imageSize); break; } if (imageSize != i_size && !(i_maskIgnores & SBE_XIP_IGNORE_FILE_SIZE)) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "sbe_xip_validate2(%p, %u) : " "The image size recorded in the image " "(%u) does not match the i_size parameter.\n", i_image, i_size, imageSize); break; } if (extent <= linkAddress) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "sbe_xip_validate2(%p, %u) : " "Given the link address (%u) and the " "image size, the image wraps the address space\n", i_image, i_size, linkAddress); break; } if ((imageSize % SBE_XIP_FINAL_ALIGNMENT) != 0) { rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR, "sbe_xip_validate2(%p, %u) : " "The image size (%u) is not a multiple of %u\n", i_image, i_size, imageSize, SBE_XIP_FINAL_ALIGNMENT); break; } // Validate that all sections appear to be within the image // bounds, and are aligned correctly. for (i = 0; i < SBE_XIP_SECTIONS; i++) { offset = hostHeader.iv_section[i].iv_offset; size = hostHeader.iv_section[i].iv_size; alignment = hostHeader.iv_section[i].iv_alignment; if ((offset > imageSize) || ((offset + size) > imageSize) || ((offset + size) < offset)) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "Section %d does not appear to be within " "the bounds of the image\n" "offset = %u, size = %u, image size = %u\n", i, offset, size, imageSize); break; } if ((offset % alignment) != 0) { rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR, "Section %d requires %d-byte initial " "alignment but the section offset is %u\n", i, alignment, offset); break; } } if (rc) break; // If the TOC exists and the image is normalized, validate each TOC // entry. size = hostHeader.iv_section[SBE_XIP_SECTION_TOC].iv_size; if (size != 0) { if (xipNormalized(i_image)) { rc = sbe_xip_map_toc(i_image, xipValidateTocEntry, 0); if (rc) break; } } } while (0); return rc; } // Normalization: // // 1. Normalize the TOC, unless the image is already normalized. The image // must be marked as normalized before sorting. // // 2. Sort the TOC. // // 3. Clear the section offsets of any empty sections to make the section // table reports less confusing. // // 4. Clear normalization status on any failure. int sbe_xip_normalize(void* io_image) { int rc, i; SbeXipSection section; SbeXipToc* imageToc; SbeXipHashedToc* fixedImageToc = NULL; SbeXipHashedToc* fixedTocEntry = NULL; size_t tocEntries = 0; size_t fixedTocEntries = 0; size_t fixedEntriesRemaining = 0; do { rc = xipQuickCheck(io_image, 0); if (rc) break; if (!xipNormalized(io_image)) { rc = xipGetToc(io_image, &imageToc, &tocEntries, 0, 0); if (rc) break; rc = xipGetFixedToc(io_image, &fixedImageToc, &fixedTocEntries); if (rc) break; fixedTocEntry = fixedImageToc; fixedEntriesRemaining = fixedTocEntries; for (; tocEntries--; imageToc++) { rc = xipNormalizeToc(io_image, imageToc, &fixedTocEntry, &fixedEntriesRemaining); if (rc) break; } if (rc) break; if (fixedEntriesRemaining != 0) { rc = TRACE_ERRORX(SBE_XIP_TOC_ERROR, "Not enough TOC entries for .fixed"); break; } rc = xipHashCollision(fixedImageToc, fixedTocEntries); if (rc) break; ((SbeXipHeader*)io_image)->iv_normalized = 1; } rc = xipSortToc(io_image); if (rc) break; for (i = 0; i < SBE_XIP_SECTIONS; i++) { rc = sbe_xip_get_section(io_image, i, §ion); if (rc) break; if (section.iv_size == 0) { xipSetSectionOffset(io_image, i, 0); } } if (rc) break; } while(0); ((SbeXipHeader*)io_image)->iv_normalized = (rc == 0); return rc; } int sbe_xip_image_size(void* io_image, uint32_t* o_size) { int rc; rc = xipQuickCheck(io_image, 0); if (!rc) { *o_size = xipImageSize(io_image); } return rc; } int sbe_xip_get_section(const void* i_image, const int i_sectionId, SbeXipSection* o_hostSection) { int rc; SbeXipSection *imageSection; rc = xipGetSectionPointer(i_image, i_sectionId, &imageSection); if (!rc) { xipTranslateSection(o_hostSection, imageSection); } return rc; } // If the 'big' TOC is not present, search the mini-TOCs that only index the // .fixed and .header sections. int sbe_xip_find(void* i_image, const char* i_id, SbeXipItem* o_item) { int rc; SbeXipToc* toc; SbeXipItem item, *pitem; SbeXipSection* tocSection; do { rc = xipQuickCheck(i_image, 1); if (rc) break; rc = xipGetSectionPointer(i_image, SBE_XIP_SECTION_TOC, &tocSection); if (rc) break; if (tocSection->iv_size == 0) { rc = xipFixedFind(i_image, i_id, o_item); if (rc) { rc = xipHeaderFind(i_image, i_id, o_item); } break; } if (xipSorted(i_image)) { rc = xipBinarySearch(i_image, i_id, &toc); } else { rc = xipLinearSearch(i_image, i_id, &toc); } if (rc) break; if (o_item) { pitem = o_item; } else { pitem = &item; } rc = xipDecodeToc(i_image, toc, pitem); if (rc) break; } while (0); return rc; } int sbe_xip_map_halt(void* io_image, int (*i_fn)(void* io_image, const uint64_t i_poreAddress, const char* i_rcString, void* io_arg), void* io_arg) { int rc; SbeXipSection haltSection; SbeXipHalt *halt; uint32_t size; uint32_t actualSize; do { rc = xipQuickCheck(io_image, 0); if (rc) break; rc = sbe_xip_get_section(io_image, SBE_XIP_SECTION_HALT, &haltSection); if (rc) break; halt = (SbeXipHalt*)((unsigned long)io_image + haltSection.iv_offset); size = haltSection.iv_size; while (size) { rc = i_fn(io_image, xipRevLe64(halt->iv_address), halt->iv_string, io_arg); if (rc) break; // The SbeXipHalt structure claims a 4-character string. The // computation below computes the actual record size based on the // actual length of the string, including the 0-byte termination. actualSize = 8 + (((strlen(halt->iv_string) + 4) / 4) * 4); if (size < actualSize) { rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "The .halt section is improperly formed\n"); break; } size -= actualSize; halt = (SbeXipHalt*)((unsigned long)halt + actualSize); }; if (rc) break; } while (0); return rc; } typedef struct { uint64_t iv_address; const char* iv_string; } GetHaltStruct; XIP_STATIC int xipGetHaltMap(void* io_image, const uint64_t i_poreAddress, const char* i_rcString, void* io_arg) { int rc; GetHaltStruct* s = (GetHaltStruct*)io_arg; (void)io_image; if (i_poreAddress == s->iv_address) { s->iv_string = i_rcString; rc = -1; } else { rc = 0; } return rc; } int sbe_xip_get_halt(void* io_image, const uint64_t i_poreAddress, const char** o_rcString) { int rc; GetHaltStruct s; s.iv_address = i_poreAddress; do { rc = xipQuickCheck(io_image, 0); if (rc) break; rc = sbe_xip_map_halt(io_image, xipGetHaltMap, &s); if (rc == 0) { rc = TRACE_ERRORX(SBE_XIP_ITEM_NOT_FOUND, "sbe_xip_get_halt: No HALT code is associated " "with address " F0x012llx "\n", i_poreAddress); } else if (rc < 0) { *o_rcString = s.iv_string; rc = 0; } } while (0); return rc; } int sbe_xip_get_scalar(void *i_image, const char* i_id, uint64_t* o_data) { int rc; SbeXipItem item; rc = sbe_xip_find(i_image, i_id, &item); if (!rc) { switch (item.iv_type) { case SBE_XIP_UINT8: *o_data = *((uint8_t*)(item.iv_imageData)); break; case SBE_XIP_UINT32: *o_data = xipRevLe32(*((uint32_t*)(item.iv_imageData))); break; case SBE_XIP_UINT64: *o_data = xipRevLe64(*((uint64_t*)(item.iv_imageData))); break; case SBE_XIP_ADDRESS: *o_data = item.iv_address; break; default: rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR); break; } } return rc; } int sbe_xip_get_element(void *i_image, const char* i_id, const uint32_t i_index, uint64_t* o_data) { int rc; SbeXipItem item; do { rc = sbe_xip_find(i_image, i_id, &item); if (rc) break; if ((item.iv_elements != 0) && (i_index >= item.iv_elements)) { rc = TRACE_ERROR(SBE_XIP_BOUNDS_ERROR); break; } switch (item.iv_type) { case SBE_XIP_UINT8: *o_data = ((uint8_t*)(item.iv_imageData))[i_index]; break; case SBE_XIP_UINT32: *o_data = xipRevLe32(((uint32_t*)(item.iv_imageData))[i_index]); break; case SBE_XIP_UINT64: *o_data = xipRevLe64(((uint64_t*)(item.iv_imageData))[i_index]); break; default: rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR); break; } if (rc) break; } while (0); return rc; } int sbe_xip_get_string(void *i_image, const char* i_id, char** o_data) { int rc; SbeXipItem item; rc = sbe_xip_find(i_image, i_id, &item); if (!rc) { switch (item.iv_type) { case SBE_XIP_STRING: *o_data = (char*)(item.iv_imageData); break; default: rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR); break; } } return rc; } int sbe_xip_read_uint64(const void *i_image, const uint64_t i_poreAddress, uint64_t* o_data) { int rc; do { rc = xipQuickCheck(i_image, 0); if (rc) break; rc = xipValidatePoreAddress(i_image, i_poreAddress, 8); if (rc) break; if (i_poreAddress % 8) { rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR); break; } *o_data = xipRevLe64(*((uint64_t*)xipPore2Host(i_image, i_poreAddress))); } while(0); return rc; } int sbe_xip_set_scalar(void* io_image, const char* i_id, const uint64_t i_data) { int rc; SbeXipItem item; rc = sbe_xip_find(io_image, i_id, &item); if (!rc) { switch(item.iv_type) { case SBE_XIP_UINT8: *((uint8_t*)(item.iv_imageData)) = (uint8_t)i_data; break; case SBE_XIP_UINT32: *((uint32_t*)(item.iv_imageData)) = xipRevLe32((uint32_t)i_data); break; case SBE_XIP_UINT64: *((uint64_t*)(item.iv_imageData)) = xipRevLe64((uint64_t)i_data); break; default: rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR); break; } } return rc; } int sbe_xip_set_element(void *i_image, const char* i_id, const uint32_t i_index, const uint64_t i_data) { int rc; SbeXipItem item; do { rc = sbe_xip_find(i_image, i_id, &item); if (rc) break; if ((item.iv_elements != 0) && (i_index >= item.iv_elements)) { rc = TRACE_ERROR(SBE_XIP_BOUNDS_ERROR); break; } switch (item.iv_type) { case SBE_XIP_UINT8: ((uint8_t*)(item.iv_imageData))[i_index] = (uint8_t)i_data; break; case SBE_XIP_UINT32: ((uint32_t*)(item.iv_imageData))[i_index] = xipRevLe32((uint32_t)i_data); break; case SBE_XIP_UINT64: ((uint64_t*)(item.iv_imageData))[i_index] = xipRevLe64((uint64_t)i_data); break; default: rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR); break; } if (rc) break; } while (0); return rc; } int sbe_xip_set_string(void *i_image, const char* i_id, const char* i_data) { int rc; SbeXipItem item; char* dest; rc = sbe_xip_find(i_image, i_id, &item); if (!rc) { switch (item.iv_type) { case SBE_XIP_STRING: dest = (char*)(item.iv_imageData); if (strlen(dest) < strlen(i_data)) { memcpy(dest, i_data, strlen(dest)); } else { strcpy(dest, i_data); } break; default: rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR); break; } } return rc; } int sbe_xip_write_uint64(void *io_image, const uint64_t i_poreAddress, const uint64_t i_data) { int rc; do { rc = xipQuickCheck(io_image, 0); if (rc) break; rc = xipValidatePoreAddress(io_image, i_poreAddress, 8); if (rc) break; if (i_poreAddress % 8) { rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR); break; } *((uint64_t*)xipPore2Host(io_image, i_poreAddress)) = xipRevLe64(i_data); } while(0); return rc; } int sbe_xip_delete_section(void* io_image, const int i_sectionId) { int rc, final; SbeXipSection section; do { rc = xipQuickCheck(io_image, 1); if (rc) break; rc = sbe_xip_get_section(io_image, i_sectionId, §ion); if (rc) break; // Deleting an empty section is a NOP. Otherwise the section must be // the final section of the image. Update the sizes and re-establish // the final image alignment. if (section.iv_size == 0) break; rc = xipFinalSection(io_image, &final); if (rc) break; if (final != i_sectionId) { rc = TRACE_ERRORX(SBE_XIP_SECTION_ERROR, "Attempt to delete non-final section %d\n", i_sectionId); break; } xipSetSectionOffset(io_image, i_sectionId, 0); xipSetSectionSize(io_image, i_sectionId, 0); // For cleanliness we also remove any alignment padding that had been // appended between the now-last section and the deleted section, then // re-establish the final alignment. The assumption is that all images // always have the correct final alignment, so there is no way this // could overflow a designated buffer space since the image size is // the same or has been reduced. rc = xipFinalSection(io_image, &final); if (rc) break; rc = sbe_xip_get_section(io_image, final, §ion); if (rc) break; xipSetImageSize(io_image, section.iv_offset + section.iv_size); xipFinalAlignment(io_image); } while (0); return rc; } #ifndef PPC_HYP // This API is not needed by PHYP procedures, and is elided since PHYP does // not support malloc(). int sbe_xip_duplicate_section(const void* i_image, const int i_sectionId, void** o_duplicate, uint32_t* o_size) { SbeXipSection section; int rc; *o_duplicate = 0; do { rc = xipQuickCheck(i_image, 0); if (rc) break; rc = sbe_xip_get_section(i_image, i_sectionId, §ion); if (rc) break; if (section.iv_size == 0) { rc = TRACE_ERRORX(SBE_XIP_SECTION_ERROR, "Attempt to duplicate empty section %d\n", i_sectionId); break; } *o_duplicate = malloc(section.iv_size); *o_size = section.iv_size; if (*o_duplicate == 0) { rc = TRACE_ERROR(SBE_XIP_NO_MEMORY); break; } memcpy(*o_duplicate, xipHostAddressFromOffset(i_image, section.iv_offset), section.iv_size); } while (0); if (rc) { free(*o_duplicate); *o_duplicate = 0; *o_size = 0; } return rc; } #endif // PPC_HYP // The append must be done in such a way that if the append fails, the image // is not modified. This behavior is required by applications that // speculatively append until the allocation fails, but still require the // final image to be valid. To accomplish this the initial image size and // section statistics are captured at entry, and restored in the event of an // error. int sbe_xip_append(void* io_image, const int i_sectionId, const void* i_data, const uint32_t i_size, const uint32_t i_allocation, uint32_t* o_sectionOffset) { SbeXipSection section, initialSection; int rc, final, restoreOnError; void* hostAddress; uint32_t pad, initialSize; do { restoreOnError = 0; rc = xipQuickCheck(io_image, 1); if (rc) break; rc = sbe_xip_get_section(io_image, i_sectionId, §ion); if (rc) break; if (i_size == 0) break; initialSection = section; initialSize = xipImageSize(io_image); restoreOnError = 1; if (section.iv_size == 0) { // The section is empty, and now becomes the final section. Pad // the image to the specified section alignment. Note that the // size of the previously final section does not change. rc = xipPadImage(io_image, i_allocation, section.iv_alignment, &pad); if (rc) break; section.iv_offset = xipImageSize(io_image); } else { // Otherwise, the section must be the final section in order to // continue. Remove any padding from the image. rc = xipFinalSection(io_image, &final); if (rc) break; if (final != i_sectionId) { rc = TRACE_ERRORX(SBE_XIP_SECTION_ERROR, "Attempt to append to non-final section " "%d\n", i_sectionId); break; } xipSetImageSize(io_image, section.iv_offset + section.iv_size); } // Make sure the allocated space won't overflow. Set the return // parameter o_sectionOffset and copy the new data into the image (or // simply clear the space). if ((xipImageSize(io_image) + i_size) > i_allocation) { rc = TRACE_ERROR(SBE_XIP_WOULD_OVERFLOW); break; } if (o_sectionOffset != 0) { *o_sectionOffset = section.iv_size; } hostAddress = xipHostAddressFromOffset(io_image, xipImageSize(io_image)); if (i_data == 0) { memset(hostAddress, 0, i_size); } else { memcpy(hostAddress, i_data, i_size); } // Update the image size and section table. Note that the final // alignment may push out of the allocation. xipSetImageSize(io_image, xipImageSize(io_image) + i_size); xipFinalAlignment(io_image); if (xipImageSize(io_image) > i_allocation) { rc = TRACE_ERROR(SBE_XIP_WOULD_OVERFLOW); break; } section.iv_size += i_size; if (xipPutSection(io_image, i_sectionId, §ion) != 0) { rc = TRACE_ERROR(SBE_XIP_BUG); /* Can't happen */ break; } // Special case if (i_sectionId == SBE_XIP_SECTION_TOC) { ((SbeXipHeader*)io_image)->iv_tocSorted = 0; } } while (0); if (rc && restoreOnError) { if (xipPutSection(io_image, i_sectionId, &initialSection) != 0) { rc = TRACE_ERROR(SBE_XIP_BUG); /* Can't happen */ } xipSetImageSize(io_image, initialSize); } return rc; } int sbe_xip_section2pore(const void* i_image, const int i_sectionId, const uint32_t i_offset, uint64_t* o_poreAddress) { int rc; SbeXipSection section; do { rc = xipQuickCheck(i_image, 0); if (rc) break; rc = sbe_xip_get_section(i_image, i_sectionId, §ion); if (rc) break; if (section.iv_size == 0) { rc = TRACE_ERROR(SBE_XIP_SECTION_ERROR); break; } if (i_offset > (section.iv_offset + section.iv_size)) { rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT); break; } *o_poreAddress = xipLinkAddress(i_image) + section.iv_offset + i_offset; if (*o_poreAddress % 4) { rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR); break; } } while(0); return rc; } int sbe_xip_pore2section(const void* i_image, const uint64_t i_poreAddress, int* i_section, uint32_t* i_offset) { int rc; do { rc = xipQuickCheck(i_image, 0); if (rc) break; rc = xipPore2Section(i_image, i_poreAddress, i_section, i_offset); } while(0); return rc; } int sbe_xip_pore2host(const void* i_image, const uint64_t i_poreAddress, void** o_hostAddress) { int rc; do { rc = xipQuickCheck(i_image, 0); if (rc) break; if ((i_poreAddress < xipLinkAddress(i_image)) || (i_poreAddress > (xipLinkAddress(i_image) + xipImageSize(i_image)))) { rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT); break; } *o_hostAddress = xipHostAddressFromOffset(i_image, i_poreAddress - xipLinkAddress(i_image)); } while(0); return rc; } int sbe_xip_host2pore(const void* i_image, void* i_hostAddress, uint64_t* o_poreAddress) { int rc; do { rc = xipQuickCheck(i_image, 0); if (rc) break; if ((i_hostAddress < i_image) || (i_hostAddress > xipHostAddressFromOffset(i_image, xipImageSize(i_image)))) { rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT); break; } *o_poreAddress = xipLinkAddress(i_image) + ((unsigned long)i_hostAddress - (unsigned long)i_image); if (*o_poreAddress % 4) { rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR); break; } } while(0); return rc; } void sbe_xip_translate_header(SbeXipHeader* o_dest, const SbeXipHeader* i_src) { #ifndef _BIG_ENDIAN int i; SbeXipSection* destSection; const SbeXipSection* srcSection; #if SBE_XIP_HEADER_VERSION != 8 #error This code assumes the SBE-XIP header version 8 layout #endif o_dest->iv_magic = xipRevLe64(i_src->iv_magic); o_dest->iv_entryOffset = xipRevLe64(i_src->iv_entryOffset); o_dest->iv_linkAddress = xipRevLe64(i_src->iv_linkAddress); for (i = 0; i < 5; i++) { o_dest->iv_reserved64[i] = 0; } for (i = 0, destSection = o_dest->iv_section, srcSection = i_src->iv_section; i < SBE_XIP_SECTIONS; i++, destSection++, srcSection++) { xipTranslateSection(destSection, srcSection); } o_dest->iv_imageSize = xipRevLe32(i_src->iv_imageSize); o_dest->iv_buildDate = xipRevLe32(i_src->iv_buildDate); o_dest->iv_buildTime = xipRevLe32(i_src->iv_buildTime); for (i = 0; i < 5; i++) { o_dest->iv_reserved32[i] = 0; } o_dest->iv_headerVersion = i_src->iv_headerVersion; o_dest->iv_normalized = i_src->iv_normalized; o_dest->iv_tocSorted = i_src->iv_tocSorted; for (i = 0; i < 3; i++) { o_dest->iv_reserved8[i] = 0; } memcpy(o_dest->iv_buildUser, i_src->iv_buildUser, sizeof(i_src->iv_buildUser)); memcpy(o_dest->iv_buildHost, i_src->iv_buildHost, sizeof(i_src->iv_buildHost)); memcpy(o_dest->iv_reservedChar, i_src->iv_reservedChar, sizeof(i_src->iv_reservedChar)); #else if (o_dest != i_src) { *o_dest = *i_src; } #endif /* _BIG_ENDIAN */ } int sbe_xip_map_toc(void* io_image, int (*i_fn)(void* io_image, const SbeXipItem* i_item, void* io_arg), void* io_arg) { int rc; SbeXipToc *imageToc; SbeXipItem item; size_t entries; do { rc = xipQuickCheck(io_image, 0); if (rc) break; rc = xipGetToc(io_image, &imageToc, &entries, 0, 0); if (rc) break; for (; entries--; imageToc++) { rc = xipDecodeToc(io_image, imageToc, &item); if (rc) break; rc = i_fn(io_image, &item, io_arg); if (rc) break; } } while(0); return rc; } skiboot-skiboot-5.1.13/libpore/sbe_xip_image.h000066400000000000000000002064271265204436200213420ustar00rootroot00000000000000/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/sbe_xip_image.h $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ #ifndef __SBE_XIP_IMAGE_H #define __SBE_XIP_IMAGE_H // $Id: sbe_xip_image.h,v 1.24 2013/06/13 20:26:33 bcbrock Exp $ // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ipl/sbe/sbe_xip_image.h,v $ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2011 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //----------------------------------------------------------------------------- // *! OWNER NAME: Bishop Brock Email: bcbrock@us.ibm.com //------------------------------------------------------------------------------ /// \file sbe_xip_image.h /// \brief Everything related to creating and manipulating SBE-XIP binary /// images. #include "fapi_sbe_common.H" /// Current version (fields, layout, sections) of the SBE_XIP header /// /// If any changes are made to this file or to sbe_xip_header.H, please update /// the header version and follow-up on all of the error messages. #define SBE_XIP_HEADER_VERSION 8 /// \defgroup sbe_xip_magic_numbers SBE-XIP magic numbers /// /// An SBE-XIP magic number is a 64-bit constant. The 4 high-order bytes /// contain the ASCII characters "XIP " and identify the image as an SBE-XIP /// image, while the 4 low-order bytes identify the type of the image. /// /// @{ #define SBE_XIP_MAGIC 0x58495020 // "XIP " #define SBE_BASE_MAGIC ULL(0x5849502042415345) // "XIP BASE" #define SBE_SEEPROM_MAGIC ULL(0x584950205345504d) // "XIP SEPM" #define SBE_CENTAUR_MAGIC ULL(0x58495020434e5452) // "XIP CNTR" /// @} /// \defgroup sbe_xip_sections SBE-XIP Image Section Indexes /// /// These constants define the order that the SbeXipSection structures appear /// in the header, which is not necessarily the order the sections appear in /// the binary image. Given that SBE-XIP image contents are tightly /// controlled, we use this simple indexing scheme for the allowed sections /// rather than a more general approach, e.g., allowing arbitrary sections /// identified by their names. /// /// @{ // -*- DO NOT REORDER OR EDIT THIS SET OF CONSTANTS WITHOUT ALSO EDITING -*- // -*- THE ASSEMBLER LAYOUT IN sbe_xip_header.H. -*- #define SBE_XIP_SECTION_HEADER 0 #define SBE_XIP_SECTION_FIXED 1 #define SBE_XIP_SECTION_FIXED_TOC 2 #define SBE_XIP_SECTION_IPL_TEXT 3 #define SBE_XIP_SECTION_IPL_DATA 4 #define SBE_XIP_SECTION_TEXT 5 #define SBE_XIP_SECTION_DATA 6 #define SBE_XIP_SECTION_TOC 7 #define SBE_XIP_SECTION_STRINGS 8 #define SBE_XIP_SECTION_HALT 9 #define SBE_XIP_SECTION_PIBMEM0 10 #define SBE_XIP_SECTION_DCRINGS 11 #define SBE_XIP_SECTION_RINGS 12 #define SBE_XIP_SECTION_SLW 13 #define SBE_XIP_SECTION_FIT 14 #define SBE_XIP_SECTION_FFDC 15 #define SBE_XIP_SECTIONS 16 /// @} /// \defgroup sbe_xip_validate() ignore masks. /// /// These defines, when matched in sbe_xip_validate(), cause the validation /// to skip the check of the corresponding property. The purpose is to more /// effectively debug images that may be damaged and which have excess info /// before or after the image. The latter will be the case when dumping the /// image as a memory block without knowing where the image starts and ends. /// /// @{ #define SBE_XIP_IGNORE_FILE_SIZE (uint32_t)0x00000001 #define SBE_XIP_IGNORE_ALL (uint32_t)0x80000000 /// @} #ifndef __ASSEMBLER__ /// Applications can expand this macro to create an array of section names. #define SBE_XIP_SECTION_NAMES(var) \ const char* var[] = { \ ".header", \ ".fixed", \ ".fixed_toc", \ ".ipl_text", \ ".ipl_data", \ ".text", \ ".data", \ ".toc", \ ".strings", \ ".halt", \ ".pibmem0", \ ".dcrings", \ ".rings", \ ".slw", \ ".fit", \ ".ffdc", \ } /// Applications can use this macro to safely index the array of section /// names. #define SBE_XIP_SECTION_NAME(var, n) \ ((((n) < 0) || ((n) > (int)(sizeof(var) / sizeof(char*)))) ? \ "Bug : Invalid SBE-XIP section name" : var[n]) #endif /* __ASSEMBLER__ */ /// Maximum section alignment for SBE-XIP sections #define SBE_XIP_MAX_SECTION_ALIGNMENT 128 /// \defgroup sbe_xip_toc_types SBE-XIP Table of Contents data types /// /// These are the data types stored in the \a iv_type field of the SbeXipToc /// objects. These must be defined as manifest constants because they are /// required to be recognized as manifest constants in C (as opposed to C++) /// code. /// /// NB: The 0x0 code is purposefully left undefined to catch bugs. /// /// @{ /// Data is a single unsigned byte #define SBE_XIP_UINT8 0x01 /// Data is a 32-bit unsigned integer #define SBE_XIP_UINT32 0x02 /// Data is a 64-bit unsigned integer #define SBE_XIP_UINT64 0x03 /// Data is a 0-byte terminated ASCII string #define SBE_XIP_STRING 0x04 /// Data is an address #define SBE_XIP_ADDRESS 0x05 /// The maximum type number #define SBE_XIP_MAX_TYPE_INDEX 0x05 /// Applications can expand this macro to get access to string forms of the /// SBE-XIP data types if desired. #define SBE_XIP_TYPE_STRINGS(var) \ const char* var[] = { \ "Illegal 0 Code", \ "SBE_XIP_UINT8", \ "SBE_XIP_UINT32", \ "SBE_XIP_UINT64", \ "SBE_XIP_STRING", \ "SBE_XIP_ADDRESS", \ } /// Applications can expand this macro to get access to abbreviated string /// forms of the SBE-XIP data types if desired. #define SBE_XIP_TYPE_ABBREVS(var) \ const char* var[] = { \ "Illegal 0 Code", \ "u8 ", \ "u32", \ "u64", \ "str", \ "adr", \ } /// Applications can use this macro to safely index either array of SBE-XIP /// type strings. #define SBE_XIP_TYPE_STRING(var, n) \ (((n) > (sizeof(var) / sizeof(char*))) ? \ "Invalid SBE-XIP type specification" : var[n]) /// @} /// Final alignment constraint for SBE-XIP images. /// /// PORE images are required to be multiples of 8 bytes in length, to /// gaurantee that the PoreVe will be able to complete any 8-byte load/store. #define SBE_XIP_FINAL_ALIGNMENT 8 //////////////////////////////////////////////////////////////////////////// // C Definitions //////////////////////////////////////////////////////////////////////////// #ifndef __ASSEMBLER__ #include #ifdef __cplusplus extern "C" { #endif #if 0 } /* So __cplusplus doesn't mess w/auto-indent */ #endif /// SBE-XIP Section information /// /// This structure defines the data layout of section table entries in the /// SBE-XIP image header. // -*- DO NOT REORDER OR EDIT THIS STRUCTURE DEFINITION WITHOUT ALSO -*- // -*- EDITING THE ASSEMBLER LAYOUT IN sbe_xip_header.H -*- typedef struct { /// The offset (in bytes) of the section from the beginning of the image /// /// In normalized images the section offset will always be 0 if the /// section size is also 0. uint32_t iv_offset; /// The size of the section in bytes, exclusive of alignment padding /// /// This is the size of the program-significant data in the section, /// exclusive of any alignment padding or reserved or extra space. The /// alignment padding (reserved space) is not represented explicitly, but /// is only implied by the offset of any subsequent non-empty section, or /// in the case of the final section in the image, the image size. /// /// Regardless of the \a iv_offset, if the \a iv_size of a section is 0 it /// should be considered "not present" in the image. In normalized images /// the section offset will always be 0 if the section size is also 0. uint32_t iv_size; /// The required initial alignment for the section offset /// /// The PORE and the applications using SBE-XIP images have strict /// alignment/padding requirements. The PORE does not handle any type of /// unaligned instruction or data fetches. Some sections and subsections /// must also be POWER cache-line aligned. The \a iv_alignment applies to /// the first byte of the section. PORE images are also required to be /// multiples of 8 bytes in length, to gaurantee that the PoreVe will be /// able to complete any 8-byte load/store. These constraints are checked /// by sbe_xip_validate() and enforced by sbe_xip_append(). The alignment /// constraints may force a section to be padded, which may create "holes" /// in the image as explained in the comments for the \a iv_size field. /// /// Note that alignment constraints are always checked relative to the /// first byte of the image for in-memory images, not relative to the host /// address. Alignment specifications are required to be a power-of-2. uint8_t iv_alignment; /// Reserved structure alignment padding; Pad to 12 bytes uint8_t iv_reserved8[3]; } SbeXipSection; /// The SbeXipSection structure is created by assembler code and is expected /// to have the same size in C code. This constraint is checked in /// sbe_xip_validate(). #define SIZE_OF_SBE_XIP_SECTION 12 /// SBE-XIP binary image header /// /// This header occupies the initial bytes of an SBE-XIP binary image. /// The header contents are documented here, however the structure is actually /// defined in the file sbe_xip_header.S, and these two definitions must be /// kept consistent. /// /// The header is a fixed-format representation of the most critical /// information about the image. The large majority of information about the /// image and its contents are available through the searchable table of /// contents. PORE code itself normally accesses the data directly through /// global symbols. /// /// The header only contains information 1) required by OTPROM code (e.g., the /// entry point); 2) required by search and updating APIs (e.g., the /// locations and sizes of all of the sections.); a few pieces of critical /// meta-data (e.g., information about the image build process). /// /// Any entries that are accessed by PORE code are required to be 64 bits, and /// will appear at the beginning of the header. /// /// The header also contains bytewise offsets and sizes of all of the sections /// that are assembled to complete the image. The offsets are relative to the /// start of the image (where the header is loaded). The sizes include any /// padding inserted by the link editor to guarantee section alignment. /// /// Every field of the header is also accesssible through the searchable table /// of contents as documented in sbe_xip_header.S. // -*- DO NOT REORDER OR EDIT THIS STRUCTURE DEFINITION WITHOUT ALSO -*- // -*- EDITING THE ASSEMBLER LAYOUT IN sbe_xip_header.S, AND WITHOUT -*- // -*- UPDATING THE sbe_xip_translate_header() API IN sbe_xip_image.c. -*- typedef struct { ////////////////////////////////////////////////////////////////////// // Identification - 8-byte aligned; 8 entries ////////////////////////////////////////////////////////////////////// /// Contains SBE_XIP_MAGIC to identify an SBE-XIP image uint64_t iv_magic; /// The offset of the SBE-XIP entry point from the start of the image uint64_t iv_entryOffset; /// The base address used to link the image, as a full relocatable PORE /// address uint64_t iv_linkAddress; /// Reserved for future expansion uint64_t iv_reserved64[5]; ////////////////////////////////////////////////////////////////////// // Section Table - 4-byte aligned; 16 entries ////////////////////////////////////////////////////////////////////// SbeXipSection iv_section[SBE_XIP_SECTIONS]; ////////////////////////////////////////////////////////////////////// // Other information - 4-byte aligned; 8 entries ////////////////////////////////////////////////////////////////////// /// The size of the image (including padding) in bytes uint32_t iv_imageSize; /// Build date generated by `date +%Y%m%d`, e.g., 20110630 uint32_t iv_buildDate; /// Build time generated by `date +%H%M`, e.g., 0756 uint32_t iv_buildTime; /// Reserved for future expansion uint32_t iv_reserved32[5]; ////////////////////////////////////////////////////////////////////// // Other Information - 1-byte aligned; 8 entries ////////////////////////////////////////////////////////////////////// /// Header format version number uint8_t iv_headerVersion; /// Indicates whether the image has been normalized (0/1) uint8_t iv_normalized; /// Indicates whether the TOC has been sorted to speed searching (0/1) uint8_t iv_tocSorted; /// Reserved for future expansion uint8_t iv_reserved8[5]; ////////////////////////////////////////////////////////////////////// // Strings; 64 characters allocated ////////////////////////////////////////////////////////////////////// /// Build user, generated by `id -un` char iv_buildUser[16]; /// Build host, generated by `hostname` char iv_buildHost[24]; /// Reserved for future expansion char iv_reservedChar[24]; } SbeXipHeader; /// A C-structure form of the SBE-XIP Table of Contents (TOC) entries /// /// The .toc section consists entirely of an array of these structures. /// TOC entries are never accessed by PORE code. /// /// These structures store indexing information for global data required to be /// manipulated by external tools. The actual data is usually allocated in a /// data section and manipulated by the SBE code using global or local symbol /// names. Each TOC entry contains a pointer to a keyword string naming the /// data, the address of the data (or the data itself), the data type, /// meta-information about the data, and for vectors the vector size. // -*- DO NOT REORDER OR EDIT THIS STRUCTURE DEFINITION WITHOUT ALSO -*- // -*- EDITING THE ASSEMBLER MACROS (BELOW) THAT CREATE THE TABLE OF -*- // -*- CONTENTS ENTRIES. -*- typedef struct { /// A pointer to a 0-byte terminated ASCII string identifying the data. /// /// When allocated by the .xip_toc macro this is a pointer to the string /// form of the symbol name for the global or local symbol associated with /// the data which is allocated in the .strings section. This pointer is /// not aligned. /// /// When the image is normalized this pointer is replaced by the offset of /// the string in the .strings section. uint32_t iv_id; /// A 32-bit pointer locating the data /// /// This field is initially populated by the link editor. For scalar, /// vector and string types this is the final relocated address of the /// first byte of the data. For address types, this is the relocated /// address. When the image is normalized, these addresses are converted /// into the equivalent offsets from the beginning of the section holding /// the data. uint32_t iv_data; /// The type of the data; See \ref sbe_xip_toc_types. uint8_t iv_type; /// The section containing the data; See \ref sbe_xip_sections. uint8_t iv_section; /// The number of elements for vector types, otherwise 1 for scalar types /// and addresses. /// /// Vectors are naturally limited in size, e.g. to the number of cores, /// chips in a node, DD-levels etc. If \a iv_elements is 0 then no bounds /// checking is done on get/set accesses of the data. uint8_t iv_elements; /// Structure alignment padding; Pad to 12 bytes uint8_t iv_pad; } SbeXipToc; /// The SbeXipToc structure is created by assembler code and is expected /// to have the same size in C code. This constraint is checked in /// sbe_xip_validate(). #define SIZE_OF_SBE_XIP_TOC 12 /// A C-structure form of hashed SBE-XIP Table of Contents (TOC) entries /// /// This structure was introduced in order to allow a small TOC for the .fixed /// section to support minimum-sized SEEPROM images in which the global TOC /// and all strings have been stripped out. In this structure the index /// string has been replaced by a 32-bit hash, and there is no longer a record /// of the original data name other then the hash. The section of the data is /// assumed to be .fixed, with a maximum 16-bit offset. /// /// These structures are created when entries are made in the .fixed section. /// They are created empty, then filled in during image normalization. /// /// This structure allows the sbe_xip_get*() and sbe_xip_set*() APIs to work /// even on highly-stripped SEEPROM images. typedef struct { /// A 32-bit hash (FNV-1a) of the Id string. uint32_t iv_hash; /// The offset in bytes from the start of the (implied) section of the data uint16_t iv_offset; /// The type of the data; See \ref sbe_xip_toc_types. uint8_t iv_type; /// The number of elements for vector types, otherwise 1 for scalar types /// and addresses. /// /// Vectors are naturally limited in size, e.g. to the number of cores, /// chips in a node, DD-levels etc. If \a iv_elements is 0 then no bounds /// checking is done on get/set accesses of the data. uint8_t iv_elements; } SbeXipHashedToc; /// The SbeXipHashedToc structure is created by assembler code and is expected /// to have the same size in C code. This constraint is checked in /// sbe_xip_validate(). #define SIZE_OF_SBE_XIP_HASHED_TOC 8 /// A decoded TOC entry for use by applications /// /// This structure is a decoded form of a normalized TOC entry, filled in by /// the sbe_xip_decode_toc() and sbe_xip_find() APIs. This structure is /// always returned with data elements in host-endian format. /// /// In the event that the TOC has been removed from the image, this structure /// will also be returned by sbe_xip_find() with information populated from /// the .fixed_toc section if possible. In this case the field \a iv_partial /// will be set and only the fields \a iv_address, \a iv_imageData, \a iv_type /// and \a iv_elements will be populated (all other fields will be set to 0). /// /// \note Only special-purpose applications will ever need to use this /// structure given that the higher-level APIs sbe_xip_get_*() and /// sbe_xip_set_*() are provided and should be used if possible, especially /// given that the information may be truncated as described above. typedef struct { /// A pointer to the associated TOC entry as it exists in the image /// /// If \a iv_partial is set this field is returned as 0. SbeXipToc* iv_toc; /// The full relocatable PORE address /// /// All relocatable addresses are computed from the \a iv_linkAddress /// stored in the header. For scalar and string data, this is the /// relocatable address of the data. For address-only entries, this is /// the indexed address itself. uint64_t iv_address; /// A host pointer to the first byte of text or data within the image /// /// For scalar or string types this is a host pointer to the first byte of /// the data. For code pointers (addresses) this is host pointer to the /// first byte of code. Note that any use of this field requires the /// caller to handle conversion of the data to host endian-ness if /// required. Only 8-bit and string data can be used directly on all /// hosts. void* iv_imageData; /// The item name /// /// This is a pointer in host memory to a string that names the TOC entry /// requested. This field is set to a pointer to the ID string of the TOC /// entry inside the image. If \a iv_partial is set this field is returned /// as 0. char* iv_id; /// The data type, one of the SBE_XIP_* constants uint8_t iv_type; /// The number of elements in a vector /// /// This field is set from the TOC entry when the TOC entry is /// decoded. This value is stored as 1 for scalar declarations, and may be /// set to 0 for vectors with large or undeclared sizes. Otherwise it is /// used to bounds check indexed accesses. uint8_t iv_elements; /// Is this record only partially populated? /// /// This field is set to 0 normally, and only set to 1 if a lookup is made /// in an image that only has the fixed TOC and the requested Id hashes to /// the fixed TOC. uint8_t iv_partial; } SbeXipItem; /// Prototype entry in the .halt section /// /// The .halt section is generated by the 'reqhalt' macro. This structure /// associates the address of each halt with the string form of the FAPI /// return code associated with the halt. The string form is used because the /// FAPI error return code is not constant. The .halt section is 4-byte /// aligned, and each address/string entry is always padded to a multiple of 4 /// bytes. /// /// In the .halt section the \a iv_string may be any length, thus the size of /// each actual record is variable (although guaranteed to always be a /// multiple of 4 bytes). Although the C compiler might natuarlly align /// instances of this structure on a 64-bit boundary, the APIs that allow /// access to the .halt section assume that the underlying machine can do /// non-aligned loads from a pointer to this structure. typedef struct { /// The 64-bit relocatable address of the halt /// /// This is the address found in the PC (Status Register bits 16:63) when /// the PORE halts. The full 64-bit form is used rather than the simple /// 32-bit offset to support merging SEEPROM and PIBMEM .halt sections in /// the SEEPROM IPL images. uint64_t iv_address; /// A C-prototype for a variable-length 0-terminated ASCII string /// /// This is a prototype only to simplify C programming. The actual string /// may be any length. char iv_string[4]; } SbeXipHalt; /// Validate an SBE-XIP image /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. /// /// \param[in] i_size The putative size of the image /// /// \param[in] i_maskIgnores Array of ignore bits representing which properties /// should not be checked for in sbe_xip_validate2(). /// /// This API should be called first by all applications that manipulate /// SBE-XIP images in host memory. The magic number is validated, and /// the image is checked for consistency of the section table and table of /// contents. The \a iv_imageSize field of the header must also match the /// provided \a i_size parameter. Validation does not modify the image. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_validate(void* i_image, const uint32_t i_size); int sbe_xip_validate2(void* i_image, const uint32_t i_size, const uint32_t i_maskIgnores); /// Normalize the SBE-XIP image /// /// \param[in] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// SBE-XIP images must be normalized before any other APIs are allowed to /// operate on the image. Since normalization modifies the image, an explicit /// call to normalize the image is required. Briefly, normalization modifies /// the TOC entries created by the final link to simplify search, updates, /// modification and relocation of the image. Normalization is explained in /// the written documentation of the SBE-XIP binary format. Normalization does /// not modify the size of the image. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_normalize(void* io_image); /// Return the size of an SBE-XIP image from the image header /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// \param[out] o_size A pointer to a variable returned as the size of the /// image in bytes, as recorded in the image header. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_image_size(void* i_image, uint32_t* o_size); /// Locate a section table entry and translate into host format /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. /// /// \param[in] i_sectionId Identifies the section to be queried. See \ref /// sbe_xip_sections. /// /// \param[out] o_hostSection Updated to contain the section table entry /// translated to host byte order. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_get_section(const void* i_image, const int i_sectionId, SbeXipSection* o_hostSection); /// Endian translation of an SbeXipHeader object /// /// \param[out] o_hostHeader The destination object. /// /// \param[in] i_imageHeader The source object. /// /// Translation of a SbeXipHeader includes translation of all data members /// including traslation of the embedded section table. This translation /// works even if \a o_src == \a o_dest, i.e., in the destructive case. void sbe_xip_translate_header(SbeXipHeader* o_hostHeader, const SbeXipHeader* i_imageHeader); /// Get scalar data from an SBE-XIP image /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item /// requested. /// /// \param[out] o_data A pointer to an 8-byte integer to receive the scalar /// data. Assuming the item is located this variable is assigned by the call. /// In the event of an error the final state of \a o_data is not specified. /// /// This API searches the SBE-XIP Table of Contents (TOC) for the item named /// \a i_id, assigning \a o_data from the image if the item is found and is a /// scalar value. Scalar values include 8- 32- and 64-bit integers and PORE /// addresses. Image data smaller than 64 bits are extracted as unsigned /// types, and it is the caller's responsibility to cast or convert the /// returned data as appropriate. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_get_scalar(void *i_image, const char* i_id, uint64_t* o_data); /// Get an integral element from a vector held in an SBE-XIP image /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item /// requested. /// /// \param[in] i_index The index of the vector element to return. /// /// \param[out] o_data A pointer to an 8-byte integer to receive the /// data. Assuming the item is located this variable is assigned by the call. /// In the event of an error the final state of \a o_data is not specified. /// /// This API searches the SBE-XIP Table of Contents (TOC) for the \a i_index /// element of the item named \a i_id, assigning \a o_data from the image if /// the item is found, is a vector of an integral type, and the \a i_index is /// in bounds. Vector elements smaller than 64 bits are extracted as unsigned /// types, and it is the caller's responsibility to cast or convert the /// returned data as appropriate. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_get_element(void *i_image, const char* i_id, const uint32_t i_index, uint64_t* o_data); /// Get string data from an SBE-XIP image /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item /// requested. /// /// \param[out] o_data A pointer to a character pointer. Assuming the /// item is located this variable is assigned by the call to point to the /// string as it exists in the \a i_image. In the event of an error the final /// state of \a o_data is not specified. /// /// This API searches the SBE-XIP Table of Contents (TOC) for the item named /// \a i_id, assigning \a o_data if the item is found and is a string. It is /// the caller's responsibility to copy the string from the \a i_image memory /// space if necessary. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_get_string(void *i_image, const char* i_id, char** o_data); /// Directly read 64-bit data from the image based on a PORE address /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// \param[in] i_poreAddress A relocatable PORE address contained in the /// image, presumably of an 8-byte data area. The \a i_poreAddress is /// required to be 8-byte aligned, otherwise the SBE_XIP_ALIGNMENT_ERROR code /// is returned. /// /// \param[out] o_data The 64 bit data in host format that was found at \a /// i_poreAddress. /// /// This API is provided for applications that need to manipulate SBE-XIP /// images in terms of their relocatable PORE addresses. The API checks that /// the \a i_poreAddress is properly aligned and contained in the image, then /// reads the contents of \a i_poreAddress into \a o_data, performing /// image-to-host endianess conversion if required. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_read_uint64(const void *i_image, const uint64_t i_poreAddress, uint64_t* o_data); /// Set scalar data in an SBE-XIP image /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. /// The image is assumed to be consistent with the information contained in /// the header regarding the presence of and sizes of all sections. The image /// is also required to have been normalized. /// /// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item /// to be modified. /// /// \param[in] i_data The new scalar data. /// /// This API searches the SBE-XIP Table of Contents (TOC) for the item named /// by \a i_id, updating the image from \a i_data if the item is found, has /// a scalar type and can be modified. For this API the scalar types include /// 8- 32- and 64-bit integers. Although PORE addresses are considered a /// scalar type for sbe_xip_get_scalar(), PORE addresses can not be modified /// by this API. The caller is responsible for ensuring that the \a i_data is /// of the correct size for the underlying data element in the image. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_set_scalar(void* io_image, const char* i_id, const uint64_t i_data); /// Set an integral element in a vector held in an SBE-XIP image /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item /// to be updated. /// /// \param[in] i_index The index of the vector element to update. /// /// \param[out] i_data The new vector element. /// /// This API searches the SBE-XIP Table of Contents (TOC) for the \a i_index /// element of the item named \a i_id, update the image from \a i_data if the /// item is found, is a vector of an integral type, and the \a i_index is in /// bounds. The caller is responsible for ensuring that the \a i_data is of /// the correct size for the underlying data element in the image. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_set_element(void *i_image, const char* i_id, const uint32_t i_index, const uint64_t i_data); /// Set string data in an SBE-XIP image /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item /// to be modified. /// /// \param[in] i_data A pointer to the new string data. /// /// This API searches the SBE-XIP Table of Contents (TOC) for the item named /// \a i_id, which must be a string variable. If found, then the string data /// in the image is overwritten with \a i_data. Strings are held 0-terminated /// in the image, and the SBE-XIP format does not maintain a record of the /// amount of memory allocated for an individual string. If a string is /// overwritten by a shorter string then the 'excess' storage is effectively /// lost. If the length of \a i_data is longer that the current strlen() of /// the string data then \a i_data is silently truncated to the first /// strlen(old_string) characters. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_set_string(void *io_image, const char* i_id, const char* i_data); /// Directly write 64-bit data into the image based on a PORE address /// /// \param[in, out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// \param[in] i_poreAddress A relocatable PORE address contained in the /// image, presumably of an 8-byte data area. The \a i_poreAddress is /// required to be 8-byte aligned, otherwise the SBE_XIP_ALIGNMENT_ERROR code /// is returned. /// /// \param[in] i_data The 64 bit data in host format to be written to \a /// i_poreAddress. /// /// This API is provided for applications that need to manipulate SBE-XIP /// images in terms of their relocatable PORE addresses. The API checks that /// the \a i_poreAddress is properly aligned and contained in the image, then /// updates the contents of \a i_poreAddress with \a i_data, performing /// host-to-image endianess conversion if required. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_write_uint64(void *io_image, const uint64_t i_poreAddress, const uint64_t i_data); /// Map over an SBE-XIP image Table of Contents /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_fn A pointer to a function to call on each TOC entry. The /// function has the prototype: /// /// \code /// int (*i_fn)(void* io_image, /// const SbeXipItem* i_item, /// void* io_arg) /// \endcode /// /// \param[in,out] io_arg The private argument of \a i_fn. /// /// This API iterates over each entry of the TOC, calling \a i_fn with /// pointers to the image, an SbeXipItem* pointer, and a private argument. The /// iteration terminates either when all TOC entries have been mapped, or \a /// i_fn returns a non-zero code. /// /// \retval 0 Success; All TOC entries were mapped, including the case that /// the .toc section is empty. /// /// \retval non-0 May be either one of the SBE-XIP image error codes (see \ref /// sbe_xip_image_errors), or a non-zero code from \a i_fn. Since the standard /// SBE_XIP return codes are > 0, application-defined codes should be < 0. int sbe_xip_map_toc(void* io_image, int (*i_fn)(void* io_image, const SbeXipItem* i_item, void* io_arg), void* io_arg); /// Find an SBE-XIP TOC entry /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_id A 0-byte terminated ASCII string naming the item to be /// searched for. /// /// \param[out] o_item If the search is successful, then the object /// pointed to by \a o_item is filled in with the decoded form of the /// TOC entry for \a i_id. If the API returns a non-0 error code then the /// final state of the storage at \a o_item is undefined. This parameter may /// be suppied as 0, in which case sbe_xip_find() serves as a simple predicate /// on whether an item is indexded in the TOC. /// /// This API searches the TOC of a normalized SBE-XIP image for the item named /// \a i_id, and if found, fills in the structure pointed to by \a /// o_item with a decoded form of the TOC entry. If the item is not found, /// the following two return codes may be considered non-error codes: /// /// - SBE_XIP_ITEM_NOT_FOUND : No TOC record for \a i_id was found. /// /// - SBE_XIP_DATA_NOT_PRESENT : The item appears in the TOC, however the /// section containing the data is no longer present in the image. /// /// If the TOC section has been deleted from the image, then the search is /// restricted to the abbreviated TOC that indexes data in the .fixed section. /// In this case the \a o_item structure is marked with a 1 in the \a /// iv_partial field since the abbreviated TOC can not populate the entire /// SbeXipItem structure. /// /// \note This API should typically only be used as a predicate, not as a way /// to access the image via the returned SbeXipItem structure. To obtain data /// from the image or update data in the image use the sbe_xip_get_*() and /// sbe_xip_set_*() APIs respectively. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_find(void* i_image, const char* i_id, SbeXipItem* o_item); /// Map over an SBE-XIP image .halt section /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// \param[in] i_fn A pointer to a function to call on each entry in .halt. /// The function has the prototype: /// /// \code /// int (*i_fn)(void* io_image, /// const uint64_t i_poreAddress, /// const char* i_rcString, /// void* io_arg) /// /// \endcode /// /// \param[in,out] io_arg The private argument of \a i_fn. /// /// This API iterates over each entry of the .halt section, calling \a i_fn /// with each HALT address, the string form of the return code associated with /// that HALT address, and a private argument. The iteration terminates either /// when all .halt entries have been mapped, or \a i_fn returns a non-zero /// code. The \a i_poreAddddress passed to \a i_fn is the full 48-bit /// relocatable PORE address. /// /// \retval 0 Success, including the case that the image has no .halt section. /// /// \retval non-0 May be either one of the SBE-XIP image error codes (see \ref /// sbe_xip_image_errors), or any non-zero code from \a i_fn. Since the /// standard SBE_XIP return codes are \> 0, application-defined codes should /// be \< 0. int sbe_xip_map_halt(void* io_image, int (*i_fn)(void* io_image, const uint64_t i_poreAddress, const char* i_rcString, void* io_arg), void* io_arg); /// Get the string from of a HALT code from an SBE-XIP image .halt section /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// \param[in] i_poreAddress This is the 48-bit address found in the PC when /// the PORE halts. This address is actually 4 bytes beyond the actual HALT /// instruction, however for simplicity this is the address used to index the /// HALT. /// /// \param[out] o_rcString The caller provides the address of a string-pointer /// variable which is updated with a pointer to the string form of the halt /// code associated with \a i_poreAddress (assuming a successful completion). /// /// \retval 0 Success /// /// \revtal SBE_XIP_ITEM_NOT_FOUND The \a i_poreAddress is not associated /// with a halt code in .halt. /// /// \revtal Other See \ref sbe_xip_image_errors int sbe_xip_get_halt(void* io_image, const uint64_t i_poreAddress, const char** o_rcString); /// Delete a section from an SBE-XIP image in host memory /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_sectionId Identifies the section to be deleted. See \ref /// sbe_xip_sections. /// /// This API effectively deletes a section from an SBE-XIP image held in host /// memory. Unless the requested section \a i_section is already empty, only /// the final (highest address offset) section of the image may be deleted. /// Deleting the final section of the image means that the section size is set /// to 0, and the size of the image recorded in the header is reduced by the /// section size. Any alignment padding of the now-last section is also /// removed. /// /// \note This API does not check for or warn if other sections in the image /// reference the deleted section. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_delete_section(void* io_image, const int i_sectionId); #ifndef PPC_HYP /// Duplicate a section from an SBE-XIP image in host memory /// /// \param[in,out] i_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. /// /// \param[in] i_sectionId Identifies the section to be duplicated. See \ref /// sbe_xip_sections. /// /// \param[out] o_duplicate At exit, points to the newly allocated and /// initialized duplicate of the given section. The caller is responsible for /// free()-ing this memory when no longer required. /// /// \param[out] o_size At exit, contains the size (in bytes) of the duplicated /// section. /// /// This API creates a bytewise duplicate of a non-empty section into newly /// malloc()-ed memory. At exit \a o_duplicate points to the duplicate, and \a /// o_size is set the the size of the duplicated section. The caller is /// responsible for free()-ing the memory when no longer required. The /// pointer at \a o_duplicate is set to NULL (0) and the \a o_size is set to 0 /// in the event of any failure. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_duplicate_section(const void* i_image, const int i_sectionId, void** o_duplicate, uint32_t* o_size); #endif // PPC_HYP /// Append binary data to an SBE-XIP image held in host memory /// /// \param[in,out] io_image A pointer to an SBE-XIP image in host memory. The /// image is assumed to be consistent with the information contained in the /// header regarding the presence of and sizes of all sections. The image is /// also required to have been normalized. /// /// \param[in] i_sectionId Identifies the section to contain the new data. /// /// \param[in] i_data A pointer to the data to be appended to the image. If /// this pointer is NULL (0), then the effect is as if \a i_data were a /// pointer to an \a i_size array of 0 bytes. /// /// \param[in] i_size The size of the data to be appended in bytes. If \a /// i_data is 0, then this is the number of bytes to clear. /// /// \param[in] i_allocation The size of the memory region containing the /// image, measured from the first byte of the image. The call will fail if /// appending the new data plus any alignment padding would overflow the /// allocated memory. /// /// \param[out] o_sectionOffset If non-0 at entry, then the API updates the /// location pointed to by \a o_sectionOffset with the offset of the first /// byte of the appended data within the indicated section. This return value /// is invalid in the event of a non-0 return code. /// /// This API copies data from \a i_data to the end of the indicated \a /// i_section. The section \a i_section must either be empty, or must be the /// final (highest address) section in the image. If the section is initially /// empty and \a i_size is non-0 then the section is created at the end of the /// image. The size of \a i_section and the size of the image are always /// adjusted to reflect the newly added data. This is a simple binary copy /// without any interpretation (e.g., endian-translation) of the copied data. /// The caller is responsible for insuring that the host memory area /// containing the SBE-XIP image is large enough to hold the newly appended /// data without causing addressing errors or buffer overrun errors. /// /// The final parameter \a o_sectionOffset is optional, and may be passed as /// NULL (0) if the application does not require the information. This return /// value is provided to simplify typical use cases of this API: /// /// - A scan program is appended to the image, or a run-time data area is /// allocated and cleared at the end of the image. /// /// - Pointer variables in the image are updated with PORE addresses obtained /// via sbe_xip_section2pore(), or /// other procedure code initializes a newly allocated and cleared data area /// via host addresses obtained from sbe_xip_section2host(). /// /// Regarding alignment, note that the SBE-XIP format requires that sections /// maintain an initial alignment that varies by section, and the API will /// enforce these alignment constraints for all sections created by the API. /// All alignment is relative to the first byte of the image (\a io_image) - /// \e not to the current in-memory address of the image. By specification /// SBE-XIP images must be loaded at a 4K alignment in order for PORE hardware /// relocation to work, however the APIs don't require this 4K alignment for /// in-memory manipulation of images. Images to be executed on PoreVe will /// normally require at least 8-byte final aligment in order to guarantee that /// the PoreVe can execute an 8-byte fetch or load/store of the final /// doubleword. /// /// \note If the TOC section is modified then the image is marked as having an /// unsorted TOC. /// /// \note If the call fails for any reason (other than a bug in the API /// itself) then the \a io_image data is returned unmodified. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_append(void* io_image, const int i_sectionId, const void* i_data, const uint32_t i_size, const uint32_t i_allocation, uint32_t* o_sectionOffset); /// Convert an SBE-XIP section offset to a relocatable PORE address /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory /// /// \param[in] i_sectionId A valid SBE-XIP section identifier; The section /// must be non-empty. /// /// \param[in] i_offset An offset (in bytes) within the section. At least one /// byte at \a i_offset must be currently allocated in the section. /// /// \param[in] o_poreAddress The equivalent relocatable PORE address is /// returned via this pointer. Since valid PORE addresses are always either /// 4-byte (code) or 8-byte (data) aligned, this API checks the aligment of /// the translated address and returns SBE_XIP_ALIGNMENT_ERROR if the PORE /// address is not at least 4-byte aligned. Note that the translated address /// is still returned even if incorrectly aligned. /// /// This API is typically used to translate section offsets returned from /// sbe_xip_append() into relocatable PORE addresses. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_section2pore(const void* i_image, const int i_sectionId, const uint32_t i_offset, uint64_t* o_poreAddress); /// Convert an SBE-XIP relocatable PORE address to a host memory address /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. /// /// \param[in] i_poreAddress A relocatable PORE address putatively addressing /// relocatable memory contained in the image. /// /// \param[out] o_hostAddress The API updates the location pointed to by \a /// o_hostAddress with the host address of the memory addressed by \a /// i_poreAddress. In the event of an error (non-0 return code) the final /// content of \a o_hostAddress is undefined. /// /// This API is typically used to translate relocatable PORE addresses stored /// in the SBE-XIP image into the equivalent host address of the in-memory /// image, allowing host-code to manipulate arbitrary data structures in the /// image. If the \a i_poreAddress does not refer to memory within the image /// (as determined by the link address and image size) then the /// SBE_XIP_INVALID_ARGUMENT error code is returned. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_pore2host(const void* i_image, const uint64_t i_poreAddress, void** o_hostAddress); /// Convert an SBE-XIP relocatable PORE address to section Id and offset /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory. /// /// \param[in] i_poreAddress A relocatable PORE address putatively addressing /// relocatable memory contained in the image. /// /// \param[out] o_section The API updates the location pointed to by \a /// o_section with the section Id of the memory addressed by \a /// i_poreAddress. In the event of an error (non-0 return code) the final /// content of \a o_section is undefined. /// /// \param[out] o_offset The API updates the location pointed to by \a /// o_offset with the byte offset of the memory addressed by \a i_poreAddress /// within \a o_section. In the event of an error (non-0 return code) the /// final content of \a o_offset is undefined. /// /// This API is typically used to translate relocatable PORE addresses stored /// in the SBE-XIP image into the equivalent section + offset form, allowing /// host-code to manipulate arbitrary data structures in the image. If the \a /// i_poreAddress does not refer to memory within the image (as determined by /// the link address and image size) then the SBE_XIP_INVALID_ARGUMENT error /// code is returned. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_pore2section(const void* i_image, const uint64_t i_poreAddress, int* o_section, uint32_t* o_offset); /// Convert an in-memory SBE-XIP host address to a relocatable PORE address /// /// \param[in] i_image A pointer to an SBE-XIP image in host memory /// /// \param[in] i_hostAddress A host address addressing data within the image. /// /// \param[out] o_poreAddress The API updates the location pointed to by \a /// o_poreAddress with the equivelent relocatable PORE address of the memory /// addressed by i_hostAddress. Since valid PORE addresses are always either /// 4-byte (code) or 8-byte (data) aligned, this API checks the aligment of /// the translated address and returns SBE_XIP_ALIGNMENT_ERROR if the PORE /// address is not at least 4-byte aligned. Note that the translated address /// is still returned evn if incorrectly aligned. /// /// This API is provided as a convenient way to convert host memory addresses /// for an in-memory SBE-XIP image into PORE addresses correctly relocated for /// the image, for example to update pointer variables in the image. If the /// \a i_hostAddress does not refer to memory within the image (as determined /// by the image address and image size) then the SBE_XIP_INVALID_ARGUMENT /// error code is returned. /// /// \retval 0 Success /// /// \retval non-0 See \ref sbe_xip_image_errors int sbe_xip_host2pore(const void* i_image, void* i_hostAddress, uint64_t* o_poreAddress); /// \defgroup sbe_xip_image_errors Error codes from SBE-XIP image APIs /// /// @{ /// A putative SBE-XIP image does not have the correct magic number, or /// contains some other major inconsistency. #define SBE_XIP_IMAGE_ERROR 1 /// The TOC may be missing, partially present or may have an alignment problem. #define SBE_XIP_TOC_ERROR 2 /// A named item was not found in the SBE-XIP TOC, or a putative HALT address /// is not associated with a halt code in .halt. #define SBE_XIP_ITEM_NOT_FOUND 3 /// A named item appears in the SBE-XIP TOC, but the data is not present in /// the image. This error can occur if sections have been deleted from the /// image. #define SBE_XIP_DATA_NOT_PRESENT 4 /// A named item appears in the SBE-XIP TOC, but the data can not be /// modified. This error will occur if an attempt is made to modify an /// address-only entry. #define SBE_XIP_CANT_MODIFY 5 /// A direct or implied argument is invalid, e.g. an illegal data type or /// section identifier, or an address not contained within the image. #define SBE_XIP_INVALID_ARGUMENT 6 /// A data type mismatch or an illegal type was specified or implied for an /// operation. #define SBE_XIP_TYPE_ERROR 7 /// A bug in an SBE-XIP image API #define SBE_XIP_BUG 8 /// The image must first be normalized with sbe_xip_normalize(). #define SBE_XIP_NOT_NORMALIZED 9 /// Attempt to delete a non-empty section that is not the final section of the /// image, or an attempt to append data to a non-empty section that is not the /// final section of the image, or an attempt to operate on an empty section /// for those APIs that prohibit this. #define SBE_XIP_SECTION_ERROR 10 /// An address translation API returned a PORE address that was not at least /// 4-byte aligned, or alignment violations were observed by /// sbe_xip_validate() or sbe_xip_append(). #define SBE_XIP_ALIGNMENT_ERROR 11 /// An API that performs dynamic memory allocation was unable to allocate /// memory. #define SBE_XIP_NO_MEMORY 12 /// Attempt to get or set a vector element with an index that is outside of /// the declared bounds of the vector. #define SBE_XIP_BOUNDS_ERROR 13 /// Attempt to grow the image past its defined memory allocation #define SBE_XIP_WOULD_OVERFLOW 14 /// Error associated with the disassembler occured. #define SBE_XIP_DISASSEMBLER_ERROR 15 /// hash collision creating the .fixed_toc section #define SBE_XIP_HASH_COLLISION 16 /// Applications can expand this macro to declare an array of string forms of /// the error codes if desired. #define SBE_XIP_ERROR_STRINGS(var) \ const char* var[] = { \ "Success", \ "SBE_XIP_IMAGE_ERROR", \ "SBE_XIP_TOC_ERROR", \ "SBE_XIP_ITEM_NOT_FOUND", \ "SBE_XIP_DATA_NOT_PRESENT", \ "SBE_XIP_CANT_MODIFY", \ "SBE_XIP_INVALID_ARGUMENT", \ "SBE_XIP_TYPE_ERROR", \ "SBE_XIP_BUG", \ "SBE_XIP_NOT_NORMALIZED", \ "SBE_XIP_SECTION_ERROR", \ "SBE_XIP_ALIGNMENT_ERROR", \ "SBE_XIP_NO_MEMORY", \ "SBE_XIP_BOUNDS_ERROR", \ "SBE_XIP_WOULD_OVERFLOW", \ "SBE_XIP_DISASSEMBLER_ERROR", \ "SBE_XIP_HASH_COLLISION", \ } /// Applications can use this macro to safely index the array of error /// strings. #define SBE_XIP_ERROR_STRING(var, n) \ ((((n) < 0) || ((n) > (int)(sizeof(var) / sizeof(char*)))) ? \ "Bug : Invalid SBE-XIP error code" : var[n]) /// @} /// Disassembler error codes. #define DIS_IMAGE_ERROR 1 #define DIS_MEMORY_ERROR 2 #define DIS_DISASM_ERROR 3 #define DIS_RING_NAME_ADDR_MATCH_SUCCESS 4 #define DIS_RING_NAME_ADDR_MATCH_FAILURE 5 #define DIS_TOO_MANY_DISASM_WARNINGS 6 #define DIS_DISASM_TROUBLES 7 #define DIS_ERROR_STRINGS(var) \ const char* var[] = { \ "Success", \ "DIS_IMAGE_ERROR", \ "DIS_MEMORY_ERROR", \ "DIS_DISASM_ERROR", \ "DIS_RING_NAME_ADDR_MATCH_SUCCESS", \ "DIS_RING_NAME_ADDR_MATCH_FAILURE", \ "DIS_TOO_MANY_DISASM_WARNINGS", \ "DIS_DISASM_TROUBLES", \ } #define DIS_ERROR_STRING(var, n) \ ((((n) < 0) || ((n) > (int)(sizeof(var) / sizeof(char*)))) ? \ "Bug : Invalid DIS error code" : var[n]) #if 0 { /* So __cplusplus doesn't mess w/auto-indent */ #endif #ifdef __cplusplus } #endif #endif // __ASSEMBLER__ //////////////////////////////////////////////////////////////////////////// // Assembler Definitions //////////////////////////////////////////////////////////////////////////// #ifdef __ASSEMBLER__ /// Create an XIP TOC entry /// /// \param[in] index The string form of the \a index symbol is created and /// linked from the TOC entry to allow external search procedures to locate /// the \a address. /// /// \param[in] type One of the SBE_XIP_* type constants; See \ref /// sbe_xip_toc_types. /// /// \param[in] address The address of the idexed code or data; This wlll /// typically be a symbol. /// /// \param[in] elements For vector types, number of elements in the /// vector, which is limited to an 8-bit unsigned integer. This parameter /// defaults to 1 which indicates a scalar type. Declaring a vector with 0 /// elements disables bounds checking on vector accesses, and can be used if /// very large or indeterminate sized vectors are required. The TOC format /// does not support vectors of strings or addresses. /// /// The \c .xip_toc macro creates a XIP Table of Contents (TOC) structure in /// the \c .toc section, as specified by the parameters. This macro is /// typically not used directly in assembly code. Instead programmers should /// use .xip_quad, .xip_quada, .xip_quadia, .xip_address, .xip_string or /// .xip_cvs_revision. .macro .xip_toc, index:req, type:req, address:req, elements=1 .if (((\type) < 1) || ((\type) > SBE_XIP_MAX_TYPE_INDEX)) .error ".xip_toc : Illegal type index" .endif // First push into the .strings section to lay down the // string form of the index name under a local label. .pushsection .strings 7667862: .asciz "\index" .popsection // Now the 12-byte TOC entry is created. Push into the .toc section // and lay down the first 4 bytes which are always a pointer to the // string just declared. The next 4 bytes are the address of the data // (or the address itself in the case of address types). The final 4 // bytes are the type, section (always 0 prior to normalization), // number of elements, and a padding byte. .pushsection .toc .long 7667862b, (\address) .byte (\type), 0, (\elements), 0 .popsection .endm /// Allocate and initialize 64-bit global scalar or vector data and create the /// TOC entry. /// /// \param[in] symbol The name of the scalar or vector; this name is also used /// as the TOC index of the data. /// /// \param[in] init The initial value of (each element of) the data. /// This is a 64-bit integer; To allocate address pointers use .xip_quada. /// /// \param[in] elements The number of 64-bit elements in the data structure, /// defaulting to 1, with a maximum value of 255. /// /// \param[in] section The section where the data will be allocated, /// default depends on the memory space .macro .xip_quad, symbol:req, init:req, elements=1, section ..xip_quad_helper .quad, \symbol, (\init), (\elements), \section .endm /// Allocate and initialize 64-bit global scalar or vector data containing a /// relocatable address in and create the TOC entry. /// /// \param[in] symbol The name of the scalar or vector; this name is also used /// as the TOC index of the data. /// /// \param[in] init The initial value of (each element of) the data. This /// will typically be a symbolic address. If the intention is to define an /// address that will always be filled in later by image manipulation tools, /// then use the .xip_quad macro with a 0 initial value. /// /// \param[in] elements The number of 64-bit elements in the data structure, /// defaulting to 1, with a maximum value of 255. /// /// \param[in] section The section where the data will be allocated, /// default depends on the memory space .macro .xip_quada, symbol:req, offset:req, elements=1, section ..xip_quad_helper .quada, \symbol, (\offset), (\elements), \section .endm /// Helper for .xip_quad and .xip_quada .macro ..xip_quad_helper, directive, symbol, init, elements, section .if (((\elements) < 1) || ((\elements) > 255)) .error "The number of vector elements must be in the range 1..255" .endif ..xip_pushsection \section .balign 8 .global \symbol \symbol\(): .rept (\elements) \directive (\init) .endr .popsection .xip_toc \symbol, SBE_XIP_UINT64, \symbol, (\elements) .endm /// Allocate and initialize 64-bit global scalar or vector data containing /// full 64-bit addresses and create a TOC entry /// /// \param[in] symbol The name of the scalar or vector; this name is also used /// as the TOC index of the data. /// /// \param[in] space A valid PORE memory space descriptor /// /// \param[in] offset A 32-bit relocatable offset /// /// \param[in] elements The number of 64-bit elements in the data structure, /// defaulting to 1, with a maximum value of 255. /// /// \param[in] section The section where the data will be allocated, /// default depends on the memory space .macro .xip_quadia, symbol:req, space:req, offset:req, \ elements=1, section .if (((\elements) < 1) || ((\elements) > 255)) .error "The number of vector elements must be in the range 1..255" .endif ..xip_pushsection \section .balign 8 .global \symbol \symbol\(): .rept (\elements) .quadia (\space), (\offset) .endr .popsection .xip_toc \symbol, SBE_XIP_UINT64, \symbol, (\elements) .endm /// Default push into .ipl_data unless in an OCI space, then .data .macro ..xip_pushsection, section .ifnb \section .pushsection \section .else .if (_PGAS_DEFAULT_SPACE == PORE_SPACE_OCI) .pushsection .data .else .pushsection .ipl_data .endif .endif .balign 8 .endm /// Allocate and initialize a string in .strings /// /// \param[in] index The string will be stored in the TOC using this index /// symbol. /// /// \param[in] string The string to be allocated in .strings. String space is /// fixed once allocated. Strings designed to be overwritten by external tools /// should be allocated to be as long as eventually needed (e.g., by a string /// of blanks.) .macro .xip_string, index:req, string:req .pushsection .strings 7874647: .asciz "\string" .popsection .xip_toc \index, SBE_XIP_STRING, 7874647b .endm /// Allocate and initialize a CVS Revison string in .strings /// /// \param[in] index The string will be stored in the TOC using this index /// symbol. /// /// \param[in] string A CVS revision string to be allocated in .strings. CVS /// revision strings are formatted by stripping out and only storing the /// actual revision number : /// /// \code /// "$Revision . $" -> "." /// \endcode .macro .xip_cvs_revision, index:req, string:req .pushsection .strings 7874647: ..cvs_revision_string "\string" .popsection .xip_toc \index, SBE_XIP_STRING, 7874647b .endm /// Shorthand to create a TOC entry for an address /// /// \param[in] index The symbol will be indexed as this name /// /// \param[in] symbol The symbol to index; by default the same as /// the index. .macro .xip_address, index:req, symbol .ifb \symbol .xip_toc \index, SBE_XIP_ADDRESS, \index .else .xip_toc \index, SBE_XIP_ADDRESS, \symbol .endif .endm /// Edit and allocate a CVS revision string /// /// CVS revision strings are formatted by stripping out and only storing the /// actual revision number : /// \code /// "$Revision . $" -> "." /// \endcode .macro ..cvs_revision_string, rev:req .irpc c, \rev .ifnc "\c", "$" .ifnc "\c", "R" .ifnc "\c", "e" .ifnc "\c", "v" .ifnc "\c", "i" .ifnc "\c", "s" .ifnc "\c", "i" .ifnc "\c", "o" .ifnc "\c", "n" .ifnc "\c", ":" .ifnc "\c", " " .ascii "\c" .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endif .endr .byte 0 .endm #endif // __ASSEMBLER__ #endif // __SBE_XIP_TOC_H skiboot-skiboot-5.1.13/make_offsets.sh000077500000000000000000000002371265204436200177360ustar00rootroot00000000000000#!/bin/sh cat < /dev/null 2>&1; then version=`git describe --exact-match 2>/dev/null` if [ -z "$version" ]; then version=`git describe 2>/dev/null` fi if [ -z "$version" ]; then version=`git rev-parse --verify --short HEAD 2>/dev/null` fi if [ ! -z "$EXTRA_VERSION" ]; then version="$version-$EXTRA_VERSION" fi if git diff-index --name-only HEAD |grep -qv '.git'; then if [ ! -z "$USER" ]; then version="$version-$USER" fi version="$version-dirty" diffsha=`git diff|sha1sum` diffsha=`cut -c-7 <<< "$diffsha"` version="$version-$diffsha" fi if [ $# -eq 1 ]; then version=`echo $version | sed s/skiboot/$1/` fi echo $version else if [ ! -z "$SKIBOOT_VERSION" ]; then echo $SKIBOOT_VERSION else if [ ! -z "`cat .version`" ]; then cat .version else exit 1; fi fi fi skiboot-skiboot-5.1.13/opal-ci/000077500000000000000000000000001265204436200162535ustar00rootroot00000000000000skiboot-skiboot-5.1.13/opal-ci/Makefile000066400000000000000000000062771265204436200177270ustar00rootroot00000000000000 OP_BUILD_GIT?=git@github.com:open-power/op-build.git BUILDROOT_GIT?=https://github.com/open-power/buildroot DL_CACHE?=~/op-build/dl SCRATCH?=/scratch/stewart/op-build/ all: op-build-images op-build-images: build-op-build-v1.0 build-op-build-v1.1 build-op-build-v1.2 build-op-build-v1.2.1 op-build: git clone ${OP_BUILD_GIT} op-build (cd op-build && git submodule init) (cd op-build && git config submodule.buildroot.url ${BUILDROOT_GIT}) (cd op-build && git submodule update --reference ${OP_BUILD_GIT}) op-build-v1.0: op-build git clone -s op-build op-build-v1.0 (cd op-build && git submodule init) (cd op-build-v1.0 && git config submodule.buildroot.url ${BUILDROOT_GIT}) (cd op-build-v1.0 && git checkout v1.0) (cd op-build-v1.0 && git submodule update --recursive) (cd op-build-v1.0 && cp -rl ${DL_CACHE}/* dl/) rm -rf ${SCRATCH}/op-build-v1.0 && mkdir ${SCRATCH}/op-build-v1.0 (rm -rf op-build-v1.0/output && ln -s ${SCRATCH}/op-build-v1.0 op-build-v1.0/output) op-build-v1.1: op-build git clone -s op-build op-build-v1.1 (cd op-build && git submodule init) (cd op-build-v1.1 && git config submodule.buildroot.url ${BUILDROOT_GIT}) (cd op-build-v1.1 && git checkout v1.1) (cd op-build-v1.1 && git submodule update --recursive) (cd op-build-v1.1 && cp -rl ${DL_CACHE}/* dl/) rm -rf ${SCRATCH}/op-build-v1.1 && mkdir ${SCRATCH}/op-build-v1.1 (rm -rf op-build-v1.1/output && ln -s ${SCRATCH}/op-build-v1.1 op-build-v1.1/output) op-build-v1.2: op-build git clone -s op-build op-build-v1.2 (cd op-build && git submodule init) (cd op-build-v1.2 && git config submodule.buildroot.url ${BUILDROOT_GIT}) (cd op-build-v1.2 && git checkout v1.2) (cd op-build-v1.2 && git submodule update --recursive) (cd op-build-v1.2 && cp -rl ${DL_CACHE}/* dl/) rm -rf ${SCRATCH}/op-build-v1.2 && mkdir ${SCRATCH}/op-build-v1.2 (rm -rf op-build-v1.2/output && ln -s ${SCRATCH}/op-build-v1.2 op-build-v1.2/output) op-build-v1.2.1: op-build git clone -s op-build op-build-v1.2.1 (cd op-build && git submodule init) (cd op-build-v1.2.1 && git config submodule.buildroot.url ${BUILDROOT_GIT}) (cd op-build-v1.2.1 && git checkout v1.2.1) (cd op-build-v1.2.1 && git submodule update --recursive) (cd op-build-v1.2.1 && cp -rl ${DL_CACHE}/* dl/) rm -rf ${SCRATCH}/op-build-v1.2.1 && mkdir ${SCRATCH}/op-build-v1.2.1 (rm -rf op-build-v1.2.1/output && ln -s ${SCRATCH}/op-build-v1.2.1 op-build-v1.2.1/output) build-op-build-v1.0: op-build-v1.0 cd op-build-v1.0 && ../run-op-build-mambo.sh build-op-build-v1.1: op-build-v1.1 cd op-build-v1.1 && ../run-op-build-mambo.sh build-op-build-v1.2: op-build-v1.2 cd op-build-v1.2 && ../run-op-build-mambo.sh build-op-build-v1.2.1: op-build-v1.2.1 cd op-build-v1.2.1 && ../run-op-build-mambo.sh images/op-build-v1.0: images-dir cp -r op-build-v1.0/output/images images/op-build-v1.0 images/op-build-v1.1: images-dir cp -r op-build-v1.1/output/images images/op-build-v1.1 images/op-build-v1.2: images-dir cp -r op-build-v1.2/output/images images/op-build-v1.2 images/op-build-v1.2.1: images-dir cp -r op-build-v1.2.1/output/images images/op-build-v1.2.1 images-dir: mkdir images; .PHONY: images images: images/op-build-v1.0 images/op-build-v1.1 images/op-build-v1.2 images/op-build-v1.2.1 skiboot-skiboot-5.1.13/opal-ci/README000066400000000000000000000015131265204436200171330ustar00rootroot00000000000000OPAL-CI ------- Magic scripts for doing CI regression testing. Currently the makefile targets building op-build firmware (targeted at Mambo simulator) for all current op-build releases. Since skiboot maintains compatibility, these binaries should *ALWAYS* boot and work. The provided makefile to recreate the various zImage.epapr images should be a good starting point. I run it like this: cd opal-ci export BUILDROOT_GIT=~/op-build/.git/modules/buildroot/ export OP_BUILD_GIT=~/op-build/ export DL_CACHE=~/op-build/dl export SCRATCH=/scratch/stewart/op-build/ make -j2 images DL_CACHE points to an existing op-build tree, so we don't have to download all the source tarballs again SCRATCH is where all compilation will happen, must have >20GB free space BUILDROOT_GIT and OP_BUILD_GIT exist to prevent you having to clone from github.skiboot-skiboot-5.1.13/platforms/000077500000000000000000000000001265204436200167365ustar00rootroot00000000000000skiboot-skiboot-5.1.13/platforms/Makefile.inc000066400000000000000000000005611265204436200211500ustar00rootroot00000000000000PLATDIR = platforms SUBDIRS += $(PLATDIR) PLATFORMS = $(PLATDIR)/built-in.o include $(SRC)/$(PLATDIR)/ibm-fsp/Makefile.inc include $(SRC)/$(PLATDIR)/rhesus/Makefile.inc include $(SRC)/$(PLATDIR)/astbmc/Makefile.inc include $(SRC)/$(PLATDIR)/mambo/Makefile.inc include $(SRC)/$(PLATDIR)/qemu/Makefile.inc $(PLATFORMS): $(IBM_FSP) $(RHESUS) $(ASTBMC) $(MAMBO) $(QEMU) skiboot-skiboot-5.1.13/platforms/astbmc/000077500000000000000000000000001265204436200202075ustar00rootroot00000000000000skiboot-skiboot-5.1.13/platforms/astbmc/Makefile.inc000066400000000000000000000003101265204436200224110ustar00rootroot00000000000000SUBDIRS += $(PLATDIR)/astbmc ASTBMC_OBJS = palmetto.o habanero.o firestone.o garrison.o pnor.o common.o slots.o ASTBMC = $(PLATDIR)/astbmc/built-in.o $(ASTBMC): $(ASTBMC_OBJS:%=$(PLATDIR)/astbmc/%) skiboot-skiboot-5.1.13/platforms/astbmc/astbmc.h000066400000000000000000000026151265204436200216350ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ASTBMC_H #define __ASTBMC_H #define ST_LOC_PHB(chip_id, phb_idx) ((chip_id) << 16 | (phb_idx)) #define ST_LOC_DEVFN(dev, fn) ((dev) << 3 | (fn)) struct slot_table_entry { enum slot_table_etype { st_end, /* End of list */ st_phb, st_pluggable_slot, st_builtin_dev, } etype; uint32_t location; const char *name; const struct slot_table_entry *children; }; extern void astbmc_early_init(void); extern int64_t astbmc_ipmi_reboot(void); extern int64_t astbmc_ipmi_power_down(uint64_t request); extern void astbmc_init(void); extern void astbmc_ext_irq_serirq_cpld(unsigned int chip_id); extern int pnor_init(void); extern void slot_table_init(const struct slot_table_entry *top_table); extern void slot_table_get_slot_info(struct phb *phb, struct pci_device * pd); #endif /* __ASTBMC_H */ skiboot-skiboot-5.1.13/platforms/astbmc/common.c000066400000000000000000000206701265204436200216500ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "astbmc.h" /* UART1 config */ #define UART_IO_BASE 0x3f8 #define UART_IO_COUNT 8 #define UART_LPC_IRQ 4 /* BT config */ #define BT_IO_BASE 0xe4 #define BT_IO_COUNT 3 #define BT_LPC_IRQ 10 void astbmc_ext_irq_serirq_cpld(unsigned int chip_id) { lpc_all_interrupts(chip_id); } static void astbmc_ipmi_error(struct ipmi_msg *msg) { prlog(PR_DEBUG, "ASTBMC: error sending msg. cc = %02x\n", msg->cc); ipmi_free_msg(msg); } static void astbmc_ipmi_setenables(void) { struct ipmi_msg *msg; struct { uint8_t oem2_en : 1; uint8_t oem1_en : 1; uint8_t oem0_en : 1; uint8_t reserved : 1; uint8_t sel_en : 1; uint8_t msgbuf_en : 1; uint8_t msgbuf_full_int_en : 1; uint8_t rxmsg_queue_int_en : 1; } data; memset(&data, 0, sizeof(data)); /* The spec says we need to read-modify-write to not clobber * the state of the other flags. These are set on by the bmc */ data.rxmsg_queue_int_en = 1; data.sel_en = 1; /* These are the ones we want to set on */ data.msgbuf_en = 1; msg = ipmi_mkmsg_simple(IPMI_SET_ENABLES, &data, sizeof(data)); if (!msg) { prlog(PR_ERR, "ASTBMC: failed to set enables\n"); return; } msg->error = astbmc_ipmi_error; ipmi_queue_msg(msg); } static int astbmc_fru_init(void) { const struct dt_property *prop; struct dt_node *node; uint8_t fru_id; node = dt_find_by_path(dt_root, "bmc"); if (!node) return -1; prop = dt_find_property(node, "firmware-fru-id"); if (!prop) return -1; fru_id = dt_property_get_cell(prop, 0) & 0xff; ipmi_fru_init(fru_id); return 0; } void astbmc_init(void) { /* Initialize PNOR/NVRAM */ pnor_init(); /* Register the BT interface with the IPMI layer */ bt_init(); ipmi_sel_init(); ipmi_wdt_init(); ipmi_rtc_init(); ipmi_opal_init(); astbmc_fru_init(); elog_init(); ipmi_sensor_init(); /* As soon as IPMI is up, inform BMC we are in "S0" */ ipmi_set_power_state(IPMI_PWR_SYS_S0_WORKING, IPMI_PWR_NOCHANGE); /* Enable IPMI OEM message interrupts */ astbmc_ipmi_setenables(); ipmi_set_fw_progress_sensor(IPMI_FW_MOTHERBOARD_INIT); /* Setup UART console for use by Linux via OPAL API */ if (!dummy_console_enabled()) uart_setup_opal_console(); } int64_t astbmc_ipmi_power_down(uint64_t request) { if (request != IPMI_CHASSIS_PWR_DOWN) { prlog(PR_WARNING, "PLAT: unexpected shutdown request %llx\n", request); } return ipmi_chassis_control(request); } int64_t astbmc_ipmi_reboot(void) { return ipmi_chassis_control(IPMI_CHASSIS_HARD_RESET); } static void astbmc_fixup_dt_system_id(void) { /* Make sure we don't already have one */ if (dt_find_property(dt_root, "system-id")) return; dt_add_property_strings(dt_root, "system-id", "unavailable"); } static void astbmc_fixup_dt_bt(struct dt_node *lpc) { struct dt_node *bt; char namebuf[32]; /* First check if the BT interface is already there */ dt_for_each_child(lpc, bt) { if (dt_node_is_compatible(bt, "bt")) return; } snprintf(namebuf, sizeof(namebuf), "ipmi-bt@i%x", BT_IO_BASE); bt = dt_new(lpc, namebuf); dt_add_property_cells(bt, "reg", 1, /* IO space */ BT_IO_BASE, BT_IO_COUNT); dt_add_property_strings(bt, "compatible", "ipmi-bt"); /* Mark it as reserved to avoid Linux trying to claim it */ dt_add_property_strings(bt, "status", "reserved"); dt_add_property_cells(bt, "interrupts", BT_LPC_IRQ); dt_add_property_cells(bt, "interrupt-parent", lpc->phandle); } static void astbmc_fixup_dt_uart(struct dt_node *lpc) { /* * The official OF ISA/LPC binding is a bit odd, it prefixes * the unit address for IO with "i". It uses 2 cells, the first * one indicating IO vs. Memory space (along with bits to * represent aliasing). * * We pickup that binding and add to it "2" as a indication * of FW space. */ struct dt_node *uart; char namebuf[32]; /* First check if the UART is already there */ dt_for_each_child(lpc, uart) { if (dt_node_is_compatible(uart, "ns16550")) return; } /* Otherwise, add a node for it */ snprintf(namebuf, sizeof(namebuf), "serial@i%x", UART_IO_BASE); uart = dt_new(lpc, namebuf); dt_add_property_cells(uart, "reg", 1, /* IO space */ UART_IO_BASE, UART_IO_COUNT); dt_add_property_strings(uart, "compatible", "ns16550", "pnpPNP,501"); dt_add_property_cells(uart, "clock-frequency", 1843200); dt_add_property_cells(uart, "current-speed", 115200); /* * This is needed by Linux for some obscure reasons, * we'll eventually need to sanitize it but in the meantime * let's make sure it's there */ dt_add_property_strings(uart, "device_type", "serial"); /* Add interrupt */ dt_add_property_cells(uart, "interrupts", UART_LPC_IRQ); dt_add_property_cells(uart, "interrupt-parent", lpc->phandle); } static void del_compatible(struct dt_node *node) { struct dt_property *prop; prop = __dt_find_property(node, "compatible"); if (prop) dt_del_property(node, prop); } static void astbmc_fixup_bmc_sensors(void) { struct dt_node *parent, *node; parent = dt_find_by_path(dt_root, "bmc"); if (!parent) return; del_compatible(parent); parent = dt_find_by_name(parent, "sensors"); if (!parent) return; del_compatible(parent); dt_for_each_child(parent, node) { if (dt_find_property(node, "compatible")) continue; dt_add_property_string(node, "compatible", "ibm,ipmi-sensor"); } } static void astbmc_fixup_dt(void) { struct dt_node *n, *primary_lpc = NULL; /* Find the primary LPC bus */ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") { if (!primary_lpc || dt_has_node_property(n, "primary", NULL)) primary_lpc = n; if (dt_has_node_property(n, "#address-cells", NULL)) break; } if (!primary_lpc) return; /* Fixup the UART, that might be missing from HB */ astbmc_fixup_dt_uart(primary_lpc); /* BT is not in HB either */ astbmc_fixup_dt_bt(primary_lpc); /* The pel logging code needs a system-id property to work so make sure we have one. */ astbmc_fixup_dt_system_id(); astbmc_fixup_bmc_sensors(); } static void astbmc_fixup_psi_bar(void) { struct proc_chip *chip = next_chip(NULL); uint64_t psibar; /* Read PSI BAR */ if (xscom_read(chip->id, 0x201090A, &psibar)) { prerror("PLAT: Error reading PSI BAR\n"); return; } /* Already configured, bail out */ if (psibar & 1) return; /* Hard wire ... yuck */ psibar = 0x3fffe80000001; printf("PLAT: Fixing up PSI BAR on chip %d BAR=%llx\n", chip->id, psibar); /* Now write it */ xscom_write(chip->id, 0x201090A, psibar); } void astbmc_early_init(void) { /* Hostboot's device-tree isn't quite right yet */ astbmc_fixup_dt(); /* Hostboot forgets to populate the PSI BAR */ astbmc_fixup_psi_bar(); /* Send external interrupts to me */ psi_set_external_irq_policy(EXTERNAL_IRQ_POLICY_SKIBOOT); /* Initialize AHB accesses via AST2400 */ ast_io_init(); /* * Depending on which image we are running, it may be configuring * the virtual UART or not. Check if VUART is enabled and use * SIO if not. We also correct the configuration of VUART as some * BMC images don't setup the interrupt properly */ if (ast_is_vuart1_enabled()) { printf("PLAT: Using virtual UART\n"); ast_disable_sio_uart1(); ast_setup_vuart1(UART_IO_BASE, UART_LPC_IRQ); } else { printf("PLAT: Using SuperIO UART\n"); ast_setup_sio_uart1(UART_IO_BASE, UART_LPC_IRQ); } /* Similarly, some BMCs don't configure the BT interrupt properly */ ast_setup_ibt(BT_IO_BASE, BT_LPC_IRQ); /* Setup UART and use it as console with interrupts */ uart_init(true); prd_init(); } skiboot-skiboot-5.1.13/platforms/astbmc/firestone.c000066400000000000000000000070131265204436200223520ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "astbmc.h" static const struct slot_table_entry firestone_phb0_0_slot[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0,0), .name = "Slot5", }, { .etype = st_end }, }; static const struct slot_table_entry firestone_phb0_1_slot[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0,0), .name = "Slot4", }, { .etype = st_end }, }; static const struct slot_table_entry firestone_phb8_0_slot[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0,0), .name = "Slot2", }, { .etype = st_end }, }; static const struct slot_table_entry firestone_plx_slots[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(1,0), .name = "Slot3", }, { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(9,0), .name = "Backplane USB", }, { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0xa,0), .name = "Backplane SATA", }, { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0xb,0), .name = "Backplane BMC", }, { .etype = st_end }, }; static const struct slot_table_entry firestone_plx_up[] = { { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0,0), .children = firestone_plx_slots, }, { .etype = st_end }, }; static const struct slot_table_entry firestone_phb8_1_slot[] = { { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0,0), .name = "Backplane PLX", .children = firestone_plx_up, }, { .etype = st_end }, }; static const struct slot_table_entry firestone_phb8_2_slot[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0,0), .name = "Slot1", }, { .etype = st_end }, }; static const struct slot_table_entry firestone_phb_table[] = { { .etype = st_phb, .location = ST_LOC_PHB(0,0), .children = firestone_phb0_0_slot, }, { .etype = st_phb, .location = ST_LOC_PHB(0,1), .children = firestone_phb0_1_slot, }, { .etype = st_phb, .location = ST_LOC_PHB(8,0), .children = firestone_phb8_0_slot, }, { .etype = st_phb, .location = ST_LOC_PHB(8,1), .children = firestone_phb8_1_slot, }, { .etype = st_phb, .location = ST_LOC_PHB(8,2), .children = firestone_phb8_2_slot, }, { .etype = st_end }, }; static bool firestone_probe(void) { if (!dt_node_is_compatible(dt_root, "ibm,firestone")) return false; /* Lot of common early inits here */ astbmc_early_init(); slot_table_init(firestone_phb_table); return true; } DECLARE_PLATFORM(firestone) = { .name = "Firestone", .probe = firestone_probe, .init = astbmc_init, .pci_get_slot_info = slot_table_get_slot_info, .external_irq = astbmc_ext_irq_serirq_cpld, .cec_power_down = astbmc_ipmi_power_down, .cec_reboot = astbmc_ipmi_reboot, .elog_commit = ipmi_elog_commit, .start_preload_resource = flash_start_preload_resource, .resource_loaded = flash_resource_loaded, .exit = ipmi_wdt_final_reset, .terminate = ipmi_terminate, }; skiboot-skiboot-5.1.13/platforms/astbmc/garrison.c000066400000000000000000000032671265204436200222070ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "astbmc.h" static bool garrison_probe(void) { if (!dt_node_is_compatible(dt_root, "ibm,garrison")) return false; /* Lot of common early inits here */ astbmc_early_init(); /* * Override external interrupt policy -> send to Linux * * On Naples, we get LPC interrupts via the built-in LPC * controller demuxer, not an external CPLD. The external * interrupt is for other uses, such as the TPM chip, we * currently route it to Linux, but we might change that * later if we decide we need it. */ psi_set_external_irq_policy(EXTERNAL_IRQ_POLICY_LINUX); return true; } DECLARE_PLATFORM(garrison) = { .name = "Garrison", .probe = garrison_probe, .init = astbmc_init, .cec_power_down = astbmc_ipmi_power_down, .cec_reboot = astbmc_ipmi_reboot, .elog_commit = ipmi_elog_commit, .start_preload_resource = flash_start_preload_resource, .resource_loaded = flash_resource_loaded, .exit = ipmi_wdt_final_reset, .terminate = ipmi_terminate, }; skiboot-skiboot-5.1.13/platforms/astbmc/habanero.c000066400000000000000000000067411265204436200221420ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "astbmc.h" static const struct slot_table_entry habanero_phb0_slot[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0,0), .name = "Slot3", }, { .etype = st_end }, }; static const struct slot_table_entry habanero_plx_slots[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(1,0), .name = "Network Mezz", }, { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(2,0), .name = "Network Mezz", }, { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(8,0), .name = "Storage Mezz", }, { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(9,0), .name = "Backplane USB", }, { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0xa,0), .name = "Backplane BMC", }, { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0x10,0), .name = "Slot2", }, { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0x11,0), .name = "Slot1", }, { .etype = st_end }, }; static const struct slot_table_entry habanero_plx_up[] = { { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0,0), .children = habanero_plx_slots, }, { .etype = st_end }, }; static const struct slot_table_entry habanero_phb1_slot[] = { { .etype = st_builtin_dev, .location = ST_LOC_DEVFN(0,0), .name = "Backplane PLX", .children = habanero_plx_up, }, { .etype = st_end }, }; static const struct slot_table_entry habanero_phb2_slot[] = { { .etype = st_pluggable_slot, .location = ST_LOC_DEVFN(0,0), .name = "Slot4", }, { .etype = st_end }, }; static const struct slot_table_entry habanero_phb_table[] = { { .etype = st_phb, .location = ST_LOC_PHB(0,0), .children = habanero_phb0_slot, }, { .etype = st_phb, .location = ST_LOC_PHB(0,1), .children = habanero_phb1_slot, }, { .etype = st_phb, .location = ST_LOC_PHB(0,2), .children = habanero_phb2_slot, }, { .etype = st_end }, }; static bool habanero_probe(void) { const char *model; if (!dt_node_is_compatible(dt_root, "ibm,powernv")) return false; /* Temporary ... eventually we'll get that in compatible */ model = dt_prop_get_def(dt_root, "model", NULL); if ((!model || !strstr(model, "habanero")) && (!dt_node_is_compatible(dt_root, "tyan,habanero"))) return false; /* Lot of common early inits here */ astbmc_early_init(); slot_table_init(habanero_phb_table); return true; } DECLARE_PLATFORM(habanero) = { .name = "Habanero", .probe = habanero_probe, .init = astbmc_init, .pci_get_slot_info = slot_table_get_slot_info, .external_irq = astbmc_ext_irq_serirq_cpld, .cec_power_down = astbmc_ipmi_power_down, .cec_reboot = astbmc_ipmi_reboot, .elog_commit = ipmi_elog_commit, .start_preload_resource = flash_start_preload_resource, .resource_loaded = flash_resource_loaded, .exit = ipmi_wdt_final_reset, .terminate = ipmi_terminate, }; skiboot-skiboot-5.1.13/platforms/astbmc/palmetto.c000066400000000000000000000031061265204436200222000ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "astbmc.h" static bool palmetto_probe(void) { const char *model; if (!dt_node_is_compatible(dt_root, "ibm,powernv")) return false; /* Temporary ... eventually we'll get that in compatible */ model = dt_prop_get_def(dt_root, "model", NULL); if ((!model || !strstr(model, "palmetto")) && (!dt_node_is_compatible(dt_root, "tyan,palmetto"))) return false; /* Lot of common early inits here */ astbmc_early_init(); return true; } DECLARE_PLATFORM(palmetto) = { .name = "Palmetto", .probe = palmetto_probe, .init = astbmc_init, .external_irq = astbmc_ext_irq_serirq_cpld, .cec_power_down = astbmc_ipmi_power_down, .cec_reboot = astbmc_ipmi_reboot, .elog_commit = ipmi_elog_commit, .start_preload_resource = flash_start_preload_resource, .resource_loaded = flash_resource_loaded, .exit = ipmi_wdt_final_reset, .terminate = ipmi_terminate, }; skiboot-skiboot-5.1.13/platforms/astbmc/pnor.c000066400000000000000000000032001265204436200213240ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "astbmc.h" int pnor_init(void) { struct spi_flash_ctrl *pnor_ctrl; struct blocklevel_device *bl = NULL; int rc; /* Open controller and flash. If the LPC->AHB doesn't point to * the PNOR flash base we assume we're booting from BMC system * memory (or some other place setup by the BMC to support LPC * FW reads & writes). */ if (ast_is_ahb_lpc_pnor()) rc = ast_sf_open(AST_SF_TYPE_PNOR, &pnor_ctrl); else { printf("PLAT: Memboot detected\n"); rc = ast_sf_open(AST_SF_TYPE_MEM, &pnor_ctrl); } if (rc) { prerror("PLAT: Failed to open PNOR flash controller\n"); goto fail; } rc = flash_init(pnor_ctrl, &bl, NULL); if (rc) { prerror("PLAT: Failed to open init PNOR driver\n"); goto fail; } rc = flash_register(bl, true); if (!rc) return 0; fail: if (bl) flash_exit(bl); if (pnor_ctrl) ast_sf_close(pnor_ctrl); return rc; } skiboot-skiboot-5.1.13/platforms/astbmc/slots.c000066400000000000000000000052121265204436200215170ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "astbmc.h" static const struct slot_table_entry *slot_top_table; void slot_table_init(const struct slot_table_entry *top_table) { slot_top_table = top_table; } static const struct slot_table_entry *match_slot_phb_entry(struct phb *phb) { uint32_t chip_id = dt_get_chip_id(phb->dt_node); uint32_t phb_idx = dt_prop_get_u32_def(phb->dt_node, "ibm,phb-index", 0); const struct slot_table_entry *ent; if (!slot_top_table) return NULL; for (ent = slot_top_table; ent->etype != st_end; ent++) { if (ent->etype != st_phb) { prerror("SLOT: Bad DEV entry type in table !\n"); continue; } if (ent->location == ST_LOC_PHB(chip_id, phb_idx)) return ent; } return NULL; } static const struct slot_table_entry *match_slot_dev_entry(struct phb *phb, struct pci_device *pd) { const struct slot_table_entry *parent, *ent; /* Find a parent recursively */ if (pd->parent) parent = match_slot_dev_entry(phb, pd->parent); else { /* No parent, this is a root complex, find the PHB */ parent = match_slot_phb_entry(phb); } /* No parent ? Oops ... */ if (!parent || !parent->children) return NULL; for (ent = parent->children; ent->etype != st_end; ent++) { if (ent->etype == st_phb) { prerror("SLOT: Bad PHB entry type in table !\n"); continue; } if (ent->location == (pd->bdfn & 0xff)) return ent; } return NULL; } void slot_table_get_slot_info(struct phb *phb, struct pci_device * pd) { const struct slot_table_entry *ent; struct pci_slot_info *si; if (!pd || pd->slot_info) return; ent = match_slot_dev_entry(phb, pd); if (!ent || !ent->name) return; pd->slot_info = si = zalloc(sizeof(struct pci_slot_info)); assert(pd->slot_info); strncpy(si->label, ent->name, sizeof(si->label) - 1); si->pluggable = ent->etype == st_pluggable_slot; si->power_ctl = false; si->wired_lanes = -1; si->bus_clock = -1; si->connector_type = -1; si->card_desc = -1; si->card_mech = -1; si->pwr_led_ctl = -1; si->attn_led_ctl = -1; } skiboot-skiboot-5.1.13/platforms/ibm-fsp/000077500000000000000000000000001265204436200202735ustar00rootroot00000000000000skiboot-skiboot-5.1.13/platforms/ibm-fsp/Makefile.inc000066400000000000000000000002561265204436200225060ustar00rootroot00000000000000SUBDIRS += $(PLATDIR)/ibm-fsp IBM_FSP_OBJS = common.o lxvpd.o apollo.o firenze.o IBM_FSP = $(PLATDIR)/ibm-fsp/built-in.o $(IBM_FSP): $(IBM_FSP_OBJS:%=$(PLATDIR)/ibm-fsp/%) skiboot-skiboot-5.1.13/platforms/ibm-fsp/apollo.c000066400000000000000000000035701265204436200217320ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "ibm-fsp.h" #include "lxvpd.h" static bool apollo_probe(void) { return dt_node_is_compatible(dt_root, "ibm,apollo"); } static void apollo_setup_phb(struct phb *phb, unsigned int index) { struct dt_node *ioc_node; /* Grab the device-tree node of the IOC */ ioc_node = phb->dt_node->parent; if (!ioc_node) return; /* * Process the pcie slot entries from the lx vpd lid * * FIXME: We currently assume chip 1 always, this will have to be * fixed once we understand the right way to get the BRxy/BRxy "x" * "x" value. (this actually seems to work...) */ lxvpd_process_slot_entries(phb, ioc_node, 1, index); } DECLARE_PLATFORM(apollo) = { .name = "Apollo", .probe = apollo_probe, .init = ibm_fsp_init, .exit = ibm_fsp_exit, .cec_power_down = ibm_fsp_cec_power_down, .cec_reboot = ibm_fsp_cec_reboot, .pci_setup_phb = apollo_setup_phb, .pci_get_slot_info = lxvpd_get_slot_info, .nvram_info = fsp_nvram_info, .nvram_start_read = fsp_nvram_start_read, .nvram_write = fsp_nvram_write, .elog_commit = elog_fsp_commit, .start_preload_resource = fsp_start_preload_resource, .resource_loaded = fsp_resource_loaded, .sensor_read = ibm_fsp_sensor_read, .terminate = ibm_fsp_terminate, }; skiboot-skiboot-5.1.13/platforms/ibm-fsp/common.c000066400000000000000000000130711265204436200217310ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "ibm-fsp.h" static void map_debug_areas(void) { uint64_t t, i; /* Our memcons is in a section of its own and already * aligned to 4K. The buffers are mapped as a whole */ fsp_tce_map(PSI_DMA_MEMCONS, &memcons, 0x1000); fsp_tce_map(PSI_DMA_LOG_BUF, (void*)INMEM_CON_START, INMEM_CON_LEN); debug_descriptor.memcons_tce = PSI_DMA_MEMCONS; t = memcons.obuf_phys - INMEM_CON_START + PSI_DMA_LOG_BUF; debug_descriptor.memcons_obuf_tce = t; t = memcons.ibuf_phys - INMEM_CON_START + PSI_DMA_LOG_BUF; debug_descriptor.memcons_ibuf_tce = t; /* We only have space in the TCE table for the trace * areas on P8 */ if (proc_gen != proc_gen_p8) return; t = PSI_DMA_TRACE_BASE; for (i = 0; i < debug_descriptor.num_traces; i++) { /* * Trace buffers are misaligned by 0x10 due to the lock * in the trace structure, and their size is also not * completely aligned. (They are allocated so that with * the lock included, they do cover entire multiple of * a 4K page however). * * This means we have to map the lock into the TCEs and * align everything. Not a huge deal but needs to be * taken into account. * * Note: Maybe we should map them read-only... */ uint64_t tstart, tend, toff, tsize; tstart = ALIGN_DOWN(debug_descriptor.trace_phys[i], 0x1000); tend = ALIGN_UP(debug_descriptor.trace_phys[i] + debug_descriptor.trace_size[i], 0x1000); toff = debug_descriptor.trace_phys[i] - tstart; tsize = tend - tstart; fsp_tce_map(t, (void *)tstart, tsize); debug_descriptor.trace_tce[i] = t + toff; t += tsize; } } void ibm_fsp_init(void) { /* Early initializations of the FSP interface */ fsp_init(); map_debug_areas(); fsp_sysparam_init(); /* Get ready to receive E0 class messages. We need to respond * to some of these for the init sequence to make forward progress */ fsp_console_preinit(); /* Get ready to receive OCC related messages */ occ_fsp_init(); /* Get ready to receive Memory [Un]corretable Error messages. */ fsp_memory_err_init(); /* Initialize elog access */ fsp_elog_read_init(); fsp_elog_write_init(); /* Initiate dump service */ fsp_dump_init(); /* Start FSP/HV state controller & perform OPL */ fsp_opl(); /* Preload hostservices lids */ hservices_lid_preload(); /* Initialize SP attention area */ fsp_attn_init(); /* Initialize monitoring of TOD topology change event notification */ fsp_chiptod_init(); /* Send MDST table notification to FSP */ op_display(OP_LOG, OP_MOD_INIT, 0x0000); fsp_mdst_table_init(); /* Initialize the panel */ op_display(OP_LOG, OP_MOD_INIT, 0x0001); fsp_oppanel_init(); /* Start the surveillance process */ op_display(OP_LOG, OP_MOD_INIT, 0x0002); fsp_init_surveillance(); /* IPMI */ fsp_ipmi_init(); ipmi_opal_init(); /* Initialize sensor access */ op_display(OP_LOG, OP_MOD_INIT, 0x0003); fsp_init_sensor(); /* LED */ op_display(OP_LOG, OP_MOD_INIT, 0x0004); fsp_led_init(); /* Monitor for DIAG events */ op_display(OP_LOG, OP_MOD_INIT, 0x0005); fsp_init_diag(); /* Finish initializing the console */ op_display(OP_LOG, OP_MOD_INIT, 0x0006); fsp_console_init(); /* Read our initial RTC value */ op_display(OP_LOG, OP_MOD_INIT, 0x0008); fsp_rtc_init(); /* Initialize code update access */ op_display(OP_LOG, OP_MOD_INIT, 0x0009); fsp_code_update_init(); /* EPOW */ op_display(OP_LOG, OP_MOD_INIT, 0x000A); fsp_epow_init(); /* EPOW */ op_display(OP_LOG, OP_MOD_INIT, 0x000B); fsp_dpo_init(); /* Setup console */ if (fsp_present()) fsp_console_add_nodes(); } void ibm_fsp_exit(void) { /* * LED related SPCN commands might take a while to * complete. Call this as late as possible to * ensure we have all the LED information. */ create_led_device_nodes(); } int64_t ibm_fsp_cec_reboot(void) { uint32_t cmd = FSP_CMD_REBOOT; if (!fsp_present()) return OPAL_UNSUPPORTED; /* Flash new firmware */ if (fsp_flash_term_hook && fsp_flash_term_hook() == OPAL_SUCCESS) cmd = FSP_CMD_DEEP_REBOOT; printf("FSP: Sending 0x%02x reboot command to FSP...\n", cmd); /* If that failed, talk to the FSP */ if (fsp_sync_msg(fsp_mkmsg(cmd, 0), true)) return OPAL_INTERNAL_ERROR; return OPAL_SUCCESS; } int64_t ibm_fsp_cec_power_down(uint64_t request) { /* Request is: * * 0 = normal * 1 = immediate * (we do not allow 2 for "pci cfg reset" just yet) */ if (request !=0 && request != 1) return OPAL_PARAMETER; if (!fsp_present()) return OPAL_UNSUPPORTED; /* Flash new firmware */ if (fsp_flash_term_hook) fsp_flash_term_hook(); printf("FSP: Sending shutdown command to FSP...\n"); if (fsp_sync_msg(fsp_mkmsg(FSP_CMD_POWERDOWN_NORM, 1, request), true)) return OPAL_INTERNAL_ERROR; fsp_reset_links(); return OPAL_SUCCESS; } int64_t ibm_fsp_sensor_read(uint32_t sensor_hndl, int token, uint32_t *sensor_data) { return fsp_opal_read_sensor(sensor_hndl, token, sensor_data); } skiboot-skiboot-5.1.13/platforms/ibm-fsp/firenze.c000066400000000000000000000362421265204436200221100ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "ibm-fsp.h" #include "lxvpd.h" /* Structure used to send PCIe card info to FSP */ struct fsp_pcie_entry { uint32_t hw_proc_id; uint16_t slot_idx; uint16_t reserved; uint16_t vendor_id; uint16_t device_id; uint16_t subsys_vendor_id; uint16_t subsys_device_id; }; struct fsp_pcie_inventory { uint32_t version; /* currently 1 */ uint32_t num_entries; uint32_t entry_size; uint32_t entry_offset; struct fsp_pcie_entry entries[]; }; static struct fsp_pcie_inventory *fsp_pcie_inv; static unsigned int fsp_pcie_inv_alloc_count; #define FSP_PCIE_INV_ALLOC_CHUNK 4 static uint64_t lx_vpd_id; struct lock fsp_pcie_inv_lock = LOCK_UNLOCKED; static struct dt_node *dt_create_i2c_master(struct dt_node *n, uint32_t eng_id) { struct dt_node *i2cm; /* Each master registers set is of length 0x20 */ i2cm = dt_new_addr(n, "i2cm", 0xa0000 + eng_id * 0x20); if (!i2cm) return NULL; dt_add_property_string(i2cm, "compatible", "ibm,power8-i2cm"); dt_add_property_cells(i2cm, "reg", 0xa0000 + eng_id * 0x20, 0x20); dt_add_property_cells(i2cm, "clock-frequency", 50000000); dt_add_property_cells(i2cm, "chip-engine#", eng_id); dt_add_property_cells(i2cm, "#address-cells", 1); dt_add_property_cells(i2cm, "#size-cells", 0); return i2cm; } static struct dt_node *dt_create_i2c_bus(struct dt_node *i2cm, const char *port_name, uint32_t port_id) { static struct dt_node *port; port = dt_new_addr(i2cm, "i2c-bus", port_id); if (!port) return NULL; dt_add_property_strings(port, "compatible", "ibm,power8-i2c-port", "ibm,opal-i2c"); dt_add_property_string(port, "ibm,port-name", port_name); dt_add_property_cells(port, "reg", port_id); dt_add_property_cells(port, "bus-frequency", 400000); dt_add_property_cells(port, "#address-cells", 1); dt_add_property_cells(port, "#size-cells", 0); return port; } static struct dt_node *dt_create_i2c_device(struct dt_node *bus, uint8_t addr, const char *name, const char *compat, const char *label) { struct dt_node *dev; dev = dt_new_addr(bus, name, addr); if (!dev) return NULL; dt_add_property_string(dev, "compatible", compat); dt_add_property_string(dev, "label", label); dt_add_property_cells(dev, "reg", addr); dt_add_property_string(dev, "status", "reserved"); return dev; } static struct i2c_bus *firenze_pci_find_i2c_bus(uint8_t chip, uint8_t eng, uint8_t port) { struct dt_node *np, *child; uint32_t reg; /* Iterate I2C masters */ dt_for_each_compatible(dt_root, np, "ibm,power8-i2cm") { if (!np->parent || !dt_node_is_compatible(np->parent, "ibm,power8-xscom")) continue; /* Check chip index */ reg = dt_prop_get_u32(np->parent, "ibm,chip-id"); if (reg != chip) continue; /* Check I2C master index */ reg = dt_prop_get_u32(np, "chip-engine#"); if (reg != eng) continue; /* Iterate I2C buses */ dt_for_each_child(np, child) { if (!dt_node_is_compatible(child, "ibm,power8-i2c-port")) continue; /* Check I2C port index */ reg = dt_prop_get_u32(child, "reg"); if (reg != port) continue; reg = dt_prop_get_u32(child, "ibm,opal-id"); return i2c_find_bus_by_id(reg); } } return NULL; } static void firenze_dt_fixup_i2cm(void) { struct dt_node *master, *bus, *dev; struct proc_chip *c; const uint32_t *p; char name[32]; uint64_t lx; if (dt_find_compatible_node(dt_root, NULL, "ibm,power8-i2cm")) return; p = dt_prop_get_def(dt_root, "ibm,vpd-lx-info", NULL); if (!p) return; lx = ((uint64_t)p[1] << 32) | p[2]; switch (lx) { case LX_VPD_2S4U_BACKPLANE: case LX_VPD_2S2U_BACKPLANE: case LX_VPD_SHARK_BACKPLANE: /* XXX confirm ? */ /* i2c nodes on chip 0x10 */ c = get_chip(0x10); if (c) { /* Engine 1 */ master = dt_create_i2c_master(c->devnode, 1); assert(master); snprintf(name, sizeof(name), "p8_%08x_e%dp%d", c->id, 1, 0); bus = dt_create_i2c_bus(master, name, 0); assert(bus); dev = dt_create_i2c_device(bus, 0x39, "power-control", "maxim,5961", "pcie-hotplug"); assert(dev); dt_add_property_strings(dev, "target-list", "slot-C4", "slot-C5"); dev = dt_create_i2c_device(bus, 0x3a, "power-control", "maxim,5961", "pcie-hotplug"); assert(dev); dt_add_property_strings(dev, "target-list", "slot-C2", "slot-C3"); } else { prlog(PR_INFO, "PLAT: Chip not found for the id 0x10\n"); } /* Fall through */ case LX_VPD_1S4U_BACKPLANE: case LX_VPD_1S2U_BACKPLANE: /* i2c nodes on chip 0 */ c = get_chip(0); if (!c) { prlog(PR_INFO, "PLAT: Chip not found for the id 0x0\n"); break; } /* Engine 1*/ master = dt_create_i2c_master(c->devnode, 1); assert(master); snprintf(name, sizeof(name), "p8_%08x_e%dp%d", c->id, 1, 0); bus = dt_create_i2c_bus(master, name, 0); assert(bus); dev = dt_create_i2c_device(bus, 0x32, "power-control", "maxim,5961", "pcie-hotplug"); assert(dev); dt_add_property_strings(dev, "target-list", "slot-C10", "slot-C11"); dev = dt_create_i2c_device(bus, 0x35, "power-control", "maxim,5961", "pcie-hotplug"); assert(dev); dt_add_property_strings(dev, "target-list", "slot-C6", "slot-C7"); dev = dt_create_i2c_device(bus, 0x36, "power-control", "maxim,5961", "pcie-hotplug"); assert(dev); dt_add_property_strings(dev, "target-list", "slot-C8", "slot-C9"); dev = dt_create_i2c_device(bus, 0x39, "power-control", "maxim,5961", "pcie-hotplug"); assert(dev); dt_add_property_strings(dev, "target-list", "slot-C12"); break; default: break; } } static bool firenze_probe(void) { if (!dt_node_is_compatible(dt_root, "ibm,firenze")) return false; firenze_dt_fixup_i2cm(); return true; } static void firenze_send_pci_inventory(void) { uint64_t base, abase, end, aend, offset; int64_t rc; if (!fsp_pcie_inv) return; prlog(PR_DEBUG, "PLAT: Sending PCI inventory to FSP, table has" " %d entries\n", fsp_pcie_inv->num_entries); { unsigned int i; prlog(PR_DEBUG, "HWP SLT VDID DVID SVID SDID\n"); prlog(PR_DEBUG, "---------------------------\n"); for (i = 0; i < fsp_pcie_inv->num_entries; i++) { struct fsp_pcie_entry *e = &fsp_pcie_inv->entries[i]; prlog(PR_DEBUG, "%03d %03d %04x %04x %04x %04x\n", e->hw_proc_id, e->slot_idx, e->vendor_id, e->device_id, e->subsys_vendor_id, e->subsys_device_id); } } /* * Get the location of the table in a form we can send * to the FSP */ base = (uint64_t)fsp_pcie_inv; end = base + sizeof(struct fsp_pcie_inventory) + fsp_pcie_inv->num_entries * fsp_pcie_inv->entry_size; abase = base & ~0xffful; aend = (end + 0xffful) & ~0xffful; offset = PSI_DMA_PCIE_INVENTORY + (base & 0xfff); /* We can only accommodate so many entries in the PSI map */ if ((aend - abase) > PSI_DMA_PCIE_INVENTORY_SIZE) { prerror("PLAT: PCIe inventory too large (%lld bytes)\n", aend - abase); goto bail; } /* Map this in the TCEs */ fsp_tce_map(PSI_DMA_PCIE_INVENTORY, (void *)abase, aend - abase); /* Send FSP message */ rc = fsp_sync_msg(fsp_mkmsg(FSP_CMD_PCI_POWER_CONF, 3, hi32(offset), lo32(offset), end - base), true); if (rc) prerror("PLAT: FSP error %lld sending inventory\n", rc); /* Unmap */ fsp_tce_unmap(PSI_DMA_PCIE_INVENTORY, aend - abase); bail: /* * We free the inventory. We'll have to redo that on hotplug * when we support it but that isn't the case yet */ free(fsp_pcie_inv); fsp_pcie_inv = NULL; } static void firenze_i2c_complete(int rc, struct i2c_request *req) { *(int *)req->user_data = rc; } static void firenze_do_i2c_byte(uint8_t chip, uint8_t eng, uint8_t port, uint8_t addr, uint8_t reg, uint8_t data) { struct i2c_bus *bus; struct i2c_request *req; uint8_t verif; int rc; bus = firenze_pci_find_i2c_bus(chip, eng, port); if (!bus) { prerror("FIRENZE: Failed to find i2c (%d/%d/%d)\n", chip, eng, port); return; } req = i2c_alloc_req(bus); if (!req) { prerror("FIRENZE: Failed to allocate i2c request\n"); return; } req->op = SMBUS_WRITE; req->dev_addr = addr >> 1; req->offset_bytes = 1; req->offset = reg; req->rw_buf = &data; req->rw_len = 1; req->completion = firenze_i2c_complete; req->user_data = &rc; rc = 1; i2c_queue_req(req); while(rc == 1) { time_wait_us(10); } if (rc != 0) { prerror("FIRENZE: I2C error %d writing byte\n", rc); return; } req->op = SMBUS_READ; req->dev_addr = addr >> 1; req->offset_bytes = 1; req->offset = reg; req->rw_buf = &verif; req->rw_len = 1; req->completion = firenze_i2c_complete; req->user_data = &rc; rc = 1; i2c_queue_req(req); while(rc == 1) { time_wait_us(10); } if (rc != 0) { prerror("FIRENZE: I2C error %d reading byte\n", rc); return; } if (verif != data) { prerror("FIRENZE: I2C miscompare want %02x got %02x\n", data, verif); } } static void firenze_fixup_pcie_slot_power(struct pci_device * pd) { const char *label = pd->slot_info->label; if (!pd->slot_info->pluggable) return; if (lx_vpd_id != LX_VPD_2S4U_BACKPLANE && lx_vpd_id != LX_VPD_1S4U_BACKPLANE) return; printf("FIRENZE: Checking slot %s for power fixup\n", label); /* Note: We apply the settings twice for C6/C7 but that shouldn't * be a problem */ if (!strncmp(label, "C6 ", 3) || !strncmp(label, "C7 ", 3)) { printf("FIRENZE: Fixing power on %s...\n", label); firenze_do_i2c_byte(0, 1, 0, 0x6a, 0x5e, 0xfa); firenze_do_i2c_byte(0, 1, 0, 0x6a, 0x5a, 0xff); firenze_do_i2c_byte(0, 1, 0, 0x6a, 0x5b, 0xff); } if (!strncmp(label, "C5 ", 3)) { printf("FIRENZE: Fixing power on %s...\n", label); firenze_do_i2c_byte(0, 1, 0, 0x72, 0x5e, 0xfb); firenze_do_i2c_byte(0, 1, 0, 0x72, 0x5b, 0xff); } if (!strncmp(label, "C3 ", 3)) { printf("FIRENZE: Fixing power on %s...\n", label); firenze_do_i2c_byte(0, 1, 0, 0x74, 0x5e, 0xfb); firenze_do_i2c_byte(0, 1, 0, 0x74, 0x5b, 0xff); } } static void firenze_add_pcidev_to_fsp_inventory(struct phb *phb, struct pci_device *pd) { struct fsp_pcie_entry *entry; struct proc_chip *chip; /* Check if we need to do some (Re)allocation */ if (!fsp_pcie_inv || fsp_pcie_inv->num_entries == fsp_pcie_inv_alloc_count) { unsigned int new_count; size_t new_size; bool need_init = !fsp_pcie_inv; /* (Re)allocate the block to the new size */ new_count = fsp_pcie_inv_alloc_count + FSP_PCIE_INV_ALLOC_CHUNK; new_size = sizeof(struct fsp_pcie_inventory); new_size += sizeof(struct fsp_pcie_entry) * new_count; fsp_pcie_inv = realloc(fsp_pcie_inv, new_size); fsp_pcie_inv_alloc_count = new_count; /* Initialize the header for a new inventory */ if (need_init) { fsp_pcie_inv->version = 1; fsp_pcie_inv->num_entries = 0; fsp_pcie_inv->entry_size = sizeof(struct fsp_pcie_entry); fsp_pcie_inv->entry_offset = offsetof(struct fsp_pcie_inventory, entries); } } /* Add entry */ entry = &fsp_pcie_inv->entries[fsp_pcie_inv->num_entries++]; chip = get_chip(dt_get_chip_id(phb->dt_node)); if (!chip) { prerror("PLAT: Failed to get chip for PHB !\n"); return; } entry->hw_proc_id = chip->pcid; entry->slot_idx = pd->parent->slot_info->slot_index; entry->reserved = 0; pci_cfg_read16(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &entry->vendor_id); pci_cfg_read16(phb, pd->bdfn, PCI_CFG_DEVICE_ID, &entry->device_id); if (pd->is_bridge) { int64_t ssvc = pci_find_cap(phb, pd->bdfn, PCI_CFG_CAP_ID_SUBSYS_VID); if (ssvc < 0) { entry->subsys_vendor_id = 0xffff; entry->subsys_device_id = 0xffff; } else { pci_cfg_read16(phb, pd->bdfn, ssvc + PCICAP_SUBSYS_VID_VENDOR, &entry->subsys_vendor_id); pci_cfg_read16(phb, pd->bdfn, ssvc + PCICAP_SUBSYS_VID_DEVICE, &entry->subsys_device_id); } } else { pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_VENDOR_ID, &entry->subsys_vendor_id); pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_ID, &entry->subsys_device_id); } } static void firenze_get_slot_info(struct phb *phb, struct pci_device * pd) { /* Call the main LXVPD function first */ lxvpd_get_slot_info(phb, pd); /* * Do we need to add that to the FSP inventory for power management ? * * For now, we only add devices that: * * - Are function 0 * - Are not an RC or a downstream bridge * - Have a direct parent that has a slot entry * - Slot entry says pluggable * - Aren't an upstream switch that has slot info */ if (!pd) return; if (pd->dev_type == PCIE_TYPE_ROOT_PORT || pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) { firenze_fixup_pcie_slot_power(pd); return; } if (pd->bdfn & 7) return; if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT && pd->slot_info) return; if (!pd->parent || !pd->parent->slot_info) return; if (!pd->parent->slot_info->pluggable) return; lock(&fsp_pcie_inv_lock); firenze_add_pcidev_to_fsp_inventory(phb, pd); unlock(&fsp_pcie_inv_lock); } static void firenze_setup_phb(struct phb *phb, unsigned int index) { uint32_t hub_id; /* Grab Hub ID used to parse VPDs */ hub_id = dt_prop_get_u32_def(phb->dt_node, "ibm,hub-id", 0); /* Process the pcie slot entries from the lx vpd lid */ lxvpd_process_slot_entries(phb, dt_root, hub_id, index); } static uint32_t ibm_fsp_occ_timeout(void) { /* Use a fixed 60s value for now */ return 60; } static void firenze_init(void) { /* We call hservices_init to relocate the hbrt image now, as the FSP * may request an OCC load any time after ibm_fsp_init. */ hservices_init(); ibm_fsp_init(); } DECLARE_PLATFORM(firenze) = { .name = "Firenze", .probe = firenze_probe, .init = firenze_init, .exit = ibm_fsp_exit, .cec_power_down = ibm_fsp_cec_power_down, .cec_reboot = ibm_fsp_cec_reboot, .pci_setup_phb = firenze_setup_phb, .pci_get_slot_info = firenze_get_slot_info, .pci_probe_complete = firenze_send_pci_inventory, .nvram_info = fsp_nvram_info, .nvram_start_read = fsp_nvram_start_read, .nvram_write = fsp_nvram_write, .occ_timeout = ibm_fsp_occ_timeout, .elog_commit = elog_fsp_commit, .start_preload_resource = fsp_start_preload_resource, .resource_loaded = fsp_resource_loaded, .sensor_read = ibm_fsp_sensor_read, .terminate = ibm_fsp_terminate, } ; skiboot-skiboot-5.1.13/platforms/ibm-fsp/ibm-fsp.h000066400000000000000000000017661265204436200220130ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __IBM_FSP_COMMON_H #define __IBM_FSP_COMMON_H extern void ibm_fsp_init(void); extern void ibm_fsp_exit(void); extern int64_t ibm_fsp_cec_power_down(uint64_t request); extern int64_t ibm_fsp_cec_reboot(void); struct errorlog; extern int elog_fsp_commit(struct errorlog *buf); extern int64_t ibm_fsp_sensor_read(uint32_t sensor_hndl, int token, uint32_t *sensor_data); #endif /* __IBM_FSP_COMMON_H */ skiboot-skiboot-5.1.13/platforms/ibm-fsp/lxvpd.c000066400000000000000000000216251265204436200216020ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * LXVPD support * */ #include #include #include #include #include #include "lxvpd.h" struct lxvpd_slot_info { uint8_t switch_id; uint8_t vswitch_id; uint8_t dev_id; struct pci_slot_info ps; }; /* * XXX TODO: Add 1006 maps to add function loc codes and loc code maps * (ie. -Tn part of the location code) */ struct lxvpd_slot_info_data { uint8_t num_slots; struct lxvpd_slot_info info[]; }; static bool lxvpd_supported_slot(struct phb *phb, struct pci_device *pd) { /* PCI/PCI-X we only support top level PHB with NULL "pd" */ if (phb->phb_type < phb_type_pcie_v1) return pd == NULL; /* Now we have PCI Express, we should never have a NULL "pd" */ if (!pd) return false; /* We support the root complex at the top level */ if (pd->dev_type == PCIE_TYPE_ROOT_PORT && !pd->parent) return true; /* We support an upstream switch port below the root complex */ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT && pd->parent && pd->parent->dev_type == PCIE_TYPE_ROOT_PORT && !pd->parent->parent) return true; /* We support a downstream switch port below an upstream port * below the root complex */ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT && pd->parent && pd->parent->dev_type == PCIE_TYPE_SWITCH_UPPORT && pd->parent->parent && pd->parent->parent->dev_type == PCIE_TYPE_ROOT_PORT && !pd->parent->parent->parent) return true; /* Anything else, bail */ return false; } void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd) { struct lxvpd_slot_info_data *sdata = phb->platform_data; bool is_phb = (pd && pd->parent) ? false : true; bool entry_found = false; uint8_t idx; /* Check if we have slot info */ if (!sdata) return; prlog(PR_TRACE, "LXVPD: Get Slot Info PHB%d pd=%x\n", phb->opal_id, pd ? pd->bdfn : 0); /* * This code only handles PHBs and PCIe switches at the top level. * * We do not handle any other switch nor any other type of PCI/PCI-X * bridge. */ if (!lxvpd_supported_slot(phb, pd)) { prlog(PR_TRACE, "LXVPD: Unsupported slot\n"); return; } /* Iterate the slot map */ for (idx = 0; idx <= sdata->num_slots; idx++) { struct lxvpd_slot_info *info = &sdata->info[idx]; uint8_t pd_dev = (pd->bdfn >> 3) & 0x1f; /* Match PHB with switch_id == 0 */ if (is_phb && info->switch_id == 0) { entry_found = true; break; } /* Match switch port with switch_id != 0 */ if (!is_phb && info->switch_id !=0 && info->dev_id == pd_dev) { entry_found = true; break; } } if (entry_found) { pd->slot_info = &sdata->info[idx].ps; prlog(PR_TRACE, "PCI: PCIE Slot Info: \n" " Label %s\n" " Pluggable 0x%x\n" " Power Ctl 0x%x\n" " Wired Lanes 0x%x\n" " Bus Clock 0x%x\n" " Connector 0x%x\n" " Slot Index %d\n", pd->slot_info->label, pd->slot_info->pluggable?1:0, pd->slot_info->power_ctl?1:0, pd->slot_info->wired_lanes, pd->slot_info->bus_clock, pd->slot_info->connector_type, pd->slot_info->slot_index); } else { prlog(PR_TRACE, "PCI: PCIE Slot Info Not Found\n"); } } static struct lxvpd_slot_info *lxvpd_alloc_slot_info(struct phb *phb, int count) { struct lxvpd_slot_info_data *data; data = zalloc(sizeof(struct lxvpd_slot_info_data) * count * sizeof(struct lxvpd_slot_info)); assert(data); data->num_slots = count; phb->platform_data = data; return data->info; } static void lxvpd_parse_1004_map(struct phb *phb, const uint8_t *sm, uint8_t sz) { const struct pci_slot_entry_1004 *entry = NULL; struct lxvpd_slot_info *slot_info, *info; uint8_t num_slots, slot, idx; num_slots = (sz / sizeof(struct pci_slot_entry_1004)); slot_info = lxvpd_alloc_slot_info(phb, num_slots); /* Iterate through the entries in the keyword */ entry = (const struct pci_slot_entry_1004 *)sm; for (slot = 0; slot < num_slots; slot++) { info = &slot_info[slot]; /* Put slot info into pci device structure */ info->switch_id = entry->pba >> 4; info->vswitch_id = entry->pba &0xf; info->dev_id = entry->sba; for (idx = 0; idx < 3; idx++) info->ps.label[idx] = entry->label[idx]; info->ps.label[3] = 0; info->ps.pluggable = ((entry->p0.byte & 0x20) == 0); info->ps.power_ctl = ((entry->p0.power_ctl & 0x40) == 1); switch(entry->p1.wired_lanes) { case 1: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_32; break; case 2: /* fall through */ case 3: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_64; break; case 4: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X1; break; case 5: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X4; break; case 6: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X8; break; case 7: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X16; break; default: info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN; } info->ps.wired_lanes = (entry->p1.wired_lanes - 3); info->ps.bus_clock = (entry->p2.bus_clock - 4); info->ps.connector_type = (entry->p2.connector_type - 5); if (entry->p3.byte < 0xC0) info->ps.card_desc = ((entry->p3.byte >> 6) - 4) ; else info->ps.card_desc = (entry->p3.byte >> 6); info->ps.card_mech = ((entry->p3.byte >> 4) & 0x3); info->ps.pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2); info->ps.attn_led_ctl = (entry->p3.byte & 0x3); info->ps.slot_index = entry->slot_index; entry++; } } static void lxvpd_parse_1005_map(struct phb *phb, const uint8_t *sm, uint8_t sz) { const struct pci_slot_entry_1005 *entry = NULL; struct lxvpd_slot_info *slot_info, *info; uint8_t num_slots, slot, idx; num_slots = (sz / sizeof(struct pci_slot_entry_1005)); slot_info = lxvpd_alloc_slot_info(phb, num_slots); /* Iterate through the entries in the keyword */ entry = (const struct pci_slot_entry_1005 *)sm; for (slot = 0; slot < num_slots; slot++) { info = &slot_info[slot]; /* Put slot info into pci device structure */ info->switch_id = entry->pba >> 4; info->vswitch_id = entry->pba &0xf; info->dev_id = entry->switch_device_id; for (idx = 0; idx < 8; idx++) info->ps.label[idx] = entry->label[idx]; info->ps.label[8] = 0; info->ps.pluggable = (entry->p0.pluggable == 0); info->ps.power_ctl = entry->p0.power_ctl; info->ps.wired_lanes = entry->p1.wired_lanes; if (info->ps.wired_lanes > PCI_SLOT_WIRED_LANES_PCIE_X32) info->ps.wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN; info->ps.bus_clock = entry->p2.bus_clock; info->ps.connector_type = entry->p2.connector_type; info->ps.card_desc = (entry->p3.byte >> 6); info->ps.card_mech = ((entry->p3.byte >> 4) & 0x3); info->ps.pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2); info->ps.attn_led_ctl = (entry->p3.byte & 0x3); info->ps.slot_index = entry->slot_index; entry++; } } void lxvpd_process_slot_entries(struct phb *phb, struct dt_node *node, uint8_t chip_id, uint8_t index) { const void *lxvpd; const uint8_t *pr_rec, *pr_end, *sm; size_t lxvpd_size, pr_size; const uint16_t *mf = NULL; char record[5] = "PR00"; uint8_t mf_sz, sm_sz; bool found = false; record[2] += chip_id; record[3] += index; record[4] = 0; /* Get LX VPD pointer */ lxvpd = dt_prop_get_def_size(node, "ibm,io-vpd", NULL, &lxvpd_size); if (!lxvpd) { printf("LXVPD: LX VPD not found for %s in %p\n", record, phb->dt_node); return; } pr_rec = vpd_find_record(lxvpd, lxvpd_size, record, &pr_size); if (!pr_rec) { printf("LXVPD: %s record not found in LX VPD in %p\n", record, phb->dt_node); return; } pr_end = pr_rec + pr_size; prlog(PR_TRACE, "LXVPD: %s record for PHB%d is %ld bytes\n", record, phb->opal_id, pr_size); /* As long as there's still something in the PRxy record... */ while(pr_rec < pr_end) { pr_size = pr_end - pr_rec; /* Find the next MF keyword */ mf = vpd_find_keyword(pr_rec, pr_size, "MF", &mf_sz); /* And the corresponding SM */ sm = vpd_find_keyword(pr_rec, pr_size, "SM", &sm_sz); if (!mf || !sm) { if (!found) printf("LXVPD: Slot Map keyword %s not found\n", record); return; } prlog(PR_TRACE, "LXVPD: Found 0x%04x map...\n", *mf); switch (*mf) { case 0x1004: lxvpd_parse_1004_map(phb, sm + 1, sm_sz - 1); found = true; break; case 0x1005: lxvpd_parse_1005_map(phb, sm + 1, sm_sz - 1); found = true; break; /* Add support for 0x1006 maps ... */ } pr_rec = sm + sm_sz; } } skiboot-skiboot-5.1.13/platforms/ibm-fsp/lxvpd.h000066400000000000000000000055641265204436200216130ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LXVPD_H #define __LXVPD_H #define LX_VPD_1S2U_BACKPLANE 0x3100040100300041ull #define LX_VPD_2S2U_BACKPLANE 0x3100040100300042ull #define LX_VPD_SHARK_BACKPLANE 0x3100040100300942ull #define LX_VPD_1S4U_BACKPLANE 0x3100040100300043ull #define LX_VPD_2S4U_BACKPLANE 0x3100040100300044ull /* P8 PCI Slot Entry Definitions -- 1005 */ struct slot_p0 { union { uint8_t byte; struct { uint8_t pluggable:1; uint8_t pluggable_location:3; uint8_t power_ctl:1; uint8_t rsvd_5:1; uint8_t upstream_port:1; uint8_t alt_load_source:1; }; }; }; struct slot_p1 { uint8_t rsvd_0:1; uint8_t wired_lanes:3; uint8_t rsvd_4:4; }; struct slot_p2 { uint8_t rsvd_0:1; uint8_t bus_clock:3; uint8_t connector_type:4; }; struct slot_p3 { union { uint8_t byte; struct { uint8_t height:1; uint8_t length:1; uint8_t left_mech:1; uint8_t right_mech:1; uint8_t pow_led_kvm:1; uint8_t pow_led_fsp:1; uint8_t attn_led_kvm:1; uint8_t attn_led_fsp:1; }; }; }; struct pci_slot_entry_1004 { uint8_t pba; uint8_t sba; uint8_t phb_or_slot_type; char label[3]; uint16_t bis; struct slot_p0 p0; struct slot_p1 p1; struct slot_p2 p2; struct slot_p3 p3; uint8_t left_pitch; uint8_t right_pitch; uint8_t slot_index; uint8_t max_slot_power; }; struct pci_slot_entry_1005 { union { uint8_t pba; struct { uint8_t switch_id:4; uint8_t vswitch_id:4; }; }; uint8_t switch_device_id; uint8_t slot_type:4; uint8_t phb_id:4; char label[8]; uint8_t rsvd_11[4]; struct slot_p0 p0; struct slot_p1 p1; struct slot_p2 p2; struct slot_p3 p3; uint8_t left_pitch; uint8_t right_pitch; uint8_t slot_index; uint8_t rsvd_22[2]; }; struct phb; extern void lxvpd_process_slot_entries(struct phb *phb, struct dt_node *node, uint8_t chip_id, uint8_t index); extern void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd); #endif /* __LXVPD_H */ skiboot-skiboot-5.1.13/platforms/mambo/000077500000000000000000000000001265204436200200315ustar00rootroot00000000000000skiboot-skiboot-5.1.13/platforms/mambo/Makefile.inc000066400000000000000000000002041265204436200222350ustar00rootroot00000000000000SUBDIRS += $(PLATDIR)/mambo MAMBO_OBJS = mambo.o MAMBO = $(PLATDIR)/mambo/built-in.o $(MAMBO): $(MAMBO_OBJS:%=$(PLATDIR)/mambo/%) skiboot-skiboot-5.1.13/platforms/mambo/mambo.c000066400000000000000000000041131265204436200212670ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include extern int64_t mambo_get_time(void); static bool mambo_probe(void) { if (!dt_find_by_path(dt_root, "/mambo")) return false; return true; } static int64_t mambo_rtc_read(uint32_t *ymd, uint64_t *hmsm) { int64_t mambo_time; struct tm t; time_t mt; if (!ymd || !hmsm) return OPAL_PARAMETER; mambo_time = mambo_get_time(); mt = mambo_time >> 32; gmtime_r(&mt, &t); tm_to_datetime(&t, ymd, hmsm); return OPAL_SUCCESS; } static void mambo_rtc_init(void) { struct dt_node *np = dt_new(opal_node, "rtc"); dt_add_property_strings(np, "compatible", "ibm,opal-rtc"); opal_register(OPAL_RTC_READ, mambo_rtc_read, 2); } static void mambo_platform_init(void) { force_dummy_console(); mambo_rtc_init(); } static int64_t mambo_cec_power_down(uint64_t request __unused) { if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) mambo_sim_exit(); return OPAL_UNSUPPORTED; } static int mambo_nvram_info(uint32_t *total_size) { *total_size = 0x100000; return OPAL_SUCCESS; } static int mambo_nvram_start_read(void *dst, uint32_t src, uint32_t len) { memset(dst+src, 0, len); nvram_read_complete(true); return OPAL_SUCCESS; } DECLARE_PLATFORM(mambo) = { .name = "Mambo", .probe = mambo_probe, .init = mambo_platform_init, .cec_power_down = mambo_cec_power_down, .nvram_info = mambo_nvram_info, .nvram_start_read = mambo_nvram_start_read, }; skiboot-skiboot-5.1.13/platforms/qemu/000077500000000000000000000000001265204436200177055ustar00rootroot00000000000000skiboot-skiboot-5.1.13/platforms/qemu/Makefile.inc000066400000000000000000000001741265204436200221170ustar00rootroot00000000000000SUBDIRS += $(PLATDIR)/qemu QEMU_OBJS = qemu.o QEMU = $(PLATDIR)/qemu/built-in.o $(QEMU): $(QEMU_OBJS:%=$(PLATDIR)/qemu/%) skiboot-skiboot-5.1.13/platforms/qemu/qemu.c000066400000000000000000000073131265204436200210240ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include static void qemu_init(void) { /* Setup UART console for use by Linux via OPAL API */ if (!dummy_console_enabled()) uart_setup_opal_console(); /* Setup LPC RTC and use it as time source. Call after * chiptod_init() */ lpc_rtc_init(); } static void qemu_dt_fixup_uart(struct dt_node *lpc) { /* * The official OF ISA/LPC binding is a bit odd, it prefixes * the unit address for IO with "i". It uses 2 cells, the first * one indicating IO vs. Memory space (along with bits to * represent aliasing). * * We pickup that binding and add to it "2" as a indication * of FW space. * * TODO: Probe the UART instead if the LPC bus allows for it */ struct dt_node *uart; char namebuf[32]; #define UART_IO_BASE 0x3f8 #define UART_IO_COUNT 8 #define UART_LPC_IRQ 4 snprintf(namebuf, sizeof(namebuf), "serial@i%x", UART_IO_BASE); uart = dt_new(lpc, namebuf); dt_add_property_cells(uart, "reg", 1, /* IO space */ UART_IO_BASE, UART_IO_COUNT); dt_add_property_strings(uart, "compatible", "ns16550", "pnpPNP,501"); dt_add_property_cells(uart, "clock-frequency", 1843200); dt_add_property_cells(uart, "current-speed", 115200); dt_add_property_cells(uart, "interrupts", UART_LPC_IRQ); dt_add_property_cells(uart, "interrupt-parent", lpc->phandle); /* * This is needed by Linux for some obscure reasons, * we'll eventually need to sanitize it but in the meantime * let's make sure it's there */ dt_add_property_strings(uart, "device_type", "serial"); } /* * This adds the legacy RTC device to the device-tree * for Linux to use */ static void qemu_dt_fixup_rtc(struct dt_node *lpc) { struct dt_node *rtc; char namebuf[32]; /* * Follows the structure expected by the kernel file * arch/powerpc/sysdev/rtc_cmos_setup.c */ snprintf(namebuf, sizeof(namebuf), "rtc@i%x", 0x70); rtc = dt_new(lpc, namebuf); dt_add_property_string(rtc, "compatible", "pnpPNP,b00"); dt_add_property_cells(rtc, "reg", 1, /* IO space */ 0x70, 2); } static void qemu_dt_fixup(void) { struct dt_node *n, *primary_lpc = NULL; /* Find the primary LPC bus */ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") { if (!primary_lpc || dt_has_node_property(n, "primary", NULL)) primary_lpc = n; if (dt_has_node_property(n, "#address-cells", NULL)) break; } if (!primary_lpc) return; qemu_dt_fixup_rtc(primary_lpc); qemu_dt_fixup_uart(primary_lpc); } static void qemu_ext_irq_serirq_cpld(unsigned int chip_id) { lpc_all_interrupts(chip_id); } static bool qemu_probe(void) { if (!dt_node_is_compatible(dt_root, "qemu,powernv")) return false; /* Add missing bits of device-tree such as the UART */ qemu_dt_fixup(); psi_set_external_irq_policy(EXTERNAL_IRQ_POLICY_SKIBOOT); /* * Setup UART and use it as console. For now, we * don't expose the interrupt as we know it's not * working properly yet */ uart_init(true); return true; } DECLARE_PLATFORM(qemu) = { .name = "Qemu", .probe = qemu_probe, .init = qemu_init, .external_irq = qemu_ext_irq_serirq_cpld, }; skiboot-skiboot-5.1.13/platforms/rhesus/000077500000000000000000000000001265204436200202475ustar00rootroot00000000000000skiboot-skiboot-5.1.13/platforms/rhesus/Makefile.inc000066400000000000000000000002141265204436200224540ustar00rootroot00000000000000SUBDIRS += $(PLATDIR)/rhesus RHESUS_OBJS = rhesus.o RHESUS = $(PLATDIR)/rhesus/built-in.o $(RHESUS): $(RHESUS_OBJS:%=$(PLATDIR)/rhesus/%) skiboot-skiboot-5.1.13/platforms/rhesus/rhesus.c000066400000000000000000000156361265204436200217370ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * EC GPIO mapping */ #define RHESUS_RST_UCD90160_N EC_GPIO_PORT_J, 3 #define RHESUS_FM_PWR_CYCLE_N EC_GPIO_PORT_K, 2 #define RHESUS_EN_PWR_ON_SEQ EC_GPIO_PORT_R, 1 #define RHESUS_BOARD_REVISION0 EC_GPIO_PORT_F, 3 #define RHESUS_BOARD_REVISION1 EC_GPIO_PORT_F, 2 #define RHESUS_BOARD_REVISION2 EC_GPIO_PORT_E, 5 #define RHESUS_BOARD_REVISION3 EC_GPIO_PORT_E, 4 #define RHESUS_BOARD_REVISION4 EC_GPIO_PORT_E, 1 /* * IO accessors for the EC driver */ void ec_outb(uint16_t addr, uint8_t data) { lpc_outb(data, addr); } uint8_t ec_inb(uint16_t addr) { return lpc_inb(addr); } static int rhesus_board_revision(void) { int revision = 0, ret = 0, i = 0; static const struct { EcGpioPort port; uint8_t pin; } revision_gpios[] = { { RHESUS_BOARD_REVISION0 }, { RHESUS_BOARD_REVISION1 }, { RHESUS_BOARD_REVISION2 }, { RHESUS_BOARD_REVISION3 }, { RHESUS_BOARD_REVISION4 }, }; for (i = 0; i < sizeof(revision_gpios) / sizeof(revision_gpios[0]); ++i) { ret = ec_gpio_read(revision_gpios[i].port, revision_gpios[i].pin); if (ret < 0) return ret; revision <<= 1; revision |= ret; } return revision; } static int64_t rhesus_reboot(void) { // TODO(rlippert): This should use EC_SYS_RST_N, but there is nothing to // deassert that at the moment. int ret = 0; ret = ec_gpio_set(RHESUS_FM_PWR_CYCLE_N, 0); if (ret < 0) { return ret; } ret = ec_gpio_setup(RHESUS_FM_PWR_CYCLE_N, EC_GPIO_OUTPUT, EC_GPIO_PULLUP_DISABLE); if (ret < 0) { return ret; } return 0; } static int64_t rhesus_power_down(uint64_t request __unused) { int ret = 0; ret = ec_gpio_set(RHESUS_EN_PWR_ON_SEQ, 0); if (ret < 0) { return ret; } ret = ec_gpio_setup(RHESUS_EN_PWR_ON_SEQ, EC_GPIO_OUTPUT, EC_GPIO_PULLUP_DISABLE); if (ret < 0) { return ret; } return 0; } static int rhesus_pnor_init(void) { struct spi_flash_ctrl *pnor_ctrl; struct blocklevel_device *bl = NULL; int rc; /* Open controller, flash and ffs */ rc = sfc_open(&pnor_ctrl); if (rc) { prerror("PLAT: Failed to open PNOR flash controller\n"); goto fail; } rc = flash_init(pnor_ctrl, &bl, NULL); if (rc) { prerror("PLAT: Failed to open init PNOR driver\n"); goto fail; } rc = flash_register(bl, true); if (!rc) return 0; fail: if (bl) flash_exit(bl); if (pnor_ctrl) sfc_close(pnor_ctrl); return rc; } static void rhesus_init(void) { /* Initialize PNOR/NVRAM */ rhesus_pnor_init(); /* Setup UART for direct use by Linux */ uart_setup_linux_passthrough(); } static void rhesus_dt_fixup_uart(struct dt_node *lpc, bool has_irq) { /* * The official OF ISA/LPC binding is a bit odd, it prefixes * the unit address for IO with "i". It uses 2 cells, the first * one indicating IO vs. Memory space (along with bits to * represent aliasing). * * We pickup that binding and add to it "2" as a indication * of FW space. * * TODO: Probe the UART instead if the LPC bus allows for it */ struct dt_node *uart; char namebuf[32]; #define UART_IO_BASE 0x3f8 #define UART_IO_COUNT 8 snprintf(namebuf, sizeof(namebuf), "serial@i%x", UART_IO_BASE); uart = dt_new(lpc, namebuf); dt_add_property_cells(uart, "reg", 1, /* IO space */ UART_IO_BASE, UART_IO_COUNT); dt_add_property_strings(uart, "compatible", "ns16550", "pnpPNP,501"); dt_add_property_cells(uart, "clock-frequency", 1843200); dt_add_property_cells(uart, "current-speed", 115200); /* * This is needed by Linux for some obscure reasons, * we'll eventually need to sanitize it but in the meantime * let's make sure it's there */ dt_add_property_strings(uart, "device_type", "serial"); /* Expose the external interrupt if supported */ if (has_irq) { uint32_t chip_id = dt_get_chip_id(lpc); uint32_t irq = get_psi_interrupt(chip_id) + P8_IRQ_PSI_HOST_ERR; dt_add_property_cells(uart, "interrupts", irq); dt_add_property_cells(uart, "interrupt-parent", get_ics_phandle()); } } /* * This adds the legacy RTC device to the device-tree * for Linux to use */ static void rhesus_dt_fixup_rtc(struct dt_node *lpc) { struct dt_node *rtc; /* * Follows the structure expected by the kernel file * arch/powerpc/sysdev/rtc_cmos_setup.c */ rtc = dt_new_addr(lpc, "rtc", EC_RTC_PORT_BASE); assert(rtc); dt_add_property_string(rtc, "compatible", "pnpPNP,b00"); dt_add_property_cells(rtc, "reg", 1, /* IO space */ EC_RTC_PORT_BASE, /* 1 index/data pair per 128 bytes */ (EC_RTC_BLOCK_SIZE / 128) * 2); } static void rhesus_dt_fixup(bool has_uart_irq) { struct dt_node *n, *primary_lpc = NULL; /* Find the primary LPC bus */ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") { if (!primary_lpc || dt_has_node_property(n, "primary", NULL)) primary_lpc = n; if (dt_has_node_property(n, "#address-cells", NULL)) break; } if (!primary_lpc) return; rhesus_dt_fixup_rtc(primary_lpc); rhesus_dt_fixup_uart(primary_lpc, has_uart_irq); } static bool rhesus_probe(void) { const char *model; int rev; bool has_uart_irq = false; if (!dt_node_is_compatible(dt_root, "ibm,powernv")) return false; model = dt_prop_get_def(dt_root, "model", NULL); if (!model || !(strstr(model, "rhesus") || strstr(model, "RHESUS"))) return false; /* Grab board version from EC */ rev = rhesus_board_revision(); if (rev >= 0) { printf("Rhesus board rev %d\n", rev); dt_add_property_cells(dt_root, "revision-id", rev); } else prerror("Rhesus board revision not found !\n"); /* Add missing bits of device-tree such as the UART */ rhesus_dt_fixup(has_uart_irq); /* * Setup UART and use it as console. For now, we * don't expose the interrupt as we know it's not * working properly yet */ uart_init(has_uart_irq); return true; } DECLARE_PLATFORM(rhesus) = { .name = "Rhesus", .probe = rhesus_probe, .init = rhesus_init, .cec_power_down = rhesus_power_down, .cec_reboot = rhesus_reboot, }; skiboot-skiboot-5.1.13/skiboot.lds.S000066400000000000000000000052671265204436200173200ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include ENTRY(boot_entry); SECTIONS { . = 0; .head : { KEEP(*(.head)) } . = SPIRA_OFF; .spira : { KEEP(*(.spira.data)) } . = PROCIN_OFF; .procin.data : { KEEP(*(.procin.data)) } . = MDST_TABLE_OFF; .mdst : { KEEP(*(.mdst.data)) } . = CPU_CTL_OFF; .cpuctrl : { KEEP(*(.cpuctrl.data)) } . = ALIGN(0x10); _stext = .; .text : { *(.text*) *(.sfpr) } _etext = .; .rodata : { __rodata_start = .; *(.rodata .rodata.*) __rodata_end = .; } .data : { /* * A couple of things that need to be 4K aligned and * to reside in their own pages for the sake of TCE * mappings */ . = ALIGN(0x1000); *(.data.memcons); . = ALIGN(0x1000); *(.data.boot_trace); . = ALIGN(0x1000); *(.data*) *(.force.data) *(.toc1) *(.branch_lt) } . = ALIGN(0x10); .init : { __ctors_start = .; *(.ctors) *(.init_array) __ctors_end = .; } . = ALIGN(0x10); .opd : { *(.opd) } . = ALIGN(0x100); .got : { __toc_start = . + 0x8000; *(.got) *(.toc) } . = ALIGN(0x10); .opal_table : { __opal_table_start = .; KEEP(*(.opal_table)) __opal_table_end = .; } .platforms : { __platforms_start = .; KEEP(*(.platforms)) __platforms_end = .; } /* Do I need to keep these ? */ .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } /* Relocations */ . = ALIGN(0x10); .dynamic : { __dynamic_start = .; *(.dynamic) __dynamic_end = .; } . = ALIGN(0x10); .rela.dyn : { __rela_dyn_start = .; *(.rela*) __rela_dyn_end = .; } .hash : { *(.hash) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } . = ALIGN(0x10); .sym_map : { __sym_map_start = . ; KEEP(*(.sym_map)) __sym_map_end = . ; KEEP(*(.sym_map)) } /* We locate the BSS at 2M to leave room for the symbol map */ . = 0x200000; _sbss = .; .bss : { *(.bss*) } . = ALIGN(0x10000); _ebss = .; _end = .; /* Optional kernel image */ . = ALIGN(0x10000); .builtin_kernel : { __builtin_kernel_start = .; KEEP(*(.builtin_kernel)) __builtin_kernel_end = .; } /* Discards */ /DISCARD/ : { *(.comment) *(.interp) } } skiboot-skiboot-5.1.13/test/000077500000000000000000000000001265204436200157065ustar00rootroot00000000000000skiboot-skiboot-5.1.13/test/Makefile.check000066400000000000000000000015451265204436200204270ustar00rootroot00000000000000check: boot-check boot-check: skiboot.lid ./test/run_boot_test.sh OP_BUILD_BOOT_CHECK=op-build-v1.0 op-build-v1.1 op-build-v1.2 op-build-v1.2.1 boot-check-%: skiboot.lid skiboot.map SKIBOOT_MEM_DUMP=skiboot-$(@:boot-check-%=%).dump SKIBOOT_ZIMAGE=`pwd`/opal-ci/images/$(@:boot-check-%=%)/zImage.epapr ./test/run_boot_test.sh boot-tests: boot-check $(OP_BUILD_BOOT_CHECK:%=boot-check-%) boot-coverage-report: boot-tests extract-gcov skiboot.map all-boot-hardware CROSS=$(CROSS) BOOT_TESTS="hello_world boot_test ${OP_BUILD_BOOT_CHECK} ${FSP_GCOV_MACHINES}" ./test/make-boot-coverage-report.sh boot-fsp-hardware-%: skiboot.lid skiboot.map ./external/boot-tests/boot_test.sh -v -p -b fsp -t $(@:boot-fsp-hardware-%=%) -1 skiboot.lid ./external/boot-tests/extract_gcov.sh $(@:boot-fsp-hardware-%=%) all-boot-hardware: $(FSP_GCOV_MACHINES:%=boot-fsp-hardware-%) skiboot-skiboot-5.1.13/test/dt_common.c000066400000000000000000000032211265204436200200270ustar00rootroot00000000000000/* Copyright 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "../include/device.h" /* dump_dt() is used in hdata/test/hdata_to_dt.c and core/test/run-device.c * this file is directly #included in both */ static void indent_num(unsigned indent) { unsigned int i; for (i = 0; i < indent; i++) putc(' ', stdout); } static void dump_val(unsigned indent, const void *prop, size_t size) { size_t i; int width = 78 - indent; for (i = 0; i < size; i++) { printf("%02x", ((unsigned char *)prop)[i]); width -= 2; if(width < 2) { printf("\n"); indent_num(indent); width = 80 - indent; } } } static void dump_dt(const struct dt_node *root, unsigned indent, bool show_props) { const struct dt_node *i; const struct dt_property *p; indent_num(indent); printf("node: %s\n", root->name); if (show_props) { list_for_each(&root->properties, p, list) { indent_num(indent); printf("prop: %s size: %zu val: ", p->name, p->len); dump_val(indent, p->prop, p->len); printf("\n"); } } list_for_each(&root->children, i, list) dump_dt(i, indent + 2, show_props); } skiboot-skiboot-5.1.13/test/hello_world/000077500000000000000000000000001265204436200202205ustar00rootroot00000000000000skiboot-skiboot-5.1.13/test/hello_world/Makefile.check000066400000000000000000000016401265204436200227350ustar00rootroot00000000000000HELLO_WORLD_TEST := test/hello_world/hello_kernel/hello_kernel check: $(HELLO_WORLD_TEST:%=%-check) boot-tests: $(HELLO_WORLD_TEST:%=%-check) $(HELLO_WORLD_TEST:%=%-check) : %-check: % skiboot.lid ./test/hello_world/run_hello_world.sh test/hello_world/hello_kernel/hello_kernel.o: test/hello_world/hello_kernel/hello_kernel.S test/hello_world/hello_kernel/hello_kernel.ld $(call Q,CC, $(CC) -m64 -c -MMD -o $@ $< ,$@) hello_kernel_LDFLAGS=-m64 -Wl,--build-id=none -T test/hello_world/hello_kernel/hello_kernel.ld -ffreestanding -nostdlib -Ttext=0x0 -include $(wildcard test/hello_world/hello_kernel/*.d) test/hello_world/hello_kernel/hello_kernel: test/hello_world/hello_kernel/hello_kernel.o $(call Q,LD, $(CC) $(hello_kernel_LDFLAGS) -o $@ $^ , $@) clean: hello_world-test-clean hello_world-test-clean: $(RM) -f test/hello_world/hello_kernel/hello_kernel $(RM) -f test/hello_world/hello_kernel/hello_kernel.[od] skiboot-skiboot-5.1.13/test/hello_world/hello_kernel/000077500000000000000000000000001265204436200226635ustar00rootroot00000000000000skiboot-skiboot-5.1.13/test/hello_world/hello_kernel/hello_kernel.S000066400000000000000000000023431265204436200254540ustar00rootroot00000000000000/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* hello_kernel.S! --------------- Because skiboot has its own stack, we don't even need that! All we need to do is make an OPAL call to write to the console. */ . = 0x0 .globl _start _start: mr %r2, %r8 /* r8 is the OPAL base passed in by skiboot */ bl here here: mflr %r8 /* work out where we are running */ li %r0, 1 /* OPAL_CONSOLE_WRITE */ li %r3, 0 /* terminal 0 */ addi %r4, %r8, len - here /* ptr to length of string */ addi %r5, %r8, str - here /* ptr to string start */ mtctr %r9 /* R9 is the OPAL entry point passed in by skiboot */ bctrl attn len: .long 0x00 .long (strend - str) str: .string "Hello World!\n" strend: skiboot-skiboot-5.1.13/test/hello_world/hello_kernel/hello_kernel.ld000066400000000000000000000004111265204436200256430ustar00rootroot00000000000000ENTRY(_start) SECTIONS { .text : { _start = .; *(.text) } . = ALIGN(4096); .data : { *(.rodata*) *(.data*) *(.sdata*) *(.got2) } . = ALIGN(4096); .bss : { _edata = .; __bss_start = .; *(.sbss) *(.bss) *(COMMON) _end = . ; } } skiboot-skiboot-5.1.13/test/hello_world/run_hello_world.sh000077500000000000000000000020241265204436200237530ustar00rootroot00000000000000#!/bin/bash if [ -z "$MAMBO_PATH" ]; then MAMBO_PATH=/opt/ibm/systemsim-p8/ fi if [ -z "$MAMBO_BINARY" ]; then MAMBO_BINARY="/run/pegasus/power8" fi if [ ! -x "$MAMBO_PATH/$MAMBO_BINARY" ]; then echo 'Could not find executable MAMBO_BINARY. Skipping hello_world test'; exit 0; fi if [ -n "$KERNEL" ]; then echo 'Please rebuild skiboot without KERNEL set. Skipping hello_world test'; exit 0; fi if [ ! `command -v expect` ]; then echo 'Could not find expect binary. Skipping hello_world test'; exit 0; fi export SKIBOOT_ZIMAGE=`pwd`/test/hello_world/hello_kernel/hello_kernel # Currently getting some core dumps from mambo, so disable them! OLD_ULIMIT_C=`ulimit -c` ulimit -c 0 ( cd external/mambo; cat <