diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt new file mode 100644 index 0000000000..bac4afa3b1 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt @@ -0,0 +1,136 @@ +*** Reserved memory regions *** + +Reserved memory is specified as a node under the /reserved-memory node. +The operating system shall exclude reserved memory from normal usage +one can create child nodes describing particular reserved (excluded from +normal use) memory regions. Such memory regions are usually designed for +the special usage by various device drivers. + +Parameters for each memory region can be encoded into the device tree +with the following nodes: + +/reserved-memory node +--------------------- +#address-cells, #size-cells (required) - standard definition + - Should use the same values as the root node +ranges (required) - standard definition + - Should be empty + +/reserved-memory/ child nodes +----------------------------- +Each child of the reserved-memory node specifies one or more regions of +reserved memory. Each child node may either use a 'reg' property to +specify a specific range of reserved memory, or a 'size' property with +optional constraints to request a dynamically allocated block of memory. + +Following the generic-names recommended practice, node names should +reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit +address (@
) should be appended to the name if the node is a +static allocation. + +Properties: +Requires either a) or b) below. +a) static allocation + reg (required) - standard definition +b) dynamic allocation + size (required) - length based on parent's #size-cells + - Size in bytes of memory to reserve. + alignment (optional) - length based on parent's #size-cells + - Address boundary for alignment of allocation. + alloc-ranges (optional) - prop-encoded-array (address, length pairs). + - Specifies regions of memory that are + acceptable to allocate from. + +If both reg and size are present, then the reg property takes precedence +and size is ignored. + +Additional properties: +compatible (optional) - standard definition + - may contain the following strings: + - shared-dma-pool: This indicates a region of memory meant to be + used as a shared pool of DMA buffers for a set of devices. It can + be used by an operating system to instantiate the necessary pool + management subsystem if necessary. + - vendor specific string in the form ,[-] +no-map (optional) - empty property + - Indicates the operating system must not create a virtual mapping + of the region as part of its standard mapping of system memory, + nor permit speculative access to it under any circumstances other + than under the control of the device driver using the region. +reusable (optional) - empty property + - The operating system can use the memory in this region with the + limitation that the device driver(s) owning the region need to be + able to reclaim it back. Typically that means that the operating + system can use that region to store volatile or cached data that + can be otherwise regenerated or migrated elsewhere. + +Linux implementation note: +- If a "linux,cma-default" property is present, then Linux will use the + region for the default pool of the contiguous memory allocator. + +- If a "linux,dma-default" property is present, then Linux will use the + region for the default pool of the consistent DMA allocator. + +Device node references to reserved memory +----------------------------------------- +Regions in the /reserved-memory node may be referenced by other device +nodes by adding a memory-region property to the device node. + +memory-region (optional) - phandle, specifier pairs to children of /reserved-memory + +Example +------- +This example defines 3 contiguous regions are defined for Linux kernel: +one default of all device drivers (named linux,cma@72000000 and 64MiB in size), +one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and +one for multimedia processing (named multimedia-memory@77000000, 64MiB). + +/ { + #address-cells = <1>; + #size-cells = <1>; + + memory { + reg = <0x40000000 0x40000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0x4000000>; + alignment = <0x2000>; + linux,cma-default; + }; + + display_reserved: framebuffer@78000000 { + reg = <0x78000000 0x800000>; + }; + + multimedia_reserved: multimedia@77000000 { + compatible = "acme,multimedia-memory"; + reg = <0x77000000 0x4000000>; + }; + }; + + /* ... */ + + fb0: video@12300000 { + memory-region = <&display_reserved>; + /* ... */ + }; + + scaler: scaler@12500000 { + memory-region = <&multimedia_reserved>; + /* ... */ + }; + + codec: codec@12600000 { + memory-region = <&multimedia_reserved>; + /* ... */ + }; +}; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 87d8e5bcc9..6d4134c053 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -493,6 +493,7 @@ compatible = "denx,u-boot-probe-test"; first-syscon = <&syscon0>; second-sys-ctrl = <&another_system_controller>; + third-syscon = <&syscon2>; }; }; @@ -597,7 +598,7 @@ 0x38 8>; }; - syscon@2 { + syscon2: syscon@2 { compatible = "simple-mfd", "syscon"; reg = <0x40 5 0x48 6 diff --git a/arch/sandbox/include/asm/sdl.h b/arch/sandbox/include/asm/sdl.h index 1027b59e73..c45dbddd70 100644 --- a/arch/sandbox/include/asm/sdl.h +++ b/arch/sandbox/include/asm/sdl.h @@ -104,7 +104,7 @@ static inline int sandbox_sdl_sound_start(uint frequency) return -ENODEV; } -int sandbox_sdl_sound_play(const void *data, uint count) +static inline int sandbox_sdl_sound_play(const void *data, uint count) { return -ENODEV; } @@ -114,7 +114,7 @@ static inline int sandbox_sdl_sound_stop(void) return -ENODEV; } -int sandbox_sdl_sound_init(int rate, int channels) +static inline int sandbox_sdl_sound_init(int rate, int channels) { return -ENODEV; } diff --git a/arch/sandbox/include/asm/types.h b/arch/sandbox/include/asm/types.h index 7cd56b4147..c1a5d2af82 100644 --- a/arch/sandbox/include/asm/types.h +++ b/arch/sandbox/include/asm/types.h @@ -18,21 +18,21 @@ typedef unsigned short umode_t; /* * Number of bits in a C 'long' on this architecture. */ -#ifdef CONFIG_PHYS64 +#ifdef CONFIG_PHYS_64BIT #define BITS_PER_LONG 64 -#else /* CONFIG_PHYS64 */ +#else /* CONFIG_PHYS_64BIT */ #define BITS_PER_LONG 32 -#endif /* CONFIG_PHYS64 */ +#endif /* CONFIG_PHYS_64BIT */ -#ifdef CONFIG_PHYS64 +#ifdef CONFIG_PHYS_64BIT typedef unsigned long long dma_addr_t; typedef u64 phys_addr_t; typedef u64 phys_size_t; -#else /* CONFIG_PHYS64 */ +#else /* CONFIG_PHYS_64BIT */ typedef unsigned long dma_addr_t; typedef u32 phys_addr_t; typedef u32 phys_size_t; -#endif /* CONFIG_PHYS64 */ +#endif /* CONFIG_PHYS_64BIT */ #endif /* __KERNEL__ */ diff --git a/arch/sandbox/lib/pci_io.c b/arch/sandbox/lib/pci_io.c index 5039973cd7..01822c6069 100644 --- a/arch/sandbox/lib/pci_io.c +++ b/arch/sandbox/lib/pci_io.c @@ -34,7 +34,7 @@ int pci_map_physmem(phys_addr_t paddr, unsigned long *lenp, return 0; } - debug("%s: failed: addr=%x\n", __func__, paddr); + debug("%s: failed: addr=%pap\n", __func__, &paddr); return -ENOSYS; } diff --git a/common/fdt_support.c b/common/fdt_support.c index 42583e3ed8..ab08a0114f 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -456,12 +456,6 @@ int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks) if (!banks) return 0; - for (i = 0; i < banks; i++) - if (start[i] == 0 && size[i] == 0) - break; - - banks = i; - len = fdt_pack_reg(blob, tmp, start, size, banks); err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index da4bdced31..c04ecd915a 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -194,6 +194,7 @@ CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y +CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 193e41896c..bb508a8d02 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -215,6 +215,7 @@ CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y +CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y diff --git a/doc/README.chromium b/doc/README.chromium index 45eaeced2d..096bc4f1f7 100644 --- a/doc/README.chromium +++ b/doc/README.chromium @@ -1,239 +1,177 @@ -Running U-Boot from coreboot on Chromebooks -=========================================== +Chromium OS Support in U-Boot +============================= -U-Boot can be used as a secondary boot loader in a few situations such as from -UEFI and coreboot (see README.x86). Recent Chromebooks use coreboot even on -ARM platforms to start up the machine. +Introduction +------------ -This document aims to provide a guide to booting U-Boot on a Chromebook. It -is only a starting point, and there are many guides on the interwebs. But -placing this information in the U-Boot tree should make it easier to find for -those who use U-Boot habitually. +This describes how to use U-Boot with Chromium OS. Several options are +available: -Most of these platforms are supported by U-Boot natively, but it is risky to -replace the ROM unless you have a servo board and cable to restore it with. + - Running U-Boot from the 'altfw' feature, which is available on selected + Chromebooks from 2019 onwards (initially Grunt). Press '1' from the + developer-mode screen to get into U-Boot. See here for details: + https://sites.google.com/a/chromium.org/dev/chromium-os/poking-around-your-chrome-os-device?pli=1 + + - Running U-Boot from the disk partition. This involves signing U-Boot and + placing it on the disk, for booting as a 'kernel'. See + README.chromium-chainload for information on this. This is the only + option on non-U-Boot Chromebooks from 2013 to 2018 and is somewhat + more involved. + + - Running U-Boot with Chromium OS verified boot. This allows U-Boot to be + used instead of either or both of depthcharge (a bootloader which forked + from U-Boot in 2013) and coreboot. See below for more information on + this. -For all of these the standard U-Boot build instructions apply. For example on -ARM: +U-Boot with Chromium OS verified boot +------------------------------------- - sudo apt install gcc-arm-linux-gnueabi - mkdir b - make O=b/nyan_big CROSS_COMPILE=arm-linux-gnueabi- nyan-big_defconfig all +To obtain: -You can obtain the vbutil_kernel utility here: + git clone https://github.com/sglass68/u-boot.git + cd u-boot + git checkout cros-master - https://drive.google.com/open?id=0B7WYZbZ9zd-3dHlVVXo4VXE2T0U +To build for sandbox: + + UB=/tmp/b/chromeos_sandbox # U-Boot build directory + CROS=/home/sglass/cosarm # Chromium OS directory + make O=$UB/chromeos_sandbox_defconfig + make O=$UB -j20 -s VBOOT_SOURCE=$CROS/src/platform/vboot_reference \ + MAKEFLAGS_VBOOT=DEBUG=1 QUIET=1 + +Replace sandbox with another supported target. + +This produces $UB/image.bin which contains the firmware binaries in a SPI +flash image. + +To run on sandbox: + + $UB/tpl/u-boot-tpl -d $UB/u-boot.dtb.out \ + -L6 -c "host bind 0 $CROS/src/build/images/cheza/latest/chromiumos_image.bin; vboot go auto" \ + -l -w -s state.dtb -r + +To run on other boards: + Install image.bin in the SPI flash of your device + Boot your system -Snow (Samsung ARM Chromebook) ------------------------------ +Sandbox +------- -See here: +Most Chromium OS development with U-Boot is undertaken using sandbox. There is +a sandbox target available (chromeos_sandbox) which allows running U-Boot on +a Linux machine completion with emulations of the display, TPM, disk, etc. -https://www.chromium.org/chromium-os/firmware-porting-guide/using-nv-u-boot-on-the-samsung-arm-chromebook +Running sandbox starts TPL, which contains the first phase of vboot, providing +a device tree and binding a Chromium OS disk image for use to find kernels +(any Chromium OS image will do). It also saves driver state between U-Boot +phases into state.dtb and will automatically ensure that memory is shared +between all phases. TPL will jump to SPL and then on to U-Boot proper. + +It is possible to run with debugging on, e.g. + + gdb --args $UB/tpl/u-boot-tpl -d .... + +Breakpoints can be set in any U-Boot phase. Overall this is a good debugging +environment for new verified-boot features. -Nyan-big +Samus +----- + +Basic support is available for samus, using the chromeos_samus target. If you +have an em100, use: + + sudo em100 -s -c W25Q128FW -d $UB/image.bin -t -r + +to write the image and then boot samus (Power-Refresh). + + +Boot flow +--------- + +Verified boot starts in TPL, which selects the A or B SPL, which in turn selects +the A or B U-Boot. Then this jumps to the selected kernel. If anything goes +wrong, the device reboots and the recovery SPL and U-Boot are used instead. + +More details are available here: + + https://www.chromium.org/chromium-os/chromiumos-design-docs/firmware-boot-and-recovery + + +New uclasses +------------ + +Several uclasses are provided in cros/: + + UCLASS_CROS_AUX_FW Chrome OS auxiliary firmware + UCLASS_CROS_FWSTORE Chrome OS firmware storage + UCLASS_CROS_NVDATA Chrome OS non-volatile data device + UCLASS_CROS_VBOOT_EC Chrome OS vboot EC operations + UCLASS_CROS_VBOOT_FLAG Chrome OS verified boot flag + +The existing UCLASS_CROS_EC is also used. + + +Commands -------- -Compiled based on information here: -https://lists.denx.de/pipermail/u-boot/2015-March/209530.html -https://git.collabora.com/cgit/user/tomeu/u-boot.git/commit/?h=nyan-big -https://lists.denx.de/pipermail/u-boot/2017-May/289491.html -https://github.com/chromeos-nvidia-androidtv/gnu-linux-on-acer-chromebook-13#copy-data-to-the-sd-card +A new 'vboot' command is provided to run particular vboot stages. The most +useful command is 'vboot go auto', which continues where the last stage left +off. -1. Build U-Boot +Note that TPL and SPL do not supports commands as yet, so the vboot code is +called directly from the SPL boot devices (BOOT_DEVICE_CROS_VBOOT). See +cros_load_image_tpl() and cros_load_image_spl() which both call +vboot_run_auto(). - mkdir b - make -j8 O=b/nyan-big CROSS_COMPILE=arm-linux-gnueabi- nyan-big_defconfig all +Config options +-------------- -2. Select a .its file +The main option is CONFIG_CHROMEOS, which enables a wide array of other options +so that the required features are present. -Select something from doc/chromium which matches your board, or create your -own. -Note that the device tree node is required, even though it is not actually -used by U-Boot. This is because the Chromebook expects to pass it to the -kernel, and crashes if it is not present. +Device-tree config +------------------ +Various options are available which control the operation of verified boot. +See cros/dts/bindings/config.txt for details. Most config is handled at run- +time, although build-time config (with Kconfig) could also be added fairly +easily. -3. Build and sign an image - ./b/nyan-big/tools/mkimage -f doc/chromium/nyan-big.its u-boot-chromium.fit - echo test >dummy.txt - vbutil_kernel --arch arm --keyblock doc/chromium/devkeys/kernel.keyblock \ - --signprivate doc/chromium/devkeys/kernel_data_key.vbprivk \ - --version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \ - --bootloader dummy.txt --pack u-boot.kpart +Porting to other hardware +------------------------- +A basic port to samus (Chromebook Pixel 2015) is in a basic working state, +using the chromeos_samus target. Patches will likely be forthcoming in early +2019. Ports to an ARM board and coreboot (for x86 Chromebooks) are in the +dreaming state. -4. Prepare an SD card - DISK=/dev/sdc # Replace with your actual SD card device - sudo cgpt create $DISK - sudo cgpt add -b 34 -s 32768 -P 1 -S 1 -t kernel $DISK - sudo cgpt add -b 32802 -s 2000000 -t rootfs $DISK - sudo gdisk $DISK # Enter command 'w' to write a protective MBR to the disk +Tests +----- +Chromium OS firmware has a very limited set of tests. The tests that originally +existed in U-Boot were not brought over to coreboot or depthcharge. -5. Write U-Boot to the SD card +The U-Boot tests ('make check') do operate, but at present there are no +Chromium OS tests available. These will hopefully come together over time. Of +course the above sandbox feature provides a sort of functional test and can +detecte problems that affect the flow or particular vboot features. - sudo dd if=u-boot.kpart of=/dev/sdc1; sync +TO DO +----- -6. Start it up +- Support for booting from coreboot (patches expected March 2019) +- Support for booting from an ARM board, e.g. bob -Reboot the device in dev mode. Make sure that you have USB booting enabled. To -do this, login as root (via Ctrl-Alt-forward_arrow) and type -'enable_dev_usb_boot'. You only need to do this once. -Reboot the device with the SD card inserted. Press Clrl-U at the developer -mode screen. It should show something like the following on the display: - - U-Boot 2017.07-00637-g242eb42-dirty (May 22 2017 - 06:14:21 -0600) - - Model: Acer Chromebook 13 CB5-311 - Board: Google/NVIDIA Nyan-big, ID: 1 - - Net: No ethernet found. - Hit any key to stop autoboot: 0 - Tegra124 (Nyan-big) # - - -7. Known problems - -On the serial console the word MMC is chopped at the start of the line: - -C: sdhci@700b0000: 2, sdhci@700b0400: 1, sdhci@700b0600: 0 - -This is likely due to some problem with change-over of the serial driver -during relocation (or perhaps updating the clock setup in board_init()). - - -9. Notes - -To check that you copied the u-boot.its file correctly, use these commands. -You should see that the data at 0x100 in u-boot-chromium.fit is the first few -bytes of U-Boot: - - hd u-boot-chromium.fit |head -20 - ... - 00000100 b8 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 |................| - - hd b/nyan-big/u-boot.bin |head - 00000000 b8 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 |................| - - -The 'data' property of the FIT is set up to start at offset 0x100 bytes into -the file. The change to CONFIG_SYS_TEXT_BASE is also an offset of 0x100 bytes -from the load address. If this changes, you either need to modify U-Boot to be -fully relocatable, or expect it to hang. - - -chromebook_jerry ----------------- - -The instruction are similar to those for Nyan with changes as noted below: - -1. Patch U-Boot - -Open include/configs/rk3288_common.h - -Change: - -#define CONFIG_SYS_TEXT_BASE 0x00100000 - -to: - -#define CONFIG_SYS_TEXT_BASE 0x02000100 - - - -2. Build U-Boot - - mkdir b - make -j8 O=b/chromebook_jerry CROSS_COMPILE=arm-linux-gnueabi- \ - chromebook_jerry_defconfig all - - -3. See above - -4. Build and sign an image - - ./b/chromebook_jerry/tools/mkimage -f doc/chromium/chromebook_jerry.its \ - u-boot-chromium.fit - echo test >dummy.txt - vbutil_kernel --arch arm --keyblock doc/chromium/devkeys/kernel.keyblock \ - --signprivate doc/chromium/devkeys/kernel_data_key.vbprivk \ - --version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \ - --bootloader dummy.txt --pack u-boot.kpart - - -5. See above - -6. See above - -7. Start it up - -Reboot the device in dev mode. Make sure that you have USB booting enabled. To -do this, login as root (via Ctrl-Alt-forward_arrow) and type -'enable_dev_usb_boot'. You only need to do this once. - -Reboot the device with the SD card inserted. Press Clrl-U at the developer -mode screen. It should show something like the following on the display: - - U-Boot 2017.05-00649-g72acdbf-dirty (May 29 2017 - 14:57:05 -0600) - - Model: Google Jerry - Net: Net Initialization Skipped - No ethernet found. - Hit any key to stop autoboot: 0 - - -8. Known problems - -None as yet. - - -9. Notes - -None as yet. - - -Other notes -=========== - -flashrom --------- - - Used to make a backup of your firmware, or to replace it. - - See: https://www.chromium.org/chromium-os/packages/cros-flashrom - - -coreboot --------- - -Coreboot itself is not designed to actually boot an OS. Instead, a program -called Depthcharge is used. This originally came out of U-Boot and was then -heavily hacked and modified such that is is almost unrecognisable. It does -include a very small part of the U-Boot command-line interface but is not -usable as a general-purpose boot loader. - -In addition, it has a very unusual design in that it does not do device init -itself, but instead relies on coreboot. This is similar to (in U-Boot) having -a SPI driver with an empty probe() method, relying on whatever was set up -beforehand. It can be quite hard to figure out between these two code bases -what settings are actually used. When chain-loading into U-Boot we must be -careful to reinit anything that U-Boot expects. If not, some peripherals (or -the whole machine) may not work. This makes the process of chainloading more -complicated than it could be on some platforms. - -Finally, it supports only a subset of the U-Boot's FIT format. In particular -it uses a fixed address to load the FIT and does not support load/exec -addresses. This means that U-Boot must be able to boot from whatever -address Depthcharge happens to use (it is the CONFIG_KERNEL_START setting -in Depthcharge). In practice this means that the data in the kernel@1 FIT node -(see above) must start at the same address as U-Boot's CONFIG_SYS_TEXT_BASE. +Simon Glass +sjg@chromium.org +7 October 2018 diff --git a/doc/README.chromium-chainload b/doc/README.chromium-chainload new file mode 100644 index 0000000000..45eaeced2d --- /dev/null +++ b/doc/README.chromium-chainload @@ -0,0 +1,239 @@ +Running U-Boot from coreboot on Chromebooks +=========================================== + +U-Boot can be used as a secondary boot loader in a few situations such as from +UEFI and coreboot (see README.x86). Recent Chromebooks use coreboot even on +ARM platforms to start up the machine. + +This document aims to provide a guide to booting U-Boot on a Chromebook. It +is only a starting point, and there are many guides on the interwebs. But +placing this information in the U-Boot tree should make it easier to find for +those who use U-Boot habitually. + +Most of these platforms are supported by U-Boot natively, but it is risky to +replace the ROM unless you have a servo board and cable to restore it with. + + +For all of these the standard U-Boot build instructions apply. For example on +ARM: + + sudo apt install gcc-arm-linux-gnueabi + mkdir b + make O=b/nyan_big CROSS_COMPILE=arm-linux-gnueabi- nyan-big_defconfig all + +You can obtain the vbutil_kernel utility here: + + https://drive.google.com/open?id=0B7WYZbZ9zd-3dHlVVXo4VXE2T0U + + +Snow (Samsung ARM Chromebook) +----------------------------- + +See here: + +https://www.chromium.org/chromium-os/firmware-porting-guide/using-nv-u-boot-on-the-samsung-arm-chromebook + + +Nyan-big +-------- + +Compiled based on information here: +https://lists.denx.de/pipermail/u-boot/2015-March/209530.html +https://git.collabora.com/cgit/user/tomeu/u-boot.git/commit/?h=nyan-big +https://lists.denx.de/pipermail/u-boot/2017-May/289491.html +https://github.com/chromeos-nvidia-androidtv/gnu-linux-on-acer-chromebook-13#copy-data-to-the-sd-card + +1. Build U-Boot + + mkdir b + make -j8 O=b/nyan-big CROSS_COMPILE=arm-linux-gnueabi- nyan-big_defconfig all + + +2. Select a .its file + +Select something from doc/chromium which matches your board, or create your +own. + +Note that the device tree node is required, even though it is not actually +used by U-Boot. This is because the Chromebook expects to pass it to the +kernel, and crashes if it is not present. + + +3. Build and sign an image + + ./b/nyan-big/tools/mkimage -f doc/chromium/nyan-big.its u-boot-chromium.fit + echo test >dummy.txt + vbutil_kernel --arch arm --keyblock doc/chromium/devkeys/kernel.keyblock \ + --signprivate doc/chromium/devkeys/kernel_data_key.vbprivk \ + --version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \ + --bootloader dummy.txt --pack u-boot.kpart + + +4. Prepare an SD card + + DISK=/dev/sdc # Replace with your actual SD card device + sudo cgpt create $DISK + sudo cgpt add -b 34 -s 32768 -P 1 -S 1 -t kernel $DISK + sudo cgpt add -b 32802 -s 2000000 -t rootfs $DISK + sudo gdisk $DISK # Enter command 'w' to write a protective MBR to the disk + + +5. Write U-Boot to the SD card + + sudo dd if=u-boot.kpart of=/dev/sdc1; sync + + +6. Start it up + +Reboot the device in dev mode. Make sure that you have USB booting enabled. To +do this, login as root (via Ctrl-Alt-forward_arrow) and type +'enable_dev_usb_boot'. You only need to do this once. + +Reboot the device with the SD card inserted. Press Clrl-U at the developer +mode screen. It should show something like the following on the display: + + U-Boot 2017.07-00637-g242eb42-dirty (May 22 2017 - 06:14:21 -0600) + + Model: Acer Chromebook 13 CB5-311 + Board: Google/NVIDIA Nyan-big, ID: 1 + + Net: No ethernet found. + Hit any key to stop autoboot: 0 + Tegra124 (Nyan-big) # + + +7. Known problems + +On the serial console the word MMC is chopped at the start of the line: + +C: sdhci@700b0000: 2, sdhci@700b0400: 1, sdhci@700b0600: 0 + +This is likely due to some problem with change-over of the serial driver +during relocation (or perhaps updating the clock setup in board_init()). + + +9. Notes + +To check that you copied the u-boot.its file correctly, use these commands. +You should see that the data at 0x100 in u-boot-chromium.fit is the first few +bytes of U-Boot: + + hd u-boot-chromium.fit |head -20 + ... + 00000100 b8 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 |................| + + hd b/nyan-big/u-boot.bin |head + 00000000 b8 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 |................| + + +The 'data' property of the FIT is set up to start at offset 0x100 bytes into +the file. The change to CONFIG_SYS_TEXT_BASE is also an offset of 0x100 bytes +from the load address. If this changes, you either need to modify U-Boot to be +fully relocatable, or expect it to hang. + + +chromebook_jerry +---------------- + +The instruction are similar to those for Nyan with changes as noted below: + +1. Patch U-Boot + +Open include/configs/rk3288_common.h + +Change: + +#define CONFIG_SYS_TEXT_BASE 0x00100000 + +to: + +#define CONFIG_SYS_TEXT_BASE 0x02000100 + + + +2. Build U-Boot + + mkdir b + make -j8 O=b/chromebook_jerry CROSS_COMPILE=arm-linux-gnueabi- \ + chromebook_jerry_defconfig all + + +3. See above + +4. Build and sign an image + + ./b/chromebook_jerry/tools/mkimage -f doc/chromium/chromebook_jerry.its \ + u-boot-chromium.fit + echo test >dummy.txt + vbutil_kernel --arch arm --keyblock doc/chromium/devkeys/kernel.keyblock \ + --signprivate doc/chromium/devkeys/kernel_data_key.vbprivk \ + --version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \ + --bootloader dummy.txt --pack u-boot.kpart + + +5. See above + +6. See above + +7. Start it up + +Reboot the device in dev mode. Make sure that you have USB booting enabled. To +do this, login as root (via Ctrl-Alt-forward_arrow) and type +'enable_dev_usb_boot'. You only need to do this once. + +Reboot the device with the SD card inserted. Press Clrl-U at the developer +mode screen. It should show something like the following on the display: + + U-Boot 2017.05-00649-g72acdbf-dirty (May 29 2017 - 14:57:05 -0600) + + Model: Google Jerry + Net: Net Initialization Skipped + No ethernet found. + Hit any key to stop autoboot: 0 + + +8. Known problems + +None as yet. + + +9. Notes + +None as yet. + + +Other notes +=========== + +flashrom +-------- + + Used to make a backup of your firmware, or to replace it. + + See: https://www.chromium.org/chromium-os/packages/cros-flashrom + + +coreboot +-------- + +Coreboot itself is not designed to actually boot an OS. Instead, a program +called Depthcharge is used. This originally came out of U-Boot and was then +heavily hacked and modified such that is is almost unrecognisable. It does +include a very small part of the U-Boot command-line interface but is not +usable as a general-purpose boot loader. + +In addition, it has a very unusual design in that it does not do device init +itself, but instead relies on coreboot. This is similar to (in U-Boot) having +a SPI driver with an empty probe() method, relying on whatever was set up +beforehand. It can be quite hard to figure out between these two code bases +what settings are actually used. When chain-loading into U-Boot we must be +careful to reinit anything that U-Boot expects. If not, some peripherals (or +the whole machine) may not work. This makes the process of chainloading more +complicated than it could be on some platforms. + +Finally, it supports only a subset of the U-Boot's FIT format. In particular +it uses a fixed address to load the FIT and does not support load/exec +addresses. This means that U-Boot must be able to boot from whatever +address Depthcharge happens to use (it is the CONFIG_KERNEL_START setting +in Depthcharge). In practice this means that the data in the kernel@1 FIT node +(see above) must start at the same address as U-Boot's CONFIG_SYS_TEXT_BASE. diff --git a/drivers/clk/altera/clk-arria10.c b/drivers/clk/altera/clk-arria10.c index 612a1718dc..179869df45 100644 --- a/drivers/clk/altera/clk-arria10.c +++ b/drivers/clk/altera/clk-arria10.c @@ -254,7 +254,8 @@ static int socfpga_a10_clk_bind(struct udevice *dev) fdt_node_check_compatible(fdt, offset, "fixed-clock")) continue; - if (pre_reloc_only && !dm_fdt_pre_reloc(fdt, offset)) + if (pre_reloc_only && + !dm_ofnode_pre_reloc(offset_to_ofnode(offset))) continue; ret = device_bind_driver_to_node(dev, "clk-a10", name, diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 7cfbabc96d..6b55ec59d6 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -61,7 +61,7 @@ int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name) offset > 0; offset = fdt_next_subnode(fdt, offset)) { if (pre_reloc_only && - !dm_fdt_pre_reloc(fdt, offset)) + !dm_ofnode_pre_reloc(offset_to_ofnode(offset))) continue; /* * If this node has "compatible" property, this is not diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 0e584c12dc..785f5c3acf 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -254,14 +254,13 @@ int ofnode_read_size(ofnode node, const char *propname) fdt_addr_t ofnode_get_addr_index(ofnode node, int index) { int na, ns; - fdt_size_t size; if (ofnode_is_np(node)) { const __be32 *prop_val; uint flags; prop_val = of_get_address(ofnode_to_np(node), index, - (u64 *)&size, &flags); + NULL, &flags); if (!prop_val) return FDT_ADDR_T_NONE; @@ -278,7 +277,7 @@ fdt_addr_t ofnode_get_addr_index(ofnode node, int index) ns = ofnode_read_simple_size_cells(ofnode_get_parent(node)); return fdtdec_get_addr_size_fixed(gd->fdt_blob, ofnode_to_offset(node), "reg", - index, na, ns, &size, true); + index, na, ns, NULL, true); } return FDT_ADDR_T_NONE; @@ -700,18 +699,18 @@ int ofnode_read_simple_size_cells(ofnode node) bool ofnode_pre_reloc(ofnode node) { +#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD) + /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass + * had property dm-pre-reloc or u-boot,dm-spl/tpl. + * They are removed in final dtb (fdtgrep 2nd pass) + */ + return true; +#else if (ofnode_read_bool(node, "u-boot,dm-pre-reloc")) return true; if (ofnode_read_bool(node, "u-boot,dm-pre-proper")) return true; -#ifdef CONFIG_TPL_BUILD - if (ofnode_read_bool(node, "u-boot,dm-tpl")) - return true; -#elif defined(CONFIG_SPL_BUILD) - if (ofnode_read_bool(node, "u-boot,dm-spl")) - return true; -#else /* * In regular builds individual spl and tpl handling both * count as handled pre-relocation for later second init. @@ -719,9 +718,9 @@ bool ofnode_pre_reloc(ofnode node) if (ofnode_read_bool(node, "u-boot,dm-spl") || ofnode_read_bool(node, "u-boot,dm-tpl")) return true; -#endif return false; +#endif } int ofnode_read_resource(ofnode node, uint index, struct resource *res) diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index afac6d6e37..5bb38e329c 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -57,18 +57,64 @@ static int syscon_pre_probe(struct udevice *dev) #endif } +static int syscon_probe_by_ofnode(ofnode node, struct udevice **devp) +{ + struct udevice *dev, *parent; + int ret; + + /* found node with "syscon" compatible, not bounded to SYSCON UCLASS */ + if (!ofnode_device_is_compatible(node, "syscon")) { + dev_dbg(dev, "invalid compatible for syscon device\n"); + return -EINVAL; + } + + /* bound to driver with same ofnode or to root if not found */ + if (device_find_global_by_ofnode(node, &parent)) + parent = dm_root(); + + /* force bound to syscon class */ + ret = device_bind_driver_to_node(parent, "syscon", + ofnode_get_name(node), + node, &dev); + if (ret) { + dev_dbg(dev, "unable to bound syscon device\n"); + return ret; + } + ret = device_probe(dev); + if (ret) { + dev_dbg(dev, "unable to probe syscon device\n"); + return ret; + } + + *devp = dev; + return 0; +} + struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev, const char *name) { struct udevice *syscon; struct regmap *r; + u32 phandle; + ofnode node; int err; err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, name, &syscon); if (err) { - dev_dbg(dev, "unable to find syscon device\n"); - return ERR_PTR(err); + /* found node with "syscon" compatible, not bounded to SYSCON */ + err = ofnode_read_u32(dev_ofnode(dev), name, &phandle); + if (err) + return ERR_PTR(err); + + node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(node)) { + dev_dbg(dev, "unable to find syscon device\n"); + return ERR_PTR(-EINVAL); + } + err = syscon_probe_by_ofnode(node, &syscon); + if (err) + return ERR_PTR(-ENODEV); } r = syscon_get_regmap(syscon); @@ -152,29 +198,18 @@ U_BOOT_DRIVER(generic_syscon) = { */ struct regmap *syscon_node_to_regmap(ofnode node) { - struct udevice *dev, *parent; - int ret; + struct udevice *dev; + struct regmap *r; - if (!uclass_get_device_by_ofnode(UCLASS_SYSCON, node, &dev)) - return syscon_get_regmap(dev); + if (uclass_get_device_by_ofnode(UCLASS_SYSCON, node, &dev)) + if (syscon_probe_by_ofnode(node, &dev)) + return ERR_PTR(-ENODEV); - if (!ofnode_device_is_compatible(node, "syscon")) - return ERR_PTR(-EINVAL); + r = syscon_get_regmap(dev); + if (!r) { + dev_dbg(dev, "unable to find regmap\n"); + return ERR_PTR(-ENODEV); + } - /* bound to driver with same ofnode or to root if not found */ - if (device_find_global_by_ofnode(node, &parent)) - parent = dm_root(); - - /* force bound to syscon class */ - ret = device_bind_driver_to_node(parent, "syscon", - ofnode_get_name(node), - node, &dev); - if (ret) - return ERR_PTR(ret); - - ret = device_probe(dev); - if (ret) - return ERR_PTR(ret); - - return syscon_get_regmap(dev); + return r; } diff --git a/drivers/core/util.c b/drivers/core/util.c index 27a6848703..96e47dc707 100644 --- a/drivers/core/util.c +++ b/drivers/core/util.c @@ -31,42 +31,18 @@ int list_count_items(struct list_head *head) return count; } -bool dm_fdt_pre_reloc(const void *blob, int offset) -{ - if (fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) - return true; - -#ifdef CONFIG_TPL_BUILD - if (fdt_getprop(blob, offset, "u-boot,dm-tpl", NULL)) - return true; -#elif defined(CONFIG_SPL_BUILD) - if (fdt_getprop(blob, offset, "u-boot,dm-spl", NULL)) - return true; -#else - /* - * In regular builds individual spl and tpl handling both - * count as handled pre-relocation for later second init. - */ - if (fdt_getprop(blob, offset, "u-boot,dm-spl", NULL) || - fdt_getprop(blob, offset, "u-boot,dm-tpl", NULL)) - return true; -#endif - - return false; -} - bool dm_ofnode_pre_reloc(ofnode node) { +#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD) + /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass + * had property dm-pre-reloc or u-boot,dm-spl/tpl. + * They are removed in final dtb (fdtgrep 2nd pass) + */ + return true; +#else if (ofnode_read_bool(node, "u-boot,dm-pre-reloc")) return true; -#ifdef CONFIG_TPL_BUILD - if (ofnode_read_bool(node, "u-boot,dm-tpl")) - return true; -#elif defined(CONFIG_SPL_BUILD) - if (ofnode_read_bool(node, "u-boot,dm-spl")) - return true; -#else /* * In regular builds individual spl and tpl handling both * count as handled pre-relocation for later second init. @@ -74,7 +50,7 @@ bool dm_ofnode_pre_reloc(ofnode node) if (ofnode_read_bool(node, "u-boot,dm-spl") || ofnode_read_bool(node, "u-boot,dm-tpl")) return true; -#endif return false; +#endif } diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index 0e3260afd1..0e6c559d5e 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -27,28 +27,6 @@ int pinctrl_decode_pin_config(const void *blob, int node) return flags; } -/* - * TODO: this function is temporary for v2019.01. - * It should be renamed to pinctrl_decode_pin_config(), - * the original pinctrl_decode_pin_config() function should - * be removed and all callers of the original function should - * be migrated to use the new one. - */ -int pinctrl_decode_pin_config_dm(struct udevice *dev) -{ - int pinconfig = 0; - - if (dev->uclass->uc_drv->id != UCLASS_PINCONFIG) - return -EINVAL; - - if (dev_read_bool(dev, "bias-pull-up")) - pinconfig |= 1 << PIN_CONFIG_BIAS_PULL_UP; - else if (dev_read_bool(dev, "bias-pull-down")) - pinconfig |= 1 << PIN_CONFIG_BIAS_PULL_DOWN; - - return pinconfig; -} - #if CONFIG_IS_ENABLED(PINCTRL_FULL) /** * pinctrl_config_one() - apply pinctrl settings for a single node @@ -149,6 +127,9 @@ static int pinconfig_post_bind(struct udevice *dev) ofnode_get_property(node, "compatible", &ret); if (ret >= 0) continue; + /* If this node has "gpio-controller" property, skip */ + if (ofnode_read_bool(node, "gpio-controller")) + continue; if (ret != -FDT_ERR_NOTFOUND) return ret; @@ -201,11 +182,14 @@ static int pinctrl_select_state_simple(struct udevice *dev) int ret; /* - * For simplicity, assume the first device of PINCTRL uclass - * is the correct one. This is most likely OK as there is - * usually only one pinctrl device on the system. + * For most system, there is only one pincontroller device. But in + * case of multiple pincontroller devices, probe the one with sequence + * number 0 (defined by alias) to avoid race condition. */ - ret = uclass_get_device(UCLASS_PINCTRL, 0, &pctldev); + ret = uclass_get_device_by_seq(UCLASS_PINCTRL, 0, &pctldev); + if (ret) + /* if not found, get the first one */ + ret = uclass_get_device(UCLASS_PINCTRL, 0, &pctldev); if (ret) return ret; diff --git a/drivers/sysreset/sysreset_syscon.c b/drivers/sysreset/sysreset_syscon.c index 34506402ac..3fb39b9952 100644 --- a/drivers/sysreset/sysreset_syscon.c +++ b/drivers/sysreset/sysreset_syscon.c @@ -36,20 +36,9 @@ static struct sysreset_ops syscon_reboot_ops = { int syscon_reboot_probe(struct udevice *dev) { struct syscon_reboot_priv *priv = dev_get_priv(dev); - int err; - u32 phandle; - ofnode node; - err = ofnode_read_u32(dev_ofnode(dev), "regmap", &phandle); - if (err) - return err; - - node = ofnode_get_by_phandle(phandle); - if (!ofnode_valid(node)) - return -EINVAL; - - priv->regmap = syscon_node_to_regmap(node); - if (!priv->regmap) { + priv->regmap = syscon_regmap_lookup_by_phandle(dev, "regmap"); + if (IS_ERR(priv->regmap)) { pr_err("unable to find regmap\n"); return -ENODEV; } diff --git a/dts/Kconfig b/dts/Kconfig index a5f30c8fd0..c9ab66cccc 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -291,10 +291,10 @@ config SPL_MULTI_DTB_FIT_USER_DEF_ADDR config OF_SPL_REMOVE_PROPS string "List of device tree properties to drop for SPL" depends on SPL_OF_CONTROL - default "interrupt-parent" if SPL_PINCTRL && SPL_CLK - default "clocks clock-names interrupt-parent" if SPL_PINCTRL - default "pinctrl-0 pinctrl-names interrupt-parent" if SPL_CLK - default "pinctrl-0 pinctrl-names clocks clock-names interrupt-parent" + default "interrupt-parent interrupts" if SPL_PINCTRL && SPL_CLK + default "clocks clock-names interrupt-parent interrupts" if SPL_PINCTRL + default "pinctrl-0 pinctrl-names interrupt-parent interrupts" if SPL_CLK + default "pinctrl-0 pinctrl-names clocks clock-names interrupt-parent interrupts" help Since SPL normally runs in a reduced memory space, the device tree is cut down to only what is needed to load and start U-Boot. Only diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h index ff2b82e7c2..63a7d55b88 100644 --- a/include/dm/pinctrl.h +++ b/include/dm/pinctrl.h @@ -354,18 +354,6 @@ int pinctrl_get_periph_id(struct udevice *dev, struct udevice *periph); */ int pinctrl_decode_pin_config(const void *blob, int node); -/** - * pinctrl_decode_pin_config_dm() - decode pin configuration flags - * - * This decodes some of the PIN_CONFIG values into flags, with each value - * being (1 << pin_cfg). This does not support things with values like the - * slew rate. - * - * @pinconfig: Pinconfig udevice - * @return decoded flag value, or -ve on error - */ -int pinctrl_decode_pin_config_dm(struct udevice *dev); - /** * pinctrl_get_gpio_mux() - get the mux value for a particular GPIO * diff --git a/include/dm/util.h b/include/dm/util.h index 9ff6531d1b..60d3b93dec 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -39,32 +39,6 @@ static inline void dm_dump_devres(void) } #endif -/** - * Check if a dt node should be or was bound before relocation. - * - * Devicetree nodes can be marked as needed to be bound - * in the loader stages via special devicetree properties. - * - * Before relocation this function can be used to check if nodes - * are required in either SPL or TPL stages. - * - * After relocation and jumping into the real U-Boot binary - * it is possible to determine if a node was bound in one of - * SPL/TPL stages. - * - * There are 3 settings currently in use - * - - * - u-boot,dm-pre-reloc: legacy and indicates any of TPL or SPL - * Existing platforms only use it to indicate nodes needed in - * SPL. Should probably be replaced by u-boot,dm-spl for - * existing platforms. - * @blob: devicetree - * @offset: node offset - * - * Returns true if node is needed in SPL/TL, false otherwise. - */ -bool dm_fdt_pre_reloc(const void *blob, int offset); - /** * Check if an of node should be or was bound before relocation. * diff --git a/include/fdtdec.h b/include/fdtdec.h index ad00f79f20..266c58271f 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -23,15 +23,44 @@ */ typedef phys_addr_t fdt_addr_t; typedef phys_size_t fdt_size_t; + +static inline fdt32_t fdt_addr_unpack(fdt_addr_t addr, fdt32_t *upper) +{ + if (upper) +#ifdef CONFIG_PHYS_64BIT + *upper = addr >> 32; +#else + *upper = 0; +#endif + + return addr; +} + +static inline fdt32_t fdt_size_unpack(fdt_size_t size, fdt32_t *upper) +{ + if (upper) +#ifdef CONFIG_PHYS_64BIT + *upper = size >> 32; +#else + *upper = 0; +#endif + + return size; +} + #ifdef CONFIG_PHYS_64BIT #define FDT_ADDR_T_NONE (-1U) #define fdt_addr_to_cpu(reg) be64_to_cpu(reg) #define fdt_size_to_cpu(reg) be64_to_cpu(reg) +#define cpu_to_fdt_addr(reg) cpu_to_be64(reg) +#define cpu_to_fdt_size(reg) cpu_to_be64(reg) typedef fdt64_t fdt_val_t; #else #define FDT_ADDR_T_NONE (-1U) #define fdt_addr_to_cpu(reg) be32_to_cpu(reg) #define fdt_size_to_cpu(reg) be32_to_cpu(reg) +#define cpu_to_fdt_addr(reg) cpu_to_be32(reg) +#define cpu_to_fdt_size(reg) cpu_to_be32(reg) typedef fdt32_t fdt_val_t; #endif @@ -991,6 +1020,146 @@ int fdtdec_setup_memory_banksize_fdt(const void *blob); */ int fdtdec_setup_memory_banksize(void); +/** + * fdtdec_set_phandle() - sets the phandle of a given node + * + * @param blob FDT blob + * @param node offset in the FDT blob of the node whose phandle is to + * be set + * @param phandle phandle to set for the given node + * @return 0 on success or a negative error code on failure + */ +int fdtdec_set_phandle(void *blob, int node, uint32_t phandle); + +/** + * fdtdec_add_reserved_memory() - add or find a reserved-memory node + * + * If a reserved-memory node already exists for the given carveout, a phandle + * for that node will be returned. Otherwise a new node will be created and a + * phandle corresponding to it will be returned. + * + * See Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt + * for details on how to use reserved memory regions. + * + * As an example, consider the following code snippet: + * + * struct fdt_memory fb = { + * .start = 0x92cb3000, + * .end = 0x934b2fff, + * }; + * uint32_t phandle; + * + * fdtdec_add_reserved_memory(fdt, "framebuffer", &fb, &phandle); + * + * This results in the following subnode being added to the top-level + * /reserved-memory node: + * + * reserved-memory { + * #address-cells = <0x00000002>; + * #size-cells = <0x00000002>; + * ranges; + * + * framebuffer@92cb3000 { + * reg = <0x00000000 0x92cb3000 0x00000000 0x00800000>; + * phandle = <0x0000004d>; + * }; + * }; + * + * If the top-level /reserved-memory node does not exist, it will be created. + * The phandle returned from the function call can be used to reference this + * reserved memory region from other nodes. + * + * See fdtdec_set_carveout() for a more elaborate example. + * + * @param blob FDT blob + * @param basename base name of the node to create + * @param carveout information about the carveout region + * @param phandlep return location for the phandle of the carveout region + * @return 0 on success or a negative error code on failure + */ +int fdtdec_add_reserved_memory(void *blob, const char *basename, + const struct fdt_memory *carveout, + uint32_t *phandlep); + +/** + * fdtdec_get_carveout() - reads a carveout from an FDT + * + * Reads information about a carveout region from an FDT. The carveout is a + * referenced by its phandle that is read from a given property in a given + * node. + * + * @param blob FDT blob + * @param node name of a node + * @param name name of the property in the given node that contains + * the phandle for the carveout + * @param index index of the phandle for which to read the carveout + * @param carveout return location for the carveout information + * @return 0 on success or a negative error code on failure + */ +int fdtdec_get_carveout(const void *blob, const char *node, const char *name, + unsigned int index, struct fdt_memory *carveout); + +/** + * fdtdec_set_carveout() - sets a carveout region for a given node + * + * Sets a carveout region for a given node. If a reserved-memory node already + * exists for the carveout, the phandle for that node will be reused. If no + * such node exists, a new one will be created and a phandle to it stored in + * a specified property of the given node. + * + * As an example, consider the following code snippet: + * + * const char *node = "/host1x@50000000/dc@54240000"; + * struct fdt_memory fb = { + * .start = 0x92cb3000, + * .end = 0x934b2fff, + * }; + * + * fdtdec_set_carveout(fdt, node, "memory-region", 0, "framebuffer", &fb); + * + * dc@54200000 is a display controller and was set up by the bootloader to + * scan out the framebuffer specified by "fb". This would cause the following + * reserved memory region to be added: + * + * reserved-memory { + * #address-cells = <0x00000002>; + * #size-cells = <0x00000002>; + * ranges; + * + * framebuffer@92cb3000 { + * reg = <0x00000000 0x92cb3000 0x00000000 0x00800000>; + * phandle = <0x0000004d>; + * }; + * }; + * + * A "memory-region" property will also be added to the node referenced by the + * offset parameter. + * + * host1x@50000000 { + * ... + * + * dc@54240000 { + * ... + * memory-region = <0x0000004d>; + * ... + * }; + * + * ... + * }; + * + * @param blob FDT blob + * @param node name of the node to add the carveout to + * @param prop_name name of the property in which to store the phandle of + * the carveout + * @param index index of the phandle to store + * @param name base name of the reserved-memory node to create + * @param carveout information about the carveout to add + * @return 0 on success or a negative error code on failure + */ +int fdtdec_set_carveout(void *blob, const char *node, const char *prop_name, + unsigned int index, const char *name, + const struct fdt_memory *carveout); + /** * Set up the device tree ready for use */ diff --git a/lib/Kconfig b/lib/Kconfig index 8fe5d85a05..2120216593 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -436,4 +436,8 @@ source lib/efi/Kconfig source lib/efi_loader/Kconfig source lib/optee/Kconfig +config TEST_FDTDEC + bool "enable fdtdec test" + depends on OF_LIBFDT + endmenu diff --git a/lib/fdtdec.c b/lib/fdtdec.c index a51dc5e986..efec3c2717 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -1261,6 +1261,231 @@ __weak void *board_fdt_blob_setup(void) } #endif +int fdtdec_set_phandle(void *blob, int node, uint32_t phandle) +{ + fdt32_t value = cpu_to_fdt32(phandle); + + return fdt_setprop(blob, node, "phandle", &value, sizeof(value)); +} + +static int fdtdec_init_reserved_memory(void *blob) +{ + int na, ns, node, err; + fdt32_t value; + + /* inherit #address-cells and #size-cells from the root node */ + na = fdt_address_cells(blob, 0); + ns = fdt_size_cells(blob, 0); + + node = fdt_add_subnode(blob, 0, "reserved-memory"); + if (node < 0) + return node; + + err = fdt_setprop(blob, node, "ranges", NULL, 0); + if (err < 0) + return err; + + value = cpu_to_fdt32(ns); + + err = fdt_setprop(blob, node, "#size-cells", &value, sizeof(value)); + if (err < 0) + return err; + + value = cpu_to_fdt32(na); + + err = fdt_setprop(blob, node, "#address-cells", &value, sizeof(value)); + if (err < 0) + return err; + + return node; +} + +int fdtdec_add_reserved_memory(void *blob, const char *basename, + const struct fdt_memory *carveout, + uint32_t *phandlep) +{ + fdt32_t cells[4] = {}, *ptr = cells; + uint32_t upper, lower, phandle; + int parent, node, na, ns, err; + char name[64]; + + /* create an empty /reserved-memory node if one doesn't exist */ + parent = fdt_path_offset(blob, "/reserved-memory"); + if (parent < 0) { + parent = fdtdec_init_reserved_memory(blob); + if (parent < 0) + return parent; + } + + /* only 1 or 2 #address-cells and #size-cells are supported */ + na = fdt_address_cells(blob, parent); + if (na < 1 || na > 2) + return -FDT_ERR_BADNCELLS; + + ns = fdt_size_cells(blob, parent); + if (ns < 1 || ns > 2) + return -FDT_ERR_BADNCELLS; + + /* find a matching node and return the phandle to that */ + fdt_for_each_subnode(node, blob, parent) { + const char *name = fdt_get_name(blob, node, NULL); + phys_addr_t addr, size; + + addr = fdtdec_get_addr_size(blob, node, "reg", &size); + if (addr == FDT_ADDR_T_NONE) { + debug("failed to read address/size for %s\n", name); + continue; + } + + if (addr == carveout->start && (addr + size) == carveout->end) { + *phandlep = fdt_get_phandle(blob, node); + return 0; + } + } + + /* + * Unpack the start address and generate the name of the new node + * base on the basename and the unit-address. + */ + lower = fdt_addr_unpack(carveout->start, &upper); + + if (na > 1 && upper > 0) + snprintf(name, sizeof(name), "%s@%x,%x", basename, upper, + lower); + else { + if (upper > 0) { + debug("address %08x:%08x exceeds addressable space\n", + upper, lower); + return -FDT_ERR_BADVALUE; + } + + snprintf(name, sizeof(name), "%s@%x", basename, lower); + } + + node = fdt_add_subnode(blob, parent, name); + if (node < 0) + return node; + + err = fdt_generate_phandle(blob, &phandle); + if (err < 0) + return err; + + err = fdtdec_set_phandle(blob, node, phandle); + if (err < 0) + return err; + + /* store one or two address cells */ + if (na > 1) + *ptr++ = cpu_to_fdt32(upper); + + *ptr++ = cpu_to_fdt32(lower); + + /* store one or two size cells */ + lower = fdt_size_unpack(carveout->end - carveout->start + 1, &upper); + + if (ns > 1) + *ptr++ = cpu_to_fdt32(upper); + + *ptr++ = cpu_to_fdt32(lower); + + err = fdt_setprop(blob, node, "reg", cells, (na + ns) * sizeof(*cells)); + if (err < 0) + return err; + + /* return the phandle for the new node for the caller to use */ + if (phandlep) + *phandlep = phandle; + + return 0; +} + +int fdtdec_get_carveout(const void *blob, const char *node, const char *name, + unsigned int index, struct fdt_memory *carveout) +{ + const fdt32_t *prop; + uint32_t phandle; + int offset, len; + fdt_size_t size; + + offset = fdt_path_offset(blob, node); + if (offset < 0) + return offset; + + prop = fdt_getprop(blob, offset, name, &len); + if (!prop) { + debug("failed to get %s for %s\n", name, node); + return -FDT_ERR_NOTFOUND; + } + + if ((len % sizeof(phandle)) != 0) { + debug("invalid phandle property\n"); + return -FDT_ERR_BADPHANDLE; + } + + if (len < (sizeof(phandle) * (index + 1))) { + debug("invalid phandle index\n"); + return -FDT_ERR_BADPHANDLE; + } + + phandle = fdt32_to_cpu(prop[index]); + + offset = fdt_node_offset_by_phandle(blob, phandle); + if (offset < 0) { + debug("failed to find node for phandle %u\n", phandle); + return offset; + } + + carveout->start = fdtdec_get_addr_size_auto_noparent(blob, offset, + "reg", 0, &size, + true); + if (carveout->start == FDT_ADDR_T_NONE) { + debug("failed to read address/size from \"reg\" property\n"); + return -FDT_ERR_NOTFOUND; + } + + carveout->end = carveout->start + size - 1; + + return 0; +} + +int fdtdec_set_carveout(void *blob, const char *node, const char *prop_name, + unsigned int index, const char *name, + const struct fdt_memory *carveout) +{ + uint32_t phandle; + int err, offset; + fdt32_t value; + + /* XXX implement support for multiple phandles */ + if (index > 0) { + debug("invalid index %u\n", index); + return -FDT_ERR_BADOFFSET; + } + + err = fdtdec_add_reserved_memory(blob, name, carveout, &phandle); + if (err < 0) { + debug("failed to add reserved memory: %d\n", err); + return err; + } + + offset = fdt_path_offset(blob, node); + if (offset < 0) { + debug("failed to find offset for node %s: %d\n", node, offset); + return offset; + } + + value = cpu_to_fdt32(phandle); + + err = fdt_setprop(blob, offset, prop_name, &value, sizeof(value)); + if (err < 0) { + debug("failed to set %s property for node %s: %d\n", prop_name, + node, err); + return err; + } + + return 0; +} + int fdtdec_setup(void) { #if CONFIG_IS_ENABLED(OF_CONTROL) diff --git a/lib/fdtdec_test.c b/lib/fdtdec_test.c index a82e27de94..f6defe16c5 100644 --- a/lib/fdtdec_test.c +++ b/lib/fdtdec_test.c @@ -15,48 +15,28 @@ /* The size of our test fdt blob */ #define FDT_SIZE (16 * 1024) -/** - * Check if an operation failed, and if so, print an error - * - * @param oper_name Name of operation - * @param err Error code to check - * - * @return 0 if ok, -1 if there was an error - */ -static int fdt_checkerr(const char *oper_name, int err) -{ - if (err) { - printf("%s: %s: %s\n", __func__, oper_name, fdt_strerror(err)); - return -1; - } +#define CHECK(op) ({ \ + int err = op; \ + if (err < 0) { \ + printf("%s: %s: %s\n", __func__, #op, \ + fdt_strerror(err)); \ + return err; \ + } \ + \ + err; \ + }) - return 0; -} +#define CHECKVAL(op, expected) ({ \ + int err = op; \ + if (err != expected) { \ + printf("%s: %s: expected %d, but returned %d\n",\ + __func__, #op, expected, err); \ + return err; \ + } \ + \ + err; \ + }) -/** - * Check the result of an operation and if incorrect, print an error - * - * @param oper_name Name of operation - * @param expected Expected value - * @param value Actual value - * - * @return 0 if ok, -1 if there was an error - */ -static int checkval(const char *oper_name, int expected, int value) -{ - if (expected != value) { - printf("%s: %s: expected %d, but returned %d\n", __func__, - oper_name, expected, value); - return -1; - } - - return 0; -} - -#define CHECK(op) if (fdt_checkerr(#op, op)) return -1 -#define CHECKVAL(op, expected) \ - if (checkval(#op, expected, op)) \ - return -1 #define CHECKOK(op) CHECKVAL(op, 0) /* maximum number of nodes / aliases to generate */ @@ -79,7 +59,9 @@ static int make_fdt(void *fdt, int size, const char *aliases, { char name[20], value[20]; const char *s; +#if defined(DEBUG) && defined(CONFIG_SANDBOX) int fd; +#endif CHECK(fdt_create(fdt, size)); CHECK(fdt_finish_reservemap(fdt)); @@ -136,7 +118,7 @@ static int run_test(const char *aliases, const char *nodes, const char *expect) CHECKVAL(make_fdt(blob, FDT_SIZE, aliases, nodes), 0); CHECKVAL(fdtdec_find_aliases_for_id(blob, "i2c", COMPAT_UNKNOWN, - list, ARRAY_SIZE(list)), strlen(expect)); + list, ARRAY_SIZE(list)), (int)strlen(expect)); /* Check we got the right ones */ for (i = 0, s = expect; *s; s++, i++) { @@ -159,6 +141,156 @@ static int run_test(const char *aliases, const char *nodes, const char *expect) return 0; } +static int make_fdt_carveout_device(void *fdt, uint32_t na, uint32_t ns) +{ + const char *basename = "/display"; + struct fdt_memory carveout = { +#ifdef CONFIG_PHYS_64BIT + .start = 0x180000000, + .end = 0x18fffffff, +#else + .start = 0x80000000, + .end = 0x8fffffff, +#endif + }; + fdt32_t cells[4], *ptr = cells; + uint32_t upper, lower; + char name[32]; + int offset; + + /* store one or two address cells */ + lower = fdt_addr_unpack(carveout.start, &upper); + + if (na > 1 && upper > 0) + snprintf(name, sizeof(name), "%s@%x,%x", basename, upper, + lower); + else + snprintf(name, sizeof(name), "%s@%x", basename, lower); + + if (na > 1) + *ptr++ = cpu_to_fdt32(upper); + + *ptr++ = cpu_to_fdt32(lower); + + /* store one or two size cells */ + lower = fdt_size_unpack(carveout.end - carveout.start + 1, &upper); + + if (ns > 1) + *ptr++ = cpu_to_fdt32(upper); + + *ptr++ = cpu_to_fdt32(lower); + + offset = CHECK(fdt_add_subnode(fdt, 0, name + 1)); + CHECK(fdt_setprop(fdt, offset, "reg", cells, (na + ns) * sizeof(*cells))); + + return fdtdec_set_carveout(fdt, name, "memory-region", 0, + "framebuffer", &carveout); +} + +static int check_fdt_carveout(void *fdt, uint32_t address_cells, + uint32_t size_cells) +{ +#ifdef CONFIG_PHYS_64BIT + const char *name = "/display@1,80000000"; + const struct fdt_memory expected = { + .start = 0x180000000, + .end = 0x18fffffff, + }; +#else + const char *name = "/display@80000000"; + const struct fdt_memory expected = { + .start = 0x80000000, + .end = 0x8fffffff, + }; +#endif + struct fdt_memory carveout; + + printf("carveout: %pap-%pap na=%u ns=%u: ", &expected.start, + &expected.end, address_cells, size_cells); + + CHECK(fdtdec_get_carveout(fdt, name, "memory-region", 0, &carveout)); + + if ((carveout.start != expected.start) || + (carveout.end != expected.end)) { + printf("carveout: %pap-%pap, expected %pap-%pap\n", + &carveout.start, &carveout.end, + &expected.start, &expected.end); + return 1; + } + + printf("pass\n"); + return 0; +} + +static int make_fdt_carveout(void *fdt, int size, uint32_t address_cells, + uint32_t size_cells) +{ + fdt32_t na = cpu_to_fdt32(address_cells); + fdt32_t ns = cpu_to_fdt32(size_cells); +#if defined(DEBUG) && defined(CONFIG_SANDBOX) + char filename[512]; + int fd; +#endif + int err; + + CHECK(fdt_create(fdt, size)); + CHECK(fdt_finish_reservemap(fdt)); + CHECK(fdt_begin_node(fdt, "")); + CHECK(fdt_property(fdt, "#address-cells", &na, sizeof(na))); + CHECK(fdt_property(fdt, "#size-cells", &ns, sizeof(ns))); + CHECK(fdt_end_node(fdt)); + CHECK(fdt_finish(fdt)); + CHECK(fdt_pack(fdt)); + + CHECK(fdt_open_into(fdt, fdt, FDT_SIZE)); + + err = make_fdt_carveout_device(fdt, address_cells, size_cells); + +#if defined(DEBUG) && defined(CONFIG_SANDBOX) + snprintf(filename, sizeof(filename), "/tmp/fdtdec-carveout-%u-%u.dtb", + address_cells, size_cells); + + fd = os_open(filename, OS_O_CREAT | OS_O_WRONLY); + if (fd < 0) { + printf("could not open .dtb file to write\n"); + goto out; + } + + os_write(fd, fdt, size); + os_close(fd); + +out: +#endif + return err; +} + +static int check_carveout(void) +{ + void *fdt; + + fdt = malloc(FDT_SIZE); + if (!fdt) { + printf("%s: out of memory\n", __func__); + return 1; + } + +#ifndef CONFIG_PHYS_64BIT + CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 1), 0); + CHECKOK(check_fdt_carveout(fdt, 1, 1)); + CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 2), 0); + CHECKOK(check_fdt_carveout(fdt, 1, 2)); +#else + CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 1), -FDT_ERR_BADVALUE); + CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 2), -FDT_ERR_BADVALUE); +#endif + CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 2, 1), 0); + CHECKOK(check_fdt_carveout(fdt, 2, 1)); + CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 2, 2), 0); + CHECKOK(check_fdt_carveout(fdt, 2, 2)); + + return 0; +} + static int do_test_fdtdec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -200,6 +332,8 @@ static int do_test_fdtdec(cmd_tbl_t *cmdtp, int flag, int argc, CHECKOK(run_test("2a 1a 0a", "a", " a")); CHECKOK(run_test("0a 1a 2a", "a", "a")); + CHECKOK(check_carveout()); + printf("Test passed\n"); return 0; } diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c index b6ca4e0b0c..693de9aa5a 100644 --- a/lib/libfdt/fdt_ro.c +++ b/lib/libfdt/fdt_ro.c @@ -73,6 +73,37 @@ uint32_t fdt_get_max_phandle(const void *fdt) return 0; } +int fdt_generate_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max = 0; + int offset = -1; + + while (true) { + uint32_t value; + + offset = fdt_next_node(fdt, offset, NULL); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) + break; + + return offset; + } + + value = fdt_get_phandle(fdt, offset); + + if (value > max) + max = value; + } + + if (max == FDT_MAX_PHANDLE) + return -FDT_ERR_NOPHANDLES; + + if (phandle) + *phandle = max + 1; + + return 0; +} + int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) { FDT_CHECK_HEADER(fdt); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 1b6c154d8d..2403825dc9 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -457,7 +457,6 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, return device_path_string(buf, end, ptr, field_width, precision, flags); #endif -#ifdef CONFIG_CMD_NET case 'a': flags |= SPECIAL | ZEROPAD; @@ -469,6 +468,7 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, break; } break; +#ifdef CONFIG_CMD_NET case 'm': flags |= SPECIAL; /* Fallthrough */ diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 70de9bb13a..de67677f61 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -525,4 +525,5 @@ quiet_cmd_fdtgrep = FDTGREP $@ cmd_fdtgrep = $(objtree)/tools/fdtgrep $(fdtgrep_props) -RT $< \ -n /chosen -n /config -O dtb | \ $(objtree)/tools/fdtgrep -r -O dtb - -o $@ \ + -P u-boot,dm-pre-reloc -P u-boot,dm-spl -P u-boot,dm-tpl \ $(addprefix -P ,$(subst $\",,$(CONFIG_OF_SPL_REMOVE_PROPS))) diff --git a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c index dfb3236da3..dc499884e4 100644 --- a/scripts/dtc/libfdt/fdt_ro.c +++ b/scripts/dtc/libfdt/fdt_ro.c @@ -115,6 +115,37 @@ uint32_t fdt_get_max_phandle(const void *fdt) return 0; } +int fdt_generate_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max = 0; + int offset = -1; + + while (true) { + uint32_t value; + + offset = fdt_next_node(fdt, offset, NULL); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) + break; + + return offset; + } + + value = fdt_get_phandle(fdt, offset); + + if (value > max) + max = value; + } + + if (max == FDT_MAX_PHANDLE) + return -FDT_ERR_NOPHANDLES; + + if (phandle) + *phandle = max + 1; + + return 0; +} + int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) { FDT_CHECK_HEADER(fdt); diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h index fd73688f9e..cf86ddba88 100644 --- a/scripts/dtc/libfdt/libfdt.h +++ b/scripts/dtc/libfdt/libfdt.h @@ -139,6 +139,10 @@ #define FDT_ERR_MAX 17 +/* constants */ +#define FDT_MAX_PHANDLE 0xfffffffe + /* Valid values for phandles range from 1 to 2^32-2. */ + /**********************************************************************/ /* Low-level functions (you probably don't need these) */ /**********************************************************************/ @@ -313,6 +317,21 @@ const char *fdt_string(const void *fdt, int stroffset); */ uint32_t fdt_get_max_phandle(const void *fdt); +/** + * fdt_generate_phandle - return a new, unused phandle for a device tree blob + * @fdt: pointer to the device tree blob + * @phandle: return location for the new phandle + * + * Walks the device tree blob and looks for the highest phandle value. On + * success, the new, unused phandle value (one higher than the previously + * highest phandle value in the device tree blob) will be returned in the + * @phandle parameter. + * + * Returns: + * 0 on success or a negative error-code on failure + */ +int fdt_generate_phandle(const void *fdt, uint32_t *phandle); + /** * fdt_num_mem_rsv - retrieve the number of memory reserve map entries * @fdt: pointer to the device tree blob diff --git a/scripts/dtc/libfdt/libfdt_env.h b/scripts/dtc/libfdt/libfdt_env.h index bd24746287..3ff9e28630 100644 --- a/scripts/dtc/libfdt/libfdt_env.h +++ b/scripts/dtc/libfdt/libfdt_env.h @@ -52,6 +52,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include diff --git a/test/dm/syscon.c b/test/dm/syscon.c index a294dda02e..0ff9da7ec6 100644 --- a/test/dm/syscon.c +++ b/test/dm/syscon.c @@ -67,6 +67,13 @@ static int dm_test_syscon_by_phandle(struct unit_test_state *uts) ut_assert(!IS_ERR(map)); ut_asserteq(4, map->range_count); + ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev, + "third-syscon")); + map = syscon_regmap_lookup_by_phandle(dev, "third-syscon"); + ut_assert(map); + ut_assert(!IS_ERR(map)); + ut_asserteq(4, map->range_count); + ut_assert(IS_ERR(syscon_regmap_lookup_by_phandle(dev, "not-present"))); return 0; diff --git a/tools/mkimage.c b/tools/mkimage.c index 2899adff81..d1e1a6743d 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -403,14 +403,21 @@ int main(int argc, char **argv) exit (EXIT_FAILURE); } - /* - * scan through mkimage registry for all supported image types - * and verify the input image file header for match - * Print the image information for matched image type - * Returns the error code if not matched - */ - retval = imagetool_verify_print_header_by_type(ptr, &sbuf, - tparams, ¶ms); + if (params.fflag) { + /* + * Verifies the header format based on the expected header for image + * type in tparams + */ + retval = imagetool_verify_print_header_by_type(ptr, &sbuf, + tparams, ¶ms); + } else { + /** + * When listing the image, we are not given the image type. Simply check all + * image types to find one that matches our header + */ + retval = imagetool_verify_print_header(ptr, &sbuf, + tparams, ¶ms); + } (void) munmap((void *)ptr, sbuf.st_size); (void) close (ifd);