Change cboot boot logo (L4T 28.2)

Is there any way to change the NVIDIA logo that cboot displays since L4T 28.2 to my own picture?

gavin.lofts,

I am checking this internally. Thanks for you patience.

Any news on that?

I would also like to understand how to change the boot logo show by what I believe is cboot.

The image I believe that currently gets flashed is under bootloader/bmp.blob and when I open this image as a raw RGB I am presented with an image that has some special formatting. It seems there is a few bytes of header, and three images in total in this image. In total, the image is 10mb.

Could anyone say what format the bootloader/bmp.blob is in? I believe a simple replacement of the existing file with a correctly formatted image will let it be displayed.

I have used this guide for reference, and it does not work with “normal RGB” images.

It could be that this is an “RGB Planar” format that I don’t have the right stuff to decode.

A followup question would be to ask if there is a way to display multiple logos, so that I could show nvidia and then a linux penguin, and then a happy face. I am unaware if cboot supports this.

Thank you for reading.

When you say “it doesn’t work,” what do you mean? It displays another image, but the image looks wrong?
If so, how does it look wrong?
If the RGB data is inside the bootloader, it should be possible to simply patch the image data using some hex editor or a simple script/tool, but that assumes that the image format is something that is reasonably well understood.

Hi guys, it’s in some special format that requires internal tools to build at the moment, unfortunately there isn’t public documentation of how to edit this. But if you can figure it out in hex editor, more power to you!

@snarky - thanks for prompting me for clarity. I see you have left your mark on these forums. :) I meant to convey that when I use the tutorial linked to replace the image, I see a black screen in place of the boot logo. Otherwise behavior is normal. I believe it simply fails to display a format it doesn’t understand.

@dusty_nv - This might not be the place, but first thank you for your jetson github repo. It was essential to me in wrapping my head around the jetson.
Yes, I was hoping it was some kind of planar RGB format… And it seems that it is… But some special version. Best I can tell, there seems to be a 1x1 full scale image, and 4 1/4 res images. And they are all “stacked on top” of each other. This will be a fun challenge.

I’m hoping there is no legal issue with modifying the logo. I intend to show the nvidia logo with a Linux logo, and a company logo. I may have to accomplish this in one image. Of course, I am aiming for “powered by Nvidia” more than implying any form of ongoong business relation.

As always, Thanks. I don’t believe I can post the code for this due to contract obligations, but I will of course post the formatting of the image structure so others can follow.

The format is very simple. Just search for the magic number of the most common image format. Then just replace (make sure images have exactly (especially the first one) the same size in bytes as before).

The bmp.blob contains a header (192 bytes, IIRC), then 3 BMPs - 640x480, 1280x720, and 1920x1080. Each BMP is the same image - the green on black NVIDIA logo. We’re working on releasing the simple tools we use to create the header to the public. Until then, hopefully you can edit it w/hexedit, etc. Note that the formats are fixed, so you can use your logo, but it must be 480, 720 and 1080.

HTH

You could try generating a new blob using something like this.
Note that I haven’t actually flashed this output, just poked at the bytes to make sure it looks OK.
(And sliced out the original BMPs and put them back using the tool, and verified that I got the same blob back out.)

Usage:
blobbify input1080.png # if single image, should be 1920x1080 picture, will be scaled/cropped
blobbify input1080.png input720.jpg input480.bmp # if multiple images, should be those sizes
Input formats supported are whatever stb_image supports (png, jpg, bmp, tga, and a few others.)

