Boot Time Optimization

1. Introduction

1.1 Purpose

This document describes how to reduce the boot time for the Genio series EVK using Yocto Linux. It is applicable to the following platforms:

  • Genio-510

  • Genio-700

  • Genio-1200

The objectives of this document include:

  • Bootloader optimization

  • Linux kernel optimization

1.2 Software environment

‘Tera Term’ is used to receive the UART log with timestamp to measure the boot time. Yocto Linux BSP release v23.2 is used in this document.

1.3 Hardware equipment

This document uses the following EVK to demonstration and measurement.

  • Genio 510-EVK

2. General description

This section provides a summary of the common changes needed to reduce boot times.

2.1 Reduce the bootloader boot time

  • Remove the U-Boot autoboot prompt can save two seconds over the default configuration with only minor modifications, as it bypasses the pause waiting for a key press during the boot process.

  • Merge the kernel DTBO into main DTS to reduce the U-Boot DTBO loading time.

2.2 Reduce the Linux kernel boot time

  • Reduce log

  • Slim down the kernel image

3. Prerequisites

First, we should build the Yocto image. Please refer to the following link.

https://mediatek.gitlab.io/aiot/doc/aiot-dev-guide/master/sw/yocto/get-started/build-code.html

This document describes how to optimize the boot time base on rity-bsp-image. Compared to demo-image, bsp-image does not include various demo apps, resulting in a smaller image size. Therefore, use the following command to build the image:

MACHINE=genio-510-evk bitbake rity-bsp-image

Flash image:

genio-flash --load-dtbo gpu-mali.dtbo --load-dtbo apusys.dtbo --load-dtbo video.dtbo

4. Measurements

This section describes how to use the open-source software ‘Tera Term’ to capture UART logs with timestamps from the EVK in order to measure the boot time of each stage.

4.1 Tool

You can download Tera Term from the GitHub page:

https://teratermproject.github.io/index-en.html

4.2 Obtain Log

To begin, configure the serial port as follows:

SetupSerial Port...

../_images/boot_time_optimization_teraterm1.png

Enable the feature to save logs to a file, including a timestamp:

FileLog...

../_images/boot_time_optimization_teraterm2.png

Then, power on the EVK, and you will see the logs on the Tera Term. After booting to the Linux login prompt, close Tera Term.

genio-510-evk login:

Open the log file, you will see logs with timestamp similar to the following

[2024-05-07 09:59:06.612] NOTICE:  BL2: v2.6(release):rity-kirkstone-v23.0-68-g8f34d053e0-dirty
[2024-05-07 09:59:06.612] NOTICE:  BL2: Built : 05:57:46, Feb 21 2024
[2024-05-07 09:59:06.612] NOTICE:  WDT: Status = 0x0
[2024-05-07 09:59:06.612] NOTICE:  WDT: Last reset was cold boot

Important

Please use the logs from the second boot onwards, because the first boot will take a longer time to perform DRAM calibration and save the calibration data to storage.

4.3 Log point of each stage

Stage

Log From

To

BL2

NOTICE:  BL2: Built :

mcupm_init

BL31+BL32

mcupm_init

<debug_uart>

BL33

<debug_uart>

Starting kernel

Kernel mount rootfs

Starting kernel

VFS: Mounted root

Userspace login

VFS: Mounted root

login:

Example:

[2024-05-07 09:59:06.612] NOTICE:  BL2: Built : 05:57:46, Feb 21 2024
[2024-05-07 09:59:07.471] NOTICE:  MT8188 mcupm_init
[2024-05-07 09:59:08.143] <debug_uart>
[2024-05-07 09:59:11.815] Starting kernel ...
[2024-05-07 09:59:13.408] [    1.339589] VFS: Mounted root (ext4 filesystem) readonly on device 179:10.
[2024-05-07 09:59:19.674] genio-510-evk login:

Stage

From

To

Duration

BL2

09:59:06.612

09:59:07.471

0.859

BL31+BL32

09:59:07.471

09:59:08.143

0.672

BL33

09:59:08.143

09:59:11.815

3.672

Kernel mount rootfs

09:59:11.815

09:59:13.408

1.593

Userspace login

09:59:13.408

09:59:19.674

6.266

Note

The table you measure may not be the same as the one above, because many factors can affect the timing, such as the speed of the eMMC which will affect the boot time, and the UART-USB dongle which will affect the time when the logs are received, and so on.

4.4 Script for Parsing Log

You can use the following python script to parse the log.

import sys
import re
from datetime import datetime

log_markers = {
    "NOTICE:  BL2: Built :" : "BROM",
    "mcupm_init" : "BL2",
    "<debug_uart>" : "BL31",
    "Starting kernel" : "BL33 U-Boot",
    "VFS: Mounted root" : "Kernel mount rootfs",
    "login:" : "Userspace login",
}

# Function to parse the timestamp from a log line
def parse_timestamp(log_line):
    timestamp_pattern = r'\[(.*?)\]'
    match = re.search(timestamp_pattern, log_line)
    if match:
        timestamp_str = match.group(1)
        try:
            timestamp = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S.%f')
        except:
            timestamp = datetime.strptime(timestamp_str, '%H:%M:%S.%f')
        return timestamp
    return None

# Check if the script has the correct number of arguments
if len(sys.argv) != 2:
    print(f"Usage: python {sys.argv[0]} <log_file>")
    sys.exit(1)

log_file_path = sys.argv[1]

try:
    with open(log_file_path, 'r', errors='ignore') as log_file:
        log_lines = log_file.readlines()

    # Parse the first timestamp to use as a reference
    first_timestamp = None
    for line in log_lines:
        if any(marker in line for marker in log_markers):
            first_timestamp = parse_timestamp(line)
            if first_timestamp:
                break

    if not first_timestamp:
        print("Error: No valid timestamp found in the log lines with specified markers.")
        sys.exit(1)

    # Process each log line and calculate the time difference
    pre_ts = first_timestamp
    print("elapsed  spend boot stage")
    for line in log_lines:
        for marker, stage_string in log_markers.items():
            if marker in line:
                current_timestamp = parse_timestamp(line)
                if current_timestamp:
                    only_log = re.sub(r'\[(.*?)\]', "", line)
                    only_log = only_log.strip()
                    time_diff = (current_timestamp - first_timestamp).total_seconds()
                    time_diff2 = (current_timestamp - pre_ts).total_seconds()
                    print(f"{time_diff:>7.3f} {time_diff2:>6.3f} {stage_string}")
                pre_ts = current_timestamp

except FileNotFoundError:
    print(f"Error: The file {log_file_path} does not exist.")
    sys.exit(1)
except Exception as e:
    print(f"An error occurred: {e}")
    sys.exit(1)

It will print the elapsed boot time and the duration of each boot stage as follows:

elapsed  spend boot stage
  0.000  0.000 BROM
  0.859  0.859 BL2
  1.531  0.672 BL31
  5.203  3.672 BL33 U-Boot
  6.796  1.593 Kernel mount rootfs
 13.062  6.266 Userspace login

5. Bootloader boot time optimization

5.1 Remove the U-Boot autoboot prompt

U-Boot features a two-second countdown prompt that allows the user to interrupt the autoboot process and enter the U-Boot shell. If this is unnecessary, the user can disable it. There are two methods that can be used to remove the autoboot prompt.

  1. For temporary verification, you can set the bootdelay environment variable in the U-Boot shell.

  2. For permanent modification, modify the U-Boot defconfig file.

5.1.1 Set bootdelay

Enter the U-Boot shell and execute the following commands.

setenv bootdelay 0
saveenv
reset

5.1.2 Modify defconfig

Obtain U-Boot source code.

$ devtool modify u-boot
$ cd build/workspace/u-boot

Add bootdelay config:

CONFIG_BOOTDELAY=0

to the U-Boot defconfig:

configs/genio_510_evk_defconfig
configs/genio_700_evk_defconfig
configs/genio_1200_evk_defconfig

Rebuild the rity-bsp-image and flash it onto the device.

5.2 Merge the kernel DTO

