From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm2-f12.google.com (mail-wm2-f12.google.com [74.125.225.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D4BD7580387 for ; Wed, 23 Sep 2026 20:11:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.140 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194293; cv=none; b=Ls0q6qYyJro0sASB4mZSi0qFtqT1yDgKgBKsgAap/eW3IHaSHUF45wIGJnj4ww3z3fCgYRGeYnLbMlEQNEhg7fwck8pp76hKE1aQRXB6K6nOam+MGmymQwyC6yJPUiZKPEJPfMtFiXIbS46xpiHv+2WmSophmXsA1ZSUirVaDi0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194293; c=relaxed/simple; bh=7oqKquhakJe90oiy+DxuOhrOlnIl+OIGd9ser/F9AeM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HHtHoxgGVZBUZyCVEFOVSDGMJv+w2TyYsMyqTA7GPqQibz2JZohxPUobPg6nWOgQVuxnmlOMpt7wgh3hmcCzvw2A+s39muNKb1Zfn5fultoLWA/7ZVVgQHB67iuRbNboP8eQnBRJhCbWnqHquJ/UCF+Foknf0ULFo+Q2HEfHGTk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=U4Jd6et6; arc=none smtp.client-ip=74.125.225.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U4Jd6et6" Received: by mail-wm2-f12.google.com with SMTP id 5b1f17b1804b1-49b912e4ad9so7772575e9.2 for ; Wed, 23 Sep 2026 13:11:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194256; x=1790799056; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5EUHnFPULkkfl9EPKbDpfgSFJiqys4NK1JcsO8IiCyo=; b=U4Jd6et6NatnLFrAhJCTQsC5cuINZRmg111cCCHHYlyjG7Gj1bodZTXOsG4puSQJqr IR2k2QW3Znqdz78D5WIjSnepRFhtW1+oCGTsz+24DY1kg1ZFmdwjY7iev8LlOspxMQMN 3eSDqNa8slwWu3y+3Kfk2qM+zD3s4KjFTAsapUSXDUCk64G0dfYqFLQinMscYxGr7YUN Z++T+qGGcjIKSljR8rqZMBzB/3cbfK3DTfvqyVDvRF6yiMg9v7GbjjC6JmZb4WO9O67V e0UhrjDRSZOw1DxVxlztXdk0nFCf12gZBFAT0nUp5t2r3LESdAlUUV1K/Lt2vcS6+FO5 s+kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194256; x=1790799056; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=5EUHnFPULkkfl9EPKbDpfgSFJiqys4NK1JcsO8IiCyo=; b=u2uRWJE3FLYntcgEEof/CLPaI/Dg6UoedS0gb/RM3JxYicyrGKHhPx92s4+Wlmhe2m HMxiVgTHslapOfh/53yAggHIZdrLk6iGSD6NVyYOBi6Gl8IP+lZoGbph47U7GhIA7pgv UTvs3Q1CL+50h9hEV+44KIGR5HSTPH+T7Y3+jlleCfkXKZPZJK+5BGiw/uBkTWkpZ2Yq 0u3lc1uHKCh1/lxiuBi4shHFURr+bEn8dhjDYK+KZ+KM0sk4/THFHA4uS6YwtsRUsD7U 21ATJnEdrRu2Y/p3/1PpgHiQhyq3gLqaGp5LtSZbfj2ej4ElumwgBCLYOtxeDQWxF5P1 8u+g== X-Forwarded-Encrypted: i=1; AKwUvBz03urh3heEWnTthM1kSRQ4LiZuYlZ+NigQbUmerQxLJ7NkzQCXMqli09uB2o0OnFtM5SKTiq/jniYWGd8=@vger.kernel.org X-Gm-Message-State: AFuF++leLZR/EerD9qrqVjdJG1UtxJlQ2TTfRg1jMmTRxBhPMOf3Y+qK ikxtPz9kk7yuR/3y0lt7N16ie5imrRBEsQ2UZ+8vaVR2OKjsD0ymuWB7 X-Gm-Gg: AYBFou3f5rgf1xJUD6TVSG5pfxySCfwK2x2z8VfIAM3KKNlyyRUA+pYqWozR8MjQYqX VGXYXnLABbBAAiTf3KZv2B1TM0XLU4C+VHCZvmbT2GtbtuP0EX8wdm/Y9ja/flv1RThW+kn0Dbc /S53GNnSMILtVnA+z5s/Yl2R/5xWdvfpg5V8wzm+uvdG/53RJxU6pl4LzwsG+309zZvSU2JvEXa M2tRSszqy5snMFmvLIqKdS7FZHSMaE2bagoL4UOhnEmdICD1PfkxqA5SZ/QXzlhB+BMyBZLuVGf rWJSP6IDt4c4Ho4YPkLjLsSxiffy424biGqNIRzIQzsac0NXcnBYepbhW5y47fIZfE0rk71EiyY J6ST4Gc00AHxc4JdDIQta1RGAqan/boyZyOXuGOgivS4C/mQDiPkSdYuKoJwSKGv3d+59j+Kqli OSNOVgT6ztCyFFaUMb9Qe7rowBEvbmByBtIWO9OHPbUpv4pjWqRXOT7zzHIjZwfV4NO5jQC/AFh gldIov/2ooBMceC6xznEODm X-Received: by 2002:a05:600c:c8f:b0:49c:dc14:d681 with SMTP id 5b1f17b1804b1-49fe66bdf3bmr4527355e9.3.1790194256374; Wed, 23 Sep 2026 13:10:56 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:56 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 3/7] video: logo: allow the boot logo to come from the device tree Date: Wed, 23 Sep 2026 22:10:31 +0200 Message-Id: <20260923201035.51007-4-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add CONFIG_LOGO_DT_CLUT224, which makes fb_find_logo() look for a node compatible with "boot-logo-clut224" under /chosen before falling back to the logos built into the kernel image. The image is validated before it is used: the palette must have at most 224 entries, the pixel data length must match the geometry, and every pixel must reference an entry that exists. A malformed node is reported and ignored rather than drawn, so a bad device tree cannot take the display down with it. The image is copied out of the device tree so that the 32 entry offset the frame buffer layer reserves for the console can be applied to the pixels. The pixels are allocated with kvmalloc(), since a full screen image is larger than kmalloc() will comfortably serve. Unlike the built-in logos the copy is never freed. Those are initdata and go away in free_initmem(), which runs after async_synchronize_full(); anything this code could hook into runs before that, so releasing the image here would pull it out from under a display driver whose probe is still in flight. It is a modest allocation and it lives as long as the device tree it came from, which also means fb_find_logo() can still hand it out after the built-in logos are gone. The node lives under /chosen because a logo is configuration handed over by firmware rather than a description of the hardware, which is also where simple-framebuffer nodes live for the same reason. The lookup is guarded with IS_ENABLED() rather than wrapped in an #ifdef, so that the code is compile checked whatever the configuration and the compiler drops it when the option is off. Built for x86_64 with CONFIG_OF=n the object is left with no unresolved of_* symbol and none of the data. Signed-off-by: Max Pedraza --- drivers/video/logo/Kconfig | 12 +++ drivers/video/logo/logo.c | 154 ++++++++++++++++++++++++++++++++++++- include/linux/linux_logo.h | 3 + 3 files changed, 168 insertions(+), 1 deletion(-) diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index cda15b9589..cce89948f1 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -76,4 +76,16 @@ config LOGO_LINUX_CLUT224_FILE magick source_image -compress none -colors 224 destination.ppm +config LOGO_DT_CLUT224 + bool "224-color logo supplied by the device tree" + depends on OF + help + Look for a boot logo in the device tree, in a node compatible with + "boot-logo-clut224" under /chosen, instead of using one of + the logos built into the kernel image. This allows a single kernel + image to be used by several products that only differ in branding. + + If no such node is present, or it is disabled, the built-in logo + selected above is used, so saying Y here is safe. + endif # LOGO diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 91535f8848..84afd5b337 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -11,6 +11,9 @@ */ #include +#include +#include +#include #include #include @@ -22,6 +25,139 @@ static bool nologo; module_param(nologo, bool, 0); MODULE_PARM_DESC(nologo, "Disables startup logo"); +/* Boot logo supplied by the device tree */ + +#define LOGO_DT_MAX_CLUT 224 +/* + * The first 32 palette entries are reserved for the console, so the logo + * colours start at index 32. That is an implementation detail of the frame + * buffer layer rather than a property of the image, so the device tree stores + * plain indices and the offset is applied here. + */ +#define LOGO_DT_CLUT_OFFSET 32 +/* + * Sanity limit on the image size, a device tree is not a good place for more: + * a 4K screen is 8.3M pixels, and the copy is kept for the life of the kernel. + * One byte per pixel, so this is a byte count as well. + */ +#define LOGO_DT_MAX_PIXELS SZ_16M + +static struct linux_logo logo_dt_clut224 = { + .type = LINUX_LOGO_CLUT224, +}; + +static unsigned char *logo_dt_clut; +static unsigned char *logo_dt_data; + +static int logo_dt_parse(struct device_node *np) +{ + unsigned int clutsize, npixels, i; + unsigned char *clut, *data; + u32 width, height; + int len, ret; + + ret = of_property_read_u32(np, "width", &width); + if (ret) + return ret; + + ret = of_property_read_u32(np, "height", &height); + if (ret) + return ret; + + if (!width || !height || (u64)width * height > LOGO_DT_MAX_PIXELS) + return -EINVAL; + + npixels = width * height; + + len = of_property_count_u8_elems(np, "clut"); + if (len < 3 || len % 3) + return -EINVAL; + + clutsize = len / 3; + if (clutsize > LOGO_DT_MAX_CLUT) + return -EINVAL; + + ret = of_property_count_u8_elems(np, "data"); + if (ret < 0) + return ret; + if ((unsigned int)ret != npixels) + return -EINVAL; + + clut = kmalloc(len, GFP_KERNEL); + if (!clut) + return -ENOMEM; + + /* The palette is at most 672 bytes, the pixels can be megabytes */ + data = kvmalloc(npixels, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err_free_clut; + } + + ret = of_property_read_u8_array(np, "clut", clut, len); + if (ret) + goto err_free_data; + + ret = of_property_read_u8_array(np, "data", data, npixels); + if (ret) + goto err_free_data; + + for (i = 0; i < npixels; i++) { + if (data[i] >= clutsize) { + ret = -ERANGE; + goto err_free_data; + } + data[i] += LOGO_DT_CLUT_OFFSET; + } + + logo_dt_clut = clut; + logo_dt_data = data; + + logo_dt_clut224.width = width; + logo_dt_clut224.height = height; + logo_dt_clut224.clutsize = clutsize; + logo_dt_clut224.clut = clut; + logo_dt_clut224.data = data; + + return 0; + +err_free_data: + kvfree(data); +err_free_clut: + kfree(clut); + return ret; +} + +static const struct linux_logo *logo_dt_find(void) +{ + static bool probed; + struct device_node *np; + int ret; + + if (!IS_ENABLED(CONFIG_LOGO_DT_CLUT224)) + return NULL; + + if (probed) + return logo_dt_data ? &logo_dt_clut224 : NULL; + + probed = true; + + np = of_get_compatible_child(of_chosen, LOGO_DT_COMPATIBLE); + if (!np) + return NULL; + + if (of_device_is_available(np)) { + ret = logo_dt_parse(np); + if (ret) + pr_warn("logo: ignoring malformed %pOF node (%d)\n", + np, ret); + } + + of_node_put(np); + + return logo_dt_data ? &logo_dt_clut224 : NULL; +} + /* * Logos are located in the initdata, and will be freed in kernel_init. * Use late_init to mark the logos as freed to prevent any further use. @@ -45,7 +181,23 @@ const struct linux_logo * __ref fb_find_logo(int depth) { const struct linux_logo *logo = NULL; - if (nologo || logos_freed) + if (nologo) + return NULL; + + /* + * A logo supplied by the device tree wins over the built-in ones. It + * is an ordinary allocation rather than initdata, so unlike them it + * stays valid for the life of the kernel, and is still there for a + * display driver whose probe finishes after the built-in logos have + * gone. + */ + if (depth >= 8) { + logo = logo_dt_find(); + if (logo) + return logo; + } + + if (logos_freed) return NULL; #ifdef CONFIG_LOGO_LINUX_MONO diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index b3b15d3800..1e7e9db6dd 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -22,6 +22,9 @@ #define LINUX_LOGO_CLUT224 3 /* 224 colors */ #define LINUX_LOGO_GRAY256 4 /* 256 levels grayscale */ +/* Compatible of the /chosen child describing a device tree supplied logo */ +#define LOGO_DT_COMPATIBLE "boot-logo-clut224" + struct linux_logo { int type; /* one of LINUX_LOGO_* */ -- 2.39.5