pax_global_header00006660000000000000000000000064133645057150014523gustar00rootroot0000000000000052 comment=b72248d700903e5c2e87f2ff21101508dcef8677 pdbg-2.0/000077500000000000000000000000001336450571500123005ustar00rootroot00000000000000pdbg-2.0/.build.sh000077700000000000000000000000001336450571500165772utils/build.shustar00rootroot00000000000000pdbg-2.0/.gitignore000066400000000000000000000005241336450571500142710ustar00rootroot00000000000000*~ *.o *.d *.a TAGS tags cscope.out INSTALL Makefile.in aclocal.m4 autom4te.cache/ compile config.h.in configure depcomp install-sh missing Makefile pdbg .deps .dirstamp stamp-h1 config.h config.log config.status libpdbg.a .libs config.guess config.sub *.la *.lo ltmain.sh libtool m4 *.dtsi *.dts *.dt.h optcmd_test *.trs *.log test-driver pdbg-2.0/.travis.yml000066400000000000000000000001151336450571500144060ustar00rootroot00000000000000services: - docker script: - ./utils/build.sh - ./utils/test.sh pdbg-2.0/AUTHORS000066400000000000000000000000001336450571500133360ustar00rootroot00000000000000pdbg-2.0/COPYING000066400000000000000000000261361336450571500133430ustar00rootroot00000000000000 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. pdbg-2.0/ChangeLog000066400000000000000000000000001336450571500140400ustar00rootroot00000000000000pdbg-2.0/Makefile.am000066400000000000000000000124331336450571500143370ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign AM_MAKEFLAGS = --no-print-directory GIT_SHA1 ?= `git --work-tree=$(top_srcdir) --git-dir=$(top_srcdir)/.git describe --always --long --dirty 2>/dev/null || echo unknown` libpdbg_tests = libpdbg_target_test \ libpdbg_probe_test1 \ libpdbg_probe_test2 \ libpdbg_probe_test3 bin_PROGRAMS = pdbg check_PROGRAMS = $(libpdbg_tests) optcmd_test PDBG_TESTS = \ tests/test_selection.sh \ tests/test_hw_bmc.sh TESTS = $(libpdbg_tests) optcmd_test $(PDBG_TESTS) test: $(libpdbg_tests) TEST_EXTENSIONS = .sh SH_LOG_DRIVER = $(SHELL) $(srcdir)/tests/run_test.sh ACLOCAL_AMFLAGS = -Im4 AM_CFLAGS = -I$(top_srcdir)/ccan/array_size -Wall -Werror -O2 EXTRA_DIST = \ fake.dts.m4 \ p8-fsi.dts.m4 \ p8-host.dts.m4 \ p8-i2c.dts.m4 \ p8-kernel.dts.m4 \ p8-pib.dts.m4 \ p9-fsi.dtsi.m4 \ p9-host.dts.m4 \ p9-kernel.dts.m4 \ p9-pib.dts.m4 \ p9r-fsi.dts.m4 \ p9w-fsi.dts.m4 \ p9z-fsi.dts.m4 \ template.S \ generate_dt_header.sh \ tests/driver.sh \ tests/run_test.sh \ tests/test_driver.sh \ $(PDBG_TESTS) if TARGET_ARM DT_ARM = p8-fsi.dts p8-i2c.dts p8-kernel.dts \ p9w-fsi.dts p9r-fsi.dts p9z-fsi.dts p9-kernel.dts ARCH_FLAGS="-DTARGET_ARM=1" endif if TARGET_PPC DT_PPC = p8-host.dts p9-host.dts ARCH_FLAGS="-DTARGET_PPC=1" endif DT = fake.dts $(DT_ARM) $(DT_PPC) DT_objects = $(DT:.dts=.dtb.o) DT_headers = $(DT:.dts=.dt.h) optcmd_test_SOURCES = src/optcmd.c src/parsers.c src/tests/optcmd_test.c optcmd_test_CFLAGS = -Wall -g pdbg_SOURCES = \ src/main.c \ src/cfam.c \ src/scom.c \ src/reg.c \ src/mem.c \ src/thread.c \ src/ring.c \ src/htm.c \ src/progress.c \ src/parsers.c \ src/optcmd.c \ src/options_@ARCH@.c \ src/htm.h \ src/main.h \ src/optcmd.h \ src/options.h \ src/parsers.h \ src/progress.h src/main.c: $(DT_headers) pdbg_LDADD = $(DT_objects) libpdbg.la libccan.a \ -L.libs -lrt pdbg_LDFLAGS = -Wl,--whole-archive,-lpdbg,--no-whole-archive pdbg_CFLAGS = -I$(top_srcdir)/libpdbg -Wall -Werror -DGIT_SHA1=\"${GIT_SHA1}\" \ $(ARCH_FLAGS) lib_LTLIBRARIES = libpdbg.la libfdt.la libfdt_la_CFLAGS = -I$(top_srcdir)/libfdt libpdbg_la_CFLAGS = -I$(top_srcdir)/libfdt -Wall -Werror libfdt_la_SOURCES = \ libfdt/fdt.c \ libfdt/fdt_ro.c \ libfdt/fdt_wip.c \ libfdt/fdt_sw.c \ libfdt/fdt_rw.c \ libfdt/fdt_strerror.c \ libfdt/fdt_empty_tree.c \ libfdt/fdt_addresses.c \ libfdt/fdt_overlay.c \ libfdt/fdt.h \ libfdt/libfdt_env.h \ libfdt/libfdt.h \ libfdt/libfdt_internal.h libpdbg_la_SOURCES = \ libpdbg/libpdbg.c \ libpdbg/host.c \ libpdbg/kernel.c \ libpdbg/fake.c \ libpdbg/chip.c \ libpdbg/p8chip.c \ libpdbg/p9chip.c \ libpdbg/bmcfsi.c \ libpdbg/cfam.c \ libpdbg/i2c.c \ libpdbg/adu.c \ libpdbg/device.c \ libpdbg/target.c \ libpdbg/htm.c \ libpdbg/debug.c \ libpdbg/backend.h \ libpdbg/bitutils.h \ libpdbg/compiler.h \ libpdbg/debug.h \ libpdbg/device.h \ libpdbg/operations.h \ libpdbg/libpdbg.h \ libpdbg/target.h libpdbg_la_LIBADD = libfdt.la noinst_LIBRARIES = libccan.a libccan_a_SOURCES = \ ccan/array_size/array_size.h \ ccan/build_assert/build_assert.h \ ccan/check_type/check_type.h \ ccan/container_of/container_of.h \ ccan/cppmagic/cppmagic.h \ ccan/list/list.c \ ccan/list/list.h \ ccan/short_types/short_types.h \ ccan/str/str.c \ ccan/str/str.h libpdbg_test_cflags = -I$(top_srcdir)/libpdbg -I$(top_srcdir)/libfdt libpdbg_test_ldflags = -Wl,--whole-archive,-lpdbg,--no-whole-archive libpdbg_test_ldadd = -L.libs libpdbg.la libpdbg_target_test_SOURCES = src/tests/libpdbg_target_test.c libpdbg_target_test_CFLAGS = $(libpdbg_test_cflags) libpdbg_target_test_LDFLAGS = $(libpdbg_test_ldflags) libpdbg_target_test_LDADD = fake.dtb.o $(libpdbg_test_ldadd) src/tests/libpdbg_target_test.c: fake.dt.h libpdbg_probe_test1_SOURCES = src/tests/libpdbg_probe_test.c libpdbg_probe_test1_CFLAGS = $(libpdbg_test_cflags) -DTEST_ID=1 libpdbg_probe_test1_LDFLAGS = $(libpdbg_test_ldflags) libpdbg_probe_test1_LDADD = fake.dtb.o $(libpdbg_test_ldadd) libpdbg_probe_test2_SOURCES = src/tests/libpdbg_probe_test.c libpdbg_probe_test2_CFLAGS = $(libpdbg_test_cflags) -DTEST_ID=2 libpdbg_probe_test2_LDFLAGS = $(libpdbg_test_ldflags) libpdbg_probe_test2_LDADD = fake.dtb.o $(libpdbg_test_ldadd) libpdbg_probe_test3_SOURCES = src/tests/libpdbg_probe_test.c libpdbg_probe_test3_CFLAGS = $(libpdbg_test_cflags) -DTEST_ID=3 libpdbg_probe_test3_LDFLAGS = $(libpdbg_test_ldflags) libpdbg_probe_test3_LDADD = fake.dtb.o $(libpdbg_test_ldadd) src/tests/libpdbg_probe_test.c: fake.dt.h M4_V = $(M4_V_$(V)) M4_V_ = $(M4_V_$(AM_DEFAULT_VERBOSITY)) M4_V_0 = @echo " M4 " $@; DTC_V = $(DTC_V_$(V)) DTC_V_ = $(DTC_V_$(AM_DEFAULT_VERBOSITY)) DTC_V_0 = @echo " DTC " $@; GEN_V = $(GEN_V_$(V)) GEN_V_ = $(GEN_V_$(AM_DEFAULT_VERBOSITY)) GEN_V_0 = @echo " GEN " $@; %.dts: %.dts.m4 $(M4_V)$(M4) -I$(dir $<) $< | $(DTC) -I dts -O dts > $@ %.dtsi: %.dtsi.m4 $(M4_V)$(M4) -I$(dir $<) $< > $@ p9-fsi.dtsi: p9-fsi.dtsi.m4 p9-pib.dts.m4 p9w-fsi.dts: p9w-fsi.dts.m4 p9-fsi.dtsi p9r-fsi.dts: p9r-fsi.dts.m4 p9-fsi.dtsi p9z-fsi.dts: p9z-fsi.dts.m4 p9-fsi.dtsi %.dtb: %.dts $(DTC_V)$(DTC) -i$(dir $@) -I dts $< -O dtb > $@ %.dt.h: %.dtb $(GEN_V)$(srcdir)/generate_dt_header.sh $< > $@ %.dtb.o: %.dtb $(AM_V_CC)$(CC) -c $(srcdir)/template.S -DSYMBOL_PREFIX=$(shell echo $@ | tr '.-' '_') -DFILENAME=\"$<\" -o $@ MOSTLYCLEANFILES = *.dtb *.dts *.dt.h p9-fsi.dtsi pdbg-2.0/NEWS000066400000000000000000000000001336450571500127650ustar00rootroot00000000000000pdbg-2.0/README000077700000000000000000000000001336450571500144322README.mdustar00rootroot00000000000000pdbg-2.0/README.md000066400000000000000000000261121336450571500135610ustar00rootroot00000000000000# PDBG pdbg is a simple application to allow debugging of the host POWER processors from the BMC. It works in a similar way to JTAG programmers for embedded system development in that it allows you to access GPRs, SPRs and system memory. A remote gdb sever is under development to allow integration with standard debugging tools. ## Building The output of autoconf is not included in the git tree so it needs to be generated using autoreconf. This can be done by running `./bootstrap.sh` in the top level directory. Static linking is supported and can be performed by adding `CFLAGS=-static` to the command line passed to configure. ### Cross compiling for BMC (ARM) ``` apt-get install gcc-arm-linux-gnueabi ./bootstrap.sh ./configure --host=arm-linux-gnueabi CFLAGS="-static" make rsync pdbg root@bmc:/usr/local/bin ``` ## Usage Several backends are supported depending on which system you are using and are selected using the `-b` option: POWER8 Backends: - i2c (default): Uses an i2c connection between BMC and host processor - fsi: Uses a bit-banging GPIO backend which accesses BMC registers directly via /dev/mem/. Requires `-d p8` to specify you are running on a POWER8 system. POWER9 Backends: - kernel (default): Uses the in kernel OpenFSI driver provided by OpenBMC - fsi: Uses a bit-banging GPIO backend which accesses BMC registers directly via /dev/mem. Requiers `-d p9w/p9r/p9z` as appropriate for the system. When using the fsi backend POWER8 AMI based BMC's must first be put into debug mode to allow access to the relevant GPIOs: `ipmitool -H -U -P raw 0x3a 0x01` On POWER9 when using the fsi backend it is also a good idea to put the BMC into debug mode to prevent conflicts with the OpenFSI driver. On the BMC run: `systemctl start fsi-disable.service && systemctl stop host-failure-reboots@0.service` Usage is straight forward. Note that if the binary is not statically linked all commands need to be prefixed with LD\_LIBRARY\_PATH= in addition to the arguments for selecting a backend. ## Examples ``` $ ./pdbg --help Usage: ./pdbg [options] command ... Options: -p, --processor=processor-id -c, --chip=chiplet-id -t, --thread=thread -a, --all Run command on all possible processors/chips/threads (default) -b, --backend=backend fsi: An experimental backend that uses bit-banging to access the host processor via the FSI bus. i2c: The P8 only backend which goes via I2C. kernel: The default backend which goes the kernel FSI driver. -d, --device=backend device For I2C the device node used by the backend to access the bus. For FSI the system board type, one of p8 or p9w Defaults to /dev/i2c4 for I2C -s, --slave-address=backend device address Device slave address to use for the backend. Not used by FSI and defaults to 0x50 for I2C -V, --version -h, --help Commands: getcfam
putcfam
[] getscom
putscom
[] getmem
putmem
getvmem getgpr putgpr getnia putnia getspr putspr start step stop threadstatus probe ``` ### Probe chip/processor/thread numbers ``` $ ./pdbg -a probe BMC GPIO bit-banging FSI master CFAM hMFSI Port p1: POWER FSI2PIB POWER9 ADU c16: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c17: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c18: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c19: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c20: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c21: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c22: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c23: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread p0: POWER FSI2PIB POWER9 ADU c5: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c7: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c14: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c15: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c19: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c20: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c21: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread c22: POWER9 Core t0: POWER9 Thread t1: POWER9 Thread t2: POWER9 Thread t3: POWER9 Thread Note that only selected targets will be shown above. If none are shown try adding '-a' to select all targets ``` Core-IDs are core/chip numbers which should be passed as arguments to `-c` when performing operations such as getgpr that operate on particular cores. Processor-IDs should be passed as arguments to `-p` to operate on different processor chips. Specifying no targets is an error and will result in the following error message: ``` Note that only selected targets will be shown above. If none are shown try adding '-a' to select all targets ``` If the above error occurs even though targets were specified it means the specified targets were not found when probing the system. ### Read SCOM register ``` $ ./pdbg -a getscom 0xf000f p0:0xf000f = 0x220ea04980000000 p1:0xf000f = 0x220ea04980800000 ``` ### Write SCOM register on secondary processor `$ ./pdbg -p1 putscom 0x8013c02 0x0` ### Get thread status ``` $ ./pdbg -a threadstatus p0t: 0 1 2 3 4 5 6 7 c22: A A A A c21: A A A A c20: A A A A c19: A A A A c15: A A A A c14: A A A A c07: A A A A c05: A A A A p1t: 0 1 2 3 4 5 6 7 c23: A A A A c22: A A A A c21: A A A A c20: A A A A c19: A A A A c18: A A A A c17: A A A A c16: A A A A ``` ### Stop thread execution on thread 0-4 of processor 0 core/chip 22 Reading thread register values requires all threads on a given core to be in the quiesced state. ``` $ ./pdbg -p0 -c22 -t0 -t1 -t2 -t3 stop $ ./pdbg -p0 -c22 -t0 -t1 -t2 -t3 threadstatus p0t: 0 1 2 3 4 5 6 7 c22: Q Q Q Q ``` ### Read GPR on thread 0 of processor 0 core/chip 22 ``` $ ./pdbg -p0 -c22 -t0 getgpr 2 p0:c22:t0:gpr02: 0xc000000000f09900 ``` ### Read SPR 8 (LR) on thread 0 of processor 0 core/chip 22 ``` $ ./pdbg -p0 -c22 -t0 getspr 8 p0:c22:t0:spr008: 0xc0000000008a97f0 ``` ### Restart thread 0-4 execution on processor 0 core/chip 22 ``` ./pdbg -p0 -c22 -t0 -t1 -t2 -t3 start ./pdbg -p0 -c22 -t0 -t1 -t2 -t3 threadstatus p0t: 0 1 2 3 4 5 6 7 c22: A A A A ``` ### Write to memory through processor 1 ``` $ echo hello | sudo ./pdbg -p 1 putmem 0x250000001 Wrote 6 bytes starting at 0x0000000250000001 ``` ### Read 6 bytes from memory through processor 1 ``` $ sudo ./pdbg -p 1 getmem 0x250000001 6 | hexdump -C 00000000 68 65 6c 6c 6f 0a |hello.| 00000006 ``` ### Write to cache-inhibited memory through processor 1 ``` $ echo hello | sudo ./pdbg -p 1 putmem -ci 0x3fe88202 Wrote 6 bytes starting at 0x000000003fe88202 ``` ### Read from cache-inhibited memory through processor 1 ``` $ sudo ./pdbg -p 1 getmem -ci 0x3fe88202 6 | hexdump -C 00000000 68 65 6c 6c 6f 0a |hello.| 00000006 ``` ### Read 4 bytes from the hardware RNG ``` $ lsprop /proc/device-tree/hwrng@3ffff40000000/ ibm,chip-id 00000000 compatible "ibm,power-rng" reg 0003ffff 40000000 00000000 00001000 phandle 100003bd (268436413) name "hwrng" $ sudo ./pdbg -p 0 getmem -ci 0x0003ffff40000000 4 |hexdump -C 00000000 01 c0 d1 79 |...y| 00000004 $ sudo ./pdbg -p 0 getmem -ci 0x0003ffff40000000 4 |hexdump -C 00000000 77 9b ab ce |w...| 00000004 $ sudo ./pdbg -p 0 getmem -ci 0x0003ffff40000000 4 |hexdump -C 00000000 66 8d fb 42 |f..B| 00000004 $ sudo ./pdbg -p 0 getmem -ci 0x0003ffff40000000 4 |hexdump -C 00000000 fa 9b e3 44 |...D| 00000004 ``` ### Hardware Trace Macro (HTM) Exploitation of HTM is limited to POWER8 Core from the powerpc host. #### Prerequisites Core HTM on POWER8 needs to run SMT1 and no power save, so you need to run this first: ``` ppc64_cpu --smt=1 for i in /sys/devices/system/cpu/cpu*/cpuidle/state*/disable;do echo 1 > $i;done ``` Also, using HTM requires a kernel built with both `CONFIG_PPC_MEMTRACE=y` (v4.14) and `CONFIG_SCOM_DEBUGFS=y`. debugfs should be mounted at `/sys/kernel/debug`. Ubuntu 18.04 has this by default. #### How to run HTM pdbg provides a `htm` command with a variety of sub-commands. The most useful command is `record` which will start the trace, wait for buffer to fill (~1 sec), stop and then dump the trace to a file (~5 sec). eg. ``` pdbg -l 0 htm core record ``` pdbg -l allows users to specify CPUs using the same addressing as scheme as taskset -c. This can be useful for tracing workloads. eg. ``` taskset -c 0 myworkload sleep 1 pdbg -l 0 htm core record ``` There are also low level htm commands which can also be used: - `start` will configure the hardware and start tracing in wrapping mode. - `stop` will still stop the trace and de-configure the hardware. - `dump` will dump the trace to a file. pdbg-2.0/bootstrap.sh000077500000000000000000000000311336450571500146460ustar00rootroot00000000000000#!/bin/sh autoreconf -i pdbg-2.0/ccan/000077500000000000000000000000001336450571500132045ustar00rootroot00000000000000pdbg-2.0/ccan/array_size/000077500000000000000000000000001336450571500153545ustar00rootroot00000000000000pdbg-2.0/ccan/array_size/LICENSE000066400000000000000000000143571336450571500163730ustar00rootroot00000000000000Statement 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. pdbg-2.0/ccan/array_size/_info000066400000000000000000000020061336450571500163670ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/array_size/array_size.h000066400000000000000000000015711336450571500177010ustar00rootroot00000000000000/* 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 */ pdbg-2.0/ccan/array_size/test/000077500000000000000000000000001336450571500163335ustar00rootroot00000000000000pdbg-2.0/ccan/array_size/test/compile_fail-function-param.c000066400000000000000000000007651336450571500240530ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/array_size/test/compile_fail.c000066400000000000000000000005021336450571500211170ustar00rootroot00000000000000#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 } pdbg-2.0/ccan/array_size/test/run.c000066400000000000000000000014241336450571500173040ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/build_assert/000077500000000000000000000000001336450571500156645ustar00rootroot00000000000000pdbg-2.0/ccan/build_assert/LICENSE000066400000000000000000000143571336450571500167030ustar00rootroot00000000000000Statement 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. pdbg-2.0/ccan/build_assert/_info000066400000000000000000000025061336450571500167040ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/build_assert/build_assert.h000066400000000000000000000023141336450571500205150ustar00rootroot00000000000000/* 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 */ pdbg-2.0/ccan/build_assert/test/000077500000000000000000000000001336450571500166435ustar00rootroot00000000000000pdbg-2.0/ccan/build_assert/test/compile_fail-expr.c000066400000000000000000000002341336450571500224050ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL return BUILD_ASSERT_OR_ZERO(1 == 0); #else return 0; #endif } pdbg-2.0/ccan/build_assert/test/compile_fail.c000066400000000000000000000002071336450571500214310ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL BUILD_ASSERT(1 == 0); #endif return 0; } pdbg-2.0/ccan/build_assert/test/compile_ok.c000066400000000000000000000001641336450571500211310ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { BUILD_ASSERT(1 == 1); return 0; } pdbg-2.0/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c000066400000000000000000000003271336450571500227720ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/check_type/000077500000000000000000000000001336450571500153225ustar00rootroot00000000000000pdbg-2.0/ccan/check_type/LICENSE000066400000000000000000000143571336450571500163410ustar00rootroot00000000000000Statement 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. pdbg-2.0/ccan/check_type/_info000066400000000000000000000015111336450571500163350ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/check_type/check_type.h000066400000000000000000000045051336450571500176150ustar00rootroot00000000000000/* 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 */ pdbg-2.0/ccan/check_type/test/000077500000000000000000000000001336450571500163015ustar00rootroot00000000000000pdbg-2.0/ccan/check_type/test/compile_fail-check_type.c000066400000000000000000000002051336450571500232010ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { #ifdef FAIL check_type(argc, char); #endif return 0; } pdbg-2.0/ccan/check_type/test/compile_fail-check_type_unsigned.c000066400000000000000000000003751336450571500251050ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/check_type/test/compile_fail-check_types_match.c000066400000000000000000000002421336450571500245410ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { unsigned char x = argc; #ifdef FAIL check_types_match(argc, x); #endif return x; } pdbg-2.0/ccan/check_type/test/run.c000066400000000000000000000011151336450571500172470ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/container_of/000077500000000000000000000000001336450571500156525ustar00rootroot00000000000000pdbg-2.0/ccan/container_of/LICENSE000066400000000000000000000143571336450571500166710ustar00rootroot00000000000000Statement 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. pdbg-2.0/ccan/container_of/_info000066400000000000000000000024741336450571500166760ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/container_of/container_of.h000066400000000000000000000061201336450571500204700ustar00rootroot00000000000000/* 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 */ pdbg-2.0/ccan/container_of/test/000077500000000000000000000000001336450571500166315ustar00rootroot00000000000000pdbg-2.0/ccan/container_of/test/compile_fail-bad-type.c000066400000000000000000000005511336450571500231240ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/container_of/test/compile_fail-types.c000066400000000000000000000006321336450571500225630ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/container_of/test/compile_fail-var-types.c000066400000000000000000000007561336450571500233600ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/container_of/test/run.c000066400000000000000000000011161336450571500176000ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/cppmagic/000077500000000000000000000000001336450571500147675ustar00rootroot00000000000000pdbg-2.0/ccan/cppmagic/LICENSE000077700000000000000000000000001336450571500212042../../licenses/BSD-MITustar00rootroot00000000000000pdbg-2.0/ccan/cppmagic/_info000066400000000000000000000012131336450571500160010ustar00rootroot00000000000000#include "config.h" #include #include /** * cppmagic - Abuse of the C preprocessor * * This contains a bunch of fancy macro techniques such as * preprocessor-time evaluated conditionals and (quasi) recursion and * iteration. * * It's based on these articles: * - http://jhnet.co.uk/articles/cpp_magic * - https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms * and code from the Boost C++ library. * * License: BSD-MIT */ int main(int argc, char *argv[]) { /* Expect exactly one argument */ if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { return 0; } return 1; } pdbg-2.0/ccan/cppmagic/cppmagic.h000066400000000000000000000166741336450571500167410ustar00rootroot00000000000000/* MIT (BSD) license - see LICENSE file for details */ #ifndef CCAN_CPPMAGIC_H #define CCAN_CPPMAGIC_H /** * CPPMAGIC_NOTHING - expands to nothing */ #define CPPMAGIC_NOTHING() /** * CPPMAGIC_STRINGIFY - convert arguments to a string literal */ #define _CPPMAGIC_STRINGIFY(...) #__VA_ARGS__ #define CPPMAGIC_STRINGIFY(...) _CPPMAGIC_STRINGIFY(__VA_ARGS__) /** * CPPMAGIC_GLUE2 - glue arguments together * * CPPMAGIC_GLUE2(@a_, @b_) * expands to the expansion of @a_ followed immediately * (combining tokens) by the expansion of @b_ */ #define _CPPMAGIC_GLUE2(a_, b_) a_##b_ #define CPPMAGIC_GLUE2(a_, b_) _CPPMAGIC_GLUE2(a_, b_) /** * CPPMAGIC_1ST - return 1st argument * * CPPMAGIC_1ST(@a_, ...) * expands to the expansion of @a_ */ #define CPPMAGIC_1ST(a_, ...) a_ /** * CPPMAGIC_2ND - return 2nd argument * * CPPMAGIC_2ST(@a_, @b_, ...) * expands to the expansion of @b_ */ #define CPPMAGIC_2ND(a_, b_, ...) b_ /** * CPPMAGIC_ISZERO - is argument '0' * * CPPMAGIC_ISZERO(@a) * expands to '1' if @a is '0', otherwise expands to '0'. */ #define _CPPMAGIC_ISPROBE(...) CPPMAGIC_2ND(__VA_ARGS__, 0) #define _CPPMAGIC_PROBE() $, 1 #define _CPPMAGIC_ISZERO_0 _CPPMAGIC_PROBE() #define CPPMAGIC_ISZERO(a_) \ _CPPMAGIC_ISPROBE(CPPMAGIC_GLUE2(_CPPMAGIC_ISZERO_, a_)) /** * CPPMAGIC_NONZERO - is argument not '0' * * CPPMAGIC_NONZERO(@a) * expands to '0' if @a is '0', otherwise expands to '1'. */ #define CPPMAGIC_NONZERO(a_) CPPMAGIC_ISZERO(CPPMAGIC_ISZERO(a_)) /** * CPPMAGIC_NONEMPTY - does the macro have any arguments? * * CPPMAGIC_NONEMPTY() * expands to '0' * CPPMAGIC_NONEMPTY(@a) * CPPMAGIC_NONEMPTY(@a, ...) * expand to '1' */ #define CHECK_N(x, n, ...) n #define CHECK(...) CHECK_N(__VA_ARGS__, , ) #define PROBE(x) x, 1, #define IS_PAREN(x, ...) CHECK(IS_PAREN_PROBE x) #define IS_PAREN_PROBE(...) PROBE(~) #define _CPPMAGIC_EOA(a_, ...) 0 #define CPPMAGIC_NONEMPTY(...) \ CPPMAGIC_NONZERO(CPPMAGIC_1ST(_CPPMAGIC_EOA IS_PAREN(__VA_ARGS__) __VA_ARGS__)()) /** * CPPMAGIC_ISEMPTY - does the macro have no arguments? * * CPPMAGIC_ISEMPTY() * expands to '1' * CPPMAGIC_ISEMPTY(@a) * CPPMAGIC_ISEMPTY(@a, ...) * expand to '0' */ #define CPPMAGIC_ISEMPTY(...) \ CPPMAGIC_ISZERO(CPPMAGIC_NONEMPTY(__VA_ARGS__)) /* * CPPMAGIC_IFELSE - preprocessor conditional * * CPPMAGIC_IFELSE(@cond)(@if)(@else) * expands to @else if @cond is '0', otherwise expands to @if */ #define _CPPMAGIC_IF_0(...) _CPPMAGIC_IF_0_ELSE #define _CPPMAGIC_IF_1(...) __VA_ARGS__ _CPPMAGIC_IF_1_ELSE #define _CPPMAGIC_IF_0_ELSE(...) __VA_ARGS__ #define _CPPMAGIC_IF_1_ELSE(...) #define _CPPMAGIC_IFELSE(cond_) CPPMAGIC_GLUE2(_CPPMAGIC_IF_, cond_) #define CPPMAGIC_IFELSE(cond_) \ _CPPMAGIC_IFELSE(CPPMAGIC_NONZERO(cond_)) /** * CPPMAGIC_EVAL - force multiple expansion passes * * Forces macros in the arguments to be expanded repeatedly (up to * 1024 times) even when CPP would usually stop expanding. */ #define CPPMAGIC_EVAL1(...) __VA_ARGS__ #define CPPMAGIC_EVAL2(...) \ CPPMAGIC_EVAL1(CPPMAGIC_EVAL1(__VA_ARGS__)) #define CPPMAGIC_EVAL4(...) \ CPPMAGIC_EVAL2(CPPMAGIC_EVAL2(__VA_ARGS__)) #define CPPMAGIC_EVAL8(...) \ CPPMAGIC_EVAL4(CPPMAGIC_EVAL4(__VA_ARGS__)) #define CPPMAGIC_EVAL16(...) \ CPPMAGIC_EVAL8(CPPMAGIC_EVAL8(__VA_ARGS__)) #define CPPMAGIC_EVAL32(...) \ CPPMAGIC_EVAL16(CPPMAGIC_EVAL16(__VA_ARGS__)) #define CPPMAGIC_EVAL64(...) \ CPPMAGIC_EVAL32(CPPMAGIC_EVAL32(__VA_ARGS__)) #define CPPMAGIC_EVAL128(...) \ CPPMAGIC_EVAL64(CPPMAGIC_EVAL64(__VA_ARGS__)) #define CPPMAGIC_EVAL256(...) \ CPPMAGIC_EVAL128(CPPMAGIC_EVAL128(__VA_ARGS__)) #define CPPMAGIC_EVAL512(...) \ CPPMAGIC_EVAL256(CPPMAGIC_EVAL256(__VA_ARGS__)) #define CPPMAGIC_EVAL1024(...) \ CPPMAGIC_EVAL512(CPPMAGIC_EVAL512(__VA_ARGS__)) #define CPPMAGIC_EVAL(...) CPPMAGIC_EVAL1024(__VA_ARGS__) /** * CPPMAGIC_DEFER1, CPPMAGIC_DEFER2 - defer expansion */ #define CPPMAGIC_DEFER1(a_) a_ CPPMAGIC_NOTHING() #define CPPMAGIC_DEFER2(a_) a_ CPPMAGIC_NOTHING CPPMAGIC_NOTHING()() /** * CPPMAGIC_MAP - iterate another macro across arguments * @m: name of a one argument macro * * CPPMAGIC_MAP(@m, @a1, @a2, ... @an) * expands to the expansion of @m(@a1) , @m(@a2) , ... , @m(@an) */ #define _CPPMAGIC_MAP_() _CPPMAGIC_MAP #define _CPPMAGIC_MAP(m_, a_, ...) \ m_(a_) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (, CPPMAGIC_DEFER2(_CPPMAGIC_MAP_)()(m_, __VA_ARGS__)) \ () #define CPPMAGIC_MAP(m_, ...) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (CPPMAGIC_EVAL(_CPPMAGIC_MAP(m_, __VA_ARGS__))) \ () /** * CPPMAGIC_2MAP - iterate another macro across pairs of arguments * @m: name of a two argument macro * * CPPMAGIC_2MAP(@m, @a1, @b1, @a2, @b2, ..., @an, @bn) * expands to the expansion of * @m(@a1, @b1) , @m(@a2, @b2) , ... , @m(@an, @bn) */ #define _CPPMAGIC_2MAP_() _CPPMAGIC_2MAP #define _CPPMAGIC_2MAP(m_, a_, b_, ...) \ m_(a_, b_) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (, CPPMAGIC_DEFER2(_CPPMAGIC_2MAP_)()(m_, __VA_ARGS__)) \ () #define CPPMAGIC_2MAP(m_, ...) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (CPPMAGIC_EVAL(_CPPMAGIC_2MAP(m_, __VA_ARGS__))) \ () /** * CPPMAGIC_MAP_CNT - iterate antoher macro across arguments adding a count * @m: name of a two argument macro * * CPPMAGIC_MAP_CNT(@m, @a1, @a2, ..., @an) * expands to the expansion of * @m(0, @a1) , @m(1, @a2) , ... , @m(n - 1, @an) */ #define _CPPMAGIC_MAP_CNT_() _CPPMAGIC_MAP_CNT #define _CPPMAGIC_MAP_CNT(m_, c_, a_, ...) \ m_(c_, a_) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (, CPPMAGIC_DEFER2(_CPPMAGIC_MAP_CNT_)()(m_, CPPMAGIC_INC(c_), __VA_ARGS__)) \ () #define CPPMAGIC_MAP_CNT(m_, ...) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (CPPMAGIC_EVAL(_CPPMAGIC_MAP_CNT(m_, 0, __VA_ARGS__))) \ () /** * CPPMAGIC_JOIN - separate arguments with given delimiter * @d: delimiter * * CPPMAGIC_JOIN(@d, @a1, @a2, ..., @an) * expands to the expansion of @a1 @d @a2 @d ... @d @an */ #define _CPPMAGIC_JOIN_() _CPPMAGIC_JOIN #define _CPPMAGIC_JOIN(d_, a_, ...) \ a_ \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (d_ CPPMAGIC_DEFER2(_CPPMAGIC_JOIN_)()(d_, __VA_ARGS__)) \ () #define CPPMAGIC_JOIN(d_, ...) \ CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__)) \ (CPPMAGIC_EVAL(_CPPMAGIC_JOIN(d_, __VA_ARGS__))) \ () /** * CPPMAGIC_INC - increment the argument * @d: integer between 0 and 32 to increment * * Increments the integer to a maximum of 32 and saturates */ #define CPPMAGIC_INC(d_) CPPMAGIC_GLUE2(_CPPMAGIC_INC_, d_) #define _CPPMAGIC_INC_0 1 #define _CPPMAGIC_INC_1 2 #define _CPPMAGIC_INC_2 3 #define _CPPMAGIC_INC_3 4 #define _CPPMAGIC_INC_4 5 #define _CPPMAGIC_INC_5 6 #define _CPPMAGIC_INC_6 7 #define _CPPMAGIC_INC_7 8 #define _CPPMAGIC_INC_8 9 #define _CPPMAGIC_INC_9 10 #define _CPPMAGIC_INC_10 11 #define _CPPMAGIC_INC_11 12 #define _CPPMAGIC_INC_12 13 #define _CPPMAGIC_INC_13 14 #define _CPPMAGIC_INC_14 15 #define _CPPMAGIC_INC_15 16 #define _CPPMAGIC_INC_16 17 #define _CPPMAGIC_INC_17 18 #define _CPPMAGIC_INC_18 19 #define _CPPMAGIC_INC_19 20 #define _CPPMAGIC_INC_20 21 #define _CPPMAGIC_INC_21 22 #define _CPPMAGIC_INC_22 23 #define _CPPMAGIC_INC_23 24 #define _CPPMAGIC_INC_24 25 #define _CPPMAGIC_INC_25 26 #define _CPPMAGIC_INC_26 27 #define _CPPMAGIC_INC_27 28 #define _CPPMAGIC_INC_28 29 #define _CPPMAGIC_INC_29 30 #define _CPPMAGIC_INC_30 31 #define _CPPMAGIC_INC_31 32 #define _CPPMAGIC_INC_32 32 #endif /* CCAN_CPPMAGIC_H */ pdbg-2.0/ccan/cppmagic/test/000077500000000000000000000000001336450571500157465ustar00rootroot00000000000000pdbg-2.0/ccan/cppmagic/test/run.c000066400000000000000000000051361336450571500167230ustar00rootroot00000000000000#include "config.h" #include #include #include static inline void check1(const char *orig, const char *expand, const char *match) { ok(strcmp(expand, match) == 0, "%s => %s : %s", orig, expand, match); } #define CHECK1(orig, match) \ check1(#orig, CPPMAGIC_STRINGIFY(orig), match) #define TESTRECURSE() R CPPMAGIC_DEFER1(_TESTRECURSE) ()() #define _TESTRECURSE() TESTRECURSE #define TESTMAP1(x) <> #define TESTMAP2(x) [[ x #define TESTMAP3(x) x ]] #define TEST2MAP(x, y) x ** y int main(void) { plan_tests(42); CHECK1(CPPMAGIC_NOTHING(), ""); CHECK1(CPPMAGIC_GLUE2(a, b), "ab"); CHECK1(CPPMAGIC_1ST(a), "a"); CHECK1(CPPMAGIC_1ST(a, b), "a"); CHECK1(CPPMAGIC_1ST(a, b, c), "a"); CHECK1(CPPMAGIC_2ND(a, b), "b"); CHECK1(CPPMAGIC_2ND(a, b, c), "b"); CHECK1(CPPMAGIC_ISZERO(0), "1"); CHECK1(CPPMAGIC_ISZERO(1), "0"); CHECK1(CPPMAGIC_ISZERO(123), "0"); CHECK1(CPPMAGIC_ISZERO(abc), "0"); CHECK1(CPPMAGIC_NONZERO(0), "0"); CHECK1(CPPMAGIC_NONZERO(1), "1"); CHECK1(CPPMAGIC_NONZERO(123), "1"); CHECK1(CPPMAGIC_NONZERO(abc), "1"); CHECK1(CPPMAGIC_NONEMPTY(), "0"); CHECK1(CPPMAGIC_NONEMPTY(0), "1"); CHECK1(CPPMAGIC_NONEMPTY(a, b, c), "1"); CHECK1(CPPMAGIC_ISEMPTY(), "1"); CHECK1(CPPMAGIC_ISEMPTY(0), "0"); CHECK1(CPPMAGIC_ISEMPTY(a, b, c), "0"); CHECK1(CPPMAGIC_IFELSE(0)(abc)(def), "def"); CHECK1(CPPMAGIC_IFELSE(1)(abc)(def), "abc"); CHECK1(CPPMAGIC_IFELSE(not zero)(abc)(def), "abc"); CHECK1(TESTRECURSE(), "R R _TESTRECURSE ()()"); CHECK1(CPPMAGIC_EVAL1(TESTRECURSE()), "R R R _TESTRECURSE ()()"); CHECK1(CPPMAGIC_EVAL2(TESTRECURSE()), "R R R R R _TESTRECURSE ()()"); CHECK1(CPPMAGIC_MAP(TESTMAP1), ""); CHECK1(CPPMAGIC_MAP(TESTMAP1, a), "<>"); CHECK1(CPPMAGIC_MAP(TESTMAP1, a, b), "<> , <>"); CHECK1(CPPMAGIC_MAP(TESTMAP1, a, b, c), "<> , <> , <>"); CHECK1(CPPMAGIC_2MAP(TEST2MAP), ""); CHECK1(CPPMAGIC_2MAP(TEST2MAP, a, 1), "a ** 1"); CHECK1(CPPMAGIC_2MAP(TEST2MAP, a, 1, b, 2), "a ** 1 , b ** 2"); CHECK1(CPPMAGIC_JOIN(;), ""); CHECK1(CPPMAGIC_JOIN(;, a), "a"); CHECK1(CPPMAGIC_JOIN(;, a, b), "a ; b"); CHECK1(CPPMAGIC_JOIN(;, a, b, c), "a ; b ; c"); /* Check chaining of MAPs */ CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3)), ""); CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3, a)), "[[ a ]]"); CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3, a, b)), "[[ a ]] , [[ b ]]"); CHECK1(CPPMAGIC_MAP(TESTMAP2, CPPMAGIC_MAP(TESTMAP3, a, b, c)), "[[ a ]] , [[ b ]] , [[ c ]]"); /* This exits depending on whether all tests passed */ return exit_status(); } pdbg-2.0/ccan/list/000077500000000000000000000000001336450571500141575ustar00rootroot00000000000000pdbg-2.0/ccan/list/LICENSE000066400000000000000000000017771336450571500152000ustar00rootroot00000000000000Permission 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. pdbg-2.0/ccan/list/_info000066400000000000000000000027011336450571500151740ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/list/list.c000066400000000000000000000017501336450571500153010ustar00rootroot00000000000000/* 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; } pdbg-2.0/ccan/list/list.h000066400000000000000000000355551336450571500153200ustar00rootroot00000000000000/* 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_empty_nocheck - is a list empty? * @h: the list_head * * If the list is empty, returns true. This doesn't perform any * debug check for list consistency, so it can be called without * locks, racing with the list being modified. This is ok for * checks where an incorrect result is not an issue (optimized * bail out path for example). */ static inline bool list_empty_nocheck(const struct list_head *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 which 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. With all that in mind * remember that given the wrong pointer/offset couple this macro will * happily churn all you memory until 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 which 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 */ pdbg-2.0/ccan/list/test/000077500000000000000000000000001336450571500151365ustar00rootroot00000000000000pdbg-2.0/ccan/list/test/compile_ok-constant.c000066400000000000000000000015641336450571500212600ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/list/test/helper.c000066400000000000000000000024261336450571500165650ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/list/test/helper.h000066400000000000000000000003711336450571500165670ustar00rootroot00000000000000/* 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); pdbg-2.0/ccan/list/test/run-check-corrupt.c000066400000000000000000000041151336450571500206560ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/list/test/run-list_del_from-assert.c000066400000000000000000000014361336450571500222310ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/list/test/run-single-eval.c000066400000000000000000000112041336450571500203100ustar00rootroot00000000000000/* 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(); } pdbg-2.0/ccan/list/test/run-with-debug.c000066400000000000000000000001641336450571500201440ustar00rootroot00000000000000/* Just like run.c, but with all debug checks enabled. */ #define CCAN_LIST_DEBUG 1 #include pdbg-2.0/ccan/list/test/run.c000066400000000000000000000112431336450571500161070ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/short_types/000077500000000000000000000000001336450571500155675ustar00rootroot00000000000000pdbg-2.0/ccan/short_types/LICENSE000066400000000000000000000143571336450571500166060ustar00rootroot00000000000000Statement 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. pdbg-2.0/ccan/short_types/_info000066400000000000000000000051631336450571500166110ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/short_types/short_types.h000066400000000000000000000014311336450571500203220ustar00rootroot00000000000000/* 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 */ pdbg-2.0/ccan/short_types/test/000077500000000000000000000000001336450571500165465ustar00rootroot00000000000000pdbg-2.0/ccan/short_types/test/run-endian.c000066400000000000000000000005351336450571500207550ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/short_types/test/run.c000066400000000000000000000010251336450571500175140ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/str/000077500000000000000000000000001336450571500140145ustar00rootroot00000000000000pdbg-2.0/ccan/str/LICENSE000066400000000000000000000143571336450571500150330ustar00rootroot00000000000000Statement 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. pdbg-2.0/ccan/str/_info000066400000000000000000000025101336450571500150270ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/str/str.c000066400000000000000000000004331336450571500147700ustar00rootroot00000000000000/* 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; } pdbg-2.0/ccan/str/str.h000066400000000000000000000064301336450571500150000ustar00rootroot00000000000000/* 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 */ pdbg-2.0/ccan/str/test/000077500000000000000000000000001336450571500147735ustar00rootroot00000000000000pdbg-2.0/ccan/str/test/compile_fail-STR_MAX_CHARS.c000066400000000000000000000004561336450571500216620ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/str/test/compile_fail-isalnum.c000066400000000000000000000005611336450571500212320ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isalpha.c000066400000000000000000000005611336450571500212030ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isascii.c000066400000000000000000000005611336450571500212060ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isblank.c000066400000000000000000000006531336450571500212070ustar00rootroot00000000000000#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 } pdbg-2.0/ccan/str/test/compile_fail-iscntrl.c000066400000000000000000000005611336450571500212400ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isdigit.c000066400000000000000000000005611336450571500212160ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-islower.c000066400000000000000000000005611336450571500212460ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isprint.c000066400000000000000000000005611336450571500212520ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-ispunct.c000066400000000000000000000005611336450571500212470ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isspace.c000066400000000000000000000005611336450571500212110ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isupper.c000066400000000000000000000005611336450571500212510ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-isxdigit.c000066400000000000000000000005631336450571500214100ustar00rootroot00000000000000#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); } pdbg-2.0/ccan/str/test/compile_fail-strchr.c000066400000000000000000000004211336450571500210620ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/str/test/compile_fail-strrchr.c000066400000000000000000000004221336450571500212450ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/str/test/compile_fail-strstr.c000066400000000000000000000004241336450571500211210ustar00rootroot00000000000000#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; } pdbg-2.0/ccan/str/test/debug.c000066400000000000000000000003241336450571500162240ustar00rootroot00000000000000/* 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 pdbg-2.0/ccan/str/test/run-STR_MAX_CHARS.c000066400000000000000000000034131336450571500200370ustar00rootroot00000000000000#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(); } pdbg-2.0/ccan/str/test/run.c000066400000000000000000000051651336450571500157520ustar00rootroot00000000000000#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(); } pdbg-2.0/configure.ac000066400000000000000000000013061336450571500145660ustar00rootroot00000000000000AC_INIT([pdbg], [2.0]) AM_INIT_AUTOMAKE([subdir-objects]) AM_SILENT_RULES([yes]) AC_PROG_CC AC_PROG_LIBTOOL AC_PATH_PROG([M4], [m4]) if test x"$ac_cv_path_M4" = "x" ; then AC_MSG_ERROR([Command 'm4' not found.]) fi AC_SUBST([M4]) AC_PATH_PROG([DTC], [dtc]) if test x"$ac_cv_path_DTC" = x ; then AC_MSG_ERROR([Command 'dtc' not found.]) fi AC_SUBST([DTC]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) AC_LANG(C) case "$host" in arm*-*-*) ARCH="arm" ;; powerpc*-*-*) ARCH="ppc" ;; *) ARCH="def" ;; esac AC_SUBST([ARCH]) AM_CONDITIONAL([TARGET_ARM], [test x"$ARCH" = "xarm"]) AM_CONDITIONAL([TARGET_PPC], [test x"$ARCH" = "xppc"]) AC_OUTPUT pdbg-2.0/fake.dts.m4000066400000000000000000000041551336450571500142460ustar00rootroot00000000000000dnl dnl forloop([var], [start], [end], [iterator]) dnl divert(`-1') define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')') define(`_forloop', `$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@)')') dnl dnl dump_thread([index]) dnl define(`dump_thread', ` thread@$1 { #address-cells = <0x0>; #size-cells = <0x0>; compatible = "ibm,fake-thread"; reg = <0x$1 0x0>; index = <0x$1>; }; ')dnl dnl dnl dump_core_pre([index], [addr]) dnl define(`dump_core_pre', ` core@$2 { #address-cells = <0x1>; #size-cells = <0x1>; compatible = "ibm,fake-core"; reg = <0x$2 0x0>; index = <0x$1>;') dnl dnl dump_core_post() dnl define(`dump_core_post', ` }; ')dnl dnl dnl dump_core([index], [addr], [num_threads]) dnl define(`dump_core', `dump_core_pre(`$1', `$2') forloop(`i', `0', eval(`$3-1'), `dump_thread(i)') dump_core_post()') dnl dnl dump_processor_pre([index], [addr]) dnl define(`dump_processor_pre', ` pib@$2 { #address-cells = <0x1>; #size-cells = <0x1>; compatible = "ibm,fake-pib"; reg = <0x$2 0x0>; index = <0x$1>;') dnl dnl dump_processor_post() dnl define(`dump_processor_post', ` }; ')dnl dnl dnl dump_processor([index], [addr], [num_cores], [num_threads]) dnl define(`dump_processor',dnl `dump_processor_pre(`$1', `$2') forloop(`i', `0', eval(`$3-1'), `dump_core(i, eval($2+(i+1)*10), $4)') dump_processor_post()') dnl dnl dump_fsi_pre([index], [addr]) dnl define(`dump_fsi_pre', ` fsi@$2 { #address-cells = <0x1>; #size-cells = <0x1>; compatible = "ibm,fake-fsi"; reg = <0x$2 0x0>; index = <0x$1>;') dnl dnl dump_fsi_post() dnl define(`dump_fsi_post', ` };') dnl dnl dump_fsi([index], [addr], [num_processors], [num_cores], [num_threads]) dnl define(`dump_fsi', `dump_fsi_pre(`$1', `$2') forloop(`i', `0', eval(`$3-1'), `dump_processor(i, eval(10000+i*1000), $4, $5)') dump_fsi_post()') divert`'dnl /dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x1>; dump_fsi(0, 0, 8, 4, 2) }; pdbg-2.0/generate_dt_header.sh000077500000000000000000000007411336450571500164320ustar00rootroot00000000000000#!/bin/sh if [ $# -ne 1 ] ; then echo "Usage: $0 " exit 1 fi SYMBOL=$(echo "$1" | tr '.-' '_') SYM_START="_binary_${SYMBOL}_o_start" SYM_END="_binary_${SYMBOL}_o_end" SYM_SIZE="_binary_${SYMBOL}_o_size" HEADER="$f.h" cat - < #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) { unsigned absoffset = offset + fdt_off_dt_struct(fdt); if ((absoffset < offset) || ((absoffset + len) < absoffset) || (absoffset + len) > fdt_totalsize(fdt)) return NULL; if (fdt_version(fdt) >= 0x11) if (((offset + len) < offset) || ((offset + len) > fdt_size_dt_struct(fdt))) return NULL; return _fdt_offset_ptr(fdt, offset); } uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) { const fdt32_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_check_prop_offset(const void *fdt, int offset) { if ((offset < 0) || (offset % FDT_TAGSIZE) || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 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; } int fdt_first_subnode(const void *fdt, int offset) { int depth = 0; offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth != 1) return -FDT_ERR_NOTFOUND; return offset; } int fdt_next_subnode(const void *fdt, int offset) { int depth = 1; /* * With respect to the parent, the depth of the next subnode will be * the same as the last. */ do { offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth < 1) return -FDT_ERR_NOTFOUND; } while (depth > 1); 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; } pdbg-2.0/libfdt/fdt.h000066400000000000000000000076661336450571500145110ustar00rootroot00000000000000#ifndef _FDT_H #define _FDT_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. * * 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. */ #ifndef __ASSEMBLY__ struct fdt_header { fdt32_t magic; /* magic word FDT_MAGIC */ fdt32_t totalsize; /* total size of DT block */ fdt32_t off_dt_struct; /* offset to structure */ fdt32_t off_dt_strings; /* offset to strings */ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ fdt32_t version; /* format version */ fdt32_t last_comp_version; /* last compatible version */ /* version 2 fields below */ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */ /* version 3 fields below */ fdt32_t size_dt_strings; /* size of the strings block */ /* version 17 fields below */ fdt32_t size_dt_struct; /* size of the structure block */ }; struct fdt_reserve_entry { fdt64_t address; fdt64_t size; }; struct fdt_node_header { fdt32_t tag; char name[0]; }; struct fdt_property { fdt32_t tag; fdt32_t len; fdt32_t nameoff; char data[0]; }; #endif /* !__ASSEMBLY */ #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(fdt32_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(fdt32_t)) #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) #define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) #define FDT_V16_SIZE FDT_V3_SIZE #define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) #endif /* _FDT_H */ pdbg-2.0/libfdt/fdt_addresses.c000066400000000000000000000062311336450571500165240ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2014 David Gibson * * 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_address_cells(const void *fdt, int nodeoffset) { const fdt32_t *ac; int val; int len; ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len); if (!ac) return 2; if (len != sizeof(*ac)) return -FDT_ERR_BADNCELLS; val = fdt32_to_cpu(*ac); if ((val <= 0) || (val > FDT_MAX_NCELLS)) return -FDT_ERR_BADNCELLS; return val; } int fdt_size_cells(const void *fdt, int nodeoffset) { const fdt32_t *sc; int val; int len; sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len); if (!sc) return 2; if (len != sizeof(*sc)) return -FDT_ERR_BADNCELLS; val = fdt32_to_cpu(*sc); if ((val < 0) || (val > FDT_MAX_NCELLS)) return -FDT_ERR_BADNCELLS; return val; } pdbg-2.0/libfdt/fdt_empty_tree.c000066400000000000000000000055271336450571500167330ustar00rootroot00000000000000/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2012 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_create_empty_tree(void *buf, int bufsize) { int err; err = fdt_create(buf, bufsize); if (err) return err; err = fdt_finish_reservemap(buf); if (err) return err; err = fdt_begin_node(buf, ""); if (err) return err; err = fdt_end_node(buf); if (err) return err; err = fdt_finish(buf); if (err) return err; return fdt_open_into(buf, buf, bufsize); } pdbg-2.0/libfdt/fdt_overlay.c000066400000000000000000000414671336450571500162420ustar00rootroot00000000000000#include "libfdt_env.h" #include #include #include "libfdt_internal.h" /** * overlay_get_target_phandle - retrieves the target phandle of a fragment * @fdto: pointer to the device tree overlay blob * @fragment: node offset of the fragment in the overlay * * overlay_get_target_phandle() retrieves the target phandle of an * overlay fragment when that fragment uses a phandle (target * property) instead of a path (target-path property). * * returns: * the phandle pointed by the target property * 0, if the phandle was not found * -1, if the phandle was malformed */ static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) { const uint32_t *val; int len; val = fdt_getprop(fdto, fragment, "target", &len); if (!val) return 0; if ((len != sizeof(*val)) || (*val == (uint32_t)-1)) return (uint32_t)-1; return fdt32_to_cpu(*val); } /** * overlay_get_target - retrieves the offset of a fragment's target * @fdt: Base device tree blob * @fdto: Device tree overlay blob * @fragment: node offset of the fragment in the overlay * * overlay_get_target() retrieves the target offset in the base * device tree of a fragment, no matter how the actual targetting is * done (through a phandle or a path) * * returns: * the targetted node offset in the base device tree * Negative error code on error */ static int overlay_get_target(const void *fdt, const void *fdto, int fragment) { uint32_t phandle; const char *path; int path_len; /* Try first to do a phandle based lookup */ phandle = overlay_get_target_phandle(fdto, fragment); if (phandle == (uint32_t)-1) return -FDT_ERR_BADPHANDLE; if (phandle) return fdt_node_offset_by_phandle(fdt, phandle); /* And then a path based lookup */ path = fdt_getprop(fdto, fragment, "target-path", &path_len); if (!path) { /* * If we haven't found either a target or a * target-path property in a node that contains a * __overlay__ subnode (we wouldn't be called * otherwise), consider it a improperly written * overlay */ if (path_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; return path_len; } return fdt_path_offset(fdt, path); } /** * overlay_phandle_add_offset - Increases a phandle by an offset * @fdt: Base device tree blob * @node: Device tree overlay blob * @name: Name of the property to modify (phandle or linux,phandle) * @delta: offset to apply * * overlay_phandle_add_offset() increments a node phandle by a given * offset. * * returns: * 0 on success. * Negative error code on error */ static int overlay_phandle_add_offset(void *fdt, int node, const char *name, uint32_t delta) { const uint32_t *val; uint32_t adj_val; int len; val = fdt_getprop(fdt, node, name, &len); if (!val) return len; if (len != sizeof(*val)) return -FDT_ERR_BADPHANDLE; adj_val = fdt32_to_cpu(*val); if ((adj_val + delta) < adj_val) return -FDT_ERR_NOPHANDLES; adj_val += delta; if (adj_val == (uint32_t)-1) return -FDT_ERR_NOPHANDLES; return fdt_setprop_inplace_u32(fdt, node, name, adj_val); } /** * overlay_adjust_node_phandles - Offsets the phandles of a node * @fdto: Device tree overlay blob * @node: Offset of the node we want to adjust * @delta: Offset to shift the phandles of * * overlay_adjust_node_phandles() adds a constant to all the phandles * of a given node. This is mainly use as part of the overlay * application process, when we want to update all the overlay * phandles to not conflict with the overlays of the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_adjust_node_phandles(void *fdto, int node, uint32_t delta) { int child; int ret; ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); if (ret && ret != -FDT_ERR_NOTFOUND) return ret; ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); if (ret && ret != -FDT_ERR_NOTFOUND) return ret; fdt_for_each_subnode(child, fdto, node) { ret = overlay_adjust_node_phandles(fdto, child, delta); if (ret) return ret; } return 0; } /** * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay * @fdto: Device tree overlay blob * @delta: Offset to shift the phandles of * * overlay_adjust_local_phandles() adds a constant to all the * phandles of an overlay. This is mainly use as part of the overlay * application process, when we want to update all the overlay * phandles to not conflict with the overlays of the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) { /* * Start adjusting the phandles from the overlay root */ return overlay_adjust_node_phandles(fdto, 0, delta); } /** * overlay_update_local_node_references - Adjust the overlay references * @fdto: Device tree overlay blob * @tree_node: Node offset of the node to operate on * @fixup_node: Node offset of the matching local fixups node * @delta: Offset to shift the phandles of * * overlay_update_local_nodes_references() update the phandles * pointing to a node within the device tree overlay by adding a * constant delta. * * This is mainly used as part of a device tree application process, * where you want the device tree overlays phandles to not conflict * with the ones from the base device tree before merging them. * * returns: * 0 on success * Negative error code on failure */ static int overlay_update_local_node_references(void *fdto, int tree_node, int fixup_node, uint32_t delta) { int fixup_prop; int fixup_child; int ret; fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { const uint32_t *fixup_val; const char *tree_val; const char *name; int fixup_len; int tree_len; int i; fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, &name, &fixup_len); if (!fixup_val) return fixup_len; if (fixup_len % sizeof(uint32_t)) return -FDT_ERR_BADOVERLAY; tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); if (!tree_val) { if (tree_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; return tree_len; } for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { uint32_t adj_val, poffset; poffset = fdt32_to_cpu(fixup_val[i]); /* * phandles to fixup can be unaligned. * * Use a memcpy for the architectures that do * not support unaligned accesses. */ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); adj_val = fdt32_to_cpu(adj_val); adj_val += delta; adj_val = cpu_to_fdt32(adj_val); ret = fdt_setprop_inplace_namelen_partial(fdto, tree_node, name, strlen(name), poffset, &adj_val, sizeof(adj_val)); if (ret == -FDT_ERR_NOSPACE) return -FDT_ERR_BADOVERLAY; if (ret) return ret; } } fdt_for_each_subnode(fixup_child, fdto, fixup_node) { const char *fixup_child_name = fdt_get_name(fdto, fixup_child, NULL); int tree_child; tree_child = fdt_subnode_offset(fdto, tree_node, fixup_child_name); if (ret == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; if (tree_child < 0) return tree_child; ret = overlay_update_local_node_references(fdto, tree_child, fixup_child, delta); if (ret) return ret; } return 0; } /** * overlay_update_local_references - Adjust the overlay references * @fdto: Device tree overlay blob * @delta: Offset to shift the phandles of * * overlay_update_local_references() update all the phandles pointing * to a node within the device tree overlay by adding a constant * delta to not conflict with the base overlay. * * This is mainly used as part of a device tree application process, * where you want the device tree overlays phandles to not conflict * with the ones from the base device tree before merging them. * * returns: * 0 on success * Negative error code on failure */ static int overlay_update_local_references(void *fdto, uint32_t delta) { int fixups; fixups = fdt_path_offset(fdto, "/__local_fixups__"); if (fixups < 0) { /* There's no local phandles to adjust, bail out */ if (fixups == -FDT_ERR_NOTFOUND) return 0; return fixups; } /* * Update our local references from the root of the tree */ return overlay_update_local_node_references(fdto, 0, fixups, delta); } /** * overlay_fixup_one_phandle - Set an overlay phandle to the base one * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @path: Path to a node holding a phandle in the overlay * @path_len: number of path characters to consider * @name: Name of the property holding the phandle reference in the overlay * @name_len: number of name characters to consider * @poffset: Offset within the overlay property where the phandle is stored * @label: Label of the node referenced by the phandle * * overlay_fixup_one_phandle() resolves an overlay phandle pointing to * a node in the base device tree. * * This is part of the device tree overlay application process, when * you want all the phandles in the overlay to point to the actual * base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_one_phandle(void *fdt, void *fdto, int symbols_off, const char *path, uint32_t path_len, const char *name, uint32_t name_len, int poffset, const char *label) { const char *symbol_path; uint32_t phandle; int symbol_off, fixup_off; int prop_len; if (symbols_off < 0) return symbols_off; symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len); if (!symbol_path) return prop_len; symbol_off = fdt_path_offset(fdt, symbol_path); if (symbol_off < 0) return symbol_off; phandle = fdt_get_phandle(fdt, symbol_off); if (!phandle) return -FDT_ERR_NOTFOUND; fixup_off = fdt_path_offset_namelen(fdto, path, path_len); if (fixup_off == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; if (fixup_off < 0) return fixup_off; phandle = cpu_to_fdt32(phandle); return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, name, name_len, poffset, &phandle, sizeof(phandle)); }; /** * overlay_fixup_phandle - Set an overlay phandle to the base one * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @property: Property offset in the overlay holding the list of fixups * * overlay_fixup_phandle() resolves all the overlay phandles pointed * to in a __fixups__ property, and updates them to match the phandles * in use in the base device tree. * * This is part of the device tree overlay application process, when * you want all the phandles in the overlay to point to the actual * base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, int property) { const char *value; const char *label; int len; value = fdt_getprop_by_offset(fdto, property, &label, &len); if (!value) { if (len == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; return len; } do { const char *path, *name, *fixup_end; const char *fixup_str = value; uint32_t path_len, name_len; uint32_t fixup_len; char *sep, *endptr; int poffset, ret; fixup_end = memchr(value, '\0', len); if (!fixup_end) return -FDT_ERR_BADOVERLAY; fixup_len = fixup_end - fixup_str; len -= fixup_len + 1; value += fixup_len + 1; path = fixup_str; sep = memchr(fixup_str, ':', fixup_len); if (!sep || *sep != ':') return -FDT_ERR_BADOVERLAY; path_len = sep - path; if (path_len == (fixup_len - 1)) return -FDT_ERR_BADOVERLAY; fixup_len -= path_len + 1; name = sep + 1; sep = memchr(name, ':', fixup_len); if (!sep || *sep != ':') return -FDT_ERR_BADOVERLAY; name_len = sep - name; if (!name_len) return -FDT_ERR_BADOVERLAY; poffset = strtoul(sep + 1, &endptr, 10); if ((*endptr != '\0') || (endptr <= (sep + 1))) return -FDT_ERR_BADOVERLAY; ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, path, path_len, name, name_len, poffset, label); if (ret) return ret; } while (len > 0); return 0; } /** * overlay_fixup_phandles - Resolve the overlay phandles to the base * device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_fixup_phandles() resolves all the overlay phandles pointing * to nodes in the base device tree. * * This is one of the steps of the device tree overlay application * process, when you want all the phandles in the overlay to point to * the actual base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_phandles(void *fdt, void *fdto) { int fixups_off, symbols_off; int property; /* We can have overlays without any fixups */ fixups_off = fdt_path_offset(fdto, "/__fixups__"); if (fixups_off == -FDT_ERR_NOTFOUND) return 0; /* nothing to do */ if (fixups_off < 0) return fixups_off; /* And base DTs without symbols */ symbols_off = fdt_path_offset(fdt, "/__symbols__"); if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) return symbols_off; fdt_for_each_property_offset(property, fdto, fixups_off) { int ret; ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); if (ret) return ret; } return 0; } /** * overlay_apply_node - Merges a node into the base device tree * @fdt: Base Device Tree blob * @target: Node offset in the base device tree to apply the fragment to * @fdto: Device tree overlay blob * @node: Node offset in the overlay holding the changes to merge * * overlay_apply_node() merges a node into a target base device tree * node pointed. * * This is part of the final step in the device tree overlay * application process, when all the phandles have been adjusted and * resolved and you just have to merge overlay into the base device * tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_apply_node(void *fdt, int target, void *fdto, int node) { int property; int subnode; fdt_for_each_property_offset(property, fdto, node) { const char *name; const void *prop; int prop_len; int ret; prop = fdt_getprop_by_offset(fdto, property, &name, &prop_len); if (prop_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; if (prop_len < 0) return prop_len; ret = fdt_setprop(fdt, target, name, prop, prop_len); if (ret) return ret; } fdt_for_each_subnode(subnode, fdto, node) { const char *name = fdt_get_name(fdto, subnode, NULL); int nnode; int ret; nnode = fdt_add_subnode(fdt, target, name); if (nnode == -FDT_ERR_EXISTS) { nnode = fdt_subnode_offset(fdt, target, name); if (nnode == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; } if (nnode < 0) return nnode; ret = overlay_apply_node(fdt, nnode, fdto, subnode); if (ret) return ret; } return 0; } /** * overlay_merge - Merge an overlay into its base device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_merge() merges an overlay into its base device tree. * * This is the final step in the device tree overlay application * process, when all the phandles have been adjusted and resolved and * you just have to merge overlay into the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_merge(void *fdt, void *fdto) { int fragment; fdt_for_each_subnode(fragment, fdto, 0) { int overlay; int target; int ret; /* * Each fragments will have an __overlay__ node. If * they don't, it's not supposed to be merged */ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); if (overlay == -FDT_ERR_NOTFOUND) continue; if (overlay < 0) return overlay; target = overlay_get_target(fdt, fdto, fragment); if (target < 0) return target; ret = overlay_apply_node(fdt, target, fdto, overlay); if (ret) return ret; } return 0; } int fdt_overlay_apply(void *fdt, void *fdto) { uint32_t delta = fdt_get_max_phandle(fdt); int ret; FDT_CHECK_HEADER(fdt); FDT_CHECK_HEADER(fdto); ret = overlay_adjust_local_phandles(fdto, delta); if (ret) goto err; ret = overlay_update_local_references(fdto, delta); if (ret) goto err; ret = overlay_fixup_phandles(fdt, fdto); if (ret) goto err; ret = overlay_merge(fdt, fdto); if (ret) goto err; /* * The overlay has been damaged, erase its magic. */ fdt_set_magic(fdto, ~0); return 0; err: /* * The overlay might have been damaged, erase its magic. */ fdt_set_magic(fdto, ~0); /* * The base device tree might have been damaged, erase its * magic. */ fdt_set_magic(fdt, ~0); return ret; } pdbg-2.0/libfdt/fdt_ro.c000066400000000000000000000400601336450571500151650ustar00rootroot00000000000000/* * 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); } uint32_t fdt_get_max_phandle(const void *fdt) { uint32_t max_phandle = 0; int offset; for (offset = fdt_next_node(fdt, -1, NULL);; offset = fdt_next_node(fdt, offset, NULL)) { uint32_t phandle; if (offset == -FDT_ERR_NOTFOUND) return max_phandle; if (offset < 0) return (uint32_t)-1; phandle = fdt_get_phandle(fdt, offset); if (phandle == (uint32_t)-1) continue; if (phandle > max_phandle) max_phandle = phandle; } return 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; } static int _nextprop(const void *fdt, int offset) { uint32_t tag; int nextoffset; do { tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_END: if (nextoffset >= 0) return -FDT_ERR_BADSTRUCTURE; else return nextoffset; case FDT_PROP: return offset; } offset = nextoffset; } while (tag == FDT_NOP); return -FDT_ERR_NOTFOUND; } 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_subnode_offset(const void *fdt, int parentoffset, const char *name) { return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) { const char *end = path + namelen; const char *p = path; int offset = 0; FDT_CHECK_HEADER(fdt); /* see if we have an alias */ if (*path != '/') { const char *q = memchr(path, '/', end - p); 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 < end) { const char *q; while (*p == '/') { p++; if (p == end) return offset; } q = memchr(p, '/', end - p); if (! q) q = end; offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); if (offset < 0) return offset; p = q; } return offset; } int fdt_path_offset(const void *fdt, const char *path) { return fdt_path_offset_namelen(fdt, path, strlen(path)); } 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; } int fdt_first_property_offset(const void *fdt, int nodeoffset) { int offset; if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) return offset; return _nextprop(fdt, offset); } int fdt_next_property_offset(const void *fdt, int offset) { if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) return offset; return _nextprop(fdt, offset); } const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp) { int err; const struct fdt_property *prop; if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { if (lenp) *lenp = err; return NULL; } prop = _fdt_offset_ptr(fdt, offset); if (lenp) *lenp = fdt32_to_cpu(prop->len); return prop; } const struct fdt_property *fdt_get_property_namelen(const void *fdt, int offset, const char *name, int namelen, int *lenp) { for (offset = fdt_first_property_offset(fdt, offset); (offset >= 0); (offset = fdt_next_property_offset(fdt, offset))) { const struct fdt_property *prop; if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { offset = -FDT_ERR_INTERNAL; break; } if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), name, namelen)) return prop; } if (lenp) *lenp = offset; 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_by_offset(const void *fdt, int offset, const char **namep, int *lenp) { const struct fdt_property *prop; prop = fdt_get_property_by_offset(fdt, offset, lenp); if (!prop) return NULL; if (namep) *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 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 fdt32_t *php; int len; /* FIXME: This is a bit sub-optimal, since we potentially scan * over all the properties twice. */ php = fdt_getprop(fdt, nodeoffset, "phandle", &len); if (!php || (len != sizeof(*php))) { 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) { int offset; if ((phandle == 0) || (phandle == -1)) return -FDT_ERR_BADPHANDLE; FDT_CHECK_HEADER(fdt); /* FIXME: The algorithm here is pretty horrible: we * potentially scan each property of a node in * fdt_get_phandle(), 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, -1, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { if (fdt_get_phandle(fdt, offset) == phandle) return offset; } return offset; /* error from fdt_next_node() */ } 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_stringlist_count(const void *fdt, int nodeoffset, const char *property) { const char *list, *end; int length, count = 0; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length; end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE; list += length; count++; } return count; } int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string) { int length, len, idx = 0; const char *list, *end; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length; len = strlen(string) + 1; end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE; if (length == len && memcmp(list, string, length) == 0) return idx; list += length; idx++; } return -FDT_ERR_NOTFOUND; } const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int idx, int *lenp) { const char *list, *end; int length; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) { if (lenp) *lenp = length; return NULL; } end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) { if (lenp) *lenp = -FDT_ERR_BADVALUE; return NULL; } if (idx == 0) { if (lenp) *lenp = length - 1; return list; } list += length; idx--; } if (lenp) *lenp = -FDT_ERR_NOTFOUND; return NULL; } 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; return !fdt_stringlist_contains(prop, len, compatible); } 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() */ } pdbg-2.0/libfdt/fdt_rw.c000066400000000000000000000313411336450571500151770ustar00rootroot00000000000000/* * 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 ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) 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); FDT_RW_CHECK_HEADER(fdt); if (n >= fdt_num_mem_rsv(fdt)) return -FDT_ERR_NOTFOUND; return _fdt_splice_mem_rsv(fdt, re, 1, 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_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { struct fdt_property *prop; int err, oldlen, newlen; FDT_RW_CHECK_HEADER(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); if (prop) { newlen = len + oldlen; err = _fdt_splice_struct(fdt, prop->data, FDT_TAGALIGN(oldlen), FDT_TAGALIGN(newlen)); if (err) return err; prop->len = cpu_to_fdt32(newlen); memcpy(prop->data + oldlen, val, len); } else { 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; fdt32_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 = (fdt32_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; } pdbg-2.0/libfdt/fdt_strerror.c000066400000000000000000000070341336450571500164330ustar00rootroot00000000000000/* * 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" 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_BADPHANDLE), 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), FDT_ERRTABENT(FDT_ERR_INTERNAL), FDT_ERRTABENT(FDT_ERR_BADNCELLS), FDT_ERRTABENT(FDT_ERR_BADVALUE), FDT_ERRTABENT(FDT_ERR_BADOVERLAY), FDT_ERRTABENT(FDT_ERR_NOPHANDLES), }; #define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) const char *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 ""; } pdbg-2.0/libfdt/fdt_sw.c000066400000000000000000000173101336450571500152000ustar00rootroot00000000000000/* * 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_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_resize(void *fdt, void *buf, int bufsize) { size_t headsize, tailsize; char *oldtail, *newtail; FDT_SW_CHECK_HEADER(fdt); headsize = fdt_off_dt_struct(fdt); tailsize = fdt_size_dt_strings(fdt); if ((headsize + tailsize) > bufsize) return -FDT_ERR_NOSPACE; oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; newtail = (char *)buf + bufsize - tailsize; /* Two cases to avoid clobbering data if the old and new * buffers partially overlap */ if (buf <= fdt) { memmove(buf, fdt, headsize); memmove(newtail, oldtail, tailsize); } else { memmove(newtail, oldtail, tailsize); memmove(buf, fdt, headsize); } fdt_set_off_dt_strings(buf, bufsize); fdt_set_totalsize(buf, 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) { fdt32_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_finish(void *fdt) { char *p = (char *)fdt; fdt32_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; } pdbg-2.0/libfdt/fdt_wip.c000066400000000000000000000100771336450571500153510ustar00rootroot00000000000000/* * 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_namelen_partial(void *fdt, int nodeoffset, const char *name, int namelen, uint32_t idx, const void *val, int len) { void *propval; int proplen; propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, &proplen); if (!propval) return proplen; if (proplen < (len + idx)) return -FDT_ERR_NOSPACE; memcpy((char *)propval + idx, val, len); return 0; } int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len) { const void *propval; int proplen; propval = fdt_getprop(fdt, nodeoffset, name, &proplen); if (! propval) return proplen; if (proplen != len) return -FDT_ERR_NOSPACE; return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, strlen(name), 0, val, len); } static void _fdt_nop_region(void *start, int len) { fdt32_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; } pdbg-2.0/libfdt/libfdt.h000066400000000000000000002011441336450571500151630ustar00rootroot00000000000000#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 #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. * This can be caused either by an invalid phandle property * length, or the phandle value was either 0 or -1, which 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. */ /* Errors in device tree content */ #define FDT_ERR_BADNCELLS 14 /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells * or similar property with a bad format or value */ #define FDT_ERR_BADVALUE 15 /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected * value. For example: a property expected to contain a string list * is not NUL-terminated within the length of its value. */ #define FDT_ERR_BADOVERLAY 16 /* FDT_ERR_BADOVERLAY: The device tree overlay, while * correctly structured, cannot be applied due to some * unexpected or missing value, property or node. */ #define FDT_ERR_NOPHANDLES 17 /* FDT_ERR_NOPHANDLES: The device tree doesn't have any * phandle available anymore without causing an overflow */ #define FDT_ERR_MAX 17 /**********************************************************************/ /* 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); /** * fdt_first_subnode() - get offset of first direct subnode * * @fdt: FDT blob * @offset: Offset of node to check * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none */ int fdt_first_subnode(const void *fdt, int offset); /** * fdt_next_subnode() - get offset of next direct subnode * * After first calling fdt_first_subnode(), call this function repeatedly to * get direct subnodes of a parent node. * * @fdt: FDT blob * @offset: Offset of previous subnode * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more * subnodes */ int fdt_next_subnode(const void *fdt, int offset); /** * fdt_for_each_subnode - iterate over all subnodes of a parent * * @node: child node (int, lvalue) * @fdt: FDT blob (const void *) * @parent: parent node (int) * * This is actually a wrapper around a for loop and would be used like so: * * fdt_for_each_subnode(node, fdt, parent) { * Use node * ... * } * * if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) { * Error handling * } * * Note that this is implemented as a macro and @node is used as * iterator in the loop. The parent variable be constant or even a * literal. * */ #define fdt_for_each_subnode(node, fdt, parent) \ for (node = fdt_first_subnode(fdt, parent); \ node >= 0; \ node = fdt_next_subnode(fdt, node)) /**********************************************************************/ /* 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_get_max_phandle - retrieves the highest phandle in a tree * @fdt: pointer to the device tree blob * * fdt_get_max_phandle retrieves the highest phandle in the given * device tree. This will ignore badly formatted phandles, or phandles * with a value of 0 or -1. * * returns: * the highest phandle on success * 0, if no phandle was found in the device tree * -1, if an error occurred */ uint32_t fdt_get_max_phandle(const void *fdt); /** * 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_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_path_offset_namelen - find a tree node by its full path * @fdt: pointer to the device tree blob * @path: full path of the node to locate * @namelen: number of characters of path to consider * * Identical to fdt_path_offset(), but only consider the first namelen * characters of path as the path name. */ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); /** * 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_first_property_offset - find the offset of a node's first property * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of a node * * fdt_first_property_offset() finds the first property of the node at * the given structure block offset. * * returns: * structure block offset of the property (>=0), on success * -FDT_ERR_NOTFOUND, if the requested node has no properties * -FDT_ERR_BADOFFSET, if nodeoffset 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_first_property_offset(const void *fdt, int nodeoffset); /** * fdt_next_property_offset - step through a node's properties * @fdt: pointer to the device tree blob * @offset: structure block offset of a property * * fdt_next_property_offset() finds the property immediately after the * one at the given structure block offset. This will be a property * of the same node as the given property. * * returns: * structure block offset of the next property (>=0), on success * -FDT_ERR_NOTFOUND, if the given property is the last in its node * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_next_property_offset(const void *fdt, int offset); /** * fdt_for_each_property_offset - iterate over all properties of a node * * @property_offset: property offset (int, lvalue) * @fdt: FDT blob (const void *) * @node: node offset (int) * * This is actually a wrapper around a for loop and would be used like so: * * fdt_for_each_property_offset(property, fdt, node) { * Use property * ... * } * * if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) { * Error handling * } * * Note that this is implemented as a macro and property is used as * iterator in the loop. The node variable can be constant or even a * literal. */ #define fdt_for_each_property_offset(property, fdt, node) \ for (property = fdt_first_property_offset(fdt, node); \ property >= 0; \ property = fdt_next_property_offset(fdt, property)) /** * fdt_get_property_by_offset - retrieve the property at a given offset * @fdt: pointer to the device tree blob * @offset: offset of the property to retrieve * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_property_by_offset() retrieves a pointer to the * fdt_property structure within the device tree blob at the given * offset. 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_BADOFFSET, nodeoffset did not point to FDT_PROP 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_by_offset(const void *fdt, int offset, 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(), 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_by_offset - retrieve the value of a property at a given offset * @fdt: pointer to the device tree blob * @ffset: offset of the property to read * @namep: pointer to a string variable (will be overwritten) or NULL * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop_by_offset() retrieves a pointer to the value of the * property at structure block offset 'offset' (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. If namep is non-NULL, * the property's namne will also be returned in the char * pointed to * by namep (this will be a pointer to within the device tree's string * block, not a new copy of the name). * * returns: * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * if namep is non-NULL *namep contiains a pointer to the property * name. * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *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); static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, namelen, 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', if 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); /** * fdt_stringlist_contains - check a string list property for a string * @strlist: Property containing a list of strings to check * @listlen: Length of property * @str: String to search for * * This is a utility function provided for convenience. The list contains * one or more strings, each terminated by \0, as is found in a device tree * "compatible" property. * * @return: 1 if the string is found in the list, 0 not found, or invalid list */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); /** * fdt_stringlist_count - count the number of strings in a string list * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @return: * the number of strings in the given property * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist */ int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); /** * fdt_stringlist_search - find a string in a string list and return its index * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @string: string to look up in the string list * * Note that it is possible for this function to succeed on property values * that are not NUL-terminated. That's because the function will stop after * finding the first occurrence of @string. This can for example happen with * small-valued cell properties, such as #address-cells, when searching for * the empty string. * * @return: * the index of the string in the list of strings * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist or does not contain * the given string */ int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string); /** * fdt_stringlist_get() - obtain the string at a given index in a string list * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @index: index of the string to return * @lenp: return location for the string length or an error code on failure * * Note that this will successfully extract strings from properties with * non-NUL-terminated values. For example on small-valued cell properties * this function will return the empty string. * * If non-NULL, the length of the string (on success) or a negative error-code * (on failure) will be stored in the integer pointer to by lenp. * * @return: * A pointer to the string at the given index in the string list or NULL on * failure. On success the length of the string will be stored in the memory * location pointed to by the lenp parameter, if non-NULL. On failure one of * the following negative error codes will be returned in the lenp parameter * (if non-NULL): * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist */ const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int index, int *lenp); /**********************************************************************/ /* Read-only functions (addressing related) */ /**********************************************************************/ /** * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells * * This is the maximum value for #address-cells, #size-cells and * similar properties that will be processed by libfdt. IEE1275 * requires that OF implementations handle values up to 4. * Implementations may support larger values, but in practice higher * values aren't used. */ #define FDT_MAX_NCELLS 4 /** * fdt_address_cells - retrieve address size for a bus represented in the tree * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to find the address size for * * When the node has a valid #address-cells property, returns its value. * * returns: * 0 <= n < FDT_MAX_NCELLS, on success * 2, if the node has no #address-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #address-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_address_cells(const void *fdt, int nodeoffset); /** * fdt_size_cells - retrieve address range size for a bus represented in the * tree * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to find the address range size for * * When the node has a valid #size-cells property, returns its value. * * returns: * 0 <= n < FDT_MAX_NCELLS, on success * 2, if the node has no #address-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #size-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_size_cells(const void *fdt, int nodeoffset); /**********************************************************************/ /* Write-in-place functions */ /**********************************************************************/ /** * fdt_setprop_inplace_namelen_partial - 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 * @namelen: number of characters of name to consider * @idx: index of the property to change in the array * @val: pointer to data to replace the property value with * @len: length of the property value * * Identical to fdt_setprop_inplace(), but modifies the given property * starting from the given index, and using only the first characters * of the name. It is useful when you want to manipulate only one value of * an array and you have a string that doesn't end with \0. */ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, const char *name, int namelen, uint32_t idx, const void *val, int len); /** * 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_u32 - change the value of a 32-bit integer 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: 32-bit integer value to replace the property with * * fdt_setprop_inplace_u32() replaces the value of a given property * with the 32-bit integer 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_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_inplace_u64 - change the value of a 64-bit integer 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: 64-bit integer value to replace the property with * * fdt_setprop_inplace_u64() replaces the value of a given property * with the 64-bit integer 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 * 8. * * 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 8 * -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_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_inplace_cell - change the value of a single-cell property * * This is an alternative name for fdt_setprop_inplace_u32() */ static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_setprop_inplace_u32(fdt, nodeoffset, name, 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_resize(void *fdt, 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_u32(void *fdt, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) { return fdt_property_u32(fdt, name, val); } #define fdt_property_string(fdt, name, str) \ fdt_property(fdt, name, str, strlen(str)+1) int fdt_end_node(void *fdt); int fdt_finish(void *fdt); /**********************************************************************/ /* Read-write functions */ /**********************************************************************/ int fdt_create_empty_tree(void *buf, int bufsize); 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_u32 - set a property to a 32-bit integer * @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_u32() sets the value of the named property in the given * node to the given 32-bit integer 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_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_u64 - set a property to a 64-bit integer * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value for the property (native endian) * * fdt_setprop_u64() sets the value of the named property in the given * node to the given 64-bit integer 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_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_cell - set a property to a single cell value * * This is an alternative name for fdt_setprop_u32() */ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_setprop_u32(fdt, nodeoffset, name, 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_appendprop - append to or create a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to append to * @val: pointer to data to append to the property value * @len: length of the data to append to the property value * * fdt_appendprop() appends the value to the named property in the * given node, creating the property if it does not already exist. * * This function may insert data into 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_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_appendprop_u32 - append a 32-bit integer value to 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: 32-bit integer value to append to the property (native endian) * * fdt_appendprop_u32() appends the given 32-bit integer value * (converting to big-endian if necessary) to the value of the named * property in the given node, or creates a new property with that * value if it does not already exist. * * This function may insert data into 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_appendprop_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_appendprop_u64 - append a 64-bit integer value to 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: 64-bit integer value to append to the property (native endian) * * fdt_appendprop_u64() appends the given 64-bit integer value * (converting to big-endian if necessary) to the value of the named * property in the given node, or creates a new property with that * value if it does not already exist. * * This function may insert data into 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_appendprop_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_appendprop_cell - append a single cell value to a property * * This is an alternative name for fdt_appendprop_u32() */ static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_appendprop_u32(fdt, nodeoffset, name, val); } /** * fdt_appendprop_string - append a string to 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 * @str: string value to append to the property * * fdt_appendprop_string() appends the given string to the value of * the named property in the given node, or creates a new property * with that value if it does not already exist. * * This function may insert data into 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_appendprop_string(fdt, nodeoffset, name, str) \ fdt_appendprop((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); /** * fdt_overlay_apply - Applies a DT overlay on a base DT * @fdt: pointer to the base device tree blob * @fdto: pointer to the device tree overlay blob * * fdt_overlay_apply() will apply the given device tree overlay on the * given base device tree. * * Expect the base device tree to be modified, even if the function * returns an error. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there's not enough space in the base device tree * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or * properties in the base DT * -FDT_ERR_BADPHANDLE, * -FDT_ERR_BADOVERLAY, * -FDT_ERR_NOPHANDLES, * -FDT_ERR_INTERNAL, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADOFFSET, * -FDT_ERR_BADPATH, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADSTATE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_overlay_apply(void *fdt, void *fdto); /**********************************************************************/ /* Debugging / informational functions */ /**********************************************************************/ const char *fdt_strerror(int errval); #endif /* _LIBFDT_H */ pdbg-2.0/libfdt/libfdt_env.h000066400000000000000000000077621336450571500160450ustar00rootroot00000000000000#ifndef _LIBFDT_ENV_H #define _LIBFDT_ENV_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. * * 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 #include #ifdef __CHECKER__ #define __force __attribute__((force)) #define __bitwise __attribute__((bitwise)) #else #define __force #define __bitwise #endif typedef uint16_t __bitwise fdt16_t; typedef uint32_t __bitwise fdt32_t; typedef uint64_t __bitwise fdt64_t; #define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) #define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) #define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) #define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) static inline uint16_t fdt16_to_cpu(fdt16_t x) { return (__force uint16_t)CPU_TO_FDT16(x); } static inline fdt16_t cpu_to_fdt16(uint16_t x) { return (__force fdt16_t)CPU_TO_FDT16(x); } static inline uint32_t fdt32_to_cpu(fdt32_t x) { return (__force uint32_t)CPU_TO_FDT32(x); } static inline fdt32_t cpu_to_fdt32(uint32_t x) { return (__force fdt32_t)CPU_TO_FDT32(x); } static inline uint64_t fdt64_to_cpu(fdt64_t x) { return (__force uint64_t)CPU_TO_FDT64(x); } static inline fdt64_t cpu_to_fdt64(uint64_t x) { return (__force fdt64_t)CPU_TO_FDT64(x); } #undef CPU_TO_FDT64 #undef CPU_TO_FDT32 #undef CPU_TO_FDT16 #undef EXTRACT_BYTE #endif /* _LIBFDT_ENV_H */ pdbg-2.0/libfdt/libfdt_internal.h000066400000000000000000000071321336450571500170600ustar00rootroot00000000000000#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); int _fdt_check_prop_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 */ pdbg-2.0/libfdt/version.lds000066400000000000000000000024551336450571500157430ustar00rootroot00000000000000LIBFDT_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_namelen; 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; fdt_appendprop; fdt_create_empty_tree; fdt_first_property_offset; fdt_get_property_by_offset; fdt_getprop_by_offset; fdt_next_property_offset; fdt_first_subnode; fdt_next_subnode; fdt_address_cells; fdt_size_cells; fdt_stringlist_contains; fdt_resize; fdt_overlay_apply; local: *; }; pdbg-2.0/libpdbg/000077500000000000000000000000001336450571500137035ustar00rootroot00000000000000pdbg-2.0/libpdbg/adu.c000066400000000000000000000310421336450571500146200ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "operations.h" #include "bitutils.h" #include "debug.h" /* P8 ADU SCOM Register Definitions */ #define P8_ALTD_CONTROL_REG 0x0 #define P8_ALTD_CMD_REG 0x1 #define P8_ALTD_STATUS_REG 0x2 #define P8_ALTD_DATA_REG 0x3 /* P9 ADU SCOM Register Definitions */ #define P9_ALTD_CONTROL_REG 0x0 #define P9_ALTD_CMD_REG 0x1 #define P9_ALTD_STATUS_REG 0x3 #define P9_ALTD_DATA_REG 0x4 /* Common ALTD_CMD_REG fields */ #define FBC_ALTD_START_OP PPC_BIT(2) #define FBC_ALTD_CLEAR_STATUS PPC_BIT(3) #define FBC_ALTD_RESET_AD_PCB PPC_BIT(4) #define FBC_ALTD_SCOPE PPC_BITMASK(16, 18) #define FBC_ALTD_AUTO_INC PPC_BIT(19) #define FBC_ALTD_DROP_PRIORITY PPC_BITMASK(20, 21) #define FBC_LOCKED PPC_BIT(11) /* P9_ALTD_CMD_REG fields */ #define P9_TTYPE_TREAD PPC_BIT(5) #define P9_TTYPE_TWRITE 0 #define P9_FBC_ALTD_TTYPE PPC_BITMASK(25, 31) #define P9_TTYPE_CI_PARTIAL_WRITE 0b110111 #define P9_TTYPE_CI_PARTIAL_OOO_WRITE 0b110110 #define P9_TTYPE_CI_PARTIAL_READ 0b110100 #define P9_TTYPE_DMA_PARTIAL_READ 0b000110 #define P9_TTYPE_DMA_PARTIAL_WRITE 0b100110 #define P9_FBC_ALTD_TSIZE PPC_BITMASK(32, 39) #define P9_FBC_ALTD_ADDRESS PPC_BITMASK(8, 63) #define DROP_PRIORITY_LOW 0ULL #define DROP_PRIORITY_MEDIUM 1ULL #define DROP_PRIORITY_HIGH 2ULL #define SCOPE_NODAL 0 #define SCOPE_GROUP 1 #define SCOPE_SYSTEM 2 #define SCOPE_REMOTE 3 /* P8_ALTD_CONTROL_REG fields */ #define P8_FBC_ALTD_TTYPE PPC_BITMASK(0, 5) #define P8_TTYPE_TREAD PPC_BIT(6) #define P8_TTYPE_TWRITE 0 #define P8_FBC_ALTD_TSIZE PPC_BITMASK(7, 13) #define P8_FBC_ALTD_ADDRESS PPC_BITMASK(14, 63) #define P8_TTYPE_CI_PARTIAL_WRITE 0b110111 #define P8_TTYPE_CI_PARTIAL_OOO_WRITE 0b110110 #define P8_TTYPE_DMA_PARTIAL_WRITE 0b100110 #define P8_TTYPE_CI_PARTIAL_READ 0b110100 #define P8_TTYPE_DMA_PARTIAL_READ 0b110101 #define P8_TTYPE_PBOPERATION 0b111111 /* P8/P9_ALTD_STATUS_REG fields */ #define FBC_ALTD_ADDR_DONE PPC_BIT(2) #define FBC_ALTD_DATA_DONE PPC_BIT(3) #define FBC_ALTD_PBINIT_MISSING PPC_BIT(18) int adu_getmem(struct pdbg_target *adu_target, uint64_t start_addr, uint8_t *output, uint64_t size) { return __adu_getmem(adu_target, start_addr, output, size, false); } int adu_getmem_ci(struct pdbg_target *adu_target, uint64_t start_addr, uint8_t *output, uint64_t size) { return __adu_getmem(adu_target, start_addr, output, size, true); } int __adu_getmem(struct pdbg_target *adu_target, uint64_t start_addr, uint8_t *output, uint64_t size, bool ci) { struct adu *adu; uint8_t *output0; int rc = 0; uint64_t addr0, addr; assert(!strcmp(adu_target->class, "adu")); adu = target_to_adu(adu_target); output0 = output; /* Align start address to 8-byte boundary */ addr0 = 8 * (start_addr / 8); /* We read data in 8-byte aligned chunks */ for (addr = addr0; addr < start_addr + size; addr += 8) { uint64_t data; if (adu->getmem(adu, addr, &data, ci)) return -1; /* ADU returns data in big-endian form in the register */ data = __builtin_bswap64(data); if (addr < start_addr) { size_t offset = start_addr - addr; size_t n = (size <= 8-offset ? size : 8-offset); memcpy(output, ((uint8_t *) &data) + offset, n); output += n; } else if (addr + 8 > start_addr + size) { uint64_t offset = start_addr + size - addr; memcpy(output, &data, offset); output += offset; } else { memcpy(output, &data, 8); output += 8; } pdbg_progress_tick(output - output0, size); } pdbg_progress_tick(size, size); return rc; } int adu_putmem(struct pdbg_target *adu_target, uint64_t start_addr, uint8_t *output, uint64_t size) { return __adu_putmem(adu_target, start_addr, output, size, false); } int adu_putmem_ci(struct pdbg_target *adu_target, uint64_t start_addr, uint8_t *output, uint64_t size) { return __adu_putmem(adu_target, start_addr, output, size, true); } int __adu_putmem(struct pdbg_target *adu_target, uint64_t start_addr, uint8_t *input, uint64_t size, bool ci) { struct adu *adu; int rc = 0, tsize; uint64_t addr, data, end_addr; assert(!strcmp(adu_target->class, "adu")); adu = target_to_adu(adu_target); end_addr = start_addr + size; for (addr = start_addr; addr < end_addr; addr += tsize, input += tsize) { if ((addr % 8) || (addr + 8 > end_addr)) { /* If the address is not 64-bit aligned we * copy in a byte at a time until it is. */ tsize = 1; /* Copy the input data in with correct alignment */ data = ((uint64_t) *input) << 8*(8 - (addr % 8) - 1); } else { tsize = 8; memcpy(&data, input, sizeof(data)); data = __builtin_bswap64(data); } adu->putmem(adu, addr, data, tsize, ci); pdbg_progress_tick(addr - start_addr, size); } pdbg_progress_tick(size, size); return rc; } static int adu_lock(struct adu *adu) { uint64_t val; CHECK_ERR(pib_read(&adu->target, P8_ALTD_CMD_REG, &val)); if (val & FBC_LOCKED) PR_INFO("ADU already locked! Ignoring.\n"); val |= FBC_LOCKED; CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, val)); return 0; } static int adu_unlock(struct adu *adu) { uint64_t val; CHECK_ERR(pib_read(&adu->target, P8_ALTD_CMD_REG, &val)); if (!(val & FBC_LOCKED)) { PR_INFO("ADU already unlocked!\n"); return 0; } val &= ~FBC_LOCKED; CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, val)); return 0; } static int adu_reset(struct adu *adu) { uint64_t val; CHECK_ERR(pib_read(&adu->target, P8_ALTD_CMD_REG, &val)); val |= FBC_ALTD_CLEAR_STATUS | FBC_ALTD_RESET_AD_PCB; CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, val)); return 0; } static int p8_adu_getmem(struct adu *adu, uint64_t addr, uint64_t *data, int ci) { uint64_t ctrl_reg, cmd_reg, val; int rc = 0; CHECK_ERR(adu_lock(adu)); ctrl_reg = P8_TTYPE_TREAD; if (ci) /* Do cache inhibited access */ ctrl_reg = SETFIELD(P8_FBC_ALTD_TTYPE, ctrl_reg, P8_TTYPE_CI_PARTIAL_READ); else ctrl_reg = SETFIELD(P8_FBC_ALTD_TTYPE, ctrl_reg, P8_TTYPE_DMA_PARTIAL_READ); ctrl_reg = SETFIELD(P8_FBC_ALTD_TSIZE, ctrl_reg, 8); CHECK_ERR_GOTO(out, rc = pib_read(&adu->target, P8_ALTD_CMD_REG, &cmd_reg)); cmd_reg |= FBC_ALTD_START_OP; cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM); cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM); retry: /* Clear status bits */ CHECK_ERR_GOTO(out, rc = adu_reset(adu)); /* Set the address */ ctrl_reg = SETFIELD(P8_FBC_ALTD_ADDRESS, ctrl_reg, addr); CHECK_ERR_GOTO(out, rc = pib_write(&adu->target, P8_ALTD_CONTROL_REG, ctrl_reg)); /* Start the command */ CHECK_ERR_GOTO(out, rc = pib_write(&adu->target, P8_ALTD_CMD_REG, cmd_reg)); /* Wait for completion */ do { CHECK_ERR_GOTO(out, rc = pib_read(&adu->target, P8_ALTD_STATUS_REG, &val)); } while (!val); if( !(val & FBC_ALTD_ADDR_DONE) || !(val & FBC_ALTD_DATA_DONE)) { /* PBINIT_MISSING is expected occasionally so just retry */ if (val & FBC_ALTD_PBINIT_MISSING) goto retry; else { PR_ERROR("Unable to read memory. " \ "ALTD_STATUS_REG = 0x%016" PRIx64 "\n", val); adu_unlock(adu); return -1; } } /* Read data */ CHECK_ERR_GOTO(out, rc = pib_read(&adu->target, P8_ALTD_DATA_REG, data)); out: adu_unlock(adu); return rc; } int p8_adu_putmem(struct adu *adu, uint64_t addr, uint64_t data, int size, int ci) { int rc = 0; uint64_t cmd_reg, ctrl_reg, val; CHECK_ERR(adu_lock(adu)); ctrl_reg = P8_TTYPE_TWRITE; if (ci) /* Do cache inhibited access */ ctrl_reg = SETFIELD(P8_FBC_ALTD_TTYPE, ctrl_reg, P8_TTYPE_CI_PARTIAL_WRITE); else ctrl_reg = SETFIELD(P8_FBC_ALTD_TTYPE, ctrl_reg, P8_TTYPE_DMA_PARTIAL_WRITE); ctrl_reg = SETFIELD(P8_FBC_ALTD_TSIZE, ctrl_reg, size); CHECK_ERR_GOTO(out, rc = pib_read(&adu->target, P8_ALTD_CMD_REG, &cmd_reg)); cmd_reg |= FBC_ALTD_START_OP; cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM); cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM); /* Clear status bits */ CHECK_ERR_GOTO(out, rc = adu_reset(adu)); /* Set the address */ ctrl_reg = SETFIELD(P8_FBC_ALTD_ADDRESS, ctrl_reg, addr); retry: CHECK_ERR_GOTO(out, rc = pib_write(&adu->target, P8_ALTD_CONTROL_REG, ctrl_reg)); /* Write the data */ CHECK_ERR_GOTO(out, rc = pib_write(&adu->target, P8_ALTD_DATA_REG, data)); /* Start the command */ CHECK_ERR_GOTO(out, rc = pib_write(&adu->target, P8_ALTD_CMD_REG, cmd_reg)); /* Wait for completion */ do { CHECK_ERR_GOTO(out, rc = pib_read(&adu->target, P8_ALTD_STATUS_REG, &val)); } while (!val); if( !(val & FBC_ALTD_ADDR_DONE) || !(val & FBC_ALTD_DATA_DONE)) { /* PBINIT_MISSING is expected occasionally so just retry */ if (val & FBC_ALTD_PBINIT_MISSING) goto retry; else { PR_ERROR("Unable to write memory. " \ "P8_ALTD_STATUS_REG = 0x%016" PRIx64 "\n", val); rc = -1; } } out: adu_unlock(adu); return rc; } static int p9_adu_getmem(struct adu *adu, uint64_t addr, uint64_t *data, int ci) { uint64_t ctrl_reg, cmd_reg, val; cmd_reg = P9_TTYPE_TREAD; if (ci) /* Do cache inhibited access */ cmd_reg = SETFIELD(P9_FBC_ALTD_TTYPE, cmd_reg, P9_TTYPE_CI_PARTIAL_READ); else cmd_reg = SETFIELD(P9_FBC_ALTD_TTYPE, cmd_reg, P9_TTYPE_DMA_PARTIAL_READ); /* For a read size is apparently always 0 */ cmd_reg = SETFIELD(P9_FBC_ALTD_TSIZE, cmd_reg, 0); cmd_reg |= FBC_ALTD_START_OP; cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_REMOTE); cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_LOW); retry: /* Clear status bits */ CHECK_ERR(adu_reset(adu)); /* Set the address */ ctrl_reg = SETFIELD(P9_FBC_ALTD_ADDRESS, 0ULL, addr); CHECK_ERR(pib_write(&adu->target, P9_ALTD_CONTROL_REG, ctrl_reg)); /* Start the command */ CHECK_ERR(pib_write(&adu->target, P9_ALTD_CMD_REG, cmd_reg)); /* Wait for completion */ do { CHECK_ERR(pib_read(&adu->target, P9_ALTD_STATUS_REG, &val)); } while (!val); if( !(val & FBC_ALTD_ADDR_DONE) || !(val & FBC_ALTD_DATA_DONE)) { /* PBINIT_MISSING is expected occasionally so just retry */ if (val & FBC_ALTD_PBINIT_MISSING) goto retry; else { PR_ERROR("Unable to read memory. " \ "ALTD_STATUS_REG = 0x%016" PRIx64 "\n", val); return -1; } } /* Read data */ CHECK_ERR(pib_read(&adu->target, P9_ALTD_DATA_REG, data)); return 0; } static int p9_adu_putmem(struct adu *adu, uint64_t addr, uint64_t data, int size, int ci) { uint64_t ctrl_reg, cmd_reg, val; /* Format to tsize. This is the "secondary encode" and is shifted left on for writes. */ size <<= 1; cmd_reg = P9_TTYPE_TWRITE; if (ci) /* Do cache inhibited access */ cmd_reg = SETFIELD(P9_FBC_ALTD_TTYPE, cmd_reg, P9_TTYPE_CI_PARTIAL_WRITE); else cmd_reg = SETFIELD(P9_FBC_ALTD_TTYPE, cmd_reg, P9_TTYPE_DMA_PARTIAL_WRITE); cmd_reg = SETFIELD(P9_FBC_ALTD_TSIZE, cmd_reg, size); cmd_reg |= FBC_ALTD_START_OP; cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_REMOTE); cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_LOW); /* Clear status bits */ CHECK_ERR(adu_reset(adu)); /* Set the address */ ctrl_reg = SETFIELD(P9_FBC_ALTD_ADDRESS, 0ULL, addr); retry: CHECK_ERR(pib_write(&adu->target, P9_ALTD_CONTROL_REG, ctrl_reg)); /* Write the data */ CHECK_ERR(pib_write(&adu->target, P9_ALTD_DATA_REG, data)); /* Start the command */ CHECK_ERR(pib_write(&adu->target, P9_ALTD_CMD_REG, cmd_reg)); /* Wait for completion */ do { CHECK_ERR(pib_read(&adu->target, P9_ALTD_STATUS_REG, &val)); } while (!val); if( !(val & FBC_ALTD_ADDR_DONE) || !(val & FBC_ALTD_DATA_DONE)) { /* PBINIT_MISSING is expected occasionally so just retry */ if (val & FBC_ALTD_PBINIT_MISSING) goto retry; else { PR_ERROR("Unable to read memory. " \ "ALTD_STATUS_REG = 0x%016" PRIx64 "\n", val); return -1; } } return 0; } static struct adu p8_adu = { .target = { .name = "POWER8 ADU", .compatible = "ibm,power8-adu", .class = "adu", }, .getmem = p8_adu_getmem, .putmem = p8_adu_putmem, }; DECLARE_HW_UNIT(p8_adu); static struct adu p9_adu = { .target = { .name = "POWER9 ADU", .compatible = "ibm,power9-adu", .class = "adu", }, .getmem = p9_adu_getmem, .putmem = p9_adu_putmem, }; DECLARE_HW_UNIT(p9_adu); pdbg-2.0/libpdbg/backend.h000066400000000000000000000015311336450571500154430ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 __BACKEND_H #define __BACKEND_H /* We only support slave 0 at the moment */ #define SLAVE_ID 0x0 /* backend initialisation */ struct scom_backend *fsi_init(void); /* i2c backend initialisation */ struct scom_backend *i2c_init(char *bus, int addr); #endif pdbg-2.0/libpdbg/bitutils.h000066400000000000000000000032011336450571500157070ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_ffsll(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 */ pdbg-2.0/libpdbg/bmcfsi.c000066400000000000000000000264631336450571500153250ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "bitutils.h" #include "operations.h" #include "device.h" #include "target.h" #include "debug.h" #define GPIO_BASE 0x1e780000 #define GPIO_DATA 0x0 #define GPIO_DIR 0x4 #define CRC_LEN 4 /* Defines a GPIO. The Aspeed devices dont have consistent stride * between registers so we need to encode register bit number and base * address offset */ struct gpio_pin { uint32_t offset; int bit; }; enum gpio { GPIO_FSI_CLK = 0, GPIO_FSI_DAT = 1, GPIO_FSI_DAT_EN = 2, GPIO_FSI_ENABLE = 3, GPIO_CRONUS_SEL = 4, }; /* Pointer to the GPIO pins to use for this system */ #define FSI_CLK &gpio_pins[GPIO_FSI_CLK] #define FSI_DAT &gpio_pins[GPIO_FSI_DAT] #define FSI_DAT_EN &gpio_pins[GPIO_FSI_DAT_EN] #define FSI_ENABLE &gpio_pins[GPIO_FSI_ENABLE] #define CRONUS_SEL &gpio_pins[GPIO_CRONUS_SEL] static struct gpio_pin gpio_pins[GPIO_CRONUS_SEL + 1]; /* FSI result symbols */ enum fsi_result { FSI_MERR_TIMEOUT = -2, FSI_MERR_C = -1, FSI_ACK = 0x0, FSI_BUSY = 0x1, FSI_ERR_A = 0x2, FSI_ERR_C = 0x3, }; static int clock_delay = 0; #define FSI_DATA0_REG 0x1000 #define FSI_DATA1_REG 0x1001 #define FSI_CMD_REG 0x1002 #define FSI_CMD_REG_WRITE PPC_BIT32(0) #define FSI_RESET_REG 0x1006 #define FSI_RESET_CMD PPC_BIT32(0) #define FSI_SET_PIB_RESET_REG 0x1007 #define FSI_SET_PIB_RESET PPC_BIT32(0) /* For some reason the FSI2PIB engine dies with frequent * access. Letting it have a bit of a rest seems to stop the * problem. This sets the number of usecs to sleep between SCOM * accesses. */ #define FSI2PIB_RELAX 50 /* FSI private data */ static void *gpio_reg = NULL; static int mem_fd = 0; static void fsi_reset(struct fsi *fsi); static uint32_t readl(void *addr) { asm volatile("" : : : "memory"); return *(volatile uint32_t *) addr; } static void writel(uint32_t val, void *addr) { asm volatile("" : : : "memory"); *(volatile uint32_t *) addr = val; } static int __attribute__((unused)) get_direction(struct gpio_pin *pin) { void *offset = gpio_reg + pin->offset + GPIO_DIR; return !!(readl(offset) & (1ULL << pin->bit)); } static void set_direction_out(struct gpio_pin *pin) { uint32_t x; void *offset = gpio_reg + pin->offset + GPIO_DIR; x = readl(offset); x |= 1ULL << pin->bit; writel(x, offset); } static void set_direction_in(struct gpio_pin *pin) { uint32_t x; void *offset = gpio_reg + pin->offset + GPIO_DIR; x = readl(offset); x &= ~(1ULL << pin->bit); writel(x, offset); } static int read_gpio(struct gpio_pin *pin) { void *offset = gpio_reg + pin->offset + GPIO_DATA; return (readl(offset) >> pin->bit) & 0x1; } static void write_gpio(struct gpio_pin *pin, int val) { uint32_t x; void *offset = gpio_reg + pin->offset + GPIO_DATA; x = readl(offset); if (val) x |= 1ULL << pin->bit; else x &= ~(1ULL << pin->bit); writel(x, offset); } static inline void clock_cycle(struct gpio_pin *pin, int num_clks) { int i; volatile int j; /* Need to introduce delays when inlining this function */ for (j = 0; j < clock_delay; j++); for (i = 0; i < num_clks; i++) { write_gpio(pin, 0); write_gpio(pin, 1); } for (j = 0; j < clock_delay; j++); } static uint8_t crc4(uint8_t c, int b) { uint8_t m = 0; c &= 0xf; m = b ^ ((c >> 3) & 0x1); m = (m << 2) | (m << 1) | (m); c <<= 1; c ^= m; return c & 0xf; } /* FSI bits should be reading on the falling edge. Read a bit and * clock the next one out. */ static inline unsigned int fsi_read_bit(void) { int x; x = read_gpio(FSI_DAT); clock_cycle(FSI_CLK, 1); /* The FSI hardware is active low (ie. inverted) */ return !(x & 1); } static inline void fsi_send_bit(uint64_t bit) { write_gpio(FSI_DAT, !bit); clock_cycle(FSI_CLK, 1); } /* Format a CFAM address into an FSI slaveId, command and address. */ static uint64_t fsi_abs_ar(uint32_t addr, int read) { uint32_t slave_id = (addr >> 21) & 0x3; /* Reformat the address. I'm not sure I fully understand this * yet but we basically shift the bottom byte and add 0b01 * (for the write word?) */ addr = ((addr & 0x1ffc00) | ((addr & 0x3ff) << 2)) << 1; addr |= 0x3; addr |= slave_id << 26; addr |= (0x8ULL | !!(read)) << 22; return addr; } static uint64_t fsi_d_poll(uint8_t slave_id) { return slave_id << 3 | 0x2; } static void fsi_break(void) { set_direction_out(FSI_CLK); set_direction_out(FSI_DAT); write_gpio(FSI_DAT_EN, 1); /* Crank things - not sure if we need this yet */ write_gpio(FSI_CLK, 1); write_gpio(FSI_DAT, 1); /* Data standby state */ /* Send break command */ write_gpio(FSI_DAT, 0); clock_cycle(FSI_CLK, 256); } /* Send a sequence, including start bit and crc */ static void fsi_send_seq(uint64_t seq, int len) { int i; uint8_t crc; set_direction_out(FSI_CLK); set_direction_out(FSI_DAT); write_gpio(FSI_DAT_EN, 1); write_gpio(FSI_DAT, 1); clock_cycle(FSI_CLK, 50); /* Send the start bit */ write_gpio(FSI_DAT, 0); clock_cycle(FSI_CLK, 1); /* crc includes start bit */ crc = crc4(0, 1); for (i = 63; i >= 64 - len; i--) { crc = crc4(crc, !!(seq & (1ULL << i))); fsi_send_bit(seq & (1ULL << i)); } /* Send the CRC */ for (i = 3; i >= 0; i--) fsi_send_bit(crc & (1ULL << i)); write_gpio(FSI_CLK, 0); } /* Read a response. Only supports upto 60 bits at the moment. */ static enum fsi_result fsi_read_resp(uint64_t *result, int len) { int i, x; uint8_t crc; uint64_t resp = 0; uint8_t ack = 0; write_gpio(FSI_DAT_EN, 0); set_direction_in(FSI_DAT); /* Wait for start bit */ for (i = 0; i < 512; i++) { x = fsi_read_bit(); if (x) break; } if (i == 512) { PR_DEBUG("Timeout waiting for start bit\n"); return FSI_MERR_TIMEOUT; } crc = crc4(0, 1); /* Read the response code (ACK, ERR_A, etc.) */ for (i = 0; i < 4; i++) { ack <<= 1; ack |= fsi_read_bit(); crc = crc4(crc, ack & 0x1); } /* A non-ACK response has no data but should include a CRC */ if (ack != FSI_ACK) len = 7; for (; i < len + CRC_LEN; i++) { resp <<= 1; resp |= fsi_read_bit(); crc = crc4(crc, resp & 0x1); } if (crc != 0) { PR_ERROR("CRC error: 0x%" PRIx64 "\n", resp); return FSI_MERR_C; } write_gpio(FSI_CLK, 0); /* Strip the CRC off */ *result = resp >> 4; return ack & 0x3; } static enum fsi_result fsi_d_poll_wait(uint8_t slave_id, uint64_t *resp, int len) { int i; uint64_t seq; enum fsi_result rc; /* Poll for response if busy */ for (i = 0; i < 512; i++) { seq = fsi_d_poll(slave_id) << 59; fsi_send_seq(seq, 5); if ((rc = fsi_read_resp(resp, len)) != FSI_BUSY) break; } return rc; } static int fsi_getcfam(struct fsi *fsi, uint32_t addr, uint32_t *value) { uint64_t seq; uint64_t resp; enum fsi_result rc; /* Format of the read sequence is: * 6666555555555544444444443333333333222222222211111111110000000000 * 3210987654321098765432109876543210987654321098765432109876543210 * * ii1001aaaaaaaaaaaaaaaaaaa011cccc * * Where: * ii = slaveId * a = address bit * 011 = write word size * d = data bit * c = crc bit * * When applying the sequence it should be inverted (active * low) */ seq = fsi_abs_ar(addr, 1) << 36; fsi_send_seq(seq, 28); if ((rc = fsi_read_resp(&resp, 36)) == FSI_BUSY) rc = fsi_d_poll_wait(0, &resp, 36); if (rc != FSI_ACK) { PR_DEBUG("getcfam error. Response: 0x%01x\n", rc); rc = -1; } *value = resp & 0xffffffff; return rc; } static int fsi_putcfam(struct fsi *fsi, uint32_t addr, uint32_t data) { uint64_t seq; uint64_t resp; enum fsi_result rc; /* Format of the sequence is: * 6666555555555544444444443333333333222222222211111111110000000000 * 3210987654321098765432109876543210987654321098765432109876543210 * * ii1000aaaaaaaaaaaaaaaaaaa011ddddddddddddddddddddddddddddddddcccc * * Where: * ii = slaveId * a = address bit * 011 = write word size * d = data bit * c = crc bit * * When applying the sequence it should be inverted (active * low) */ seq = fsi_abs_ar(addr, 0) << 36; seq |= ((uint64_t) data & 0xffffffff) << (4); fsi_send_seq(seq, 60); if ((rc = fsi_read_resp(&resp, 4)) == FSI_BUSY) rc = fsi_d_poll_wait(0, &resp, 4); if (rc != FSI_ACK) PR_DEBUG("putcfam error. Response: 0x%01x\n", rc); else rc = 0; return rc; } static void fsi_reset(struct fsi *fsi) { uint32_t val; fsi_break(); /* Clear own id on the master CFAM to access hMFSI ports */ fsi_getcfam(fsi, 0x800, &val); val &= ~(PPC_BIT32(6) | PPC_BIT32(7)); fsi_putcfam(fsi, 0x800, val); } void fsi_destroy(struct pdbg_target *target) { set_direction_out(FSI_CLK); set_direction_out(FSI_DAT); write_gpio(FSI_DAT_EN, 1); /* Crank things - this is needed to use this tool for kicking off system boot */ write_gpio(FSI_CLK, 1); write_gpio(FSI_DAT, 1); /* Data standby state */ clock_cycle(FSI_CLK, 5000); write_gpio(FSI_DAT_EN, 0); write_gpio(FSI_CLK, 0); write_gpio(FSI_ENABLE, 0); write_gpio(CRONUS_SEL, 0); } int bmcfsi_probe(struct pdbg_target *target) { struct fsi *fsi = target_to_fsi(target); if (!mem_fd) { mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) { perror("Unable to open /dev/mem"); exit(1); } } if (!gpio_reg) { gpio_pins[GPIO_FSI_CLK].offset = dt_prop_get_u32_index(target, "fsi_clk", 0); gpio_pins[GPIO_FSI_CLK].bit = dt_prop_get_u32_index(target, "fsi_clk", 1); gpio_pins[GPIO_FSI_DAT].offset = dt_prop_get_u32_index(target, "fsi_dat", 0); gpio_pins[GPIO_FSI_DAT].bit = dt_prop_get_u32_index(target, "fsi_dat", 1); gpio_pins[GPIO_FSI_DAT_EN].offset = dt_prop_get_u32_index(target, "fsi_dat_en", 0); gpio_pins[GPIO_FSI_DAT_EN].bit = dt_prop_get_u32_index(target, "fsi_dat_en", 1); gpio_pins[GPIO_FSI_ENABLE].offset = dt_prop_get_u32_index(target, "fsi_enable", 0); gpio_pins[GPIO_FSI_ENABLE].bit = dt_prop_get_u32_index(target, "fsi_enable", 1); gpio_pins[GPIO_CRONUS_SEL].offset = dt_prop_get_u32_index(target, "cronus_sel", 0); gpio_pins[GPIO_CRONUS_SEL].bit = dt_prop_get_u32_index(target, "cronus_sel", 1); clock_delay = dt_prop_get_u32(target, "clock_delay"); /* We only have to do this init once per backend */ gpio_reg = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE); if (gpio_reg == MAP_FAILED) { perror("Unable to map GPIO register memory"); exit(-1); } set_direction_out(CRONUS_SEL); set_direction_out(FSI_ENABLE); set_direction_out(FSI_DAT_EN); write_gpio(FSI_ENABLE, 1); write_gpio(CRONUS_SEL, 1); fsi_reset(fsi); } return 0; } static struct fsi bmcfsi = { .target = { .name = "BMC GPIO bit-banging FSI master", .compatible = "ibm,bmcfsi", .class = "fsi", .probe = bmcfsi_probe, }, .read = fsi_getcfam, .write = fsi_putcfam, }; DECLARE_HW_UNIT(bmcfsi); pdbg-2.0/libpdbg/cfam.c000066400000000000000000000214331336450571500147600ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Most of the PIB2OPB code is based on code from skiboot. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "target.h" #include "bitutils.h" #include "operations.h" #include "debug.h" #define FSI_DATA0_REG 0x0 #define FSI_DATA1_REG 0x1 #define FSI_CMD_REG 0x2 #define FSI_CMD_REG_WRITE PPC_BIT32(0) #define FSI_RESET_REG 0x6 #define FSI_RESET_CMD PPC_BIT32(0) #define FSI_SET_PIB_RESET_REG 0x7 #define FSI_SET_PIB_RESET PPC_BIT32(0) /* For some reason the FSI2PIB engine dies with frequent * access. Letting it have a bit of a rest seems to stop the * problem. This sets the number of usecs to sleep between SCOM * accesses. */ #define FSI2PIB_RELAX 50 /* * 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 #define OPB_ERR_XSCOM_ERR -1; #define OPB_ERR_TIMEOUT_ERR -1; #define OPB_ERR_BAD_OPB_ADDR -1; /* We try up to 1.2ms for an OPB access */ #define MFSI_OPB_MAX_TRIES 1200 static int fsi2pib_getscom(struct pib *pib, uint64_t addr, uint64_t *value) { uint32_t result; usleep(FSI2PIB_RELAX); /* Get scom works by putting the address in FSI_CMD_REG and * reading the result from FST_DATA[01]_REG. */ CHECK_ERR(fsi_write(&pib->target, FSI_CMD_REG, addr)); CHECK_ERR(fsi_read(&pib->target, FSI_DATA0_REG, &result)); *value = ((uint64_t) result) << 32; CHECK_ERR(fsi_read(&pib->target, FSI_DATA1_REG, &result)); *value |= result; return 0; } static int fsi2pib_putscom(struct pib *pib, uint64_t addr, uint64_t value) { usleep(FSI2PIB_RELAX); CHECK_ERR(fsi_write(&pib->target, FSI_DATA0_REG, (value >> 32) & 0xffffffff)); CHECK_ERR(fsi_write(&pib->target, FSI_DATA1_REG, value & 0xffffffff)); CHECK_ERR(fsi_write(&pib->target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr)); return 0; } static int fsi2pib_reset(struct pdbg_target *target) { /* Reset the PIB master interface. We used to reset the entire FSI2PIB * engine but that had the unfortunate side effect of clearing existing * settings such as the true mask register (0xd) */ CHECK_ERR(fsi_write(target, FSI_SET_PIB_RESET_REG, FSI_SET_PIB_RESET)); return 0; } static struct pib fsi_pib = { .target = { .name = "POWER FSI2PIB", .compatible = "ibm,fsi-pib", .class = "pib", .probe = fsi2pib_reset, }, .read = fsi2pib_getscom, .write = fsi2pib_putscom, }; DECLARE_HW_UNIT(fsi_pib); static uint64_t opb_poll(struct opb *opb, 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 = pib_read(&opb->target, PIB2OPB_REG_STAT, &sval); if (rc) { /* Do something here ? */ PR_ERROR("XSCOM error %" PRId64 " read OPB STAT\n", rc); return -1; } PR_DEBUG(" STAT=0x%16" PRIx64 "...\n", sval); stat = sval >> 32; /* Complete */ if (!(stat & OPB_STAT_BUSY)) break; if (retries-- == 0) { /* This isn't supposed to happen (HW timeout) */ PR_ERROR("OPB POLL timeout !\n"); return -1; } usleep(1); } /* * TODO: Add the full error analysis that skiboot has. For now * we just reset things so we can continue. Also need to * improve error handling as we expect these occasionally when * probing the system. */ if (stat & OPB_STAT_ANY_ERR) { pib_write(&opb->target, PIB2OPB_REG_RESET, PPC_BIT(0)); pib_write(&opb->target, PIB2OPB_REG_STAT, PPC_BIT(0)); PR_DEBUG("OPB Error. Status 0x%08x\n", stat); rc = -1; } else if (read_data) { if (!(stat & OPB_STAT_READ_VALID)) { PR_DEBUG("Read successful but no data !\n"); rc = -1; } *read_data = sval & 0xffffffff; } return rc; } static int p8_opb_read(struct opb *opb, uint32_t addr, uint32_t *data) { uint64_t opb_cmd = OPB_CMD_READ | OPB_CMD_32BIT; int64_t rc; if (addr > 0x00ffffff) return OPB_ERR_BAD_OPB_ADDR; /* Turn the address into a byte address */ addr = (addr & 0xffff00) | ((addr & 0xff) << 2); opb_cmd |= addr; opb_cmd <<= 32; PR_DEBUG("MFSI_OPB_READ: Writing 0x%16" PRIx64 "\n", opb_cmd); rc = pib_write(&opb->target, PIB2OPB_REG_CMD, opb_cmd); if (rc) { PR_ERROR("XSCOM error %" PRId64 " writing OPB CMD\n", rc); return OPB_ERR_XSCOM_ERR; } return opb_poll(opb, data); } static int p8_opb_write(struct opb *opb, uint32_t addr, uint32_t data) { uint64_t opb_cmd = OPB_CMD_WRITE | OPB_CMD_32BIT; int64_t rc; if (addr > 0x00ffffff) return OPB_ERR_BAD_OPB_ADDR; addr = (addr & 0xffff00) | ((addr & 0xff) << 2); opb_cmd |= addr; opb_cmd <<= 32; opb_cmd |= data; PR_DEBUG("MFSI_OPB_WRITE: Writing 0x%16" PRIx64 "\n", opb_cmd); rc = pib_write(&opb->target, PIB2OPB_REG_CMD, opb_cmd); if (rc) { PR_ERROR("XSCOM error %" PRId64 " writing OPB CMD\n", rc); return OPB_ERR_XSCOM_ERR; } return opb_poll(opb, NULL); } static struct opb p8_opb = { .target = { .name = "POWER8 OPB", .compatible = "ibm,power8-opb", .class = "opb", }, .read = p8_opb_read, .write = p8_opb_write, }; DECLARE_HW_UNIT(p8_opb); enum chip_type get_chip_type(uint64_t chip_id) { switch(GETFIELD(PPC_BITMASK32(12, 19), chip_id)) { case 0xea: return CHIP_P8; case 0xd3: return CHIP_P8NV; case 0xd1: return CHIP_P9; default: return CHIP_UNKNOWN; } } static int p8_hmfsi_read(struct fsi *fsi, uint32_t addr, uint32_t *data) { return opb_read(&fsi->target, addr, data); } static int p8_hmfsi_write(struct fsi *fsi, uint32_t addr, uint32_t data) { return opb_write(&fsi->target, addr, data); } static int p8_hmfsi_probe(struct pdbg_target *target) { struct fsi *fsi = target_to_fsi(target); uint32_t value; int rc; if ((rc = opb_read(&fsi->target, 0xc09, &value))) return rc; fsi->chip_type = get_chip_type(value); PR_DEBUG("Found chip type %x\n", fsi->chip_type); if (fsi->chip_type == CHIP_UNKNOWN) return -1; return 0; } static struct fsi p8_opb_hmfsi = { .target = { .name = "POWER8 OPB attached hMFSI", .compatible = "ibm,power8-opb-hmfsi", .class = "fsi", .probe = p8_hmfsi_probe, }, .read = p8_hmfsi_read, .write = p8_hmfsi_write, }; DECLARE_HW_UNIT(p8_opb_hmfsi); static int cfam_hmfsi_read(struct fsi *fsi, uint32_t addr, uint32_t *data) { struct pdbg_target *parent_fsi = pdbg_target_require_parent("fsi", &fsi->target); addr += dt_get_address(&fsi->target, 0, NULL); return fsi_read(parent_fsi, addr, data); } static int cfam_hmfsi_write(struct fsi *fsi, uint32_t addr, uint32_t data) { struct pdbg_target *parent_fsi = pdbg_target_require_parent("fsi", &fsi->target); addr += dt_get_address(&fsi->target, 0, NULL); return fsi_write(parent_fsi, addr, data); } static int cfam_hmfsi_probe(struct pdbg_target *target) { struct fsi *fsi = target_to_fsi(target); struct pdbg_target *fsi_parent = target->parent; uint32_t value, port; int rc; /* Enable the port in the upstream control register */ port = dt_prop_get_u32(target, "port"); fsi_read(fsi_parent, 0x3404, &value); value |= 1 << (31 - port); if ((rc = fsi_write(fsi_parent, 0x3404, value))) { PR_ERROR("Unable to enable HMFSI port %d\n", port); return rc; } if ((rc = fsi_read(&fsi->target, 0xc09, &value))) return rc; fsi->chip_type = get_chip_type(value); PR_DEBUG("Found chip type %x\n", fsi->chip_type); if (fsi->chip_type == CHIP_UNKNOWN) return -1; return 0; } static struct fsi cfam_hmfsi = { .target = { .name = "CFAM hMFSI Port", .compatible = "ibm,fsi-hmfsi", .class = "fsi", .probe = cfam_hmfsi_probe, }, .read = cfam_hmfsi_read, .write = cfam_hmfsi_write, }; DECLARE_HW_UNIT(cfam_hmfsi); pdbg-2.0/libpdbg/chip.c000066400000000000000000000333741336450571500150040ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "target.h" #include "operations.h" #include "bitutils.h" #include "debug.h" uint64_t mfspr(uint64_t reg, uint64_t spr) { if (reg > 31) PR_ERROR("Invalid register specified for mfspr\n"); return MFSPR_OPCODE | (reg << 21) | ((spr & 0x1f) << 16) | ((spr & 0x3e0) << 6); } uint64_t mtspr(uint64_t spr, uint64_t reg) { if (reg > 31) PR_ERROR("Invalid register specified for mtspr\n"); return MTSPR_OPCODE | (reg << 21) | ((spr & 0x1f) << 16) | ((spr & 0x3e0) << 6); } static uint64_t mfocrf(uint64_t reg, uint64_t cr) { if (reg > 31) PR_ERROR("Invalid register specified for mfocrf\n"); if (cr > 7) PR_ERROR("Invalid CR field specified\n"); return MFOCRF_OPCODE | (reg << 21) | (1U << (12 + cr)); } static uint64_t mtocrf(uint64_t cr, uint64_t reg) { if (reg > 31) { PR_ERROR("Invalid register specified for mfocrf\n"); exit(1); } if (cr > 7) { PR_ERROR("Invalid CR field specified\n"); exit(1); } return MTOCRF_OPCODE | (reg << 21) | (1U << (12 + cr)); } static uint64_t mfnia(uint64_t reg) { if (reg > 31) PR_ERROR("Invalid register specified for mfnia\n"); return MFNIA_OPCODE | (reg << 21); } static uint64_t mtnia(uint64_t reg) { if (reg > 31) PR_ERROR("Invalid register specified for mtnia\n"); return MTNIA_OPCODE | (reg << 21); } static uint64_t mfmsr(uint64_t reg) { if (reg > 31) PR_ERROR("Invalid register specified for mfmsr\n"); return MFMSR_OPCODE | (reg << 21); } static uint64_t mtmsr(uint64_t reg) { if (reg > 31) PR_ERROR("Invalid register specified for mtmsr\n"); return MTMSR_OPCODE | (reg << 21); } static uint64_t ld(uint64_t rt, uint64_t ds, uint64_t ra) { if ((rt > 31) | (ra > 31) | (ds > 0x3fff)) PR_ERROR("Invalid register specified\n"); return LD_OPCODE | (rt << 21) | (ra << 16) | (ds << 2); } struct thread_state thread_status(struct pdbg_target *target) { struct thread *thread; assert(!strcmp(target->class, "thread")); thread = target_to_thread(target); return thread->status; } /* * Single step the thread count instructions. */ int ram_step_thread(struct pdbg_target *thread_target, int count) { struct thread *thread; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); return thread->step(thread, count); } int ram_start_thread(struct pdbg_target *thread_target) { struct thread *thread; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); return thread->start(thread); } int ram_stop_thread(struct pdbg_target *thread_target) { struct thread *thread; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); return thread->stop(thread); } int ram_sreset_thread(struct pdbg_target *thread_target) { struct thread *thread; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); return thread->sreset(thread); } /* * RAMs the opcodes in *opcodes and store the results of each opcode * into *results. *results must point to an array the same size as * *opcodes. Each entry from *results is put into SCR0 prior to * executing an opcode so that it may also be used to pass in * data. Note that only registers r0 and r1 are saved and restored so * opcode sequences must preserve other registers. */ int ram_instructions(struct pdbg_target *thread_target, uint64_t *opcodes, uint64_t *results, int len, unsigned int lpar) { uint64_t opcode = 0, r0 = 0, r1 = 0, scratch = 0; int i; int exception = 0; struct thread *thread; bool did_setup = false; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); if (!thread->ram_is_setup) { CHECK_ERR(thread->ram_setup(thread)); did_setup = true; } /* RAM instructions */ for (i = -2; i < len + 2; i++) { if (i == -2) /* Save r1 (assumes opcodes don't touch other registers) */ opcode = mtspr(277, 1); else if (i == -1) /* Save r0 (assumes opcodes don't touch other registers) */ opcode = mtspr(277, 0); else if (i < len) { scratch = results[i]; opcode = opcodes[i]; } else if (i == len) { /* Restore r0 */ scratch = r0; opcode = mfspr(0, 277); } else if (i == len + 1) { /* Restore r1 */ scratch = r1; opcode = mfspr(1, 277); } if (thread->ram_instruction(thread, opcode, &scratch)) { PR_DEBUG("%s: %d, %016" PRIx64 "\n", __FUNCTION__, __LINE__, opcode); exception = 1; if (i >= 0 && i < len) /* skip the rest and attempt to restore r0 and r1 */ i = len - 1; else break; } if (i == -2) r1 = scratch; else if (i == -1) r0 = scratch; else if (i < len) results[i] = scratch; } if (did_setup) CHECK_ERR(thread->ram_destroy(thread)); return exception; } /* * Get gpr value. Chip must be stopped. */ int ram_getgpr(struct pdbg_target *thread, int gpr, uint64_t *value) { uint64_t opcodes[] = {mtspr(277, gpr)}; uint64_t results[] = {0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); *value = results[0]; return 0; } int ram_putgpr(struct pdbg_target *thread, int gpr, uint64_t value) { uint64_t opcodes[] = {mfspr(gpr, 277)}; uint64_t results[] = {value}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); return 0; } int ram_getnia(struct pdbg_target *thread, uint64_t *value) { uint64_t opcodes[] = {mfnia(0), mtspr(277, 0)}; uint64_t results[] = {0, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); *value = results[1]; return 0; } /* * P9 must MTNIA from LR, P8 can MTNIA from R0. So we set both LR and R0 * to value. LR must be saved and restored. * * This is a hack and should be made much cleaner once we have target * specific putspr commands. */ int ram_putnia(struct pdbg_target *thread, uint64_t value) { uint64_t opcodes[] = { mfspr(1, 8), /* mflr r1 */ mfspr(0, 277), /* value -> r0 */ mtspr(8, 0), /* mtlr r0 */ mtnia(0), mtspr(8, 1), }; /* mtlr r1 */ uint64_t results[] = {0, value, 0, 0, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); return 0; } int ram_getspr(struct pdbg_target *thread, int spr, uint64_t *value) { uint64_t opcodes[] = {mfspr(0, spr), mtspr(277, 0)}; uint64_t results[] = {0, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); *value = results[1]; return 0; } int ram_putspr(struct pdbg_target *thread, int spr, uint64_t value) { uint64_t opcodes[] = {mfspr(0, 277), mtspr(spr, 0)}; uint64_t results[] = {value, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); return 0; } int ram_getmsr(struct pdbg_target *thread, uint64_t *value) { uint64_t opcodes[] = {mfmsr(0), mtspr(277, 0)}; uint64_t results[] = {0, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); *value = results[1]; return 0; } int ram_getcr(struct pdbg_target *thread, uint32_t *value) { uint64_t opcodes[] = {mfocrf(0, 0), mtspr(277, 0), mfocrf(0, 1), mtspr(277, 0), mfocrf(0, 2), mtspr(277, 0), mfocrf(0, 3), mtspr(277, 0), mfocrf(0, 4), mtspr(277, 0), mfocrf(0, 5), mtspr(277, 0), mfocrf(0, 6), mtspr(277, 0), mfocrf(0, 7), mtspr(277, 0)}; uint64_t results[16] = {0}; uint32_t cr_field, cr = 0; int i; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); for (i = 1; i < 16; i += 2) { cr_field = results[i]; /* We are not guaranteed that the other bits will be zeroed out */ cr |= cr_field & (0xf << 2*(i-1)); } *value = cr; return 0; } int ram_putcr(struct pdbg_target *thread, uint32_t value) { uint64_t opcodes[] = {mfspr(0, 277), mtocrf(0, 0), mtocrf(1, 0), mtocrf(2, 0), mtocrf(3, 0), mtocrf(4, 0), mtocrf(5, 0), mtocrf(6, 0), mtocrf(7, 0)}; uint64_t results[] = {value}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); return 0; } int ram_putmsr(struct pdbg_target *thread, uint64_t value) { uint64_t opcodes[] = {mfspr(0, 277), mtmsr(0)}; uint64_t results[] = {value, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); return 0; } int ram_getmem(struct pdbg_target *thread, uint64_t addr, uint64_t *value) { uint64_t opcodes[] = {mfspr(0, 277), mfspr(1, 277), ld(0, 0, 1), mtspr(277, 0)}; uint64_t results[] = {0xdeaddeaddeaddead, addr, 0, 0}; CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); *value = results[3]; return 0; } int ram_getxer(struct pdbg_target *thread_target, uint64_t *value) { struct thread *thread; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); CHECK_ERR(thread->ram_getxer(thread_target, value)); return 0; } int ram_putxer(struct pdbg_target *thread_target, uint64_t value) { struct thread *thread; assert(!strcmp(thread_target->class, "thread")); thread = target_to_thread(thread_target); CHECK_ERR(thread->ram_putxer(thread_target, value)); return 0; } /* * Read the given ring from the given chiplet. Result must be large enough to hold ring_len bits. */ int getring(struct pdbg_target *chiplet_target, uint64_t ring_addr, uint64_t ring_len, uint32_t result[]) { struct chiplet *chiplet; assert(!strcmp(chiplet_target->class, "chiplet")); chiplet = target_to_chiplet(chiplet_target); return chiplet->getring(chiplet, ring_addr, ring_len, result); } int ram_state_thread(struct pdbg_target *thread, struct thread_regs *regs) { struct thread_regs _regs; struct thread *t; uint64_t value = 0; int i; if (!regs) regs = &_regs; assert(!strcmp(thread->class, "thread")); t = target_to_thread(thread); CHECK_ERR(t->ram_setup(t)); /* * It would be neat to do all the ramming up front, then go through * and print everything out somewhere else. In practice so far it * can help to diagnose checkstop issues with ramming to print as * we go. Once it's more robust and tested, maybe. */ ram_getnia(thread, ®s->nia); printf("NIA : 0x%016" PRIx64 "\n", regs->nia); ram_getspr(thread, 28, ®s->cfar); printf("CFAR : 0x%016" PRIx64 "\n", regs->cfar); ram_getmsr(thread, ®s->msr); printf("MSR : 0x%016" PRIx64 "\n", regs->msr); ram_getspr(thread, 8, ®s->lr); printf("LR : 0x%016" PRIx64 "\n", regs->lr); ram_getspr(thread, 9, ®s->ctr); printf("CTR : 0x%016" PRIx64 "\n", regs->ctr); ram_getspr(thread, 815, ®s->tar); printf("TAR : 0x%016" PRIx64 "\n", regs->tar); ram_getcr(thread, ®s->cr); printf("CR : 0x%08" PRIx32 "\n", regs->cr); ram_getxer(thread, ®s->xer); printf("XER : 0x%08" PRIx64 "\n", regs->xer); printf("GPRS :\n"); for (i = 0; i < 32; i++) { ram_getgpr(thread, i, ®s->gprs[i]); printf(" 0x%016" PRIx64 "", regs->gprs[i]); if (i % 4 == 3) printf("\n"); } ram_getspr(thread, 318, ®s->lpcr); printf("LPCR : 0x%016" PRIx64 "\n", regs->lpcr); ram_getspr(thread, 464, ®s->ptcr); printf("PTCR : 0x%016" PRIx64 "\n", regs->ptcr); ram_getspr(thread, 319, ®s->lpidr); printf("LPIDR : 0x%016" PRIx64 "\n", regs->lpidr); ram_getspr(thread, 48, ®s->pidr); printf("PIDR : 0x%016" PRIx64 "\n", regs->pidr); ram_getspr(thread, 190, ®s->hfscr); printf("HFSCR : 0x%016" PRIx64 "\n", regs->hfscr); ram_getspr(thread, 306, &value); regs->hdsisr = value; printf("HDSISR: 0x%08" PRIx32 "\n", regs->hdsisr); ram_getspr(thread, 307, ®s->hdar); printf("HDAR : 0x%016" PRIx64 "\n", regs->hdar); ram_getspr(thread, 339, &value); regs->heir = value; printf("HEIR : 0x%016" PRIx32 "\n", regs->heir); ram_getspr(thread, 1008, ®s->hid); printf("HID0 : 0x%016" PRIx64 "\n", regs->hid); ram_getspr(thread, 314, ®s->hsrr0); printf("HSRR0 : 0x%016" PRIx64 "\n", regs->hsrr0); ram_getspr(thread, 315, ®s->hsrr1); printf("HSRR1 : 0x%016" PRIx64 "\n", regs->hsrr1); ram_getspr(thread, 310, ®s->hdec); printf("HDEC : 0x%016" PRIx64 "\n", regs->hdec); ram_getspr(thread, 304, ®s->hsprg0); printf("HSPRG0: 0x%016" PRIx64 "\n", regs->hsprg0); ram_getspr(thread, 305, ®s->hsprg1); printf("HSPRG1: 0x%016" PRIx64 "\n", regs->hsprg1); ram_getspr(thread, 153, ®s->fscr); printf("FSCR : 0x%016" PRIx64 "\n", regs->fscr); ram_getspr(thread, 18, &value); regs->dsisr = value; printf("DSISR : 0x%08" PRIx32 "\n", regs->dsisr); ram_getspr(thread, 19, ®s->dar); printf("DAR : 0x%016" PRIx64 "\n", regs->dar); ram_getspr(thread, 26, ®s->srr0); printf("SRR0 : 0x%016" PRIx64 "\n", regs->srr0); ram_getspr(thread, 27, ®s->srr1); printf("SRR1 : 0x%016" PRIx64 "\n", regs->srr1); ram_getspr(thread, 22, ®s->dec); printf("DEC : 0x%016" PRIx64 "\n", regs->dec); ram_getspr(thread, 268, ®s->tb); printf("TB : 0x%016" PRIx64 "\n", regs->tb); ram_getspr(thread, 272, ®s->sprg0); printf("SPRG0 : 0x%016" PRIx64 "\n", regs->sprg0); ram_getspr(thread, 273, ®s->sprg1); printf("SPRG1 : 0x%016" PRIx64 "\n", regs->sprg1); ram_getspr(thread, 274, ®s->sprg2); printf("SPRG2 : 0x%016" PRIx64 "\n", regs->sprg2); ram_getspr(thread, 275, ®s->sprg3); printf("SPRG3 : 0x%016" PRIx64 "\n", regs->sprg3); ram_getspr(thread, 896, ®s->ppr); printf("PPR : 0x%016" PRIx64 "\n", regs->ppr); CHECK_ERR(t->ram_destroy(t)); return 0; } pdbg-2.0/libpdbg/chip.h000066400000000000000000000017301336450571500150000ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 __LIBPDBG_CHIP_H #define __LIBPDBG_CHIP_H uint64_t mfspr(uint64_t reg, uint64_t spr) __attribute__ ((visibility("hidden"))); uint64_t mtspr(uint64_t spr, uint64_t reg) __attribute__ ((visibility("hidden"))); int ram_instructions(struct pdbg_target *thread_target, uint64_t *opcodes, uint64_t *results, int len, unsigned int lpar) __attribute__ ((visibility("hidden"))); #endif pdbg-2.0/libpdbg/compiler.h000066400000000000000000000027561336450571500157000ustar00rootroot00000000000000/* 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 __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 */ pdbg-2.0/libpdbg/debug.c000066400000000000000000000024621336450571500151410ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "debug.h" static int pdbg_loglevel = PDBG_ERROR; static void pdbg_logfunc_default(int loglevel, const char *fmt, va_list ap) { vfprintf(stderr, fmt, ap); } static pdbg_log_func_t pdbg_logfunc = pdbg_logfunc_default; void pdbg_set_logfunc(pdbg_log_func_t fn) { if (fn == NULL) return; pdbg_logfunc = fn; } void pdbg_set_loglevel(int loglevel) { if (loglevel < PDBG_ERROR) pdbg_loglevel = PDBG_ERROR; else if (loglevel > PDBG_DEBUG) pdbg_loglevel = PDBG_DEBUG; else pdbg_loglevel = loglevel; } void pdbg_log(int loglevel, const char *fmt, ...) { va_list ap; if (loglevel > pdbg_loglevel) return; va_start(ap, fmt); pdbg_logfunc(loglevel, fmt, ap); va_end(ap); } pdbg-2.0/libpdbg/debug.h000066400000000000000000000021441336450571500151430ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 __LIBPDBG_DEBUG_H #define __LIBPDBG_DEBUG_H #include "libpdbg.h" void pdbg_log(int log_level, const char* fmt, ...) __attribute__((format (printf, 2, 3))); #define PR_ERROR(x, args...) \ pdbg_log(PDBG_ERROR, x, ##args) #define PR_WARNING(x, args...) \ pdbg_log(PDBG_WARNING, x, ##args) #define PR_NOTICE(x, args...) \ pdbg_log(PDBG_NOTICE, x, ##args) #define PR_INFO(x, args...) \ pdbg_log(PDBG_INFO, x, ##args) #define PR_DEBUG(x, args...) \ pdbg_log(PDBG_DEBUG, "%s[%d]: " x, __FUNCTION__, __LINE__, ##args) #endif pdbg-2.0/libpdbg/device.c000066400000000000000000000477041336450571500153220ustar00rootroot00000000000000/* 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 "device.h" #include #include #include #include "target.h" #include #include #include #include #include "debug.h" #define zalloc(size) calloc(1, size) #define prerror printf #define is_rodata(p) false /* Used to give unique handles. */ u32 last_phandle = 0; struct pdbg_target *dt_root; struct pdbg_target *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); } struct pdbg_target *dt_new_node(const char *name, const void *fdt, int node_offset) { struct hw_unit_info *hw_info = NULL; const struct fdt_property *prop; struct pdbg_target *node; size_t size = sizeof(*node); if (fdt) { prop = fdt_get_property(fdt, node_offset, "compatible", NULL); if (prop) { int i, prop_len = fdt32_to_cpu(prop->len); /* * If I understand correctly, the property we have * here can be a stringlist with a few compatible * strings */ i = 0; while (i < prop_len) { hw_info = find_compatible_target(&prop->data[i]); if (hw_info) { size = hw_info->size; break; } i += strlen(&prop->data[i]) + 1; } } } node = calloc(1, size); if (!node) { prerror("Failed to allocate node\n"); abort(); } if (hw_info) { struct pdbg_target_class *target_class; /* hw_info->hw_unit points to a per-target struct type. This * works because the first member in the per-target struct is * guaranteed to be the struct pdbg_target (see the comment * above DECLARE_HW_UNIT). */ memcpy(node, hw_info->hw_unit, size); target_class = get_target_class(node->class); list_add_tail(&target_class->targets, &node->class_link); } node->dn_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; } static const char *get_unitname(const struct pdbg_target *node) { const char *c = strchr(node->dn_name, '@'); if (!c) return NULL; return c + 1; } int dt_cmp_subnodes(const struct pdbg_target *a, const struct pdbg_target *b) { const char *a_unit = get_unitname(a); const char *b_unit = get_unitname(b); ptrdiff_t basenamelen = a_unit - a->dn_name; /* sort hex unit addresses by number */ if (a_unit && b_unit && !strncmp(a->dn_name, b->dn_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->dn_name, b->dn_name); } bool dt_attach_root(struct pdbg_target *parent, struct pdbg_target *root) { struct pdbg_target *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->dn_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 pdbg_target *dn) { if (!dn) return; free_name(dn->dn_name); free(dn); } char *dt_get_path(const struct pdbg_target *node) { unsigned int len = 0; const struct pdbg_target *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->dn_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->dn_name); p -= len; memcpy(p, n->dn_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 pdbg_target *dt_find_by_path(struct pdbg_target *root, const char *path) { struct pdbg_target *n; const char *pn, *pa = NULL, *p = path, *nn = NULL, *na = NULL; 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->dn_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 pdbg_target *dt_find_by_name(struct pdbg_target *root, const char *name) { struct pdbg_target *child, *match; list_for_each(&root->children, child, list) { if (!strcmp(child->dn_name, name)) return child; match = dt_find_by_name(child, name); if (match) return match; } return NULL; } static struct dt_property *new_property(struct pdbg_target *node, const char *name, size_t size) { struct dt_property *p = malloc(sizeof(*p) + size); char *path; if (!p) { path = dt_get_path(node); prerror("Failed to allocate property \"%s\" for %s of %zu bytes\n", name, path, size); free(path); abort(); } if (dt_find_property(node, name)) { path = dt_get_path(node); prerror("Duplicate property \"%s\" in node %s\n", name, path); free(path); 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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *dt_first(const struct pdbg_target *root) { return list_top(&root->children, struct pdbg_target, list); } /* Return next node, or NULL. */ struct pdbg_target *dt_next(const struct pdbg_target *root, const struct pdbg_target *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 pdbg_target,list); /* No more siblings, move up to parent. */ prev = prev->parent; } while (prev != root); return NULL; } struct dt_property *__dt_find_property(struct pdbg_target *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; } struct dt_property *dt_find_property(const struct pdbg_target *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; } void dt_check_del_prop(struct pdbg_target *node, const char *name) { struct dt_property *p; p = __dt_find_property(node, name); if (p) dt_del_property(node, p); } const struct dt_property *dt_require_property(const struct pdbg_target *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 pdbg_target *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 pdbg_target *node, const char *compat) { const struct dt_property *p = dt_find_property(node, "compatible"); return dt_prop_find_string(p, compat); } struct pdbg_target *dt_find_compatible_node(struct pdbg_target *root, struct pdbg_target *prev, const char *compat) { struct pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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); } u32 dt_prop_get_u32_index(const struct pdbg_target *node, const char *prop, u32 index) { const struct dt_property *p = dt_require_property(node, prop, -1); return dt_property_get_cell(p, index); } const void *dt_prop_get(const struct pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *node) { struct pdbg_target *child; struct dt_property *p; while ((child = list_top(&node->children, struct pdbg_target, 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); } enum pdbg_target_status str_to_status(const char *status) { if (!strcmp(status, "enabled")) { /* There isn't really a use case for this at the moment except * perhaps as some kind of expert mode which bypasses the usual * probing process. However simply marking a top-level node as * enabled would not be enough to make parent nodes enabled so * this wouldn't work as expected anyway. */ assert(0); return PDBG_TARGET_ENABLED; } else if (!strcmp(status, "disabled")) return PDBG_TARGET_DISABLED; else if (!strcmp(status, "mustexist")) return PDBG_TARGET_MUSTEXIST; else if (!strcmp(status, "nonexistent")) return PDBG_TARGET_NONEXISTENT; else if (!strcmp(status, "unknown")) return PDBG_TARGET_UNKNOWN; else assert(0); } int dt_expand_node(struct pdbg_target *node, const void *fdt, int fdt_node) { const struct fdt_property *prop; int offset, nextoffset, err; struct pdbg_target *child; const char *name; uint32_t tag; uint32_t data; if (((err = fdt_check_header(fdt)) != 0) || (fdt_node < 0) || (fdt_node % FDT_TAGSIZE) || (fdt_next_tag(fdt, fdt_node, &fdt_node) != FDT_BEGIN_NODE)) { prerror("FDT: Error %d parsing node 0x%x\n", err, fdt_node); return -1; } nextoffset = fdt_node; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_PROP: prop = fdt_offset_ptr(fdt, offset, 0); name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); if (strcmp("index", name) == 0) { memcpy(&data, prop->data, sizeof(data)); node->index = fdt32_to_cpu(data); } if (strcmp("status", name) == 0) node->status = str_to_status(prop->data); 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_node(name, fdt, offset); 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) { PR_DEBUG("FDT: Parsing fdt @%p\n", fdt); 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) | be32toh(*(p++)); return ret; } u32 dt_n_address_cells(const struct pdbg_target *node) { if (!node->parent) return 0; return dt_prop_get_u32_def(node->parent, "#address-cells", 2); } u32 dt_n_size_cells(const struct pdbg_target *node) { if (!node->parent) return 0; return dt_prop_get_u32_def(node->parent, "#size-cells", 1); } u64 dt_get_address(const struct pdbg_target *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 pdbg_target *node) { const struct dt_property *prop; for (; node; node = node->parent) { prop = dt_find_property(node, "chip-id"); if (prop) return dt_property_get_cell(prop, 0); } return 0xffffffff; } u32 dt_get_chip_id(const struct pdbg_target *node) { u32 id = __dt_get_chip_id(node); assert(id != 0xffffffff); return id; } struct pdbg_target *dt_find_compatible_node_on_chip(struct pdbg_target *root, struct pdbg_target *prev, const char *compat, uint32_t chip_id) { struct pdbg_target *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 pdbg_target *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 pdbg_target *node, unsigned int index, u64 *out_size) { /* XXX TODO */ return dt_get_address(node, index, out_size); } bool dt_node_is_enabled(struct pdbg_target *node) { const struct dt_property *p = dt_find_property(node, "status"); if (!p) return true; return p->len > 1 && p->prop[0] == 'o' && p->prop[1] == 'k'; } pdbg-2.0/libpdbg/device.h000066400000000000000000000201741336450571500153170ustar00rootroot00000000000000/* 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 "compiler.h" /* 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 */]; }; /* This is shared with device_tree.c .. make it static when * the latter is gone (hopefully soon) */ extern u32 last_phandle; extern struct pdbg_target *dt_root; extern struct pdbg_target *dt_chosen; /* Create a new node. */ struct pdbg_target *dt_new_node(const char *name, const void *fdt, int offset); /* Graft a root node into this tree. */ bool dt_attach_root(struct pdbg_target *parent, struct pdbg_target *root); /* Add a property node, various forms. */ struct dt_property *dt_add_property(struct pdbg_target *node, const char *name, const void *val, size_t size); struct dt_property *dt_add_property_string(struct pdbg_target *node, const char *name, const char *value); struct dt_property *dt_add_property_nstr(struct pdbg_target *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 pdbg_target *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 pdbg_target *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 pdbg_target *node, const char *name, int count, ...); static inline struct dt_property *dt_add_property_u64(struct pdbg_target *node, const char *name, u64 val) { return dt_add_property_cells(node, name, (u32)(val >> 32), (u32)val); } void dt_del_property(struct pdbg_target *node, struct dt_property *prop); void dt_check_del_prop(struct pdbg_target *node, const char *name); /* 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 pdbg_target *dt_first(const struct pdbg_target *root); /* Return next node, or NULL. */ struct pdbg_target *dt_next(const struct pdbg_target *root, const struct pdbg_target *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 pdbg_target *node, const char *compat); /* Find a node based on compatible property */ struct pdbg_target *dt_find_compatible_node(struct pdbg_target *root, struct pdbg_target *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 pdbg_target *dt_find_compatible_node_on_chip(struct pdbg_target *root, struct pdbg_target *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;) /* Check status property */ bool dt_node_is_enabled(struct pdbg_target *node); /* Build the full path for a node. Return a new block of memory, caller * shall free() it */ char *dt_get_path(const struct pdbg_target *node); /* Find a node by path */ struct pdbg_target *dt_find_by_path(struct pdbg_target *root, const char *path); /* Find a child node by name */ struct pdbg_target *dt_find_by_name(struct pdbg_target *root, const char *name); /* Find a property by name. */ struct dt_property *dt_find_property(const struct pdbg_target *node,\ const char *name); const struct dt_property *dt_require_property(const struct pdbg_target *node, const char *name, int wanted_len); /* non-const variant */ struct dt_property *__dt_find_property(struct pdbg_target *node, const char *name); /* Find a property by name, check if it's the same as val. */ bool dt_has_node_property(const struct pdbg_target *node, const char *name, const char *val); /* Free a node (and any children). */ void dt_free(struct pdbg_target *node); /* Parse an initial fdt */ void dt_expand(const void *fdt); int dt_expand_node(struct pdbg_target *node, const void *fdt, int fdt_node) __warn_unused_result; /* Simplified accessors */ u64 dt_prop_get_u64(const struct pdbg_target *node, const char *prop); u64 dt_prop_get_u64_def(const struct pdbg_target *node, const char *prop, u64 def); u32 dt_prop_get_u32(const struct pdbg_target *node, const char *prop); u32 dt_prop_get_u32_def(const struct pdbg_target *node, const char *prop, u32 def); u32 dt_prop_get_u32_index(const struct pdbg_target *node, const char *prop, u32 index); const void *dt_prop_get(const struct pdbg_target *node, const char *prop); const void *dt_prop_get_def(const struct pdbg_target *node, const char *prop, void *def); const void *dt_prop_get_def_size(const struct pdbg_target *node, const char *prop, void *def, size_t *len); u32 dt_prop_get_cell(const struct pdbg_target *node, const char *prop, u32 cell); u32 dt_prop_get_cell_def(const struct pdbg_target *node, const char *prop, u32 cell, u32 def); /* Parsing helpers */ u32 dt_n_address_cells(const struct pdbg_target *node); u32 dt_n_size_cells(const struct pdbg_target *node); u64 dt_get_number(const void *pdata, unsigned int cells); /* Find an 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 pdbg_target *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 pdbg_target *node, unsigned int index, u64 *out_size); /* Count "reg" property entries */ unsigned int dt_count_addresses(const struct pdbg_target *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 pdbg_target *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 pdbg_target *a, const struct pdbg_target *b); #endif /* __DEVICE_H */ pdbg-2.0/libpdbg/fake.c000066400000000000000000000040531336450571500147570ustar00rootroot00000000000000/* copyright 2016 ibm corp. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without 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 "operations.h" #include #include static int fake_fsi_read(struct fsi *fsi, uint32_t addr, uint32_t *value) { *value = 0xfeed0cfa; printf("fake_fsi_read(0x%04" PRIx32 ", 0x%04" PRIx32 ")\n", addr, *value); return 0; } static int fake_fsi_write(struct fsi *fsi, uint32_t addr, uint32_t value) { printf("fake_fsi_write(0x%04" PRIx32 ", 0x%04" PRIx32 ")\n", addr, value); return 0; } static struct fsi fake_fsi = { .target = { .name = "Fake FSI", .compatible = "ibm,fake-fsi", .class = "fsi", }, .read = fake_fsi_read, .write = fake_fsi_write, }; DECLARE_HW_UNIT(fake_fsi); static int fake_pib_read(struct pib *pib, uint64_t addr, uint64_t *value) { *value = 0xdeadbeef; printf("fake_pib_read(0x%08" PRIx64 ", 0x%08" PRIx64 ")\n", addr, *value); return 0; } static int fake_pib_write(struct pib *pib, uint64_t addr, uint64_t value) { printf("fake_pib_write(0x%08" PRIx64 ", 0x%08" PRIx64 ")\n", addr, value); return 0; } static struct pib fake_pib = { .target = { .name = "Fake PIB", .compatible = "ibm,fake-pib", .class = "pib", }, .read = fake_pib_read, .write = fake_pib_write, }; DECLARE_HW_UNIT(fake_pib); static struct core fake_core = { .target = { .name = "Fake Core", .compatible = "ibm,fake-core", .class = "core", }, }; DECLARE_HW_UNIT(fake_core); static struct thread fake_thread = { .target = { .name = "Fake Thread", .compatible = "ibm,fake-thread", .class = "thread", }, }; DECLARE_HW_UNIT(fake_thread); pdbg-2.0/libpdbg/host.c000066400000000000000000000054231336450571500150300ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "bitutils.h" #include "operations.h" #include "target.h" #define XSCOM_BASE_PATH "/sys/kernel/debug/powerpc/scom" static uint64_t xscom_mangle_addr(uint64_t addr) { uint64_t tmp; /* * Shift the top 4 bits (indirect mode) down by 4 bits so we * don't lose going through the debugfs interfaces. */ tmp = (addr & 0xf000000000000000) >> 4; addr &= 0x00ffffffffffffff; addr |= tmp; /* Shift up by 3 for debugfs */ return addr << 3; } static int xscom_read(struct pib *pib, uint64_t addr, uint64_t *val) { int rc; int fd = *(int *) pib->priv; addr = xscom_mangle_addr(addr); lseek64(fd, addr, SEEK_SET); rc = read(fd, val, 8); if (rc != 8) return -1; return 0; } static int xscom_write(struct pib *pib, uint64_t addr, uint64_t val) { int rc; int fd = *(int *) pib->priv; addr = xscom_mangle_addr(addr); lseek64(fd, addr, SEEK_SET); rc = write(fd, &val, 8); if (rc != 8) return -1; return 0; } static int host_pib_probe(struct pdbg_target *target) { struct pib *pib = target_to_pib(target); int *fd; char *access_fn; uint32_t chip_id; fd = malloc(sizeof(fd)); if (!fd) return -1; chip_id = dt_get_chip_id(target); if (chip_id == -1) goto out; /* This check should probably be done earlier */ if (access(XSCOM_BASE_PATH, F_OK) == -1) { PR_ERROR("Can not access %s. ", XSCOM_BASE_PATH); PR_ERROR("Is CONFIG_SCOM_DEBUGFS set? "); PR_ERROR("You may need to re-run the command as root.\n"); } if (asprintf(&access_fn, "%s/%08d/access", XSCOM_BASE_PATH, chip_id) < 0) goto out; *fd = open(access_fn, O_RDWR); free(access_fn); if (*fd < 0) goto out; pib->priv = fd; return 0; out: free(fd); return -1; } static struct pib host_pib = { .target = { .name = "Host based debugfs SCOM", .compatible = "ibm,host-pib", .class = "pib", .probe = host_pib_probe, }, .read = xscom_read, .write = xscom_write, }; DECLARE_HW_UNIT(host_pib); pdbg-2.0/libpdbg/htm.c000066400000000000000000000634041336450571500146460ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "operations.h" #include "bitutils.h" #include "target.h" #include "debug.h" #define HTM_ERR(x) ({int rc = (x); if (rc) {PR_ERROR("HTM Error %d %s:%d\n", \ rc, __FILE__, __LINE__);} \ rc;}) #define MIN(x,y) ((x < y) ? x : y) #define DEBUGFS_POWERPC "/sys/kernel/debug/powerpc" #define DEBUGFS_SCOM DEBUGFS_POWERPC"/scom" #define DEBUGFS_MEMTRACE DEBUGFS_POWERPC"/memtrace" #define DEBUGFS_MEMTRACE_ENABLE DEBUGFS_MEMTRACE"/enable" /* * This is a CORE register not a HTM register, don't pass the HTM * target to it. */ #define HID0_REGISTER 0x1329C #define HID0_ONE_PER_GROUP PPC_BIT(0) #define HID0_DO_SINGLE PPC_BIT(1) #define HID0_SINGLE_DECODE PPC_BIT(4) #define HID0_EN_INST_TRACE PPC_BIT(17) #define HID0_TRACE_EN PPC_BIT(23) #define HID0_TRACE_BITS (HID0_ONE_PER_GROUP | HID0_DO_SINGLE |\ HID0_SINGLE_DECODE | HID0_EN_INST_TRACE |\ HID0_TRACE_EN) /* * This is a CORE register not a HTM register, don't pass the HTM * target to it. */ #define NCU_MODE_REGISTER 0x10C0A #define NCU_MODE_HTM_ENABLE PPC_BIT(0) #define HTM_COLLECTION_MODE 0 #define HTM_MODE_ENABLE PPC_BIT(0) #define HTM_MODE_CONTENT_SEL PPC_BITMASK(1,2) #define NHTM_MODE_CAPTURE PPC_BITMASK(4,12) #define NHTM_MODE_CAPTURE_PMISC PPC_BIT(5) #define NHTM_MODE_CRESP_PRECISE PPC_BIT(6) #define CHTM_MODE_NO_ASSERT_LLAT_L3 PPC_BIT(6) #define HTM_MODE_WRAP PPC_BIT(13) #define HTM_MEMORY_CONF 1 #define HTM_MEM_ALLOC PPC_BIT(0) #define HTM_MEM_SCOPE PPC_BITMASK(1,3) /* Note: the next 3 change on P9 */ #define HTM_MEM_PRIORITY PPC_BITMASK(4,5) #define HTM_MEM_SIZE_SMALL PPC_BIT(13) #define HTM_MEM_BASE PPC_BITMASK(14,39) #define HTM_MEM_SIZE PPC_BITMASK(40,48) #define HTM_STATUS 2 #define HTM_STATUS_MASK PPC_BITMASK(2,19) #define HTM_STATUS_CRESP_OV PPC_BIT(2) #define HTM_STATUS_REPAIR PPC_BIT(3) #define HTM_STATUS_BUF_WAIT PPC_BIT(4) #define HTM_STATUS_TRIG_DROPPED PPC_BIT(5) #define HTM_STATUS_ADDR_ERROR PPC_BIT(6) #define HTM_STATUS_REC_DROPPED PPC_BIT(7) #define HTM_STATUS_INIT PPC_BIT(8) #define HTM_STATUS_PREREQ PPC_BIT(9) #define HTM_STATUS_READY PPC_BIT(10) #define HTM_STATUS_TRACING PPC_BIT(11) #define HTM_STATUS_PAUSED PPC_BIT(12) #define HTM_STATUS_FLUSH PPC_BIT(13) #define HTM_STATUS_COMPLETE PPC_BIT(14) #define HTM_STATUS_ENABLE PPC_BIT(15) #define HTM_STATUS_STAMP PPC_BIT(16) #define HTM_STATUS_SCOM_ERROR PPC_BIT(17) #define HTM_STATUS_PARITY_ERROR PPC_BIT(18) #define HTM_STATUS_INVALID_CRESP PPC_BIT(19) #define HTM_STATUS_STATE_MASK (PPC_BITMASK(8,16) | HTM_STATUS_REPAIR) #define HTM_STATUS_ERROR_MASK (HTM_STATUS_CRESP_OV | PPC_BITMASK(4,7) | PPC_BITMASK(17,19)) #define HTM_LAST_ADDRESS 3 #define HTM_LAST_ADDRESS_MASK PPC_BITMASK(8,56) #define HTM_SCOM_TRIGGER 4 #define HTM_TRIG_START PPC_BIT(0) #define HTM_TRIG_STOP PPC_BIT(1) #define HTM_TRIG_PAUSE PPC_BIT(2) #define HTM_TRIG_STOP_ALT PPC_BIT(3) #define HTM_TRIG_RESET PPC_BIT(4) #define HTM_TRIG_MARK_VALID PPC_BIT(5) #define HTM_TRIG_MARK_TYPE PPC_BITMASK(6,15) #define HTM_TRIGGER_CONTROL 5 #define HTM_CTRL_TRIG PPC_BITMASK(0,1) #define HTM_CTRL_MASK PPC_BITMASK(4,5) #define HTM_CTRL_XSTOP_STOP PPC_BIT(13) #define NHTM_FILTER_CONTROL 6 #define NHTM_FILTER_RCMD_SCOPE PPC_BITMASK(17,19) #define NHTM_FILTER_RCMD_SOURCE PPC_BITMASK(20,21) #define NHTM_FILTER_CRESP_PATTERN PPC_BITMASK(27,31) #define NHTM_FILTER_MASK PPC_BITMASK(32,54) #define NHTM_FILTER_CRESP_MASK PPC_BITMASK(59,63) #define NHTM_TTYPE_FILTER_CONTROL 7 #define NHTM_TTYPE_TYPE_MASK PPC_BITMASK(17,23) #define NHTM_TTYPE_SIZE_MASK PPC_BITMASK(24,31) #define NHTM_CONFIGURATION 8 #define NHTM_CONF_HANG_DIV_RATIO PPC_BITMASK(0,4) #define NHTM_CONF_RTY_DROP_COUNT PPC_BITMASK(5,8) #define NHTM_CONF_DROP_PRIORITY_INC PPC_BIT(9) #define NHTM_CONF_RETRY_BACKOFF PPC_BIT(10) #define NHTM_CONF_OPERALIONAL_HANG PPC_BIT(11) #define NHTM_FLEX_MUX 9 #define NHTM_FLEX_MUX_MASK PPC_BITMASK(0,35) #define NHTM_FLEX_DEFAULT 0xCB3456129 enum htm_state { INIT, REPAIR, PREREQ, READY, TRACING, PAUSED, FLUSH, COMPLETE, ENABLE, STAMP, UNINITIALIZED, }; enum htm_error { NONE, CRESP_OV, BUF_WAIT, TRIGGER_DROPPED, ADDR_ERROR, RECORD_DROPPED, SCOM_ERROR, PARITY_ERROR, INVALID_CRESP, }; struct htm_status { enum htm_state state; enum htm_error error; bool mem_size_select; uint16_t mem_size; uint64_t mem_base; uint64_t mem_last; uint64_t raw; }; static struct htm *check_and_convert(struct pdbg_target *target) { if (!pdbg_target_is_class(target, "nhtm") && !pdbg_target_is_class(target, "chtm")) return NULL; return target_to_htm(target); } int htm_start(struct pdbg_target *target) { struct htm *htm = check_and_convert(target); return htm ? htm->start(htm) : -1; } int htm_stop(struct pdbg_target *target) { struct htm *htm = check_and_convert(target); return htm ? htm->stop(htm) : -1; } int htm_status(struct pdbg_target *target) { struct htm *htm = check_and_convert(target); return htm ? htm->status(htm) : -1; } int htm_dump(struct pdbg_target *target, char *filename) { struct htm *htm = check_and_convert(target); if (!htm || !filename) return -1; return htm->dump(htm, filename); } int htm_record(struct pdbg_target *target, char *filename) { struct htm *htm = check_and_convert(target); if (!htm || !filename) return -1; return htm->record(htm, filename); } static int get_status(struct htm *htm, struct htm_status *status) { uint64_t val; if (HTM_ERR(pib_read(&htm->target, HTM_STATUS, &status->raw))) return -1; switch (status->raw & HTM_STATUS_ERROR_MASK) { case HTM_STATUS_CRESP_OV: status->error = CRESP_OV; break; case HTM_STATUS_BUF_WAIT: status->error = BUF_WAIT; break; case HTM_STATUS_TRIG_DROPPED: status->error = TRIGGER_DROPPED; break; case HTM_STATUS_ADDR_ERROR: status->error = ADDR_ERROR; break; case HTM_STATUS_REC_DROPPED: status->error = RECORD_DROPPED; break; case HTM_STATUS_SCOM_ERROR: status->error = SCOM_ERROR; break; case HTM_STATUS_PARITY_ERROR: status->error = PARITY_ERROR; break; case HTM_STATUS_INVALID_CRESP: status->error = INVALID_CRESP; break; default: status->error = NONE; } switch (status->raw & HTM_STATUS_STATE_MASK) { case HTM_STATUS_REPAIR: status->state = REPAIR; break; case HTM_STATUS_INIT: status->state = INIT; break; case HTM_STATUS_PREREQ: status->state = PREREQ; break; case HTM_STATUS_READY: status->state = READY; break; case HTM_STATUS_TRACING: status->state = TRACING; break; case HTM_STATUS_PAUSED: status->state = PAUSED; break; case HTM_STATUS_FLUSH: status->state = FLUSH; break; case HTM_STATUS_COMPLETE: status->state = COMPLETE; break; case HTM_STATUS_ENABLE: status->state = ENABLE; break; case HTM_STATUS_STAMP: status->state = STAMP; break; default: status->state = UNINITIALIZED; } if (HTM_ERR(pib_read(&htm->target, HTM_MEMORY_CONF, &val))) return -1; status->mem_size_select = val & HTM_MEM_SIZE_SMALL; status->mem_size = GETFIELD(HTM_MEM_SIZE,val); status->mem_base = val & HTM_MEM_BASE; if (HTM_ERR(pib_read(&htm->target, HTM_LAST_ADDRESS, &status->mem_last))) return -1; return 0; } /* * This sequence will trigger all HTM to start at "roughly" the same * time. * We don't have any documentation on what the commands are, they do * work on POWER9 dd2.0 * * Alternatively, writing a 1 into the HTM Start Trigger bit works * too, it has to be done for each HTM but its much cleaner and * clearly what is going on. */ #if 0 static int do_adu_magic(struct pdbg_target *target, uint32_t index, uint64_t *arg1, uint64_t *arg2) { uint64_t val; int i = 0; /* * pib_write(target, 1, PPC_BIT(11); get the lock, but since * we don't check, why bother? */ if (HTM_ERR(pib_write(target, 1, PPC_BIT(3) | PPC_BIT(4) | PPC_BIT(14)))) return -1; /* If bothering with the lock, write PPC_BIT(11) here */ if (HTM_ERR(pib_write(target, 1, 0))) return -1; if (HTM_ERR(pib_write(target, 0, PPC_BIT(46)))) return -1; if (HTM_ERR(pib_write(target, 1, 0x2210aab140000000))) return -1; do { sleep(1); if (HTM_ERR(pib_read(target, 3, &val))) return -1; i++; } while (val != 0x2000000000000004 && i < 10); if (val != 0x2000000000000004) { P_INFO("Unexpected status on HTM start trigger PMISC command: 0x%" PRIx64 "\n", val); return -1; } /* * If we locked the adu before we should be polite and * pib_write(target, 1, 0); */ return 1; } #endif /* * This function doesn't do the update_mcs_regs() from the older p8 * only tool. Not clear what this actually does. Attempting to ignore * the problem. * The scoms it does use are: * SCOM ADDRESS FOR MCS4 * #define MCS4_MCFGPQ 0x02011c00 * #define MCS4_MCMODE 0x02011c07 * #define MCS4_FIRMASK 0x02011c43 * * SCOM ADDRESS FOR MCS5 * #define MCS5_MCFGPQ 0x02011c80 * #define MCS5_MCMODE 0x02011c87 * #define MCS5_FIRMASK 0x02011cc3 * * SCOM ADDRESS FOR MCS6 * #define MCS6_MCFGPQ 0x02011d00 * #define MCS6_MCMODE 0x02011d07 * #define MCS6_FIRMASK 0x02011d43 * * SCOM ADDRESS FOR MCS7 * #define MCS7_MCFGPQ 0x02011d80 * #define MCS7_MCMODE 0x02011d87 * #define MCS7_FIRMASK 0x02011dc3 */ static int configure_debugfs_memtrace(struct htm *htm) { uint64_t memtrace_size; FILE *file; file = fopen(DEBUGFS_MEMTRACE_ENABLE, "r+"); if (!file) { PR_ERROR("Couldn't open %s: %m\n", DEBUGFS_MEMTRACE_ENABLE); return -1; } if (fscanf(file, "0x%016" PRIx64 "\n", &memtrace_size) != 1) { PR_ERROR("fscanf() didn't match: %m\n"); fclose(file); return -1; } if (memtrace_size == 0) { uint64_t size = 1 << 30; /* 1GB... TODO */ int rc = fprintf(file, "0x%016" PRIx64 "\n", size); PR_DEBUG("memtrace_size is zero! Informing the kernel!\n"); if (rc <= 0) { PR_ERROR("Couldn't set memtrace_size\n"); PR_ERROR("Attempted to write: 0x%016" PRIx64 " got %d: %m\n", size, rc); fclose(file); return -1; } } fclose(file); return 0; } static int configure_chtm(struct htm *htm, bool wrap) { uint64_t hid0, ncu, val; struct pdbg_target *core; if (!pdbg_target_is_class(&htm->target, "chtm")) return 0; if (HTM_ERR(configure_debugfs_memtrace(htm))) return -1; val = wrap ? HTM_MODE_WRAP : 0; if (HTM_ERR(pib_write(&htm->target, HTM_COLLECTION_MODE, HTM_MODE_ENABLE | val))) return -1; core = pdbg_target_require_parent("core", &htm->target); if (HTM_ERR(pib_read(core, HID0_REGISTER, &hid0))) return -1; hid0 |= HID0_TRACE_BITS; if (HTM_ERR(pib_write(core, HID0_REGISTER, hid0))) return -1; if (HTM_ERR(pib_read(core, NCU_MODE_REGISTER, &ncu))) return -1; ncu |= NCU_MODE_HTM_ENABLE; if (HTM_ERR(pib_write(core, NCU_MODE_REGISTER, ncu))) return -1; return 0; } static int deconfigure_chtm(struct htm *htm) { uint64_t hid0, ncu; struct pdbg_target *core; if (!pdbg_target_is_class(&htm->target, "chtm")) return 0; core = pdbg_target_require_parent("core", &htm->target); if (HTM_ERR(pib_read(core, NCU_MODE_REGISTER, &ncu))) return -1; ncu &= ~NCU_MODE_HTM_ENABLE; if (HTM_ERR(pib_write(core, NCU_MODE_REGISTER, ncu))) return -1; if (HTM_ERR(pib_read(core, HID0_REGISTER, &hid0))) return -1; hid0 &= ~(HID0_TRACE_BITS); if (HTM_ERR(pib_write(core, HID0_REGISTER, hid0))) return -1; if (HTM_ERR(pib_write(&htm->target, HTM_COLLECTION_MODE,0))) return -1; // FIXME this needs kernel work to happen // if (HTM_ERR(deconfigure_debugfs_memtrace(htm))) // return -1; return 0; } static int configure_nhtm(struct htm *htm, bool wrap) { uint64_t val; if (!pdbg_target_is_class(&htm->target, "nhtm")) return 0; if (HTM_ERR(configure_debugfs_memtrace(htm))) return -1; /* * The constant is the VGTARGET field, taken from a cronus * booted system which presumably set it up correctly */ val = wrap ? HTM_MODE_WRAP : 0; if (HTM_ERR(pib_write(&htm->target, HTM_COLLECTION_MODE, HTM_MODE_ENABLE | NHTM_MODE_CRESP_PRECISE | val | 0xFFFF000000))) return -1; /* Stop on core xstop */ if (HTM_ERR(pib_write(&htm->target, HTM_TRIGGER_CONTROL, HTM_CTRL_XSTOP_STOP))) return -1;; /* * These values are taken from a cronus booted system. */ if (HTM_ERR(pib_write(&htm->target, NHTM_FILTER_CONTROL, NHTM_FILTER_MASK | /* no pattern matching */ NHTM_FILTER_CRESP_MASK))) /* no pattern matching */ return -1; if (HTM_ERR(pib_write(&htm->target, NHTM_TTYPE_FILTER_CONTROL, NHTM_TTYPE_TYPE_MASK | /* no pattern matching */ NHTM_TTYPE_SIZE_MASK ))) /* no pattern matching */ return -1; if (dt_node_is_compatible(&htm->target, "ibm,power9-nhtm")) { if (HTM_ERR(pib_read(&htm->target, NHTM_FLEX_MUX, &val))) return -1; if (GETFIELD(NHTM_FLEX_MUX_MASK, val) != NHTM_FLEX_DEFAULT) { PR_ERROR("The HTM Flex wasn't default value\n"); return -1; } } return 0; } static int deconfigure_nhtm(struct htm *htm) { if (!pdbg_target_is_class(&htm->target, "nhtm")) return 0; // FIXME: write and test this return 0; } static int is_startable(struct htm_status *status) { return (status->state == READY || status->state == PAUSED); } static char *get_debugfs_file(struct htm *htm, const char *file) { uint32_t chip_id; char *filename; chip_id = dt_get_chip_id(&htm->target); if (chip_id == -1) { PR_ERROR("Couldn't find a chip id\n"); return NULL; } if (asprintf(&filename, "%s/%08x/%s", DEBUGFS_MEMTRACE, chip_id, file) == -1) { PR_ERROR("Couldn't asprintf() '%s/%08x/size': %m\n", DEBUGFS_MEMTRACE, chip_id); return NULL; } return filename; } static int get_trace_size(struct htm *htm, uint64_t *size) { char *filename; FILE *file; int rc; filename = get_debugfs_file(htm, "size"); if (!filename) return -1; file = fopen(filename, "r"); if (file == NULL) { PR_ERROR("Failed to open %s: %m\n", filename); free(filename); return -1; } rc = fscanf(file, "0x%016" PRIx64 "\n", size); fclose(file); free(filename); return !(rc == 1); } static int get_trace_base(struct htm *htm, uint64_t *base) { char *filename; FILE *file; int rc; filename = get_debugfs_file(htm, "start"); if (!filename) return -1; file = fopen(filename, "r"); if (file == NULL) { PR_ERROR("Failed to open %s: %m\n", filename); free(filename); return -1; } rc = fscanf(file, "0x%016" PRIx64 "\n", base); fclose(file); free(filename); return !(rc == 1); } static int configure_memory(struct htm *htm) { uint64_t size, base, val, small, mem_size; int shift; if (HTM_ERR(get_trace_size(htm, &size))) return -1; if (HTM_ERR(get_trace_base(htm, &base))) return -1; if (HTM_ERR(pib_read(&htm->target, HTM_MEMORY_CONF, &val))) return -1; /* * Tell HTM that we've alloced mem, perhaps do this in probe(), * HTM searches for 0 -> 1 transition, so clear it now */ val &= ~HTM_MEM_ALLOC; if (HTM_ERR(pib_write(&htm->target, HTM_MEMORY_CONF, val))) return -1; if (HTM_ERR(pib_read(&htm->target, HTM_MEMORY_CONF, &val))) return -1; /* Put mem alloc back in */ val |= HTM_MEM_ALLOC; /* * HTMSC_MEM_SIZE_SMALL: * 0 = Trace Mem Size from 512M to 256G * 1 = Trace Mem Size from 16M to 8G */ if (size < 16777216UL) { PR_ERROR("memtrace size must be atleast 16MB. Currently:%" PRIx64 "\n", size); return -1; } if (size > 274877906944UL) { PR_ERROR("memtrace size must be smaller than 256GB. Currently:%" PRIx64 "\n", size); return -1; } /* * The hardware has two sizes, small and large * small: 16M - 8G * large: 512M - 256G * We use small for 16M-256, then large for 512M-256G * * Hardware buffer sizes: * Large Small HTMSC_MEM_SIZE encoding * 512M 16M 0b000000000 * 1G 32M 0b000000001 * 2G 64M 0b000000011 * 4G 128M 0b000000111 * 8G 256M 0b000001111 * 16G 512M 0b000011111 * 32G 1G 0b000111111 * 64G 2G 0b001111111 * 128G 4G 0b011111111 * 256G 8G 0b111111111 */ small = 0; if (size < 512*1024*1024) small = 1; val = SETFIELD(HTM_MEM_SIZE_SMALL, val, small); shift = 29; /* large */ if (small) shift = 24; mem_size = (size >> shift) - 1; val = SETFIELD(HTM_MEM_SIZE, val, mem_size); /* * Clear out the base * Don't use SETFIELD to write the base in since the value is * already shifted correct as read from the kernel, or'ing it * in works fine. */ val = SETFIELD(HTM_MEM_BASE, val, 0); val |= base; if (HTM_ERR(pib_write(&htm->target, HTM_MEMORY_CONF, val))) return -1; if (HTM_ERR(pib_write(&htm->target, HTM_SCOM_TRIGGER, HTM_TRIG_RESET))) return -1; return 0; } static int do_htm_reset(struct htm *htm, bool wrap) { struct htm_status status; if (HTM_ERR(get_status(htm, &status))) return -1; if (configure_nhtm(htm, wrap) < 0) return -1; if (configure_chtm(htm, wrap) < 0) return -1; if (HTM_ERR(configure_memory(htm))) return -1; return 1; } /* Stolen from p8chip.c */ #define RAS_MODE_REG 0x1 #define MR_THREAD_IN_DEBUG PPC_BIT(43) static int htm_toggle_debug_bit(struct htm *htm) { struct pdbg_target *target; struct pdbg_target *core; uint64_t reg; /* NHTM doesn't have a core as a parent but donesn't need this * bit toggled */ core = pdbg_target_parent("core", &htm->target); if (!core) return 0; /* nhtm case */ /* FIXME: this is a hack for P8 */ if (!dt_node_is_compatible(core, "ibm,power8-core")) { PR_ERROR("HTM is POWER8 only currently\n"); return -1; } pdbg_for_each_target("thread", core, target) { if (pdbg_target_index(target) == 0) { /* Need to set this bit to ensure HTM starts */ pib_read (target, RAS_MODE_REG, ®); pib_write(target, RAS_MODE_REG, reg | MR_THREAD_IN_DEBUG); pib_write(target, RAS_MODE_REG, reg); } } return 0; } static int __do_htm_start(struct htm *htm, bool wrap) { struct htm_status status; if (do_htm_reset(htm, wrap) < 0) return -1; if (HTM_ERR(get_status(htm, &status))) return -1; if (!is_startable(&status)) { PR_ERROR("HTM not in a startable state!\n"); return -1; } PR_INFO("* Sending START trigger to HTM\n"); if (HTM_ERR(pib_write(&htm->target, HTM_SCOM_TRIGGER, HTM_TRIG_MARK_VALID))) return -1; if (HTM_ERR(pib_write(&htm->target, HTM_SCOM_TRIGGER, HTM_TRIG_START))) return -1; if (htm_toggle_debug_bit(htm)) return -1; /* * Instead of the HTM_TRIG_START, this is where you might want * to call do_adu_magic() * for_each_child_target("adu", core, do_adu_magic, NULL, NULL); * see what I mean? */ return 1; } static int do_htm_start(struct htm *htm) { return __do_htm_start(htm, true); } static int do_htm_stop(struct htm *htm) { struct htm_status status; get_status(htm, &status); if (HTM_ERR(get_status(htm, &status))) return -1; if (status.state == UNINITIALIZED) { PR_INFO("* Skipping STOP trigger, HTM appears uninitialized\n"); return -1; } if (status.state == TRACING) { PR_INFO("* Sending STOP trigger to HTM\n"); if (HTM_ERR(pib_write(&htm->target, HTM_SCOM_TRIGGER, HTM_TRIG_STOP))) return -1; } else { PR_INFO("* Skipping STOP trigger, HTM is not running\n"); } if (deconfigure_chtm(htm) < 0) return -1; if (deconfigure_nhtm(htm) < 0) return -1; return 1; } static uint64_t htm_trace_size(struct htm_status *status) { uint64_t size; uint64_t mem_size = status->mem_size; if (status->mem_size_select) size = 16; else size = 512; while (mem_size) { size <<= 1; mem_size >>= 1; } return size << 20; } static bool htm_complete(struct htm_status *status) { return (status->state == COMPLETE); } static int htm_wait_complete(struct htm *htm) { struct htm_status status; while (1) { if (HTM_ERR(get_status(htm, &status))) return -1; PR_DEBUG("loop curr:0x%016" PRIx64 "\n", status.mem_last); if (htm_complete(&status)) break; usleep(100000); } return 0; } static int do_htm_status(struct htm *htm) { struct htm_status status; uint64_t val, total; int i, regs = 9; if (dt_node_is_compatible(&htm->target, "ibm,power9-nhtm")) regs++; PR_INFO("HTM register dump:\n"); for (i = 0; i < regs; i++) { if (HTM_ERR(pib_read(&htm->target, i, &val))) { PR_ERROR("Couldn't read HTM reg: %d\n", i); continue; } PR_INFO(" %d: 0x%016" PRIx64 "\n", i, val); } if (HTM_ERR(get_status(htm, &status))) return -1; total = htm_trace_size(&status); PR_DEBUG("HTM status : 0x%016" PRIx64 "\n", status.raw); printf("State: "); switch (status.state) { case INIT: printf("INIT"); break; case PREREQ: printf("PREREQ"); break; case READY: printf("READY"); break; case TRACING: printf("TRACING"); break; case PAUSED: printf("PAUSED"); break; case FLUSH: printf("FLUSH"); break; case COMPLETE: printf("COMPLETE"); break; case ENABLE: printf("ENABLE"); break; case STAMP: printf("STAMP"); break; default: printf("UNINITIALIZED"); } printf("\n"); printf("addr:0x%016" PRIx64 "\n", status.mem_base); printf("size:0x%016" PRIx64 " ", total); if (total >= 0x20000000) printf("[ %" PRIu64 "GB ]", total >> 30); else printf("[ %" PRIu64 "MB ]", total >> 20); printf("\n"); printf("curr:0x%016" PRIx64 "\n", status.mem_last); return 1; } static int copy_file(int output, int input, uint64_t size) { size_t r; int pipefd[2]; int rc = -1; if (pipe(pipefd)) { perror("pipe"); exit(1); } while (size) { r = splice(input, 0, pipefd[1], 0, size, 0); if (r == -1) { PR_ERROR("Failed to read\n"); goto out; } if (r == 0) { PR_ERROR("Unexpect EOF\n"); goto out; } if (splice(pipefd[0], 0, output, 0, r, 0) != r) { PR_ERROR("Short write!\n"); goto out; } size -= r; } rc = 0; out: close(pipefd[1]); close(pipefd[0]); return rc; } static int do_htm_dump(struct htm *htm, char *filename) { char *trace_file; struct htm_status status; uint64_t last, end, trace_size; int trace_fd, dump_fd; uint32_t eyecatcher; uint32_t chip_id; size_t r; bool wrapped; if (!filename) return -1; if (HTM_ERR(get_status(htm, &status))) return -1; if (status.state != COMPLETE) { PR_INFO("* Skipping DUMP tigger, HTM is not in complete state\n"); return -1; } chip_id = dt_get_chip_id(&htm->target); if (chip_id == -1) return -1; trace_file = get_debugfs_file(htm, "trace"); if (!trace_file) return -1; trace_fd = open(trace_file, O_RDWR); if (trace_fd == -1) { PR_ERROR("Failed to open %s: %m\n", trace_file); r = trace_fd; goto out3; } r = read(trace_fd, &eyecatcher, 4); if (r == -1) { PR_ERROR("Failed to read from %s: %m\n", trace_file); goto out2; } wrapped = true; if (eyecatcher == 0x00f0efac) wrapped = false; last = status.mem_last - status.mem_base; end = htm_trace_size(&status); trace_size = wrapped ? end : last; printf("Dumping %" PRIi64 " MB to %s\n", trace_size >> 20, filename); dump_fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (dump_fd == -1) { PR_ERROR("Failed to open %s: %m\n", filename); r = dump_fd; goto out2; } /* * Trace buffer: * ------------------------------------------------ * | | | * ------------------------------------------------ * start last end * * If the trace buffer has wrapped, the start of the trace is * after the last written value. In this case we need to copy * last -> end to dump file. Then copy start -> last to the * dump file. */ if (wrapped) { /* Copy last -> end first */ r = lseek(trace_fd, last, SEEK_SET); if (r == -1) goto out1; r = copy_file(dump_fd, trace_fd, end - last); if (r) goto out1; } /* Copy start -> last */ r = lseek(trace_fd, 0, SEEK_SET); if (r == -1) goto out1; r = copy_file(dump_fd, trace_fd, last); if (r) goto out1; r = 1; out1: close(dump_fd); out2: close(trace_fd); out3: free(trace_file); return r; } static int do_htm_record(struct htm *htm, char *filename) { if (__do_htm_start(htm, false) < 0) return -1; if (htm_wait_complete(htm)) return -1; if (do_htm_stop(htm) < 0) return -1; if (do_htm_dump(htm, filename) < 0) return -1; return 1; } static bool is_debugfs_memtrace_ok(void) { return access(DEBUGFS_MEMTRACE, F_OK) == 0; } static bool is_debugfs_scom_ok(void) { return access(DEBUGFS_SCOM, F_OK) == 0; } static int nhtm_probe(struct pdbg_target *target) { uint64_t val; if (!is_debugfs_memtrace_ok() || !is_debugfs_scom_ok()) return -1; if (dt_node_is_compatible(target, "ibm,power9-nhtm")) { pib_read(target, NHTM_FLEX_MUX, &val); if (GETFIELD(NHTM_FLEX_MUX_MASK, val) != NHTM_FLEX_DEFAULT) { PR_DEBUG("FLEX_MUX not default\n"); return -1; } } return 0; } static int chtm_probe(struct pdbg_target *target) { return is_debugfs_memtrace_ok() && is_debugfs_scom_ok() ? 0 : -1; } static struct htm nhtm = { .target = { .name = "Nest HTM", .compatible = "ibm,power8-nhtm", "ibm,power9-nhtm", .class = "nhtm", .probe = nhtm_probe, }, .start = do_htm_start, .stop = do_htm_stop, .record = do_htm_record, .status = do_htm_status, .dump = do_htm_dump, }; DECLARE_HW_UNIT(nhtm); static struct htm chtm = { .target = { .name = "POWER8 Core HTM", .compatible = "ibm,power8-chtm", .class = "chtm", .probe = chtm_probe, }, .start = do_htm_start, .stop = do_htm_stop, .record = do_htm_record, .status = do_htm_status, .dump = do_htm_dump, }; DECLARE_HW_UNIT(chtm); pdbg-2.0/libpdbg/i2c.c000066400000000000000000000075521336450571500145350ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "bitutils.h" #include "operations.h" #include "debug.h" struct i2c_data { int addr; int fd; }; static int i2c_set_addr(int fd, int addr) { if (ioctl(fd, I2C_SLAVE, addr) < 0) { PR_ERROR("Unable to set i2c slave address\n"); return -1; } return 0; } static int i2c_set_scom_addr(struct i2c_data *i2c_data, uint32_t addr) { uint8_t data[4]; addr <<= 1; data[3] = GETFIELD(PPC_BITMASK32(0, 7), addr); data[2] = GETFIELD(PPC_BITMASK32(8, 15), addr); data[1] = GETFIELD(PPC_BITMASK32(16, 23), addr); data[0] = GETFIELD(PPC_BITMASK32(23, 31), addr); if (write(i2c_data->fd, data, sizeof(data)) != 4) { PR_ERROR("Error writing address bytes\n"); return -1; } return 0; } static int i2c_getscom(struct pib *pib, uint64_t addr, uint64_t *value) { struct i2c_data *i2c_data = pib->priv; uint64_t data; CHECK_ERR(i2c_set_scom_addr(i2c_data, addr)); if (read(i2c_data->fd, &data, sizeof(data)) != 8) { PR_ERROR("Error reading data\n"); return -1; } *value = le64toh(data); return 0; } static int i2c_putscom(struct pib *pib, uint64_t addr, uint64_t value) { struct i2c_data *i2c_data = pib->priv; uint8_t data[12]; /* Setup scom address */ addr <<= 1; data[3] = GETFIELD(PPC_BITMASK32(0, 7), addr); data[2] = GETFIELD(PPC_BITMASK32(8, 15), addr); data[1] = GETFIELD(PPC_BITMASK32(16, 23), addr); data[0] = GETFIELD(PPC_BITMASK32(23, 31), addr); /* Add data value */ data[11] = GETFIELD(PPC_BITMASK(0, 7), value); data[10] = GETFIELD(PPC_BITMASK(8, 15), value); data[9] = GETFIELD(PPC_BITMASK(16, 23), value); data[8] = GETFIELD(PPC_BITMASK(23, 31), value); data[7] = GETFIELD(PPC_BITMASK(32, 39), value); data[6] = GETFIELD(PPC_BITMASK(40, 47), value); data[5] = GETFIELD(PPC_BITMASK(48, 55), value); data[4] = GETFIELD(PPC_BITMASK(56, 63), value); /* Write value */ if (write(i2c_data->fd, data, sizeof(data)) != 12) { PR_ERROR("Error writing data bytes\n"); return -1; } return 0; } #if 0 /* TODO: At present we don't have a generic destroy method as there aren't many * use cases for it. So for the moment we can just let the OS close the file * descriptor on exit. */ static void i2c_destroy(struct pib *pib) { struct i2c_data *i2c_data = pib->priv; close(i2c_data->fd); free(i2c_data); } #endif /* * Initialise a i2c backend on the given bus at the given bus address. */ int i2c_target_probe(struct pdbg_target *target) { struct pib *pib = target_to_pib(target); struct i2c_data *i2c_data; const char *bus; int addr; bus = dt_prop_get_def(&pib->target, "bus", "/dev/i2c4"); addr = dt_get_address(&pib->target, 0, NULL); assert(addr); i2c_data = malloc(sizeof(*i2c_data)); if (!i2c_data) exit(1); i2c_data->addr = addr; i2c_data->fd = open(bus, O_RDWR); if (i2c_data->fd < 0) { perror("Error opening bus"); return -1; } if (i2c_set_addr(i2c_data->fd, addr) < 0) return -1; pib->priv = i2c_data; return 0; } static struct pib p8_i2c_pib = { .target = { .name = "POWER8 I2C Slave", .compatible = "ibm,power8-i2c-slave", .class = "pib", .probe = i2c_target_probe, }, .read = i2c_getscom, .write = i2c_putscom, }; DECLARE_HW_UNIT(p8_i2c_pib); pdbg-2.0/libpdbg/kernel.c000066400000000000000000000065061336450571500153360ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "bitutils.h" #include "operations.h" #include "target.h" #define FSI_SCAN_PATH "/sys/bus/platform/devices/gpio-fsi/fsi0/rescan" #define FSI_CFAM_PATH "/sys/devices/platform/gpio-fsi/fsi0/slave@00:00/raw" int fsi_fd; static int kernel_fsi_getcfam(struct fsi *fsi, uint32_t addr64, uint32_t *value) { int rc; uint32_t tmp, addr = (addr64 & 0x7ffc00) | ((addr64 & 0x3ff) << 2); rc = lseek(fsi_fd, addr, SEEK_SET); if (rc < 0) { warn("Failed to seek %s", FSI_CFAM_PATH); return errno; } rc = read(fsi_fd, &tmp, 4); if (rc < 0) { if ((addr64 & 0xfff) != 0xc09) /* We expect reads of 0xc09 to occasionally * fail as the probing code uses it to see * if anything is present on the link. */ warn("Failed to read from 0x%08" PRIx32 " (%016" PRIx32 ")", (uint32_t) addr, addr64); return errno; } *value = be32toh(tmp); return 0; } static int kernel_fsi_putcfam(struct fsi *fsi, uint32_t addr64, uint32_t data) { int rc; uint32_t tmp, addr = (addr64 & 0x7ffc00) | ((addr64 & 0x3ff) << 2); rc = lseek(fsi_fd, addr, SEEK_SET); if (rc < 0) { warn("Failed to seek %s", FSI_CFAM_PATH); return errno; } tmp = htobe32(data); rc = write(fsi_fd, &tmp, 4); if (rc < 0) { warn("Failed to write to 0x%08" PRIx32 " (%016" PRIx32 ")", addr, addr64); return errno; } return 0; } #if 0 /* TODO: At present we don't have a generic destroy method as there aren't many * use cases for it. So for the moment we can just let the OS close the file * descriptor on exit. */ static void kernel_fsi_destroy(struct pdbg_target *target) { close(fsi_fd); } #endif static void kernel_fsi_scan_devices(void) { const char one = '1'; int rc, fd; fd = open(FSI_SCAN_PATH, O_WRONLY | O_SYNC); if (fd < 0) err(errno, "Unable to open %s", FSI_SCAN_PATH); rc = write(fd, &one, sizeof(one)); if (rc < 0) err(errno, "Unable to write to %s", FSI_SCAN_PATH); close(fd); } int kernel_fsi_probe(struct pdbg_target *target) { if (!fsi_fd) { int tries = 5; while (tries) { /* Open first raw device */ fsi_fd = open(FSI_CFAM_PATH, O_RDWR | O_SYNC); if (fsi_fd >= 0) return 0; tries--; /* Scan */ kernel_fsi_scan_devices(); sleep(1); } if (fsi_fd < 0) { err(errno, "Unable to open %s", FSI_CFAM_PATH); return -1; } } return -1; } static struct fsi kernel_fsi = { .target = { .name = "Kernel based FSI master", .compatible = "ibm,kernel-fsi", .class = "fsi", .probe = kernel_fsi_probe, }, .read = kernel_fsi_getcfam, .write = kernel_fsi_putcfam, }; DECLARE_HW_UNIT(kernel_fsi); pdbg-2.0/libpdbg/libpdbg.c000066400000000000000000000113621336450571500154550ustar00rootroot00000000000000#include #include "target.h" #include "device.h" #include "libpdbg.h" static pdbg_progress_tick_t progress_tick; struct pdbg_target *__pdbg_next_target(const char *class, struct pdbg_target *parent, struct pdbg_target *last) { struct pdbg_target *next, *tmp; struct pdbg_target_class *target_class; if (class && !find_target_class(class)) return NULL; target_class = require_target_class(class); retry: /* No more targets left to check in this class */ if ((last && last->class_link.next == &target_class->targets.n) || list_empty(&target_class->targets)) return NULL; if (last) next = list_entry(last->class_link.next, struct pdbg_target, class_link); else if (!(next = list_top(&target_class->targets, struct pdbg_target, class_link))) return NULL; if (!parent) /* Parent is null, no need to check if this is a child */ return next; else { /* Check if this target is a child of the given parent */ for (tmp = next; tmp && next->parent && tmp != parent; tmp = tmp->parent) {} if (tmp == parent) return next; else { last = next; goto retry; } } } struct pdbg_target *__pdbg_next_child_target(struct pdbg_target *parent, struct pdbg_target *last) { if (!parent || list_empty(&parent->children)) return NULL; if (!last) return list_top(&parent->children, struct pdbg_target, list); if (last->list.next == &parent->children.n) return NULL; return list_entry(last->list.next, struct pdbg_target, list); } enum pdbg_target_status pdbg_target_status(struct pdbg_target *target) { return target->status; } void pdbg_target_status_set(struct pdbg_target *target, enum pdbg_target_status status) { /* It's a programming error for user code to attempt anything else so * blow up obviously if this happens */ assert(status == PDBG_TARGET_DISABLED || status == PDBG_TARGET_MUSTEXIST); target->status = status; } /* Searches up the tree and returns the first valid index found */ uint32_t pdbg_target_index(struct pdbg_target *target) { struct pdbg_target *dn; for (dn = target; dn && dn->index == -1; dn = dn->parent); if (!dn) return -1; else return dn->index; } /* Find a target parent from the given class */ struct pdbg_target *pdbg_target_parent(const char *class, struct pdbg_target *target) { struct pdbg_target *parent; if (!class) return target->parent; for (parent = target->parent; parent && parent->parent; parent = parent->parent) { if (!strcmp(class, pdbg_target_class_name(parent))) return parent; } return NULL; } struct pdbg_target *pdbg_target_require_parent(const char *class, struct pdbg_target *target) { struct pdbg_target *parent = pdbg_target_parent(class, target); assert(parent); return parent; } /* Searched up the tree for the first target of the right class and returns its index */ uint32_t pdbg_parent_index(struct pdbg_target *target, char *class) { struct pdbg_target *parent; parent = pdbg_target_parent(class, target); if (parent) return pdbg_target_index(parent); else return -1; } char *pdbg_target_class_name(struct pdbg_target *target) { return target->class; } char *pdbg_target_name(struct pdbg_target *target) { return target->name; } const char *pdbg_target_dn_name(struct pdbg_target *target) { return target->dn_name; } void pdbg_set_target_property(struct pdbg_target *target, const char *name, const void *val, size_t size) { struct dt_property *p; if ((p = dt_find_property(target, name))) { if (size > p->len) { dt_resize_property(&p, size); p->len = size; } memcpy(p->prop, val, size); } else { dt_add_property(target, name, val, size); } } void *pdbg_get_target_property(struct pdbg_target *target, const char *name, size_t *size) { struct dt_property *p; p = dt_find_property(target, name); if (p) { if (size) *size = p->len; return p->prop; } else if (size) *size = 0; return NULL; } uint64_t pdbg_get_address(struct pdbg_target *target, uint64_t *size) { return dt_get_address(target, 0, size); } /* Difference from below is that it doesn't search up the tree for the given * property. As nothing uses this yet we don't export it for use, but we may in * future */ static int pdbg_get_target_u64_property(struct pdbg_target *target, const char *name, uint64_t *val) { struct dt_property *p; p = dt_find_property(target, name); if (!p) return -1; *val = dt_get_number(p->prop, 2); return 0; } int pdbg_get_u64_property(struct pdbg_target *target, const char *name, uint64_t *val) { struct pdbg_target *dn; for (dn = target; dn; dn = dn->parent) { if (!pdbg_get_target_u64_property(dn, name, val)) return 0; } return -1; } void pdbg_progress_tick(uint64_t cur, uint64_t end) { if (progress_tick) progress_tick(cur, end); } void pdbg_set_progress_tick(pdbg_progress_tick_t fn) { progress_tick = fn; } pdbg-2.0/libpdbg/libpdbg.h000066400000000000000000000176321336450571500154700ustar00rootroot00000000000000#ifndef __LIBPDBG_H #define __LIBPDBG_H #include #include #include #include #include struct pdbg_target; struct pdbg_target_class; /* loops/iterators */ struct pdbg_target *__pdbg_next_target(const char *klass, struct pdbg_target *parent, struct pdbg_target *last); struct pdbg_target *__pdbg_next_child_target(struct pdbg_target *parent, struct pdbg_target *last); /* * Each target has a status associated with it. This is what each status means: * * enabled - the target exists and has been probed, will be released by * the release call. * * disabled - the target has not been probed and will never be probed. Target * selection code may use this to prevent probing of certain * targets if it knows they are unnecessary. * * nonexistant - the target has been probed but did not exist. It will never be * reprobed. * * unknown - the target has not been probed but will be probed if required. * * mustexist - the target has not been probed but an error will be reported if * it does not exist as it is required for correct operation. * Selection code may set this. * * released - the target was enabled and has now been released. * * Initially these properties are read from the device tree. This allows the * client application to select which targets it does not care about to avoid * unneccessary probing by marking them disabled. If no status property exists * it defaults to "unknown". */ enum pdbg_target_status {PDBG_TARGET_UNKNOWN = 0, PDBG_TARGET_ENABLED, PDBG_TARGET_DISABLED, PDBG_TARGET_MUSTEXIST, PDBG_TARGET_NONEXISTENT, PDBG_TARGET_RELEASED}; #define pdbg_for_each_target(class, parent, target) \ for (target = __pdbg_next_target(class, parent, NULL); \ target; \ target = __pdbg_next_target(class, parent, target)) #define pdbg_for_each_class_target(class, target) \ for (target = __pdbg_next_target(class, NULL, NULL); \ target; \ target = __pdbg_next_target(class, NULL, target)) #define pdbg_for_each_child_target(parent, target) \ for (target = __pdbg_next_child_target(parent, NULL); \ target; \ target = __pdbg_next_child_target(parent, target)) /* Return the first parent target of the given class, or NULL if the given * target does not have a parent of the given class. */ struct pdbg_target *pdbg_target_parent(const char *klass, struct pdbg_target *target); /* Same as above but instead of returning NULL causes an assert failure. */ struct pdbg_target *pdbg_target_require_parent(const char *klass, struct pdbg_target *target); /* Set the given property. Will automatically add one if one doesn't exist */ void pdbg_set_target_property(struct pdbg_target *target, const char *name, const void *val, size_t size); /* Get the given property and return the size */ void *pdbg_get_target_property(struct pdbg_target *target, const char *name, size_t *size); int pdbg_get_u64_property(struct pdbg_target *target, const char *name, uint64_t *val); uint64_t pdbg_get_address(struct pdbg_target *target, uint64_t *size); /* Misc. */ void pdbg_targets_init(void *fdt); void pdbg_target_probe_all(struct pdbg_target *parent); enum pdbg_target_status pdbg_target_probe(struct pdbg_target *target); void pdbg_target_release(struct pdbg_target *target); enum pdbg_target_status pdbg_target_status(struct pdbg_target *target); void pdbg_target_status_set(struct pdbg_target *target, enum pdbg_target_status status); uint32_t pdbg_target_index(struct pdbg_target *target); uint32_t pdbg_parent_index(struct pdbg_target *target, char *klass); char *pdbg_target_class_name(struct pdbg_target *target); char *pdbg_target_name(struct pdbg_target *target); const char *pdbg_target_dn_name(struct pdbg_target *target); void *pdbg_target_priv(struct pdbg_target *target); void pdbg_target_priv_set(struct pdbg_target *target, void *priv); struct pdbg_target *pdbg_target_root(void); /* Procedures */ int fsi_read(struct pdbg_target *target, uint32_t addr, uint32_t *val); int fsi_write(struct pdbg_target *target, uint32_t addr, uint32_t val); int pib_read(struct pdbg_target *target, uint64_t addr, uint64_t *val); int pib_write(struct pdbg_target *target, uint64_t addr, uint64_t val); int pib_wait(struct pdbg_target *pib_dt, uint64_t addr, uint64_t mask, uint64_t data); struct thread_regs { uint64_t nia; uint64_t msr; uint64_t cfar; uint64_t lr; uint64_t ctr; uint64_t tar; uint32_t cr; uint64_t xer; uint64_t gprs[32]; uint64_t lpcr; uint64_t ptcr; uint64_t lpidr; uint64_t pidr; uint64_t hfscr; uint32_t hdsisr; uint64_t hdar; uint64_t hsrr0; uint64_t hsrr1; uint64_t hdec; uint32_t heir; uint64_t hid; uint64_t hsprg0; uint64_t hsprg1; uint64_t fscr; uint32_t dsisr; uint64_t dar; uint64_t srr0; uint64_t srr1; uint64_t dec; uint64_t tb; uint64_t sprg0; uint64_t sprg1; uint64_t sprg2; uint64_t sprg3; uint64_t ppr; }; int ram_putmsr(struct pdbg_target *target, uint64_t val); int ram_putnia(struct pdbg_target *target, uint64_t val); int ram_putspr(struct pdbg_target *target, int spr, uint64_t val); int ram_putgpr(struct pdbg_target *target, int spr, uint64_t val); int ram_getmsr(struct pdbg_target *target, uint64_t *val); int ram_getcr(struct pdbg_target *thread, uint32_t *value); int ram_putcr(struct pdbg_target *thread, uint32_t value); int ram_getnia(struct pdbg_target *target, uint64_t *val); int ram_getspr(struct pdbg_target *target, int spr, uint64_t *val); int ram_getgpr(struct pdbg_target *target, int gpr, uint64_t *val); int ram_start_thread(struct pdbg_target *target); int ram_step_thread(struct pdbg_target *target, int steps); int ram_stop_thread(struct pdbg_target *target); int ram_sreset_thread(struct pdbg_target *target); int ram_state_thread(struct pdbg_target *target, struct thread_regs *regs); struct thread_state thread_status(struct pdbg_target *target); int ram_getxer(struct pdbg_target *thread, uint64_t *value); int ram_putxer(struct pdbg_target *thread, uint64_t value); int getring(struct pdbg_target *chiplet_target, uint64_t ring_addr, uint64_t ring_len, uint32_t result[]); enum pdbg_sleep_state {PDBG_THREAD_STATE_RUN, PDBG_THREAD_STATE_DOZE, PDBG_THREAD_STATE_NAP, PDBG_THREAD_STATE_SLEEP, PDBG_THREAD_STATE_STOP}; enum pdbg_smt_state {PDBG_SMT_UNKNOWN, PDBG_SMT_1, PDBG_SMT_2, PDBG_SMT_4, PDBG_SMT_8}; struct thread_state { bool active; bool quiesced; enum pdbg_sleep_state sleep_state; enum pdbg_smt_state smt_state; }; int htm_start(struct pdbg_target *target); int htm_stop(struct pdbg_target *target); int htm_status(struct pdbg_target *target); int htm_dump(struct pdbg_target *target, char *filename); int htm_record(struct pdbg_target *target, char *filename); int adu_getmem(struct pdbg_target *target, uint64_t addr, uint8_t *ouput, uint64_t size); int adu_putmem(struct pdbg_target *target, uint64_t addr, uint8_t *input, uint64_t size); int adu_getmem_ci(struct pdbg_target *target, uint64_t addr, uint8_t *ouput, uint64_t size); int adu_putmem_ci(struct pdbg_target *target, uint64_t addr, uint8_t *input, uint64_t size); int __adu_getmem(struct pdbg_target *target, uint64_t addr, uint8_t *ouput, uint64_t size, bool ci); int __adu_putmem(struct pdbg_target *target, uint64_t addr, uint8_t *input, uint64_t size, bool ci); int opb_read(struct pdbg_target *target, uint32_t addr, uint32_t *data); int opb_write(struct pdbg_target *target, uint32_t addr, uint32_t data); typedef void (*pdbg_progress_tick_t)(uint64_t cur, uint64_t end); void pdbg_set_progress_tick(pdbg_progress_tick_t fn); void pdbg_progress_tick(uint64_t cur, uint64_t end); #define PDBG_ERROR 0 #define PDBG_WARNING 1 #define PDBG_NOTICE 2 #define PDBG_INFO 3 #define PDBG_DEBUG 4 typedef void (*pdbg_log_func_t)(int loglevel, const char *fmt, va_list ap); void pdbg_set_logfunc(pdbg_log_func_t fn); void pdbg_set_loglevel(int loglevel); void pdbg_log(int loglevel, const char *fmt, ...); #endif pdbg-2.0/libpdbg/operations.h000066400000000000000000000047371336450571500162520ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 __OPERATIONS_H #define __OPERATIONS_H #include "bitutils.h" #include "target.h" #include "debug.h" #define PRINT_ERR PR_DEBUG("failed\n"); #define CHECK_ERR(x) do { \ if (x) { \ PRINT_ERR; \ return x; \ } \ } while(0) #define CHECK_ERR_GOTO(label, x) do { \ if ((x)) { \ PRINT_ERR; \ goto label; \ } \ } while(0) #define FSI2PIB_BASE 0x1000 /* Functions to ram instructions */ #define THREAD_STATUS_DISABLED PPC_BIT(0) #define THREAD_STATUS_ACTIVE PPC_BIT(63) #define THREAD_STATUS_STATE PPC_BITMASK(61, 62) #define THREAD_STATUS_DOZE PPC_BIT(62) #define THREAD_STATUS_NAP PPC_BIT(61) #define THREAD_STATUS_SLEEP PPC_BITMASK(61, 62) #define THREAD_STATUS_QUIESCE PPC_BIT(60) #define THREAD_STATUS_SMT PPC_BITMASK(57, 59) #define THREAD_STATUS_SMT_1 PPC_BIT(59) #define THREAD_STATUS_SMT_2SH PPC_BIT(58) #define THREAD_STATUS_SMT_2SP (PPC_BIT(58) | PPC_BIT(59)) #define THREAD_STATUS_SMT_4 PPC_BIT(57) #define THREAD_STATUS_SMT_8 (PPC_BIT(57) | PPC_BIT(59)) /* Opcodes for instruction ramming */ #define OPCODE_MASK 0xfc0003ffUL #define MTNIA_OPCODE 0x00000002UL #define MFNIA_OPCODE 0x00000004UL #define MFMSR_OPCODE 0x7c0000a6UL #define MTMSR_OPCODE 0x7c000124UL #define MFSPR_OPCODE 0x7c0002a6UL #define MTSPR_OPCODE 0x7c0003a6UL #define MFOCRF_OPCODE 0x7c100026UL #define MTOCRF_OPCODE 0x7C100120UL #define MFSPR_MASK (MFSPR_OPCODE | ((0x1f) << 16) | ((0x3e0) << 6)) #define MFXER_OPCODE (MFSPR_OPCODE | ((1 & 0x1f) << 16) | ((1 & 0x3e0) << 6)) #define MTXER_OPCODE (MTSPR_OPCODE | ((1 & 0x1f) << 16) | ((1 & 0x3e0) << 6)) #define LD_OPCODE 0xe8000000UL #define MXSPR_SPR(opcode) (((opcode >> 16) & 0x1f) | ((opcode >> 6) & 0x3e0)) /* GDB server functionality */ int gdbserver_start(uint16_t port); enum fsi_system_type {FSI_SYSTEM_P8, FSI_SYSTEM_P9W, FSI_SYSTEM_P9R, FSI_SYSTEM_P9Z}; enum chip_type get_chip_type(uint64_t chip_id); #endif pdbg-2.0/libpdbg/p8chip.c000066400000000000000000000355521336450571500152540ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "target.h" #include "operations.h" #include "bitutils.h" #include "debug.h" #define RAS_STATUS_TIMEOUT 100 #define DIRECT_CONTROLS_REG 0x0 #define DIRECT_CONTROL_SP_STEP PPC_BIT(61) #define DIRECT_CONTROL_SP_START PPC_BIT(62) #define DIRECT_CONTROL_SP_STOP PPC_BIT(63) #define RAS_MODE_REG 0x1 #define MR_THREAD_IN_DEBUG PPC_BIT(43) #define MR_DO_SINGLE_MODE PPC_BIT(50) #define RAS_STATUS_REG 0x2 #define RAS_STATUS_SRQ_EMPTY PPC_BIT(8) #define RAS_STATUS_LSU_QUIESCED PPC_BIT(9) #define RAS_STATUS_INST_COMPLETE PPC_BIT(12) #define RAS_STATUS_THREAD_ACTIVE PPC_BIT(48) #define RAS_STATUS_TS_QUIESCE PPC_BIT(49) #define POW_STATUS_REG 0x4 #define PMC_POW_STATE PPC_BITMASK(4, 5) #define PMC_POW_STATE_RUN 0x0 #define PMC_POW_STATE_DOZE 0x1 #define PMC_POW_STATE_NAP 0x2 #define PMC_POW_STATE_SLEEP 0x3 #define PMC_POW_SMT PPC_BITMASK(6, 8) #define PMC_POW_SMT_0 0x0 #define PMC_POW_SMT_1 0x1 #define PMC_POW_SMT_2SH 0x2 #define PMC_POW_SMT_2SP 0x3 #define PMC_POW_SMT_4_3 0x4 #define PMC_POW_SMT_4_4 0x6 #define PMC_POW_SMT_8_5 0x5 #define PMC_POW_SMT_8_8 0x7 #define CORE_POW_STATE PPC_BITMASK(23, 25) #define THREAD_ACTIVE_REG 0x1310e #define THREAD_ACTIVE PPC_BITMASK(0, 7) #define RAM_THREAD_ACTIVE PPC_BITMASK(8, 15) #define SPR_MODE_REG 0x13281 #define SPR_MODE_SPRC_WR_EN PPC_BIT(3) #define SPR_MODE_SPRC_SEL PPC_BITMASK(16, 19) #define SPR_MODE_SPRC_T_SEL PPC_BITMASK(20, 27) #define L0_SCOM_SPRC_REG 0x13280 #define SCOM_SPRC_SCRATCH_SPR 0x40 #define SCR0_REG 0x13283 #define RAM_MODE_REG 0x13c00 #define RAM_MODE_ENABLE PPC_BIT(0) #define RAM_CTRL_REG 0x13c01 #define RAM_THREAD_SELECT PPC_BITMASK(0, 2) #define RAM_INSTR PPC_BITMASK(3, 34) #define RAM_STATUS_REG 0x13c02 #define RAM_CONTROL_RECOV PPC_BIT(0) #define RAM_STATUS PPC_BIT(1) #define RAM_EXCEPTION PPC_BIT(2) #define LSU_EMPTY PPC_BIT(3) #define SCOM_EX_GP3 0xf0012 #define PMSPCWKUPFSP_REG 0xf010d #define FSP_SPECIAL_WAKEUP PPC_BIT(0) #define EX_PM_GP0_REG 0xf0100 #define SPECIAL_WKUP_DONE PPC_BIT(31) #define HID0_REG 0x1329c #define EN_ATTN PPC_BIT(31) /* p8 specific opcodes for instruction ramming*/ #define MTXERF0_OPCODE 0x00000008UL #define MTXERF1_OPCODE 0x00000108UL #define MTXERF2_OPCODE 0x00000208UL #define MTXERF3_OPCODE 0x00000308UL #define MFXERF0_OPCODE 0x00000010UL #define MFXERF1_OPCODE 0x00000110UL #define MFXERF2_OPCODE 0x00000210UL #define MFXERF3_OPCODE 0x00000310UL /* How long (in us) to wait for a special wakeup to complete */ #define SPECIAL_WKUP_TIMEOUT 10 #include "chip.h" static uint64_t mfxerf(uint64_t reg, uint64_t field) { if (reg > 31) PR_ERROR("Invalid register specified for mfxerf\n"); switch (field) { case 0: return MFXERF0_OPCODE | (reg << 21); case 1: return MFXERF1_OPCODE | (reg << 21); case 2: return MFXERF2_OPCODE | (reg << 21); case 3: return MFXERF3_OPCODE | (reg << 21); default: PR_ERROR("Invalid XER field specified\n"); } return 0; } static uint64_t mtxerf(uint64_t reg, uint64_t field) { if (reg > 31) PR_ERROR("Invalid register specified for mtxerf\n"); switch (field) { case 0: return MTXERF0_OPCODE | (reg << 21); case 1: return MTXERF1_OPCODE | (reg << 21); case 2: return MTXERF2_OPCODE | (reg << 21); case 3: return MTXERF3_OPCODE | (reg << 21); default: PR_ERROR("Invalid XER field specified\n"); } return 0; } static int assert_special_wakeup(struct core *chip) { int i = 0; uint64_t gp0; /* Assert special wakeup to prevent low power states */ CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP)); /* Poll for completion */ do { usleep(1); CHECK_ERR(pib_read(&chip->target, EX_PM_GP0_REG, &gp0)); if (i++ > SPECIAL_WKUP_TIMEOUT) { PR_ERROR("Timeout waiting for special wakeup on %s@0x%08" PRIx64 "\n", chip->target.name, dt_get_address(&chip->target, 0, NULL)); return -1; } } while (!(gp0 & SPECIAL_WKUP_DONE)); return 0; } #if 0 /* TODO: Work out when to do this. */ static int deassert_special_wakeup(struct core *chip) { /* Assert special wakeup to prevent low power states */ CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, 0)); return 0; } #endif static struct thread_state get_thread_status(struct thread *thread) { uint64_t val, mode_reg; struct thread_state thread_status; /* Need to activete debug mode to get complete status */ pib_read(&thread->target, RAS_MODE_REG, &mode_reg); pib_write(&thread->target, RAS_MODE_REG, mode_reg | MR_THREAD_IN_DEBUG); /* Read status */ pib_read(&thread->target, RAS_STATUS_REG, &val); thread_status.active = !!(val & RAS_STATUS_THREAD_ACTIVE); thread_status.quiesced = !!(val & RAS_STATUS_TS_QUIESCE); /* Read POW status */ pib_read(&thread->target, POW_STATUS_REG, &val); switch (GETFIELD(PMC_POW_STATE, val)) { case PMC_POW_STATE_RUN: thread_status.sleep_state = PDBG_THREAD_STATE_RUN; break; case PMC_POW_STATE_DOZE: thread_status.sleep_state = PDBG_THREAD_STATE_DOZE; break; case PMC_POW_STATE_NAP: thread_status.sleep_state = PDBG_THREAD_STATE_NAP; break; case PMC_POW_STATE_SLEEP: thread_status.sleep_state = PDBG_THREAD_STATE_SLEEP; break; default: /* PMC_POW_STATE is a 2-bit field and we test all values so it * should be impossible to get here. */ assert(0); } switch (GETFIELD(PMC_POW_SMT, val)) { case PMC_POW_SMT_0: thread_status.smt_state = PDBG_SMT_UNKNOWN; break; case PMC_POW_SMT_1: thread_status.smt_state = PDBG_SMT_1; break; case PMC_POW_SMT_2SH: case PMC_POW_SMT_2SP: thread_status.smt_state = PDBG_SMT_2; break; /* It's unclear from the documentation what the difference between these * two are. */ case PMC_POW_SMT_4_3: case PMC_POW_SMT_4_4: thread_status.smt_state = PDBG_SMT_4; break; /* Ditto */ case PMC_POW_SMT_8_5: case PMC_POW_SMT_8_8: thread_status.smt_state = PDBG_SMT_8; break; default: assert(0); } /* Clear debug mode */ pib_write(&thread->target, RAS_MODE_REG, mode_reg); return thread_status; } static int p8_thread_step(struct thread *thread, int count) { int i; uint64_t ras_mode, ras_status; /* Activate single-step mode */ CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &ras_mode)); ras_mode |= MR_DO_SINGLE_MODE; CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode)); /* Step the core */ for (i = 0; i < count; i++) { CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP)); /* Wait for step to complete */ do { CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &ras_status)); } while (!(ras_status & RAS_STATUS_INST_COMPLETE)); } /* Deactivate single-step mode */ ras_mode &= ~MR_DO_SINGLE_MODE; CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode)); return 0; } static int p8_thread_stop(struct thread *thread) { int i = 0; uint64_t val; struct core *chip = target_to_core( pdbg_target_require_parent("core", &thread->target)); do { /* Quiese active thread */ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP)); /* Wait for thread to quiese */ CHECK_ERR(pib_read(&chip->target, RAS_STATUS_REG, &val)); if (i++ > RAS_STATUS_TIMEOUT) { PR_ERROR("Unable to quiesce thread %d (0x%016" PRIx64 ").\n", thread->id, val); PR_ERROR("Continuing anyway.\n"); if (val & PPC_BIT(48)) { PR_ERROR("Unable to continue\n"); } break; } /* We can continue ramming if either the * thread is not active or the SRQ/LSU/TS bits * are set. */ } while ((val & RAS_STATUS_THREAD_ACTIVE) && !((val & RAS_STATUS_SRQ_EMPTY) && (val & RAS_STATUS_LSU_QUIESCED) && (val & RAS_STATUS_TS_QUIESCE))); /* Make the threads RAM thread active */ CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val)); val |= PPC_BIT(8) >> thread->id; CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val)); return 0; } static int p8_thread_start(struct thread *thread) { uint64_t val; struct core *chip = target_to_core( pdbg_target_require_parent("core", &thread->target)); /* Activate thread */ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START)); /* Restore thread active */ CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val)); val &= ~(PPC_BIT(8) >> thread->id); val |= PPC_BIT(thread->id); CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val)); return 0; } static int p8_thread_sreset(struct thread *thread) { /* Broken on p8 */ return 1; } static int p8_ram_setup(struct thread *thread) { struct pdbg_target *target; struct core *chip = target_to_core( pdbg_target_require_parent("core", &thread->target)); uint64_t ram_mode, val; if (thread->ram_is_setup) return 1; /* We can only ram a thread if all the threads on the core/chip are * quiesced */ dt_for_each_compatible(&chip->target, target, "ibm,power8-thread") { struct thread *tmp; /* If this thread wasn't enabled it may not yet have been probed so do that now. This will also update the thread status */ if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED) return 1; tmp = target_to_thread(target); if (!(get_thread_status(tmp).quiesced)) return 1; } if (!(thread->status.active)) return 2; /* Activate RAM mode */ CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode)); ram_mode |= RAM_MODE_ENABLE; CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode)); /* Setup SPRC to use SPRD */ val = SPR_MODE_SPRC_WR_EN; val = SETFIELD(SPR_MODE_SPRC_SEL, val, 1 << (3 - 0)); val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread->id)); CHECK_ERR(pib_write(&chip->target, SPR_MODE_REG, val)); CHECK_ERR(pib_write(&chip->target, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR)); thread->ram_is_setup = true; return 0; } static int p8_ram_instruction(struct thread *thread, uint64_t opcode, uint64_t *scratch) { struct core *chip = target_to_core( pdbg_target_require_parent("core", &thread->target)); uint64_t val; if (!thread->ram_is_setup) return 1; CHECK_ERR(pib_write(&chip->target, SCR0_REG, *scratch)); /* ram instruction */ val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread->id); val = SETFIELD(RAM_INSTR, val, opcode); CHECK_ERR(pib_write(&chip->target, RAM_CTRL_REG, val)); /* wait for completion */ do { CHECK_ERR(pib_read(&chip->target, RAM_STATUS_REG, &val)); } while (!((val & PPC_BIT(1)) || ((val & PPC_BIT(2)) && (val & PPC_BIT(3))))); if (!(val & PPC_BIT(1))) { if (GETFIELD(PPC_BITMASK(2,3), val) == 0x3) { return 1; } else { PR_ERROR("RAMMING failed with status 0x%" PRIx64 "\n", val); return 2; } } /* Save the results */ CHECK_ERR(pib_read(&chip->target, SCR0_REG, scratch)); return 0; } static int p8_ram_destroy(struct thread *thread) { struct core *chip = target_to_core( pdbg_target_require_parent("core", &thread->target)); uint64_t ram_mode; /* Disable RAM mode */ CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode)); ram_mode &= ~RAM_MODE_ENABLE; CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode)); thread->ram_is_setup = false; return 0; } static int p8_ram_getxer(struct pdbg_target *thread, uint64_t *value) { uint64_t opcodes[] = {mfxerf(0, 0), mtspr(277, 0), mfxerf(0, 1), mtspr(277, 0), mfxerf(0, 2), mtspr(277, 0), mfxerf(0, 3), mtspr(277, 0)}; uint64_t results[] = {0, 0, 0, 0, 0, 0, 0, 0}; /* On POWER8 we can't get xer with getspr. We seem to only be able to * get and set IBM bits 32-34 and 44-56. */ PR_WARNING("Can only get/set IBM bits 32-34 and 44-56 of the XER register\n"); CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); *value = results[1] | results[3] | results[5] | results[7]; return 0; } static int p8_ram_putxer(struct pdbg_target *thread, uint64_t value) { uint64_t fields[] = {value, value, value, value, 0}; uint64_t opcodes[] = {mfspr(0, 277), mtxerf(0, 0), mtxerf(0, 1), mtxerf(0, 2), mtxerf(0, 3)}; /* We seem to only be able to get and set IBM bits 32-34 and 44-56.*/ PR_WARNING("Can only set IBM bits 32-34 and 44-56 of the XER register\n"); CHECK_ERR(ram_instructions(thread, opcodes, fields, ARRAY_SIZE(opcodes), 0)); return 0; } /* * Initialise all viable threads for ramming on the given core. */ static int p8_thread_probe(struct pdbg_target *target) { struct thread *thread = target_to_thread(target); thread->id = (dt_get_address(target, 0, NULL) >> 4) & 0xf; thread->status = get_thread_status(thread); return 0; } static int p8_get_hid0(struct pdbg_target *chip, uint64_t *value) { CHECK_ERR(pib_read(chip, HID0_REG, value)); return 0; } static int p8_put_hid0(struct pdbg_target *chip, uint64_t value) { CHECK_ERR(pib_write(chip, HID0_REG, value)); return 0; } static int p8_enable_attn(struct pdbg_target *target) { struct pdbg_target *core; uint64_t hid0; core = pdbg_target_parent("core", target); if (core == NULL) { PR_ERROR("CORE NOT FOUND\n"); return 1; } /* Need to enable the attn instruction in HID0 */ if (p8_get_hid0(core, &hid0)) { PR_ERROR("Unable to get HID0\n"); return 1; } PR_INFO("HID0 was 0x%"PRIx64 " \n", hid0); hid0 |= EN_ATTN; PR_INFO("writing 0x%"PRIx64 " to HID0\n", hid0); if (p8_put_hid0(core, hid0)) { PR_ERROR("Unable to set HID0\n"); return 1; } return 0; } static struct thread p8_thread = { .target = { .name = "POWER8 Thread", .compatible = "ibm,power8-thread", .class = "thread", .probe = p8_thread_probe, }, .step = p8_thread_step, .start = p8_thread_start, .stop = p8_thread_stop, .sreset = p8_thread_sreset, .ram_setup = p8_ram_setup, .ram_instruction = p8_ram_instruction, .ram_destroy = p8_ram_destroy, .ram_getxer = p8_ram_getxer, .ram_putxer = p8_ram_putxer, .enable_attn = p8_enable_attn, }; DECLARE_HW_UNIT(p8_thread); static int p8_core_probe(struct pdbg_target *target) { uint64_t value; struct core *core = target_to_core(target); /* Work out if this chip is actually present */ if (pib_read(target, SCOM_EX_GP3, &value)) { PR_DEBUG("Error reading chip GP3 register\n"); return -1; } if (!GETFIELD(PPC_BIT(0), value)) return -1; assert_special_wakeup(core); return 0; } static struct core p8_core = { .target = { .name = "POWER8 Core", .compatible = "ibm,power8-core", .class = "core", .probe = p8_core_probe, }, }; DECLARE_HW_UNIT(p8_core); pdbg-2.0/libpdbg/p9chip.c000066400000000000000000000360161336450571500152510ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "target.h" #include "operations.h" #include "bitutils.h" #include "debug.h" /* * NOTE! * All timeouts and scom procedures in general through the file should be kept * in synch with skiboot (e.g., core/direct-controls.c) as far as possible. * If you fix a bug here, fix it in skiboot, and vice versa. */ #define P9_RAS_STATUS 0x10a02 #define P9_CORE_THREAD_STATE 0x10ab3 #define P9_THREAD_INFO 0x10a9b #define P9_DIRECT_CONTROL 0x10a9c #define P9_RAS_MODEREG 0x10a9d #define P9_RAM_MODEREG 0x10a4e #define P9_RAM_CTRL 0x10a4f #define P9_RAM_STATUS 0x10a50 #define P9_SCOMC 0x10a80 #define P9_SPR_MODE 0x10a84 #define P9_SCR0_REG 0x10a86 #define CHIPLET_CTRL0_WOR 0x10 #define CHIPLET_CTRL0_CLEAR 0x20 #define CHIPLET_CTRL0_CTRL_CC_ABIST_MUXSEL_DC PPC_BIT(0) #define CHIPLET_CTRL0_TC_UNIT_SYNCCLK_MUXSEL_DC PPC_BIT(1) #define CHIPLET_CTRL0_CTRL_CC_FLUSHMODE_INH_DC PPC_BIT(2) #define CHIPLET_CTRL1_WOR 0x11 #define CHIPLET_CTRL1_TC_VITL_REGION_FENCE PPC_BIT(3) #define CHIPLET_STAT0 0x100 #define CHIPLET_STAT0_CC_CTRL_OPCG_DONE_DC PPC_BIT(8) #define CHIPLET_SCAN_REGION_TYPE 0x30005 #define CHIPLET_CLK_REGION 0x30006 #define CHIPLET_CLK_REGION_CLOCK_CMD PPC_BITMASK(0, 1) #define CHIPLET_CLK_REGION_CLOCK_CMD_STOP 0x2 #define CHIPLET_CLK_REGION_SLAVE_MODE PPC_BIT(2) #define CHIPLET_CLK_REGION_MASTER_MODE PPC_BIT(3) #define CHIPLET_CLK_REGION_REGIONS PPC_BITMASK(4, 14) #define CHIPLET_CLK_REGION_SEL_THOLD PPC_BITMASK(48, 50) /* PCB Slave Registers */ #define NET_CTRL0 0xf0040 #define NET_CTRL0_CHIPLET_ENABLE PPC_BIT(0) #define NET_CTRL0_FENCE_EN PPC_BIT(18) #define NET_CTRL0_WOR 0xf0042 #define PPM_GPMMR 0xf0100 #define PPM_SPWKUP_FSP 0xf010b #define PPM_SSHFSP 0xf0111 #define SPECIAL_WKUP_DONE PPC_BIT(1) #define RAS_STATUS_TIMEOUT 100 /* 100ms */ #define SPECIAL_WKUP_TIMEOUT 100 /* 100ms */ static uint64_t thread_read(struct thread *thread, uint64_t addr, uint64_t *data) { struct pdbg_target *chip = require_target_parent(&thread->target); return pib_read(chip, addr, data); } static uint64_t thread_write(struct thread *thread, uint64_t addr, uint64_t data) { struct pdbg_target *chip = require_target_parent(&thread->target); return pib_write(chip, addr, data); } static struct thread_state p9_get_thread_status(struct thread *thread) { uint64_t value; struct thread_state thread_state; thread_read(thread, P9_RAS_STATUS, &value); thread_state.quiesced = (GETFIELD(PPC_BITMASK(8*thread->id, 3 + 8*thread->id), value) == 0xf); thread_read(thread, P9_THREAD_INFO, &value); thread_state.active = !!(value & PPC_BIT(thread->id)); thread_read(thread, P9_CORE_THREAD_STATE, &value); if (value & PPC_BIT(56 + thread->id)) thread_state.sleep_state = PDBG_THREAD_STATE_STOP; else thread_state.sleep_state = PDBG_THREAD_STATE_RUN; thread_state.smt_state = PDBG_SMT_UNKNOWN; return thread_state; } static int p9_thread_probe(struct pdbg_target *target) { struct thread *thread = target_to_thread(target); thread->id = dt_prop_get_u32(target, "tid"); thread->status = p9_get_thread_status(thread); return 0; } static void p9_thread_release(struct pdbg_target *target) { struct core *core = target_to_core(pdbg_target_require_parent("core", target)); struct thread *thread = target_to_thread(target); if (thread->status.quiesced) /* This thread is still quiesced so don't release spwkup */ core->release_spwkup = false;} static int p9_thread_start(struct thread *thread) { if (!(thread->status.quiesced)) return 1; if ((!(thread->status.active)) || (thread->status.sleep_state == PDBG_THREAD_STATE_STOP)) { /* Inactive or active ad stopped: Clear Maint */ thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(3 + 8*thread->id)); } else { /* Active and not stopped: Start */ thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(6 + 8*thread->id)); } thread->status = p9_get_thread_status(thread); return 0; } static int p9_thread_stop(struct thread *thread) { int i = 0; thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(7 + 8*thread->id)); while (!(p9_get_thread_status(thread).quiesced)) { usleep(1000); if (i++ > RAS_STATUS_TIMEOUT) { PR_ERROR("Unable to quiesce thread\n"); break; } } thread->status = p9_get_thread_status(thread); return 0; } static int p9_thread_step(struct thread *thread, int count) { uint64_t value; int i; /* Can only step if a thread is quiesced */ if (!(thread->status.quiesced)) return 1; /* Core must be active to step */ if (!(thread->status.active)) return 1; /* Stepping a stop instruction doesn't really work */ if (thread->status.sleep_state == PDBG_THREAD_STATE_STOP) return 1; /* Fence interrupts. */ thread_write(thread, P9_RAS_MODEREG, PPC_BIT(57)); /* Step the core */ for (i = 0; i < count; i++) { /* Step */ thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(5 + 8*thread->id)); /* Poll PPC complete */ do { thread_read(thread, P9_RAS_STATUS, &value); } while (!(value & PPC_BIT(4 + 8*thread->id))); } /* Un-fence */ thread_write(thread, P9_RAS_MODEREG, 0); return 0; } static int p9_thread_sreset(struct thread *thread) { /* Can only sreset if a thread is quiesced */ if (!(thread->status.quiesced)) return 1; thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(4 + 8*thread->id)); thread->status = p9_get_thread_status(thread); return 0; } static int p9_ram_setup(struct thread *thread) { struct pdbg_target *target; struct core *chip = target_to_core( pdbg_target_require_parent("core", &thread->target)); uint64_t value; if (thread->ram_is_setup) return 1; /* We can only ram a thread if all the threads on the core/chip are * quiesced */ pdbg_for_each_target("thread", &chip->target, target) { struct thread *tmp; /* If this thread wasn't enabled it may not yet have been probed so do that now. This will also update the thread status */ if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED) goto out_fail; tmp = target_to_thread(target); if (!(tmp->status.quiesced)) goto out_fail; } /* Wait for NEST_ACTIVE to clear */ do { thread_read(thread, P9_RAS_STATUS, &value); } while (value & PPC_BIT(32)); /* Wait for THREAD_ACTION_IN_PROGRESS to clear */ do { thread_read(thread, P9_THREAD_INFO, &value); } while (value & PPC_BIT(23)); /* Activate thread for ramming */ CHECK_ERR_GOTO(out_fail, thread_write(thread, P9_THREAD_INFO, PPC_BIT(18 + thread->id))); /* Enable ram mode */ CHECK_ERR_GOTO(out_fail, thread_write(thread, P9_RAM_MODEREG, PPC_BIT(0))); /* Setup SPRC to use SPRD */ CHECK_ERR_GOTO(out_fail, thread_write(thread, P9_SPR_MODE, 0x00000ff000000000)); CHECK_ERR_GOTO(out_fail, thread_write(thread, P9_SCOMC, 0x0)); thread->status = p9_get_thread_status(thread); thread->ram_is_setup = true; return 0; out_fail: return 1; } static int __p9_ram_instruction(struct thread *thread, uint64_t opcode, uint64_t *scratch) { uint64_t predecode, value; int rc; if (!thread->ram_is_setup) return 1; switch(opcode & OPCODE_MASK) { case MTNIA_OPCODE: opcode = 0x4c0000a4; opcode |= 0x001e0000; predecode = 8; break; case MFNIA_OPCODE: opcode = 0x1ac804; predecode = 2; break; case MTMSR_OPCODE: opcode |= 0x001e0000; predecode = 8; break; case MFSPR_OPCODE: switch(MXSPR_SPR(opcode)) { case 1: /* XER */ predecode = 4; break; default: predecode = 0; break; } break; case MTSPR_OPCODE: switch(MXSPR_SPR(opcode)) { case 1: /* XER */ predecode = 4; break; default: predecode = 0; break; } break; default: predecode = 0; } CHECK_ERR(thread_write(thread, P9_SCR0_REG, *scratch)); value = SETFIELD(PPC_BITMASK(0, 1), 0ull, thread->id); value = SETFIELD(PPC_BITMASK(2, 5), value, predecode); value = SETFIELD(PPC_BITMASK(8, 39), value, opcode); CHECK_ERR(thread_write(thread, P9_RAM_CTRL, value)); CHECK_ERR(thread_read(thread, P9_RAM_STATUS, &value)); rc = 0; if (value & PPC_BIT(0)) { printf("Error RAMing opcode=%" PRIx64 " attempting to RAM while in recovery (status=%" PRIx64")\n", opcode, value); rc = 1; goto out; } if (value & PPC_BIT(2)) { printf("Error RAMing opcode=%" PRIx64 " exception or interrupt (status=%" PRIx64")\n", opcode, value); rc = 1; goto out; } if (!(value & PPC_BIT(1))) { printf("Warning RAMing opcode=%" PRIx64 " unexpected status=%" PRIx64"\n", opcode, value); } out: if ((opcode & OPCODE_MASK) == LD_OPCODE) { while (!(value & PPC_BIT(3))) { CHECK_ERR(thread_read(thread, P9_RAM_STATUS, &value)); } } if (!rc) CHECK_ERR(thread_read(thread, P9_SCR0_REG, scratch)); return rc; } static int p9_ram_instruction(struct thread *thread, uint64_t opcode, uint64_t *scratch) { if ((opcode & OPCODE_MASK) == LD_OPCODE) { printf("RAM LSU opcodes are disabled for POWER9 because exceptions will checkstop. Use ADU instead.\n"); return 1; } if ((opcode & OPCODE_MASK) == LD_OPCODE) { /* * Loads must be rammed twice, the value of the second used. * A fault should still be returned though. Unfortunately * any load fault seems to be a checkstop. */ int rc = __p9_ram_instruction(thread, opcode, scratch); if (rc) return rc; } return __p9_ram_instruction(thread, opcode, scratch); } static int p9_ram_destroy(struct thread *thread) { if (!thread->ram_is_setup) return 1; /* Disable ram mode */ CHECK_ERR(thread_write(thread, P9_RAM_MODEREG, 0)); /* Deactivate thread for ramming */ CHECK_ERR(thread_write(thread, P9_THREAD_INFO, 0)); thread->status = p9_get_thread_status(thread); thread->ram_is_setup = false; return 0; } static int p9_ram_getxer(struct pdbg_target *thread, uint64_t *value) { CHECK_ERR(ram_getspr(thread, 1, value)); return 0; } static int p9_ram_putxer(struct pdbg_target *thread, uint64_t value) { CHECK_ERR(ram_putspr(thread, 1, value)); return 0; } static struct thread p9_thread = { .target = { .name = "POWER9 Thread", .compatible = "ibm,power9-thread", .class = "thread", .probe = p9_thread_probe, .release = p9_thread_release, }, .start = p9_thread_start, .stop = p9_thread_stop, .step = p9_thread_step, .sreset = p9_thread_sreset, .ram_setup = p9_ram_setup, .ram_instruction = p9_ram_instruction, .ram_destroy = p9_ram_destroy, .ram_getxer = p9_ram_getxer, .ram_putxer = p9_ram_putxer, }; DECLARE_HW_UNIT(p9_thread); #define HEADER_CHECK_DATA ((uint64_t) 0xc0ffee03 << 32) static int p9_chiplet_getring(struct chiplet *chiplet, uint64_t ring_addr, int64_t ring_len, uint32_t result[]) { uint64_t scan_type_addr; uint64_t scan_data_addr; uint64_t scan_header_addr; uint64_t scan_type_data; uint64_t set_pulse = 1; uint64_t bits = 32; uint64_t data; /* We skip the first word in the results so we can write it later as it * should contain the header read out at the end */ int i = 0; scan_type_addr = (ring_addr & 0x7fff0000) | 0x7; scan_data_addr = (scan_type_addr & 0xffff0000) | 0x8000; scan_header_addr = scan_data_addr & 0xffffe000; scan_type_data = (ring_addr & 0xfff0) << 13; scan_type_data |= 0x800 >> (ring_addr & 0xf); scan_type_data <<= 32; pib_write(&chiplet->target, scan_type_addr, scan_type_data); pib_write(&chiplet->target, scan_header_addr, HEADER_CHECK_DATA); /* The final 32 bit read is the header which we do at the end */ ring_len -= 32; i = 1; while (ring_len > 0) { ring_len -= bits; if (set_pulse) { scan_data_addr |= 0x4000; set_pulse = 0; } else scan_data_addr &= ~0x4000ULL; scan_data_addr &= ~0xffull; scan_data_addr |= bits; pib_read(&chiplet->target, scan_data_addr, &data); /* Discard lower 32 bits */ /* TODO: We always read 64-bits from the ring on P9 so we could * optimise here by reading 64-bits at a time, but I'm not * confident I've figured that out and 32-bits is what Hostboot * does and seems to work. */ data >>= 32; /* Left-align data */ data <<= 32 - bits; result[i++] = data; if (ring_len > 0 && (ring_len < bits)) bits = ring_len; } pib_read(&chiplet->target, scan_header_addr | 0x20, &data); data &= 0xffffffff00000000; result[0] = data >> 32; if (data != HEADER_CHECK_DATA) printf("WARNING: Header check failed. Make sure you specified the right ring length!\n" "Ring data is probably corrupt now.\n"); return 0; } static int p9_core_probe(struct pdbg_target *target) { struct core *core = target_to_core(target); int i = 0; uint64_t value; if (pib_read(target, NET_CTRL0, &value)) return -1; if (!(value & NET_CTRL0_CHIPLET_ENABLE)) return -1; CHECK_ERR(pib_write(target, PPM_SPWKUP_FSP, PPC_BIT(0))); do { usleep(1000); CHECK_ERR(pib_read(target, PPM_SSHFSP, &value)); if (i++ > SPECIAL_WKUP_TIMEOUT) { PR_ERROR("Timeout waiting for special wakeup on %s@0x%08" PRIx64 "\n", target->name, dt_get_address(target, 0, NULL)); break; } } while (!(value & SPECIAL_WKUP_DONE)); /* Child threads will set this to false if they are released while quiesced */ core->release_spwkup = true; return 0; } static void p9_core_release(struct pdbg_target *target) { struct pdbg_target *child; struct core *core = target_to_core(target); enum pdbg_target_status status; usleep(1); /* enforce small delay before and after it is cleared */ /* Probe and release all threads to ensure release_spwkup is up to * date */ pdbg_for_each_target("thread", target, child) { status = pdbg_target_status(child); /* This thread has already been release so should have set * release_spwkup to false if it was quiesced, */ if (status == PDBG_TARGET_RELEASED) continue; status = pdbg_target_probe(child); if (status != PDBG_TARGET_ENABLED) continue; /* Release the thread to ensure release_spwkup is updated. */ pdbg_target_release(child); } if (!core->release_spwkup) return; pib_write(target, PPM_SPWKUP_FSP, 0); usleep(10000); } static struct core p9_core = { .target = { .name = "POWER9 Core", .compatible = "ibm,power9-core", .class = "core", .probe = p9_core_probe, .release = p9_core_release, }, }; DECLARE_HW_UNIT(p9_core); static int p9_chiplet_probe(struct pdbg_target *target) { uint64_t value; if (pib_read(target, NET_CTRL0, &value)) return -1; if (!(value & NET_CTRL0_CHIPLET_ENABLE)) return -1; return 0; } static struct chiplet p9_chiplet = { .target = { .name = "POWER9 Chiplet", .compatible = "ibm,power9-chiplet", .class = "chiplet", .probe = p9_chiplet_probe, }, .getring = p9_chiplet_getring, }; DECLARE_HW_UNIT(p9_chiplet); pdbg-2.0/libpdbg/target.c000066400000000000000000000233571336450571500153470ustar00rootroot00000000000000#include #include #include #include #include #include #include "bitutils.h" #include "target.h" #include "device.h" #include "operations.h" #include "debug.h" struct list_head empty_list = LIST_HEAD_INIT(empty_list); struct list_head target_classes = LIST_HEAD_INIT(target_classes); /* Work out the address to access based on the current target and * final class name */ static struct pdbg_target *get_class_target_addr(struct pdbg_target *target, const char *name, uint64_t *addr) { /* Check class */ while (strcmp(target->class, name)) { /* Keep walking the tree translating addresses */ *addr += dt_get_address(target, 0, NULL); target = target->parent; /* The should always be a parent. If there isn't it * means we traversed up the whole device tree and * didn't find a parent matching the given class. */ assert(target); } return target; } /* The indirect access code was largely stolen from hw/xscom.c in skiboot */ #define PIB_IND_MAX_RETRIES 10 #define PIB_IND_READ PPC_BIT(0) #define PIB_IND_ADDR PPC_BITMASK(12, 31) #define PIB_IND_DATA PPC_BITMASK(48, 63) #define PIB_DATA_IND_COMPLETE PPC_BIT(32) #define PIB_DATA_IND_ERR PPC_BITMASK(33, 35) #define PIB_DATA_IND_DATA PPC_BITMASK(48, 63) static int pib_indirect_read(struct pib *pib, uint64_t addr, uint64_t *data) { uint64_t indirect_addr; int retries; if ((addr >> 60) & 1) { PR_ERROR("Indirect form 1 not supported\n"); return -1; } indirect_addr = addr & 0x7fffffff; *data = PIB_IND_READ | (addr & PIB_IND_ADDR); CHECK_ERR(pib->write(pib, indirect_addr, *data)); /* Wait for completion */ for (retries = 0; retries < PIB_IND_MAX_RETRIES; retries++) { CHECK_ERR(pib->read(pib, indirect_addr, data)); if ((*data & PIB_DATA_IND_COMPLETE) && ((*data & PIB_DATA_IND_ERR) == 0)) { *data = *data & PIB_DATA_IND_DATA; break; } if ((*data & PIB_DATA_IND_COMPLETE) || (retries >= PIB_IND_MAX_RETRIES)) { PR_ERROR("Error reading indirect register"); return -1; } } return 0; } static int pib_indirect_write(struct pib *pib, uint64_t addr, uint64_t data) { uint64_t indirect_addr; int retries; if ((addr >> 60) & 1) { PR_ERROR("Indirect form 1 not supported\n"); return -1; } indirect_addr = addr & 0x7fffffff; data &= PIB_IND_DATA; data |= addr & PIB_IND_ADDR; CHECK_ERR(pib->write(pib, indirect_addr, data)); /* Wait for completion */ for (retries = 0; retries < PIB_IND_MAX_RETRIES; retries++) { CHECK_ERR(pib->read(pib, indirect_addr, &data)); if ((data & PIB_DATA_IND_COMPLETE) && ((data & PIB_DATA_IND_ERR) == 0)) break; if ((data & PIB_DATA_IND_COMPLETE) || (retries >= PIB_IND_MAX_RETRIES)) { PR_ERROR("Error writing indirect register"); return -1; } } return 0; } int pib_read(struct pdbg_target *pib_dt, uint64_t addr, uint64_t *data) { struct pib *pib; uint64_t target_addr = addr; int rc; pib_dt = get_class_target_addr(pib_dt, "pib", &target_addr); pib = target_to_pib(pib_dt); if (target_addr & PPC_BIT(0)) rc = pib_indirect_read(pib, target_addr, data); else rc = pib->read(pib, target_addr, data); PR_DEBUG("addr:0x%08" PRIx64 " data:0x%016" PRIx64 "\n", target_addr, *data); return rc; } int pib_write(struct pdbg_target *pib_dt, uint64_t addr, uint64_t data) { struct pib *pib; uint64_t target_addr = addr; int rc; pib_dt = get_class_target_addr(pib_dt, "pib", &target_addr); pib = target_to_pib(pib_dt); PR_DEBUG("addr:0x%08" PRIx64 " data:0x%016" PRIx64 "\n", target_addr, data); if (target_addr & PPC_BIT(0)) rc = pib_indirect_write(pib, target_addr, data); else rc = pib->write(pib, target_addr, data); return rc; } /* Wait for a SCOM register addr to match value & mask == data */ int pib_wait(struct pdbg_target *pib_dt, uint64_t addr, uint64_t mask, uint64_t data) { struct pib *pib; uint64_t tmp; int rc; pib_dt = get_class_target_addr(pib_dt, "pib", &addr); pib = target_to_pib(pib_dt); do { if (addr & PPC_BIT(0)) rc = pib_indirect_read(pib, addr, &tmp); else rc = pib->read(pib, addr, &tmp); if (rc) return rc; } while ((tmp & mask) != data); return 0; } int opb_read(struct pdbg_target *opb_dt, uint32_t addr, uint32_t *data) { struct opb *opb; uint64_t addr64 = addr; opb_dt = get_class_target_addr(opb_dt, "opb", &addr64); opb = target_to_opb(opb_dt); return opb->read(opb, addr64, data); } int opb_write(struct pdbg_target *opb_dt, uint32_t addr, uint32_t data) { struct opb *opb; uint64_t addr64 = addr; opb_dt = get_class_target_addr(opb_dt, "opb", &addr64); opb = target_to_opb(opb_dt); return opb->write(opb, addr64, data); } int fsi_read(struct pdbg_target *fsi_dt, uint32_t addr, uint32_t *data) { struct fsi *fsi; uint64_t addr64 = addr; fsi_dt = get_class_target_addr(fsi_dt, "fsi", &addr64); fsi = target_to_fsi(fsi_dt); return fsi->read(fsi, addr64, data); } int fsi_write(struct pdbg_target *fsi_dt, uint32_t addr, uint32_t data) { struct fsi *fsi; uint64_t addr64 = addr; fsi_dt = get_class_target_addr(fsi_dt, "fsi", &addr64); fsi = target_to_fsi(fsi_dt); return fsi->write(fsi, addr64, data); } struct pdbg_target *require_target_parent(struct pdbg_target *target) { assert(target->parent); return target->parent; } /* Finds the given class. Returns NULL if not found. */ struct pdbg_target_class *find_target_class(const char *name) { struct pdbg_target_class *target_class; list_for_each(&target_classes, target_class, class_head_link) if (!strcmp(target_class->name, name)) return target_class; return NULL; } /* Same as above but dies with an assert if the target class doesn't * exist */ struct pdbg_target_class *require_target_class(const char *name) { struct pdbg_target_class *target_class; target_class = find_target_class(name); if (!target_class) { PR_ERROR("Couldn't find class %s\n", name); assert(0); } return target_class; } /* Returns the existing class or allocates space for a new one */ struct pdbg_target_class *get_target_class(const char *name) { struct pdbg_target_class *target_class; if ((target_class = find_target_class(name))) return target_class; /* Need to allocate a new class */ PR_DEBUG("Allocating %s target class\n", name); target_class = calloc(1, sizeof(*target_class)); assert(target_class); target_class->name = strdup(name); list_head_init(&target_class->targets); list_add_tail(&target_classes, &target_class->class_head_link); return target_class; } extern struct hw_unit_info *__start_hw_units; extern struct hw_init_info *__stop_hw_units; struct hw_unit_info *find_compatible_target(const char *compat) { struct hw_unit_info **p; struct pdbg_target *target; for (p = &__start_hw_units; p < (struct hw_unit_info **) &__stop_hw_units; p++) { target = (*p)->hw_unit; if (!strcmp(target->compatible, compat)) return *p; } return NULL; } void pdbg_targets_init(void *fdt) { dt_root = dt_new_node("", NULL, 0); dt_expand(fdt); } /* We walk the tree root down disabling targets which might/should * exist but don't */ enum pdbg_target_status pdbg_target_probe(struct pdbg_target *target) { struct pdbg_target *parent; enum pdbg_target_status status; assert(target); status = pdbg_target_status(target); assert(status != PDBG_TARGET_RELEASED); if (status == PDBG_TARGET_DISABLED || status == PDBG_TARGET_NONEXISTENT || status == PDBG_TARGET_ENABLED) /* We've already tried probing this target and by assumption * it's status won't have changed */ return status; parent = target->parent; if (parent) { /* Recurse up the tree to probe and set parent target status */ pdbg_target_probe(parent); status = pdbg_target_status(parent); switch(status) { case PDBG_TARGET_NONEXISTENT: /* The parent doesn't exist neither does it's * children */ target->status = PDBG_TARGET_NONEXISTENT; return PDBG_TARGET_NONEXISTENT; case PDBG_TARGET_DISABLED: /* The parent is disabled, we know nothing of the child * so leave it in it's current state unless it must * exist. * TODO: Must exist error reporting */ assert(pdbg_target_status(target) != PDBG_TARGET_MUSTEXIST); return pdbg_target_status(target); case PDBG_TARGET_RELEASED: case PDBG_TARGET_MUSTEXIST: case PDBG_TARGET_UNKNOWN: /* We must know by now if the parent exists or not */ assert(0); break; case PDBG_TARGET_ENABLED: break; } } /* At this point any parents must exist and have already been probed */ if (target->probe && target->probe(target)) { /* Could not find the target */ assert(pdbg_target_status(target) != PDBG_TARGET_MUSTEXIST); target->status = PDBG_TARGET_NONEXISTENT; return PDBG_TARGET_NONEXISTENT; } target->status = PDBG_TARGET_ENABLED; return PDBG_TARGET_ENABLED; } /* Releases a target by first recursively releasing all its children */ void pdbg_target_release(struct pdbg_target *target) { struct pdbg_target *child; if (pdbg_target_status(target) != PDBG_TARGET_ENABLED) return; pdbg_for_each_child_target(target, child) pdbg_target_release(child); /* Release the target */ if (target->release) target->release(target); target->status = PDBG_TARGET_RELEASED; } /* * Probe all targets in the device tree. */ void pdbg_target_probe_all(struct pdbg_target *parent) { struct pdbg_target *child; if (!parent) parent = dt_root; pdbg_for_each_child_target(parent, child) { pdbg_target_probe_all(child); pdbg_target_probe(child); } } bool pdbg_target_is_class(struct pdbg_target *target, const char *class) { if (!target || !target->class || !class) return false; return strcmp(target->class, class) == 0; } void *pdbg_target_priv(struct pdbg_target *target) { return target->priv; } void pdbg_target_priv_set(struct pdbg_target *target, void *priv) { target->priv = priv; } struct pdbg_target *pdbg_target_root(void) { return dt_root; } pdbg-2.0/libpdbg/target.h000066400000000000000000000125171336450571500153500ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 __TARGET_H #define __TARGET_H #include #include #include #include #include "compiler.h" #include "device.h" #include "libpdbg.h" enum chip_type {CHIP_UNKNOWN, CHIP_P8, CHIP_P8NV, CHIP_P9}; struct pdbg_target_class { char *name; struct list_head targets; struct list_node class_head_link; }; struct pdbg_target { char *name; char *compatible; char *class; int (*probe)(struct pdbg_target *target); void (*release)(struct pdbg_target *target); int index; enum pdbg_target_status status; const char *dn_name; struct list_node list; struct list_head properties; struct list_head children; struct pdbg_target *parent; u32 phandle; bool probed; struct list_node class_link; void *priv; }; struct pdbg_target *require_target_parent(struct pdbg_target *target); struct pdbg_target_class *find_target_class(const char *name); struct pdbg_target_class *require_target_class(const char *name); struct pdbg_target_class *get_target_class(const char *name); bool pdbg_target_is_class(struct pdbg_target *target, const char *class); extern struct list_head empty_list; extern struct list_head target_classes; #define for_each_class_target(class_name, target) \ list_for_each((find_target_class(class_name) ? &require_target_class(class_name)->targets : &empty_list), target, class_link) #define for_each_target_class(target_class) \ list_for_each(&target_classes, target_class, class_head_link) struct hw_unit_info { void *hw_unit; size_t size; }; struct hw_unit_info *find_compatible_target(const char *compat); /* We can't pack the structs themselves directly into a special * section because there doesn't seem to be any standard way of doing * that due to alignment rules. So instead we pack pointers into a * special section. * * If this macro fails to compile for you, you've probably not * declared the struct pdbg_target as the first member of the * container struct. Not doing so will break other assumptions. * */ #define DECLARE_HW_UNIT(name) \ static inline void name ##_hw_unit_check(void) { \ ((void)sizeof(char[1 - 2 * (container_off(typeof(name), target) != 0)])); \ } \ const struct hw_unit_info __used name ##_hw_unit = \ { .hw_unit = &name, .size = sizeof(name) }; \ const struct hw_unit_info __used __section("hw_units") *name ##_hw_unit_p = &name ##_hw_unit struct htm { struct pdbg_target target; int (*start)(struct htm *); int (*stop)(struct htm *); int (*status)(struct htm *); int (*dump)(struct htm *, char *); int (*record)(struct htm *, char *); }; #define target_to_htm(x) container_of(x, struct htm, target) struct adu { struct pdbg_target target; int (*getmem)(struct adu *, uint64_t, uint64_t *, int); int (*putmem)(struct adu *, uint64_t, uint64_t, int, int); }; #define target_to_adu(x) container_of(x, struct adu, target) struct pib { struct pdbg_target target; int (*read)(struct pib *, uint64_t, uint64_t *); int (*write)(struct pib *, uint64_t, uint64_t); void *priv; }; #define target_to_pib(x) container_of(x, struct pib, target) struct opb { struct pdbg_target target; int (*read)(struct opb *, uint32_t, uint32_t *); int (*write)(struct opb *, uint32_t, uint32_t); }; #define target_to_opb(x) container_of(x, struct opb, target) struct fsi { struct pdbg_target target; int (*read)(struct fsi *, uint32_t, uint32_t *); int (*write)(struct fsi *, uint32_t, uint32_t); enum chip_type chip_type; }; #define target_to_fsi(x) container_of(x, struct fsi, target) struct core { struct pdbg_target target; bool release_spwkup; }; #define target_to_core(x) container_of(x, struct core, target) struct thread { struct pdbg_target target; struct thread_state status; int id; int (*step)(struct thread *, int); int (*start)(struct thread *); int (*stop)(struct thread *); int (*sreset)(struct thread *); bool ram_did_quiesce; /* was the thread quiesced by ram mode */ /* ram_setup() should be called prior to using ram_instruction() to * actually ram the instruction and return the result. ram_destroy() * should be called at completion to clean-up. */ bool ram_is_setup; int (*ram_setup)(struct thread *); int (*ram_instruction)(struct thread *, uint64_t opcode, uint64_t *scratch); int (*ram_destroy)(struct thread *); int (*ram_getxer)(struct pdbg_target *, uint64_t *value); int (*ram_putxer)(struct pdbg_target *, uint64_t value); int (*enable_attn)(struct pdbg_target *); }; #define target_to_thread(x) container_of(x, struct thread, target) /* Place holder for chiplets which we just want translation for */ struct chiplet { struct pdbg_target target; int (*getring)(struct chiplet *, uint64_t, int64_t, uint32_t[]); }; #define target_to_chiplet(x) container_of(x, struct chiplet, target) #endif pdbg-2.0/p8-fsi.dts.m4000066400000000000000000000026011336450571500144400ustar00rootroot00000000000000/dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x0>; fsi@0 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,bmcfsi"; reg = <0x0 0x0 0x0>; /* GPIO pin definitions */ fsi_clk = <0x0 0x4>; /* A4 */ fsi_dat = <0x0 0x5>; /* A5 */ fsi_dat_en = <0x20 0x1e>; /* H6 */ fsi_enable = <0x0 0x18>; /* D0 */ cronus_sel = <0x0 0x6>; /* A6 */ clock_delay = <0x14>; index = <0x0>; status = "mustexist"; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; index = <0x0>; include(p8-pib.dts.m4)dnl }; hmfsi@100000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,fsi-hmfsi"; reg = <0x0 0x100000 0x8000>; port = <0x1>; index = <0x1>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; index = <0x1>; include(p8-pib.dts.m4)dnl }; }; hmfsi@180000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,fsi-hmfsi"; reg = <0x0 0x180000 0x80000>; port = <0x2>; index = <0x2>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; index = <0x2>; include(p8-pib.dts.m4)dnl }; }; }; }; pdbg-2.0/p8-host.dts.m4000066400000000000000000000011701336450571500146340ustar00rootroot00000000000000define(`CHIP',`pib@$1 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,host-pib"; reg = <$1>; chip-id = <$1>; index = <$1>; include(p8-pib.dts.m4)dnl }')dnl /dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x0>; CHIP(0); CHIP(1); CHIP(2); CHIP(3); CHIP(4); CHIP(5); CHIP(6); CHIP(7); CHIP(8); CHIP(9); CHIP(10); CHIP(11); CHIP(12); CHIP(13); CHIP(14); CHIP(15); CHIP(16); CHIP(17); CHIP(18); CHIP(19); CHIP(20); CHIP(21); CHIP(22); CHIP(23); CHIP(24); CHIP(25); CHIP(26); CHIP(27); CHIP(28); CHIP(29); CHIP(30); CHIP(31); }; pdbg-2.0/p8-i2c.dts.m4000066400000000000000000000023151336450571500143360ustar00rootroot00000000000000/dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x0>; /* I2C attached pib */ pib@50 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,power8-i2c-slave"; bus = "/dev/i2c4"; reg = <0x50>; index = <0x0>; status = "mustexist"; include(p8-pib.dts.m4)dnl opb@20010 { #address-cells = <0x1>; #size-cells = <0x1>; reg = <0x0 0x20010 0xa>; compatible = "ibm,power8-opb"; hmfsi@100000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,power8-opb-hmfsi"; reg = <0x100000 0x80000>; port = <0x1>; index = <0x1>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; index = <0x1>; include(p8-pib.dts.m4)dnl }; }; hmfsi@180000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,power8-opb-hmfsi"; reg = <0x180000 0x80000>; port = <0x2>; index = <0x2>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; index = <0x2>; include(p8-pib.dts.m4)dnl }; }; }; }; }; pdbg-2.0/p8-kernel.dts.m4000066400000000000000000000016641336450571500151470ustar00rootroot00000000000000/dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x0>; fsi0: kernelfsi@0 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,kernel-fsi"; reg = <0x0 0x0 0x0>; index = <0x0>; status = "mustexist"; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; index = <0x0>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; include(p8-pib.dts.m4)dnl }; hmfsi@100000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,fsi-hmfsi"; reg = <0x0 0x100000 0x8000>; port = <0x1>; index = <0x1>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib"; index = <0x1>; include(p8-pib.dts.m4)dnl }; }; }; }; pdbg-2.0/p8-pib.dts.m4000066400000000000000000000020721336450571500144330ustar00rootroot00000000000000define(`CONCAT', `$1$2')dnl define(`HEX', `CONCAT(0x, $1)')dnl define(`CORE_BASE', `eval(0x10000000 + $1 * 0x1000000, 16)')dnl define(`CORE', `core@CORE_BASE($1) { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,power8-core"; reg = <0x0 HEX(CORE_BASE($1)) 0xfffff>; index = ; chtm@11000 { compatible = "ibm,power8-chtm"; reg = <0x0 0x11000 0xB>; }; THREAD(0); THREAD(1); THREAD(2); THREAD(3); THREAD(4); THREAD(5); THREAD(6); THREAD(7); }')dnl define(`THREAD_BASE', `eval(0x13000 + $1 * 0x10, 16)')dnl define(`THREAD',`thread@THREAD_BASE($1) { reg = <0x0 HEX(THREAD_BASE($1)) 0x10>; compatible = "ibm,power8-thread"; index = ; }')dnl dnl define(`PROC_CORES', `CORE(1, 1); CORE(2, 2); CORE(3, 3); CORE(4, 4); CORE(5, 5); CORE(6, 6); CORE(9, 9); CORE(10, 10); CORE(11, 11); CORE(12, 12); CORE(13, 13); CORE(14, 14)')dnl adu@2020000 { compatible = "ibm,power8-adu"; reg = <0x0 0x2020000 0x4>; }; nhtm@2010880 { compatible = "ibm,power8-nhtm"; reg = <0x0 0x2010880 0x8>; index = <0x0>; }; PROC_CORES; pdbg-2.0/p9-fsi.dtsi.m4000066400000000000000000000014411336450571500146130ustar00rootroot00000000000000 / { #address-cells = <0x1>; #size-cells = <0x0>; fsi0: fsi@0 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,bmcfsi"; reg = <0x0 0x0 0x0>; index = <0x0>; status = "mustexist"; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; index = <0x0>; compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib"; include(p9-pib.dts.m4)dnl }; hmfsi@100000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,fsi-hmfsi"; reg = <0x0 0x100000 0x8000>; port = <0x1>; index = <0x1>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; index = <0x1>; compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib"; include(p9-pib.dts.m4)dnl }; }; }; }; pdbg-2.0/p9-host.dts.m4000066400000000000000000000010021336450571500146270ustar00rootroot00000000000000/dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x0>; /* Host based debugfs access */ pib@0 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,host-pib"; reg = <0x0>; chip-id = <0x0>; index = <0x0>; include(p9-pib.dts.m4)dnl }; pib@8 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,host-pib"; reg = <0x8>; chip-id = <0x8>; index = <0x1>; include(p9-pib.dts.m4)dnl }; }; pdbg-2.0/p9-kernel.dts.m4000066400000000000000000000014621336450571500151440ustar00rootroot00000000000000/dts-v1/; / { #address-cells = <0x1>; #size-cells = <0x0>; fsi0: kernelfsi@0 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,kernel-fsi"; reg = <0x0 0x0 0x0>; index = <0x0>; status = "mustexist"; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; index = <0x0>; compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib"; include(p9-pib.dts.m4)dnl }; hmfsi@100000 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,fsi-hmfsi"; reg = <0x0 0x100000 0x8000>; port = <0x1>; index = <0x1>; pib@1000 { #address-cells = <0x2>; #size-cells = <0x1>; reg = <0x0 0x1000 0x7>; index = <0x1>; compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib"; include(p9-pib.dts.m4)dnl }; }; }; }; pdbg-2.0/p9-pib.dts.m4000066400000000000000000000034211336450571500144330ustar00rootroot00000000000000define(`CONCAT', `$1$2')dnl define(`HEX', `CONCAT(0x, $1)')dnl define(`CORE_BASE', `eval(0x20000000 + $1 * 0x1000000, 16)')dnl define(`CORE', `chiplet@CORE_BASE($1) { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "ibm,power9-chiplet"; index = ; reg = <0x0 HEX(CORE_BASE($1)) 0xfffff>; core@0 { #address-cells = <0x1>; #size-cells = <0x0>; compatible = "ibm,power9-core"; index = ; reg = <0x0 0x0 0xfffff>; THREAD(0); THREAD(1); THREAD(2); THREAD(3); }; }')dnl define(`THREAD_BASE', `eval($1, 16)')dnl define(`THREAD',`thread@THREAD_BASE($1) { compatible = "ibm,power9-thread"; reg = <0x0>; tid = ; index = ; }')dnl define(`CHIPLET_BASE', `eval(0x1000000 * $1, 16)')dnl define(`CHIPLET', `chiplet@CHIPLET_BASE($1) { compatible = "ibm,power9-chiplet"; index = ; reg = <0x0 HEX(CHIPLET_BASE($1)) 0xfffff>; }')dnl adu@90000 { compatible = "ibm,power9-adu"; reg = <0x0 0x90000 0x5>; }; htm@5012880 { compatible = "ibm,power9-nhtm"; reg = <0x0 0x5012880 0x40>; index = <0x0>; }; htm@50128C0 { compatible = "ibm,power9-nhtm"; reg = <0x0 0x50128C0 0x40>; index = <0x1>; }; CORE(0, 0); CORE(1, 1); CORE(2, 2); CORE(3, 3); CORE(4, 4); CORE(5, 5); CORE(6, 6); CORE(7, 7); CORE(8, 8); CORE(9, 9); CORE(10, 10); CORE(11, 11); CORE(12, 12); CORE(13, 13); CORE(14, 14); CORE(15, 15); CORE(16, 16); CORE(17, 17); CORE(18, 18); CORE(19, 19); CORE(20, 20); CORE(21, 21); CORE(22, 22); CORE(23, 23); CHIPLET(1); CHIPLET(2); CHIPLET(3); CHIPLET(4); CHIPLET(5); CHIPLET(6); CHIPLET(7); CHIPLET(8); CHIPLET(9); CHIPLET(12); CHIPLET(13); CHIPLET(14); CHIPLET(15); CHIPLET(16); CHIPLET(17); CHIPLET(18); CHIPLET(19); CHIPLET(20); CHIPLET(21); pdbg-2.0/p9r-fsi.dts.m4000066400000000000000000000004331336450571500146240ustar00rootroot00000000000000/dts-v1/; /include/ "p9-fsi.dtsi" / { }; &fsi0 { /* GPIO pin definitions */ fsi_clk = <0x1e0 0x10>; /* AA0 */ fsi_dat = <0x1e0 0x12>; /* AA2 */ fsi_dat_en = <0x80 0xa>; /* R2 */ fsi_enable = <0x0 0x18>; /* D0 */ cronus_sel = <0x0 0x6>; /* A6 */ clock_delay = <0x14>; }; pdbg-2.0/p9w-fsi.dts.m4000066400000000000000000000004311336450571500146270ustar00rootroot00000000000000/dts-v1/; /include/ "p9-fsi.dtsi" / { }; &fsi0 { /* GPIO pin definitions */ fsi_clk = <0x1e0 0x10>; /* AA0 */ fsi_dat = <0x20 0x0>; /* E0 */ fsi_dat_en = <0x80 0xa>; /* R2 */ fsi_enable = <0x0 0x18>; /* D0 */ cronus_sel = <0x0 0x6>; /* A6 */ clock_delay = <0x14>; }; pdbg-2.0/p9z-fsi.dts.m4000066400000000000000000000004301336450571500146310ustar00rootroot00000000000000/dts-v1/; /include/ "p9-fsi.dtsi" / { }; &fsi0 { /* GPIO pin definitions */ fsi_clk = <0x0 0x13>; /* C3 */ fsi_dat = <0x0 0x12>; /* C2 */ fsi_dat_en = <0x78 0x16>; /* O6 */ fsi_enable = <0x0 0x18>; /* D0 */ cronus_sel = <0x78 0x1e>; /* P6 */ clock_delay = <0x14>; }; pdbg-2.0/src/000077500000000000000000000000001336450571500130675ustar00rootroot00000000000000pdbg-2.0/src/cfam.c000066400000000000000000000030111336450571500141340ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #include "optcmd.h" static int _getcfam(struct pdbg_target *target, uint32_t index, uint64_t *addr, uint64_t *unused) { uint32_t value; if (fsi_read(target, *addr, &value)) return 0; printf("p%d:0x%x = 0x%08x\n", index, (uint32_t) *addr, value); return 1; } static int getcfam(uint32_t addr) { uint64_t addr64 = addr; return for_each_target("fsi", _getcfam, &addr64, NULL); } OPTCMD_DEFINE_CMD_WITH_ARGS(getcfam, getcfam, (ADDRESS32)); static int _putcfam(struct pdbg_target *target, uint32_t index, uint64_t *addr, uint64_t *data) { if (fsi_write(target, *addr, *data)) return 0; return 1; } static int putcfam(uint32_t addr, uint32_t data) { uint64_t addr64 = addr, data64 = data; return for_each_target("fsi", _putcfam, &addr64, &data64); } OPTCMD_DEFINE_CMD_WITH_ARGS(putcfam, putcfam, (ADDRESS32, DATA32)); pdbg-2.0/src/htm.c000066400000000000000000000177751336450571500140440ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 does the Hardware Trace Macro command parsing for pdbg * the program. * It will call into libpdbg backend with a target to either a 'nhtm' * or a 'chtm' which will ultimately do the work. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "main.h" #define HTM_ENUM_TO_STRING(e) ((e == HTM_NEST) ? "nhtm" : "chtm") #define PR_ERROR(x, args...) \ pdbg_log(PDBG_ERROR, x, ##args) enum htm_type { HTM_CORE, HTM_NEST, }; static inline void print_htm_address(enum htm_type type, struct pdbg_target *target) { if (type == HTM_CORE) { printf("p%d:", pdbg_parent_index(target, "pib")); printf("c%d:", pdbg_parent_index(target, "core")); } printf("t%d\n", pdbg_target_index(target)); } static char *get_htm_dump_filename(struct pdbg_target *target) { char *filename; int rc; rc = asprintf(&filename, "htm-p%d-c%d-t%d.dump", pdbg_parent_index(target, "pib"), pdbg_parent_index(target, "core"), pdbg_target_index(target)); if (rc == -1) return NULL; return filename; } static int run_start(enum htm_type type) { struct pdbg_target *target; int rc = 0; pdbg_for_each_class_target(HTM_ENUM_TO_STRING(type), target) { if (!target_selected(target)) continue; pdbg_target_probe(target); if (target_is_disabled(target)) continue; printf("Starting with buffer wrapping HTM@"); print_htm_address(type, target); if (htm_start(target) != 1) { printf("Couldn't start HTM@"); print_htm_address(type, target); } rc++; } return rc; } static int run_stop(enum htm_type type) { struct pdbg_target *target; int rc = 0; pdbg_for_each_class_target(HTM_ENUM_TO_STRING(type), target) { if (!target_selected(target)) continue; pdbg_target_probe(target); if (target_is_disabled(target)) continue; printf("Stopping HTM@"); print_htm_address(type, target); if (htm_stop(target) != 1) { printf("Couldn't stop HTM@"); print_htm_address(type, target); } rc++; } return rc; } static int run_status(enum htm_type type) { struct pdbg_target *target; int rc = 0; pdbg_for_each_class_target(HTM_ENUM_TO_STRING(type), target) { if (!target_selected(target)) continue; pdbg_target_probe(target); if (target_is_disabled(target)) continue; printf("Status HTM@"); print_htm_address(type, target); if (htm_status(target) != 1) { printf("Couldn't get HTM@"); print_htm_address(type, target); } rc++; } return rc; } static int run_dump(enum htm_type type) { struct pdbg_target *target; char *filename; int rc = 0; pdbg_for_each_class_target(HTM_ENUM_TO_STRING(type), target) { if (!target_selected(target)) continue; pdbg_target_probe(target); if (target_is_disabled(target)) continue; filename = get_htm_dump_filename(target); if (!filename) return 0; /* size = 0 will dump everything */ printf("Dumping HTM@"); print_htm_address(type, target); if (htm_dump(target, filename) != 1) { printf("Couldn't dump HTM@"); print_htm_address(type, target); } rc++; free(filename); } return rc; } static int run_record(enum htm_type type) { struct pdbg_target *target; char *filename; int rc = 0; pdbg_for_each_class_target(HTM_ENUM_TO_STRING(type), target) { if (!target_selected(target)) continue; pdbg_target_probe(target); if (target_is_disabled(target)) continue; filename = get_htm_dump_filename(target); if (!filename) return 0; /* size = 0 will dump everything */ printf("Recording till buffer wraps HTM@"); print_htm_address(type, target); if (htm_record(target, filename) != 1) { printf("Couldn't record HTM@"); print_htm_address(type, target); } rc++; free(filename); } return rc; } static struct { const char *name; const char *args; const char *desc; int (*fn)(enum htm_type); } actions[] = { { "start", "", "Start %s HTM", &run_start }, { "stop", "", "Stop %s HTM", &run_stop }, { "status", "", "Get %s HTM status", &run_status }, { "dump", "", "Dump %s HTM buffer to file", &run_dump }, { "record", "", "Start, wait & dump %s HTM", &run_record }, }; static void print_usage(enum htm_type type) { int i; for (i = 0; i < ARRAY_SIZE(actions); i++) { printf("%s %s", actions[i].name, actions[i].args); printf(actions[i].desc, HTM_ENUM_TO_STRING(type)); printf("\n"); } } static bool is_smt1(struct pdbg_target *target) { /* primary thread */ if (pdbg_target_index(target) == 0) { if (((thread_status(target).active)) && (thread_status(target).sleep_state == PDBG_THREAD_STATE_RUN)) return true; goto fail; } /* secondary thread */ if (thread_status(target).quiesced) return true; fail: fprintf(stderr, "Error: core HTM needs to run in SMT1 with no powersave. Try\n"); fprintf(stderr, " ppc64_cpu --smt=1\n"); fprintf(stderr, " for i in /sys/devices/system/cpu/cpu*/cpuidle/state*/disable;do echo 1 > $i;done\n"); return false; } int run_htm(int optind, int argc, char *argv[]) { struct pdbg_target *target, *nhtm; enum htm_type type; struct pdbg_target *core_target = NULL; int i, rc = 0; /* * As the index of the last argument is one less than argc, the difference * between optind and argc will always be at least 1. Here optind is pointing * to the 'htm' arg and we need at least 2 more following arguments, eg: * htm * so argc-optind >= 3 to proceed. */ if (argc - optind < 3) { fprintf(stderr, "Expecting one of 'core' or 'nest' with a command\n"); return 0; } optind++; if (strcmp(argv[optind], "core") == 0) { type = HTM_CORE; } else if (strcmp(argv[optind], "nest") == 0) { type = HTM_NEST; } else { fprintf(stderr, "Expecting one of 'core' or 'nest' not %s\n", argv[optind]); return 0; } if (type == HTM_CORE) { pdbg_for_each_class_target("core", target) { if (target_selected(target)) { if (!core_target) { core_target = target; } else { fprintf(stderr, "It doesn't make sense to core trace on" " multiple cores at once.\n"); fprintf(stderr, "What you probably want is -p 0 -c x\n"); return 0; } } } if (!core_target) { fprintf(stderr, "You haven't selected any HTM Cores\n"); return 0; } /* Check that powersave is off */ pdbg_for_each_class_target("thread", target) { pdbg_target_probe(target); if (pdbg_target_status(target) == PDBG_TARGET_NONEXISTENT) continue; if (!is_smt1(target)) return 0; } /* Select the correct chtm target */ pdbg_for_each_child_target(core_target, target) { if (!strcmp(pdbg_target_class_name(target), "chtm")) { target_select(target); pdbg_target_probe(target); } } } if (type == HTM_NEST) { pdbg_for_each_class_target("pib", target) { if (!target_selected(target)) continue; pdbg_for_each_target("nhtm", target, nhtm) target_select(nhtm); } } optind++; for (i = 0; i < ARRAY_SIZE(actions); i++) { if (strcmp(argv[optind], actions[i].name) == 0) { rc = actions[i].fn(type); break; } } if (i == ARRAY_SIZE(actions)) { PR_ERROR("Unsupported command: %s\n", argv[optind]); print_usage(type); return 0; } else if (rc == 0) { fprintf(stderr, "Couldn't run the HTM command.\n"); fprintf(stderr, "Double check that your kernel has debugfs mounted and the memtrace patches\n"); } return rc; } pdbg-2.0/src/htm.h000066400000000000000000000012511336450571500140270ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 int run_htm(int optind, int argc, char *argv[]); pdbg-2.0/src/main.c000066400000000000000000000520161336450571500141630ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #include #include "main.h" #include "htm.h" #include "options.h" #include "optcmd.h" #include "progress.h" #define PR_ERROR(x, args...) \ pdbg_log(PDBG_ERROR, x, ##args) #include "fake.dt.h" #ifdef TARGET_ARM #include "p8-i2c.dt.h" #include "p8-fsi.dt.h" #include "p8-kernel.dt.h" #include "p9w-fsi.dt.h" #include "p9r-fsi.dt.h" #include "p9z-fsi.dt.h" #include "p9-kernel.dt.h" #endif #ifdef TARGET_PPC #include "p8-host.dt.h" #include "p9-host.dt.h" #endif #define THREADS_PER_CORE 8 static enum backend backend = KERNEL; static char const *device_node; static int i2c_addr = 0x50; #define MAX_PROCESSORS 64 #define MAX_CHIPS 24 #define MAX_THREADS THREADS_PER_CORE #define MAX_LINUX_CPUS (MAX_PROCESSORS * MAX_CHIPS * MAX_THREADS) static int **processorsel[MAX_PROCESSORS]; static int *chipsel[MAX_PROCESSORS][MAX_CHIPS]; static int threadsel[MAX_PROCESSORS][MAX_CHIPS][MAX_THREADS]; static int probe(void); /* TODO: We are repeating ourselves here. A little bit more macro magic could * easily fix this but I was hesitant to introduce too much magic all at * once. */ extern struct optcmd_cmd optcmd_getscom, optcmd_putscom, optcmd_getcfam, optcmd_putcfam, optcmd_getgpr, optcmd_putgpr, optcmd_getspr, optcmd_putspr, optcmd_getnia, optcmd_putnia, optcmd_getmsr, optcmd_putmsr, optcmd_getring, optcmd_start, optcmd_stop, optcmd_step, optcmd_threadstatus, optcmd_sreset, optcmd_regs, optcmd_probe, optcmd_getmem, optcmd_putmem, optcmd_getxer, optcmd_putxer, optcmd_getcr, optcmd_putcr; static struct optcmd_cmd *cmds[] = { &optcmd_getscom, &optcmd_putscom, &optcmd_getcfam, &optcmd_putcfam, &optcmd_getgpr, &optcmd_putgpr, &optcmd_getspr, &optcmd_putspr, &optcmd_getnia, &optcmd_putnia, &optcmd_getmsr, &optcmd_putmsr, &optcmd_getring, &optcmd_start, &optcmd_stop, &optcmd_step, &optcmd_threadstatus, &optcmd_sreset, &optcmd_regs, &optcmd_probe, &optcmd_getmem, &optcmd_putmem, &optcmd_getxer, &optcmd_putxer, &optcmd_getcr, &optcmd_putcr, }; /* Purely for printing usage text. We could integrate printing argument and flag * help into optcmd if desired. */ struct action { const char *name; const char *args; const char *desc; }; static struct action actions[] = { { "getgpr", "", "Read General Purpose Register (GPR)" }, { "putgpr", " ", "Write General Purpose Register (GPR)" }, { "getnia", "", "Get Next Instruction Address (NIA)" }, { "putnia", "", "Write Next Instrution Address (NIA)" }, { "getspr", "", "Get Special Purpose Register (SPR)" }, { "putspr", " ", "Write Special Purpose Register (SPR)" }, { "getmsr", "", "Get Machine State Register (MSR)" }, { "putmsr", "", "Write Machine State Register (MSR)" }, { "getcr", "", "Get Condition Register (CR)" }, { "putcr", "", "Write Condition Register (CR)" }, { "getxer", "", "Get Fixed Point Exception Register (XER)" }, { "putxer", "", "Write Fixed Point Exception Register (XER)" }, { "getring", " ", "Read a ring. Length must be correct" }, { "start", "", "Start thread" }, { "step", "", "Set a thread instructions" }, { "stop", "", "Stop thread" }, { "htm", "core|nest start|stop|status|dump|record", "Hardware Trace Macro" }, { "probe", "", "" }, { "getcfam", "
", "Read system cfam" }, { "putcfam", "
[]", "Write system cfam" }, { "getscom", "
", "Read system scom" }, { "putscom", "
[]", "Write system scom" }, { "getmem", "
", "Read system memory" }, { "putmem", "
", "Write to system memory" }, { "threadstatus", "", "Print the status of a thread" }, { "sreset", "", "Reset" }, { "regs", "[--backtrace]", "State (optionally display backtrace)" }, }; static void print_usage(char *pname) { int i; printf("Usage: %s [options] command ...\n\n", pname); printf(" Options:\n"); printf("\t-p, --processor=<0-%d>||\n", MAX_PROCESSORS-1); printf("\t-c, --chip=<0-%d>||\n", MAX_CHIPS-1); printf("\t-t, --thread=<0-%d>||\n", MAX_THREADS-1); #ifdef TARGET_PPC printf("\t-l, --cpu=<0-%d>||\n", MAX_PROCESSORS-1); #endif printf("\t-a, --all\n"); printf("\t\tRun command on all possible processors/chips/threads (default)\n"); printf("\t-b, --backend=backend\n"); printf("\t\tfsi:\tAn experimental backend that uses\n"); printf("\t\t\tbit-banging to access the host processor\n"); printf("\t\t\tvia the FSI bus.\n"); printf("\t\ti2c:\tThe P8 only backend which goes via I2C.\n"); printf("\t\thost:\tUse the debugfs xscom nodes.\n"); printf("\t\tkernel:\tThe default backend which goes the kernel FSI driver.\n"); printf("\t-d, --device=backend device\n"); printf("\t\tFor I2C the device node used by the backend to access the bus.\n"); printf("\t\tFor FSI the system board type, one of p8 or p9w\n"); printf("\t\tDefaults to /dev/i2c4 for I2C\n"); printf("\t-s, --slave-address=backend device address\n"); printf("\t\tDevice slave address to use for the backend. Not used by FSI\n"); printf("\t\tand defaults to 0x50 for I2C\n"); printf("\t-D, --debug=\n"); printf("\t\t0:error (default) 1:warning 2:notice 3:info 4:debug\n"); printf("\t-S, --shutup\n"); printf("\t\tShut up those annoying progress bars\n"); printf("\t-V, --version\n"); printf("\t-h, --help\n"); printf("\n"); printf(" Commands:\n"); for (i = 0; i < ARRAY_SIZE(actions); i++) printf(" %-15s %-27s %s\n", actions[i].name, actions[i].args, actions[i].desc); } /* Parse argument of the form 0-5,7,9-11,15,17 */ static bool parse_list(const char *arg, int max, int *list, int *count) { char str[strlen(arg)+1]; char *tok, *tmp, *saveptr = NULL; int i; assert(max < INT_MAX); strcpy(str, arg); tmp = str; while ((tok = strtok_r(tmp, ",", &saveptr)) != NULL) { char *a, *b, *endptr, *saveptr2 = NULL; unsigned long int from, to; a = strtok_r(tok, "-", &saveptr2); if (a == NULL) { return false; } else { endptr = NULL; from = strtoul(a, &endptr, 0); if (*endptr != '\0') { fprintf(stderr, "Invalid value %s\n", a); return false; } if (from >= max) { fprintf(stderr, "Value %s larger than max %d\n", a, max-1); return false; } } b = strtok_r(NULL, "-", &saveptr2); if (b == NULL) { to = from; } else { endptr = NULL; to = strtoul(b, &endptr, 0); if (*endptr != '\0') { fprintf(stderr, "Invalid value %s\n", b); return false; } if (to >= max) { fprintf(stderr, "Value %s larger than max %d\n", b, max-1); return false; } } if (from > to) { fprintf(stderr, "Invalid range %s-%s\n", a, b); return false; } for (i = from; i <= to; i++) list[i] = 1; tmp = NULL; }; if (count != NULL) { int n = 0; for (i = 0; i < max; i++) { if (list[i] == 1) n++; } *count = n; } return true; } #ifdef TARGET_PPC int get_pir(int linux_cpu) { char *filename; FILE *file; int pir = -1; if(asprintf(&filename, "/sys/devices/system/cpu/cpu%i/pir", linux_cpu) < 0) return -1; file = fopen(filename, "r"); if (!file) { PR_ERROR("Invalid Linux CPU number %" PRIi32 "\n", linux_cpu); goto out2; } if(fscanf(file, "%" PRIx32 "\n", &pir) != 1) { PR_ERROR("fscanf() didn't match: %m\n"); pir = -1; goto out1; } out1: fclose(file); out2: free(filename); return pir; } /* Stolen from skiboot */ #define P9_PIR2GCID(pir) (((pir) >> 8) & 0x7f) #define P9_PIR2COREID(pir) (((pir) >> 2) & 0x3f) #define P9_PIR2THREADID(pir) ((pir) & 0x3) #define P8_PIR2GCID(pir) (((pir) >> 7) & 0x3f) #define P8_PIR2COREID(pir) (((pir) >> 3) & 0xf) #define P8_PIR2THREADID(pir) ((pir) & 0x7) void pir_map(int pir, int *chip, int *core, int *thread) { assert(chip && core && thread); if (!strncmp(device_node, "p9", 2)) { *chip = P9_PIR2GCID(pir); *core = P9_PIR2COREID(pir); *thread = P9_PIR2THREADID(pir); } else if (!strncmp(device_node, "p8", 2)) { *chip = P8_PIR2GCID(pir); *core = P8_PIR2COREID(pir); *thread = P8_PIR2THREADID(pir); } else assert(0); } #define PPC_OPTS "l:" #else int get_pir(int linux_cpu) { return -1; } void pir_map(int pir, int *chip, int *core, int *thread) {} #define PPC_OPTS #endif static bool parse_options(int argc, char *argv[]) { int c; bool opt_error = false; int p_list[MAX_PROCESSORS]; int c_list[MAX_CHIPS]; int t_list[MAX_THREADS]; int l_list[MAX_LINUX_CPUS]; int p_count = 0, c_count = 0, t_count = 0, l_count = 0; int i, j, k; struct option long_opts[] = { {"all", no_argument, NULL, 'a'}, {"backend", required_argument, NULL, 'b'}, {"chip", required_argument, NULL, 'c'}, {"device", required_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"processor", required_argument, NULL, 'p'}, {"slave-address", required_argument, NULL, 's'}, {"thread", required_argument, NULL, 't'}, #ifdef TARGET_PPC {"cpu", required_argument, NULL, 'l'}, #endif {"debug", required_argument, NULL, 'D'}, {"shutup", no_argument, NULL, 'S'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; char *endptr; memset(p_list, 0, sizeof(p_list)); memset(c_list, 0, sizeof(c_list)); memset(t_list, 0, sizeof(t_list)); memset(l_list, 0, sizeof(l_list)); do { c = getopt_long(argc, argv, "+ab:c:d:hp:s:t:D:SV" PPC_OPTS, long_opts, NULL); if (c == -1) break; switch(c) { case 'a': if (p_count == 0) { p_count = MAX_PROCESSORS; for (i = 0; i < MAX_PROCESSORS; i++) p_list[i] = 1; } if (c_count == 0) { c_count = MAX_CHIPS; for (i = 0; i < MAX_CHIPS; i++) c_list[i] = 1; } if (t_count == 0) { t_count = MAX_THREADS; for (i = 0; i < MAX_THREADS; i++) t_list[i] = 1; } break; case 'p': if (!parse_list(optarg, MAX_PROCESSORS, p_list, &p_count)) { fprintf(stderr, "Failed to parse '-p %s'\n", optarg); opt_error = true; } break; case 'c': if (!parse_list(optarg, MAX_CHIPS, c_list, &c_count)) { fprintf(stderr, "Failed to parse '-c %s'\n", optarg); opt_error = true; } break; case 't': if (!parse_list(optarg, MAX_THREADS, t_list, &t_count)) { fprintf(stderr, "Failed to parse '-t %s'\n", optarg); opt_error = true; } break; case 'l': if (!parse_list(optarg, MAX_LINUX_CPUS, l_list, &l_count)) { fprintf(stderr, "Failed to parse '-l %s'\n", optarg); opt_error = true; } break; case 'b': if (strcmp(optarg, "fsi") == 0) { backend = FSI; } else if (strcmp(optarg, "i2c") == 0) { backend = I2C; } else if (strcmp(optarg, "kernel") == 0) { backend = KERNEL; /* TODO: use device node to point at a slave * other than the first? */ } else if (strcmp(optarg, "fake") == 0) { backend = FAKE; } else if (strcmp(optarg, "host") == 0) { backend = HOST; } else { fprintf(stderr, "Invalid backend '%s'\n", optarg); print_backends(stderr); opt_error = true; } break; case 'd': device_node = optarg; break; case 's': errno = 0; i2c_addr = strtoull(optarg, &endptr, 0); opt_error = (errno || *endptr != '\0'); if (opt_error) fprintf(stderr, "Invalid slave address '%s'\n", optarg); break; case 'S': progress_shutup(); break; case 'D': pdbg_set_loglevel(atoi(optarg)); break; case 'V': printf("%s (commit %s)\n", PACKAGE_STRING, GIT_SHA1); exit(0); break; case '?': case 'h': opt_error = true; print_usage(basename(argv[0])); break; } } while (c != EOF && !opt_error); if (opt_error) { return false; } if ((c_count > 0 || t_count > 0 || p_count > 0) && (l_count > 0)) { fprintf(stderr, "Can't mix -l with -p/-c/-t/-a\n"); return false; } if ((c_count > 0 || t_count > 0) && p_count == 0) { fprintf(stderr, "No processor(s) selected\n"); fprintf(stderr, "Use -p or -a to select processor(s)\n"); return false; } if (t_count > 0 && c_count == 0) { fprintf(stderr, "No chip(s) selected\n"); fprintf(stderr, "Use -c or -a to select chip(s)\n"); return false; } for (i = 0; i < MAX_PROCESSORS; i++) { if (p_list[i] == 0) continue; processorsel[i] = &chipsel[i][0]; for (j = 0; j < MAX_CHIPS; j++) { if (c_list[j] == 0) continue; chipsel[i][j] = &threadsel[i][j][0]; for (k = 0; k < MAX_THREADS; k++) { if (t_list[k] == 0) continue; threadsel[i][j][k] = 1; } } } if (l_count) { int pir = -1, i, chip, core, thread; for (i = 0; i < MAX_LINUX_CPUS; i++) { if (l_list[i] == 1) { pir = get_pir(i); if (pir < 0) return true; pir_map(pir, &chip, &core, &thread); processorsel[chip] = &chipsel[chip][0]; chipsel[chip][core] = &threadsel[chip][core][0]; threadsel[chip][core][thread] = 1; } } } return true; } void target_select(struct pdbg_target *target) { /* We abuse the private data pointer atm to indicate the target is * selected */ pdbg_target_priv_set(target, (void *) 1); } void target_unselect(struct pdbg_target *target) { pdbg_target_priv_set(target, NULL); } bool target_selected(struct pdbg_target *target) { return (bool) pdbg_target_priv(target); } /* Returns the sum of return codes. This can be used to count how many targets the callback was run on. */ int for_each_child_target(char *class, struct pdbg_target *parent, int (*cb)(struct pdbg_target *, uint32_t, uint64_t *, uint64_t *), uint64_t *arg1, uint64_t *arg2) { int rc = 0; struct pdbg_target *target; uint32_t index; enum pdbg_target_status status; pdbg_for_each_target(class, parent, target) { if (!target_selected(target)) continue; index = pdbg_target_index(target); assert(index != -1); pdbg_target_probe(target); status = pdbg_target_status(target); if (status != PDBG_TARGET_ENABLED) continue; rc += cb(target, index, arg1, arg2); } return rc; } int for_each_target(char *class, int (*cb)(struct pdbg_target *, uint32_t, uint64_t *, uint64_t *), uint64_t *arg1, uint64_t *arg2) { struct pdbg_target *target; uint32_t index; enum pdbg_target_status status; int rc = 0; pdbg_for_each_class_target(class, target) { if (!target_selected(target)) continue; index = pdbg_target_index(target); assert(index != -1); pdbg_target_probe(target); status = pdbg_target_status(target); if (status != PDBG_TARGET_ENABLED) continue; rc += cb(target, index, arg1, arg2); } return rc; } void for_each_target_release(char *class) { struct pdbg_target *target; pdbg_for_each_class_target(class, target) { if (!target_selected(target)) continue; pdbg_target_release(target); } } static int target_selection(void) { struct pdbg_target *fsi, *pib, *chip, *thread; switch (backend) { #ifdef TARGET_ARM case I2C: pdbg_targets_init(&_binary_p8_i2c_dtb_o_start); break; case FSI: if (device_node == NULL) { PR_ERROR("FSI backend requires a device type\n"); return -1; } if (!strcmp(device_node, "p8")) pdbg_targets_init(&_binary_p8_fsi_dtb_o_start); else if (!strcmp(device_node, "p9w")) pdbg_targets_init(&_binary_p9w_fsi_dtb_o_start); else if (!strcmp(device_node, "p9r")) pdbg_targets_init(&_binary_p9r_fsi_dtb_o_start); else if (!strcmp(device_node, "p9z")) pdbg_targets_init(&_binary_p9z_fsi_dtb_o_start); else { PR_ERROR("Invalid device type specified\n"); return -1; } break; case KERNEL: if (device_node == NULL) { PR_ERROR("kernel backend requires a device type\n"); return -1; } if (!strcmp(device_node, "p8")) pdbg_targets_init(&_binary_p8_kernel_dtb_o_start); else pdbg_targets_init(&_binary_p9_kernel_dtb_o_start); break; #endif #ifdef TARGET_PPC case HOST: if (device_node == NULL) { PR_ERROR("Host backend requires a device type\n"); return -1; } if (!strcmp(device_node, "p8")) pdbg_targets_init(&_binary_p8_host_dtb_o_start); else if (!strcmp(device_node, "p9")) pdbg_targets_init(&_binary_p9_host_dtb_o_start); else { PR_ERROR("Unsupported device type for host backend\n"); return -1; } break; #endif case FAKE: pdbg_targets_init(&_binary_fake_dtb_o_start); break; default: /* parse_options deals with parsing user input, so it should be * impossible to get here */ assert(0); return -1; } /* At this point we should have a device-tree loaded. We want * to walk the tree and disabled nodes we don't care about * prior to probing. */ pdbg_for_each_class_target("pib", pib) { int proc_index = pdbg_target_index(pib); if (backend == I2C && device_node) pdbg_set_target_property(pib, "bus", device_node, strlen(device_node) + 1); if (processorsel[proc_index]) { target_select(pib); pdbg_for_each_target("core", pib, chip) { int chip_index = pdbg_target_index(chip); if (pdbg_parent_index(chip, "pib") != proc_index) continue; if (chipsel[proc_index][chip_index]) { target_select(chip); pdbg_for_each_target("thread", chip, thread) { int thread_index = pdbg_target_index(thread); if (threadsel[proc_index][chip_index][thread_index]) target_select(thread); else target_unselect(thread); } } else target_unselect(chip); } /* This is kinda broken as we're overloading what '-c' * means - it's now up to each command to select targets * based on core/chiplet. We really need a better * solution to target selection. */ pdbg_for_each_target("chiplet", pib, chip) { int chip_index = pdbg_target_index(chip); if (chipsel[proc_index][chip_index]) { target_select(chip); } else target_unselect(chip); } } else target_unselect(pib); } pdbg_for_each_class_target("fsi", fsi) { int index = pdbg_target_index(fsi); if (processorsel[index]) target_select(fsi); else target_unselect(fsi); } return 0; } void print_target(struct pdbg_target *target, int level) { int i; struct pdbg_target *next; enum pdbg_target_status status; pdbg_target_probe(target); /* Does this target actually exist? */ status = pdbg_target_status(target); if (status != PDBG_TARGET_ENABLED) return; for (i = 0; i < level; i++) printf(" "); if (target) { char c = 0; if (!pdbg_target_class_name(target)) return; if (!strcmp(pdbg_target_class_name(target), "pib")) c = 'p'; else if (!strcmp(pdbg_target_class_name(target), "core")) c = 'c'; else if (!strcmp(pdbg_target_class_name(target), "thread")) c = 't'; if (c) printf("%c%d: %s\n", c, pdbg_target_index(target), pdbg_target_name(target)); else printf("%s\n", pdbg_target_name(target)); } pdbg_for_each_child_target(target, next) { if (!target_selected(next)) continue; print_target(next, level + 1); } } static int probe(void) { struct pdbg_target *target; pdbg_for_each_class_target("pib", target) { if (!target_selected(target)) continue; print_target(target, 0); } printf("\nNote that only selected targets will be shown above. If none are shown\n" "try adding '-a' to select all targets\n"); return 1; } OPTCMD_DEFINE_CMD(probe, probe); /* * Release handler. */ static void atexit_release(void) { pdbg_target_release(dt_root); } int main(int argc, char *argv[]) { int i, rc = 0; void **args, **flags; optcmd_cmd_t *cmd; backend = default_backend(); if (!parse_options(argc, argv)) return 1; if (optind >= argc) { print_usage(basename(argv[0])); return 1; } if (!device_node) device_node = default_target(backend); /* Disable unselected targets */ if (target_selection()) return 1; atexit(atexit_release); for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!strcmp(argv[optind], cmds[i]->cmd)) { /* Found our command */ cmd = optcmd_parse(cmds[i], (const char **) &argv[optind + 1], argc - (optind + 1), &args, &flags); if (cmd) { rc = cmd(args, flags); goto found_action; } else { /* Error parsing arguments so exit return directly */ return 1; } } } /* Process subcommands. Currently only 'htm'. * TODO: Move htm command parsing to optcmd once htm clean-up is complete */ if (!strcmp(argv[optind], "htm")) { rc = run_htm(optind, argc, argv); goto found_action; } PR_ERROR("Unsupported command: %s\n", argv[optind]); return 1; found_action: if (rc > 0) return 0; printf("No valid targets found or specified. Try adding -p/-c/-t options to specify a target.\n"); printf("Alternatively run '%s -a probe' to get a list of all valid targets\n", basename(argv[0])); return 1; } pdbg-2.0/src/main.h000066400000000000000000000027411336450571500141700ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 enum backend { FSI, I2C, KERNEL, FAKE, HOST }; static inline bool target_is_disabled(struct pdbg_target *target) { return pdbg_target_status(target) == PDBG_TARGET_DISABLED || pdbg_target_status(target) == PDBG_TARGET_NONEXISTENT; } void target_select(struct pdbg_target *target); void target_unselect(struct pdbg_target *target); bool target_selected(struct pdbg_target *target); /* Returns the sum of return codes. This can be used to count how many targets the callback was run on. */ int for_each_child_target(char *class, struct pdbg_target *parent, int (*cb)(struct pdbg_target *, uint32_t, uint64_t *, uint64_t *), uint64_t *arg1, uint64_t *arg2); int for_each_target(char *class, int (*cb)(struct pdbg_target *, uint32_t, uint64_t *, uint64_t *), uint64_t *arg1, uint64_t *arg2); void for_each_target_release(char *class); pdbg-2.0/src/mem.c000066400000000000000000000052541336450571500140170ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #include "progress.h" #include "optcmd.h" #include "parsers.h" #define PR_ERROR(x, args...) \ pdbg_log(PDBG_ERROR, x, ##args) #define PUTMEM_BUF_SIZE 1024 struct mem_flags { bool ci; }; #define MEM_CI_FLAG ("--ci", ci, parse_flag_noarg, false) static int getmem(uint64_t addr, uint64_t size, struct mem_flags flags) { struct pdbg_target *target; uint8_t *buf; int rc = 0; if (size == 0) { PR_ERROR("Size must be > 0\n"); return 1; } buf = malloc(size); assert(buf); pdbg_for_each_class_target("adu", target) { if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED) continue; pdbg_set_progress_tick(progress_tick); progress_init(); if (!__adu_getmem(target, addr, buf, size, flags.ci)) { if (write(STDOUT_FILENO, buf, size) < 0) PR_ERROR("Unable to write stdout.\n"); else rc++; } else PR_ERROR("Unable to read memory.\n"); /* We only ever care about getting memory from a single processor */ progress_end(); break; } free(buf); return rc; } OPTCMD_DEFINE_CMD_WITH_FLAGS(getmem, getmem, (ADDRESS, DATA), mem_flags, (MEM_CI_FLAG)); static int putmem(uint64_t addr, struct mem_flags flags) { uint8_t *buf; int read_size, rc = 0; struct pdbg_target *adu_target; pdbg_for_each_class_target("adu", adu_target) break; if (pdbg_target_probe(adu_target) != PDBG_TARGET_ENABLED) return 0; buf = malloc(PUTMEM_BUF_SIZE); assert(buf); pdbg_set_progress_tick(progress_tick); progress_init(); do { read_size = read(STDIN_FILENO, buf, PUTMEM_BUF_SIZE); if (read_size <= 0) break; if (__adu_putmem(adu_target, addr, buf, read_size, flags.ci)) { rc = 0; printf("Unable to write memory.\n"); break; } rc += read_size; } while (read_size > 0); progress_end(); printf("Wrote %d bytes starting at 0x%016" PRIx64 "\n", rc, addr); free(buf); return rc; } OPTCMD_DEFINE_CMD_WITH_FLAGS(putmem, putmem, (ADDRESS), mem_flags, (MEM_CI_FLAG)); pdbg-2.0/src/optcmd.c000066400000000000000000000061541336450571500145270ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "optcmd.h" /* Parse a flag of the form "--long-flag=" or "-F " */ static int optcmd_parse_flag(const char *argv, struct optcmd_flag *flags, int flag_count, void **flag_results) { int i; char *flag, *arg; flag = strdup(argv); arg = strchr(flag, '='); if (arg) { *arg = '\0'; arg++; } for (i = 0; i < flag_count; i++) { if (!strcmp(flag, flags[i].name)) { flag_results[i] = flags[i].arg(arg); if (!flag_results[i]) { printf("Unable to parse argument for %s\n", flag); return 1; } return 0; } } printf("Invalid flag %s specified\n", flag); return 1; } /* Parse command arguments and flags */ optcmd_cmd_t *optcmd_parse(struct optcmd_cmd *cmd, const char *argv[], int argc, void **arg_results[], void **flag_results[]) { int i, arg_count = 0, total_arg_count, total_flag_count; struct optcmd_arg *args = cmd->args; struct optcmd_flag *flags = cmd->flags; void **tmp_arg_results, **tmp_flag_results; /* Allocate space for parser results */ for (total_arg_count = 0; args[total_arg_count].parser && total_arg_count < OPTCMD_MAX_ARGS; total_arg_count++) {} for (total_flag_count = 0; flags[total_flag_count].arg && total_flag_count < OPTCMD_MAX_FLAGS; total_flag_count++) {} tmp_arg_results = malloc(total_arg_count*sizeof(void *)); assert(tmp_arg_results); tmp_flag_results = calloc(1, total_flag_count*sizeof(void *)); assert(tmp_flag_results); for (i = 0; i < argc; i++, argv++) { if (!strncmp(*argv, "--", 2)) { if (optcmd_parse_flag(*argv, flags, total_flag_count, tmp_flag_results)) /* Unable to parse flag */ return NULL; } else { if (arg_count >= total_arg_count) { printf("Too many arguments passed to %s\n", cmd->cmd); return NULL; } tmp_arg_results[arg_count] = args[arg_count].parser(*argv); if (!tmp_arg_results[arg_count]) { printf("Unable to parse argument %s\n", *argv); return NULL; } arg_count++; } } for (arg_count = arg_count; arg_count < total_arg_count; arg_count++) { /* Process default positional arguments */ struct optcmd_arg *arg = &args[arg_count]; if (!arg->def) { printf("Not enough arguments passed to %s\n", cmd->cmd); return NULL; } tmp_arg_results[arg_count] = arg->parser(arg->def); if (!tmp_arg_results[arg_count]) { printf("Programming error - unable to parse default argument %s\n", arg->def); return NULL; } } *arg_results = tmp_arg_results; *flag_results = tmp_flag_results; return cmd->cmdp; } pdbg-2.0/src/optcmd.h000066400000000000000000000170441336450571500145340ustar00rootroot00000000000000/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 __OPTCMD_H #define __OPTCMD_H #include "ccan/check_type/check_type.h" #include "ccan/build_assert/build_assert.h" #include "ccan/cppmagic/cppmagic.h" #include "config.h" #include "parsers.h" #ifndef OPTCMD_MAX_ARGS #define OPTCMD_MAX_ARGS 10 #endif #ifndef OPTCMD_MAX_FLAGS #define OPTCMD_MAX_FLAGS 10 #endif /* * The idea of this module is to assist with easily calling C functions from the * command line without requiring shim/boilerplate functions to marshal * arguments and flags correctly which can be repetitive and error prone. * * In general a command consists of positional arguments and non-positional * flags which optionally take arguments as shown below: * * `cmd_name ... --flag1= --flag2 ... --flagM` * * It supports default values for the last N postional arguments which allows * trailing arguments to be made optional. The above definition allows a * function with the following prototype to be called directly: * * `cmd_name(arg1, arg2, ... argN, flags)` * * Where `flags` is a struct defined elsewhere containing fields which will * match with the flags specified above. * * TODO: * - Add support for short flags (ie. `-f `) * * - Add a function to free memory. The parsers allocate memory but it's never * freed. Not a big issue though as the program tends to execute one command * and exit. */ typedef void * (optcmd_parser_t)(const char *argv); typedef int (optcmd_cmd_t)(void *[], void *[]); /* * The below data structures are used internally to specify commands along with * any arguments and flags. They could be filled out without any of the macro * magic further down, however internally the parser results are all cast to * (void *) which could result in weird errors if a function expecting a * argument of type char is called with an int. * * Using the macros should guard against these types of errors by ensuring type * mismatch errors will result in compiler warnings or errors (although sadly * these tend to be rather noisy). */ struct optcmd_flag { const char *name; optcmd_parser_t *arg; }; struct optcmd_arg { optcmd_parser_t *parser; const char *def; }; struct optcmd_cmd { const char *cmd; optcmd_cmd_t *cmdp; struct optcmd_arg args[OPTCMD_MAX_ARGS]; struct optcmd_flag flags[OPTCMD_MAX_FLAGS]; }; /* Casts the given parser to a generic parser returning void * and taking a * char * argument. */ #define OPTCMD_PARSER_CAST(parser_) ((optcmd_parser_t *) parser_) /* Returns the return type definition of the given parser */ #define OPTCMD_TYPEOF_PARSER(parser_, ...) typeof(*parser_("")) /* Cast a postional argument to the right type */ #define OPTCMD_CAST_ARG(cnt_, parser_) *(OPTCMD_TYPEOF_PARSER parser_ *) args[cnt_] /* Returns a positional argument struct */ #define _OPTCMD_ARG(parser_, default_) { .parser = OPTCMD_PARSER_CAST(parser_), .def = default_ } #define OPTCMD_ARG(pos_) _OPTCMD_ARG pos_ /* Returns the type definition for a postional argument */ #define _OPTCMD_ARG_DEF(parser_, ...) OPTCMD_TYPEOF_PARSER(parser_) #define OPTCMD_ARG_DEF(pos_) _OPTCMD_ARG_DEF pos_ /* Associate a specific flag name with a parser */ #define _OPTCMD_FLAG(flag_, field_, parser_, ...) \ { .name = flag_, .arg = OPTCMD_PARSER_CAST(parser_) } #define OPTCMD_FLAG(flag_) _OPTCMD_FLAG flag_ /* Cast parser output to a specific flag field */ #define _OPTCMD_CAST_FLAGS(flag_, field_, parser_, default_) \ BUILD_ASSERT(!check_types_match(flag.field_, OPTCMD_TYPEOF_PARSER(parser_))); \ flag.field_ = *flags ? *(OPTCMD_TYPEOF_PARSER(parser_) *) *flags : default_; \ flags++; #define OPTCMD_CAST_FLAGS(flags_) _OPTCMD_CAST_FLAGS flags_ /* * Defines a new command with arguments and flags. * @cmd_name - name of command used on the command line * @cmd_func - pointer to the function to call for this command * @pos - list of positional arguments in the form ((parser, , "help text"), ...) * @flag - name of flag struct to pass @cmd_func as the last parameter * @flags - list of flags in the form (("--flag-name", , parser, , "help text"), ...) */ #define OPTCMD_DEFINE_CMD_WITH_FLAGS(cmd_name_, cmd_func_, pos_, flag_, flags_) \ int cmd_func_(CPPMAGIC_MAP(OPTCMD_ARG_DEF, CPPMAGIC_EVAL pos_), struct flag_); \ int __##cmd_func_(void *args[], void *flags[]) \ { \ struct flag_ flag; \ CPPMAGIC_JOIN(, CPPMAGIC_MAP(OPTCMD_CAST_FLAGS, CPPMAGIC_EVAL flags_)) \ return cmd_func_(CPPMAGIC_MAP_CNT(OPTCMD_CAST_ARG, CPPMAGIC_EVAL pos_), flag); \ } \ \ struct optcmd_cmd optcmd_##cmd_name_ = { \ .cmd = #cmd_name_, \ .cmdp = __##cmd_func_, \ .args = { CPPMAGIC_MAP(OPTCMD_ARG, CPPMAGIC_EVAL pos_), {NULL} }, \ .flags = { CPPMAGIC_MAP(OPTCMD_FLAG, CPPMAGIC_EVAL flags_), {NULL} }, \ } /* * Defines a new command taking only flags and no positional arguments. * @cmd_name - name of command used on the command line * @cmd_func - pointer to the function to call for this command * @flag - name of flag struct to pass @cmd_func as the last parameter * @flags - list of flags in the form (("--flag-name", , parser, , "help text"), ...) */ #define OPTCMD_DEFINE_CMD_ONLY_FLAGS(cmd_name_, cmd_func_, flag_, flags_) \ int cmd_func_(struct flag_); \ int __##cmd_func_(void *args[], void *flags[]) \ { \ struct flag_ flag; \ CPPMAGIC_JOIN(, CPPMAGIC_MAP(OPTCMD_CAST_FLAGS, CPPMAGIC_EVAL flags_)) \ return cmd_func_(flag); \ } \ \ struct optcmd_cmd optcmd_##cmd_name_ = { \ .cmd = #cmd_name_, \ .cmdp = __##cmd_func_, \ .flags = { CPPMAGIC_MAP(OPTCMD_FLAG, CPPMAGIC_EVAL flags_), {NULL} }, \ } /* * Defines a new command with arguments. * @cmd_name - name of command used on the command line * @cmd_func - pointer to the function to call for this command * @pos - list of positional arguments in the form ((parser, , "help text"), ...) */ #define OPTCMD_DEFINE_CMD_WITH_ARGS(cmd_name_, cmd_func_, pos_) \ int cmd_func_(CPPMAGIC_MAP(OPTCMD_ARG_DEF, CPPMAGIC_EVAL pos_)); \ int __##cmd_func_(void *args[], void *flags[]) \ { \ return cmd_func_(CPPMAGIC_MAP_CNT(OPTCMD_CAST_ARG, CPPMAGIC_EVAL pos_)); \ } \ \ struct optcmd_cmd optcmd_##cmd_name_ = { \ .cmd = #cmd_name_, \ .cmdp = __##cmd_func_, \ .args = { CPPMAGIC_MAP(OPTCMD_ARG, CPPMAGIC_EVAL pos_), {NULL} }, \ } /* * Defines a new command taking no arguments or flags. * @cmd_name - name of command used on the command line * @cmd_func - pointer to the function to call for this command */ #define OPTCMD_DEFINE_CMD(cmd_name_, cmd_func_) \ int cmd_func_(void); \ int __##cmd_func_(void *args[], void *flags[]) \ { \ return cmd_func_(); \ } \ \ struct optcmd_cmd optcmd_##cmd_name_ = { \ .cmd = #cmd_name_, \ .cmdp = __##cmd_func_, \ } optcmd_cmd_t *optcmd_parse(struct optcmd_cmd *cmd, const char *argv[], int argc, void **args[], void **flags[]); #endif pdbg-2.0/src/options.h000066400000000000000000000016621336450571500147400ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Default backend on this platform */ enum backend default_backend(void); /* Print all possible backends on this platform */ void print_backends(FILE *stream); /* The default (perhaps only) target for this backend */ const char *default_target(enum backend backend); /* Print all possible targets on this platform */ void print_targets(FILE *stream); pdbg-2.0/src/options_arm.c000066400000000000000000000061271336450571500155730ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #define AMI_BMC "/proc/ractrends/Helper/FwInfo" #define OPENFSI_BMC "/sys/bus/platform/devices/gpio-fsi/fsi0/" #define FSI_CFAM_ID "/sys/devices/platform/gpio-fsi/fsi0/slave@00:00/cfam_id" #define CHIP_ID_P8 0xea #define CHIP_ID_P9 0xd1 #define CHIP_ID_P8P 0xd3 enum backend default_backend(void) { int rc; rc = access(AMI_BMC, F_OK); if (rc == 0) /* AMI BMC */ return I2C; rc = access(OPENFSI_BMC, F_OK); if (rc == 0) /* Kernel interface. OpenBMC */ return KERNEL; return FAKE; } void print_backends(FILE *stream) { fprintf(stream, "Valid backends: i2c kernel fsi fake\n"); } void print_targets(FILE *stream) { fprintf(stream, "kernel: No target is necessary\n"); fprintf(stream, "i2c: No target is necessary\n"); fprintf(stream, "fsi: p8 p9w p9r p9z\n"); } static const char *default_kernel_target(void) { FILE *cfam_id_file; /* Try and determine the correct device type */ cfam_id_file = fopen(FSI_CFAM_ID, "r"); if (cfam_id_file) { uint32_t cfam_id = 0; int rc; rc = fscanf(cfam_id_file, "0x%" PRIx32, &cfam_id); if (rc != 1) pdbg_log(PDBG_ERROR, "%s", strerror(errno)); fclose(cfam_id_file); switch((cfam_id >> 4) & 0xff) { case CHIP_ID_P9: return "p9"; break; case CHIP_ID_P8: case CHIP_ID_P8P: return "p8"; break; default: pdbg_log(PDBG_ERROR, "Unknown chip-id detected\n"); pdbg_log(PDBG_ERROR, "You will need to specify a host type with -d \n"); return NULL; } } else { /* The support for POWER8 included the cfam_id * so if it doesn't exist assume we must be on * P9 */ pdbg_log(PDBG_WARNING, "Unable to determine host type, defaulting to p9\n"); return "p9"; } return NULL; } static const char *default_fsi_target(void) { FILE *dt_compatible; char line[256]; char *p; dt_compatible = fopen("/proc/device-tree/compatible", "r"); if (!dt_compatible) return NULL; p = fgets(line, sizeof(line), dt_compatible); fclose(dt_compatible); if (!p) /* Uh oh*/ return NULL; if (strstr(line, "witherspoon")) return "p9w"; if (strstr(line, "romulus")) return "p9r"; if (strstr(line, "zaius")) return "p9z"; if (strstr(line, "palmetto")) return "p8"; return NULL; } const char *default_target(enum backend backend) { switch(backend) { case I2C: return NULL; break; case KERNEL: return default_kernel_target(); break; case FSI: return default_fsi_target(); break; default: return NULL; break; } return NULL; } pdbg-2.0/src/options_def.c000066400000000000000000000017031336450571500155450ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" enum backend default_backend(void) { return FAKE; } void print_backends(FILE *stream) { fprintf(stream, "Valid backends: fake\n"); } /* Theres no target for FAKE backend */ const char *default_target(enum backend backend) { return NULL; } void print_targets(FILE *stream) { fprintf(stream, "fake: No target is necessary\n"); } pdbg-2.0/src/options_ppc.c000066400000000000000000000027441336450571500155770ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" static const char p8[] = "p8"; static const char p9[] = "p9"; enum backend default_backend(void) { return HOST; } void print_backends(FILE *stream) { fprintf(stream, "Valid backends: host fake\n"); } const char *default_target(enum backend backend) { const char *pos = NULL; char line[256]; FILE *cpuinfo; cpuinfo = fopen("/proc/cpuinfo", "r"); if (!cpuinfo) return NULL; while ((pos = fgets(line, sizeof(line), cpuinfo))) if (strncmp(line, "cpu", 3) == 0) break; fclose(cpuinfo); if (!pos) /* Got to EOF without a break */ return NULL; pos = strchr(line, ':'); if (!pos) return NULL; if (*(pos + 1) == '\0') return NULL; pos += 2; if (strncmp(pos, "POWER8", 6) == 0) return p8; if (strncmp(pos, "POWER9", 6) == 0) return p9; return NULL; } void print_targets(FILE *stream) { fprintf(stream, "host: p8 p9\n"); } pdbg-2.0/src/parsers.c000066400000000000000000000035211336450571500147130ustar00rootroot00000000000000#include #include #include #include uint64_t *parse_number64(const char *argv) { uint64_t *n = malloc(sizeof(*n)); char *endptr; if (!argv) return NULL; errno = 0; *n = strtoull(argv, &endptr, 0); if (errno || *endptr != '\0') return NULL; return n; } uint32_t *parse_number32(const char *argv) { unsigned long long tmp; uint32_t *n = malloc(sizeof(*n)); char *endptr; if (!argv) return NULL; errno = 0; tmp = strtoul(argv, &endptr, 0); if (errno || *endptr != '\0' || tmp > UINT32_MAX) return NULL; *n = tmp; return n; } uint16_t *parse_number16(const char *argv) { unsigned long long tmp; uint16_t *n = malloc(sizeof(*n)); char *endptr; if (!argv) return NULL; errno = 0; tmp = strtoul(argv, &endptr, 0); if (errno || *endptr != '\0' || tmp > UINT16_MAX) return NULL; *n = tmp; return n; } /* Parse a GPR number, returning an error if it's greater than 32 */ int *parse_gpr(const char *argv) { int *gpr = malloc(sizeof(*gpr)); char *endptr; if (!argv) return NULL; errno = 0; *gpr = strtoul(argv, &endptr, 0); if (errno || *endptr != '\0' || *gpr > 32) return NULL; return gpr; } /* Parse an SPR. Currently only supports SPR by numbers but could be extended to * support names (eg. lr) */ int *parse_spr(const char *argv) { int *spr = malloc(sizeof(*spr)); char *endptr; if (!argv) return NULL; errno = 0; *spr = strtoul(argv, &endptr, 0); if (errno || *endptr != '\0' || *spr > 0x3ff) return NULL; return spr; } /* A special parser that always returns true. Allows for boolean flags which * don't take arguments. Sets the associated field to true if specified, * otherwise sets it to the default value (usually false). */ bool *parse_flag_noarg(const char *argv) { bool *result = malloc(sizeof(*result)); *result = true; return result; } pdbg-2.0/src/parsers.h000066400000000000000000000012021336450571500147120ustar00rootroot00000000000000#ifndef __PARSERS_H #define __PARSERS_H #include #include #define ADDRESS (parse_number64, NULL) #define ADDRESS32 (parse_number32, NULL) #define DATA (parse_number64, NULL) #define DATA32 (parse_number32, NULL) #define DATA16 (parse_number16, NULL) #define DEFAULT_DATA(default) (parse_number64, default) #define GPR (parse_gpr, NULL) #define SPR (parse_spr, NULL) uint64_t *parse_number64(const char *argv); uint32_t *parse_number32(const char *argv); uint16_t *parse_number16(const char *argv); int *parse_gpr(const char *argv); int *parse_spr(const char *argv); bool *parse_flag_noarg(const char *argv); #endif pdbg-2.0/src/progress.c000066400000000000000000000046671336450571500151140ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. */ #include #include #include #include #include #include #include #include #include #include "progress.h" static uint64_t progress_pcent; static uint64_t progress_n_upd; static time_t progress_prevsec; static struct timespec progress_start; static bool shutup; #define PROGRESS_CHARS 50 static void progress_bar(unsigned int percent) { unsigned int i, progress; assert(percent <= 100); if (shutup) return; progress = (percent * PROGRESS_CHARS) / 101; fprintf(stderr, "\r["); for (i = 0; i <= progress; i++) fprintf(stderr, "="); for (; i < PROGRESS_CHARS; i++) fprintf(stderr, " "); fprintf(stderr, "] %u%%", percent); fflush(stderr); } void progress_shutup(void) { shutup = true; } void progress_init(void) { progress_pcent = 0; progress_n_upd = ULONG_MAX; progress_prevsec = ULONG_MAX; progress_bar(0); clock_gettime(CLOCK_MONOTONIC, &progress_start); } void progress_tick(uint64_t cur, uint64_t end) { struct timespec now; unsigned int pcent; double sec; if (shutup) return; pcent = (unsigned int)((cur * 100) / end); if (progress_pcent == pcent && cur < progress_n_upd && cur < end) return; progress_pcent = pcent; clock_gettime(CLOCK_MONOTONIC, &now); progress_bar(pcent); sec = difftime(now.tv_sec, progress_start.tv_sec); if (sec >= 5 && pcent > 0) { uint64_t persec = cur / sec; uint64_t 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) fprintf(stderr, " ETA:%" PRIu64 "s ", rem_sec); else { fprintf(stderr, " ETA:%" PRIu64 ":%02" PRIu64 ":%02" PRIu64 " ", rem_sec / 3600, (rem_sec / 60) % 60, rem_sec % 60); } } fflush(stderr); } void progress_end(void) { if (shutup) return; fprintf(stderr, "\n"); } pdbg-2.0/src/progress.h000066400000000000000000000013061336450571500151040ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. */ #ifndef __PROGRESS_H #define __PROGRESS_H #include void progress_init(void); void progress_tick(uint64_t cur, uint64_t end); void progress_end(void); void progress_shutup(void); #endif /* __PROGRESS_H */ pdbg-2.0/src/reg.c000066400000000000000000000114761336450571500140210ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #include "optcmd.h" #define REG_CR -5 #define REG_XER -4 #define REG_MEM -3 #define REG_MSR -2 #define REG_NIA -1 #define REG_R31 31 static void print_proc_reg(struct pdbg_target *target, uint64_t reg, uint64_t value, int rc) { int proc_index, chip_index, thread_index; thread_index = pdbg_target_index(target); chip_index = pdbg_parent_index(target, "core"); proc_index = pdbg_parent_index(target, "pib"); printf("p%d:c%d:t%d:", proc_index, chip_index, thread_index); if (reg == REG_MSR) printf("msr: "); else if (reg == REG_NIA) printf("nia: "); else if (reg == REG_XER) printf("xer: "); else if (reg == REG_CR) printf("cr: "); else if (reg > REG_R31) printf("spr%03" PRIu64 ": ", reg - REG_R31); else if (reg >= 0 && reg <= 31) printf("gpr%02" PRIu64 ": ", reg); if (rc == 1) { printf("Check threadstatus - not all threads on this chiplet are quiesced\n"); } else if (rc == 2) printf("Thread in incorrect state\n"); else printf("0x%016" PRIx64 "\n", value); } static int putprocreg(struct pdbg_target *target, uint32_t index, uint64_t *reg, uint64_t *value) { int rc; if (*reg == REG_MSR) rc = ram_putmsr(target, *value); else if (*reg == REG_NIA) rc = ram_putnia(target, *value); else if (*reg == REG_XER) rc = ram_putxer(target, *value); else if (*reg == REG_CR) rc = ram_putcr(target, *value); else if (*reg > REG_R31) rc = ram_putspr(target, *reg - REG_R31, *value); else if (*reg >= 0 && *reg <= 31) rc = ram_putgpr(target, *reg, *value); print_proc_reg(target, *reg, *value, rc); return 0; } static int getprocreg(struct pdbg_target *target, uint32_t index, uint64_t *reg, uint64_t *unused) { int rc; uint64_t value; if (*reg == REG_MSR) rc = ram_getmsr(target, &value); else if (*reg == REG_NIA) rc = ram_getnia(target, &value); else if (*reg == REG_XER) rc = ram_getxer(target, &value); else if (*reg == REG_CR) rc = ram_getcr(target, (uint32_t *)&value); else if (*reg > REG_R31) rc = ram_getspr(target, *reg - REG_R31, &value); else if (*reg >= 0 && *reg <= 31) rc = ram_getgpr(target, *reg, &value); print_proc_reg(target, *reg, value, rc); return !rc; } static int getgpr(int gpr) { uint64_t reg = gpr; return for_each_target("thread", getprocreg, ®, NULL); } OPTCMD_DEFINE_CMD_WITH_ARGS(getgpr, getgpr, (GPR)); static int putgpr(int gpr, uint64_t data) { uint64_t reg = gpr; return for_each_target("thread", putprocreg, ®, &data); } OPTCMD_DEFINE_CMD_WITH_ARGS(putgpr, putgpr, (GPR, DATA)); static int getnia(void) { uint64_t reg = REG_NIA; return for_each_target("thread", getprocreg, ®, NULL); } OPTCMD_DEFINE_CMD(getnia, getnia); static int putnia(uint64_t nia) { uint64_t reg = REG_NIA; return for_each_target("thread", putprocreg, ®, &nia); } OPTCMD_DEFINE_CMD_WITH_ARGS(putnia, putnia, (DATA)); static int getspr(int spr) { uint64_t reg = spr + REG_R31; return for_each_target("thread", getprocreg, ®, NULL); } OPTCMD_DEFINE_CMD_WITH_ARGS(getspr, getspr, (SPR)); static int putspr(int spr, uint64_t data) { uint64_t reg = spr + REG_R31; return for_each_target("thread", putprocreg, ®, &data); } OPTCMD_DEFINE_CMD_WITH_ARGS(putspr, putspr, (SPR, DATA)); static int getmsr(void) { uint64_t reg = REG_MSR; return for_each_target("thread", getprocreg, ®, NULL); } OPTCMD_DEFINE_CMD(getmsr, getmsr); static int putmsr(uint64_t data) { uint64_t reg = REG_MSR; return for_each_target("thread", putprocreg, ®, &data); } OPTCMD_DEFINE_CMD_WITH_ARGS(putmsr, putmsr, (DATA)); static int getxer(void) { uint64_t reg = REG_XER; return for_each_target("thread", getprocreg, ®, NULL); } OPTCMD_DEFINE_CMD(getxer, getxer); static int putxer(uint64_t data) { uint64_t reg = REG_XER; uint64_t d = data; return for_each_target("thread", putprocreg, ®, &d); } OPTCMD_DEFINE_CMD_WITH_ARGS(putxer, putxer, (DATA)); static int getcr(void) { uint64_t cr = REG_CR; return for_each_target("thread", getprocreg, &cr, NULL); } OPTCMD_DEFINE_CMD(getcr, getcr); static int putcr(uint32_t data) { uint64_t cr = REG_CR; uint64_t d = data; return for_each_target("thread", putprocreg, &cr, &d); } OPTCMD_DEFINE_CMD_WITH_ARGS(putcr, putcr, (DATA32)); pdbg-2.0/src/ring.c000066400000000000000000000030001336450571500141630ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #include "optcmd.h" static int pdbg_getring(struct pdbg_target *target, uint32_t index, uint64_t *addr, uint64_t *len) { uint32_t *result; int i, words; int ring_len = *len; words = (ring_len + 32 - 1)/32; result = calloc(words, sizeof(*result)); assert(result); getring(target, *addr, ring_len, result); for (i = 0; i < ring_len/32; i++) printf("%08" PRIx32, result[i]); ring_len -= i*32; /* Print out remaining bits */ for (i = 0; i < (ring_len + 4 - 1)/4; i++) printf("%01" PRIx32, (result[words - 1] >> (28 - i*4)) & 0xf); printf("\n"); return 1; } static int _getring(uint64_t ring_addr, uint64_t ring_len) { return for_each_target("chiplet", pdbg_getring, &ring_addr, &ring_len); } OPTCMD_DEFINE_CMD_WITH_ARGS(getring, _getring, (ADDRESS, DATA)); pdbg-2.0/src/scom.c000066400000000000000000000030551336450571500141770ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #include "optcmd.h" static int _getscom(struct pdbg_target *target, uint32_t index, uint64_t *addr, uint64_t *unused) { uint64_t value; if (pib_read(target, *addr, &value)) return 0; printf("p%d:0x%" PRIx64 " = 0x%016" PRIx64 "\n", index, *addr, value); return 1; } int getscom(uint64_t addr) { return for_each_target("pib", _getscom, &addr, NULL); } OPTCMD_DEFINE_CMD_WITH_ARGS(getscom, getscom, (ADDRESS)); static int _putscom(struct pdbg_target *target, uint32_t index, uint64_t *addr, uint64_t *data) { if (pib_write(target, *addr, *data)) return 0; return 1; } int putscom(uint64_t addr, uint64_t data, uint64_t mask) { /* TODO: Restore the functionality */ return for_each_target("pib", _putscom, &addr, &data); } OPTCMD_DEFINE_CMD_WITH_ARGS(putscom, putscom, (ADDRESS, DATA, DEFAULT_DATA("0xffffffffffffffff"))); pdbg-2.0/src/tests/000077500000000000000000000000001336450571500142315ustar00rootroot00000000000000pdbg-2.0/src/tests/libpdbg_probe_test.c000066400000000000000000000144461336450571500202370ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "fake.dt.h" static void for_each_target(struct pdbg_target *parent, void (*callback)(struct pdbg_target *target, enum pdbg_target_status status), enum pdbg_target_status status) { struct pdbg_target *child; assert(parent); callback(parent, status); pdbg_for_each_child_target(parent, child) { for_each_target(child, callback, status); } } static void for_target_to_root(struct pdbg_target *target, void (*callback)(struct pdbg_target *target, enum pdbg_target_status status), enum pdbg_target_status status) { assert(target); do { callback(target, status); target = pdbg_target_parent(NULL, target); } while (target != NULL); } static void check_status(struct pdbg_target *target, enum pdbg_target_status status) { struct pdbg_target *root; root = pdbg_target_root(); if (target == root) { if (pdbg_target_status(target) != status) { printf("root node: status=%u, expected=%u\n", pdbg_target_status(target), status); assert(pdbg_target_status(target) == status); } return; } assert(pdbg_target_status(target) == status); } static void test1(void) { struct pdbg_target *root, *target; pdbg_targets_init(&_binary_fake_dtb_o_start); root = pdbg_target_root(); assert(root); for_each_target(root, check_status, PDBG_TARGET_UNKNOWN); pdbg_target_probe_all(root); for_each_target(root, check_status, PDBG_TARGET_ENABLED); pdbg_for_each_class_target("core", target) { pdbg_target_release(target); } pdbg_for_each_class_target("fsi", target) { check_status(target, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("pib", target) { check_status(target, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("core", target) { for_each_target(target, check_status, PDBG_TARGET_RELEASED); } pdbg_for_each_class_target("pib", target) { pdbg_target_release(target); } pdbg_for_each_class_target("fsi", target) { check_status(target, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("pib", target) { for_each_target(target, check_status, PDBG_TARGET_RELEASED); } pdbg_for_each_class_target("fsi", target) { pdbg_target_release(target); } pdbg_for_each_class_target("fsi", target) { for_each_target(target, check_status, PDBG_TARGET_RELEASED); } check_status(root, PDBG_TARGET_ENABLED); } static void test2(void) { struct pdbg_target *root, *target; enum pdbg_target_status status; pdbg_targets_init(&_binary_fake_dtb_o_start); root = pdbg_target_root(); assert(root); for_each_target(root, check_status, PDBG_TARGET_UNKNOWN); pdbg_for_each_class_target("pib", target) { status = pdbg_target_probe(target); assert(status == PDBG_TARGET_ENABLED); for_target_to_root(target, check_status, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("core", target) { for_each_target(target, check_status, PDBG_TARGET_UNKNOWN); } pdbg_for_each_class_target("core", target) { status = pdbg_target_probe(target); assert(status == PDBG_TARGET_ENABLED); for_target_to_root(target, check_status, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("thread", target) { for_each_target(target, check_status, PDBG_TARGET_UNKNOWN); } pdbg_for_each_class_target("core", target) { pdbg_target_probe_all(target); } pdbg_for_each_class_target("thread", target) { for_each_target(target, check_status, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("core", target) { pdbg_target_release(target); } pdbg_for_each_class_target("fsi", target) { check_status(target, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("pib", target) { check_status(target, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("core", target) { check_status(target, PDBG_TARGET_RELEASED); } pdbg_for_each_class_target("thread", target) { check_status(target, PDBG_TARGET_RELEASED); } pdbg_for_each_class_target("fsi", target) { pdbg_target_release(target); } pdbg_for_each_class_target("fsi", target) { for_each_target(target, check_status, PDBG_TARGET_RELEASED); } } static void test3(void) { struct pdbg_target *root, *target; enum pdbg_target_status status; pdbg_targets_init(&_binary_fake_dtb_o_start); root = pdbg_target_root(); assert(root); for_each_target(root, check_status, PDBG_TARGET_UNKNOWN); pdbg_for_each_class_target("core", target) { pdbg_target_status_set(target, PDBG_TARGET_DISABLED); } pdbg_for_each_class_target("thread", target) { check_status(target, PDBG_TARGET_UNKNOWN); } pdbg_for_each_class_target("thread", target) { status = pdbg_target_probe(target); assert(status == PDBG_TARGET_UNKNOWN); check_status(target, PDBG_TARGET_UNKNOWN); } pdbg_target_probe_all(root); pdbg_for_each_class_target("core", target) { check_status(target, PDBG_TARGET_DISABLED); } pdbg_for_each_class_target("thread", target) { check_status(target, PDBG_TARGET_UNKNOWN); } pdbg_for_each_class_target("pib", target) { pdbg_target_release(target); } pdbg_for_each_class_target("fsi", target) { for_target_to_root(target, check_status, PDBG_TARGET_ENABLED); } pdbg_for_each_class_target("pib", target) { check_status(target, PDBG_TARGET_RELEASED); } pdbg_target_release(root); pdbg_for_each_class_target("pib", target) { for_target_to_root(target, check_status, PDBG_TARGET_RELEASED); } pdbg_for_each_class_target("core", target) { check_status(target, PDBG_TARGET_DISABLED); } pdbg_for_each_class_target("thread", target) { check_status(target, PDBG_TARGET_UNKNOWN); } } int main(void) { int test_id = TEST_ID; if (test_id == 1) { test1(); } else if (test_id == 2) { test2(); } else if (test_id == 3) { test3(); } else { printf("No test for TEST_ID=%d\n", test_id); return 1; } return 0; } pdbg-2.0/src/tests/libpdbg_target_test.c000066400000000000000000000131061336450571500204060ustar00rootroot00000000000000/* Copyright 2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "fake.dt.h" static int count_target(struct pdbg_target *parent, const char *classname) { struct pdbg_target *target; int n = 0; pdbg_for_each_target(classname, parent, target) { n++; } return n; } static int count_class_target(const char *classname) { struct pdbg_target *target; int n = 0; pdbg_for_each_class_target(classname, target) { n++; } return n; } static int count_child_target(struct pdbg_target *parent) { struct pdbg_target *child; int n = 0; pdbg_for_each_child_target(parent, child) { n++; } return n; } int main(void) { struct pdbg_target *root, *target, *parent, *parent2; const char *name; int count; pdbg_targets_init(&_binary_fake_dtb_o_start); root = pdbg_target_root(); assert(root); count = count_class_target("fsi"); assert(count == 1); count = count_class_target("pib"); assert(count == 8); count = count_class_target("core"); assert(count == 32); count = count_class_target("thread"); assert(count == 64); pdbg_for_each_class_target("fsi", target) { parent = pdbg_target_parent("fsi", target); assert(parent == NULL); parent = pdbg_target_parent("pib", target); assert(parent == NULL); parent = pdbg_target_parent("core", target); assert(parent == NULL); parent = pdbg_target_parent("thread", target); assert(parent == NULL); count = count_child_target(target); assert(count == 8); count = count_target(target, "fsi"); assert(count == 1); count = count_target(target, "pib"); assert(count == 8); count = count_target(target, "core"); assert(count == 32); count = count_target(target, "thread"); assert(count == 64); name = pdbg_target_name(target); assert(!strcmp(name, "Fake FSI")); name = pdbg_target_class_name(target); assert(!strcmp(name, "fsi")); name = pdbg_target_dn_name(target); assert(!strncmp(name, "fsi", 3)); } pdbg_for_each_class_target("pib", target) { parent = pdbg_target_parent("fsi", target); assert(parent); parent2 = pdbg_target_require_parent("fsi", target); assert(parent == parent2); parent = pdbg_target_parent("pib", target); assert(parent == NULL); parent = pdbg_target_parent("core", target); assert(parent == NULL); parent = pdbg_target_parent("thread", target); assert(parent == NULL); count = count_child_target(target); assert(count == 4); count = count_target(target, "fsi"); assert(count == 0); count = count_target(target, "pib"); assert(count == 1); count = count_target(target, "core"); assert(count == 4); count = count_target(target, "thread"); assert(count == 8); name = pdbg_target_name(target); assert(!strcmp(name, "Fake PIB")); name = pdbg_target_class_name(target); assert(!strcmp(name, "pib")); name = pdbg_target_dn_name(target); assert(!strncmp(name, "pib", 3)); } pdbg_for_each_class_target("core", target) { parent = pdbg_target_parent("fsi", target); assert(parent); parent2 = pdbg_target_require_parent("fsi", target); assert(parent == parent2); parent = pdbg_target_parent("pib", target); assert(parent); parent2 = pdbg_target_require_parent("pib", target); assert(parent == parent2); parent = pdbg_target_parent("core", target); assert(parent == NULL); parent = pdbg_target_parent("thread", target); assert(parent == NULL); count = count_child_target(target); assert(count == 2); count = count_target(target, "fsi"); assert(count == 0); count = count_target(target, "pib"); assert(count == 0); count = count_target(target, "core"); assert(count == 1); count = count_target(target, "thread"); assert(count == 2); name = pdbg_target_name(target); assert(!strcmp(name, "Fake Core")); name = pdbg_target_class_name(target); assert(!strcmp(name, "core")); name = pdbg_target_dn_name(target); assert(!strncmp(name, "core", 4)); } pdbg_for_each_class_target("thread", target) { parent = pdbg_target_parent("fsi", target); assert(parent); parent2 = pdbg_target_require_parent("fsi", target); assert(parent == parent2); parent = pdbg_target_parent("pib", target); assert(parent); parent2 = pdbg_target_require_parent("pib", target); assert(parent == parent2); parent = pdbg_target_parent("core", target); assert(parent); parent2 = pdbg_target_require_parent("core", target); assert(parent == parent2); parent = pdbg_target_parent("thread", target); assert(parent == NULL); count = count_child_target(target); assert(count == 0); count = count_target(target, "fsi"); assert(count == 0); count = count_target(target, "pib"); assert(count == 0); count = count_target(target, "core"); assert(count == 0); count = count_target(target, "thread"); assert(count == 1); name = pdbg_target_name(target); assert(!strcmp(name, "Fake Thread")); name = pdbg_target_class_name(target); assert(!strcmp(name, "thread")); name = pdbg_target_dn_name(target); assert(!strncmp(name, "thread", 6)); } return 0; } pdbg-2.0/src/tests/optcmd_test.c000066400000000000000000000112531336450571500167240ustar00rootroot00000000000000#include #include #include #include #include #include #include "../optcmd.h" #include "../parsers.h" /* The OPTCMD_TEST_BREAK_BUILD* defines can be used to test that we catch type * mistmatch errors between function/flag definitions and parsers */ #ifdef OPTCMD_TEST_BREAK_BUILD1 struct flags { bool test_bool; int test_num; }; #else struct flags { bool test_bool; uint64_t test_num; }; #endif /* Format: (, , , ) */ #define FLAG_TEST_BOOL ("--test-bool", test_bool, parse_flag_noarg, false) #define FLAG_TEST_NUM ("--test-num", test_num, parse_number64, 10) /* Format: (, ) * * may be NULL if argument must be supplied. */ #define ARG_NUM (parse_number64, NULL) #define ARG_NUM_OPT (parse_number64, "2") static uint64_t num, opt, flag; static bool bool_flag; static int test(void) { bool_flag = true; return 0; } OPTCMD_DEFINE_CMD(test, test); #ifdef OPTCMD_TEST_BREAK_BUILD2 static int test_args(int num_arg, int opt_arg) { return 0; } #else static int test_args(uint64_t num_arg, uint64_t opt_arg) { num = num_arg; opt = opt_arg; return 0; } #endif OPTCMD_DEFINE_CMD_WITH_ARGS(test_args, test_args, (ARG_NUM, ARG_NUM_OPT)); static int test_flags(uint64_t num_arg, uint64_t opt_arg, struct flags flags) { num = num_arg; opt = opt_arg; flag = flags.test_num; bool_flag = flags.test_bool; return 0; } OPTCMD_DEFINE_CMD_WITH_FLAGS(test_flags, test_flags, (ARG_NUM, ARG_NUM_OPT), flags, (FLAG_TEST_BOOL, FLAG_TEST_NUM)); static int test_only_flags(struct flags flags) { flag = flags.test_num; bool_flag = flags.test_bool; return 0; } OPTCMD_DEFINE_CMD_ONLY_FLAGS(test_only_flags, test_only_flags, flags, (FLAG_TEST_BOOL, FLAG_TEST_NUM)); int parse_argv(const char *argv[], int argc) { int i, rc; void **args, **flags; struct optcmd_cmd *cmds[] = { &optcmd_test, &optcmd_test_args, &optcmd_test_flags, &optcmd_test_only_flags }; optcmd_cmd_t *cmd; for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!strcmp(argv[0], cmds[i]->cmd)) { /* Found our command */ cmd = optcmd_parse(cmds[i], &argv[1], argc - 1, &args, &flags); if (cmd) { rc = cmd(args, flags); return rc; } } } return -1; } int main(void) { /* Tests */ const char *test1_argv[] = { "test" }; bool_flag = false; assert(!parse_argv(test1_argv, ARRAY_SIZE(test1_argv))); assert(bool_flag); const char *test2_argv[] = { "test_args", "1" }; assert(!parse_argv(test2_argv, ARRAY_SIZE(test2_argv))); assert(num == 1); assert(opt == 2); const char *test3_argv[] = { "test_args", "2", "3" }; assert(!parse_argv(test3_argv, ARRAY_SIZE(test3_argv))); assert(num == 2); assert(opt == 3); const char *test4_argv[] = { "test_flags", "4", "5" }; assert(!parse_argv(test4_argv, ARRAY_SIZE(test4_argv))); assert(num == 4); assert(opt == 5); assert(flag == 10); bool_flag = false; const char *test5_argv[] = { "test_flags", "5", "6" }; assert(!parse_argv(test5_argv, ARRAY_SIZE(test5_argv))); assert(num == 5); assert(opt == 6); assert(flag == 10); assert(!bool_flag); const char *test6_argv[] = { "test_flags", "7", "8", "--test-num=9" }; assert(!parse_argv(test6_argv, ARRAY_SIZE(test6_argv))); assert(num == 7); assert(opt == 8); assert(flag == 9); assert(!bool_flag); const char *test7_argv[] = { "test_flags", "8", "9", "--test-bool" }; assert(!parse_argv(test7_argv, ARRAY_SIZE(test7_argv))); assert(num == 8); assert(opt == 9); assert(flag == 10); assert(bool_flag); bool_flag = false; const char *test8_argv[] = { "test_flags", "9", "10", "--test-bool", "--test-num=11" }; assert(!parse_argv(test8_argv, ARRAY_SIZE(test8_argv))); assert(num == 9); assert(opt == 10); assert(flag == 11); assert(bool_flag); /* This should fail, too many arguments */ const char *test9_argv[] = { "test_flags", "9", "10", "11", "--test-bool", "--test-num=11" }; assert(parse_argv(test9_argv, ARRAY_SIZE(test9_argv))); /* So should this, unknown flag */ const char *test10_argv[] = { "test_flags", "9", "10", "--test-blah", "--test-num=11" }; assert(parse_argv(test10_argv, ARRAY_SIZE(test10_argv))); const char *test11_argv[] = { "test_only_flags" }; assert(!parse_argv(test11_argv, ARRAY_SIZE(test11_argv))); assert(flag == 10); assert(!bool_flag); const char *test12_argv[] = { "test_only_flags", "--test-num=12" }; assert(!parse_argv(test12_argv, ARRAY_SIZE(test12_argv))); assert(flag == 12); assert(!bool_flag); /* Should fail because we're passing a positional argument */ const char *test13_argv[] = { "test_only_flags", "--test-num=12", "13" }; assert(parse_argv(test13_argv, ARRAY_SIZE(test13_argv))); return 0; } pdbg-2.0/src/thread.c000066400000000000000000000135771336450571500145170ustar00rootroot00000000000000/* Copyright 2017 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "main.h" #include "optcmd.h" static int print_thread_status(struct pdbg_target *target, uint32_t index, uint64_t *arg, uint64_t *valid) { struct thread_state *status = (struct thread_state *) arg; status[index] = thread_status(target); valid[index] = true; return 1; } static int print_core_thread_status(struct pdbg_target *core_target, uint32_t index, uint64_t *maxindex, uint64_t *unused1) { struct thread_state status[8]; uint64_t valid[8] = {0}; int i, rc; printf("c%02d: ", index); /* TODO: This cast is gross. Need to rewrite for_each_child_target as an iterator. */ rc = for_each_child_target("thread", core_target, print_thread_status, (uint64_t *) &status[0], &valid[0]); for (i = 0; i <= *maxindex; i++) { if (!valid[i]) { printf(" "); continue; } if (status[i].active) printf("A"); else printf("."); switch (status[i].sleep_state) { case PDBG_THREAD_STATE_DOZE: printf("D"); break; case PDBG_THREAD_STATE_NAP: printf("N"); break; case PDBG_THREAD_STATE_SLEEP: printf("Z"); break; case PDBG_THREAD_STATE_STOP: printf("S"); break; default: printf("."); break; } if (status[i].quiesced) printf("Q"); else printf("."); printf(" "); } printf("\n"); return rc; } static bool is_real_address(struct thread_regs *regs, uint64_t addr) { return true; if ((addr & 0xf000000000000000ULL) == 0xc000000000000000ULL) return true; return false; } static int load8(struct pdbg_target *target, uint64_t addr, uint64_t *value) { if (adu_getmem(target, addr, (uint8_t *)value, 8)) { pdbg_log(PDBG_ERROR, "Unable to read memory address=%016" PRIx64 ".\n", addr); return 0; } return 1; } static int dump_stack(struct thread_regs *regs) { struct pdbg_target *target; uint64_t sp = regs->gprs[1]; uint64_t pc; pdbg_for_each_class_target("adu", target) { if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED) continue; break; } printf("STACK:\n"); if (!target) pdbg_log(PDBG_ERROR, "Unable to read memory (no ADU found)\n"); if (sp && is_real_address(regs, sp)) { if (!load8(target, sp, &sp)) return 1; while (sp && is_real_address(regs, sp)) { if (!load8(target, sp + 16, &pc)) return 1; printf(" 0x%016" PRIx64 " 0x%16" PRIx64 "\n", sp, pc); if (!load8(target, sp, &sp)) return 1; } } return 0; } static int get_thread_max_index(struct pdbg_target *target, uint32_t index, uint64_t *maxindex, uint64_t *unused) { if (index > *maxindex) *maxindex = index; return 1; } static int get_core_max_threads(struct pdbg_target *core_target, uint32_t index, uint64_t *maxindex, uint64_t *unused1) { return for_each_child_target("thread", core_target, get_thread_max_index, maxindex, NULL); } static int print_proc_thread_status(struct pdbg_target *pib_target, uint32_t index, uint64_t *unused, uint64_t *unused1) { int i; uint64_t maxindex = 0; for_each_child_target("core", pib_target, get_core_max_threads, &maxindex, NULL); printf("\np%01dt:", index); for (i = 0; i <= maxindex; i++) printf(" %d", i); printf("\n"); return for_each_child_target("core", pib_target, print_core_thread_status, &maxindex, NULL); }; static int start_thread(struct pdbg_target *thread_target, uint32_t index, uint64_t *unused, uint64_t *unused1) { return ram_start_thread(thread_target) ? 0 : 1; } static int step_thread(struct pdbg_target *thread_target, uint32_t index, uint64_t *count, uint64_t *unused1) { return ram_step_thread(thread_target, *count) ? 0 : 1; } static int stop_thread(struct pdbg_target *thread_target, uint32_t index, uint64_t *unused, uint64_t *unused1) { return ram_stop_thread(thread_target) ? 0 : 1; } static int sreset_thread(struct pdbg_target *thread_target, uint32_t index, uint64_t *unused, uint64_t *unused1) { return ram_sreset_thread(thread_target) ? 0 : 1; } static int state_thread(struct pdbg_target *thread_target, uint32_t index, uint64_t *i_doBacktrace, uint64_t *unused) { struct thread_regs regs; bool do_backtrace = (bool) i_doBacktrace; if (ram_state_thread(thread_target, ®s)) return 0; if (do_backtrace) dump_stack(®s); return 1; } static int thread_start(void) { return for_each_target("thread", start_thread, NULL, NULL); } OPTCMD_DEFINE_CMD(start, thread_start); static int thread_step(uint64_t count) { return for_each_target("thread", step_thread, &count, NULL); } OPTCMD_DEFINE_CMD_WITH_ARGS(step, thread_step, (DATA)); static int thread_stop(void) { return for_each_target("thread", stop_thread, NULL, NULL); } OPTCMD_DEFINE_CMD(stop, thread_stop); static int thread_status_print(void) { return for_each_target("pib", print_proc_thread_status, NULL, NULL); } OPTCMD_DEFINE_CMD(threadstatus, thread_status_print); static int thread_sreset(void) { return for_each_target("thread", sreset_thread, NULL, NULL); } OPTCMD_DEFINE_CMD(sreset, thread_sreset); struct reg_flags { bool do_backtrace; }; #define REG_BACKTRACE_FLAG ("--backtrace", do_backtrace, parse_flag_noarg, false) static int thread_state(struct reg_flags flags) { int err; err = for_each_target("thread", state_thread, (uint64_t *)flags.do_backtrace, NULL); for_each_target_release("thread"); return err; } OPTCMD_DEFINE_CMD_ONLY_FLAGS(regs, thread_state, reg_flags, (REG_BACKTRACE_FLAG)); pdbg-2.0/template.S000066400000000000000000000015041336450571500142370ustar00rootroot00000000000000// On platforms that define this macro, a symbol called "foo" in C must be // named "_foo" in assembler. GCC Should always define the macro, but // we add a fallback just in case #ifndef __USER_LABEL_PREFIX__ #define __USER_LABEL_PREFIX__ "" #endif #define CONCAT1(a, b, c, d) CONCAT2(a, b, c, d) #define CONCAT2(a, b, c, d) a ## b ## c ## d #define SYM_START(x) CONCAT1(__USER_LABEL_PREFIX__, _binary_, x, _start) #define SYM_END(x) CONCAT1(__USER_LABEL_PREFIX__, _binary_, x, _end) #define SYM_SIZE(x) CONCAT1(__USER_LABEL_PREFIX__, _binary_, x, _size) .section .data SYM_START(SYMBOL_PREFIX): .incbin FILENAME .align 4 SYM_END(SYMBOL_PREFIX): SYM_SIZE(SYMBOL_PREFIX): .long SYM_END(SYMBOL_PREFIX) - SYM_START(SYMBOL_PREFIX) .globl SYM_START(SYMBOL_PREFIX) .globl SYM_END(SYMBOL_PREFIX) .globl SYM_SIZE(SYMBOL_PREFIX) pdbg-2.0/tests/000077500000000000000000000000001336450571500134425ustar00rootroot00000000000000pdbg-2.0/tests/driver.sh000066400000000000000000000160271336450571500152770ustar00rootroot00000000000000#!/bin/sh # # Simple test driver for shell based testsuite # # Each testsuite is a shell script, which sources this driver file. # Following functions can be used to define testsuite and tests. # # test_group # # Define a test suite. This function should be called before calling any # other functions. # # test_setup # # Register a setup function to be executed before running the tests. # # test_cleanup # # Register a cleanup function to be executed after running the tests. # # test_result <--|output> # # Define the exit code and the output for the test. If there is no output # from the commands, then '--' can be specified to denote empty output. # Multi-line output can be added using a here document. Only the output # on stdout is captured. # # test_result_stderr # # Define the output to stderr for the test. If there is no output from # the commands, or if matching of output on stderr is not required, then # this command can be ommitted. Multi-line output can be specified # using a here document. # # test_run [] # # Define the command to execute for the test. # # test_skip # # This can be called before test_run, to skip the test. This is useful to # write tests which are dependent on the environment (e.g. architecture). # # test_wrapper # # To execute commands in a special context, test_wrapper function can be # defined. This function will be passed all the arguments to test_run # command. # # # Matching output: # # To match varying output, define result_filter() to filter the output. # This allows matching date or time stamps. # # # Example: # # test_setup "touch somefile" # test_cleanup "rm -f somefile" # # test_group "my group of tests" # # test_result 0 output1 # test_run command1 arguments1 # # test_result 1 -- # test_result_stderr stderr2 # test_run command2 arguments2 # # test_result 0 output3 # if [ $condition ] ; then # test_skip # fi # test_run command3 arguments3 # set -u TESTDIR=$(dirname "$0") if [ "$TESTDIR" = "." ] ; then TESTDIR=$(cd "$TESTDIR"; pwd) fi SRCDIR=$(dirname "$TESTDIR") PATH="$SRCDIR":$PATH test_name=${TEST_NAME:-$0} test_logfile=${TEST_LOG:-} test_trsfile=${TEST_TRS:-} test_color=${TEST_COLOR:-yes} red= grn= lgn= blu= mgn= std= if [ $test_color = yes ] ; then red='' # Red. grn='' # Green. lgn='' # Light green. blu='' # Blue. mgn='' # Magenta. std='' # No color. fi test_started=0 test_skipped=0 test_defined=0 test_stderr=0 count_total=0 count_skipped=0 count_failed=0 trap 'test_end' 0 test_setup_hooks="" test_setup () { test_setup_hooks="${test_setup_hooks}${test_setup_hooks:+ && }$*" } test_cleanup_hooks="" test_cleanup () { test_cleanup_hooks="${test_cleanup_hooks}${test_cleanup_hooks:+ ; }$*" } test_end () { trap 0 eval $test_cleanup_hooks if [ $count_total -eq 0 ] ; then status=99 elif [ $count_failed -gt 0 ] ; then status=1 elif [ $count_skipped -eq $count_total ] ; then status=77 else status=0 fi exit $status } test_error () { trap 0 echo "$@" exit 99 } test_error_skip () { trap 0 echo "$@" exit 77 } test_log () { if [ -z "$test_logfile" ] ; then echo "$@" else echo "$@" >> "$test_logfile" fi } test_trs () { if [ -n "$test_trsfile" ] ; then echo "$@" >> "$test_trsfile" fi } test_output () { rc=${1:-1} required_rc=${2:-0} output_mismatch=${3:-0} if [ $required_rc -eq 0 ] ; then expect_failure=no else expect_failure=yes fi case $rc:$expect_failure:$output_mismatch in 0:*:1) col=$red res=FAIL ;; 0:yes:*) col=$red res=XPASS ;; 0:*:*) col=$grn res=PASS ;; 77:*:*) col=$blu res=SKIP ;; *:*:1) col=$red res=FAIL ;; *:yes:*) col=$lgn res=XFAIL ;; *:*:*) col=$red res=FAIL ;; esac if [ -n "$test_logfile" ] ; then test_log "${res} ${test_cmd} (exit status: ${rc})" fi test_trs ":test-result: ${res}" if [ -n "$test_trsfile" ] ; then indent=" " else indent="" fi echo "${indent}${col}${res}${std}: ${test_cmd}" count_total=$(( count_total + 1 )) if [ $res = "SKIP" ] ; then count_skipped=$(( count_skipped + 1 )) elif [ $res = "XPASS" -o $res = "FAIL" ] ; then count_failed=$(( count_failed + 1 )) fi } test_wrapper_default () { "$@" } test_wrapper () { test_wrapper_default "$@" } result_filter_default () { cat } result_filter () { result_filter_default } #--------------------------------------------------------------------- # Public functions #--------------------------------------------------------------------- test_group () { test_name=${1:-$test_name} test_started=1 echo "-- $test_name" eval $test_setup_hooks rc=$? if [ $rc -ne 0 ] ; then if [ $rc -eq 77 ] ; then test_error_skip "setup failed, skipping" else test_error "ERROR: setup failed" fi fi } test_result () { if [ $test_started -eq 0 ] ; then test_error "ERROR: missing call to test_group" fi required_rc="${1:-0}" if [ $# -eq 2 ] ; then if [ "$2" = "--" ] ; then required_output="" else required_output="$2" fi else if ! tty -s ; then required_output=$(cat) else required_output="" fi fi test_defined=1 } test_result_stderr () { if [ $test_started -eq 0 ] ; then test_error "ERROR: missing call to test_group" fi if [ $test_defined -eq 0 ] ; then test_error "ERROR: missing call to test_result" fi if [ $# -eq 1 ] ; then required_output_stderr="$1" else if ! tty -s ; then required_output_stderr=$(cat) else required_output_stderr="" fi fi test_stderr=1 } test_skip () { if [ $test_started -eq 0 ] ; then test_error "ERROR: missing call to test_group" fi test_skipped=1 } test_run () { output_mismatch=0 if [ $test_started -eq 0 ] ; then test_error "ERROR: missing call to test_group" fi if [ $test_defined -eq 0 ] ; then test_error "ERROR: missing call to test_result" fi test_cmd="$@" if [ $test_skipped -eq 1 ] ; then test_output 77 test_skipped=0 test_defined=0 return fi stderr_file=$(mktemp) output_raw=$(test_wrapper "$@" 2>"$stderr_file") rc=$? if [ $rc -ne $required_rc ] ; then test_log "expected rc: $required_rc" test_log "output rc: $rc" fi output=$(echo "$output_raw" | result_filter) if [ "$output" != "$required_output" ] ; then test_log "expected:" test_log "$required_output" test_log "output:" test_log "$output" if [ "$output_raw" != "$output" ] ; then test_log "output raw:" test_log "$output_raw" fi output_mismatch=1 fi if [ $test_stderr -eq 1 ] ; then output_stderr_raw=$(cat "$stderr_file") output_stderr=$(cat "$stderr_file" | result_filter) if [ "$output_stderr" != "$required_output_stderr" ] ; then test_log "expected stderr:" test_log "$required_output_stderr" test_log "output stderr:" test_log "$output_stderr" if [ "$output_stderr_raw" != "$output_stderr" ] ; then test_log "output stderr raw:" test_log "$output_stderr_raw" fi output_mismatch=1 fi fi rm -f "$stderr_file" test_output $rc $required_rc $output_mismatch test_skipped=0 test_defined=0 test_stderr=0 } pdbg-2.0/tests/run_test.sh000077500000000000000000000041721336450571500156500ustar00rootroot00000000000000#!/bin/sh # Derived from automake basic testsuite driver set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <> "$TEST_LOG" echo ":global-test-result: $res" >> "$TEST_TRS" echo ":recheck: $recheck" >> "$TEST_TRS" echo ":copy-in-global-log: $gcopy" >> "$TEST_TRS" pdbg-2.0/tests/test_driver.sh000077500000000000000000000020701336450571500163320ustar00rootroot00000000000000#!/bin/sh TEST_COLOR=no TEST_LOG=/dev/null TEST_TRS="" . $(dirname "$0")/driver.sh test_setup "echo test start" test_cleanup "echo test end" test_setup "echo real start" test_cleanup "echo real end" test_group "test driver tests" echo echo "test should PASS" test_result 0 foo test_run echo foo echo "test should FAIL" test_result 0 foo test_run echo bar echo "test should XPASS" test_result 1 foo test_run echo foo echo "test should FAIL" test_result 1 foo test_run echo bar echo "test should XFAIL" test_result 1 -- test_run false echo "test should FAIL" test_result 1 -- test_run echo foo && false echo "test should SKIP" test_result 0 -- test_skip test_run echo foo echo echo_stderr () { echo "$*" >&2 } echo "match stderr" test_result 0 -- test_result_stderr foo test_run echo_stderr foo echo result_filter () { sed -e 's#[0-9][0-9][0-9]#NUM3#g' \ -e 's#[0-9][0-9]#NUM2#g' } test_result 0 NUM2 test_run echo 42 test_result 0 NUM3 test_run echo 666 echo test_wrapper () { echo "output: $*" } test_result 0 "output: foobar" test_run foobar echo pdbg-2.0/tests/test_hw_bmc.sh000077500000000000000000000032341336450571500163010ustar00rootroot00000000000000#!/bin/bash . $(dirname "$0")/driver.sh BMC_TEST=".test.bmc" BMC_HOST= BMC_USER= BMC_PASS= PDBG_ARM_BUILD= PDBG_PATH=/tmp/pdbg PDBG=${PDBG_PATH}/pdbg load_config () { if [ ! -f "$BMC_TEST" ] ; then echo "Missing file $BMC_TEST, skipping tests" return 77 fi fail=0 . "$BMC_TEST" for var in "BMC_HOST" "BMC_USER" "BMC_PASS" "PDBG_ARM_BUILD"; do eval value="\$$var" if [ -z "$value" ] ; then echo "$var not defined in $BMC_TEST" fail=1 fi done return $fail } copy_pdbg () { sshpass -p "$BMC_PASS" \ rsync -Pav "${PDBG_ARM_BUILD}/.libs/"* \ ${BMC_USER}@${BMC_HOST}:${PDBG_PATH} } test_wrapper () { sshpass -p "$BMC_PASS" \ ssh ${BMC_USER}@${BMC_HOST} \ LD_LIBRARY_PATH="${PDBG_PATH}" \ "$@" } test_setup load_config test_setup copy_pdbg test_group "BMC HW tests" hw_state=0 do_skip () { if [ $hw_state -ne 1 ] ; then test_skip fi } echo -n "Checking if the host is up... " output=$(test_wrapper /usr/sbin/obmcutil state | grep CurrentHostState) rc=$? if [ $rc -ne 0 ] || \ [ "$output" != "xyz.openbmc_project.State.Host.HostState.Running" ] ; then echo "yes" hw_state=1 else echo "no" fi result_filter () { sed -E -e 's#0x[[:xdigit:]]{16}#HEX16#' \ -E -e 's#0x[[:xdigit:]]{8}#HEX8#' } test_result 0 </dev/null) do_skip () { if [ "$arch" != "x86_64" ] ; then test_skip fi } test_result 0 <