[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1103735: syslinux-efi: Early error messages are invisible



Package: syslinux-efi
Version: 3:6.04~git20190206.bf6db5b4+dfsg1-3.1
Severity: normal
Tags: patch

Dear Maintainer,

While debugging #1103692, I came across an issue where error messages
generated early in the syslinux boot process through writestr() didn't
end up appearing on-screen.  Instead, it appeared that only the cursor
moved.

I discovered that it's because the early code passes a video attribute
of 0 to the efi_write_char() function, which is black-on-black, and
produces invisible output.

The output can actually be read on the serial line.  For example, if we
have a serial line dump in the file serial.dump, and we have problems
with loading ldlinux.e32, the following can be used to read the message:

    $ ansi2txt < serial.dump > serial.log
    $ tail -n4 serial.log
    Shell> FS0:\EFI\syslinux\syslinux.efi

    Failed to load ldlinux.e32
    Shell>

Detailed steps on reproducing this issue are present in #1103692,
and if the issue reported in that bug gets fixed, you can easily
trigger this error message by deleting the ldlinux.e32 file.

Another issue which might render the error message unreadable is that
the error message doesn't contain a newline marker, which might cause
the EFI shell command prompt to overwrite the message.  This happens
with OVMF under Qemu.


Yet another minor issue is that the efi_scroll_up() function generates
a CRLF pair on the reverse order, LFCR, which can confuse some text
editors when viewing the logs, so it would be good to put them into
the correct order.

The efi_scroll_up() function also contains a bug where it ignores
the number of rows to scroll up by and always scrolls once, but this
seems to be intentional, as it's used to mask a different bug in
the ansicon module that causes it to scroll way more than it should
under EFI.

The ansicon module is used by ldlinux, and this masked bug can be seen
if you implement the rows parameter in efi_scroll_up() and try to boot
a kernel with the default boot prompt, the "Loading <xyz>..." messages
will be spaced apart with a ludicrous amount of spaces.  Fixing this
properly is outside of the scope of this task, so for now, I think it's
okay to ignore the "rows" parameter in efi_scroll_up(), as ansicon
appears to be the only place where it's used.


Another related thing to keep in mind is that printf() and friends
don't work in this early mode.  That appears to be intentional though,
and it only affects debugging messages.  The printf() family tends
to be enabled through openconsole(), and this happens once the ldlinux
module is loaded, but in the bare syslinux.efi environment, the function
is not present (trying to use it gives an undefined symbol error),
though its behavior can thankfully be recreated pretty easily by just
adding the following lines somewhere before you start using debugging
statements with the printf() family of functions:

    close(1);
    close(2);
    opendev(&dev_null_r, &dev_stdcon_w, O_WRONLY);
    opendev(&dev_null_r, &dev_stdcon_w, O_WRONLY);


I'm attaching a patch that updates the writechr() implementation
for EFI by including a default color attribute of light gray on black,
which matches what the BIOS implementation of writechr() does.

The patch additionally also updates efi_write_char() to not change
the color attribute unconditionally whenever a character is written,
and fixes the CR LF order in efi_scroll_up().

A newline character is also added at the end of the ldlinux loading
error message.  This newline is added in efi_main() and not
in load_env32() to be consistent with how it's implemented under BIOS.
The load_env32() function is shared between EFI and BIOS, and under
BIOS, the newline is also added as part of the following error message
which tells the user that boot failed, e.g. the err_bootfailed string
in core/diskfs.inc.


Only the writechr() update and newline addition is strictly neccessary
to fix the bug this bug report is about, but the other updates make
the screen output process more efficient and improve the usability
of the generated logs with text editors.


With this patch, the error message about ldlinux is dispalyed correctly,
informing the user about a potentially misconfigured setup, e.g.
with the ldlinux.e32 / ldlinux.e64 file not being present.


Best Regards,
Marek


-- System Information:
Debian Release: trixie/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: amd64 (x86_64)

Kernel: Linux 6.14.3 (SMP w/16 CPU threads; PREEMPT)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

syslinux-efi depends on no packages.

Versions of packages syslinux-efi recommends:
ii  syslinux-common  3:6.04~git20190206.bf6db5b4+dfsg1-3.1

Versions of packages syslinux-efi suggests:
ii  dosfstools  4.2-1.2

-- no debconf information
--- syslinux-6.04~git20190206.bf6db5b4+dfsg1.orig/efi/console.c
+++ syslinux-6.04~git20190206.bf6db5b4+dfsg1/efi/console.c
@@ -34,7 +34,7 @@ void efi_console_restore(void)
 
 __export void writechr(char data)
 {
-	efi_write_char(data, 0);
+	efi_write_char(data, EFI_BACKGROUND_BLACK | EFI_LIGHTGRAY);
 }
 
 static inline EFI_STATUS open_protocol(EFI_HANDLE handle, EFI_GUID *protocol,
--- syslinux-6.04~git20190206.bf6db5b4+dfsg1.orig/efi/main.c
+++ syslinux-6.04~git20190206.bf6db5b4+dfsg1/efi/main.c
@@ -255,7 +255,10 @@ void efi_write_char(uint8_t ch, uint8_t
 	SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
 	uint16_t c[2];
 
-	uefi_call_wrapper(out->SetAttribute, 2, out, attribute);
+	if (ST->ConOut->Mode->Attribute != attribute)
+	{
+		uefi_call_wrapper(out->SetAttribute, 2, out, attribute);
+	}
 
 	/* Lookup primary Unicode encoding in the system codepage */
 	c[0] = codepage.uni[0][ch];
@@ -281,8 +284,8 @@ static void efi_set_cursor(int x, int y,
 
 static void efi_scroll_up(uint8_t cols, uint8_t rows, uint8_t attribute)
 {
-	efi_write_char('\n', 0);
-	efi_write_char('\r', 0);
+	efi_write_char('\r', attribute);
+	efi_write_char('\n', attribute);
 }
 
 static void efi_get_mode(int *cols, int *rows)
@@ -1384,7 +1387,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EF
 	} while (status == EFI_SUCCESS);
 
 	if (!setjmp(load_error_buf))
+	{
 		load_env32(NULL);
+		writestr("\r\n");
+	}
 
 	/* load_env32() failed.. cancel timer and bailout */
 	status = cancel_timer(timer_ev);

Reply to: