Tom Rini | 83d290c | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | SPDX-License-Identifier: GPL-2.0+ |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 2 | /* |
| 3 | * (C) Copyright 2014 Red Hat Inc. |
| 4 | * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. |
Karsten Merker | 8c24929 | 2015-03-21 14:15:38 +0100 | [diff] [blame] | 5 | * Copyright (C) 2015 K. Merker <merker@debian.org> |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 6 | */ |
| 7 | |
| 8 | Generic Distro Configuration Concept |
| 9 | ==================================== |
| 10 | |
| 11 | Linux distributions are faced with supporting a variety of boot mechanisms, |
| 12 | environments or bootloaders (PC BIOS, EFI, U-Boot, Barebox, ...). This makes |
| 13 | life complicated. Worse, bootloaders such as U-Boot have a configurable set |
| 14 | of features, and each board chooses to enable a different set of features. |
| 15 | Hence, distros typically need to have board-specific knowledge in order to |
| 16 | set up a bootable system. |
| 17 | |
| 18 | This document defines a common set of U-Boot features that are required for |
| 19 | a distro to support the board in a generic fashion. Any board wishing to |
| 20 | allow distros to install and boot in an out-of-the-box fashion should enable |
| 21 | all these features. Linux distros can then create a single set of boot |
| 22 | support/install logic that targets these features. This will allow distros |
| 23 | to install on many boards without the need for board-specific logic. |
| 24 | |
| 25 | In fact, some of these features can be implemented by any bootloader, thus |
| 26 | decoupling distro install/boot logic from any knowledge of the bootloader. |
| 27 | |
| 28 | This model assumes that boards will load boot configuration files from a |
| 29 | regular storage mechanism (eMMC, SD card, USB Disk, SATA disk, etc.) with |
Masahiro Yamada | 28fd00b | 2015-07-07 18:47:17 +0900 | [diff] [blame] | 30 | a standard partitioning scheme (MBR, GPT). Boards that cannot support this |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 31 | storage model are outside the scope of this document, and may still need |
| 32 | board-specific installer/boot-configuration support in a distro. |
| 33 | |
| 34 | To some extent, this model assumes that a board has a separate boot flash |
| 35 | that contains U-Boot, and that the user has somehow installed U-Boot to this |
| 36 | flash before running the distro installer. Even on boards that do not conform |
| 37 | to this aspect of the model, the extent of the board-specific support in the |
| 38 | distro installer logic would be to install a board-specific U-Boot package to |
Masahiro Yamada | 28fd00b | 2015-07-07 18:47:17 +0900 | [diff] [blame] | 39 | the boot partition during installation. This distro-supplied U-Boot can still |
| 40 | implement the same features as on any other board, and hence the distro's boot |
| 41 | configuration file generation logic can still be board-agnostic. |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 42 | |
| 43 | Locating Bootable Disks |
| 44 | ----------------------- |
| 45 | |
| 46 | Typical desktop/server PCs search all (or a user-defined subset of) attached |
| 47 | storage devices for a bootable partition, then load the bootloader or boot |
| 48 | configuration files from there. A U-Boot board port that enables the features |
| 49 | mentioned in this document will search for boot configuration files in the |
| 50 | same way. |
| 51 | |
| 52 | Thus, distros do not need to manipulate any kind of bootloader-specific |
| 53 | configuration data to indicate which storage device the system should boot |
| 54 | from. |
| 55 | |
| 56 | Distros simply need to install the boot configuration files (see next |
| 57 | section) in an ext2/3/4 or FAT partition, mark the partition bootable (via |
| 58 | the MBR bootable flag, or GPT legacy_bios_bootable attribute), and U-Boot (or |
| 59 | any other bootloader) will find those boot files and execute them. This is |
| 60 | conceptually identical to creating a grub2 configuration file on a desktop |
| 61 | PC. |
| 62 | |
Masahiro Yamada | 28fd00b | 2015-07-07 18:47:17 +0900 | [diff] [blame] | 63 | Note that in the absence of any partition that is explicitly marked bootable, |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 64 | U-Boot falls back to searching the first valid partition of a disk for boot |
| 65 | configuration files. Other bootloaders are recommended to do the same, since |
| 66 | I believe that partition table bootable flags aren't so commonly used outside |
| 67 | the realm of x86 PCs. |
| 68 | |
| 69 | U-Boot can also search for boot configuration files from a TFTP server. |
| 70 | |
| 71 | Boot Configuration Files |
| 72 | ------------------------ |
| 73 | |
| 74 | The standard format for boot configuration files is that of extlinux.conf, as |
| 75 | handled by U-Boot's "syslinux" (disk) or "pxe boot" (network). This is roughly |
| 76 | as specified at: |
| 77 | |
| 78 | http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ |
| 79 | |
| 80 | ... with the exceptions that the BootLoaderSpec document: |
| 81 | |
| 82 | * Prescribes a separate configuration per boot menu option, whereas U-Boot |
| 83 | lumps all options into a single extlinux.conf file. Hence, U-Boot searches |
| 84 | for /extlinux/extlinux.conf then /boot/extlinux/extlinux.conf on disk, or |
| 85 | pxelinux.cfg/default over the network. |
| 86 | |
| 87 | * Does not document the fdtdir option, which automatically selects the DTB to |
| 88 | pass to the kernel. |
| 89 | |
| 90 | One example extlinux.conf generated by the Fedora installer is: |
| 91 | |
| 92 | ------------------------------------------------------------ |
| 93 | # extlinux.conf generated by anaconda |
| 94 | |
| 95 | ui menu.c32 |
| 96 | |
| 97 | menu autoboot Welcome to Fedora. Automatic boot in # second{,s}. Press a key for options. |
| 98 | menu title Fedora Boot Options. |
| 99 | menu hidden |
| 100 | |
| 101 | timeout 50 |
| 102 | #totaltimeout 9000 |
| 103 | |
| 104 | default Fedora (3.17.0-0.rc4.git2.1.fc22.armv7hl+lpae) 22 (Rawhide) |
| 105 | |
| 106 | label Fedora (3.17.0-0.rc4.git2.1.fc22.armv7hl) 22 (Rawhide) |
| 107 | kernel /boot/vmlinuz-3.17.0-0.rc4.git2.1.fc22.armv7hl |
| 108 | append ro root=UUID=8eac677f-8ea8-4270-8479-d5ddbb797450 console=ttyS0,115200n8 LANG=en_US.UTF-8 drm.debug=0xf |
| 109 | fdtdir /boot/dtb-3.17.0-0.rc4.git2.1.fc22.armv7hl |
| 110 | initrd /boot/initramfs-3.17.0-0.rc4.git2.1.fc22.armv7hl.img |
| 111 | |
| 112 | label Fedora (3.17.0-0.rc4.git2.1.fc22.armv7hl+lpae) 22 (Rawhide) |
| 113 | kernel /boot/vmlinuz-3.17.0-0.rc4.git2.1.fc22.armv7hl+lpae |
| 114 | append ro root=UUID=8eac677f-8ea8-4270-8479-d5ddbb797450 console=ttyS0,115200n8 LANG=en_US.UTF-8 drm.debug=0xf |
| 115 | fdtdir /boot/dtb-3.17.0-0.rc4.git2.1.fc22.armv7hl+lpae |
| 116 | initrd /boot/initramfs-3.17.0-0.rc4.git2.1.fc22.armv7hl+lpae.img |
| 117 | |
| 118 | label Fedora-0-rescue-8f6ba7b039524e0eb957d2c9203f04bc (0-rescue-8f6ba7b039524e0eb957d2c9203f04bc) |
| 119 | kernel /boot/vmlinuz-0-rescue-8f6ba7b039524e0eb957d2c9203f04bc |
| 120 | initrd /boot/initramfs-0-rescue-8f6ba7b039524e0eb957d2c9203f04bc.img |
| 121 | append ro root=UUID=8eac677f-8ea8-4270-8479-d5ddbb797450 console=ttyS0,115200n8 |
| 122 | fdtdir /boot/dtb-3.16.0-0.rc6.git1.1.fc22.armv7hl+lpae |
| 123 | ------------------------------------------------------------ |
| 124 | |
| 125 | Another hand-crafted network boot configuration file is: |
| 126 | |
| 127 | ------------------------------------------------------------ |
| 128 | TIMEOUT 100 |
| 129 | |
| 130 | MENU TITLE TFTP boot options |
| 131 | |
| 132 | LABEL jetson-tk1-emmc |
| 133 | MENU LABEL ../zImage root on Jetson TK1 eMMC |
| 134 | LINUX ../zImage |
| 135 | FDTDIR ../ |
| 136 | APPEND console=ttyS0,115200n8 console=tty1 loglevel=8 rootwait rw earlyprintk root=PARTUUID=80a5a8e9-c744-491a-93c1-4f4194fd690b |
| 137 | |
| 138 | LABEL venice2-emmc |
| 139 | MENU LABEL ../zImage root on Venice2 eMMC |
| 140 | LINUX ../zImage |
| 141 | FDTDIR ../ |
| 142 | APPEND console=ttyS0,115200n8 console=tty1 loglevel=8 rootwait rw earlyprintk root=PARTUUID=5f71e06f-be08-48ed-b1ef-ee4800cc860f |
| 143 | |
| 144 | LABEL sdcard |
| 145 | MENU LABEL ../zImage, root on 2GB sdcard |
| 146 | LINUX ../zImage |
| 147 | FDTDIR ../ |
| 148 | APPEND console=ttyS0,115200n8 console=tty1 loglevel=8 rootwait rw earlyprintk root=PARTUUID=b2f82cda-2535-4779-b467-094a210fbae7 |
| 149 | |
| 150 | LABEL fedora-installer-fk |
| 151 | MENU LABEL Fedora installer w/ Fedora kernel |
| 152 | LINUX fedora-installer/vmlinuz |
| 153 | INITRD fedora-installer/initrd.img.orig |
| 154 | FDTDIR fedora-installer/dtb |
| 155 | APPEND loglevel=8 ip=dhcp inst.repo=http://10.0.0.2/mirrors/fedora/linux/development/rawhide/armhfp/os/ rd.shell cma=64M |
| 156 | ------------------------------------------------------------ |
| 157 | |
| 158 | U-Boot Implementation |
| 159 | ===================== |
| 160 | |
| 161 | Enabling the distro options |
| 162 | --------------------------- |
| 163 | |
Hans de Goede | 9f82361 | 2016-06-20 23:16:28 +0200 | [diff] [blame] | 164 | In your board's defconfig, enable the DISTRO_DEFAULTS option by adding |
| 165 | a line with "CONFIG_DISTRO_DEFAULTS=y". If you want to enable this |
| 166 | from Kconfig itself, for e.g. all boards using a specific SoC then |
Masahiro Yamada | 7325f6c | 2018-04-25 18:47:52 +0900 | [diff] [blame] | 167 | add a "imply DISTRO_DEFAULTS" to your SoC CONFIG option. |
Hans de Goede | 9f82361 | 2016-06-20 23:16:28 +0200 | [diff] [blame] | 168 | |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 169 | In your board configuration file, include the following: |
| 170 | |
| 171 | ------------------------------------------------------------ |
| 172 | #ifndef CONFIG_SPL_BUILD |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 173 | #include <config_distro_bootcmd.h> |
| 174 | #endif |
| 175 | ------------------------------------------------------------ |
| 176 | |
| 177 | The first of those headers primarily enables a core set of U-Boot features, |
| 178 | such as support for MBR and GPT partitions, ext* and FAT filesystems, booting |
| 179 | raw zImage and initrd (rather than FIT- or uImage-wrapped files), etc. Network |
| 180 | boot support is also enabled here, which is useful in order to boot distro |
| 181 | installers given that distros do not commonly distribute bootable install |
| 182 | media for non-PC targets at present. |
| 183 | |
| 184 | Finally, a few options that are mostly relevant only when using U-Boot- |
| 185 | specific boot.scr scripts are enabled. This enables distros to generate a |
| 186 | U-Boot-specific boot.scr script rather than extlinux.conf as the boot |
| 187 | configuration file. While doing so is fully supported, and |
Adam Ford | ba8bf94 | 2018-02-06 07:49:32 -0600 | [diff] [blame] | 188 | CONFIG_DISTRO_DEFAULTS exposes enough parameterization to boot.scr to |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 189 | allow for board-agnostic boot.scr content, this document recommends that |
| 190 | distros generate extlinux.conf rather than boot.scr. extlinux.conf is intended |
| 191 | to work across multiple bootloaders, whereas boot.scr will only work with |
| 192 | U-Boot. TODO: document the contract between U-Boot and boot.scr re: which |
| 193 | environment variables a generic boot.scr may rely upon. |
| 194 | |
| 195 | The second of those headers sets up the default environment so that $bootcmd |
| 196 | is defined in a way that searches attached disks for boot configuration files, |
| 197 | and executes them if found. |
| 198 | |
| 199 | Required Environment Variables |
| 200 | ------------------------------ |
| 201 | |
| 202 | The U-Boot "syslinux" and "pxe boot" commands require a number of environment |
| 203 | variables be set. Default values for these variables are often hard-coded into |
| 204 | CONFIG_EXTRA_ENV_SETTINGS in the board's U-Boot configuration file, so that |
| 205 | the user doesn't have to configure them. |
| 206 | |
| 207 | fdt_addr: |
| 208 | |
| 209 | Mandatory for any system that provides the DTB in HW (e.g. ROM) and wishes |
| 210 | to pass that DTB to Linux, rather than loading a DTB from the boot |
| 211 | filesystem. Prohibited for any other system. |
| 212 | |
| 213 | If specified a DTB to boot the system must be available at the given |
| 214 | address. |
| 215 | |
| 216 | fdt_addr_r: |
| 217 | |
| 218 | Mandatory. The location in RAM where the DTB will be loaded or copied to when |
| 219 | processing the fdtdir/devicetreedir or fdt/devicetree options in |
| 220 | extlinux.conf. |
| 221 | |
| 222 | This is mandatory even when fdt_addr is provided, since extlinux.conf must |
| 223 | always be able to provide a DTB which overrides any copy provided by the HW. |
| 224 | |
| 225 | A size of 1MB for the FDT/DTB seems reasonable. |
| 226 | |
| 227 | ramdisk_addr_r: |
| 228 | |
| 229 | Mandatory. The location in RAM where the initial ramdisk will be loaded to |
| 230 | when processing the initrd option in extlinux.conf. |
| 231 | |
| 232 | It is recommended that this location be highest in RAM out of fdt_addr_, |
| 233 | kernel_addr_r, and ramdisk_addr_r, so that the RAM disk can vary in size |
| 234 | and use any available RAM. |
| 235 | |
| 236 | kernel_addr_r: |
| 237 | |
| 238 | Mandatory. The location in RAM where the kernel will be loaded to when |
| 239 | processing the kernel option in the extlinux.conf. |
| 240 | |
| 241 | The kernel should be located within the first 128M of RAM in order for the |
| 242 | kernel CONFIG_AUTO_ZRELADDR option to work, which is likely enabled on any |
| 243 | distro kernel. Since the kernel will decompress itself to 0x8000 after the |
Masahiro Yamada | 28fd00b | 2015-07-07 18:47:17 +0900 | [diff] [blame] | 244 | start of RAM, kernel_addr_r should not overlap that area, or the kernel will |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 245 | have to copy itself somewhere else first before decompression. |
| 246 | |
| 247 | A size of 16MB for the kernel is likely adequate. |
| 248 | |
Vagrant Cascadian | 7101c4c | 2016-02-08 19:55:31 -0800 | [diff] [blame] | 249 | pxefile_addr_r: |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 250 | |
| 251 | Mandatory. The location in RAM where extlinux.conf will be loaded to prior |
| 252 | to processing. |
| 253 | |
| 254 | A size of 1MB for extlinux.conf is more than adequate. |
| 255 | |
| 256 | scriptaddr: |
| 257 | |
| 258 | Mandatory, if the boot script is boot.scr rather than extlinux.conf. The |
| 259 | location in RAM where boot.scr will be loaded to prior to execution. |
| 260 | |
| 261 | A size of 1MB for extlinux.conf is more than adequate. |
| 262 | |
| 263 | For suggestions on memory locations for ARM systems, you must follow the |
| 264 | guidelines specified in Documentation/arm/Booting in the Linux kernel tree. |
| 265 | |
| 266 | For a commented example of setting these values, please see the definition of |
| 267 | MEM_LAYOUT_ENV_SETTINGS in include/configs/tegra124-common.h. |
| 268 | |
| 269 | Boot Target Configuration |
| 270 | ------------------------- |
| 271 | |
| 272 | <config_distro_bootcmd.h> defines $bootcmd and many helper command variables |
| 273 | that automatically search attached disks for boot configuration files and |
| 274 | execute them. Boards must provide configure <config_distro_bootcmd.h> so that |
| 275 | it supports the correct set of possible boot device types. To provide this |
| 276 | configuration, simply define macro BOOT_TARGET_DEVICES prior to including |
| 277 | <config_distro_bootcmd.h>. For example: |
| 278 | |
| 279 | ------------------------------------------------------------ |
| 280 | #ifndef CONFIG_SPL_BUILD |
| 281 | #define BOOT_TARGET_DEVICES(func) \ |
| 282 | func(MMC, mmc, 1) \ |
| 283 | func(MMC, mmc, 0) \ |
| 284 | func(USB, usb, 0) \ |
| 285 | func(PXE, pxe, na) \ |
| 286 | func(DHCP, dhcp, na) |
| 287 | #include <config_distro_bootcmd.h> |
| 288 | #endif |
| 289 | ------------------------------------------------------------ |
| 290 | |
| 291 | Each entry in the macro defines a single boot device (e.g. a specific eMMC |
| 292 | device or SD card) or type of boot device (e.g. USB disk). The parameters to |
| 293 | the func macro (passed in by the internal implementation of the header) are: |
| 294 | |
Lukas Auer | a8da9ff | 2018-11-22 11:26:33 +0100 | [diff] [blame] | 295 | - Upper-case disk type (MMC, SATA, SCSI, IDE, USB, DHCP, PXE, VIRTIO). |
Dennis Gilmore | ffb4f6f | 2015-01-22 11:34:20 -0700 | [diff] [blame] | 296 | - Lower-case disk type (same options as above). |
| 297 | - ID of the specific disk (MMC only) or ignored for other types. |
| 298 | |
| 299 | User Configuration |
| 300 | ================== |
| 301 | |
| 302 | Once the user has installed U-Boot, it is expected that the environment will |
| 303 | be reset to the default values in order to enable $bootcmd and friends, as set |
| 304 | up by <config_distro_bootcmd.h>. After this, various environment variables may |
| 305 | be altered to influence the boot process: |
| 306 | |
| 307 | boot_targets: |
| 308 | |
| 309 | The list of boot locations searched. |
| 310 | |
| 311 | Example: mmc0, mmc1, usb, pxe |
| 312 | |
| 313 | Entries may be removed or re-ordered in this list to affect the boot order. |
| 314 | |
| 315 | boot_prefixes: |
| 316 | |
| 317 | For disk-based booting, the list of directories within a partition that are |
| 318 | searched for boot configuration files (extlinux.conf, boot.scr). |
| 319 | |
| 320 | Example: / /boot/ |
| 321 | |
| 322 | Entries may be removed or re-ordered in this list to affect the set of |
| 323 | directories which are searched. |
| 324 | |
| 325 | boot_scripts: |
| 326 | |
| 327 | The name of U-Boot style boot.scr files that $bootcmd searches for. |
| 328 | |
| 329 | Example: boot.scr.uimg boot.scr |
| 330 | |
| 331 | (Typically we expect extlinux.conf to be used, but execution of boot.scr is |
| 332 | maintained for backwards-compatibility.) |
| 333 | |
| 334 | Entries may be removed or re-ordered in this list to affect the set of |
| 335 | filenames which are supported. |
| 336 | |
| 337 | scan_dev_for_extlinux: |
| 338 | |
| 339 | If you want to disable extlinux.conf on all disks, set the value to something |
| 340 | innocuous, e.g. setenv scan_dev_for_extlinux true. |
| 341 | |
| 342 | scan_dev_for_scripts: |
| 343 | |
| 344 | If you want to disable boot.scr on all disks, set the value to something |
| 345 | innocuous, e.g. setenv scan_dev_for_scripts true. |
Karsten Merker | 8c24929 | 2015-03-21 14:15:38 +0100 | [diff] [blame] | 346 | |
Stephen Warren | 3483b75 | 2016-01-26 11:10:12 -0700 | [diff] [blame] | 347 | boot_net_usb_start: |
| 348 | |
| 349 | If you want to prevent USB enumeration by distro boot commands which execute |
| 350 | network operations, set the value to something innocuous, e.g. setenv |
| 351 | boot_net_usb_start true. This would be useful if you know your Ethernet |
| 352 | device is not attached to USB, and you wish to increase boot speed by |
| 353 | avoiding unnecessary actions. |
Karsten Merker | 8c24929 | 2015-03-21 14:15:38 +0100 | [diff] [blame] | 354 | |
Stephen Warren | 986691f | 2016-01-26 11:10:13 -0700 | [diff] [blame] | 355 | boot_net_pci_enum: |
| 356 | |
| 357 | If you want to prevent PCI enumeration by distro boot commands which execute |
| 358 | network operations, set the value to something innocuous, e.g. setenv |
| 359 | boot_net_pci_enum true. This would be useful if you know your Ethernet |
| 360 | device is not attached to PCI, and you wish to increase boot speed by |
| 361 | avoiding unnecessary actions. |
| 362 | |
Karsten Merker | 8c24929 | 2015-03-21 14:15:38 +0100 | [diff] [blame] | 363 | Interactively booting from a specific device at the u-boot prompt |
| 364 | ================================================================= |
| 365 | |
| 366 | For interactively booting from a user-selected device at the u-boot command |
| 367 | prompt, the environment provides predefined bootcmd_<target> variables for |
| 368 | every target defined in boot_targets, which can be run be the user. |
| 369 | |
| 370 | If the target is a storage device, the format of the target is always |
| 371 | <device type><device number>, e.g. mmc0. Specifying the device number is |
| 372 | mandatory for storage devices, even if only support for a single instance |
| 373 | of the storage device is actually implemented. |
| 374 | |
| 375 | For network targets (dhcp, pxe), only the device type gets specified; |
| 376 | they do not have a device number. |
| 377 | |
| 378 | Examples: |
| 379 | |
| 380 | - run bootcmd_usb0 |
| 381 | boots from the first USB mass storage device |
| 382 | |
| 383 | - run bootcmd_mmc1 |
| 384 | boots from the second MMC device |
| 385 | |
| 386 | - run bootcmd_pxe |
| 387 | boots by tftp using a pxelinux.cfg |
| 388 | |
| 389 | The list of possible targets consists of: |
| 390 | |
| 391 | - network targets |
| 392 | * dhcp |
| 393 | * pxe |
| 394 | |
| 395 | - storage targets (to which a device number must be appended) |
| 396 | * mmc |
| 397 | * sata |
| 398 | * scsi |
| 399 | * ide |
| 400 | * usb |
Lukas Auer | a8da9ff | 2018-11-22 11:26:33 +0100 | [diff] [blame] | 401 | * virtio |
Karsten Merker | 8c24929 | 2015-03-21 14:15:38 +0100 | [diff] [blame] | 402 | |
| 403 | Other *boot* variables than the ones defined above are only for internal use |
| 404 | of the boot environment and are not guaranteed to exist or work in the same |
| 405 | way in future u-boot versions. In particular the <device type>_boot |
| 406 | variables (e.g. mmc_boot, usb_boot) are a strictly internal implementation |
| 407 | detail and must not be used as a public interface. |