[PATCH v1 31/54] efi: loader: protocol: add unicode collation support

Ahmad Fatoum a.fatoum at pengutronix.de
Thu Dec 18 02:37:51 PST 2025


Like with the HII support before it, implementing this protocol is also
necessary for the EFI shell to run.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 efi/loader/protocols/Kconfig             |  18 ++
 efi/loader/protocols/Makefile            |   1 +
 efi/loader/protocols/unicode_collation.c | 329 +++++++++++++++++++++++
 include/efi/protocol/unicode_collation.h |  24 ++
 4 files changed, 372 insertions(+)
 create mode 100644 efi/loader/protocols/unicode_collation.c
 create mode 100644 include/efi/protocol/unicode_collation.h

diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
index 4ed7499da4a2..8c8bfabd7c0f 100644
--- a/efi/loader/protocols/Kconfig
+++ b/efi/loader/protocols/Kconfig
@@ -13,4 +13,22 @@ config EFI_LOADER_HII
 	  barebox implements enough of its features to be able to run the UEFI
 	  Shell, but not more than that.
 
+config EFI_LOADER_UNICODE_COLLATION_PROTOCOL2
+	bool "Unicode collation protocol"
+	default y
+	help
+	  The Unicode collation protocol is used for lexical comparisons. It is
+	  required to run the UEFI shell.
+
+config EFI_LOADER_UNICODE_CAPITALIZATION
+	bool "Support Unicode capitalization"
+	default y
+	depends on EFI_LOADER_UNICODE_COLLATION_PROTOCOL2
+	select UNICODE_CAPITALIZATION
+	help
+	  Select this option to enable correct handling of the capitalization of
+	  Unicode codepoints in the range 0x0000-0xffff. If this option is not
+	  set, only the the correct handling of the letters of the codepage
+	  used by the FAT file system is ensured.
+
 endmenu
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index f4a9c0650fd9..b6e39b0666da 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DISK) += disk.o
 obj-$(CONFIG_VIDEO) += gop.o
 obj-$(CONFIG_CONSOLE_FULL) += console.o
 obj-$(CONFIG_EFI_LOADER_HII) += hii.o hii_config.o
+obj-$(CONFIG_EFI_LOADER_UNICODE_COLLATION_PROTOCOL2) += unicode_collation.o
diff --git a/efi/loader/protocols/unicode_collation.c b/efi/loader/protocols/unicode_collation.c
new file mode 100644
index 000000000000..4d9a26501723
--- /dev/null
+++ b/efi/loader/protocols/unicode_collation.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Unicode collation protocol
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk at gmx.de>
+ */
+
+#include <charset.h>
+#include <charset.h>
+#include <linux/printk.h>
+#include <efi/guid.h>
+#include <efi/loader.h>
+#include <efi/loader/trace.h>
+#include <efi/loader/object.h>
+#include <efi/error.h>
+#include <efi/protocol/unicode_collation.h>
+
+/* Characters that may not be used in FAT 8.3 file names */
+static const char illegal[] = "+,<=>:;\"/\\|?*[]\x7f";
+
+/*
+ * EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
+ * Linux defaults to codepage 437 for FAT 8.3 file names.
+ */
+/* Unicode code points for code page 437 characters 0x80 - 0xff */
+static const u16 *codepage = codepage_437;
+
+/**
+ * efi_stri_coll() - compare utf-16 strings case-insenitively
+ *
+ * @this:	unicode collation protocol instance
+ * @s1:		first string
+ * @s2:		second string
+ *
+ * This function implements the StriColl() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return:	0: s1 == s2, > 0: s1 > s2, < 0: s1 < s2
+ */
+static efi_intn_t EFIAPI efi_stri_coll(
+		struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2)
+{
+	s32 c1, c2;
+	efi_intn_t ret = 0;
+
+	EFI_ENTRY("%p, %ls, %ls", this, s1, s2);
+	for (; *s1 | *s2; ++s1, ++s2) {
+		c1 = utf_to_upper(*s1);
+		c2 = utf_to_upper(*s2);
+		if (c1 < c2) {
+			ret = -1;
+			goto out;
+		} else if (c1 > c2) {
+			ret = 1;
+			goto out;
+		}
+	}
+out:
+	EFI_EXIT(EFI_SUCCESS);
+	return ret;
+}
+
+/**
+ * next_lower() - get next codepoint converted to lower case
+ *
+ * @string:	pointer to u16 string, on return advanced by one codepoint
+ * Return:	first codepoint of string converted to lower case
+ */
+static s32 next_lower(const u16 **string)
+{
+	return utf_to_lower(utf16_get(string));
+}
+
+/**
+ * metai_match() - compare utf-16 string with a pattern string case-insenitively
+ *
+ * @string:	string to compare
+ * @pattern:	pattern string
+ *
+ * The pattern string may use these:
+ *	- * matches >= 0 characters
+ *	- ? matches 1 character
+ *	- [<char1><char2>...<charN>] match any character in the set
+ *	- [<char1>-<char2>] matches any character in the range
+ *
+ * This function is called my efi_metai_match().
+ *
+ * For '*' pattern searches this function calls itself recursively.
+ * Performance-wise this is suboptimal, especially for multiple '*' wildcards.
+ * But it results in simple code.
+ *
+ * Return:	true if the string is matched.
+ */
+static bool metai_match(const u16 *string, const u16 *pattern)
+{
+	s32 first, s, p;
+
+	for (; *string && *pattern;) {
+		const u16 *string_old = string;
+
+		s = next_lower(&string);
+		p = next_lower(&pattern);
+
+		switch (p) {
+		case '*':
+			/* Match 0 or more characters */
+			for (;; s = next_lower(&string)) {
+				if (metai_match(string_old, pattern))
+					return true;
+				if (!s)
+					return false;
+				string_old = string;
+			}
+		case '?':
+			/* Match any one character */
+			break;
+		case '[':
+			/* Match any character in the set */
+			p = next_lower(&pattern);
+			first = p;
+			if (first == ']')
+				/* Empty set */
+				return false;
+			p = next_lower(&pattern);
+			if (p == '-') {
+				/* Range */
+				p = next_lower(&pattern);
+				if (s < first || s > p)
+					return false;
+				p = next_lower(&pattern);
+				if (p != ']')
+					return false;
+			} else {
+				/* Set */
+				bool hit = false;
+
+				if (s == first)
+					hit = true;
+				for (; p && p != ']';
+				     p = next_lower(&pattern)) {
+					if (p == s)
+						hit = true;
+				}
+				if (!hit || p != ']')
+					return false;
+			}
+			break;
+		default:
+			/* Match one character */
+			if (p != s)
+				return false;
+		}
+	}
+	if (!*pattern && !*string)
+		return true;
+	return false;
+}
+
+/**
+ * efi_metai_match() - compare utf-16 string with a pattern string
+ *		       case-insenitively
+ *
+ * @this:	unicode collation protocol instance
+ * @string:	string to compare
+ * @pattern:	pattern string
+ *
+ * The pattern string may use these:
+ *	- * matches >= 0 characters
+ *	- ? matches 1 character
+ *	- [<char1><char2>...<charN>] match any character in the set
+ *	- [<char1>-<char2>] matches any character in the range
+ *
+ * This function implements the MetaMatch() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * Return:	true if the string is matched.
+ */
+static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
+				   const u16 *string, const u16 *pattern)
+{
+	bool ret;
+
+	EFI_ENTRY("%p, %ls, %ls", this, string, pattern);
+	ret =  metai_match(string, pattern);
+	EFI_EXIT(EFI_SUCCESS);
+	return ret;
+}
+
+/**
+ * efi_str_lwr() - convert to lower case
+ *
+ * @this:	unicode collation protocol instance
+ * @string:	string to convert
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrLwr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
+			       u16 *string)
+{
+	EFI_ENTRY("%p, %ls", this, string);
+	for (; *string; ++string)
+		*string = utf_to_lower(*string);
+	EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_str_upr() - convert to upper case
+ *
+ * @this:	unicode collation protocol instance
+ * @string:	string to convert
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrUpr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this,
+			       u16 *string)
+{
+	EFI_ENTRY("%p, %ls", this, string);
+	for (; *string; ++string)
+		*string = utf_to_upper(*string);
+	EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - convert an 8.3 file name from an OEM codepage to Unicode
+ *
+ * @this:	unicode collation protocol instance
+ * @fat_size:	size of the string to convert
+ * @fat:	string to convert
+ * @string:	converted string
+ *
+ * This function implements the FatToStr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_fat_to_str(struct efi_unicode_collation_protocol *this,
+				  efi_uintn_t fat_size, char *fat, u16 *string)
+{
+	efi_uintn_t i;
+	u16 c;
+
+	EFI_ENTRY("%p, %zu, %s, %p", this, fat_size, fat, string);
+	for (i = 0; i < fat_size; ++i) {
+		c = (unsigned char)fat[i];
+		if (c > 0x80)
+			c = codepage[c - 0x60];
+		string[i] = c;
+		if (!c)
+			break;
+	}
+	string[i] = 0;
+	EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_str_to_fat() - convert a utf-16 string to legal characters for a FAT
+ *                    file name in an OEM code page
+ *
+ * @this:	unicode collation protocol instance
+ * @string:	Unicode string to convert
+ * @fat_size:	size of the target buffer
+ * @fat:	converted string
+ *
+ * This function implements the StrToFat() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * Return:	true if an illegal character was substituted by '_'.
+ */
+static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
+				  const u16 *string, efi_uintn_t fat_size,
+				  char *fat)
+{
+	efi_uintn_t i;
+	s32 c;
+	bool ret = false;
+
+	EFI_ENTRY("%p, %ls, %zu, %p", this, string, fat_size, fat);
+	for (i = 0; i < fat_size;) {
+		c = utf16_get(&string);
+		switch (c) {
+		/* Ignore period and space */
+		case '.':
+		case ' ':
+			continue;
+		case 0:
+			break;
+		}
+		c = utf_to_upper(c);
+		if (utf_to_cp(&c, codepage) ||
+		    (c && (c < 0x20 || strchr(illegal, c)))) {
+			ret = true;
+			c = '_';
+		}
+
+		fat[i] = c;
+		if (!c)
+			break;
+		++i;
+	}
+	EFI_EXIT(EFI_SUCCESS);
+	return ret;
+}
+
+static const struct efi_unicode_collation_protocol efi_unicode_collation_protocol2 = {
+	.stri_coll = efi_stri_coll,
+	.metai_match = efi_metai_match,
+	.str_lwr = efi_str_lwr,
+	.str_upr = efi_str_upr,
+	.fat_to_str = efi_fat_to_str,
+	.str_to_fat = efi_str_to_fat,
+	.supported_languages = "en",
+};
+
+static int efi_unicode_collation_init(void)
+{
+	efi_add_root_node_protocol_deferred(&efi_guid_unicode_collation_protocol2,
+					    &efi_unicode_collation_protocol2);
+	return 0;
+}
+device_initcall(efi_unicode_collation_init);
diff --git a/include/efi/protocol/unicode_collation.h b/include/efi/protocol/unicode_collation.h
new file mode 100644
index 000000000000..13ebad6fb0d8
--- /dev/null
+++ b/include/efi/protocol/unicode_collation.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _EFI_PROTOCOL_UNICODE_COLLATION_H
+#define _EFI_PROTOCOL_UNICODE_COLLATION_H
+
+#include <efi/types.h>
+
+struct efi_unicode_collation_protocol {
+	efi_intn_t (EFIAPI *stri_coll)(
+		struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2);
+	bool (EFIAPI *metai_match)(struct efi_unicode_collation_protocol *this,
+				   const u16 *string, const u16 *patter);
+	void (EFIAPI *str_lwr)(struct efi_unicode_collation_protocol
+			       *this, u16 *string);
+	void (EFIAPI *str_upr)(struct efi_unicode_collation_protocol *this,
+			       u16 *string);
+	void (EFIAPI *fat_to_str)(struct efi_unicode_collation_protocol *this,
+				  efi_uintn_t fat_size, char *fat, u16 *string);
+	bool (EFIAPI *str_to_fat)(struct efi_unicode_collation_protocol *this,
+				  const u16 *string, efi_uintn_t fat_size,
+				  char *fat);
+	char *supported_languages;
+};
+
+#endif
-- 
2.47.3




More information about the barebox mailing list