From 59546ea9d3e3e1864744a10ec35efdd12cc67763 Mon Sep 17 00:00:00 2001 From: Sriprad Potukuchi Date: Fri, 23 Aug 2024 02:02:43 +0530 Subject: [PATCH] blog: Add GSoC'24 UEFI GOP, Part IV Post Signed-off-by: Sriprad Potukuchi --- content/blog/2024-08-22-gsoc-uefi-gop.mdx | 67 ++++++++++++++++++++++ public/images/uefi-gop-colored.png | Bin 0 -> 72875 bytes 2 files changed, 67 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..59dcc7eb --- /dev/null +++ b/content/blog/2024-08-22-gsoc-uefi-gop.mdx @@ -0,0 +1,67 @@ +--- +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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