How To Update U-Boot For PostmarketOS On The Pine Phone


If you are in a hurry to update U-Boot and the SPL on your PinePhone, then please proceed directly to Write U-Boot+SPL to bootable storage.

In this article, I am going to explain what U-Boot, SoC and the SPL are. After that, I will describe the sunxi bootable storage layout as well as the PinePhone boot procedure, so you will understand what you will be updating and why. Then, I will teach you how to determine if an upgrade is required, and I will explain two different ways of upgrading U-Boot. As a special treat for the curious, I will show you the first steps to reverse engineer the U-Boot+SPL firmware blob. I hope this article peeks your curiosity and encourages you to learn more.

Discussed on Hacker News and Pine64.

What is U-Boot?

U-Boot, or rather Das U-Boot a.k.a the Universal Boot Loader, is a small program that is loaded into read-only memory (ROM) and is ultimately responsible for loading the Linux kernel. Designed with flexibility in mind, U-Boot now supports a wide variety of architectures for embedded boards, each of which may support multiple boot methods. This article is only concerned with U-Boot as it is configured for the Allwinner 64-bit boards, specifically the Allwinner A64 SoC.

What is a SoC?

No, it does not refer to the stinky fabric covering your feet. SoC stands for System on a Chip. The PinePhone contains the Allwinner A64 SoC, featuring a Quad-Core ARM Cortex-A53 ARMv8-A CPU and an ARM Mali400 MP2 GPU. See the Allwinner A64 documentation for more details.

What is the SPL?

The Secondary Program Loader’s (SPL) primary function is to load U-Boot proper, the flattened device tree (FDT) and the Arm Trusted Firmware (ATF), ultimately passing execution to the ATF. In particular, execution is passed to Trusted Firmware-A (TF-A), which is the official reference implementation used by SoCs with armv8- cores, such as Allwinner Armv8-A SoCs.

What installs the SPL?

The SPL is installed via the u-boot-pinephone package from the postmarketOS aarch64 APK repository. The package is built from the pine64 u-boot fork in which they added a pine64 specific GitLab CI/CD pipeline configuration. By listing the contents of the package using the apk info command we can see where the SPL binary is actually installed to the root file system.

second-chance:~$ apk info -L u-boot-pinephone
u-boot-pinephone-2020.04_git20200421-r1 contains:

However, this is just a convenient location to deliver the binary. The SPL must be deployed to a specific location on disk so that the BootROM can load it.

A bit about bytes

I suspect that not all of those reading this article are familiar with the various standards when it comes to measuring information. Allow me to digress with a brief introduction to these standards with respect to how they both measure and represent a kilobyte. Many of the articles that I have linked herein use the Joint Electron Device Engineering Council (JEDEC) memory standards in which the unit for kilobyte is denoted by (KB), in upper case letters and represents 1024B. This is not to be confused with the kilobyte from the International System of Quantities (SI) in which kilo is denoted with a lower case k, such that kB means 1000B. My preference is to use the kibibyte (pron. KI-BEE-BYTE), which was established by the International Electrotechnical commission (IEC) and is recognized by all major standards organizations, including those aforementioned.

Decimal Binary
Value Metric Value IEC JEDEC
1 B byte 1 B byte B byte
1000 kB kilobyte 1024 KiB kibibyte KB kilobyte
1000^2 MB megabyte 1024^2 MiB mebibyte MB megabyte
1000^3 GB gigabyte 1024^3 GiB gibibyte GB gigabyte
1000^4 TB terabyte 1024^4 TiB tebibyte -

The reasoning behind my preference is two fold:

  1. The JEDEC Terms, Definitions, and Letter Symbols for Microcomputers, Microprocessors, and Memory Integrated Circuits only defines the first three higher order prefixes: kilo (K), mega (M), giga (G), referring to them for common usage. The prefix tera was later added to the JEDEC terms dictionary to reflect common prefix usage for modern semiconductor storage capacity.
  2. IEC prefixes cannot be confused with Metric prefixes.

To make matters more confusing, sometimes lowercase k is used to mean 1024, e.g. see tar(1) OPTIONS sub section Size Suffixes located above the RETURN VALUE section. Understanding which system of measurement is being used is essential when calculating offsets.

Layout of sunxi bootable storage

The first 40 plus KiB of bootable storage for an Allwinner based board has the following default layout:

Start Size Usage
0KiB 8KiB Reserved for optional MBR or GPT
8KiB 32KiB Initial SPL
40KiB - U-Boot Proper

From the layout, one can conclude that upgrading the SPL and U-Boot for the PinePhone must involve writing the u-boot-sunxi-with-spl.bin to bootable storage starting at 8192B.

PinePhone boot procedure

Bootstrapping is complicated by initial memory address space limitations. The SPL is limited to 32 KiB, most likely because the BootROM, or BROM, loads the SPL into SRAM A1, which is a 32 KiB subsection. If the SPL is larger than 32 KiB the BROM will refuse to load it. After the SPL loads U-Boot proper and passes execution to the ATF, U-Boot proper in turn runs the Pine Phone’s u-boot command script. The command script sets the default bootargs for init and calls the booti command, which boots the Linux Kernel Image from memory given the flattend device tree (FDT) and the initial ramdisk (initrd), ultimately passing execution to Linux init.

|        BootROM        |
|     u-boot.itb+SPL    |
|       TF-A BL31       |
| U-Boot Proper (=BL33) |
|        Linux          |

You might have noticed that /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin is much larger than 32KiB.

second-chance:~$ ls -lh /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin
-rw-r--r--    1 root     root      486.0K Jun 20 12:41 /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin

That is because the SPL binary image includes a Flattened uImage Tree (FIT image) named u-boot.itb that contains the rest of the firmware.

Determine which bootable storage device is relevant

Before you can determine if U-Boot needs to be upgraded, you need to know which storage device your PinePhone is booting from. This can be easily determined by using the lsblk(8) command to list the running operating system’s current mount points. Below is the output of lsblk(8) run on my PinePhone booted from an SD card:

second-chance:~$ lsblk --output NAME,TYPE,MOUNTPOINT
mmcblk0      disk
├─mmcblk0p1  part /boot
└─mmcblk0p2  part
mmcblk2      disk
├─mmcblk2p1  part
├─mmcblk2p2  part
├─mmcblk2p1  part
└─mmcblk2p2  part
mmcblk2boot0 disk
mmcblk2boot1 disk

The disk corresponding to the /boot mountpoint is the name of the block special device that postmarketOS is currently running form. The device path to the relevant boot storage device is therefore /dev/mmcblk0. We will be using this device name in the next two sections to determine if an upgrade is needed and again to perform the actual upgrade if warranted. You must be careful to use the device name that is relevant to your own running environment if you are following along.

How to determine if U-Boot needs to be upgraded?

You can determine if an upgrade is necessary simply by comparing the version of U-Boot installed by the u-boot-pinephone package with the version of U-Boot that is written to the bootable storage device which is relevant to your running environment.

To see which version of U-Boot was installed by the u-boot-pinephone package, simply run the apk policy sub command as shown below:

second-chance:~/$ apk policy u-boot-pinephone
u-boot-pinephone policy:

Alternatively, you can use the busybox(1) strings command to search the binary’s printable strings for the regex pattern U-Boot [[:digit:]] by piping the output through a busybox(1) grep filter. As a side note, the PinePhone uses busybox, so when you find yourself looking up command line documentation with the intention of running the command from a PinePhone shell, always check the busybox(1) man pages first.

second-chance:~/packages$ strings /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin | grep -E 'U-Boot [[:digit:]]'
U-Boot 2020.04 (Jun 20 2020 - 12:41:48 +0000)

Similarly, to determine the version of U-Boot that is currently written to bootable storage, you can search for the same regex pattern in the printable strings of the boot disk after the first 8 KiB. However, since the bootable storage is significantly larger than u-boot-sunxi-with-spl.bin, it would not be efficient to use the strings command as we did previously. Instead, we will use the busybox(1) dd command, which will allow us to control where to begin and end the search. Since we can’t easily know the exact offset of the version string, which can very from build to build, my strategy has been to simply skip the first 8 KiB and then read the same number of KiB as the size of the currently installed u-boot-sunxi-with-spl.bin. If my search turns up nothing, then that means that the previously installed version was larger, and I can simply increase the count to some reasonable number of KiB until I find what I am looking for.