Merging Device Tree Overlay (DTO) into the main DTS can reduce the time U-Boot takes to read DTBO (Device Tree Blob Overlay). To merge a DTO into the main Device Tree Source (DTS) file, you need to manually copy the nodes and properties from the overlay into the corresponding locations in the main DTS file. This process typically involves the following steps:

  • Open the main DTS file.

  • Locate the target node in the main DTS file that corresponds to the target-path specified in the overlay.

  • Copy all the contents under the __overlay__ node from the overlay into the target node in the main DTS file.

  • If the overlay includes new node definitions, add these nodes to the appropriate locations in the main DTS file.

  • If the overlay modifies existing properties, update the corresponding property values in the main DTS file.

  • Save the main DTS file and rebuild image.

Here is an example showing how to merge an overlay into the main DTS file: Suppose you have the following overlay (fragment.dtsi):

fragment@0 {
    target-path = "/soc";
    __overlay__ {
        gpu: mali@13000000 {
            compatible = "mediatek,mt8188-mali", "arm,mali-midgard";
            reg = <0 0x13000000 0 0x4000>;
        };
    };
};

You need to merge it into the main DTS file (main.dts), which has the following node:

// main.dts
/ {
    // Other nodes and properties
    soc {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges;

        // Other nodes and properties
    };
};

After merging, the main DTS file should look like this:

// main.dts
/ {
    // Other nodes and properties
    soc {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges;

        // Other nodes and properties

        gpu: mali@13000000 {
            compatible = "mediatek,mt8188-mali", "arm,mali-midgard";
            reg = <0 0x13000000 0 0x4000>;
        };
    };
};

In this tutorial example, we merge the following DTS into mt8370.dtsi. It will reduce about 150ms boot time.

  • src/meta-mediatek-bsp/recipes-kernel/dtbo/mt8370/gpu-mali.dts

  • src/meta-mediatek-bsp/recipes-kernel/dtbo/mt8370/apusys.dts

  • src/meta-mediatek-bsp/recipes-kernel/dtbo/mt8370/video.dts

6. Kernel boot time optimizations

6.1 Reduce log output

During the kernel bootup procedure, a significant number of logs are generated. However, printing these logs can consume a considerable amount of time. Reducing the number of logs will also decrease the boot time. By adding ‘quiet’ to bootargs, you can reduce the number of logs output by the kernel. There are two methods that can be used to quiet to bootargs.

  1. For temporary verification, you can set the bootargs environment variable in the U-Boot shell.

  2. For permanent modification, modify the U-Boot defconfig file.

6.1.1 Set bootargs

Enter the U-Boot shell and execute the following commands.

setenv bootargs 'quiet'
saveenv
reset

6.1.2 Modify defconfig

Obtain U-Boot source code.

$ devtool modify u-boot
$ cd build/workspace/u-boot

Add bootargs config:

CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="quiet"

to the U-Boot defconfig:

configs/genio_510_evk_defconfig
configs/genio_700_evk_defconfig
configs/genio_1200_evk_defconfig

Rebuild the rity-bsp-image and flash it onto the device.

6.2 Remove the unnecessary drivers and file systems

The standard Linux kernel may include many features and drivers that are not needed for specific systems. These unnecessary components can increase the size of the kernel and prolong the boot time. Reducing the size of the kernel can speed up the time it takes for U-Boot to load the kernel image, and removing unneeded features and drivers can accelerate the boot speed of the kernel.

This application note uses the following configuration as an example to disable some kernel features. Append the following configuration to recipes-kernel/linux/linux-mtk/mt8370-evk.cfg.

# CONFIG_RD_GZIP is not set
# CONFIG_RD_BZIP2 is not set
# CONFIG_RD_LZMA is not set
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
# CONFIG_RD_LZ4 is not set
# CONFIG_RD_ZSTD is not set
# CONFIG_PERF_EVENTS is not set

# CONFIG_ARCH_ACTIONS is not set
# CONFIG_ARCH_SUNXI is not set
# CONFIG_ARCH_ALPINE is not set
# CONFIG_ARCH_APPLE is not set
# CONFIG_ARCH_BCM2835 is not set
# CONFIG_ARCH_BCM4908 is not set
# CONFIG_ARCH_BCM_IPROC is not set
# CONFIG_ARCH_BERLIN is not set
# CONFIG_ARCH_BITMAIN is not set
# CONFIG_ARCH_BRCMSTB is not set
# CONFIG_ARCH_EXYNOS is not set
# CONFIG_ARCH_SPARX5 is not set
# CONFIG_ARCH_K3 is not set
# CONFIG_ARCH_LAYERSCAPE is not set
# CONFIG_ARCH_LG1K is not set
# CONFIG_ARCH_HISI is not set
# CONFIG_ARCH_KEEMBAY is not set
# CONFIG_ARCH_MESON is not set
# CONFIG_ARCH_MVEBU is not set
# CONFIG_ARCH_MXC is not set
# CONFIG_ARCH_QCOM is not set
# CONFIG_ARCH_REALTEK is not set
# CONFIG_ARCH_RENESAS is not set
# CONFIG_ARCH_ROCKCHIP is not set
# CONFIG_ARCH_S32 is not set
# CONFIG_ARCH_SEATTLE is not set
# CONFIG_ARCH_INTEL_SOCFPGA is not set
# CONFIG_ARCH_SYNQUACER is not set
# CONFIG_ARCH_SPRD is not set
CONFIG_ARCH_TEGRA=m
# CONFIG_ARCH_THUNDER is not set
# CONFIG_ARCH_THUNDER2 is not set
# CONFIG_ARCH_UNIPHIER is not set
# CONFIG_ARCH_VEXPRESS is not set
# CONFIG_ARCH_VISCONTI is not set
# CONFIG_ARCH_XGENE is not set
# CONFIG_ARCH_ZYNQMP is not set
# CONFIG_VIRTUALIZATION is not set
# CONFIG_MTD_RAW_NAND is not set
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_NET_VENDOR_ADAPTEC is not set
# CONFIG_NET_VENDOR_AGERE is not set
# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_ALTEON is not set
# CONFIG_ALTERA_TSE is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_AMD is not set
# CONFIG_NET_VENDOR_AQUANTIA is not set
# CONFIG_NET_VENDOR_ARC is not set
# CONFIG_NET_VENDOR_ATHEROS is not set
# CONFIG_NET_VENDOR_BROADCOM is not set
# CONFIG_NET_VENDOR_CADENCE is not set
# CONFIG_NET_VENDOR_CAVIUM is not set
# CONFIG_NET_VENDOR_CHELSIO is not set
# CONFIG_NET_VENDOR_CISCO is not set
# CONFIG_NET_VENDOR_CORTINA is not set
# CONFIG_DNET is not set
# CONFIG_NET_VENDOR_DEC is not set
# CONFIG_NET_VENDOR_DLINK is not set
# CONFIG_NET_VENDOR_EMULEX is not set
# CONFIG_NET_VENDOR_EZCHIP is not set
# CONFIG_NET_VENDOR_GOOGLE is not set
# CONFIG_NET_VENDOR_HISILICON is not set
# CONFIG_NET_VENDOR_HUAWEI is not set
# CONFIG_NET_VENDOR_INTEL is not set
# CONFIG_JME is not set
# CONFIG_NET_VENDOR_LITEX is not set
# CONFIG_NET_VENDOR_MARVELL is not set
# CONFIG_NET_VENDOR_MEDIATEK is not set
# CONFIG_NET_VENDOR_MELLANOX is not set
# CONFIG_NET_VENDOR_MICREL is not set
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_NET_VENDOR_MICROSEMI is not set
# CONFIG_NET_VENDOR_MICROSOFT is not set
# CONFIG_NET_VENDOR_MYRI is not set
# CONFIG_FEALNX is not set
# CONFIG_NET_VENDOR_NI is not set
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_NETERION is not set
# CONFIG_NET_VENDOR_NETRONOME is not set
# CONFIG_NET_VENDOR_NVIDIA is not set
# CONFIG_NET_VENDOR_OKI is not set
# CONFIG_ETHOC is not set
# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
# CONFIG_NET_VENDOR_PENSANDO is not set
# CONFIG_NET_VENDOR_QLOGIC is not set
# CONFIG_NET_VENDOR_BROCADE is not set
# CONFIG_NET_VENDOR_QUALCOMM is not set
# CONFIG_NET_VENDOR_RDC is not set
# CONFIG_NET_VENDOR_REALTEK is not set
# CONFIG_NET_VENDOR_RENESAS is not set
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SILAN is not set
# CONFIG_NET_VENDOR_SIS is not set
# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_SMSC is not set
# CONFIG_NET_VENDOR_SOCIONEXT is not set
# CONFIG_NET_VENDOR_SUN is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_TEHUTI is not set
# CONFIG_NET_VENDOR_TI is not set
# CONFIG_NET_VENDOR_VIA is not set
# CONFIG_NET_VENDOR_WIZNET is not set
# CONFIG_NET_VENDOR_XILINX is not set
# CONFIG_WLAN_VENDOR_ADMTEK is not set
# CONFIG_WLAN_VENDOR_ATH is not set
# CONFIG_WLAN_VENDOR_ATMEL is not set
# CONFIG_WLAN_VENDOR_BROADCOM is not set
# CONFIG_WLAN_VENDOR_CISCO is not set
# CONFIG_WLAN_VENDOR_INTEL is not set
# CONFIG_WLAN_VENDOR_INTERSIL is not set
# CONFIG_WLAN_VENDOR_MARVELL is not set
# CONFIG_WLAN_VENDOR_MICROCHIP is not set
# CONFIG_WLAN_VENDOR_RALINK is not set
# CONFIG_WLAN_VENDOR_REALTEK is not set
# CONFIG_WLAN_VENDOR_RSI is not set
# CONFIG_WLAN_VENDOR_ST is not set
# CONFIG_WLAN_VENDOR_TI is not set
# CONFIG_WLAN_VENDOR_ZYDAS is not set
# CONFIG_WLAN_VENDOR_QUANTENNA is not set
# CONFIG_SERIO is not set
# CONFIG_USB_SERIAL is not set
# CONFIG_NEW_LEDS is not set
# CONFIG_LEDS_CLASS is not set
# CONFIG_SCSI_LOWLEVEL is not set
# CONFIG_ATA is not set
# CONFIG_MD is not set
# CONFIG_VFIO is not set
# CONFIG_VIRTIO_MENU is not set
# CONFIG_VHOST_MENU is not set
# CONFIG_HYPERV is not set
# CONFIG_STAGING is not set
# CONFIG_CHROME_PLATFORMS is not set
# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
# CONFIG_IIO is not set
# CONFIG_FPGA is not set
# CONFIG_PHY_XGENE is not set
# CONFIG_PHY_MIXEL_MIPI_DPHY is not set
# CONFIG_PHY_QCOM_USB_HS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_FUSE_FS is not set
# CONFIG_FAT_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_HUGETLBFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_NETWORK_FILESYSTEMS is not set
# CONFIG_MAGIC_SYSRQ is not set
# CONFIG_UBSAN is not set
# CONFIG_DEBUG_KERNEL is not set
# CONFIG_FTRACE is not set
# CONFIG_RUNTIME_TESTING_MENU is not set

