diff mbox series

[v2,4/4] drm/panic: Add a qr_code panic screen

Message ID 20240709084458.158659-5-jfalempe@redhat.com (mailing list archive)
State New, archived
Headers show
Series drm/panic: Add a qr_code panic screen | expand

Commit Message

Jocelyn Falempe July 9, 2024, 8:40 a.m. UTC
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 binary segment (ie raw
ascii) and only a maximum of 2953 bytes of kmsg data will be
available in the QR code.

You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION.

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)

Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
---
 drivers/gpu/drm/Kconfig         |   29 +
 drivers/gpu/drm/Makefile        |    1 +
 drivers/gpu/drm/drm_drv.c       |    3 +
 drivers/gpu/drm/drm_panic.c     |  247 ++++++++
 drivers/gpu/drm/drm_panic_qr.rs | 1004 +++++++++++++++++++++++++++++++
 include/drm/drm_panic.h         |    4 +
 6 files changed, 1288 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_panic_qr.rs

Comments

Greg KH July 9, 2024, 9:11 a.m. UTC | #1
On Tue, Jul 09, 2024 at 10:40:10AM +0200, Jocelyn Falempe wrote:
> +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
> +	  url parameter. If it's empty, the qr code will contain the kmsg as
> +	  uncompressed text only.

meta-comment, should we by default do this on a kernel.org domain so
that no specific distro has to worry about hosing this type of web
service?

thanks,

greg k-h
Greg KH July 9, 2024, 9:12 a.m. UTC | #2
On Tue, Jul 09, 2024 at 11:11:35AM +0200, Greg KH wrote:
> On Tue, Jul 09, 2024 at 10:40:10AM +0200, Jocelyn Falempe wrote:
> > +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
> > +	  url parameter. If it's empty, the qr code will contain the kmsg as
> > +	  uncompressed text only.
> 
> meta-comment, should we by default do this on a kernel.org domain so
> that no specific distro has to worry about hosing this type of web
> service?

Also, do you have the backend source for this to show how anyone can
host it themselves as well?  We can't add features to the kernel that no
one but closed-source implementations will use for obvious reasons.

thanks,

greg k-h
Miguel Ojeda July 9, 2024, 9:41 a.m. UTC | #3
Hi Jocelyn,

A quick docs-only review of the Rust side (some of these apply in
several cases -- I just wanted to give an overview for you to
consider).

On Tue, Jul 9, 2024 at 10:45 AM Jocelyn Falempe <jfalempe@redhat.com> wrote:
>
> +//! This is a simple qr encoder for DRM panic.
> +//!
> +//! Due to the Panic constraint, it doesn't allocate memory and does all

Perhaps clarify "Panic constraint" here?

> +//! the work on the stack or on the provided buffers. For
> +//! simplification, it only supports Low error correction, and apply the

"applies"?

> +//! first mask (checkboard). It will draw the smallest QRcode that can

"QR code"? "QR-code"?

