From 5bb8c1f600b0ac045c45ac3f6411056a787febaa Mon Sep 17 00:00:00 2001 From: Sriprad Potukuchi Date: Fri, 23 Aug 2024 02:02:43 +0530 Subject: [PATCH] Add GSoC'24 UEFI GOP, Part IV Post Signed-off-by: Sriprad Potukuchi --- content/blog/2024-08-22-gsoc-uefi-gop.mdx | 65 ++++++++++++++++++++++ public/images/uefi-gop-colored.png | Bin 0 -> 72875 bytes 2 files changed, 65 insertions(+) create mode 100644 content/blog/2024-08-22-gsoc-uefi-gop.mdx create mode 100644 public/images/uefi-gop-colored.png diff --git a/content/blog/2024-08-22-gsoc-uefi-gop.mdx b/content/blog/2024-08-22-gsoc-uefi-gop.mdx new file mode 100644 index 00000000..f88e0e96 --- /dev/null +++ b/content/blog/2024-08-22-gsoc-uefi-gop.mdx @@ -0,0 +1,65 @@ +--- +title: "GSoC'24: UEFI Graphics Output Protocol Support in Unikraft, Part IV" +description: | + This is the fourth post in a series of posts where I talk about my progress with the project. +publishedDate: 2024-08-22 +image: /images/unikraft-gsoc24.png +authors: +- Sriprad Potukuchi +tags: +- gsoc +- gsoc24 +- uefi +- booting +--- + +## Project Overview + +The widely available and standardized [UEFI Graphics Output Protocol](https://uefi.org/specs/UEFI/2.10/12_Protocols_Console_Support.html#efi-graphics-output-protocol) (GOP) interface is an excellent alternative to VGA or serial port consoles for printing logs to the screen. + +This project aims to implement a UEFI GOP based console. +For more information, check out [Part I](https://unikraft.org/blog/2024-06-18-gsoc-uefi-gop), [Part II](https://unikraft.org/blog/2024-07-10-gsoc-uefi-gop) and [Part III](https://unikraft.org/blog/2024-08-07-gsoc-uefi-gop) of this series. + +## Progress + +All the work referred to here can be found in [this draft PR](https://github.com/unikraft/unikraft/pull/1448). + +## ASCII code support + +Added support for ASCII codes `\a`, `\b`, `\t`. + +## Colored output support + +Enabling the configuration option `CONFIG_LIBUKDEBUG_ANSI_COLOR` generates colored logs with the use of ANSI escape codes. These codes are now parsed and the logs are colored appropriately by the GOP console. All the new code has been pushed to the [draft PR](https://github.com/unikraft/unikraft/pull/1448)! + + + +ANSI escape codes control various paramters of text output such as foreground color, background color, formatting etc. + +These escape codes are prefixed with the escape character, which has the ASCII value `27` or `033` in octal or `0x1b` in hex. + +The format of ANSI color codes is `\033[m`. +There are many of these codes, but the focus of my implementation is on parsing just the color codes that the Unikraft kernel outputs in in logs when `CONFIG_LIBUKDEBUG_ANSI_COLOR` is on, which currently are: + +| Color Name | Foreground Color Code | Background Color Code | +| :--------- | :-------------------- | :-------------------- | +| Reset | `0` | `0` | +| Black | `30` | `40` | +| Red | `31` | `41` | +| Green | `32` | `42` | +| Yellow | `33` | `43` | +| Blue | `34` | `44` | +| Magenta | `35` | `45` | +| Cyan | `36` | `46` | +| White | `37` | `47` | + +Full reference [here](https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797). + +For example, `\033[37m\033[44mHello World!` would print `Hello World!` in white on a blue background. The order in which the foreground and the background sequences are issued does not matter. + +## Acknowledgement + +I would once again like to thank all the great Unikraft folk for their continued support! This work would not have been possible without them :heart: diff --git a/public/images/uefi-gop-colored.png b/public/images/uefi-gop-colored.png new file mode 100644 index 0000000000000000000000000000000000000000..64cb5562544dbfd85d2aa61283862321c33e2d24 GIT binary patch literal 72875 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