7. Comparison

This section presents a boot time comparison between the original version and the optimized version.

Since the ‘quiet’ boot argument for the kernel disables almost all kernel logs, including our measurement point logs, we have made the following modifications of kernel to ensure that the ‘VFS: Mounted root’ log is printed.

Obtain kernel source code.

$ devtool modify linux-mtk
$ cd build/workspace/linux-mtk
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 762b534978d9..8241ebc958bd 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -380,7 +380,7 @@ static int __init do_mount_root(const char *name, const char *fs,
        init_chdir("/root");
        s = current->fs->pwd.dentry->d_sb;
        ROOT_DEV = s->s_dev;
-       printk(KERN_INFO
+       printk(KERN_ALERT
            "VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",
            s->s_type->name,
            sb_rdonly(s) ? " readonly" : "",

The boot time of the optimized version is as follows:

elapsed  spend boot stage
  0.000  0.000 BROM
  0.852  0.852 BL2
  1.574  0.722 BL31
  2.964  1.390 BL33 U-Boot
  3.837  0.873 Kernel mount rootfs
  7.096  3.259 Userspace login

The boot time comparison table is as follows:

Stage

original

optimized

spend

elapsed

spend

elapsed

BL2

0.859

0.859

0.852

0.852

BL31+BL32

0.672

1.531

0.722

1.574

BL33

3.672

5.203

1.390

2.964

Kernel mount rootfs

1.593

6.796

0.873

3.837

Userspace login

6.266

13.062

3.259

7.096