/* Simple tool to generate a bootloader image blob to replace
 * bmp.blob for the nVIDIA Jetson JetPack 3.2 distribution.
 * Put in the public domain in May 2018. Use only at your 
 * own risk, no guarantees of merchantability or fitness for 
 * any particular purpose (nor of safety or correctness) is 
 * made or implied.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "stb/stb_image.h"


unsigned char *rescale_720(unsigned char const *i1080);
unsigned char *rescale_crop_480(unsigned char const *i1080);
void swapperize(unsigned char *b, long y, long x);

extern unsigned char header1[];
extern size_t size1;
extern unsigned char header2[];
extern size_t size2;
extern unsigned char header3[];
extern size_t size3;
extern unsigned char header4[];
extern size_t size4;

int main(int argc, char const *argv[]) {
    if ((argc != 2 && argc != 4) || (argv[1][0] == '-')) {
        fprintf(stderr, "usage: jw_boot_logo input1080p.{jpg/tga/png} [input720p.xxx input480p.xxx]\n");
        fprintf(stderr, "outputs a file named bmp.blob in the working directory.\n");
        fprintf(stderr, "Prepares a bmp.blob to use for Jetson bootloader to replace the start-up logo.\n");
        fprintf(stderr, "The input image should be 1920x1080 pixels; you can also provide 1280x720 and\n");
        fprintf(stderr, "640x480 versions, although these will be computed (scaled/cropped) from the\n");
        fprintf(stderr, "1080p if not provided. (You must provide 1 or 3 images.)\n");
        fprintf(stderr, "https://github.com/jwatte/jw_boot_logo\n");
        exit(1);
    }

    int w1080 = 0, h1080 = 0, tmp;
    unsigned char *i1080 = stbi_load(argv[1], &w1080, &h1080, &tmp, 3);
    if (!i1080) {
        fprintf(stderr, "%s: could not load image (TGA, JPG and PNG supported)\n", argv[1]);
        exit(1);
    }
    if (w1080 != 1920 || h1080 != 1080) {
        fprintf(stderr, "%s: must be 1920x1080; got %dx%d\n size", argv[1], w1080, h1080);
        exit(1);
    }

    unsigned char *i720;
    unsigned char *i480;
    int w, h;
    if (argc == 4) {
        i720 = stbi_load(argv[2], &w, &h, &tmp, 3);
        if (!i720) {
            fprintf(stderr, "%s: could not load image\n", argv[2]);
            exit(1);
        }
        if (w != 1280 || h != 720) {
            fprintf(stderr, "%s: must be 1280x720, got %dx%d size\n", argv[2], w, h);
            exit(1);
        }
        i480 = stbi_load(argv[3], &w, &h, &tmp, 3);
        if (!i480) {
            fprintf(stderr, "%s: could not load image\n", argv[3]);
            exit(1);
        }
        if (w != 640 || h != 480) {
            fprintf(stderr, "%s: must be 640x480, got %dx%d size\n", argv[3], w, h);
            exit(1);
        }
    } else {
        i720 = rescale_720(i1080);
        i480 = rescale_crop_480(i1080);
    }

    FILE *f = fopen("bmp.blob", "wb");
    if (!f) {
        fprintf(stderr, "Could not create bmp.blob (do you have write permission?)\n");
        exit(1);
    }
    if (size1 != fwrite(header1, 1, size1, f)) {
        perror("bmp.blob (header1)");
        exit(1);
    }
    if (size2 != fwrite(header2, 1, size2, f)) {
        perror("bmp.blob (header2)\n");
        exit(1);
    }
    swapperize(i480, 480, 640);
    if (640*480*3 != fwrite(i480, 1, 640*480*3, f)) {
        perror("bmp.blob (480 image)\n");
        exit(1);
    }
    if (size3 != fwrite(header3, 1, size3, f)) {
        perror("bmp.blob (header3)\n");
        exit(1);
    }
    swapperize(i720, 720, 1280);
    if (1280*720*3 != fwrite(i720, 1, 1280*720*3, f)) {
        perror("bmp.blob (720 image)\n");
        exit(1);
    }
    if (size4 != fwrite(header4, 1, size4, f)) {
        perror("bmp.blob (header4)\n");
        exit(1);
    }
    swapperize(i1080, 1080, 1920);
    if (1920*1080*3 != fwrite(i1080, 1, 1920*1080*3, f)) {
        perror("bmp.blob (1080 image)\n");
        exit(1);
    }
    fclose(f);
    free(i1080);
    free(i720);
    free(i480);
    fprintf(stderr, "created bmp.blob\n");
    return 0;
}

static void sample(unsigned char const *i, float y, float x, unsigned char *o) {
    unsigned int y0 = (unsigned int)y;
    unsigned int x0 = (unsigned int)x;
    unsigned int dy = y - y0;
    unsigned int dx = x - x0;
    unsigned int b = i[(y0*1920+x0)*3] * (1 - dy) * (1 - dx);
    unsigned int g = i[(y0*1920+x0)*3+1] * (1 - dy) * (1 - dx);
    unsigned int r = i[(y0*1920+x0)*3+2] * (1 - dy) * (1 - dx);
    b += i[(y0*1920+x0+1)*3] * (1 - dy) * dx;
    g += i[(y0*1920+x0+1)*3+1] * (1 - dy) * dx;
    r += i[(y0*1920+x0+1)*3+2] * (1 - dy) * dx;
    b += i[((y0+1)*1920+x0)*3] * dy * (1 - dx);
    g += i[((y0+1)*1920+x0)*3+1] * dy * (1 - dx);
    r += i[((y0+1)*1920+x0)*3+2] * dy * (1 - dx);
    b += i[((y0+1)*1920+x0+1)*3] * dy * dx;
    g += i[((y0+1)*1920+x0+1)*3+1] * dy * dx;
    r += i[((y0+1)*1920+x0+1)*3+2] * dy * dx;
    o[0] = (unsigned char)b;
    o[1] = (unsigned char)g;
    o[2] = (unsigned char)r;
}

unsigned char *rescale_720(unsigned char const *i1080) {
    unsigned char *ret = (unsigned char*)malloc(1280*720*3);
    unsigned char *d = ret;
    for (float y = 0; y != 720; y += 1.0f) {
        for (float x = 0; x != 1280; x += 1.0f) {
            sample(i1080, y * 1080 / 720 + 0.25f, x * 1920 / 1280 + 0.25f, d);
            d += 3;
        }
    }
    return ret;
}

unsigned char *rescale_crop_480(unsigned char const *i1080) {
    unsigned char *ret = (unsigned char*)malloc(640*480*3);
    unsigned char *d = ret;
    for (float y = 0; y != 480; y += 1.0f) {
        for (float x = 0; x != 640; x += 1.0f) {
            sample(i1080, y * 1080 / 480 + 0.5f, x * 1280 / 640 + (1920-1280)/2 + 0.5f, d);
            d += 3;
        }
    }
    return ret;
}


void swapperize(unsigned char *dst, long y, long x) {
    for (long i = 0; i < y/2; ++i) {
        unsigned char *a = &dst[i*x*3];
        unsigned char *b = &dst[(y-i-1)*x*3];
        for (long q = 0; q < x; ++q) {
            unsigned char r = a[2];
            a[2] = b[0];
            unsigned char g = a[1];
            a[1] = b[1];
            unsigned char l = a[0];
            a[0] = b[2];
            b[0] = r;
            b[1] = g;
            b[2] = l;
            a += 3;
            b += 3;
        }
    }
}


unsigned char header1[192] = {
0x4e,0x56,0x49,0x44,0x49,0x41,0x5f,0x5f,0x42,0x4c,0x4f,0x42,0x5f,0x5f,0x56,0x32,
0x00,0x00,0x02,0x00,0x64,0x2d,0x97,0x00,0x24,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x38,0x10,0x0e,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x10,0x0e,0x00,
0x36,0x30,0x2a,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x2e,0x41,0x38,0x00,0x36,0xec,0x5e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
size_t size1 = sizeof(header1);

unsigned char header2[54] = {
0x42,0x4d,0x38,0x10,0x0e,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
0x00,0x00,0x80,0x02,0x00,0x00,0xe0,0x01,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
0x00,0x00,0x02,0x10,0x0e,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
};
size_t size2 = sizeof(header2);

unsigned char header3[56] = {
   0x00,0x00,
0x42,0x4d,0x36,0x30,0x2a,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
0x00,0x00,0x00,0x05,0x00,0x00,0xd0,0x02,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x2a,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
};
size_t size3 = sizeof(header3);

unsigned char header4[54] = {
0x42,0x4d,0x36,0xec,0x5e,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
0x00,0x00,0x80,0x07,0x00,0x00,0x38,0x04,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0xec,0x5e,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
};
size_t size4 = sizeof(header4);

Or, for those who prefer github:

@techtruth or @robert4f5p3: Did you try this out? I’m pretty sure it would solve the problem, but looking for feedback.

@snarky will try soon and report back. Estimates within a week-ish. Ill try to plug in the image over the weekend, but currently dont want to blow away my system state.

Thanks for checking in! I do want to get to this soon.

Conceptually it makes sense, having a header+bmp+bmp+bmp

Hello @snarky, I tried your method; the script you provided generates bmp.blob file which I replaced with default bmp.blob in my bootloader folder and flashed afterward using

sudo ./flash.sh -r -k cpu-bootloader jetson-tx2 mmcblk0p1

I have also tried flashing the board completely.

However, it was unsuccessful, Please suggest if any file other than .blob need to be replaced.

@haroon_alam,

Your flash.sh command line is correct for reflashing CBoot, but note that if your intent is to rework the BMP blob binary (bmp.blob) and reflash it, you just need to replace the bmp.blob in Linux_for_Tegra/bootloader with your new one, and do a full flash. That will pick up the bmp.blob binary and place it in the BMP partition on eMMC, which is where CBoot will look for it. Unless you’ve modified CBoot, there’s no need to specifically reflash CBoot as you are doing above, and any -k partition flash doesn’t send all of the partitions, just the one specified.

HTH

@snarky,
I can tell you that I’ve tried your code to create a blob (following your steps) and finally flashed the device: ./flash.sh mmcblk0p1

This is working all right as I can see my custom blob for a second at boot. Thanks!

@snarky,
It works on my TX2 board too. Thanks!

Hi,

It seems like replace bmp.blob is not working on L4T R32.1.
Does anyone know how to change or disable nvidia boot logo on R32.1?

Thx
Yen

Yen,

What’s not working with your bmp.blob? I assume it was working with the stock (as-shipped) BMP blob on your board w/R32.1? What board, BTW? I assume TX2, but the bmp.blob we supply is universal to all Jetson boards (TX1, TX2, AGX).

If you want to disable the BMP splash, just delete (or rename) bootloader/bmp.blob in your unpacked BSP. CBoot will give a warning about not finding it, but no harm, no foul. You shouldn’t see a splash screen on HDMI then.

As to changing it, find the thread that NicoD refers to where snarky described how to edit the bmp.blob binary to add a custom BMP file.

Let me know if you have any other CBoot or bmp.blob questions.

Tom

Hi twarren,

1.I’m working on our customize TX2 board with R32.1.
2.I followed snarky’s thread to create my own bmp.blob(see attachment) and copied to Linux_for_tegra/bootloader,
but it still showing nvidia logo when system enter ubuntu 18.04.
3.I also removed Linux_for_tegra/bootloader/bmp.blob, but it showed some error message and stop flashing.
error message:

[220.3976 ] File to be written cannot be of zero size, pFileName=bmp.blob
[220.3999 ]
Error: Return value 10
Command tegradevflash_v2 --pt flash.xml.bin --create
Failed flashing t186ref

Could you help to check if my bmp.blob has any problem?
How should I flash OS without bmp.blob?

Thx
Yen
bmp.blob.txt (9.45 MB)