First, let’s determine the size of u-boot-sunxi-with-spl.bin.

ls -lh /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin
-rw-r--r--    1 root     root      543.3K Jul 18  2020 /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin

The binary installed to disk is about 543 KiB. I will use the busybox(1) dd command to write 543 1 KiB blocks of data to standard output and then pipe that through a grep filter to search for the U-Boot version string.

second-chance:~$ sudo dd if=/dev/mmcblk0 bs=1024 skip=8 count=543 | grep -E 'U-Boot [[:digit:]]'
U-Boot 2020.04-rc3 (Mar 18 2020 - 13:16:10 +0000)
543+0 records in
543+0 records out

In case you are not confident using the dd command, here is a breakdown of the optional flags that I used in the above:

option description
if input file path. In our case it is the path to the bootable storage device.
bs block size, or number of bytes per count. Not to be confused with disk blocksize or a rude expletive. It is purposely set to 1024B so that each count represents 1 KiB.
skip number of input blocks to skip. We know from Layout of sunxi bootable storage that we can ignore the first 8KiB.
count number of input blocks to parse. In our case, dd just sends those blocks to stdout.

Finally, since the version of U-Boot compiled into /usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin is newer than the the version written to bootable storage /dev/mmcblk0, I know that it is time to upgrade U-Boot.

Name Version
u-boot-sunxi-with-spl.bin U-Boot 2020.04 (Jun 20 2020)
/dev/mmcblk0 U-Boot 2020.04-rc3 (Mar 18 2020)

Write U-Boot+SPL to bootable storage

Back in June, Crust firmware was added to pmaport and, in the same commit, the u-boot-pinephone a-pack was updated to include the update-u-boot script. The update-u-boot script, which originated in the alpine Linux u-boot repository, provides the simplest way for you to upgrade your boot storage to the latest version of U-BOOT+SPL. Simply run:

second-chance:~$ update-u-boot

To see if your version of u-boot-pinephone includes the upgrade-u-boot script, use the apk info sub command:

second-chance:~$ apk info -L u-boot-pinephone
u-boot-pinephone-2020.07_git20200612-r1 contains:

If your version of u-boot-pinephone does not include usr/sbin/update-u-boot, or if you are just curious about how to upgrade U-Boot without the upgrade-u-boot command, then read on. Use the dd command to write the latest version of u-boot to your bootable storage. Be sure to replace /dev/mmcblk0 with the path to the bootable storage device that is relevant for your running environment:

second-chance:~$ sudo dd if=/usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin of=/dev/mmcblk0 bs=1024 seek=8
486+1 records in
486+1 records out

In case you are not confident using the dd command, here is a breakdown of the optional flags that I used in the above:

option description
if input file path. In our case it is the path to the u-boot-sunxi-with-spl.bin.
of output file path. In our case it is the path to the bootable storage device.
bs block size, or number of bytes per count. Not to be confused with disk blocksize or a rude expletive. It is purposely set to 1024B so that each count represents 1 KiB.
seek number of output blocks to offset writing to disk. We know from Layout of sunxi bootable storage that the offset must be 8KiB.

Now we can verify it worked by grepping the disk for the new version.

second-chance:~$ sudo dd if=/dev/mmcblk0 bs=1024 skip=8 count=486 | grep -E 'U-Boot [[:digit:]]'
U-Boot 2020.04 (Jun 20 2020 - 12:41:48 +0000)
486+0 records in
486+0 records out

Walking the bin

I was curious about how u-boot-sunxi-with-spl.bin was structured, so I decided to investigate using binwalk, an opensource firmware analysis tool developed by ReFirm Labs Inc (no affiliation). Sure, I could discover exactly the same by browsing Pine64’s fork of u-boot, starting with board/sunxi/, but then I wouldn’t learn anything about binwalk. Besides, there can be only one single source of truth and that truth is what is compiled and running in production.

After installing binwalk on my local dev box, I downloaded the u-boot-pinephone package from the postmarketOS master aarch64 repository:

$ wget --quiet -r --no-parent --no-directories --level=1 -A "u-boot-pinephone-*.apk"
$ ls

APK packages are gzip compressed. To see this, we can use file (file(1)) to output the compression format:

$ file -b u-boot-pinephone-2020.07_git20200612-r1.apk
gzip compressed data, from Unix, original size modulo 2^32 573440

The package can be decompressed and extracted using the tar command (tar(1)).

$ tar xzf u-boot-pinephone-2020.07_git20200612-r1.apk --no-anchor u-boot-sunxi-with-spl.bin
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'

The archive will be extracted relative to the current working directory.

tree usr
└── share
    └── u-boot
        └── pine64-pinephone
            └── u-boot-sunxi-with-spl.bin

3 directories, 1 file

For convenience, I moved the binary to the root of my current working directory.

mv usr/share/u-boot/pine64-pinephone/u-boot-sunxi-with-spl.bin ./ && rm -rf usr/

Now that we have extracted the u-boot-sunxi-with-spl.bin, we can use binwalk to scan the binary for known signatures. I am currently using Binwalk v2.2.0, see binwalk wiki for usage:

binwalk --signature u-boot-sunxi-with-spl.bin
32768         0x8000          device tree image (dtb)
325944        0x4F938         CRC32 polynomial table, little endian
490112        0x77A80         device tree image (dtb)
523168        0x7FBA0         device tree image (dtb)

Binwalk recognizes three device tree blobs (DTB) and one cyclic redundancy check table. Notice, that the first DTB is located at offset 32768B, exactly 32 KiB from the start of the file. Thanks to binwalk, we know the offsets of 4 binary blobs. Let’s extract all four blobs for further examination.

binwalk -q --dd='device.*:dtb' --dd='crc32.*:crc' u-boot-sunxi-with-spl.bin
├── u-boot-pinephone-2020.07_git20200612-r1.apk
├── u-boot-sunxi-with-spl.bin
└── _u-boot-sunxi-with-spl.bin.extracted
    ├── 4F938.crc
    ├── 77A80.dtb
    ├── 7FBA0.dtb
    └── 8000.dtb

1 directory, 6 files

By default, binwalk extracts the blobs to files named after their hexadecimal offsets, with an optional file extension matching the string that follows the first colon provided to the –dd optional parameter.

Examine what we found on our walk

Using the file command, we can collect some preliminary metadata about the extracted files:

file _u-boot-sunxi-with-spl.bin.extracted/*
_u-boot-sunxi-with-spl.bin.extracted/4F938.crc: data
_u-boot-sunxi-with-spl.bin.extracted/77A80.dtb: Device Tree Blob version 17, size=33053, boot CPU=0, string block size=2345, DT structure block size=30652
_u-boot-sunxi-with-spl.bin.extracted/7FBA0.dtb: Device Tree Blob version 17, size=33189, boot CPU=0, string block size=2353, DT structure block size=30780
_u-boot-sunxi-with-spl.bin.extracted/8000.dtb:  Device Tree Blob version 17, size=1388, boot CPU=0, string block size=131, DT structure block size=1200

The files are device tree blobs, which is a binary format. Let’s use the dtc command (dtc(1)) to reverse engineer the DTBs into human readable Device Tree Source (DTS) text.

dtc -I dtb -O dts -o _u-boot-sunxi-with-spl.bin.extracted/8000.dts _u-boot-sunxi-with-spl.bin.extracted/8000.dtb
dtc -I dtb -O dts -o _u-boot-sunxi-with-spl.bin.extracted/77A80.dts _u-boot-sunxi-with-spl.bin.extracted/77A80.dtb
dtc -I dtb -O dts -o _u-boot-sunxi-with-spl.bin.extracted/7FBA0.dts _u-boot-sunxi-with-spl.bin.extracted/7FBA0.dtb
├── 0.spl
├── u-boot-pinephone-2020.07_git20200612-r1.apk
├── u-boot-sunxi-with-spl.bin
└── _u-boot-sunxi-with-spl.bin.extracted
    ├── 4F938.crc
    ├── 77A80.dtb
    ├── 77A80.dts
    ├── 7FBA0.dtb
    ├── 7FBA0.dts
    ├── 8000.dtb
    └── 8000.dts

1 directory, 10 files

Finally, let’s examine the device tree source of _u-boot-sunxi-with-spl.bin.extracted/8000.dts.


/ {
        timestamp = <0x5f1356a0>;
        description = "Configuration to load ATF before U-Boot";
        #address-cells = <0x01>;

        images {

                uboot {
                        data-size = <0x649b0>;
                        data-offset = <0x00>;
                        description = "U-Boot (64-bit)";
                        type = "standalone";
                        os = "u-boot";
                        arch = "arm64";
                        compression = "none";
                        load = <0x4a000000>;

                atf {
                        data-size = <0x817d>;
                        data-offset = <0x649b0>;
                        description = "ARM Trusted Firmware";
                        type = "firmware";
                        os = "arm-trusted-firmware";
                        arch = "arm64";
                        compression = "none";
                        load = <0x44000>;
                        entry = <0x44000>;

                scp {
                        data-size = <0x29e4>;
                        data-offset = <0x6cb30>;
                        description = "SCP firmware";
                        type = "firmware";
                        arch = "or1k";
                        compression = "none";
                        load = <0x50000>;

                fdt_1 {
                        data-size = <0x811d>;
                        data-offset = <0x6f514>;
                        description = "sun50i-a64-pinephone-1.1";
                        type = "flat_dt";
                        compression = "none";

                fdt_2 {
                        data-size = <0x81a5>;
                        data-offset = <0x77634>;
                        description = "sun50i-a64-pinephone-1.2";
                        type = "flat_dt";
                        compression = "none";

        configurations {
                default = "config_1";

                config_1 {
                        description = "sun50i-a64-pinephone-1.1";
                        firmware = "atf";
                        loadables = "scp\0uboot";
                        fdt = "fdt_1";

                config_2 {
                        description = "sun50i-a64-pinephone-1.2";
                        firmware = "atf";
                        loadables = "scp\0uboot";
                        fdt = "fdt_2";

In the images structure we can clearly see the offsets for U Boot, Arm Trusted Firmware, SCP firmware and the flattend device tree. The reason why there are two flattend device trees is because the binary includes payloads to support two different versions of PinePhone hardware. The rest of the firmware and hardware configuration is contained within the other two device tree source files as shown in the following table:

Name Compatible PinePhone Model
77A80.dts PinePhone-1.1
7FBA0.dts PinePhone-1.2

Using the offsets found within the device tree source, we could continue to reverse engineer u-boot-sunxi-with-spl.bin by extracting the firmware into individual binary files. Then, we could scan each extracted file using binwalk, perhaps it will find something new. I will leave that as an exercise to the curious reader.


I hope you have a better understanding about what U-Boot is and why it is important to keep it updated. More importantly, I hope you enjoyed reading this article and are curious to learn more about firmware development and your PinePhone. Please feel free to leave a comment or send me a direct email.


Your comment has been submitted and is now pending moderation

Thank you so much for commenting Ricardo!

i3-wm is a fabulous windows manager. Before switching to exwm, I was an advocate of fvwm. I enjoyed writing scripts for fvwm and extending its capabilities. I still like fvwm, but my passion for Emacs grew to the point that fully immersing my user-land experience within emacs was a natural progression.

Speaking of Emacs, I am looking forward to EmacsConf on November 28th and 29th. For those that are interested, here are the details:

If you do setup i3-wm on your PinePhone, I would love to hear about your experience doing so.

All the best!

Trevor Wilson

Now. I’m waiting for my pynephone, but the mine brings Majanro GNU/Linux distro (Arch-linux based) y want change every thing in it.

I have used Kubuntu, Ubuntu, Archlinux, Artix linux, Linux Mint, Red-hat (very few, in a server), Debian and now Devuan for the las 3 years. I was 100% kde, but since the last 2 years i use i3-wm i am falled in love with kde software, but i love use that software on the i3-wm simplicity and structure. Ten years being GNU/Linux user, enought for undestanding that science is being revolutionated and powered by GNU&Linux togeter. Now I want to try i3-wm on my pinephone, i hope.

Never before used GNU/Linux on arm architecture. I will learn and it is exciting.


Hi Nadja

I ejoyed the article, it was excelent explanation about booting and architecture.

Hi Trevor The paragraph before last, was like I reading about my own life. I shared it with my bioinformatics course students, maybe someday, they will know that it is the better way to learn.

Best regards to both


Hi Nadja,

Are you familiar with IRC? I encourage you to join #pinephone at if you haven’t already. You should also consider connecting to the freenode and oftc IRC servers. There you will find many open source and free software community channels where you can ask questions and connect with others that share your interest. In case you are not familiar with IRC I provided links to each server’s IRC web portal. Web clients are convenient when you are first becoming familiar with IRC, but if you are going to spend a lot of time on IRC you will most likely be happier with a desktop client.

For TLS use port 6697. Here are some relevant resources to get you started:




List of IRC clients:

Happy hacking!

Trevor Wilson

Hi Nadja,

Thank you for the complement. I am glad to that you enjoyed learning a bit about u-boot and the PinePhone’s boot procedure. Let me tell you a little bit about how I learned to learn about GNU/Linux, programming and computer technology in general.

When I was in elementary my dad purchased an APPLE IIC and gave it to me and my siblings as a Christmas Gift. I asked my mom take me to the local library in search of programming books. I checked-out every book on Applesoft BASIC. Each book contained the source code for a number of programs. I typed out every symbol and character until the program finally ran. I had absolutely no idea what I was doing, but I discovered that I could change values in the program and that would affect the program’s output. Eventually I was able to write my own simple programs. My parents were both very busy and did not know anything about computers. They were happy to check books out of the library for me, but the reading and understanding of those books was my own responsibility. I was in grade 2 at the time. That computer meant everything to me.

While I was in high school I became curious about Linux. Or at least, I became curious about this other operating system that used something called the Linux kernel. In fact, I became curious about what that word, kernel, actually meant. I didn’t have any computer literate friends and I did not realize that there was a local Linux enthusiast group that I could have connected with. I purchased a copy of Linux Mandrake from a nearby computer shop and followed the supplied instructions to dual boot my Intel 486 with Windows 95 and Mandrake. I naively explored the default suite of applications through the Gnome windows manager. When I found the terminal I was delighted to discover that the commands were completely different than DOS or Applesoft DOS. A whole new world to explore. The experience captured my attention, but I had a lot to learn.

By the time I enrolled in computer science at University I already had a full year of professional C++ development experience; However, my target platform was for the most part Microsoft Windows. At University the work stations all ran Sun Solaris. One of my courses required students to purchase Unix in a Nutshell (O’Reilly). I learned to use emacs. I took a cyber philosophy course and read: The Cathedral & The Bazaar by Eric S. Raymond, Out of Control by Kevin Kelly, Life On The Screen by Sherry Turkle and A Gift of Fire by Sara Baase. I learned about the Open Source movement, the Free Software movement and developed a deeper understanding about the relationship between humans and technology. I learned about Vivisystems, emergent behaviour and the cybernetic principal. I was inspired to learn more.

I met other students that were only running GNU/Linux at home and they knew the command line much better than I did. That weekend I formatted the drive on my Pentium desktop and performed a stage-1 install of Gentoo Linux, only this time it was not dual booted. By forcing myself to use Linux every day, and by being in an environment where others were doing the same, my fluency with Linux shell commands and programming improved. The most significant thing I learned was how to find what I needed to know to solve whatever problem I was faced with. University provided me with plenty of problems to solve and those problems gave my desire to learn a purpose.

I am not suggesting that you need to go to University to find purpose. I am suggesting that finding a purpose might help to guide your learning by narrowing the scope a bit. In my early years I had no purpose to guide my learning. What I did have was a powerful motivation to learn about computers and programming, but those are very broad topics.

To learn you need to know where to begin. To begin you need a purpose.

Trevor Wilson

Hello Trevor,

you did a great job exlaining the pinephone boot procedure, I enjoyed working through your post! I am a not-so-skilled linux enthusiast and the pinephone is challening me to deepen my understanding of the unix architecture overall. Your posts are a good source of knowledge throughout this way. Thank you and have a pleasant week.