From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 31FAD14AD10 for ; Tue, 23 Jul 2024 08:09:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721722159; cv=none; b=ZVhaJ7nrcRMg1CBja3aBJp/4rNBehIL+9qHnSgz+GoRNd+wyzCMCRH1hnP3tGJBqS+jnO1S9lhDs7tyEuC1dKnw86iV4s6dqwjXI6UUZq2z1V6Mx4fgPiBhUjVpkgUWYAQjkxJLQTR36vhDl2vi8sVWXpCyD/Jrh8Y9CAQCCv1I= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721722159; c=relaxed/simple; bh=AmZMq36mpP1MlpJ3giUA9+HAFxMj6jHApY5HBcYMaUQ=; h=Message-ID:Date:MIME-Version:Subject:To:References:From: In-Reply-To:Content-Type; b=u9+NKVDbPoeVYR4pAH5xW8TW1T0p6iKl/QnTGmCfIc1w1AeyDquTT1hHcMEGc0j1mphKw9bnHSuV4uB7UNrPLBeDS6DIgTUCK/7XIBcw+cl7MEBoaV1kmTybWPgJDH4HQlbk2lsSUs2g98W55jiJEfA9URr7Lu3cTp737Jadz50= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=DO85UkX4; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="DO85UkX4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721722154; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oVShcG367EK9vtIavWLs9IkDImh6P3YU8q7EdnVy1FE=; b=DO85UkX45snIdu/rvRHQU2QLqT87SixQPTwpQrAKKEWXbnTVBi/P0GtBgY4OMqzsKbXcUA x6ch813e/41EDYxn9z8pmJZ9jB1ieu6oOeNW9nG3dw8T57PXYSWCk1Z/bPmlkR5MtS0rUH 5BMF0HdYczaaKN3oRNdk8YrRTdSXjEs= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-308--5DlthgfMEmbzGNlS9fZ9w-1; Tue, 23 Jul 2024 04:09:12 -0400 X-MC-Unique: -5DlthgfMEmbzGNlS9fZ9w-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-42668796626so39052445e9.0 for ; Tue, 23 Jul 2024 01:09:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721722151; x=1722326951; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=oVShcG367EK9vtIavWLs9IkDImh6P3YU8q7EdnVy1FE=; b=A5XPavPQFAqPrqTdkTCfIe4PSr2MbCAzBY1Wz3gg7eISlXBxG/L9vuNv6KSP12i0sr wF0DQ1IVA1mlNl+dv/YgP7xA52Ok/tuTXRA4Ne8dETk49REk2BdfZeuAI/uHQCsPIXZp 4xkrhqkMjnRdhGF5dIABPtLUvQuB3PUTF4KiVWQ1UnzmOAMM5y+WCi9ZxSZpEqJ50ZnX 9IxGqKSRo63Eyvyvi++rKm+zEdWuBvAGE+w4bHH6xWFkYzmkgZNW5kIO9WLtPj82nQqD poFbuACcmbJrB6FLjOobwKV3+OCk+GK6nQqzXpoechZRGPo/DF/HpkrKjm8BdNfff3Wh rbhQ== X-Forwarded-Encrypted: i=1; AJvYcCXMK06ep/gS4blSUzKqpZNAuzR8ZJRftAT8HYyP7mECSOyI7h3WKxIA7fUH+SNzixOOEWEw3+BWQ1SLY1GyxHwpD1oSAAQfInTrPY0SJU0= X-Gm-Message-State: AOJu0YxAtxhWHolns2fnl8am0HACgGV/1zIymcCVr1bt65TV0MEyUjpv UG22xvnG6J7iEt7rLX0/DQb7tKF9HyuhDThdTVKYanYinsT/0PWuDd4x+b/8oMAKW6LIq5dpUvl G26dsIP1L6GCX2Dj6LmlpowLoR78oU+dz75tQpye/ErR5RG+S44afvdy7DBNkt7QZ X-Received: by 2002:a05:600c:4752:b0:426:6edb:7e14 with SMTP id 5b1f17b1804b1-427ed06197fmr13829755e9.35.1721722150361; Tue, 23 Jul 2024 01:09:10 -0700 (PDT) X-Google-Smtp-Source: AGHT+IG3JTfrJb7a7pFkW041Z2dxoy/rZ+2kVZvvr448zqo/ci4FxfmjEN3zhWIVu/3IlVwVjBTIBA== X-Received: by 2002:a05:600c:4752:b0:426:6edb:7e14 with SMTP id 5b1f17b1804b1-427ed06197fmr13829215e9.35.1721722149299; Tue, 23 Jul 2024 01:09:09 -0700 (PDT) Received: from ?IPV6:2a01:e0a:d5:a000:d3ea:62cf:3052:fac6? ([2a01:e0a:d5:a000:d3ea:62cf:3052:fac6]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-427df7b683dsm104753405e9.19.2024.07.23.01.09.08 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 23 Jul 2024 01:09:08 -0700 (PDT) Message-ID: Date: Tue, 23 Jul 2024 10:09:06 +0200 Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v4 4/4] drm/panic: Add a QR code panic screen To: Mitchell Levy , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , Bjorn Roy Baron , Benno Lossin , Andreas Hindborg , Alice Ryhl , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Danilo Krummrich References: <20240717142644.1106060-1-jfalempe@redhat.com> <20240717142644.1106060-5-jfalempe@redhat.com> From: Jocelyn Falempe In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US, fr Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 23/07/2024 01:20, Mitchell Levy wrote: > On 7/17/2024 7:24 AM, Jocelyn Falempe wrote: >> This patch adds a new panic screen, with a QR code and the kmsg data >> embedded. >> If DRM_PANIC_SCREEN_QR_CODE_URL is set, then the kmsg data will be >> compressed with zlib and encoded as a numerical segment, and appended >> to the URL as a URL parameter. This allows to save space, and put >> about ~7500 bytes of kmsg data, in a V40 QR code. >> Linux distributions can customize the URL, and put a web frontend to >> directly open a bug report with the kmsg data. >> >> Otherwise the kmsg data will be encoded as a binary segment (ie raw >> ascii) and only a maximum of 2953 bytes of kmsg data will be >> available in the QR code. > > It seems like this can cause some QR code readers to misbehave. For > example, if the QR code includes an 0xfe character (eg by `echo -e > '\xfe' > /dev/kmsg`) the Android QR code scanner on my phone refuses to > scan it. Very arguably this is a bug in the scanner (interestingly, the > QR code scanner built into Google's camera app handles this case fine), > but I'm not sure what the QR code spec has to say on this matter. Thanks for this test, I though the kernel was enforcing ascii character in kmsg. QR code specification is not enforcing special encoding. It is called "Binary mode" and each character is 8 bit. Before 2005 the default encoding was shift-JIS, and after it's ISO-8859-1. But most QR code readers use different heuristic to decode UTF-8, and that's a bit messy: https://stackoverflow.com/questions/9699657/is-utf-8-the-encoding-of-choice-for-qr-codes-with-non-ascii-chars-by-now > > It seems like dmesg handles this case by escaping the invalid byte into > the four byte string "\xfe". Of course, this adds some complexity > because the bytes encoded in the output and the bytes in the kmsg data > are no longer 1-to-1. Another solution is to replace such character with '?' which won't change the string length, but will lose information. At the same time I don't think it's common to have unprintable character in kmsg. Let's see what others think about this, and I will implement character filtering in a separate patch if needed. > >> You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION. >> >> Signed-off-by: Jocelyn Falempe >> --- >> >> v2: >>   * Rewrite the rust comments with Markdown (Alice Ryhl) >>   * Mark drm_panic_qr_generate() as unsafe (Alice Ryhl) >>   * Use CStr directly, and remove the call to as_str_unchecked() >>     (Alice Ryhl) >>   * Add a check for data_len <= data_size (Greg KH) >> >> v3: >>   * Fix all rust comments (typo, punctuation) (Miguel Ojeda) >>   * Change the wording of safety comments (Alice Ryhl) >>   * Add a link to the javascript decoder in the Kconfig (Greg KH) >>   * Fix data_size and tmp_size check in drm_panic_qr_generate() >> >> v4: >>   * Fix the logic to find next line and skip the '\n' (Alic Ryhl) >>   * Remove __LOG_PREFIX as it's not used (Alice Ryhl) >> >> >>   drivers/gpu/drm/Kconfig         |   31 + >>   drivers/gpu/drm/Makefile        |    1 + >>   drivers/gpu/drm/drm_drv.c       |    3 + >>   drivers/gpu/drm/drm_panic.c     |  249 ++++++++ >>   drivers/gpu/drm/drm_panic_qr.rs | 1003 +++++++++++++++++++++++++++++++ >>   include/drm/drm_panic.h         |    4 + >>   6 files changed, 1291 insertions(+) >>   create mode 100644 drivers/gpu/drm/drm_panic_qr.rs >> >> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig >> index 6dd0016fc9cd..50ac967b56be 100644 >> --- a/drivers/gpu/drm/Kconfig >> +++ b/drivers/gpu/drm/Kconfig >> @@ -149,6 +149,37 @@ config DRM_PANIC_SCREEN >>         or by writing to /sys/module/drm/parameters/panic_screen sysfs >> entry >>         Default is "user" >> +config DRM_PANIC_SCREEN_QR_CODE >> +    bool "Add a panic screen with a QR code" >> +    depends on DRM_PANIC && RUST >> +    help >> +      This option adds a QR code generator, and a panic screen with a QR >> +      code. The QR code will contain the last lines of kmsg and other >> debug >> +      information. This should be easier for the user to report a kernel >> +      panic, with all debug information available. >> +      To use this panic screen, also set DRM_PANIC_SCREEN to "qr_code" >> + >> +config DRM_PANIC_SCREEN_QR_CODE_URL >> +    string "Base URL of the QR code in the panic screen" >> +    depends on DRM_PANIC_SCREEN_QR_CODE >> +    help >> +      This option sets the base URL to report the kernel panic. If >> it's set >> +      the QR code will contain the URL and the kmsg compressed with >> zlib as >> +      a URL parameter. If it's empty, the QR code will contain the >> kmsg as >> +      uncompressed text only. >> +      There is a demo code in javascript, to decode and uncompress >> the kmsg >> +      data from the URL parameter at >> https://github.com/kdj0c/panic_report >> + >> +config DRM_PANIC_SCREEN_QR_VERSION >> +    int "Maximum version (size) of the QR code." >> +    depends on DRM_PANIC_SCREEN_QR_CODE >> +    default 40 >> +    help >> +      This option limits the version (or size) of the QR code. QR code >> +      version ranges from Version 1 (21x21) to Version 40 (177x177). >> +      Smaller QR code are easier to read, but will contain less >> debugging >> +      data. Default is 40. >> + >>   config DRM_DEBUG_DP_MST_TOPOLOGY_REFS >>           bool "Enable refcount backtrace history in the DP MST helpers" >>       depends on STACKTRACE_SUPPORT >> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile >> index 68cc9258ffc4..c62339b89d46 100644 >> --- a/drivers/gpu/drm/Makefile >> +++ b/drivers/gpu/drm/Makefile >> @@ -89,6 +89,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \ >>       drm_privacy_screen_x86.o >>   drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o >>   drm-$(CONFIG_DRM_PANIC) += drm_panic.o >> +drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o >>   obj-$(CONFIG_DRM)    += drm.o >>   obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += >> drm_panel_orientation_quirks.o >> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c >> index 93543071a500..27007b53a8c8 100644 >> --- a/drivers/gpu/drm/drm_drv.c >> +++ b/drivers/gpu/drm/drm_drv.c >> @@ -1067,6 +1067,7 @@ static const struct file_operations >> drm_stub_fops = { >>   static void drm_core_exit(void) >>   { >>       drm_privacy_screen_lookup_exit(); >> +    drm_panic_exit(); >>       accel_core_exit(); >>       unregister_chrdev(DRM_MAJOR, "drm"); >>       debugfs_remove(drm_debugfs_root); >> @@ -1099,6 +1100,8 @@ static int __init drm_core_init(void) >>       if (ret < 0) >>           goto error; >> +    drm_panic_init(); >> + >>       drm_privacy_screen_lookup_init(); >>       drm_core_init_complete = true; >> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c >> index 1fbefb99cf6e..1357d910b742 100644 >> --- a/drivers/gpu/drm/drm_panic.c >> +++ b/drivers/gpu/drm/drm_panic.c >> @@ -18,6 +18,8 @@ >>   #include >>   #include >>   #include >> +#include >> +#include >>   #include >>   #include >> @@ -26,6 +28,7 @@ >>   #include >>   #include >>   #include >> +#include >>   MODULE_AUTHOR("Jocelyn Falempe"); >>   MODULE_DESCRIPTION("DRM panic handler"); >> @@ -621,6 +624,234 @@ static void draw_panic_static_kmsg(struct >> drm_scanout_buffer *sb) >>       } >>   } >> +#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) >> +/* >> + * It is unwise to allocate memory in the panic callback, so the >> buffers are >> + * pre-allocated. Only 2 buffers and the zlib workspace are needed. >> + * Two buffers are enough, using the following buffer usage: >> + * 1) kmsg messages are dumped in buffer1 >> + * 2) kmsg is zlib-compressed into buffer2 >> + * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1 >> + * 4) QR-code image is generated in buffer2 >> + * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 >> bytes for >> + * data segments. >> + * >> + * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, >> which fits in >> + * a V40 QR-code (177x177). >> + * >> + * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data >> will be put >> + * directly in the QR code. >> + * 1) kmsg messages are dumped in buffer1 >> + * 2) kmsg message is encoded as byte stream in buffer2 >> + * 3) QR-code image is generated in buffer1 >> + */ >> + >> +static uint panic_qr_version = CONFIG_DRM_PANIC_SCREEN_QR_VERSION; >> +module_param(panic_qr_version, uint, 0644); >> +MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR >> code"); >> + >> +#define MAX_QR_DATA 2956 >> +#define MAX_ZLIB_RATIO 3 >> +#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also >> be > 4071  */ >> +#define QR_BUFFER2_SIZE 4096 >> +#define QR_MARGIN    4    /* 4 modules of foreground color around the >> qr code */ >> + >> +/* Compression parameters */ >> +#define COMPR_LEVEL 6 >> +#define WINDOW_BITS 12 >> +#define MEM_LEVEL 4 >> + >> +static char *qrbuf1; >> +static char *qrbuf2; >> +static struct z_stream_s stream; >> + >> +static void __init drm_panic_qr_init(void) >> +{ >> +    qrbuf1 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL); >> +    qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL); >> +    stream.workspace = >> kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), >> +                   GFP_KERNEL); >> +} >> + >> +static void drm_panic_qr_exit(void) >> +{ >> +    kfree(qrbuf1); >> +    qrbuf1 = NULL; >> +    kfree(qrbuf2); >> +    qrbuf2 = NULL; >> +    kfree(stream.workspace); >> +    stream.workspace = NULL; >> +} >> + >> +extern size_t drm_panic_qr_max_data_size(u8 version, size_t url_len); >> + >> +extern u8 drm_panic_qr_generate(const char *url, u8 *data, size_t >> data_len, size_t data_size, >> +                u8 *tmp, size_t tmp_size); >> + >> +static int drm_panic_get_qr_code_url(u8 **qr_image) >> +{ >> +    struct kmsg_dump_iter iter; >> +    char url[256]; >> +    size_t kmsg_len, max_kmsg_size; >> +    char *kmsg; >> +    int max_qr_data_size, url_len; >> + >> +    url_len = snprintf(url, sizeof(url), >> CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&zl=", >> +               utsname()->machine, utsname()->release); >> + >> +    max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, >> url_len); >> +    max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, >> QR_BUFFER1_SIZE); >> + >> +    /* get kmsg to buffer 1 */ >> +    kmsg_dump_rewind(&iter); >> +    kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, >> &kmsg_len); >> + >> +    if (!kmsg_len) >> +        return -ENODATA; >> +    kmsg = qrbuf1; >> + >> +try_again: >> +    if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, >> +                  MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) >> +        return -EINVAL; >> + >> +    stream.next_in = kmsg; >> +    stream.avail_in = kmsg_len; >> +    stream.total_in = 0; >> +    stream.next_out = qrbuf2; >> +    stream.avail_out = QR_BUFFER2_SIZE; >> +    stream.total_out = 0; >> + >> +    if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END) >> +        return -EINVAL; >> + >> +    if (zlib_deflateEnd(&stream) != Z_OK) >> +        return -EINVAL; >> + >> +    if (stream.total_out > max_qr_data_size) { >> +        /* too much data for the QR code, so skip the first line and >> try again */ >> +        kmsg = strchr(kmsg, '\n'); >> +        if (!kmsg) >> +            return -EINVAL; >> +        /* skip the first \n */ >> +        kmsg += 1; >> +        kmsg_len = strlen(kmsg); >> +        goto try_again; >> +    } >> +    *qr_image = qrbuf2; >> + >> +    /* generate qr code image in buffer2 */ >> +    return drm_panic_qr_generate(url, qrbuf2, stream.total_out, >> QR_BUFFER2_SIZE, >> +                     qrbuf1, QR_BUFFER1_SIZE); >> +} >> + >> +static int drm_panic_get_qr_code_raw(u8 **qr_image) >> +{ >> +    struct kmsg_dump_iter iter; >> +    size_t kmsg_len; >> +    size_t max_kmsg_size = >> min(drm_panic_qr_max_data_size(panic_qr_version, 0), >> +                   QR_BUFFER1_SIZE); >> + >> +    kmsg_dump_rewind(&iter); >> +    kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, >> &kmsg_len); >> +    if (!kmsg_len) >> +        return -ENODATA; >> + >> +    *qr_image = qrbuf1; >> +    return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, >> QR_BUFFER1_SIZE, >> +                     qrbuf2, QR_BUFFER2_SIZE); >> +} >> + >> +static int drm_panic_get_qr_code(u8 **qr_image) >> +{ >> +    if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0) >> +        return drm_panic_get_qr_code_url(qr_image); >> +    else >> +        return drm_panic_get_qr_code_raw(qr_image); >> +} >> + >> +/* >> + * Draw the panic message at the center of the screen, with a QR Code >> + */ >> +static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb) >> +{ >> +    u32 fg_color = >> convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, >> sb->format->format); >> +    u32 bg_color = >> convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, >> sb->format->format); >> +    const struct font_desc *font = get_default_font(sb->width, >> sb->height, NULL, NULL); >> +    struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas; >> +    size_t max_qr_size, scale; >> +    unsigned int panic_msg_width, qr_width, qr_canvas_width, v_margin; >> +    u8 *qr_image; >> +    unsigned int qr_pitch; >> + >> +    if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace) >> +        return -ENOMEM; >> + >> +    r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height); >> + >> +    drm_panic_logo_rect(&r_logo, font); >> + >> +    panic_msg_width = get_max_line_len(panic_msg, panic_msg_lines) * >> font->width; >> +    r_msg = DRM_RECT_INIT(0, 0, >> +                  min(panic_msg_width, sb->width), >> +                  min(panic_msg_lines * font->height, sb->height)); >> + >> +    max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4); >> + >> +    qr_width = drm_panic_get_qr_code(&qr_image); >> +    if (qr_width <= 0) >> +        return -ENOSPC; >> + >> +    qr_canvas_width = qr_width + QR_MARGIN * 2; >> +    scale = max_qr_size / qr_canvas_width; >> +    /* QR code is not readable if not scaled at least by 2 */ >> +    if (scale < 2) >> +        return -ENOSPC; >> + >> +    pr_debug("QR width %d and scale %zu\n", qr_width, scale); >> +    r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, >> qr_canvas_width * scale); >> + >> +    v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - >> drm_rect_height(&r_msg)) / 5; >> + >> +    drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / >> 2, 2 * v_margin); >> +    r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, >> r_qr_canvas.y1 + QR_MARGIN * scale, >> +                 qr_width * scale, qr_width * scale); >> + >> +    /* Center the panic message */ >> +    drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, >> +               3 * v_margin + drm_rect_height(&r_qr_canvas)); >> + >> +    /* Fill with the background color, and draw text on top */ >> +    drm_panic_fill(sb, &r_screen, bg_color); >> + >> +    if (!drm_rect_overlap(&r_logo, &r_msg) && >> !drm_rect_overlap(&r_logo, &r_qr)) >> +        drm_panic_logo_draw(sb, &r_logo, font, fg_color); >> + >> +    draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, >> &r_msg, fg_color); >> + >> +    /* Draw the qr code */ >> +    qr_pitch = DIV_ROUND_UP(qr_width, 8); >> +    drm_panic_fill(sb, &r_qr_canvas, fg_color); >> +    drm_panic_fill(sb, &r_qr, bg_color); >> +    drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color); >> +    return 0; >> +} >> + >> +static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) >> +{ >> +    if (_draw_panic_static_qr_code(sb)) >> +        draw_panic_static_user(sb); >> +} >> +#else >> +static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) >> +{ >> +    draw_panic_static_user(sb); >> +} >> +static void drm_panic_qr_init(void) {}; >> +static void drm_panic_qr_exit(void) {}; >> +#endif >> + >> + >>   /* >>    * drm_panic_is_format_supported() >>    * @format: a fourcc color code >> @@ -639,6 +870,8 @@ static void draw_panic_dispatch(struct >> drm_scanout_buffer *sb) >>   { >>       if (!strcmp(drm_panic_screen, "kmsg")) { >>           draw_panic_static_kmsg(sb); >> +    } else if (!strcmp(drm_panic_screen, "qr_code")) { >> +        draw_panic_static_qr_code(sb); >>       } else { >>           draw_panic_static_user(sb); >>       } >> @@ -763,3 +996,19 @@ void drm_panic_unregister(struct drm_device *dev) >>       } >>   } >>   EXPORT_SYMBOL(drm_panic_unregister); >> + >> +/** >> + * drm_panic_init() - initialize DRM panic. >> + */ >> +void __init drm_panic_init(void) >> +{ >> +    drm_panic_qr_init(); >> +} >> + >> +/** >> + * drm_panic_exit() - Free the resources taken by drm_panic_exit() >> + */ >> +void drm_panic_exit(void) >> +{ >> +    drm_panic_qr_exit(); >> +} >> diff --git a/drivers/gpu/drm/drm_panic_qr.rs >> b/drivers/gpu/drm/drm_panic_qr.rs >> new file mode 100644 >> index 000000000000..1ef56cb07dfb >> --- /dev/null >> +++ b/drivers/gpu/drm/drm_panic_qr.rs >> @@ -0,0 +1,1003 @@ >> +// SPDX-License-Identifier: MIT >> + >> +//! This is a simple QR encoder for DRM panic. >> +//! >> +//! It is called from a panic handler, so it should't allocate memory >> and >> +//! does all the work on the stack or on the provided buffers. For >> +//! simplification, it only supports low error correction, and >> applies the >> +//! first mask (checkerboard). It will draw the smallest QRcode that can >> +//! contain the string passed as parameter. To get the most compact >> +//! QR code, the start of the URL is encoded as binary, and the >> +//! compressed kmsg is encoded as numeric. >> +//! >> +//! The binary data must be a valid URL parameter, so the easiest way is >> +//! to use base64 encoding. But this wastes 25% of data space, so the >> +//! whole stack trace won't fit in the QR code. So instead it encodes >> +//! every 13bits of input into 4 decimal digits, and then uses the >> +//! efficient numeric encoding, that encode 3 decimal digits into >> +//! 10bits. This makes 39bits of compressed data into 12 decimal digits, >> +//! into 40bits in the QR code, so wasting only 2.5%. And the numbers >> are >> +//! valid URL parameter, so the website can do the reverse, to get the >> +//! binary data. >> +//! >> +//! Inspired by these 3 projects, all under MIT license: >> +//! >> +//! * >> +//! * >> +//! * >> + >> +use core::cmp; >> +use kernel::str::CStr; >> + >> +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] >> +struct Version(usize); >> + >> +// Generator polynomials for ECC, only those that are needed for low >> quality. >> +const P7: [u8; 7] = [87, 229, 146, 149, 238, 102, 21]; >> +const P10: [u8; 10] = [251, 67, 46, 61, 118, 70, 64, 94, 32, 45]; >> +const P15: [u8; 15] = [ >> +    8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105, >> +]; >> +const P18: [u8; 18] = [ >> +    215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, >> 179, 5, 98, 96, 153, >> +]; >> +const P20: [u8; 20] = [ >> +    17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, >> 156, 164, 212, 212, 188, 190, >> +]; >> +const P22: [u8; 22] = [ >> +    210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, >> 98, 80, 219, 134, 160, 105, >> +    165, 231, >> +]; >> +const P24: [u8; 24] = [ >> +    229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, >> 226, 228, 218, 111, 0, 117, >> +    232, 87, 96, 227, 21, >> +]; >> +const P26: [u8; 26] = [ >> +    173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, >> 161, 21, 245, 142, 13, 102, >> +    48, 227, 153, 145, 218, 70, >> +]; >> +const P28: [u8; 28] = [ >> +    168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, >> 27, 232, 201, 21, 43, 245, 87, >> +    42, 195, 212, 119, 242, 37, 9, 123, >> +]; >> +const P30: [u8; 30] = [ >> +    41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, >> 222, 125, 42, 173, 226, 193, >> +    224, 130, 156, 37, 251, 216, 238, 40, 192, 180, >> +]; >> + >> +/// QR Code parameters for Low quality ECC: >> +/// - Error Correction polynomial. >> +/// - Number of blocks in group 1. >> +/// - Number of blocks in group 2. >> +/// - Block size in group 1. >> +/// >> +/// (Block size in group 2 is one more than group 1). >> +struct VersionParameter(&'static [u8], u8, u8, u8); >> +const VPARAM: [VersionParameter; 40] = [ >> +    VersionParameter(&P7, 1, 0, 19),    // V1 >> +    VersionParameter(&P10, 1, 0, 34),   // V2 >> +    VersionParameter(&P15, 1, 0, 55),   // V3 >> +    VersionParameter(&P20, 1, 0, 80),   // V4 >> +    VersionParameter(&P26, 1, 0, 108),  // V5 >> +    VersionParameter(&P18, 2, 0, 68),   // V6 >> +    VersionParameter(&P20, 2, 0, 78),   // V7 >> +    VersionParameter(&P24, 2, 0, 97),   // V8 >> +    VersionParameter(&P30, 2, 0, 116),  // V9 >> +    VersionParameter(&P18, 2, 2, 68),   // V10 >> +    VersionParameter(&P20, 4, 0, 81),   // V11 >> +    VersionParameter(&P24, 2, 2, 92),   // V12 >> +    VersionParameter(&P26, 4, 0, 107),  // V13 >> +    VersionParameter(&P30, 3, 1, 115),  // V14 >> +    VersionParameter(&P22, 5, 1, 87),   // V15 >> +    VersionParameter(&P24, 5, 1, 98),   // V16 >> +    VersionParameter(&P28, 1, 5, 107),  // V17 >> +    VersionParameter(&P30, 5, 1, 120),  // V18 >> +    VersionParameter(&P28, 3, 4, 113),  // V19 >> +    VersionParameter(&P28, 3, 5, 107),  // V20 >> +    VersionParameter(&P28, 4, 4, 116),  // V21 >> +    VersionParameter(&P28, 2, 7, 111),  // V22 >> +    VersionParameter(&P30, 4, 5, 121),  // V23 >> +    VersionParameter(&P30, 6, 4, 117),  // V24 >> +    VersionParameter(&P26, 8, 4, 106),  // V25 >> +    VersionParameter(&P28, 10, 2, 114), // V26 >> +    VersionParameter(&P30, 8, 4, 122),  // V27 >> +    VersionParameter(&P30, 3, 10, 117), // V28 >> +    VersionParameter(&P30, 7, 7, 116),  // V29 >> +    VersionParameter(&P30, 5, 10, 115), // V30 >> +    VersionParameter(&P30, 13, 3, 115), // V31 >> +    VersionParameter(&P30, 17, 0, 115), // V32 >> +    VersionParameter(&P30, 17, 1, 115), // V33 >> +    VersionParameter(&P30, 13, 6, 115), // V34 >> +    VersionParameter(&P30, 12, 7, 121), // V35 >> +    VersionParameter(&P30, 6, 14, 121), // V36 >> +    VersionParameter(&P30, 17, 4, 122), // V37 >> +    VersionParameter(&P30, 4, 18, 122), // V38 >> +    VersionParameter(&P30, 20, 4, 117), // V39 >> +    VersionParameter(&P30, 19, 6, 118), // V40 >> +]; >> + >> +const MAX_EC_SIZE: usize = 30; >> +const MAX_BLK_SIZE: usize = 123; >> + >> +/// Position of the alignment pattern grid. >> +const ALIGNMENT_PATTERNS: [&[u8]; 40] = [ >> +    &[], >> +    &[6, 18], >> +    &[6, 22], >> +    &[6, 26], >> +    &[6, 30], >> +    &[6, 34], >> +    &[6, 22, 38], >> +    &[6, 24, 42], >> +    &[6, 26, 46], >> +    &[6, 28, 50], >> +    &[6, 30, 54], >> +    &[6, 32, 58], >> +    &[6, 34, 62], >> +    &[6, 26, 46, 66], >> +    &[6, 26, 48, 70], >> +    &[6, 26, 50, 74], >> +    &[6, 30, 54, 78], >> +    &[6, 30, 56, 82], >> +    &[6, 30, 58, 86], >> +    &[6, 34, 62, 90], >> +    &[6, 28, 50, 72, 94], >> +    &[6, 26, 50, 74, 98], >> +    &[6, 30, 54, 78, 102], >> +    &[6, 28, 54, 80, 106], >> +    &[6, 32, 58, 84, 110], >> +    &[6, 30, 58, 86, 114], >> +    &[6, 34, 62, 90, 118], >> +    &[6, 26, 50, 74, 98, 122], >> +    &[6, 30, 54, 78, 102, 126], >> +    &[6, 26, 52, 78, 104, 130], >> +    &[6, 30, 56, 82, 108, 134], >> +    &[6, 34, 60, 86, 112, 138], >> +    &[6, 30, 58, 86, 114, 142], >> +    &[6, 34, 62, 90, 118, 146], >> +    &[6, 30, 54, 78, 102, 126, 150], >> +    &[6, 24, 50, 76, 102, 128, 154], >> +    &[6, 28, 54, 80, 106, 132, 158], >> +    &[6, 32, 58, 84, 110, 136, 162], >> +    &[6, 26, 54, 82, 110, 138, 166], >> +    &[6, 30, 58, 86, 114, 142, 170], >> +]; >> + >> +/// Version information for format V7-V40. >> +const VERSION_INFORMATION: [u32; 34] = [ >> +    0b00_0111_1100_1001_0100, >> +    0b00_1000_0101_1011_1100, >> +    0b00_1001_1010_1001_1001, >> +    0b00_1010_0100_1101_0011, >> +    0b00_1011_1011_1111_0110, >> +    0b00_1100_0111_0110_0010, >> +    0b00_1101_1000_0100_0111, >> +    0b00_1110_0110_0000_1101, >> +    0b00_1111_1001_0010_1000, >> +    0b01_0000_1011_0111_1000, >> +    0b01_0001_0100_0101_1101, >> +    0b01_0010_1010_0001_0111, >> +    0b01_0011_0101_0011_0010, >> +    0b01_0100_1001_1010_0110, >> +    0b01_0101_0110_1000_0011, >> +    0b01_0110_1000_1100_1001, >> +    0b01_0111_0111_1110_1100, >> +    0b01_1000_1110_1100_0100, >> +    0b01_1001_0001_1110_0001, >> +    0b01_1010_1111_1010_1011, >> +    0b01_1011_0000_1000_1110, >> +    0b01_1100_1100_0001_1010, >> +    0b01_1101_0011_0011_1111, >> +    0b01_1110_1101_0111_0101, >> +    0b01_1111_0010_0101_0000, >> +    0b10_0000_1001_1101_0101, >> +    0b10_0001_0110_1111_0000, >> +    0b10_0010_1000_1011_1010, >> +    0b10_0011_0111_1001_1111, >> +    0b10_0100_1011_0000_1011, >> +    0b10_0101_0100_0010_1110, >> +    0b10_0110_1010_0110_0100, >> +    0b10_0111_0101_0100_0001, >> +    0b10_1000_1100_0110_1001, >> +]; >> + >> +/// Format info for low quality ECC. >> +const FORMAT_INFOS_QR_L: [u16; 8] = [ >> +    0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, >> +]; >> + >> +impl Version { >> +    /// Returns the smallest QR version than can hold these segments. >> +    fn from_segments(segments: &[&Segment<'_>]) -> Option { >> +        for v in (1..=40).map(|k| Version(k)) { >> +            if v.max_data() * 8 >= segments.iter().map(|s| >> s.total_size_bits(v)).sum() { >> +                return Some(v); >> +            } >> +        } >> +        None >> +    } >> + >> +    fn width(&self) -> u8 { >> +        (self.0 as u8) * 4 + 17 >> +    } >> + >> +    fn max_data(&self) -> usize { >> +        self.g1_blk_size() * self.g1_blocks() + (self.g1_blk_size() + >> 1) * self.g2_blocks() >> +    } >> + >> +    fn ec_size(&self) -> usize { >> +        VPARAM[self.0 - 1].0.len() >> +    } >> + >> +    fn g1_blocks(&self) -> usize { >> +        VPARAM[self.0 - 1].1 as usize >> +    } >> + >> +    fn g2_blocks(&self) -> usize { >> +        VPARAM[self.0 - 1].2 as usize >> +    } >> + >> +    fn g1_blk_size(&self) -> usize { >> +        VPARAM[self.0 - 1].3 as usize >> +    } >> + >> +    fn alignment_pattern(&self) -> &'static [u8] { >> +        &ALIGNMENT_PATTERNS[self.0 - 1] >> +    } >> + >> +    fn poly(&self) -> &'static [u8] { >> +        VPARAM[self.0 - 1].0 >> +    } >> + >> +    fn version_info(&self) -> u32 { >> +        if *self >= Version(7) { >> +            VERSION_INFORMATION[self.0 - 7] >> +        } else { >> +            0 >> +        } >> +    } >> +} >> + >> +/// Exponential table for Galois Field GF(256). >> +const EXP_TABLE: [u8; 256] = [ >> +    1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, >> 76, 152, 45, 90, 180, 117, >> +    234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, >> 74, 148, 53, 106, 212, 181, >> +    119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, >> 105, 210, 185, 111, 222, 161, >> +    95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, >> 120, 240, 253, 231, 211, 187, >> +    107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, >> 175, 67, 134, 17, 34, 68, 136, >> +    13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, >> 199, 147, 59, 118, 236, 197, >> +    151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, >> 33, 66, 132, 21, 42, 84, 168, >> +    77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, >> 115, 230, 209, 191, 99, 198, >> +    145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, >> 171, 75, 150, 49, 98, 196, 149, >> +    55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, >> 14, 28, 56, 112, 224, 221, 167, >> +    83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, >> 172, 69, 138, 9, 18, 36, 72, >> +    144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, >> 88, 176, 125, 250, 233, 207, >> +    131, 27, 54, 108, 216, 173, 71, 142, 1, >> +]; >> + >> +/// Reverse exponential table for Galois Field GF(256). >> +const LOG_TABLE: [u8; 256] = [ >> +    175, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, >> 4, 100, 224, 14, 52, 141, >> +    239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, >> 225, 36, 15, 33, 53, 147, 142, >> +    218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, >> 154, 9, 120, 77, 228, 114, >> +    166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, >> 145, 34, 136, 54, 208, 148, >> +    206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, >> 66, 182, 163, 195, 72, 126, >> +    110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, >> 21, 121, 43, 78, 212, 229, 172, >> +    115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, >> 222, 237, 49, 197, 254, 24, >> +    227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, >> 137, 46, 55, 63, 209, 91, 149, >> +    188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, >> 211, 171, 20, 42, 93, 158, 132, >> +    60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, >> 118, 196, 23, 73, 236, 127, 12, >> +    111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, >> 187, 204, 62, 90, 203, 89, 95, >> +    176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, >> 174, 213, 233, 230, 231, 173, >> +    232, 116, 214, 244, 234, 168, 80, 88, 175, >> +]; >> + >> +// 4 bits segment header. >> +const MODE_STOP: u16 = 0; >> +const MODE_NUMERIC: u16 = 1; >> +const MODE_BINARY: u16 = 4; >> +/// Padding bytes. >> +const PADDING: [u8; 2] = [236, 17]; >> + >> +/// Get the next 13 bits of data, starting at specified offset (in >> bits). >> +fn get_next_13b(data: &[u8], offset: usize) -> Option<(u16, usize)> { >> +    if offset < data.len() * 8 { >> +        let size = cmp::min(13, data.len() * 8 - offset); >> +        let byte_off = offset / 8; >> +        let bit_off = offset % 8; >> +        // `b` is 20 at max (`bit_off` <= 7 and `size` <= 13). >> +        let b = (bit_off + size) as u16; >> + >> +        let first_byte = (data[byte_off] << bit_off >> bit_off) as u16; >> + >> +        let number = match b { >> +            0..=8 => first_byte >> (8 - b), >> +            9..=16 => (first_byte << (b - 8)) + (data[byte_off + 1] >> >> (16 - b)) as u16, >> +            _ => { >> +                (first_byte << (b - 8)) >> +                    + ((data[byte_off + 1] as u16) << (b - 16)) >> +                    + (data[byte_off + 2] >> (24 - b)) as u16 >> +            } >> +        }; >> +        Some((number, size)) >> +    } else { >> +        None >> +    } >> +} >> + >> +/// Number of bits to encode characters in numeric mode. >> +const NUM_CHARS_BITS: [usize; 4] = [0, 4, 7, 10]; >> +const POW10: [u16; 4] = [1, 10, 100, 1000]; >> + >> +enum Segment<'a> { >> +    Numeric(&'a [u8]), >> +    Binary(&'a [u8]), >> +} >> + >> +impl Segment<'_> { >> +    fn get_header(&self) -> (u16, usize) { >> +        match self { >> +            Segment::Binary(_) => (MODE_BINARY, 4), >> +            Segment::Numeric(_) => (MODE_NUMERIC, 4), >> +        } >> +    } >> + >> +    // Returns the size of the length field in bits, depending on QR >> Version. >> +    fn length_bits_count(&self, version: Version) -> usize { >> +        let Version(v) = version; >> +        match self { >> +            Segment::Binary(_) => match v { >> +                1..=9 => 8, >> +                _ => 16, >> +            }, >> +            Segment::Numeric(_) => match v { >> +                1..=9 => 10, >> +                10..=26 => 12, >> +                _ => 14, >> +            }, >> +        } >> +    } >> + >> +    // Number of characters in the segment. >> +    fn character_count(&self) -> usize { >> +        match self { >> +            Segment::Binary(data) => data.len(), >> +            Segment::Numeric(data) => { >> +                let data_bits = data.len() * 8; >> +                let last_chars = match data_bits % 13 { >> +                    1 => 1, >> +                    k => (k + 1) / 3, >> +                }; >> +                // 4 decimal numbers per 13bits + remainder. >> +                4 * (data_bits / 13) + last_chars >> +            } >> +        } >> +    } >> + >> +    fn get_length_field(&self, version: Version) -> (u16, usize) { >> +        ( >> +            self.character_count() as u16, >> +            self.length_bits_count(version), >> +        ) >> +    } >> + >> +    fn total_size_bits(&self, version: Version) -> usize { >> +        let data_size = match self { >> +            Segment::Binary(data) => data.len() * 8, >> +            Segment::Numeric(_) => { >> +                let digits = self.character_count(); >> +                10 * (digits / 3) + NUM_CHARS_BITS[digits % 3] >> +            } >> +        }; >> +        // header + length + data. >> +        4 + self.length_bits_count(version) + data_size >> +    } >> + >> +    fn iter(&self) -> SegmentIterator<'_> { >> +        SegmentIterator { >> +            segment: self, >> +            offset: 0, >> +            carry: 0, >> +            carry_len: 0, >> +        } >> +    } >> +} >> + >> +struct SegmentIterator<'a> { >> +    segment: &'a Segment<'a>, >> +    offset: usize, >> +    carry: u16, >> +    carry_len: usize, >> +} >> + >> +impl Iterator for SegmentIterator<'_> { >> +    type Item = (u16, usize); >> + >> +    fn next(&mut self) -> Option { >> +        match self.segment { >> +            Segment::Binary(data) => { >> +                if self.offset < data.len() { >> +                    let byte = data[self.offset] as u16; >> +                    self.offset += 1; >> +                    Some((byte, 8)) >> +                } else { >> +                    None >> +                } >> +            } >> +            Segment::Numeric(data) => { >> +                if self.carry_len == 3 { >> +                    let out = (self.carry, >> NUM_CHARS_BITS[self.carry_len]); >> +                    self.carry_len = 0; >> +                    self.carry = 0; >> +                    Some(out) >> +                } else if let Some((bits, size)) = get_next_13b(data, >> self.offset) { >> +                    self.offset += size; >> +                    let new_chars = match size { >> +                        1 => 1, >> +                        k => (k + 1) / 3, >> +                    }; >> +                    if self.carry_len + new_chars > 3 { >> +                        self.carry_len = new_chars + self.carry_len - 3; >> +                        let out = ( >> +                            self.carry * POW10[new_chars - >> self.carry_len] >> +                                + bits / POW10[self.carry_len], >> +                            NUM_CHARS_BITS[3], >> +                        ); >> +                        self.carry = bits % POW10[self.carry_len]; >> +                        Some(out) >> +                    } else { >> +                        let out = ( >> +                            self.carry * POW10[new_chars] + bits, >> +                            NUM_CHARS_BITS[self.carry_len + new_chars], >> +                        ); >> +                        self.carry_len = 0; >> +                        Some(out) >> +                    } >> +                } else if self.carry_len > 0 { >> +                    let out = (self.carry, >> NUM_CHARS_BITS[self.carry_len]); >> +                    self.carry_len = 0; >> +                    Some(out) >> +                } else { >> +                    None >> +                } >> +            } >> +        } >> +    } >> +} >> + >> +struct EncodedMsg<'a> { >> +    data: &'a mut [u8], >> +    ec_size: usize, >> +    g1_blocks: usize, >> +    g2_blocks: usize, >> +    g1_blk_size: usize, >> +    g2_blk_size: usize, >> +    poly: &'static [u8], >> +    version: Version, >> +} >> + >> +/// Data to be put in the QR code, with correct segment encoding, >> padding, and >> +/// Error Code Correction. >> +impl EncodedMsg<'_> { >> +    fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> >> Option> { >> +        let version = Version::from_segments(segments)?; >> +        let ec_size = version.ec_size(); >> +        let g1_blocks = version.g1_blocks(); >> +        let g2_blocks = version.g2_blocks(); >> +        let g1_blk_size = version.g1_blk_size(); >> +        let g2_blk_size = g1_blk_size + 1; >> +        let poly = version.poly(); >> + >> +        // clear the output. >> +        data.fill(0); >> + >> +        let mut em = EncodedMsg { >> +            data: data, >> +            ec_size, >> +            g1_blocks, >> +            g2_blocks, >> +            g1_blk_size, >> +            g2_blk_size, >> +            poly, >> +            version, >> +        }; >> +        em.encode(segments); >> +        Some(em) >> +    } >> + >> +    /// Push bits of data at an offset (in bits). >> +    fn push(&mut self, offset: &mut usize, bits: (u16, usize)) { >> +        let (number, len_bits) = bits; >> +        let byte_off = *offset / 8; >> +        let bit_off = *offset % 8; >> +        let b = bit_off + len_bits; >> + >> +        match (bit_off, b) { >> +            (0, 0..=8) => { >> +                self.data[byte_off] = (number << (8 - b)) as u8; >> +            } >> +            (0, _) => { >> +                self.data[byte_off] = (number >> (b - 8)) as u8; >> +                self.data[byte_off + 1] = (number << (16 - b)) as u8; >> +            } >> +            (_, 0..=8) => { >> +                self.data[byte_off] |= (number << (8 - b)) as u8; >> +            } >> +            (_, 9..=16) => { >> +                self.data[byte_off] |= (number >> (b - 8)) as u8; >> +                self.data[byte_off + 1] = (number << (16 - b)) as u8; >> +            } >> +            _ => { >> +                self.data[byte_off] |= (number >> (b - 8)) as u8; >> +                self.data[byte_off + 1] = (number >> (b - 16)) as u8; >> +                self.data[byte_off + 2] = (number << (24 - b)) as u8; >> +            } >> +        } >> +        *offset += len_bits; >> +    } >> + >> +    fn add_segments(&mut self, segments: &[&Segment<'_>]) { >> +        let mut offset: usize = 0; >> + >> +        for s in segments.iter() { >> +            self.push(&mut offset, s.get_header()); >> +            self.push(&mut offset, s.get_length_field(self.version)); >> +            for bits in s.iter() { >> +                self.push(&mut offset, bits); >> +            } >> +        } >> +        self.push(&mut offset, (MODE_STOP, 4)); >> + >> +        let pad_offset = (offset + 7) / 8; >> +        for i in pad_offset..self.version.max_data() { >> +            self.data[i] = PADDING[(i & 1) ^ (pad_offset & 1)]; >> +        } >> +    } >> + >> +    fn error_code_for_blocks(&mut self, offset: usize, size: usize, >> ec_offset: usize) { >> +        let mut tmp: [u8; MAX_BLK_SIZE + MAX_EC_SIZE] = [0; >> MAX_BLK_SIZE + MAX_EC_SIZE]; >> + >> +        tmp[0..size].copy_from_slice(&self.data[offset..offset + size]); >> +        for i in 0..size { >> +            let lead_coeff = tmp[i] as usize; >> +            if lead_coeff == 0 { >> +                continue; >> +            } >> +            let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]); >> +            for (u, &v) in tmp[i + >> 1..].iter_mut().zip(self.poly.iter()) { >> +                *u ^= EXP_TABLE[(usize::from(v) + log_lead_coeff) % >> 255]; >> +            } >> +        } >> +        self.data[ec_offset..ec_offset + self.ec_size] >> +            .copy_from_slice(&tmp[size..size + self.ec_size]); >> +    } >> + >> +    fn compute_error_code(&mut self) { >> +        let mut offset = 0; >> +        let mut ec_offset = self.g1_blocks * self.g1_blk_size + >> self.g2_blocks * self.g2_blk_size; >> + >> +        for _ in 0..self.g1_blocks { >> +            self.error_code_for_blocks(offset, self.g1_blk_size, >> ec_offset); >> +            offset += self.g1_blk_size; >> +            ec_offset += self.ec_size; >> +        } >> +        for _ in 0..self.g2_blocks { >> +            self.error_code_for_blocks(offset, self.g2_blk_size, >> ec_offset); >> +            offset += self.g2_blk_size; >> +            ec_offset += self.ec_size; >> +        } >> +    } >> + >> +    fn encode(&mut self, segments: &[&Segment<'_>]) { >> +        self.add_segments(segments); >> +        self.compute_error_code(); >> +    } >> + >> +    fn iter(&self) -> EncodedMsgIterator<'_> { >> +        EncodedMsgIterator { >> +            em: self, >> +            offset: 0, >> +        } >> +    } >> +} >> + >> +/// Iterator, to retrieve the data in the interleaved order needed by >> QR code. >> +struct EncodedMsgIterator<'a> { >> +    em: &'a EncodedMsg<'a>, >> +    offset: usize, >> +} >> + >> +impl Iterator for EncodedMsgIterator<'_> { >> +    type Item = u8; >> + >> +    // Send the bytes in interleaved mode, first byte of first block >> of group1, >> +    // then first byte of second block of group1, ... >> +    fn next(&mut self) -> Option { >> +        let em = self.em; >> +        let blocks = em.g1_blocks + em.g2_blocks; >> +        let g1_end = em.g1_blocks * em.g1_blk_size; >> +        let g2_end = g1_end + em.g2_blocks * em.g2_blk_size; >> +        let ec_end = g2_end + em.ec_size * blocks; >> + >> +        if self.offset >= ec_end { >> +            return None; >> +        } >> + >> +        let offset = if self.offset < em.g1_blk_size * blocks { >> +            // group1 and group2 interleaved >> +            let blk = self.offset % blocks; >> +            let blk_off = self.offset / blocks; >> +            if blk < em.g1_blocks { >> +                blk * em.g1_blk_size + blk_off >> +            } else { >> +                g1_end + em.g2_blk_size * (blk - em.g1_blocks) + blk_off >> +            } >> +        } else if self.offset < g2_end { >> +            // last byte of group2 blocks >> +            let blk2 = self.offset - blocks * em.g1_blk_size; >> +            em.g1_blk_size * em.g1_blocks + blk2 * em.g2_blk_size + >> em.g2_blk_size - 1 >> +        } else { >> +            // EC blocks >> +            let ec_offset = self.offset - g2_end; >> +            let blk = ec_offset % blocks; >> +            let blk_off = ec_offset / blocks; >> + >> +            g2_end + blk * em.ec_size + blk_off >> +        }; >> +        self.offset += 1; >> +        Some(em.data[offset]) >> +    } >> +} >> + >> +/// A QR code image, encoded as a linear binary framebuffer. >> +/// 1 bit per module (pixel), each new line start at next byte boundary. >> +/// Max width is 177 for V40 QR code, so `u8` is enough for coordinate. >> +struct QrImage<'a> { >> +    data: &'a mut [u8], >> +    width: u8, >> +    stride: u8, >> +    version: Version, >> +} >> + >> +impl QrImage<'_> { >> +    fn new<'a, 'b>(em: &'b EncodedMsg<'b>, qrdata: &'a mut [u8]) -> >> QrImage<'a> { >> +        let width = em.version.width(); >> +        let stride = (width + 7) / 8; >> +        let data = qrdata; >> + >> +        let mut qr_image = QrImage { >> +            data, >> +            width, >> +            stride, >> +            version: em.version, >> +        }; >> +        qr_image.draw_all(em.iter()); >> +        qr_image >> +    } >> + >> +    fn clear(&mut self) { >> +        self.data.fill(0); >> +    } >> + >> +    // Set pixel to light color. >> +    fn set(&mut self, x: u8, y: u8) { >> +        let off = y as usize * self.stride as usize + x as usize / 8; >> +        let mut v = self.data[off]; >> +        v |= 0x80 >> (x % 8); >> +        self.data[off] = v; >> +    } >> + >> +    // Invert a module color. >> +    fn xor(&mut self, x: u8, y: u8) { >> +        let off = y as usize * self.stride as usize + x as usize / 8; >> +        self.data[off] ^= 0x80 >> (x % 8); >> +    } >> + >> +    // Draw a light square at (x, y) top left corner. >> +    fn draw_square(&mut self, x: u8, y: u8, size: u8) { >> +        for k in 0..size { >> +            self.set(x + k, y); >> +            self.set(x, y + k + 1); >> +            self.set(x + size, y + k); >> +            self.set(x + k + 1, y + size); >> +        } >> +    } >> + >> +    // Finder pattern: 3 8x8 square at the corners. >> +    fn draw_finders(&mut self) { >> +        self.draw_square(1, 1, 4); >> +        self.draw_square(self.width - 6, 1, 4); >> +        self.draw_square(1, self.width - 6, 4); >> +        for k in 0..8 { >> +            self.set(k, 7); >> +            self.set(self.width - k - 1, 7); >> +            self.set(k, self.width - 8); >> +        } >> +        for k in 0..7 { >> +            self.set(7, k); >> +            self.set(self.width - 8, k); >> +            self.set(7, self.width - 1 - k); >> +        } >> +    } >> + >> +    fn is_finder(&self, x: u8, y: u8) -> bool { >> +        let end = self.width - 8; >> +        (x < 8 && y < 8) || (x < 8 && y >= end) || (x >= end && y < 8) >> +    } >> + >> +    // Alignment pattern: 5x5 squares in a grid. >> +    fn draw_alignments(&mut self) { >> +        let positions = self.version.alignment_pattern(); >> +        for &x in positions.iter() { >> +            for &y in positions.iter() { >> +                if !self.is_finder(x, y) { >> +                    self.draw_square(x - 1, y - 1, 2); >> +                } >> +            } >> +        } >> +    } >> + >> +    fn is_alignment(&self, x: u8, y: u8) -> bool { >> +        let positions = self.version.alignment_pattern(); >> +        for &ax in positions.iter() { >> +            for &ay in positions.iter() { >> +                if self.is_finder(ax, ay) { >> +                    continue; >> +                } >> +                if x >= ax - 2 && x <= ax + 2 && y >= ay - 2 && y <= >> ay + 2 { >> +                    return true; >> +                } >> +            } >> +        } >> +        false >> +    } >> + >> +    // Timing pattern: 2 dotted line between the finder patterns. >> +    fn draw_timing_patterns(&mut self) { >> +        let end = self.width - 8; >> + >> +        for x in (9..end).step_by(2) { >> +            self.set(x, 6); >> +            self.set(6, x); >> +        } >> +    } >> + >> +    fn is_timing(&self, x: u8, y: u8) -> bool { >> +        x == 6 || y == 6 >> +    } >> + >> +    // Mask info: 15 bits around the finders, written twice for >> redundancy. >> +    fn draw_maskinfo(&mut self) { >> +        let info: u16 = FORMAT_INFOS_QR_L[0]; >> +        let mut skip = 0; >> + >> +        for k in 0..7 { >> +            if k == 6 { >> +                skip = 1; >> +            } >> +            if info & (1 << (14 - k)) == 0 { >> +                self.set(k + skip, 8); >> +                self.set(8, self.width - 1 - k); >> +            } >> +        } >> +        skip = 0; >> +        for k in 0..8 { >> +            if k == 2 { >> +                skip = 1; >> +            } >> +            if info & (1 << (7 - k)) == 0 { >> +                self.set(8, 8 - skip - k); >> +                self.set(self.width - 8 + k, 8); >> +            } >> +        } >> +    } >> + >> +    fn is_maskinfo(&self, x: u8, y: u8) -> bool { >> +        let end = self.width - 8; >> +        // Count the dark module as mask info. >> +        (x <= 8 && y == 8) || (y <= 8 && x == 8) || (x == 8 && y >= >> end) || (x >= end && y == 8) >> +    } >> + >> +    // Version info: 18bits written twice, close to the finders. >> +    fn draw_version_info(&mut self) { >> +        let vinfo = self.version.version_info(); >> +        let pos = self.width - 11; >> + >> +        if vinfo != 0 { >> +            for x in 0..3 { >> +                for y in 0..6 { >> +                    if vinfo & (1 << (x + y * 3)) == 0 { >> +                        self.set(x + pos, y); >> +                        self.set(y, x + pos); >> +                    } >> +                } >> +            } >> +        } >> +    } >> + >> +    fn is_version_info(&self, x: u8, y: u8) -> bool { >> +        let vinfo = self.version.version_info(); >> +        let pos = self.width - 11; >> + >> +        vinfo != 0 && ((x >= pos && x < pos + 3 && y < 6) || (y >= >> pos && y < pos + 3 && x < 6)) >> +    } >> + >> +    // Returns true if the module is reserved (Not usable for data >> and EC). >> +    fn is_reserved(&self, x: u8, y: u8) -> bool { >> +        self.is_alignment(x, y) >> +            || self.is_finder(x, y) >> +            || self.is_timing(x, y) >> +            || self.is_maskinfo(x, y) >> +            || self.is_version_info(x, y) >> +    } >> + >> +    // Last module to draw, at bottom left corner. >> +    fn is_last(&self, x: u8, y: u8) -> bool { >> +        x == 0 && y == self.width - 1 >> +    } >> + >> +    // Move to the next module according to QR code order. >> +    // From bottom right corner, to bottom left corner. >> +    fn next(&self, x: u8, y: u8) -> (u8, u8) { >> +        let x_adj = if x <= 6 { x + 1 } else { x }; >> +        let column_type = (self.width - x_adj) % 4; >> + >> +        match column_type { >> +            2 if y > 0 => (x + 1, y - 1), >> +            0 if y < self.width - 1 => (x + 1, y + 1), >> +            0 | 2 if x == 7 => (x - 2, y), >> +            _ => (x - 1, y), >> +        } >> +    } >> + >> +    // Find next module that can hold data. >> +    fn next_available(&self, x: u8, y: u8) -> (u8, u8) { >> +        let (mut x, mut y) = self.next(x, y); >> +        while self.is_reserved(x, y) && !self.is_last(x, y) { >> +            (x, y) = self.next(x, y); >> +        } >> +        (x, y) >> +    } >> + >> +    fn draw_data(&mut self, data: impl Iterator) { >> +        let (mut x, mut y) = (self.width - 1, self.width - 1); >> +        for byte in data { >> +            for s in 0..8 { >> +                if byte & (0x80 >> s) == 0 { >> +                    self.set(x, y); >> +                } >> +                (x, y) = self.next_available(x, y); >> +            } >> +        } >> +        // Set the remaining modules (0, 3 or 7 depending on version). >> +        // because 0 correspond to a light module. >> +        while !self.is_last(x, y) { >> +            if !self.is_reserved(x, y) { >> +                self.set(x, y); >> +            } >> +            (x, y) = self.next(x, y); >> +        } >> +    } >> + >> +    // Apply checkerboard mask to all non-reserved modules. >> +    fn apply_mask(&mut self) { >> +        for x in 0..self.width { >> +            for y in 0..self.width { >> +                if (x ^ y) % 2 == 0 && !self.is_reserved(x, y) { >> +                    self.xor(x, y); >> +                } >> +            } >> +        } >> +    } >> + >> +    // Draw the QR code with the provided data iterator. >> +    fn draw_all(&mut self, data: impl Iterator) { >> +        // First clear the table, as it may have already some data. >> +        self.clear(); >> +        self.draw_finders(); >> +        self.draw_alignments(); >> +        self.draw_timing_patterns(); >> +        self.draw_version_info(); >> +        self.draw_data(data); >> +        self.draw_maskinfo(); >> +        self.apply_mask(); >> +    } >> +} >> + >> +/// C entry point for the rust QR Code generator. >> +/// >> +/// Write the QR code image in the data buffer, and return the QR >> code width, >> +/// or 0, if the data doesn't fit in a QR code. >> +/// >> +/// * `url`: The base URL of the QR code. It will be encoded as >> Binary segment. >> +/// * `data`: A pointer to the binary data, to be encoded. if URL is >> NULL, it >> +///    will be encoded as binary segment, otherwise it will be encoded >> +///    efficiently as a numeric segment, and appended to the URL. >> +/// * `data_len`: Length of the data, that needs to be encoded, must >> be less >> +///    than data_size. >> +/// * `data_size`: Size of data buffer, it should be at least 4071 >> bytes to hold >> +///    a V40 QR code. It will then be overwritten with the QR code >> image. >> +/// * `tmp`: A temporary buffer that the QR code encoder will use, to >> write the >> +///    segments and ECC. >> +/// * `tmp_size`: Size of the temporary buffer, it must be at least >> 3706 bytes >> +///    long for V40. >> +/// >> +/// # Safety >> +/// >> +/// * `url` must be null or point at a nul-terminated string. >> +/// * `data` must be valid for reading and writing for `data_size` >> bytes. >> +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes. >> +/// >> +/// They must remain valid for the duration of the function call. >> + >> +#[no_mangle] >> +pub unsafe extern "C" fn drm_panic_qr_generate( >> +    url: *const i8, >> +    data: *mut u8, >> +    data_len: usize, >> +    data_size: usize, >> +    tmp: *mut u8, >> +    tmp_size: usize, >> +) -> u8 { >> +    if data_size < 4071 || tmp_size < 3706 || data_len > data_size { >> +        return 0; >> +    } >> +    // SAFETY: The caller ensures that `data` is a valid pointer for >> reading and >> +    // writing `data_size` bytes. >> +    let data_slice: &mut [u8] = unsafe { >> core::slice::from_raw_parts_mut(data, data_size) }; >> +    // SAFETY: The caller ensures that `tmp` is a valid pointer for >> reading and >> +    // writing `tmp_size` bytes. >> +    let tmp_slice: &mut [u8] = unsafe { >> core::slice::from_raw_parts_mut(tmp, tmp_size) }; >> +    if url.is_null() { >> +        match >> EncodedMsg::new(&[&Segment::Binary(&data_slice[0..data_len])], >> tmp_slice) { >> +            None => 0, >> +            Some(em) => { >> +                let qr_image = QrImage::new(&em, data_slice); >> +                qr_image.width >> +            } >> +        } >> +    } else { >> +        // SAFETY: The caller ensures that `url` is a valid pointer to a >> +        // nul-terminated string. >> +        let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) }; >> +        let segments = &[ >> +            &Segment::Binary(url_cstr.as_bytes()), >> +            &Segment::Numeric(&data_slice[0..data_len]), >> +        ]; >> +        match EncodedMsg::new(segments, tmp_slice) { >> +            None => 0, >> +            Some(em) => { >> +                let qr_image = QrImage::new(&em, data_slice); >> +                qr_image.width >> +            } >> +        } >> +    } >> +} >> + >> +/// Returns the maximum data size that can fit in a QR code of this >> version. >> +/// * `version`: QR code version, between 1-40. >> +/// * `url_len`: Length of the URL. >> +/// >> +/// * If `url_len` > 0, remove the 2 segments header/length and also >> count the >> +/// conversion to numeric segments. >> +/// * If `url_len` = 0, only removes 3 bytes for 1 binary segment. >> +#[no_mangle] >> +pub extern "C" fn drm_panic_qr_max_data_size(version: u8, url_len: >> usize) -> usize { >> +    if version < 1 || version > 40 { >> +        return 0; >> +    } >> +    let max_data = Version(version as usize).max_data(); >> + >> +    if url_len > 0 { >> +        // Binary segment (URL) 4 + 16 bits, numeric segment (kmsg) 4 >> + 12 bits => 5 bytes. >> +        if url_len + 5 >= max_data { >> +            0 >> +        } else { >> +            let max = max_data - url_len - 5; >> +            (max * 39) / 40 >> +        } >> +    } else { >> +        // Remove 3 bytes for the binary segment (header 4 bits, >> length 16 bits, stop 4bits). >> +        max_data - 3 >> +    } >> +} >> diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h >> index 73bb3f3d9ed9..7631548e7bbb 100644 >> --- a/include/drm/drm_panic.h >> +++ b/include/drm/drm_panic.h >> @@ -150,11 +150,15 @@ struct drm_scanout_buffer { >>   void drm_panic_register(struct drm_device *dev); >>   void drm_panic_unregister(struct drm_device *dev); >> +void drm_panic_init(void); >> +void drm_panic_exit(void); >>   #else >>   static inline void drm_panic_register(struct drm_device *dev) {} >>   static inline void drm_panic_unregister(struct drm_device *dev) {} >> +static inline void drm_panic_init(void) {} >> +static inline void drm_panic_exit(void) {} >>   #endif >