In other places "QR-code" is used -- it would be ideal to be
consistent. (Although, isn't the common spelling "QR code"?)

> +//! contain the string passed as parameter. To get the most compact
> +//! QR-code, the start of the url is encoded as binary, and the

Probably "URL".

> +//! 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 waste 25% of data space, so the

"wastes"

> +//! whole stack trace won't fit in the QR-Code. So instead it encodes
> +//! every 13bits of input into 4 decimal digits, and then use the

"uses"

> +//! 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 numbers are
> +//! valid url parameter, so the website can do the reverse, to get the

"And the numbers are valid URL parameters"?

> +//! Inspired by this 3 projects, all under MIT license:

"these"

> +// Generator polynomials for QR Code, only those that are needed for Low quality

If possible, please remember to use periods at the end for both
comments and docs. It is very pedantic, but if possible we would like
to try to be consistent across subsystems on how the documentation
looks etc. If everything looks the same, it is also easy to
remember/check how to do it for new files and so on.

> +/// QRCode parameter 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)

We typically leave a newline after a list.

> +    // Return the smallest QR Version than can hold these segments
> +    fn from_segments(segments: &[&Segment<'_>]) -> Option<Version> {

Should be docs, even if private? i.e. `///`?

Also third person and period.

> +// padding bytes
> +const PADDING: [u8; 2] = [236, 17];

`///`?

> +/// get the next 13 bits of data, starting at specified offset (in bits)

Please capitalize.

> +        // b is 20 at max (bit_off <= 7 and size <= 13)

Please use Markdown for comments too.

> +/// EncodedMsg will hold the data to be put in the QR-Code, with correct segment
> +/// encoding, padding, and Error Code Correction.

Missing newline? In addition, for the title (i.e. first paragraph), we
try to keep it short/simple, e.g. you could perhaps say something
like:

    /// Data to be put in the QR code (with correct segment encoding,
padding, and error code correction).

> +/// QrImage
> +///
> +/// A QR-Code image, encoded as a linear binary framebuffer.

Please remove the title -- the second paragraph should be the title.

> +/// Max width is 177 for V40 QR code, so u8 is enough for coordinate.

`u8`

> +/// drm_panic_qr_generate()

You can remove this title.

> +/// C entry point for the rust QR Code generator.
> +///
> +/// Write the QR code image in the data buffer, and return the qrcode size, 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.

Typically we would write a colon. after the key, e.g.

    /// * `url`: the base URL of the QR code.

> +/// # Safety
> +///
> +/// * `url` must be null or point at a nul-terminated string.
> +/// * `data` must be valid for reading and writing for `data_size` bytes.
> +/// * `data_len` must be less than `data_size`.
> +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.

It would be nice to mention for which duration these need to hold,
e.g. the call or something else.

> +        // Safety: url must be a valid pointer to a nul-terminated string.

Please use the `// SAFETY: ` prefix instead, since it is how we tag
these (i.e. differently from from the `# Safety` section).

> +/// * `version` QR code version, between 1-40.

If something like this happens to be used in several places, you may
want to consider using transparent newtypes for them. This would allow
you to avoid having to document each use point and it would enrich the
signatures.

Thanks!

Cheers,
Miguel
Alice Ryhl July 9, 2024, 9:41 a.m. UTC | #4
On Tue, Jul 9, 2024 at 10:45 AM Jocelyn Falempe <jfalempe@redhat.com> 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 binary segment (ie raw
> ascii) and only a maximum of 2953 bytes of kmsg data will be
> available in the QR code.
>
> You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION.
>
> 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)
>
> Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>

[...]

> +/// drm_panic_qr_generate()
> +///
> +/// C entry point for the rust QR Code generator.
> +///
> +/// Write the QR code image in the data buffer, and return the qrcode size, 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.
> +/// * `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.
> +/// * `data_len` must be less than `data_size`.
> +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.

You don't allow data_len == data_size?

> +#[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;
> +    }

Since you explicitly check the data_len, it does not *need* to be a
safety requirement (but it can be). Even if it's wrong, violating the
requirement does not lead to memory safety.

> +    // Safety: data must be 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: tmp must be 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) };

These safety comments explain why these calls are dangerous, but
that's not what safety comments should do. They should explain why
this particular call is okay. In this case, it's because the caller of
drm_panic_qr_generate must follow the documented safety requirements
of the current function. The wording could look like this:

// SAFETY: Due to the safety requirements on this function, the caller
ensures that tmp is a valid pointer for reading and writing tmp_size
bytes.

The wording is not much different, but it's an important distinction.

(Also, safety comments are written SAFETY: not Safety:)

> +    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: url must be a valid pointer to a nul-terminated string.
> +        let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) };

// SAFETY: The caller ensures that url is a valid pointer to a
nul-terminated string.

> +        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
> +            }
> +        }
> +    }
> +}

Alice
Jocelyn Falempe July 9, 2024, 10:04 a.m. UTC | #5
On 09/07/2024 11:12, Greg KH wrote:
> On Tue, Jul 09, 2024 at 11:11:35AM +0200, Greg KH wrote:
>> On Tue, Jul 09, 2024 at 10:40:10AM +0200, Jocelyn Falempe wrote:
>>> +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
>>> +	  url parameter. If it's empty, the qr code will contain the kmsg as
>>> +	  uncompressed text only.
>>
>> meta-comment, should we by default do this on a kernel.org domain so
>> that no specific distro has to worry about hosing this type of web
>> service?
> 
> Also, do you have the backend source for this to show how anyone can
> host it themselves as well?  We can't add features to the kernel that no
> one but closed-source implementations will use for obvious reasons.

I've made a proof of concept backend here:
https://github.com/kdj0c/panic_report/

And the javascript to decode the kmsg trace is here (under MIT licence):
https://github.com/kdj0c/panic_report/blob/main/docs/panic_report.js

It uses the pako js library to uncompress the zlib data, which is also 
under MIT/Zlib licence https://github.com/nodeca/pako/

If kernel.org want to host a default service for that, that would be 
great. It can be linked with https://bugzilla.kernel.org to easily 
create a bug, or look for similar bugs.


Best regards,
Greg KH July 9, 2024, 10:12 a.m. UTC | #6
On Tue, Jul 09, 2024 at 12:04:02PM +0200, Jocelyn Falempe wrote:
> 
> 
> On 09/07/2024 11:12, Greg KH wrote:
> > On Tue, Jul 09, 2024 at 11:11:35AM +0200, Greg KH wrote:
> > > On Tue, Jul 09, 2024 at 10:40:10AM +0200, Jocelyn Falempe wrote:
> > > > +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
> > > > +	  url parameter. If it's empty, the qr code will contain the kmsg as
> > > > +	  uncompressed text only.
> > > 
> > > meta-comment, should we by default do this on a kernel.org domain so
> > > that no specific distro has to worry about hosing this type of web
> > > service?
> > 
> > Also, do you have the backend source for this to show how anyone can
> > host it themselves as well?  We can't add features to the kernel that no
> > one but closed-source implementations will use for obvious reasons.
> 
> I've made a proof of concept backend here:
> https://github.com/kdj0c/panic_report/
> 
> And the javascript to decode the kmsg trace is here (under MIT licence):
> https://github.com/kdj0c/panic_report/blob/main/docs/panic_report.js

SPDX lines are your friend, you might want to look into that for this
stuff :)

> It uses the pako js library to uncompress the zlib data, which is also under
> MIT/Zlib licence https://github.com/nodeca/pako/

Great, can you put that in the Kconfig help area for this option in your
next version?

> If kernel.org want to host a default service for that, that would be great.
> It can be linked with https://bugzilla.kernel.org to easily create a bug, or
> look for similar bugs.

Someone should at least propose it if this is going to be an option that
the kernel supports.

thanks,

greg k-h
Jocelyn Falempe July 9, 2024, 12:02 p.m. UTC | #7
On 09/07/2024 12:12, Greg KH wrote:
> On Tue, Jul 09, 2024 at 12:04:02PM +0200, Jocelyn Falempe wrote:
>>
>>
>> On 09/07/2024 11:12, Greg KH wrote:
>>> On Tue, Jul 09, 2024 at 11:11:35AM +0200, Greg KH wrote:
>>>> On Tue, Jul 09, 2024 at 10:40:10AM +0200, Jocelyn Falempe wrote:
>>>>> +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
>>>>> +	  url parameter. If it's empty, the qr code will contain the kmsg as
>>>>> +	  uncompressed text only.
>>>>
>>>> meta-comment, should we by default do this on a kernel.org domain so
>>>> that no specific distro has to worry about hosing this type of web
>>>> service?
>>>
>>> Also, do you have the backend source for this to show how anyone can
>>> host it themselves as well?  We can't add features to the kernel that no
>>> one but closed-source implementations will use for obvious reasons.
>>
>> I've made a proof of concept backend here:
>> https://github.com/kdj0c/panic_report/
>>
>> And the javascript to decode the kmsg trace is here (under MIT licence):
>> https://github.com/kdj0c/panic_report/blob/main/docs/panic_report.js
> 
> SPDX lines are your friend, you might want to look into that for this
> stuff :)

Sure, I've added the SPDX header for this file.
> 
>> It uses the pako js library to uncompress the zlib data, which is also under
>> MIT/Zlib licence https://github.com/nodeca/pako/
> 
> Great, can you put that in the Kconfig help area for this option in your
> next version?

Yes, I will add a link to the panic_report github project.
> 
>> If kernel.org want to host a default service for that, that would be great.
>> It can be linked with https://bugzilla.kernel.org to easily create a bug, or
>> look for similar bugs.
> 
> Someone should at least propose it if this is going to be an option that
> the kernel supports.

I hope someone will volunteer to do that, as I'm not really into web 
development. Also it's a bit early, drm panic is quite new, and needs 
more driver support to be really useful.
> thanks,
> 
> greg k-h
>
Jocelyn Falempe July 9, 2024, 3:09 p.m. UTC | #8
On 09/07/2024 11:41, Miguel Ojeda wrote:
> Hi Jocelyn,
> 
> A quick docs-only review of the Rust side (some of these apply in
> several cases -- I just wanted to give an overview for you to
> consider).

Thanks, I'll fix all typo/grammar you mentioned.
> 
> On Tue, Jul 9, 2024 at 10:45 AM Jocelyn Falempe <jfalempe@redhat.com> wrote:
>>
>> +//! This is a simple qr encoder for DRM panic.
>> +//!
>> +//! Due to the Panic constraint, it doesn't allocate memory and does all
> 
> Perhaps clarify "Panic constraint" here?
> 
>> +//! the work on the stack or on the provided buffers. For
>> +//! simplification, it only supports Low error correction, and apply the
> 
> "applies"?
> 
>> +//! first mask (checkboard). It will draw the smallest QRcode that can
> 
> "QR code"? "QR-code"?
> 
> In other places "QR-code" is used -- it would be ideal to be
> consistent. (Although, isn't the common spelling "QR code"?)

Agreed, I will replace all with "QR code".

> 
>> +//! contain the string passed as parameter. To get the most compact
>> +//! QR-code, the start of the url is encoded as binary, and the
> 
> Probably "URL".

Yes, I will run s/url/URL in the comments.
> 
>> +//! 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 waste 25% of data space, so the
> 
> "wastes"
> 
>> +//! whole stack trace won't fit in the QR-Code. So instead it encodes
>> +//! every 13bits of input into 4 decimal digits, and then use the
> 
> "uses"
> 
>> +//! 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 numbers are
>> +//! valid url parameter, so the website can do the reverse, to get the
> 
> "And the numbers are valid URL parameters"?
> 
>> +//! Inspired by this 3 projects, all under MIT license:
> 
> "these"
> 
>> +// Generator polynomials for QR Code, only those that are needed for Low quality
> 
> If possible, please remember to use periods at the end for both
> comments and docs. It is very pedantic, but if possible we would like
> to try to be consistent across subsystems on how the documentation
> looks etc. If everything looks the same, it is also easy to
> remember/check how to do it for new files and so on.

Sure, I will check this again.
> 
>> +/// QRCode parameter 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)
> 
> We typically leave a newline after a list.
> 
>> +    // Return the smallest QR Version than can hold these segments
>> +    fn from_segments(segments: &[&Segment<'_>]) -> Option<Version> {
> 
> Should be docs, even if private? i.e. `///`?
> 
> Also third person and period.
> 
>> +// padding bytes
>> +const PADDING: [u8; 2] = [236, 17];
> 
> `///`?
> 
>> +/// get the next 13 bits of data, starting at specified offset (in bits)
> 
> Please capitalize.
> 
>> +        // b is 20 at max (bit_off <= 7 and size <= 13)
> 
> Please use Markdown for comments too.
> 
>> +/// EncodedMsg will hold the data to be put in the QR-Code, with correct segment
>> +/// encoding, padding, and Error Code Correction.
> 
> Missing newline? In addition, for the title (i.e. first paragraph), we
> try to keep it short/simple, e.g. you could perhaps say something
> like:
> 
>      /// Data to be put in the QR code (with correct segment encoding,
> padding, and error code correction).
> 
>> +/// QrImage
>> +///
>> +/// A QR-Code image, encoded as a linear binary framebuffer.
> 
> Please remove the title -- the second paragraph should be the title.
> 
>> +/// Max width is 177 for V40 QR code, so u8 is enough for coordinate.
> 
> `u8`
> 
>> +/// drm_panic_qr_generate()
> 
> You can remove this title.
> 
>> +/// C entry point for the rust QR Code generator.
>> +///
>> +/// Write the QR code image in the data buffer, and return the qrcode size, 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.
> 
> Typically we would write a colon. after the key, e.g.
> 
>      /// * `url`: the base URL of the QR code.
> 
>> +/// # Safety
>> +///
>> +/// * `url` must be null or point at a nul-terminated string.
>> +/// * `data` must be valid for reading and writing for `data_size` bytes.
>> +/// * `data_len` must be less than `data_size`.
>> +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.
> 
> It would be nice to mention for which duration these need to hold,
> e.g. the call or something else.

Yes, it's until the function returns, I will add this precision.

> 
>> +        // Safety: url must be a valid pointer to a nul-terminated string.
> 
> Please use the `// SAFETY: ` prefix instead, since it is how we tag
> these (i.e. differently from from the `# Safety` section).
> 
>> +/// * `version` QR code version, between 1-40.
> 
> If something like this happens to be used in several places, you may
> want to consider using transparent newtypes for them. This would allow
> you to avoid having to document each use point and it would enrich the
> signatures.
> 

I used to list all QR versions in an enum, but I find it a bit too much 
boilerplate to ensure the version is between 1 and 40.
By transparent newtypes, you mean adding "#[repr(transparent)]" to a 
struct ?
I don't plan to add more "version" usage, so probably not worth it.

> Thanks!
> 
> Cheers,
> Miguel
> 

Best regards,
Jocelyn Falempe July 9, 2024, 3:21 p.m. UTC | #9
On 09/07/2024 11:41, Alice Ryhl wrote:
> On Tue, Jul 9, 2024 at 10:45 AM Jocelyn Falempe <jfalempe@redhat.com> 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 binary segment (ie raw
>> ascii) and only a maximum of 2953 bytes of kmsg data will be
>> available in the QR code.
>>
>> You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION.
>>
>> 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)
>>
>> Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
> 
> [...]
> 
>> +/// drm_panic_qr_generate()
>> +///
>> +/// C entry point for the rust QR Code generator.
>> +///
>> +/// Write the QR code image in the data buffer, and return the qrcode size, 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.
>> +/// * `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.
>> +/// * `data_len` must be less than `data_size`.
>> +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.
> 
> You don't allow data_len == data_size?

In fact, the QR code will always be larger than the data you want to put 
into, because it adds segment header/length and ECC, so it doesn't make 
sense in practice to have data_len == data_size.
data_size must be at least 4071 bytes, and the maximum data_len you can 
put in a V40 is 3703 bytes.

> 
>> +#[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;
>> +    }
> 
> Since you explicitly check the data_len, it does not *need* to be a
> safety requirement (but it can be). Even if it's wrong, violating the
> requirement does not lead to memory safety.

Ok, that makes sense, I will move it to the previous section.
> 
>> +    // Safety: data must be 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: tmp must be 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) };
> 
> These safety comments explain why these calls are dangerous, but
> that's not what safety comments should do. They should explain why
> this particular call is okay. In this case, it's because the caller of
> drm_panic_qr_generate must follow the documented safety requirements
> of the current function. The wording could look like this:
> 
> // SAFETY: Due to the safety requirements on this function, the caller
> ensures that tmp is a valid pointer for reading and writing tmp_size
> bytes.
> 
> The wording is not much different, but it's an important distinction.

Ok, I will update it, and add the expected lifetime as Miguel pointed out.
> 
> (Also, safety comments are written SAFETY: not Safety:)
> 
>> +    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: url must be a valid pointer to a nul-terminated string.
>> +        let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) };
> 
> // SAFETY: The caller ensures that url is a valid pointer to a
> nul-terminated string.

ok
> 
>> +        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
>> +            }
>> +        }
>> +    }
>> +}
> 
> Alice
> 

Best regards,
Miguel Ojeda July 10, 2024, 9 a.m. UTC | #10
On Tue, Jul 9, 2024 at 5:10 PM Jocelyn Falempe <jfalempe@redhat.com> wrote:
>
> I used to list all QR versions in an enum, but I find it a bit too much
> boilerplate to ensure the version is between 1 and 40.
> By transparent newtypes, you mean adding "#[repr(transparent)]" to a
> struct ?
> I don't plan to add more "version" usage, so probably not worth it.

Yeah, that is what I meant. It may not be worth it in that case -- it
is just something we should generally consider when we see "raw" types
appear in parameters that need extra documentation/preconditions, but
sometimes it simply does not make sense.

Thanks!

Cheers,
Miguel
diff mbox series

Patch

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 6dd0016fc9cd..4029046d9952 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -149,6 +149,35 @@  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
+	  url parameter. If it's empty, the qr code will contain the kmsg as
+	  uncompressed text only.
+
+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 range 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 450585374ca9..8c7895194a04 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -18,6 +18,8 @@ 
 #include <linux/overflow.h>
 #include <linux/printk.h>
 #include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/zlib.h>
 
 #include <drm/drm_drv.h>
 #include <drm/drm_fourcc.h>
@@ -26,6 +28,7 @@ 
 #include <drm/drm_panic.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_print.h>
+#include <drm/drm_rect.h>
 
 MODULE_AUTHOR("Jocelyn Falempe");
 MODULE_DESCRIPTION("DRM panic handler");
@@ -621,6 +624,232 @@  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 + 1, '\n');
+		if (!kmsg)
+			return -EINVAL;
+		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 +868,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 +994,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..75cd945bc01c
--- /dev/null
+++ b/drivers/gpu/drm/drm_panic_qr.rs
@@ -0,0 +1,1004 @@ 
+// SPDX-License-Identifier: MIT
+
+//! This is a simple qr encoder for DRM panic.
+//!
+//! Due to the Panic constraint, it doesn'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 apply the
+//! first mask (checkboard). 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 waste 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 use 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 numbers are
+//! valid url parameter, so the website can do the reverse, to get the
+//! binary data.
+//!
+//! Inspired by this 3 projects, all under MIT license:
+//!
+//! * <https://github.com/kennytm/qrcode-rust>
+//! * <https://github.com/erwanvivien/fast_qr>
+//! * <https://github.com/bjguillot/qr>
+
+use core::cmp;
+use kernel::str::CStr;
+
+const __LOG_PREFIX: &[u8] = b"rust_qrcode\0";
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
+struct Version(usize);
+
+// Generator polynomials for QR Code, 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,
+];
+
+/// QRCode parameter 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 EC
+const FORMAT_INFOS_QR_L: [u16; 8] = [
+    0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976,
+];
+
+impl Version {
+    // Return the smallest QR Version than can hold these segments
+    fn from_segments(segments: &[&Segment<'_>]) -> Option<Version> {
+        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),
+        }
+    }
+
+    // Return 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<Self::Item> {
+        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,
+}
+
+/// EncodedMsg will hold the data to be put in the QR-Code, with correct segment
+/// encoding, padding, and Error Code Correction.
+/// It also implements an iterator to retrieve the data interleaved to draw the
+/// QR-code image.
+impl EncodedMsg<'_> {
+    fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> Option<EncodedMsg<'a>> {
+        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,
+        }
+    }
+}
+
+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<Self::Item> {
+        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])
+    }
+}
+
+/// QrImage
+///
+/// A QR-Code image, encoded as a linear binary framebuffer.
+/// 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 pixel 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 are 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))
+    }
+
+    // Return true if the pixel is reserved (ie 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)
+    }
+
+    fn is_last(&self, x: u8, y: u8) -> bool {
+        x == 0 && y == self.width - 1
+    }
+
+    // Move to the next pixel 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 pixel 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<Item = u8>) {
+        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 pixels (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 checkboard 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 qrcode with the provided data iterator
+    fn draw_all(&mut self, data: impl Iterator<Item = u8>) {
+        // 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();
+    }
+}
+
+/// drm_panic_qr_generate()
+///
+/// C entry point for the rust QR Code generator.
+///
+/// Write the QR code image in the data buffer, and return the qrcode size, 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.
+/// * `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.
+/// * `data_len` must be less than `data_size`.
+/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.
+
+#[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: data must be 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: tmp must be 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: url must be 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
+            }
+        }
+    }
+}
+
+/// drm_panic_qr_max_data_size()
+///
+/// * `version` QR code version, between 1-40.
+/// * `url_len` Length of the url.
+///
+/// Returns the maximum data size that can fit in a QR code of this version.
+/// * 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